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