1 /* GridText.c
2 ** CHARACTER GRID HYPERTEXT OBJECT
3 **
4 ** (c) COPYRIGHT MIT 1995.
5 ** Please first read the full copyright statement in the file COPYRIGH.
6 ** @(#) $Id$
7 **
8 ** This is the definition of the HyperDoc object and the HText interface
9 ** which is used in the current Library HTML parser
10 */
11
12 #include <assert.h>
13
14 #include "WWWLib.h"
15 #include "WWWCache.h"
16 #include "WWWApp.h"
17 #include "WWWHTML.h"
18 #include "HTBrowse.h"
19 #include "HTFont.h"
20 #include "GridStyle.h"
21 #include "GridText.h"
22
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
28 #define MAX_LINE HTScreenWidth /* No point in accumulating more */
29 #define LOADED_LIMIT 40
30
31 #ifdef CURSES
32 #define DISPLAY_LINES (HTScreenHeight)
33 #define TITLE_LINES 0
34 #else
35 #define DISPLAY_LINES (HTScreenHeight - 2) /* Exclude prompt line */
36 #define TITLE_LINES 1
37 #endif
38
39 struct _HTStream { /* only know it as object */
40 const HTStreamClass * isa;
41 /* ... */
42 };
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 */
88 TextAnchor * current_anchor;
89 /* For Internal use: */
90 HTStyle * style; /* Current style */
91 int display_on_the_fly; /* Lines left */
92 BOOL all_pages; /* Loop on the fly */
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 */
102 LineMode * pLm;
103 };
104
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 };
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,
127 0, 0, 0, HT_LEFT, 1, 0, tabs_8,
128 NO, NO, 0, 0, 0 };
129
130
131 PUBLIC void clear_screen (void); /* Forward */
132
133 PRIVATE HTList * loaded_texts; /* A list of all those in memory */
134
135 /* Creation Method
136 ** ---------------
137 **
138 ** Interactive version
139 **
140 */
141 extern LineMode * Context_getLineMode(HTRequest * request);
LMHText_new(HTRequest * request,HTParentAnchor * anchor,HTStream * outstrm)142 PUBLIC HText * LMHText_new (
143 HTRequest * request,
144 HTParentAnchor * anchor,
145 HTStream *outstrm)
146 {
147 HTLine * line;
148 HText * self;
149 if ((self = (HText *) HT_CALLOC(1, sizeof(*self))) == NULL)
150 /* HT_OUTOFMEM("HText"); */
151 return self;
152
153 self->pLm = Context_getLineMode(request);
154 if (!loaded_texts) loaded_texts = HTList_new();
155 HTList_addObject(loaded_texts, self);
156 if (HTList_count(loaded_texts) >= LOADED_LIMIT) {
157 HTTRACE(CACHE_TRACE, "MemoryCache. Freeing off cached doc.\n");
158 HText_free((HText *)HTList_removeFirstObject(loaded_texts));
159 }
160
161 if ((line = self->last_line = (HTLine *) HT_MALLOC(LINE_SIZE(MAX_LINE))) == NULL)
162 HT_OUTOFMEM("HText_New");
163 line->next = line->prev = line;
164 line->offset = line->size = 0;
165 self->lines = self->chars = 0;
166 self->title = 0;
167 self->first_anchor = self->last_anchor = self->current_anchor = 0;
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
176 HTAnchor_setDocument(anchor, (void *) self);
177
178 clear_screen();
179 HTMainText = self;
180 HTMainAnchor = anchor;
181 self->display_on_the_fly = DISPLAY_LINES;
182 self->all_pages = NO; /* One page at a time on the fly */
183
184 if (!space_string) { /* Make a blank line */
185 char *p;
186 if ((space_string = (char *) HT_MALLOC(HTScreenWidth+1)) == NULL)
187 HT_OUTOFMEM("HText_New");
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 **
200 ** Non-interative OR interactive if stream is NULL
201 ** Stream is assumed open and left open.
202 */
LMHText_new2(HTRequest * request,HTParentAnchor * anchor,HTStream * stream)203 PUBLIC HText * LMHText_new2 (HTRequest * request,
204 HTParentAnchor * anchor,
205 HTStream * stream)
206 {
207 HText * me = LMHText_new(request, anchor, stream);
208
209 if (stream) {
210 me->target = stream;
211 me->targetClass = *stream->isa; /* copy action procedures */
212 me->all_pages = YES; /* Display whole file on the fly */
213 }
214 return me;
215 }
216
217 /* Free a data object
218 ** ------------------
219 ** Removes the data object from the anchor
220 */
hyper_free(HText * self)221 PUBLIC void hyper_free (HText * self)
222 {
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);
232 } else
233 break;
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;
241 HT_FREE(self->last_line);
242 HT_FREE(self);
243 }
244 }
245
246
247 /* Free Entire Text
248 ** ----------------
249 */
HText_free(HText * self)250 PUBLIC void HText_free (HText * self)
251 {
252 if (self) {
253 HTAnchor_setDocument(self->node_anchor, NULL);
254 hyper_free(self);
255 }
256 }
257
LMHText_delete(HText * self)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
268 /*
269 ** Free all registered hypertext documents in memory
270 */
HText_freeAll(void)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
284
285 /* Display Methods
286 ** ---------------
287 */
288
289 /* Clear the screen (on screen-mode systems)
290 ** ----------------
291 */
clear_screen(void)292 PUBLIC void clear_screen (void)
293 {
294 if (WWWTRACE)
295 return; /* in trace mode, don't clear trace away */
296 #ifdef CURSES
297 if (w_text != NULL) {
298 wmove(w_text,0,0);
299 wclear(w_text);
300 }
301 #endif /* Not CURSES */
302 if (HTMainText) HTMainText->stale = YES;
303 }
304
305
306 /* Output a line
307 ** -------------
308 */
display_line(HText * text,HTLine * line)309 PRIVATE void display_line (HText * text, HTLine * line)
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)
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
329 OutputData(LineMode_getView(text->pLm), "%s%s\n", SPACES(line->offset), line->data);
330 #endif
331 }
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 */
display_title(HText * text)344 PRIVATE void display_title (HText * text)
345 {
346 const char * title = HTAnchor_title(text->node_anchor);
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
372 if (!text->target) OutputData(LineMode_getView(text->pLm), format, title, percent);
373 else {
374 char * line;
375 if ((line = (char *) HT_MALLOC(HTScreenWidth+10)) == NULL)
376 HT_OUTOFMEM("display_titile");
377 sprintf(line, format, title, percent);
378 PUTS(line);
379 HT_FREE(line);
380 }
381 #endif
382 }
383
384
385 /* Fill the screen with blank after the file
386 ** --------------------------
387 */
fill_screen(HText * text,int n)388 PRIVATE void fill_screen (HText * text, int n)
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
398 if (!text->target) OutputData(LineMode_getView(text->pLm), "%s\n", end_mark);
399 else { PUTS(end_mark); PUTC('\n'); }
400 n--;
401
402 for (; n; n--) {
403 if (!text->target) OutputData(LineMode_getView(text->pLm), "\n");
404 else PUTC('\n');
405 }
406 #endif
407 #endif /* Not CURSES */
408 }
409
410
411 /* Output a page
412 ** -------------
413 */
display_page(HText * text,int line_number)414 PRIVATE void display_page (HText * text, int line_number)
415 {
416 HTLine * line = text->last_line->prev;
417 int i;
418 const char * title = HTAnchor_title(text->node_anchor);
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 */
HText_beginAppend(HText * text)487 PUBLIC void HText_beginAppend (HText * text)
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.
503 ** text->all_pages
504 ** if set indicates all pages are to be done on the fly.
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
split_line(HText * text,int split)515 PRIVATE void split_line (HText * text, int split)
516 {
517 HTStyle * style = text->style;
518 int spare;
519 int indent = (int) (text->in_line_1 ? text->style->indent1st
520 : text->style->leftIndent);
521
522 /* Make new line
523 */
524 HTLine * previous = text->last_line;
525 HTLine * line;
526 if ((line = (HTLine *) HT_MALLOC(LINE_SIZE(MAX_LINE))) == NULL)
527 HT_OUTOFMEM("split_line");
528
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) &&
552 (previous->data[previous->size-1] == ' ')) /* Strip trailers */
553 previous->size--;
554
555 if ((previous = (HTLine *)
556 HT_REALLOC(previous, LINE_SIZE(previous->size)))==NULL)
557 HT_OUTOFMEM("split_line");
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;
564
565 /* Align left, right or center
566 */
567
568 spare = (int) (HTScreenWidth - style->rightIndent + style->leftIndent -
569 previous->size); /* @@ first line indent */
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--;
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 }
609 }
610 } /* split_line */
611
612
613 /* Allow vertical blank space
614 ** --------------------------
615 */
blank_lines(HText * text,int newlines)616 PRIVATE void blank_lines (HText * text, int newlines)
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
HText_appendParagraph(HText * text)641 PUBLIC void HText_appendParagraph (HText * text)
642 {
643 int after = (int) text->style->spaceAfter;
644 int before = (int) text->style->spaceBefore;
645 blank_lines(text, after>before ? after : before);
646 }
647
648
649 /* Set Style
650 ** ---------
651 **
652 ** Does not filter unnecessary style changes.
653 */
HText_setStyle(HText * text,HTStyle * style)654 PUBLIC void HText_setStyle (HText * text, HTStyle * style)
655 {
656 int after, before;
657
658 if (!style) return; /* Safety */
659 after = (int) text->style->spaceAfter;
660 before = (int) style->spaceBefore;
661 HTTRACE(SGML_TRACE, "Rendering... Change to style %s\n" _ style->name);
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 */
HText_appendCharacter(HText * text,char ch)670 PUBLIC void HText_appendCharacter (HText * text, char ch)
671 {
672 HTLine * line = text->last_line;
673 HTStyle * style = text->style;
674 int indent = (int)(text->in_line_1 ? style->indent1st : style->leftIndent);
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 }
699 target = (int) tab->position;
700 } else if (text->in_line_1) { /* Use 2nd indent */
701 if (here >= style->leftIndent) {
702 new_line(text); /* wrap */
703 return;
704 } else {
705 target = (int) style->leftIndent;
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) {
740 if(text->permissible_split > line->size) /* HENRIK 21/02-94 */
741 text->permissible_split = line->size;
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
HText_appendText(HText * text,const char * str)763 PUBLIC void HText_appendText (HText * text, const char * str)
764 {
765 const char * p;
766 for(p=str; *p; p++) {
767 HText_appendCharacter(text, *p);
768 }
769 }
770
771
HText_endAppend(HText * text)772 PUBLIC void HText_endAppend (HText * text)
773 {
774 new_line(text);
775
776 if (text->display_on_the_fly) { /* Not finished? */
777 fill_screen(text, text->display_on_the_fly); /* Finish it */
778 text->display_on_the_fly = 0;
779 text->next_line = text->last_line; /* Bug fix after EvA 920117 */
780 text->stale = NO;
781 }
782 }
783
784 /* Anchor handling
785 ** ---------------
786 */
787 /* Start an anchor field
788 */
LMHText_beginAnchor(HText * text,int elem_num,int attr_num,HTChildAnchor * anc,const BOOL * present,const char ** value)789 PUBLIC void LMHText_beginAnchor (HText * text,
790 int elem_num, int attr_num, HTChildAnchor * anc,
791 const BOOL *present, const char **value)
792 {
793 TextAnchor * a;
794
795 /* this is because it's called as link callback */
796 if (elem_num != HTML_A)
797 return;
798
799 if ((a = (TextAnchor *) HT_MALLOC(sizeof(*a))) == NULL)
800 HT_OUTOFMEM("HText_beginAnchor");
801 a->start = text->chars + text->last_line->size;
802 a->extent = 0;
803 if (text->last_anchor) {
804 text->last_anchor->next = a;
805 } else {
806 text->first_anchor = a;
807 }
808 a->next = 0;
809 a->anchor = anc;
810 text->last_anchor = a;
811 text->current_anchor = a;
812
813 if (HTAnchor_followMainLink((HTAnchor*)anc)) {
814 a->number = ++(text->last_anchor_number);
815 } else {
816 a->number = 0;
817 }
818 }
819
820
LMHText_endAnchor(HText * text)821 PUBLIC void LMHText_endAnchor (HText * text)
822 {
823 TextAnchor * a = text->current_anchor;
824 char marker[100];
825
826 if (!a) /* </A> without <A> */
827 return;
828
829 if (a->number && display_anchors) { /* If it goes somewhere */
830 sprintf(marker, end_reference, a->number);
831 HText_appendText(text, marker);
832 }
833 a->extent = text->chars + text->last_line->size - a->start;
834 text->current_anchor = 0;
835 }
836
837
838 /* LMHText_addText() satisfies HText callback requirement. */
LMHText_addText(HText * text,const char * str,int length)839 PUBLIC void LMHText_addText (HText * text, const char * str, int length)
840 {
841 const char * p;
842 int i;
843 for (i=0,p=str; i<length; ++i,++p) {
844 HText_appendCharacter(text, *p);
845 }
846 }
847
848 /* IMAGES
849 */
HText_appendImage(HText * text,HTChildAnchor * anc,const char * alt,const char * alignment,BOOL isMap)850 PUBLIC void HText_appendImage (
851 HText * text,
852 HTChildAnchor * anc,
853 const char * alt,
854 const char * alignment,
855 BOOL isMap)
856 {
857 HText_appendText(text, alt? alt : "[IMAGE]");
858 }
859
HText_appendObject(HText * text,int element_number,const BOOL * present,const char ** value)860 PUBLIC void HText_appendObject (HText * text, int element_number,
861 const BOOL * present, const char ** value)
862 {
863 }
864
HText_appendLink(HText * text,HTChildAnchor * anchor,const BOOL * present,const char ** value)865 PUBLIC void HText_appendLink (HText * text, HTChildAnchor * anchor,
866 const BOOL * present, const char ** value)
867 {
868 }
869
870 /* Return the anchor associated with this node
871 */
HText_nodeAnchor(HText * text)872 PUBLIC HTParentAnchor * HText_nodeAnchor (HText * text)
873 {
874 return text->node_anchor;
875 }
876
877 /* GridText specials
878 ** =================
879 */
880 /* Return the anchor with index N
881 **
882 ** The index corresponds to the number we print in the anchor.
883 */
HText_childNumber(HText * text,int number)884 PUBLIC HTChildAnchor * HText_childNumber (HText * text, int number)
885 {
886 TextAnchor * a;
887 for (a = text->first_anchor; a; a = a->next) {
888 if (a->number == number) return a->anchor;
889 }
890 return (HTChildAnchor *)0; /* Fail */
891 }
892
HText_setStale(HText * text)893 PUBLIC void HText_setStale (HText * text)
894 {
895 if (text)
896 text->stale = YES;
897 }
898
HText_refresh(HText * text)899 PUBLIC void HText_refresh (HText * text)
900 {
901 if (text && text->stale)
902 display_page(text, text->top_of_screen);
903 }
904
HText_sourceAnchors(HText * text)905 PUBLIC int HText_sourceAnchors (HText * text)
906 {
907 return (text ? text->last_anchor_number : -1);
908 }
909
HText_canScrollUp(HText * text)910 PUBLIC BOOL HText_canScrollUp (HText * text)
911 {
912 return (text && text->top_of_screen != 0);
913 }
914
HText_canScrollDown(HText * text)915 PUBLIC BOOL HText_canScrollDown (HText * text)
916 {
917 return (text && (text->top_of_screen + DISPLAY_LINES -
918 (text->title ? TITLE_LINES : 0)) < text->lines);
919 }
920
921 /* Scroll actions
922 */
HText_scrollTop(HText * text)923 PUBLIC void HText_scrollTop (HText * text)
924 {
925 display_page(text, 0);
926 }
927
HText_scrollDown(HText * text)928 PUBLIC void HText_scrollDown (HText * text)
929 {
930 display_page(text, text->top_of_screen + DISPLAY_LINES -1);
931 }
932
HText_scrollUp(HText * text)933 PUBLIC void HText_scrollUp (HText * text)
934 {
935 display_page(text, text->top_of_screen - DISPLAY_LINES +1);
936 }
937
HText_scrollBottom(HText * text)938 PUBLIC void HText_scrollBottom (HText * text)
939 {
940 display_page(text, text->lines - DISPLAY_LINES +1);
941 }
942
943
944 /* Browsing functions
945 ** ==================
946 */
947
948 /* Bring to front and highlight it
949 */
950
line_for_char(HText * text,int char_num)951 PRIVATE int line_for_char (HText * text, int char_num)
952 {
953 int line_number =0;
954 int characters = 0;
955 HTLine * line = text->last_line->next;
956 for(;;) {
957 if (line == text->last_line) return 0; /* Invalid */
958 characters = characters + line->size + 1;
959 if (characters > char_num) return line_number;
960 line_number ++;
961 line = line->next;
962 }
963 }
964
HText_select(HText * text)965 PUBLIC BOOL HText_select (HText * text)
966 {
967 if (text) {
968 HTMainText = text;
969 HTMainAnchor = text->node_anchor;
970 display_page(text, text->top_of_screen);
971 return YES;
972 }
973 HTTRACE(SGML_TRACE, "Rendering... Nothing to select!\n");
974 return NO;
975 }
976
HText_selectAnchor(HText * text,HTChildAnchor * anchor)977 PUBLIC BOOL HText_selectAnchor (HText * text, HTChildAnchor * anchor)
978 {
979 TextAnchor * a;
980
981 for(a=text->first_anchor; a; a=a->next) {
982 if (a->anchor == anchor) break;
983 }
984 if (!a) {
985 HTTRACE(SGML_TRACE, "Rendering... No such anchor in this text!\n");
986 return NO;
987 }
988
989 if (text != HTMainText) { /* Comment out by ??? */
990 HTMainText = text; /* Put back in by tbl 921208 */
991 HTMainAnchor = text->node_anchor;
992 }
993
994 {
995 int l = line_for_char(text, a->start);
996 HTTRACE(SGML_TRACE, "Rendering... Selecting anchor [%d] at char %d, line %d\n" _
997 a->number _ a->start _ l);
998
999 if ( !text->stale &&
1000 (l >= text->top_of_screen) &&
1001 ( l < text->top_of_screen + DISPLAY_LINES-1))
1002 return YES;
1003 display_page(text, l - (DISPLAY_LINES/3)); /* Scroll to it */
1004 }
1005 return YES;
1006 }
1007
1008
1009 /* Editing functions - NOT IMPLEMENTED
1010 ** =================
1011 **
1012 ** These are called from the application. There are many more functions
1013 ** not included here from the orginal text object.
1014 */
1015
1016 /* Style handling:
1017 */
1018 /* Apply this style to the selection
1019 */
HText_applyStyle(HText * me,HTStyle * style)1020 PUBLIC void HText_applyStyle (HText * me, HTStyle * style)
1021 {
1022
1023 }
1024
1025
1026 /* Update all text with changed style.
1027 */
HText_updateStyle(HText * me,HTStyle * style)1028 PUBLIC void HText_updateStyle (HText * me, HTStyle * style)
1029 {
1030
1031 }
1032
1033
1034 /* Return style of selection
1035 */
HText_selectionStyle(HText * me,HTStyleSheet * sheet)1036 PUBLIC HTStyle * HText_selectionStyle (
1037 HText * me,
1038 HTStyleSheet * sheet)
1039 {
1040 return 0;
1041 }
1042
1043
1044 /* Paste in styled text
1045 */
HText_replaceSel(HText * me,const char * aString,HTStyle * aStyle)1046 PUBLIC void HText_replaceSel (
1047 HText * me,
1048 const char * aString,
1049 HTStyle * aStyle)
1050 {
1051 }
1052
1053
1054 /* Apply this style to the selection and all similarly formatted text
1055 ** (style recovery only)
1056 */
HTextApplyToSimilar(HText * me,HTStyle * style)1057 PUBLIC void HTextApplyToSimilar (HText * me, HTStyle * style)
1058 {
1059
1060 }
1061
1062
1063 /* Select the first unstyled run.
1064 ** (style recovery only)
1065 */
HTextSelectUnstyled(HText * me,HTStyleSheet * sheet)1066 PUBLIC void HTextSelectUnstyled (HText * me, HTStyleSheet * sheet)
1067 {
1068
1069 }
1070
1071
1072 /* Anchor handling:
1073 */
HText_unlinkSelection(HText * me)1074 PUBLIC void HText_unlinkSelection (HText * me)
1075 {
1076
1077 }
1078
HText_referenceSelected(HText * me)1079 PUBLIC HTAnchor * HText_referenceSelected (HText * me)
1080 {
1081 return 0;
1082 }
1083
1084
1085 #ifdef CURSES
HText_getTopOfScreen(HText * text)1086 PUBLIC int HText_getTopOfScreen (HText * text)
1087 {
1088 return text->top_of_screen;
1089 }
1090
HText_getLines(HText * text)1091 PUBLIC int HText_getLines (HText * text)
1092 {
1093 return text->lines;
1094 }
1095 #endif
HText_linkSelTo(HText * me,HTAnchor * anchor)1096 PUBLIC HTAnchor * HText_linkSelTo (HText * me, HTAnchor * anchor)
1097 {
1098 return 0;
1099 }
1100
1101 /* HTML callback functions
1102 */
LMHText_beginElement(HText * text,int elem_num,const BOOL * present,const char ** value)1103 PUBLIC void LMHText_beginElement (HText * text,
1104 int elem_num, const BOOL * present, const char ** value)
1105 {
1106 return;
1107 }
1108
LMHText_endElement(HText * text,int elem_num)1109 PUBLIC void LMHText_endElement (HText * text, int elem_num)
1110 {
1111 switch (elem_num) {
1112 case HTML_A:
1113 LMHText_endAnchor (text);
1114 break;
1115 default:
1116 break;
1117 }
1118 return;
1119 }
1120