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