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