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