Annotation of libwww/LineMode/src/GridText.c, revision 1.16
1.13 frystyk 1: /* GridText.c
2: ** CHARACTER GRID HYPERTEXT OBJECT
3: **
4: ** (c) COPYRIGHT CERN 1994.
5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
1.1 timbl 7: */
8:
1.16 ! frystyk 9: #include <assert.h>
! 10:
! 11: #include "WWWLib.h"
! 12: #include "HTFont.h"
! 13: #include "GridStyle.h"
! 14: #include "GridText.h"
1.1 timbl 15:
1.11 howcome 16: /* HWL 18/7/94: applied patch from agl@glas2.glas.apc.org (Anton Tropashko) */
17: #ifdef CYRILLIC
18: #include "a_stdio.h"
19: #endif
20:
1.1 timbl 21: struct _HTStream { /* only know it as object */
22: CONST HTStreamClass * isa;
23: /* ... */
24: };
25:
26: #define MAX_LINE HTScreenWidth /* No point in accumulating more */
1.16 ! frystyk 27: #ifdef THINK_C
! 28: #define LOADED_LIMIT 3 /* For now, save last two texts */
! 29: #else
! 30: #define LOADED_LIMIT 6 /* For now, save last five texts */
! 31: #endif
1.1 timbl 32:
33: /* From main program:
34: */
1.2 timbl 35: extern char * start_reference; /* Template for anchors */
36: extern char * end_reference; /* Template for anchors */
1.1 timbl 37: extern char * end_mark; /* Template for end */
38: extern int HTScreenWidth, /* The screen width */
39: HTScreenHeight; /* The screen height */
40: extern BOOL display_anchors; /* anchor will be shown in text? */
41:
42: #ifdef CURSES
43: #define DISPLAY_LINES (HTScreenHeight)
44: #define TITLE_LINES 0
45: #else
46: #define DISPLAY_LINES (HTScreenHeight - 1) /* Exclude prompt line */
47: #define TITLE_LINES 1
48: #endif
49:
50:
51: /* From default style sheet:
52: */
53: extern HTStyleSheet * styleSheet; /* Default or overridden */
54:
55:
56: /* Exports
57: */
58: PUBLIC HText * HTMainText = 0; /* Equivalent of main window */
59: PUBLIC HTParentAnchor * HTMainAnchor = 0; /* Anchor for HTMainText */
60:
61: typedef struct _line {
62: struct _line *next;
63: struct _line *prev;
64: short unsigned offset; /* Implicit initial spaces */
65: short unsigned size; /* Number of characters */
66: BOOL split_after; /* Can we split after? */
67: BOOL bullet; /* Do we bullet? */
68: char data[1]; /* Space for terminator at least! */
69: } HTLine;
70:
71: #define LINE_SIZE(l) (sizeof(HTLine)+(l)) /* allow for terminator */
72:
73: typedef struct _TextAnchor {
74: struct _TextAnchor * next;
75: int number; /* For user interface */
76: int start; /* Characters */
77: int extent; /* Characters */
78: HTChildAnchor * anchor;
79: } TextAnchor;
80:
81:
82: /* Notes on struct _Htext:
83: ** next_line is valid iff state is false.
84: ** top_of_screen line means the line at the top of the screen
85: ** or just under the title if there is one.
86: */
87: struct _HText {
88: HTParentAnchor * node_anchor;
89: char * title;
90: HTLine * last_line;
91: int lines; /* Number of them */
92: int chars; /* Number of them */
93: TextAnchor * first_anchor; /* Singly linked list */
94: TextAnchor * last_anchor;
95: int last_anchor_number; /* user number */
96: /* For Internal use: */
97: HTStyle * style; /* Current style */
98: int display_on_the_fly; /* Lines left */
1.3 timbl 99: BOOL all_pages; /* Loop on the fly */
1.1 timbl 100: int top_of_screen; /* Line number */
101: HTLine * top_of_screen_line; /* Top */
102: HTLine * next_line; /* Bottom + 1 */
103: int permissible_split; /* in last line */
104: BOOL in_line_1; /* of paragraph */
105: BOOL stale; /* Must refresh */
106:
107: HTStream* target; /* Output stream */
108: HTStreamClass targetClass; /* Output routines */
109: };
110:
111:
112: #define PUTC(c) (*text->targetClass.put_character)(text->target, c)
113: #define PUTS(s) (*text->targetClass.put_string)(text->target, s)
114:
115: /* Boring static variable used for moving cursor across
116: */
117:
118: #define SPACES(n) (&space_string[HTScreenWidth - (n)])
119: /* String containing blank spaces only */
120: PRIVATE char * space_string;
121:
122: PRIVATE HTStyle default_style =
123: { 0, "(Unstyled)", "",
124: (HTFont)0, 1.0, HT_BLACK, 0, 0,
125: 0, 0, 0, HT_LEFT, 1, 0, 0,
126: NO, NO, 0, 0, 0 };
127:
128:
129: PUBLIC void clear_screen NOPARAMS; /* Forward */
130:
131: PRIVATE HTList * loaded_texts; /* A list of all those in memory */
132:
133: /* Creation Method
134: ** ---------------
1.3 timbl 135: **
136: ** Interactive version
137: **
1.1 timbl 138: */
139: PUBLIC HText * HText_new ARGS1(HTParentAnchor *,anchor)
140: {
141: HTLine * line;
142: HText * self = (HText *) malloc(sizeof(*self));
143: if (!self) return self;
144:
145: if (!loaded_texts) loaded_texts = HTList_new();
146: HTList_addObject(loaded_texts, self);
147: if (HTList_count(loaded_texts) >= LOADED_LIMIT) {
148: if (TRACE) fprintf(stderr, "GridText: Freeing off cached doc.\n");
149: HText_free((HText *)HTList_removeFirstObject(loaded_texts));
150: }
151:
152: line = self->last_line = (HTLine *)malloc(LINE_SIZE(MAX_LINE));
153: if (line == NULL) outofmem(__FILE__, "HText_New");
154: line->next = line->prev = line;
155: line->offset = line->size = 0;
156: self->lines = self->chars = 0;
157: self->title = 0;
158: self->first_anchor = self->last_anchor = 0;
159: self->style = &default_style;
160: self->top_of_screen = 0;
161: self->node_anchor = anchor;
162: self->last_anchor_number = 0; /* Numbering of them for references */
163: self->stale = YES;
164:
165: self->target = NULL;
166:
167: HTAnchor_setDocument(anchor, (HyperDoc *)self);
168:
169: clear_screen();
170: HTMainText = self;
171: HTMainAnchor = anchor;
172: self->display_on_the_fly = DISPLAY_LINES;
1.3 timbl 173: self->all_pages = NO; /* One page at a time on the fly */
1.1 timbl 174:
175: if (!space_string) { /* Make a blank line */
176: char *p;
177: space_string = (char *)malloc(HTScreenWidth+1);
178: if (space_string == NULL) outofmem(__FILE__, "HText_New");
179: for (p=space_string; p<space_string+HTScreenWidth; p++)
180: *p = ' '; /* Used for printfs later */
181: space_string[HTScreenWidth] = '\0';
182: }
183:
184: return self;
185: }
186:
187:
188: /* Creation Method 2
189: ** ---------------
190: **
1.4 timbl 191: ** Non-interative OR interactive if stream is NULL
1.1 timbl 192: ** Stream is assumed open and left open.
193: */
194: PUBLIC HText * HText_new2 ARGS2(
195: HTParentAnchor *, anchor,
196: HTStream*, stream)
197:
198: {
1.3 timbl 199: HText * me = HText_new(anchor);
1.1 timbl 200:
201: if (stream) {
1.3 timbl 202: me->target = stream;
203: me->targetClass = *stream->isa; /* copy action procedures */
1.4 timbl 204: me->all_pages = YES; /* Display whole file on the fly */
1.3 timbl 205: }
206: return me;
1.1 timbl 207: }
208:
209:
210: /* Free Entire Text
211: ** ----------------
212: */
213: PUBLIC void HText_free ARGS1(HText *,self)
214: {
215: HTAnchor_setDocument(self->node_anchor, (HyperDoc *)0);
216:
217: while(YES) { /* Free off line array */
218: HTLine * l = self->last_line;
219: l->next->prev = l->prev;
220: l->prev->next = l->next; /* Unlink l */
221: self->last_line = l->prev;
222: free(l);
223: if (l == self->last_line) break; /* empty */
224: };
225:
226: while(self->first_anchor) { /* Free off anchor array */
227: TextAnchor * l = self->first_anchor;
228: self->first_anchor = l->next;
229: free(l);
230: }
231: free(self);
1.8 frystyk 232: if (self == HTMainText) /* Henrik 24/02-94 */
233: HTMainText = NULL;
1.1 timbl 234: }
235:
236:
237: /* Display Methods
238: ** ---------------
239: */
240: /* Clear the screen (on screen-mode systems)
241: ** ----------------
242: */
243: PUBLIC void clear_screen NOARGS
244: {
1.8 frystyk 245:
1.1 timbl 246: if (TRACE) return; /* in trace mode, don't clear trace away */
247: #ifdef CURSES
248: if (w_text != NULL) {
249: wmove(w_text,0,0);
250: wclear(w_text);
251: }
252: #else
253: #ifdef VM
254: #ifdef NEWLIB
255: int newlib_ncmd = 2;
256: char newlib_cmd[2][80];
257:
258: memset(newlib_cmd, ' ', sizeof(newlib_cmd));
259: strncpy(newlib_cmd[0], "clear", 5); /* Clear screen */
260: strncpy(newlib_cmd[1], "quit", 4); /* Leave newlib */
261: newlib(newlib_cmd, &newlib_ncmd); /* Execute command */
262:
263: #else /* not NEWLIB - real VM */
264: system("CLRSCRN"); /* Clear screen */
265: #endif /* not NEWLIB */
266: #else /* Not VM */
267: /* Do nothing */
268: #endif /* Not VM */
269: #endif /* Not CURSES */
270: if (HTMainText) HTMainText->stale = YES;
271: }
272:
273:
274: /* Output a line
275: ** -------------
276: */
277: PRIVATE void display_line ARGS2(HText *,text, HTLine *,line)
278: {
279: #ifdef CURSES
280: int y, x;
281:
282: waddstr(w_text, SPACES(line->offset));
283: waddstr(w_text, line->data);
284: getyx(w_text, y, x);
285: if (y < DISPLAY_LINES-1) {
286: wmove(w_text, ++y, 0);
287: }
288: #else
289: if (!text->target)
1.11 howcome 290: {
291: #ifdef CYRILLIC
292: /* HWL 18/7/94: applied patch from agl@glas2.glas.apc.org (Anton Tropashko) */
293: a_print(SPACES(line->offset),H,stdout);
294: a_print(line->data,H,stdout);
295: fputc('\n',stdout);
296: #else
1.1 timbl 297: printf("%s%s\n", SPACES(line->offset), line->data);
1.11 howcome 298: #endif
299: }
1.1 timbl 300: else {
301: PUTS(SPACES(line->offset));
302: PUTS(line->data);
303: PUTC('\n');
304: }
305: #endif
306:
307: }
308:
309: /* Output the title line
310: ** ---------------------
311: */
312: PRIVATE void display_title ARGS1(HText *,text)
313: {
314: CONST char * title = HTAnchor_title(text->node_anchor);
315: char percent[20], format[20];
316: if (text->lines > (DISPLAY_LINES-1)) {
317: #ifdef NOPE
318: sprintf(percent, " (p%d of %d)",
319: (text->top_of_screen/(DISPLAY_LINES-1)) + 1,
320: (text->lines-1)/(DISPLAY_LINES-1) + 1);
321: sprintf(percent, " (%d%%)",
322: 100*(text->top_of_screen+DISPLAY_LINES-1)/ /* Seen */
323: (text->lines)); /* Total */
324: #else
325: sprintf(percent, " (%d/%d)",
326: text->top_of_screen+DISPLAY_LINES-1, /* Seen */
327: text->lines); /* Total */
328: #endif
329: } else {
330: percent[0] = 0; /* Null string */
331: }
332:
333: sprintf(format, "%%%d.%ds%%s\n", /* Generate format string */
334: (int)(HTScreenWidth-strlen(percent)),
335: (int)(HTScreenWidth-strlen(percent)) );
336: /* if (TRACE) fprintf(stderr, "FORMAT IS `%s'\n", format); */
337: #ifdef CURSES
338: mvwprintw(w_top, 0, 0, format, title, percent);
339: wrefresh(w_top);
340: #else
341: if (!text->target) printf(format, title, percent);
342: else {
343: char * line = (char*)malloc(HTScreenWidth+10);
344: sprintf(line, format, title, percent);
345: PUTS(line);
346: free(line);
347: }
348: #endif
349: }
350:
351:
352: /* Fill the screen with blank after the file
353: ** --------------------------
354: */
355: PRIVATE void fill_screen ARGS2(HText *, text, int,n)
356: {
357: if (n<=0 || n>1000) return; /* Large value means no pagination */
358: if (text->target) return;
359: #ifdef CURSES
360: waddstr(w_text, end_mark);
361: wclrtobot(w_text);
362: wrefresh(w_text);
363: #else
364: #ifndef VIOLA
365: if (!text->target) printf("%s\n", end_mark);
366: else { PUTS(end_mark); PUTC('\n'); }
367: n--;
368:
369: for (; n; n--) {
370: if (!text->target) printf("\n");
371: else PUTC('\n');
372: }
373: #endif
374: #endif /* Not CURSES */
375: }
376:
377:
378: /* Output a page
379: ** -------------
380: */
381: PRIVATE void display_page ARGS2(HText *,text, int,line_number)
382: {
383: HTLine * line = text->last_line->prev;
384: int i;
385: CONST char * title = HTAnchor_title(text->node_anchor);
386: int lines_of_text = title ? (DISPLAY_LINES-TITLE_LINES) : DISPLAY_LINES;
387: int last_screen = text->lines - lines_of_text;
388:
389: /* Constrain the line number to be within the document
390: */
391: if (text->lines <= lines_of_text) line_number = 0;
392: else if (line_number>last_screen) line_number = last_screen;
393: else if (line_number < 0) line_number = 0;
394:
395: for(i=0, line = text->last_line->next; /* Find line */
396: i<line_number && (line!=text->last_line);
397: i++, line=line->next) /* Loop */ assert(line->next != NULL);
398:
399: while((line!=text->last_line) && (line->size==0)) { /* Skip blank lines */
400: assert(line->next != NULL);
401: line = line->next;
402: line_number ++;
403: }
404:
405: /* Can we just scroll to it?
406: */
407: #ifndef VM /* No scrolling */
408: if (!text->stale && (line_number>=text->top_of_screen) &&
409: (line_number < text->top_of_screen + DISPLAY_LINES)) { /* Yes */
410: lines_of_text = line_number - text->top_of_screen;
411: line = text->next_line;
412: text->top_of_screen = line_number;
413: #ifdef CURSES
414: scrollok(w_text, TRUE);
415: for (i = 0; i < lines_of_text; i++) {
416: scroll(w_text);
417: }
418: #endif
419: } else
420: #endif
421: {
422: clear_screen(); /* No */
423: text->top_of_screen = line_number;
424: if (title) display_title(text);
425: }
426:
427: /* Bug: when we scroll to a part slightly futher down a page which previously
428: fitted all on one screen including the [End], the code below will add an
429: extra [End] to the screen, giving a total of two, one underneath the other.
430: */
431:
432: /* print it
433: */
434: if (line) {
435: for(i=0;
436: (i< lines_of_text) && (line != text->last_line); i++) {
437: assert(line != NULL);
438: display_line(text, line);
439: line = line->next;
440: }
441: fill_screen(text, lines_of_text - i);
442: }
443:
444: text->next_line = line; /* Line after screen */
445: text->stale = NO; /* Display is up-to-date */
446: }
447:
448:
449: /* Object Building methods
450: ** -----------------------
451: **
452: ** These are used by a parser to build the text in an object
453: */
454: PUBLIC void HText_beginAppend ARGS1(HText *,text)
455: {
456: text->permissible_split = 0;
457: text->in_line_1 = YES;
458: }
459:
460:
461: /* Add a new line of text
462: ** ----------------------
463: **
464: ** On entry,
465: **
466: ** split is zero for newline function, else number of characters
467: ** before split.
468: ** text->display_on_the_fly
469: ** may be set to indicate direct output of the finished line.
1.3 timbl 470: ** text->all_pages
471: ** if set indicates all pages are to be done on the fly.
1.1 timbl 472: ** On exit,
473: ** A new line has been made, justified according to the
474: ** current style. Text after the split (if split nonzero)
475: ** is taken over onto the next line.
476: **
477: ** If display_on_the_fly is set, then it is decremented and
478: ** the finished line is displayed.
479: */
480: #define new_line(text) split_line(text, 0)
481:
482: PRIVATE void split_line ARGS2(HText *,text, int,split)
483: {
484: HTStyle * style = text->style;
485: int spare;
1.14 frystyk 486: int indent = (int) (text->in_line_1 ? text->style->indent1st
487: : text->style->leftIndent);
1.7 frystyk 488:
1.1 timbl 489: /* Make new line
490: */
491: HTLine * previous = text->last_line;
492: HTLine * line = (HTLine *) malloc(LINE_SIZE(MAX_LINE));
493:
494: if (line == NULL) outofmem(__FILE__, "split_line");
495: text->lines++;
496:
497: previous->next->prev = line;
498: line->prev = previous;
499: line->next = previous->next;
500: previous->next = line;
501: text->last_line = line;
502: line->size = 0;
503: line->offset = 0;
504:
505: /* Split at required point
506: */
507: if (split) { /* Delete space at "split" splitting line */
508: char * p;
509: previous->data[previous->size] = 0;
510: for (p = &previous->data[split]; *p; p++)
511: if (*p != ' ') break;
512: strcpy(line->data, p);
513: line->size = strlen(line->data);
514: previous->size = split;
515: }
516:
517: /* Economise on space.
518: ** Not on the RS6000 due to a chaotic bug in realloc argument passing.
519: ** Same problem with Ultrix (4.2) : realloc() is not declared properly.
520: */
1.16 ! frystyk 521: #ifndef AIX
! 522: #ifndef ultrix
1.1 timbl 523: while ((previous->size > 0) &&
524: (previous->data[previous->size-1] == ' ')) /* Strip trailers */
525: previous->size--;
1.7 frystyk 526:
1.1 timbl 527: previous = (HTLine *) realloc (previous, LINE_SIZE(previous->size));
528: if (previous == NULL) outofmem(__FILE__, "split_line");
1.16 ! frystyk 529: #endif /* ultrix */
! 530: #endif /* AIX */
1.1 timbl 531:
532: previous->prev->next = previous; /* Link in new line */
533: previous->next->prev = previous; /* Could be same node of course */
534:
535: /* Terminate finished line for printing
536: */
537: previous->data[previous->size] = 0;
1.7 frystyk 538:
1.1 timbl 539: /* Align left, right or center
540: */
541:
1.14 frystyk 542: spare = (int) (HTScreenWidth - style->rightIndent + style->leftIndent -
543: previous->size); /* @@ first line indent */
1.1 timbl 544:
545: switch (style->alignment) {
546: case HT_CENTER :
547: previous->offset = previous->offset + indent + spare/2;
548: break;
549: case HT_RIGHT :
550: previous->offset = previous->offset + indent + spare;
551: break;
552: case HT_LEFT :
553: case HT_JUSTIFY : /* Not implemented */
554: default:
555: previous->offset = previous->offset + indent;
556: break;
557: } /* switch */
558:
559: text->chars = text->chars + previous->size + 1; /* 1 for the line */
560: text->in_line_1 = NO; /* unless caller sets it otherwise */
561:
562: /* If displaying as we go, output it:
563: */
564: if (text->display_on_the_fly) {
565: if (text->display_on_the_fly == DISPLAY_LINES) { /* First line */
566: if (previous->size == 0) {
567: text->top_of_screen++; /* Scroll white space off top */
568: return;
569: }
570: if (HTAnchor_title(text->node_anchor)) { /* Title exists */
571: display_title(text);
572: text->display_on_the_fly--;
573: }
574: }
575: display_line(text, previous);
576: text->display_on_the_fly--;
1.3 timbl 577:
578: /* Loop to top of next page? */
579: if (!text->display_on_the_fly && text->all_pages) {
580: PUTS("\f\n"); /* Form feed on its own line a la rfc1111 */
581: text->display_on_the_fly = DISPLAY_LINES;
582: }
1.1 timbl 583: }
584: } /* split_line */
585:
586:
587: /* Allow vertical blank space
588: ** --------------------------
589: */
590: PRIVATE void blank_lines ARGS2(HText *,text, int,newlines)
591: {
592: if (text->last_line->size == 0) { /* No text on current line */
593: HTLine * line = text->last_line->prev;
594: while ((line!=text->last_line) && (line->size == 0)) {
595: if (newlines==0) break;
596: newlines--; /* Don't bother: already blank */
597: line = line->prev;
598: }
599: } else {
600: newlines++; /* Need also to finish this line */
601: }
602:
603: for(;newlines;newlines--) {
604: new_line(text);
605: }
606: text->in_line_1 = YES;
607: }
608:
609:
610: /* New paragraph in current style
611: ** ------------------------------
612: ** See also: setStyle.
613: */
614:
615: PUBLIC void HText_appendParagraph ARGS1(HText *,text)
616: {
1.14 frystyk 617: int after = (int) text->style->spaceAfter;
618: int before = (int) text->style->spaceBefore;
1.1 timbl 619: blank_lines(text, after>before ? after : before);
620: }
621:
622:
623: /* Set Style
624: ** ---------
625: **
626: ** Does not filter unnecessary style changes.
627: */
628: PUBLIC void HText_setStyle ARGS2(HText *,text, HTStyle *,style)
629: {
630: int after, before;
631:
632: if (!style) return; /* Safety */
1.14 frystyk 633: after = (int) text->style->spaceAfter;
634: before = (int) style->spaceBefore;
1.12 frystyk 635: if (SGML_TRACE) fprintf(stderr, "HTML: Change to style %s\n", style->name);
1.1 timbl 636:
637: blank_lines (text, after>before ? after : before);
638:
639: text->style = style;
640: }
641:
642:
643: /* Append a character to the text object
644: ** -------------------------------------
645: */
646: PUBLIC void HText_appendCharacter ARGS2(HText *,text, char,ch)
647: {
648: HTLine * line = text->last_line;
649: HTStyle * style = text->style;
1.14 frystyk 650: int indent = (int)(text->in_line_1 ? style->indent1st : style->leftIndent);
1.1 timbl 651:
652: /* New Line
653: */
654: if (ch == '\n') {
655: new_line(text);
656: text->in_line_1 = YES; /* First line of new paragraph */
657: return;
658: }
659:
660: /* Tabs
661: */
662:
663: if (ch == '\t') {
664: HTTabStop * tab;
665: int target; /* Where to tab to */
666: int here = line->size + line->offset +indent;
667: if (style->tabs) { /* Use tab table */
668: for (tab = style->tabs;
669: tab->position <= here;
670: tab++)
671: if (!tab->position) {
672: new_line(text);
673: return;
674: }
1.14 frystyk 675: target = (int) tab->position;
1.1 timbl 676: } else if (text->in_line_1) { /* Use 2nd indent */
677: if (here >= style->leftIndent) {
678: new_line(text); /* wrap */
679: return;
680: } else {
1.14 frystyk 681: target = (int) style->leftIndent;
1.1 timbl 682: }
683: } else { /* Default tabs align with left indent mod 8 */
684: #ifdef DEFAULT_TABS_8
685: target = ((line->offset + line->size + 8) & (-8))
686: + style->leftIndent;
687: #else
688: new_line(text);
689: return;
690: #endif
691: }
692: if (target > HTScreenWidth - style->rightIndent) {
693: new_line(text);
694: return;
695: } else {
696: text->permissible_split = line->size; /* Can split here */
697: if (line->size == 0) line->offset = line->offset + target - here;
698: else for(; here<target; here++) {
699: line->data[line->size++] = ' '; /* Put character into line */
700: }
701: return;
702: }
703: /*NOTREACHED*/
704: } /* if tab */
705:
706:
707: if (ch==' ') {
708: text->permissible_split = line->size; /* Can split here */
709: }
710:
711: /* Check for end of line
712: */
713: if (indent + line->offset + line->size + style->rightIndent
714: >= HTScreenWidth) {
715: if (style->wordWrap) {
1.7 frystyk 716: if(text->permissible_split > line->size) /* HENRIK 21/02-94 */
717: text->permissible_split = line->size;
1.1 timbl 718: split_line(text, text->permissible_split);
719: if (ch==' ') return; /* Ignore space causing split */
720: } else new_line(text);
721: }
722:
723: /* Insert normal characters
724: */
725: if (ch == HT_NON_BREAK_SPACE) {
726: ch = ' ';
727: }
728: {
729: HTLine * line = text->last_line; /* May have changed */
730: HTFont font = style->font;
731: line->data[line->size++] = /* Put character into line */
732: font & HT_CAPITALS ? TOUPPER(ch) : ch;
733: if (font & HT_DOUBLE) /* Do again if doubled */
734: HText_appendCharacter(text, HT_NON_BREAK_SPACE);
735: /* NOT a permissible split */
736: }
737: }
738:
739: /* Anchor handling
740: ** ---------------
741: */
742: /* Start an anchor field
743: */
744: PUBLIC void HText_beginAnchor ARGS2(HText *,text, HTChildAnchor *,anc)
745: {
746: TextAnchor * a = (TextAnchor *) malloc(sizeof(*a));
1.2 timbl 747: char marker[100];
1.1 timbl 748:
749: if (a == NULL) outofmem(__FILE__, "HText_beginAnchor");
750: a->start = text->chars + text->last_line->size;
751: a->extent = 0;
752: if (text->last_anchor) {
753: text->last_anchor->next = a;
754: } else {
755: text->first_anchor = a;
756: }
757: a->next = 0;
758: a->anchor = anc;
759: text->last_anchor = a;
760:
761: if (HTAnchor_followMainLink((HTAnchor*)anc)) {
762: a->number = ++(text->last_anchor_number);
763: } else {
764: a->number = 0;
765: }
1.2 timbl 766:
767: if (start_reference && a->number && display_anchors) {
768: /* If it goes somewhere */
769: sprintf(marker, start_reference, a->number);
770: HText_appendText(text, marker);
771: }
1.1 timbl 772: }
773:
774:
775: PUBLIC void HText_endAnchor ARGS1(HText *,text)
776: {
777: TextAnchor * a = text->last_anchor;
778: char marker[100];
779: if (a->number && display_anchors) { /* If it goes somewhere */
1.2 timbl 780: sprintf(marker, end_reference, a->number);
1.1 timbl 781: HText_appendText(text, marker);
782: }
783: a->extent = text->chars + text->last_line->size - a->start;
784: }
785:
786:
1.5 timbl 787: /* IMAGES
788: */
789: PUBLIC void HText_appendImage ARGS5(
790: HText *, text,
791: HTChildAnchor *, anc,
792: CONST char *, alt,
1.9 frystyk 793: CONST char *, alignment,
1.5 timbl 794: BOOL, isMap)
795: {
1.9 frystyk 796: HText_appendText(text, alt? alt : "[IMAGE]");
1.5 timbl 797: }
798:
1.1 timbl 799: PUBLIC void HText_appendText ARGS2(HText *,text, CONST char *,str)
800: {
801: CONST char * p;
802: for(p=str; *p; p++) {
803: HText_appendCharacter(text, *p);
804: }
805: }
806:
807:
808: PUBLIC void HText_endAppend ARGS1(HText *,text)
809: {
810: new_line(text);
811:
812: if (text->display_on_the_fly) { /* Not finished? */
813: fill_screen(text, text->display_on_the_fly); /* Finish it */
814: text->display_on_the_fly = 0;
815: text->next_line = text->last_line; /* Bug fix after EvA 920117 */
816: text->stale = NO;
817: }
818: }
819:
820:
821:
822: /* Dump diagnostics to stderr
823: */
824: PUBLIC void HText_dump ARGS1(HText *,text)
825: {
826: fprintf(stderr, "HText: Dump called\n");
827: }
828:
829:
830: /* Return the anchor associated with this node
831: */
832: PUBLIC HTParentAnchor * HText_nodeAnchor ARGS1(HText *,text)
833: {
834: return text->node_anchor;
835: }
836:
837: /* GridText specials
838: ** =================
839: */
840: /* Return the anchor with index N
841: **
842: ** The index corresponds to the number we print in the anchor.
843: */
844: PUBLIC HTChildAnchor * HText_childNumber ARGS2(HText *,text, int,number)
845: {
846: TextAnchor * a;
847: for (a = text->first_anchor; a; a = a->next) {
848: if (a->number == number) return a->anchor;
849: }
850: return (HTChildAnchor *)0; /* Fail */
851: }
852:
853: PUBLIC void HText_setStale ARGS1(HText *,text)
854: {
1.16 ! frystyk 855: if (text)
! 856: text->stale = YES;
1.1 timbl 857: }
858:
859: PUBLIC void HText_refresh ARGS1(HText *,text)
860: {
1.16 ! frystyk 861: if (text && text->stale)
! 862: display_page(text, text->top_of_screen);
1.1 timbl 863: }
864:
865: PUBLIC int HText_sourceAnchors ARGS1(HText *,text)
866: {
1.16 ! frystyk 867: return (text ? text->last_anchor_number : -1);
1.1 timbl 868: }
869:
870: PUBLIC BOOL HText_canScrollUp ARGS1(HText *,text)
871: {
1.16 ! frystyk 872: return (text && text->top_of_screen != 0);
1.1 timbl 873: }
874:
875: PUBLIC BOOL HText_canScrollDown ARGS1(HText *,text)
876: {
1.16 ! frystyk 877: return (text && (text->top_of_screen + DISPLAY_LINES -
! 878: (text->title ? TITLE_LINES : 0)) < text->lines);
1.1 timbl 879: }
880:
881: /* Scroll actions
882: */
883: PUBLIC void HText_scrollTop ARGS1(HText *,text)
884: {
885: display_page(text, 0);
886: }
887:
888: PUBLIC void HText_scrollDown ARGS1(HText *,text)
889: {
890: display_page(text, text->top_of_screen + DISPLAY_LINES -1);
891: }
892:
893: PUBLIC void HText_scrollUp ARGS1(HText *,text)
894: {
895: display_page(text, text->top_of_screen - DISPLAY_LINES +1);
896: }
897:
898: PUBLIC void HText_scrollBottom ARGS1(HText *,text)
899: {
900: display_page(text, text->lines - DISPLAY_LINES +1);
901: }
902:
903:
904: /* Browsing functions
905: ** ==================
906: */
907:
908: /* Bring to front and highlight it
909: */
910:
911: PRIVATE int line_for_char ARGS2(HText *,text, int,char_num)
912: {
913: int line_number =0;
914: int characters = 0;
915: HTLine * line = text->last_line->next;
916: for(;;) {
917: if (line == text->last_line) return 0; /* Invalid */
918: characters = characters + line->size + 1;
919: if (characters > char_num) return line_number;
920: line_number ++;
921: line = line->next;
922: }
923: }
924:
925: PUBLIC BOOL HText_select ARGS1(HText *,text)
926: {
1.13 frystyk 927: if (text) {
1.1 timbl 928: HTMainText = text;
929: HTMainAnchor = text->node_anchor;
930: display_page(text, text->top_of_screen);
1.13 frystyk 931: return YES;
1.1 timbl 932: }
1.16 ! frystyk 933: if (TRACE)
! 934: fprintf(stderr, "HText: Nothing to select!\n");
1.13 frystyk 935: return NO;
1.1 timbl 936: }
937:
938: PUBLIC BOOL HText_selectAnchor ARGS2(HText *,text, HTChildAnchor *,anchor)
939: {
940: TextAnchor * a;
941:
942: /* This is done later, hence HText_select is unused in GridText.c
943: Should it be the contrary ? @@@
944: if (text != HTMainText) {
945: HText_select(text);
946: }
947: */
948:
949: for(a=text->first_anchor; a; a=a->next) {
950: if (a->anchor == anchor) break;
951: }
952: if (!a) {
953: if (TRACE) fprintf(stderr, "HText: No such anchor in this text!\n");
954: return NO;
955: }
956:
957: if (text != HTMainText) { /* Comment out by ??? */
958: HTMainText = text; /* Put back in by tbl 921208 */
959: HTMainAnchor = text->node_anchor;
960: }
961:
962: {
963: int l = line_for_char(text, a->start);
964: if (TRACE) fprintf(stderr,
965: "HText: Selecting anchor [%d] at character %d, line %d\n",
966: a->number, a->start, l);
967:
968: if ( !text->stale &&
969: (l >= text->top_of_screen) &&
970: ( l < text->top_of_screen + DISPLAY_LINES-1))
971: return YES;
972:
973: display_page(text, l - (DISPLAY_LINES/3)); /* Scroll to it */
974: }
975:
976: return YES;
977: }
978:
979:
980: /* Editing functions - NOT IMPLEMENTED
981: ** =================
982: **
983: ** These are called from the application. There are many more functions
984: ** not included here from the orginal text object.
985: */
986:
987: /* Style handling:
988: */
989: /* Apply this style to the selection
990: */
991: PUBLIC void HText_applyStyle ARGS2(HText *, me, HTStyle *,style)
992: {
993:
994: }
995:
996:
997: /* Update all text with changed style.
998: */
999: PUBLIC void HText_updateStyle ARGS2(HText *, me, HTStyle *,style)
1000: {
1001:
1002: }
1003:
1004:
1005: /* Return style of selection
1006: */
1007: PUBLIC HTStyle * HText_selectionStyle ARGS2(
1008: HText *,me,
1009: HTStyleSheet *,sheet)
1010: {
1011: return 0;
1012: }
1013:
1014:
1015: /* Paste in styled text
1016: */
1017: PUBLIC void HText_replaceSel ARGS3(
1018: HText *,me,
1019: CONST char *,aString,
1020: HTStyle *,aStyle)
1021: {
1022: }
1023:
1024:
1025: /* Apply this style to the selection and all similarly formatted text
1026: ** (style recovery only)
1027: */
1028: PUBLIC void HTextApplyToSimilar ARGS2(HText *,me, HTStyle *,style)
1029: {
1030:
1031: }
1032:
1033:
1034: /* Select the first unstyled run.
1035: ** (style recovery only)
1036: */
1037: PUBLIC void HTextSelectUnstyled ARGS2(HText *,me, HTStyleSheet *,sheet)
1038: {
1039:
1040: }
1041:
1042:
1043: /* Anchor handling:
1044: */
1045: PUBLIC void HText_unlinkSelection ARGS1(HText *,me)
1046: {
1047:
1048: }
1049:
1050: PUBLIC HTAnchor * HText_referenceSelected ARGS1(HText *,me)
1051: {
1052: return 0;
1053: }
1054:
1055:
1056: #ifdef CURSES
1057: PUBLIC int HText_getTopOfScreen ARGS1(HText *,text)
1058: {
1059: return text->top_of_screen;
1060: }
1061:
1062: PUBLIC int HText_getLines ARGS1(HText *,text)
1063: {
1064: return text->lines;
1065: }
1066: #endif
1067: PUBLIC HTAnchor * HText_linkSelTo ARGS2(HText *,me, HTAnchor *,anchor)
1068: {
1069: return 0;
1070: }
1071:
1072:
Webmaster