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