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