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