/**************************************************************************** * NCSA Mosaic for the X Window System * * Software Development Group * * National Center for Supercomputing Applications * * University of Illinois at Urbana-Champaign * * 605 E. Springfield, Champaign IL 61820 * * mosaic@ncsa.uiuc.edu * * * * Copyright (C) 1993, Board of Trustees of the University of Illinois * * * * NCSA Mosaic software, both binary and source (hereafter, Software) is * * copyrighted by The Board of Trustees of the University of Illinois * * (UI), and ownership remains with the UI. * * * * The UI grants you (hereafter, Licensee) a license to use the Software * * for academic, research and internal business purposes only, without a * * fee. Licensee may distribute the binary and source code (if released) * * to third parties provided that the copyright notice and this statement * * appears on all copies and that no charge is associated with such * * copies. * * * * Licensee may make derivative works. However, if Licensee distributes * * any derivative work based on or derived from the Software, then * * Licensee will (1) notify NCSA regarding its distribution of the * * derivative work, and (2) clearly notify users that such derivative * * work is a modified version and not the original NCSA Mosaic * * distributed by the UI. * * * * Any Licensee wishing to make commercial use of the Software should * * contact the UI, c/o NCSA, to negotiate an appropriate license for such * * commercial use. Commercial use includes (1) integration of all or * * part of the source code into a product for sale or license by or on * * behalf of Licensee to third parties, or (2) distribution of the binary * * code or source code to third parties that need it to utilize a * * commercial product sold or licensed by or on behalf of Licensee. * * * * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR * * ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED * * WARRANTY. THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE * * USERS OF THIS SOFTWARE. * * * * By using or copying this Software, Licensee agrees to abide by the * * copyright law and all other applicable laws of the U.S. including, but * * not limited to, export control laws, and the terms of this license. * * UI shall have the right to terminate this license immediately by * * written notice upon Licensee's breach of, or non-compliance with, any * * of its terms. Licensee may be held legally responsible for any * * copyright infringement that is caused or encouraged by Licensee's * * failure to abide by the terms of this license. * * * * Comments and questions are welcome and can be sent to * * mosaic-x@ncsa.uiuc.edu. * ****************************************************************************/ #include "config.h" #ifndef VMS #include struct timeval Tv; struct timezone Tz; #else #include #endif #include #include #include #include "HTMLP.h" /* * I need my own is ispunct function because I need a closing paren * immediately after a word to act like punctuation. */ #define MY_ISPUNCT(val) (ispunct((int)(val)) || ((val) == ')')) #define INDENT_SPACES 2 /* SWP -- Now using eptr->bwidth -- 02/08/96 #define IMAGE_BORDER 2 */ #define IMAGE_DEFAULT_BORDER 2 #define D_NONE 0 #define D_TITLE 1 #define D_TEXT 2 #define D_OLIST 3 #define D_ULIST 4 #ifndef DISABLE_TRACE extern int htmlwTrace; #endif extern struct ele_rec *AddEle(); extern void FreeLineList(); extern void FreeObjList(); extern int SwapElements(); extern struct ele_rec **MakeLineList(); extern char *ParseMarkTag(); extern char *MaxTextWidth(); extern char *IsMapForm(); extern char *DelayedHRef(); extern int IsDelayedHRef(); extern int AnchoredHeight(); extern struct mark_up *HTMLParse(); extern struct ref_rec *FindHRef(); extern struct delay_rec *FindDelayedImage(); extern ImageInfo *NoImageData(); extern ImageInfo *DelayedImageData(); extern Pixmap NoImage(); extern Pixmap DelayedImage(); extern Pixmap InfoToImage(); extern int caseless_equal(); extern void clean_white_space(); extern void WidgetRefresh(); extern WidgetInfo *MakeWidget(); extern XFontStruct *GetWidgetFont(); extern void AddNewForm(); extern void PrepareFormEnd(); extern char *ComposeCommaList(); extern void FreeCommaList(); extern TableInfo *MakeTable(); extern void TableRefresh(); /* for selective image loading */ extern Boolean currently_delaying_images; /*******************************/ /*SWP -- 8/14/95*/ int tableSupportEnabled; /* * To allow arbitrary nesting of lists */ typedef struct dtype_rec { int type; /* D_NONE, D_TITLE, D_TEXT, D_OLIST, D_ULIST */ int count; int compact; struct dtype_rec *next; } DescRec; /* * To allow arbitrary nesting of font changes */ typedef struct font_rec { XFontStruct *font; struct font_rec *next; } FontRec; static DescRec BaseDesc; static DescRec *DescType; static DescRec *ListData; static FontRec FontBase; static FontRec *FontStack; static XFontStruct *currentFont; static XFontStruct *saveFont; static unsigned long Fg; static unsigned long Bg; static int Width; static int MaxWidth; static int ElementId; static int WidgetId; static int LineNumber; static int LineHeight; static int LineBottom; static int BaseLine; static int TextIndent; static int MarginW; static int Ignore; static int Preformat; static int PF_LF_State; /* Pre-formatted linefeed state. Hack for bad HTMLs */ static int NeedSpace; static Boolean Internal; static Boolean DashedUnderlines; static Boolean Strikeout; static int Underlines; static int CharsInLine; static int IndentLevel; static struct ele_rec *Current; static char *AnchorText; static char *TitleText; static char *TextAreaBuf; static struct mark_up *Last; static FormInfo *CurrentForm; static MapInfo *CurrentMap=NULL; /* csi stuff -- swp */ static SelectInfo *CurrentSelect; static int Superscript; /* amb */ static int Subscript; static XFontStruct *nonScriptFont; static int InDocHead; static int InUnderlined; static int Centered; // SAM /* * Turned out we were taking WAY too much time mallocing and freeing * memory when composing the lines into elements. So this ineligent * method minimizes all that. */ #define COMP_LINE_BUF_LEN 1024 static char *CompLine = NULL; static int CompLineLen = 0; static char *CompWord = NULL; static int CompWordLen = 0; /* * Create a formatted element */ struct ele_rec * CreateElement(hw, type, fp, x, y, edata, w, h, bw) HTMLWidget hw; int type; XFontStruct *fp; int x, y; char *edata, *w, *h; int bw; { struct ele_rec *eptr; int baseline; if (fp != NULL) { baseline = fp->max_bounds.ascent; } else { baseline = LineHeight; } eptr = (struct ele_rec *)malloc(sizeof(struct ele_rec)); if (eptr == NULL) { fprintf(stderr, "Cannot allocate space for element buffer\n"); exit(1); } memset(eptr, 0, sizeof(struct ele_rec)); eptr->type = type; eptr->pic_data = NULL; eptr->widget_data = NULL; eptr->table_data = NULL; eptr->font = fp; eptr->alignment = ALIGN_BOTTOM; eptr->selected = False; eptr->internal = Internal; eptr->strikeout = Strikeout; eptr->bwidth = bw; eptr->x = x; eptr->y = y; eptr->y_offset = 0; eptr->width = 0; eptr->line_number = LineNumber; eptr->line_height = LineHeight; eptr->fg = Fg; eptr->bg = Bg; eptr->underline_number = Underlines; eptr->dashed_underline = DashedUnderlines; eptr->indent_level = IndentLevel; if(Centered && type != E_TABLE) eptr->alignment = ALIGN_CENTER; switch(type) { case E_TEXT: /* * get a unique element id */ ElementId++; eptr->ele_id = ElementId; eptr->y_offset = 0; eptr->edata_len = strlen(edata) + 1; eptr->edata = (char *)malloc(eptr->edata_len); if (eptr->edata == NULL) { eptr->edata_len = 0; fprintf(stderr, "Cannot allocate space for copy of text element data\n"); exit(1); } strcpy(eptr->edata, edata); /* * if this is an anchor, puts its href value into * the element. */ if (AnchorText != NULL) { eptr->anchorHRef = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } break; case E_BULLET: eptr->ele_id = ElementId; if (BaseLine == -100) { BaseLine = baseline; if (LineBottom == 0) { LineBottom = LineHeight - baseline; } else { /* * It is possible (with the first item * in a line being a top aligned image) * for LineBottom to have already been * set. It now needs to be * corrected as we set a real * BaseLine */ if ((LineHeight - baseline) > (LineBottom - baseline)) { LineBottom = LineHeight - baseline; } else { LineBottom = LineBottom - baseline; } } } else if (baseline < BaseLine) { eptr->y_offset = BaseLine - baseline; } /* * Bullets can't be underlined! */ eptr->underline_number = 0; eptr->edata = NULL; eptr->edata_len = 0; eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; break; case E_HRULE: /* * get a unique element id */ ElementId++; eptr->ele_id = ElementId; if (BaseLine == -100) { BaseLine = baseline; if (LineBottom == 0) { LineBottom = LineHeight - baseline; } else { /* * It is possible (with the first item * in a line being a top aligned image) * for LineBottom to have already been * set. It now needs to be * corrected as we set a real * BaseLine */ if ((LineHeight - baseline) > (LineBottom - baseline)) { LineBottom = LineHeight - baseline; } else { LineBottom = LineBottom - baseline; } } } else if (baseline < BaseLine) { eptr->y_offset = BaseLine - baseline; } /* * Rules can't be underlined! */ eptr->underline_number = 0; eptr->edata = NULL; eptr->edata_len = 0; eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; break; case E_LINEFEED: eptr->ele_id = ElementId; eptr->y_offset = 0; if (BaseLine == -100) { BaseLine = baseline; if (LineBottom == 0) { LineBottom = LineHeight - baseline; } else { /* * It is possible (with the first item * in a line being a top aligned image) * for LineBottom to have already been * set. It now needs to be * corrected as we set a real * BaseLine */ if ((LineHeight - baseline) > (LineBottom - baseline)) { LineBottom = LineHeight - baseline; } else { LineBottom = LineBottom - baseline; } } } /* * Linefeeds have to use the maximum line height. * Deal with bad Lucidia descents. */ #ifdef NO_EXTRA_FILLS eptr->line_height = eptr->font->ascent + eptr->font->descent; #else eptr->line_height = LineHeight; #endif /* NO_EXTRA_FILLS */ if ((BaseLine + LineBottom) > eptr->line_height) { eptr->line_height = BaseLine + LineBottom; } /* * Linefeeds can't be underlined! */ eptr->underline_number = 0; eptr->edata = NULL; eptr->edata_len = 0; /* * if this linefeed is part of a broken anchor put * its href value into the element so we can reconnect * it when activated. * If it at the beginning of an anchor, don't put * the href in, and change the color back. */ if (AnchorText != NULL) { char *tptr; tptr = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); if ((Current != NULL)&& ((Current->anchorHRef == NULL)|| (tptr == NULL)|| (strcmp(Current->anchorHRef, tptr) != 0))) { if (tptr) free(tptr); eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; /* * Without motif we use our own foreground resource instead of * using the manager's */ #ifdef MOTIF eptr->fg = hw->manager.foreground; #else eptr->fg = hw->html.foreground; #endif /* MOTIF */ } else { eptr->anchorHRef = tptr; eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } break; case E_IMAGE: /* * get a unique element id */ ElementId++; eptr->ele_id = ElementId; /* * Images can't be underlined! */ eptr->underline_number = 0; if (edata != NULL) { eptr->edata_len = strlen(edata) + 1; eptr->edata = (char *)malloc(eptr->edata_len); if (eptr->edata == NULL) { eptr->edata_len = 0; fprintf(stderr, "Cannot allocate space for copy of image element data\n"); exit(1); } strcpy(eptr->edata, edata); } else { eptr->edata_len = 0; eptr->edata = NULL; } /* * if this image is part of an anchor put * its href and name values into the element * so we can reconnect it when activated. */ if (AnchorText != NULL) { eptr->anchorHRef = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } /* * Picture stuff */ /* * if we have an image resolver, use it. */ if (hw->html.resolveImage != NULL) { int internal; /* * See if this is a special internal image */ if ((edata != NULL)&& (strncmp(edata, INTERNAL_IMAGE, strlen(INTERNAL_IMAGE)) == 0)) { internal = 1; } else { internal = 0; } /* * if we delay image fetching * internal images are not delayed. */ if (((hw->html.delay_images == True) && (!internal)) || currently_delaying_images == 1 ) { /* * see if already cached. */ eptr->pic_data = (*(resolveImageProc) (hw->html.resolveImage))(hw, edata, 1, w, h); if (eptr->pic_data != NULL) { eptr->pic_data->delayed = 0; /* * Mark images we have sucessfully * loaded at least once */ if (eptr->pic_data->image_data != NULL) { eptr->pic_data->fetched = 1; } } /* * else, not cached. */ else { /* * just image */ if (eptr->anchorHRef == NULL) { eptr->pic_data = DelayedImageData(hw, False); eptr->pic_data->delayed = 1; eptr->anchorHRef = DelayedHRef(hw); eptr->fg = hw->html.anchor_fg; } /* * else anchor and image */ else { eptr->pic_data = DelayedImageData(hw, True); eptr->pic_data->delayed = 1; } } } else { eptr->pic_data = (*(resolveImageProc) (hw->html.resolveImage))(hw, edata, 0, w, h); if (eptr->pic_data != NULL) { eptr->pic_data->delayed = 0; /* * Mark images we have sucessfully * loaded at least once */ if (eptr->pic_data->image_data != NULL) { eptr->pic_data->fetched = 1; } } } if (eptr->pic_data != NULL) { eptr->pic_data->internal = internal; } } if (eptr->pic_data == NULL) { eptr->pic_data = NoImageData(hw); eptr->pic_data->delayed = 0; eptr->pic_data->internal = 0; } break; case E_TABLE: ElementId++; eptr->ele_id = ElementId; /* * Table's can't be underlined */ eptr->underline_number = 0; /* if (eptr->edata != NULL) { free((char *)eptr->edata); } */ eptr->edata = NULL; eptr->edata_len = 0; /* * if this table is part of an anchor put * its href and name values into the element * so we can reconnect it when activated. */ /* if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } */ if (AnchorText != NULL) { eptr->anchorHRef = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject= ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } eptr->table_data = MakeTable (hw, edata, (x + IMAGE_DEFAULT_BORDER), (y + IMAGE_DEFAULT_BORDER)); break; case E_WIDGET: /* * get a unique element id */ WidgetId++; ElementId++; eptr->ele_id = ElementId; /* * Widgets can't be underlined! */ eptr->underline_number = 0; eptr->edata = NULL; eptr->edata_len = 0; /* * if this widget is part of an anchor put * its href and name values into the element * so we can reconnect it when activated. */ if (AnchorText != NULL) { eptr->anchorHRef = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } /* * Widget stuff */ eptr->widget_data = MakeWidget(hw, edata, (x + IMAGE_DEFAULT_BORDER), (y + IMAGE_DEFAULT_BORDER), WidgetId, CurrentForm); /* * I have no idea what to do if we can't create the * widget. It probably means we are so messed up we * will soon be crashing. */ if (eptr->widget_data == NULL) { } break; default: #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr, "CreateElement: Unknown type %d\n", type); } #endif eptr->ele_id = ElementId; eptr->edata = NULL; eptr->edata_len = 0; eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; break; } return(eptr); } /* CreateElement() */ /* * Set the formatted element into the format list. Use a pre-allocated * list position if possible, otherwise allocate a new list position. */ void SetElement(hw, type, fp, x, y, edata, w, h, bw) HTMLWidget hw; int type; XFontStruct *fp; int x, y; char *edata; char *w, *h; int bw; { struct ele_rec *eptr; int len; int baseline; if (fp != NULL) { baseline = fp->max_bounds.ascent; } else { baseline = LineHeight; } /* * There is not pre-allocated format list, or we have reached * the end of the pre-allocated list. Create a new element, and * add it. */ if ((hw->html.formatted_elements == NULL)|| ((Current != NULL)&&(Current->next == NULL))) { eptr = CreateElement(hw, type, fp, x, y, edata, w, h, bw); Current = AddEle(&(hw->html.formatted_elements), Current, eptr); return; } /* * If current is null, but we have a pre-allocated format list, then * this is the first SetElement() call for this formated text, and * we must set current to the head of the formatted list. Otherwise * we move current to the next pre-allocated list position. */ if (Current == NULL) { Current = hw->html.formatted_elements; } else { Current = Current->next; } eptr = Current; if (eptr == NULL) { fprintf(stderr, "SetElement: Error, setting a null element\n"); exit(1); } eptr->type = type; eptr->pic_data = NULL; eptr->widget_data = NULL; eptr->table_data = NULL; eptr->font = fp; eptr->alignment = ALIGN_BOTTOM; eptr->selected = False; eptr->internal = Internal; eptr->strikeout = Strikeout; eptr->bwidth = bw; eptr->x = x; eptr->y = y; eptr->y_offset = 0; eptr->width = 0; eptr->line_number = LineNumber; eptr->line_height = LineHeight; eptr->fg = Fg; eptr->bg = Bg; eptr->underline_number = Underlines; eptr->dashed_underline = DashedUnderlines; eptr->indent_level = IndentLevel; // SAM if(Centered && type != E_TABLE) eptr->alignment = ALIGN_CENTER; // SAM switch(type) { case E_TEXT: /* * get a unique element id */ ElementId++; eptr->ele_id = ElementId; eptr->y_offset = 0; len = strlen(edata) + 1; if (len > eptr->edata_len) { if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = (char *)malloc(len); if (eptr->edata == NULL) { eptr->edata_len = 0; fprintf(stderr, "Cannot allocate space for copy of text element data\n"); exit(1); } } eptr->edata_len = len; strcpy(eptr->edata, edata); /* * if this is an anchor, puts its href and name * values into the element. */ if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } if (AnchorText != NULL) { eptr->anchorHRef = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject= ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } break; case E_BULLET: eptr->ele_id = ElementId; if (BaseLine == -100) { BaseLine = baseline; if (LineBottom == 0) { LineBottom = LineHeight - baseline; } else { /* * It is possible (with the first item * in a line being a top aligned image) * for LineBottom to have already been * set. It now needs to be * corrected as we set a real * BaseLine */ if ((LineHeight - baseline) > (LineBottom - baseline)) { LineBottom = LineHeight - baseline; } else { LineBottom = LineBottom - baseline; } } } else if (baseline < BaseLine) { eptr->y_offset = BaseLine - baseline; } /* * Bullets can't be underlined! */ eptr->underline_number = 0; if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = NULL; eptr->edata_len = 0; if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; break; case E_HRULE: /* * get a unique element id */ ElementId++; eptr->ele_id = ElementId; if (BaseLine == -100) { BaseLine = baseline; if (LineBottom == 0) { LineBottom = LineHeight - baseline; } else { /* * It is possible (with the first item * in a line being a top aligned image) * for LineBottom to have already been * set. It now needs to be * corrected as we set a real * BaseLine */ if ((LineHeight - baseline) > (LineBottom - baseline)) { LineBottom = LineHeight - baseline; } else { LineBottom = LineBottom - baseline; } } } else if (baseline < BaseLine) { eptr->y_offset = BaseLine - baseline; } /* * Rules can't be underlined! */ eptr->underline_number = 0; if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = NULL; eptr->edata_len = 0; if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; break; case E_LINEFEED: eptr->ele_id = ElementId; eptr->y_offset = 0; if (BaseLine == -100) { BaseLine = baseline; if (LineBottom == 0) { LineBottom = LineHeight - baseline; } else { /* * It is possible (with the first item * in a line being a top aligned image) * for LineBottom to have already been * set. It now needs to be * corrected as we set a real * BaseLine */ if ((LineHeight - baseline) > (LineBottom - baseline)) { LineBottom = LineHeight - baseline; } else { LineBottom = LineBottom - baseline; } } } /* * Linefeeds have to use the maximum line height. * Deal with bad Lucidia descents. */ #ifdef NO_EXTRA_FILLS eptr->line_height = eptr->font->ascent + eptr->font->descent; #else eptr->line_height = LineHeight; #endif /* NO_EXTRA_FILLS */ if ((BaseLine + LineBottom) > eptr->line_height) { eptr->line_height = (BaseLine + LineBottom); } /* * Linefeeds can't be underlined! */ eptr->underline_number = 0; if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = NULL; eptr->edata_len = 0; /* * if this linefeed is part of a broken anchor put * its href and name values into the element * so we can reconnect it when activated. * If it at the beginning of an anchor, don't put * the href in and change the color back. */ if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } if (AnchorText != NULL) { char *tptr; tptr = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); if ((eptr->prev != NULL)&& ((eptr->prev->anchorHRef == NULL)|| (tptr == NULL)|| (strcmp(eptr->prev->anchorHRef, tptr) != 0))) { if (tptr) free(tptr); eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; /* * Without motif we use our own foreground resource instead of * using the manager's */ #ifdef MOTIF eptr->fg = hw->manager.foreground; #else eptr->fg = hw->html.foreground; #endif /* MOTIF */ } else { eptr->anchorHRef = tptr; eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } break; case E_IMAGE: /* * get a unique element id */ ElementId++; eptr->ele_id = ElementId; /* * Images can't be underlined! */ eptr->underline_number = 0; if (edata != NULL) { len = strlen(edata) + 1; if (len > eptr->edata_len) { if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = (char *)malloc(len); if (eptr->edata == NULL) { eptr->edata_len = 0; fprintf(stderr, "Cannot allocate space for copy of text element data\n"); exit(1); } } eptr->edata_len = len; strcpy(eptr->edata, edata); } else { eptr->edata_len = 0; if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = NULL; } /* * if this image is part of an anchor put * its href and name values into the element * so we can reconnect it when activated. */ if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } if (AnchorText != NULL) { eptr->anchorHRef = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } /* * Picture stuff */ /* * if we have an image resolver, use it. */ if (hw->html.resolveImage != NULL) { int internal; /* * See if this is a special internal image */ if ((edata != NULL)&& (strncmp(edata, INTERNAL_IMAGE, strlen(INTERNAL_IMAGE)) == 0)) { internal = 1; } else { internal = 0; } /* * if we delay image fetching * internal images are not delayed. */ if (((hw->html.delay_images == True) && (!internal)) || currently_delaying_images == 1 ) { /* * see if already cached. */ eptr->pic_data = (*(resolveImageProc) (hw->html.resolveImage))(hw, edata, 1, w, h); if (eptr->pic_data != NULL) { eptr->pic_data->delayed = 0; /* * Mark images we have sucessfully * loaded at least once */ if (eptr->pic_data->image_data != NULL) { eptr->pic_data->fetched = 1; } } /* * else, not cached. */ else { /* * just image */ if (eptr->anchorHRef == NULL) { eptr->pic_data = DelayedImageData(hw, False); eptr->pic_data->delayed = 1; eptr->anchorHRef = DelayedHRef(hw); eptr->fg = hw->html.anchor_fg; } /* * else anchor and image */ else { eptr->pic_data = DelayedImageData(hw, True); eptr->pic_data->delayed = 1; } } } else { eptr->pic_data = (*(resolveImageProc) (hw->html.resolveImage))(hw, edata, 0, w, h); if (eptr->pic_data != NULL) { eptr->pic_data->delayed = 0; /* * Mark images we have sucessfully * loaded at least once */ if (eptr->pic_data->image_data != NULL) { eptr->pic_data->fetched = 1; } } } if (eptr->pic_data != NULL) { eptr->pic_data->internal = internal; } } if (eptr->pic_data == NULL) { eptr->pic_data = NoImageData(hw); eptr->pic_data->delayed = 0; eptr->pic_data->internal = 0; } break; case E_TABLE: ElementId++; eptr->ele_id = ElementId; /* * Table's can't be underlined */ eptr->underline_number = 0; if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = NULL; eptr->edata_len = 0; /* * if this table is part of an anchor put * its href and name values into the element * so we can reconnect it when activated. */ if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } if (AnchorText != NULL) { eptr->anchorHRef = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject= ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; } eptr->table_data = MakeTable (hw, edata, (x + IMAGE_DEFAULT_BORDER), (y + IMAGE_DEFAULT_BORDER)); break; case E_WIDGET: /* * get a unique element id */ WidgetId++; ElementId++; eptr->ele_id = ElementId; /* * Widgets can't be underlined! */ eptr->underline_number = 0; if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = NULL; eptr->edata_len = 0; /* * if this widget is part of an anchor put * its href and name values into the element * so we can reconnect it when activated. */ if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } if (AnchorText != NULL) { eptr->anchorHRef = ParseMarkTag(AnchorText, MT_ANCHOR, AT_HREF); eptr->anchorName = ParseMarkTag(AnchorText, MT_ANCHOR, AT_NAME); eptr->anchorSubject= ParseMarkTag(AnchorText, MT_ANCHOR, AT_SUBJECT); if (!eptr->anchorSubject) { eptr->anchorSubject = ParseMarkTag(AnchorText, MT_ANCHOR, AT_TITLE); } } else { eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject= NULL; } /* * Widget stuff */ eptr->widget_data = MakeWidget(hw, edata, (x + IMAGE_DEFAULT_BORDER), (y + IMAGE_DEFAULT_BORDER), WidgetId, CurrentForm); /* * I have no idea what to do if we can't create the * widget. It probably means we are so messed up we * will soon be crashing. */ if (eptr->widget_data == NULL) { } break; default: #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr, "SetElement: Unknown type %d\n", type); } #endif eptr->ele_id = ElementId; if (eptr->edata != NULL) { free((char *)eptr->edata); } eptr->edata = NULL; eptr->edata_len = 0; if (eptr->anchorHRef != NULL) { free((char *)eptr->anchorHRef); } if (eptr->anchorName != NULL) { free((char *)eptr->anchorName); } if (eptr->anchorSubject != NULL) { free((char *)eptr->anchorSubject); } eptr->anchorHRef = NULL; eptr->anchorName = NULL; eptr->anchorSubject = NULL; break; } } /* SetElement() */ /* * Change our drawing font */ void NewFont(fp) XFontStruct *fp; { /* * Deal with bad Lucidia descents. */ if (fp->descent > fp->max_bounds.descent) { LineHeight = fp->max_bounds.ascent + fp->descent; } else { LineHeight = fp->max_bounds.ascent + fp->max_bounds.descent; } } /* * Place a linefeed at the end of a line. * Create and add the element record for it. */ void LinefeedPlace(hw, x, y) HTMLWidget hw; int *x, *y; { // SAM #if 0 if(Centered) { struct ele_rec *eptr; int width; printf("LineFeedPlace\n"); for(eptr = Current; eptr; eptr = eptr->next) { printf("type %d x %d y %d width %d bwidth %d alignment %d start %d end %d\n", eptr->type, eptr->x, eptr->y, eptr->width, eptr->bwidth, eptr->alignment, eptr->start_pos, eptr->end_pos); printf("\t'%.40s'\n", eptr->edata); // SAM Simple and wrong if(eptr->width) { // SAM How to get canvas width? if(eptr->width >= 640) printf("TOO WIDE\n"); else { int offset = (640 - eptr->width) / 2; eptr->x += offset; } } } } #endif // SAM /* * At the end of every line check if we have a new MaxWidth */ if ((*x + hw->html.margin_width) > MaxWidth) { MaxWidth = *x + hw->html.margin_width; } SetElement(hw, E_LINEFEED, currentFont, *x, *y, (char *)NULL,NULL,NULL,IMAGE_DEFAULT_BORDER); } /* * We have encountered a line break. Incrment the line counter, * and move down some space. */ void LineFeed(hw, x, y) HTMLWidget hw; int *x, *y; { /* * Manipulate linefeed state for special pre-formatted linefeed * hack for broken HTMLs */ if (Preformat) { switch(PF_LF_State) { /* * First soft linefeed */ case 0: PF_LF_State = 1; break; /* * Collapse multiple soft linefeeds within a pre */ case 1: return; break; /* * Ignore soft linefeeds after hard linefeeds * within a pre */ case 2: return; break; default: PF_LF_State = 1; break; } } /* * No blank lines allowed at the start of a document. */ else if (ElementId == 0) { return; } /* * For formatted documents there are 3 linefeed states. * 0 = in the middle of a line. * 1 = at left margin * 2 = at left margin with blank line above */ else { PF_LF_State++; if (PF_LF_State > 2) { PF_LF_State = 2; } } /* * sanity check to set some line height if none was specified. */ if (BaseLine <= 0) { BaseLine = LineHeight; } LinefeedPlace(hw, x, y); CharsInLine = 0; *x = TextIndent; *y = *y + BaseLine + LineBottom; LineBottom = 0; BaseLine = -100; NeedSpace = 0; LineNumber++; } /* * We want to make sure that future text starts at the left margin. * But if we are already there, don't put in a new line. */ void ConditionalLineFeed(hw, x, y, state) HTMLWidget hw; int *x, *y; int state; { if (PF_LF_State < state) { /* * If this funtion is being used to insert a blank line, * we need to look at the percentVerticalSpace resource * to see how high to make the line. */ if ((state == 2)&&(hw->html.percent_vert_space > 0)) { int l_height; l_height = LineHeight; LineHeight = LineHeight * hw->html.percent_vert_space / 100; LineFeed(hw, x, y); LineHeight = l_height; } else { LineFeed(hw, x, y); } } } /* * hack to make broken HTMLs within pre-formatted text have nice * looking linefeeds. */ void HardLineFeed(hw, x, y) HTMLWidget hw; int *x, *y; { /* * Manipulate linefeed state for special pre-formatted linefeed * hack for broken HTMLs */ if (Preformat) { switch(PF_LF_State) { /* * First hard linefeed */ case 0: PF_LF_State = 2; break; /* * Previous soft linefeed should have been ignored, so * ignore this hard linefeed, but set state like it * was not ignored. */ case 1: PF_LF_State = 2; return; break; /* * Honor multiple hard linefeeds. */ case 2: break; default: PF_LF_State = 2; break; } } /* * sanity check to set some line height if none was specified. */ if (BaseLine <= 0) { BaseLine = LineHeight; } LinefeedPlace(hw, x, y); CharsInLine = 0; *x = TextIndent; *y = *y + BaseLine + LineBottom; LineBottom = 0; BaseLine = -100; NeedSpace = 0; LineNumber++; } static void AdjustBaseLine() { int baseline; int supsubBaseline; baseline = Current->font->max_bounds.ascent; if ((Superscript>0) || (Subscript>0)) { supsubBaseline = nonScriptFont->max_bounds.ascent; baseline += ((supsubBaseline * .4) * Superscript); baseline -= ((supsubBaseline * .4) * Subscript); baseline += 2; } if (BaseLine == -100) { BaseLine = baseline; Current->y_offset = 0; if (LineBottom == 0) { LineBottom = LineHeight - baseline; } else { /* * It is possible (with the first item * in a line being a top aligned image) * for LineBottom to have already been * set. It now needs to be * corrected as we set a real * BaseLine */ if ((LineHeight - baseline) > (LineBottom - baseline)) { LineBottom = LineHeight - baseline; } else { LineBottom = LineBottom - baseline; } } } else if (baseline <= BaseLine) { if (baseline < BaseLine) { Current->y_offset = BaseLine - baseline; } else { Current->y_offset = 0; } if ((LineHeight - baseline) > LineBottom) { LineBottom = LineHeight - baseline; } } else { struct ele_rec *eptr; int line, incy; incy = baseline - BaseLine; BaseLine = baseline; /* * Go back over this line * and move everything down * a little. */ eptr = Current; line = eptr->line_number; while ((eptr->prev != NULL)&& (eptr->prev->line_number == line)) { eptr = eptr->prev; eptr->y_offset = eptr->y_offset + incy; } if ((LineHeight - baseline) > LineBottom) { LineBottom = LineHeight - baseline; } } } /* * Place the bullet at the beginning of an unnumbered * list item. Create and add the element record for it. */ void BulletPlace(hw, x, y) HTMLWidget hw; int *x, *y; { int width, l_height; /* * Save the font's line height, and set your own for this * element. Restore the fonts height when done. * Deal with bad Lucidia descents. */ l_height = LineHeight; if (hw->html.font->descent > hw->html.font->max_bounds.descent) { LineHeight = hw->html.font->max_bounds.ascent + hw->html.font->descent; } else { LineHeight = hw->html.font->max_bounds.ascent + hw->html.font->max_bounds.descent; } NeedSpace = 0; width = hw->html.font->max_bounds.width; SetElement(hw, E_BULLET, hw->html.font, *x, *y, (char *)NULL, NULL, NULL,IMAGE_DEFAULT_BORDER); LineHeight = l_height; /* * This should reall be here, but it is a hack for headers on list * elements to work if we leave it out PF_LF_State = 0; */ } /* * Place a horizontal rule across the page. * Create and add the element record for it. */ void HRulePlace(hw, x, y, width) HTMLWidget hw; int *x, *y; unsigned int width; { NeedSpace = 0; *x = hw->html.margin_width; SetElement(hw, E_HRULE, currentFont, *x, *y, (char *)NULL, NULL, NULL, IMAGE_DEFAULT_BORDER); *x = *x + width - (2 * hw->html.margin_width); NeedSpace = 1; PF_LF_State = 0; } /* * Place the number at the beginning of an numbered * list item. Create and add the element record for it. */ void ListNumberPlace(hw, x, y, val) HTMLWidget hw; int *x, *y; int val; { int width, my_x; int dir, ascent, descent; XCharStruct all; char buf[20]; sprintf(buf, "%d.", val); width = hw->html.font->max_bounds.lbearing + hw->html.font->max_bounds.rbearing; XTextExtents(currentFont, buf, strlen(buf), &dir, &ascent, &descent, &all); my_x = *x - (width / 2) - all.width; /* * Add a space after thenumber here so it will look right when * cut and pasted from a selection. */ width = strlen(buf); buf[width] = ' '; buf[width + 1] = '\0'; SetElement(hw, E_TEXT, currentFont, my_x, *y, buf, NULL, NULL, IMAGE_DEFAULT_BORDER); AdjustBaseLine(); CharsInLine = CharsInLine + strlen(buf); NeedSpace = 0; /* * This should reall be here, but it is a hack for headers on list * elements to work if we leave it out PF_LF_State = 0; */ } /* * Place a piece of pre-formatted text. Add an element record for it. */ void PreformatPlace(hw, mptr, x, y, width) HTMLWidget hw; struct mark_up *mptr; int *x, *y; unsigned int width; { char *text; char *start; char *end; char *ptr; char tchar; int tab_count, char_cnt; int dir, ascent, descent; XCharStruct all; char *line; int line_x; text = mptr->text; line_x = *x; line = CompLine; if (line != NULL) { line[0] = '\0'; } end = text; while (*end != '\0') { tab_count = 0; char_cnt = CharsInLine; /* * make start and end point to one word. A word is either * a lone linefeed, or all whitespace before a word, plus * the text of the word itself. */ start = end; /* * Throw out carriage returns and form-feeds */ if ((*end == '\r')||(*end == '\f')) { start++; end++; } else if (*end == '\n') { end++; char_cnt++; } else { /* * Should be only spaces and tabs here, so if it * is not a tab, make it a space. * Break on linefeeds, they must be done separately */ while (((int)((unsigned char)*end) < 128)&& (isspace(*end))) { if (*end == '\n') { break; } else if (*end == '\t') { tab_count++; char_cnt = ((char_cnt / 8) + 1) * 8; } else { *end = ' '; char_cnt++; } end++; } while (((int)((unsigned char)*end) > 127)|| ((!isspace(*end))&&(*end != '\0'))) { end++; char_cnt++; } } /* * Add the word to the end of this line, or insert * a linefeed if the word is a lone linefeed. * tabs expand to 8 spaces. */ if (start != end) { int tlen; tchar = *end; *end = '\0'; tlen = char_cnt + 1; if (tlen > CompWordLen) { CompWordLen += COMP_LINE_BUF_LEN; if (tlen > CompWordLen) { CompWordLen = tlen; } if (CompWord != NULL) { free(CompWord); } CompWord = (char *)malloc(CompWordLen); } ptr = CompWord; /* * If we have any tabs, expand them into spaces. */ if (tab_count) { char *p1, *p2; int i, new; char_cnt = CharsInLine; p1 = ptr; p2 = start; while (*p2 != '\0') { if (*p2 == '\t') { new = ((char_cnt / 8) + 1) * 8; for (i=0; i<(new-char_cnt); i++) { *p1++ = ' '; } p2++; char_cnt = new; } else { *p1++ = *p2++; char_cnt++; } } *p1 = '\0'; } else { strcpy(ptr, start); } #ifdef ASSUME_FIXED_WIDTH_PRE all.width = currentFont->max_bounds.width * strlen(ptr); #else XTextExtents(currentFont, ptr, strlen(ptr), &dir, &ascent, &descent, &all); #endif /* ASSUME_FIXED_WIDTH_PRE */ if (*start == '\n') { if ((line != NULL)&&(line[0] != '\0')) { SetElement(hw, E_TEXT, currentFont, line_x, *y, line, NULL, NULL, IMAGE_DEFAULT_BORDER); /* * Save width here to avoid an * XTextExtents call later. */ Current->width = *x - line_x + 1; AdjustBaseLine(); PF_LF_State = 0; line[0] = '\0'; } HardLineFeed(hw, x, y); line_x = *x; NeedSpace = 0; } else { char *tptr; int tlen; if (line == NULL) { tlen = strlen(ptr) + 1; } else { tlen = strlen(line) + strlen(ptr) + 1; } if (tlen > CompLineLen) { CompLineLen += COMP_LINE_BUF_LEN; if (tlen > CompLineLen) { CompLineLen = tlen; } tptr = (char *)malloc(CompLineLen); if (CompLine != NULL) { strcpy(tptr, CompLine); free(CompLine); } else { tptr[0] = '\0'; } CompLine = tptr; } line = CompLine; strcat(line, ptr); *x = *x + all.width; CharsInLine = CharsInLine + strlen(ptr); NeedSpace = 1; } *end = tchar; } } if ((line != NULL)&&(line[0] != '\0')) { SetElement(hw, E_TEXT, currentFont, line_x, *y, line, NULL, NULL, IMAGE_DEFAULT_BORDER); /* * Save width here to avoid an * XTextExtents call later. */ Current->width = *x - line_x + 1; AdjustBaseLine(); PF_LF_State = 0; line[0] = '\0'; } } /* * Format and place a piece of text. Add an element record for it. */ void FormatPlace(hw, mptr, x, y, width) HTMLWidget hw; struct mark_up *mptr; int *x, *y; unsigned int width; { char *text; char *start; char *end; char *ptr; char tchar; #ifdef DOUBLE_SPACE_AFTER_PUNCT char tchar2; #endif /* DOUBLE_SPACE_AFTER_PUNCT */ int stripped_space; int added_space; int double_space; int dir, ascent, descent; XCharStruct all; char *line; int line_x; text = mptr->text; line_x = *x; line = CompLine; if (line != NULL) { line[0] = '\0'; } end = text; while (*end != '\0') { /* * make start and end point to one word. * set flag if we removed any leading white space. * set flag if we add any leading white space. */ stripped_space = 0; added_space = 0; start = end; while (((int)((unsigned char)*start) < 128)&&(isspace(*start))) { stripped_space = 1; start++; } end = start; while (((int)((unsigned char)*end) > 127)|| ((!isspace(*end))&&(*end != '\0'))) { end++; } /* * Add the word to the end of this line, or insert * a linefeed an put the word at the start of the next line. */ if (start != end) { int nobreak; int tlen; /* * nobreak is a horrible hack that specifies special * conditions where line breaks are just not allowed */ nobreak = 0; tchar = *end; *end = '\0'; /* * Malloc temp space if needed, leave room for * 2 spaces and a end of string char */ tlen = strlen(start) + 3; if (tlen > CompWordLen) { CompWordLen += COMP_LINE_BUF_LEN; if (tlen > CompWordLen) { CompWordLen = tlen; } if (CompWord != NULL) { free(CompWord); } CompWord = (char *)malloc(CompWordLen); } ptr = CompWord; if ((NeedSpace > 0)&&(stripped_space)) { if (NeedSpace == 2) { strcpy(ptr, " "); added_space = 2; } else { strcpy(ptr, " "); added_space = 1; } } else { strcpy(ptr, ""); } strcat(ptr, start); #ifdef DOUBLE_SPACE_AFTER_PUNCT /* * If this text ends in '.', '!', or '?' we need * to set up the addition of two spaces after it. */ tchar2 = ptr[strlen(ptr) - 1]; if ((tchar2 == '.')||(tchar2 == '!')||(tchar2 == '?')) { double_space = 1; } else { double_space = 0; } #else double_space = 0; #endif /* DOUBLE_SPACE_AFTER_PUNCT */ XTextExtents(currentFont, ptr, strlen(ptr), &dir, &ascent, &descent, &all); /* * Horrible hack for punctuation following * font changes to not go on the next line. */ if ((MY_ISPUNCT(*ptr))&&(added_space == 0)) { char *tptr; /* * Take into account whole streams of * punctuation. */ nobreak = 1; tptr = ptr; while ((*tptr != '\0')&&(MY_ISPUNCT(*tptr))) { tptr++; } if (*tptr != '\0') { nobreak = 0; } } /* * No linebreaks if this whole line is just too * long. */ if (*x == TextIndent) { nobreak = 1; } if (((*x + all.width + MarginW) <= width)||(nobreak)) { char *tptr; int tlen; if (line == NULL) { tlen = strlen(ptr) + 1; } else { tlen = strlen(line) + strlen(ptr) + 1; } if (tlen > CompLineLen) { CompLineLen += COMP_LINE_BUF_LEN; if (tlen > CompLineLen) { CompLineLen = tlen; } tptr = (char *)malloc(CompLineLen); if (CompLine != NULL) { strcpy(tptr, CompLine); free(CompLine); } else { tptr[0] = '\0'; } CompLine = tptr; } line = CompLine; strcat(line, ptr); } else { char *tptr, *tptr2; int tlen; if ((line != NULL)&&(line[0] != '\0')) { SetElement(hw, E_TEXT, currentFont, line_x, *y, line, NULL, NULL, IMAGE_DEFAULT_BORDER); /* * Save width here to avoid an * XTextExtents call later. */ Current->width = *x - line_x + 1; AdjustBaseLine(); PF_LF_State = 0; line[0] = '\0'; } LineFeed(hw, x, y); line_x = *x; /* * If we added a space before, remove it now * since we are at the beginning of a new line */ if (added_space) { tptr2 = (char *)(ptr + added_space); } else { tptr2 = ptr; } XTextExtents(currentFont, tptr2, strlen(tptr2), &dir, &ascent, &descent, &all); if (line == NULL) { tlen = strlen(tptr2) + 1; } else { tlen = strlen(line) + strlen(tptr2) + 1; } if (tlen > CompLineLen) { CompLineLen += COMP_LINE_BUF_LEN; if (tlen > CompLineLen) { CompLineLen = tlen; } tptr = (char *)malloc(CompLineLen); if (CompLine != NULL) { strcpy(tptr, CompLine); free(CompLine); } else { tptr[0] = '\0'; } CompLine = tptr; } line = CompLine; strcat(line, tptr2); } /* * Set NeedSpace for one or 2 spaces based on * whether we are after a '.', '!', or '?' * or not. */ if (double_space) { NeedSpace = 2; } else { NeedSpace = 1; } *x = *x + all.width; *end = tchar; } /* * Else if there is trailing whitespace, add it now */ else if ((NeedSpace > 0)&&(stripped_space)) { char *tptr; char *spc; int tlen; if (NeedSpace == 2) { spc = (char *)malloc(strlen(" ") + 1); strcpy(spc, " "); } else { spc = (char *)malloc(strlen(" ") + 1); strcpy(spc, " "); } XTextExtents(currentFont, spc, strlen(spc), &dir, &ascent, &descent, &all); /* * Sigh, adding this one little space might force a * line break. */ if ((*x + all.width + MarginW) <= width) { if (line == NULL) { tlen = strlen(spc) + 1; } else { tlen = strlen(line) + strlen(spc) + 1; } if (tlen > CompLineLen) { CompLineLen += COMP_LINE_BUF_LEN; if (tlen > CompLineLen) { CompLineLen = tlen; } tptr = (char *)malloc(CompLineLen); if (CompLine != NULL) { strcpy(tptr, CompLine); free(CompLine); } else { tptr[0] = '\0'; } CompLine = tptr; } line = CompLine; strcat(line, spc); } /* * Ok, the space forced a linefeed, but now we must * also drop the space since we don't want it if we * have a linefeed here. */ else { if ((line != NULL)&&(line[0] != '\0')) { SetElement(hw, E_TEXT, currentFont, line_x, *y, line, NULL, NULL, IMAGE_DEFAULT_BORDER); /* * Save width here to avoid an * XTextExtents call later. */ Current->width = *x - line_x + 1; AdjustBaseLine(); PF_LF_State = 0; line[0] = '\0'; } LineFeed(hw, x, y); line_x = *x; all.width = 0; } *x = *x + all.width; if (spc) free(spc); NeedSpace = 0; } } if ((line != NULL)&&(line[0] != '\0')) { SetElement(hw, E_TEXT, currentFont, line_x, *y, line, NULL, NULL, IMAGE_DEFAULT_BORDER); /* * Save width here to avoid an * XTextExtents call later. */ Current->width = *x - line_x + 1; AdjustBaseLine(); PF_LF_State = 0; line[0] = '\0'; } } TablePlace(hw,mptr,x,y,width) HTMLWidget hw; struct mark_up **mptr; int *x, *y; unsigned int width; { int extra; #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr,"TablePlace(hw,mptr,*x=%d,*y=%d,width = %d)\n", *x,*y,width); } #endif if ((*mptr)->is_end) { /* end of table */ return; } extra = 10; LineFeed(hw, x, y); SetElement(hw, E_TABLE, currentFont, *x, *y, (char *) mptr, NULL, NULL, IMAGE_DEFAULT_BORDER); if (!Current->table_data) { /* no table */ return; } Current->alignment = ALIGN_MIDDLE; Current->width = Current->table_data->width; Current->y_offset = Current->table_data->height-extra; Current->line_height = Current->table_data->height + extra; BaseLine = Current->table_data->height; *x += Current->width + 1; LineFeed(hw, x, y); } /* * Place an image. Add an element record for it. */ void ImagePlace(hw, mptr, x, y, width) HTMLWidget hw; struct mark_up *mptr; int *x, *y; unsigned int width; { char *tptr,*tmpPtr; char *wTmp,*hTmp; int border_width; #ifdef SPACE_HACK /* * If we are starting an image in formatted * text, and it needs a preceeding space, add * that space now. */ if ((!Preformat)&&(NeedSpace > 0)) { int dir, ascent, descent; XCharStruct all; if (NeedSpace == 2) { tptr = (char *)malloc(strlen(" ") + 1); strcpy(tptr, " "); } else { tptr = (char *)malloc(strlen(" ") + 1); strcpy(tptr, " "); } XTextExtents(currentFont, tptr, strlen(tptr), &dir, &ascent, &descent, &all); SetElement(hw, E_TEXT, currentFont, *x, *y, tptr, NULL, NULL, IMAGE_DEFAULT_BORDER); /* * Save width here to avoid an * XTextExtents call later. */ Current->width = all.width; AdjustBaseLine(); *x = *x + all.width; CharsInLine = CharsInLine + strlen(tptr); if (tptr) free(tptr); PF_LF_State = 0; NeedSpace = 0; } #endif /* SPACE_HACK */ tptr = ParseMarkTag(mptr->start, MT_IMAGE, "SRC"); /**temp******/ if (!tptr) { tptr = ParseMarkTag(mptr->start, MT_FIGURE, "SRC"); } /***********/ tmpPtr=tptr; tptr = ParseMarkTag(mptr->start, MT_IMAGE, "WIDTH"); /**temp******/ if (!tptr) { tptr = ParseMarkTag(mptr->start, MT_FIGURE, "WIDTH"); } /***********/ wTmp=tptr; tptr = ParseMarkTag(mptr->start, MT_IMAGE, "HEIGHT"); /**temp******/ if (!tptr) { tptr = ParseMarkTag(mptr->start, MT_FIGURE, "HEIGHT"); } /***********/ hTmp=tptr; tptr = ParseMarkTag(mptr->start, MT_IMAGE, "BORDER"); /**temp******/ if (!tptr) { tptr = ParseMarkTag(mptr->start, MT_FIGURE, "BORDER"); } /***********/ if (!tptr || !*tptr) { border_width=IMAGE_DEFAULT_BORDER; } else if ((border_width=atoi(tptr))<0) { border_width=0; } if (tptr) { free(tptr); } SetElement(hw, E_IMAGE, currentFont, *x, *y, tmpPtr, wTmp, hTmp, border_width); /* * Only after we have placed the image do we know its dimensions. * So now look and see if the image is too wide, and if so go * back and insert a linebreak. */ if ((Current->pic_data != NULL)&&(!Preformat)) { int extra; if ((hw->html.border_images == True)|| ((Current->anchorHRef != NULL)&& (!Current->pic_data->internal))) { if (Current->pic_data->delayed) { extra=4; } else { extra = 2*border_width; } } else { extra = 0; } if (((*x + Current->pic_data->width + extra + MarginW) >width)&& (Current->prev != NULL)&& (Current->prev->line_number == LineNumber)) { Current = Current->prev; LineFeed(hw, x, y); SetElement(hw, E_IMAGE, currentFont, *x, *y, tmpPtr, wTmp, hTmp, border_width); } } /* * Clean up parsed SRC string */ if (tmpPtr) { free(tmpPtr); } if (wTmp) { free(wTmp); } if (hTmp) { free(hTmp); } /* * Yank out the name field, and stick it in text. * We may use this for ALT to at some later date. */ if (Current->pic_data != NULL) { tptr = ParseMarkTag(mptr->start, MT_IMAGE, "NAME"); /*temp******/ if (!tptr) { tptr = ParseMarkTag(mptr->start, MT_FIGURE, "NAME"); } /*temp******/ Current->pic_data->text = tptr; } /* * Check if this image has the ISMAP attribute, so we know the * x,y coordinates of the image click are important. * Due to a special case (see below), this code can acutally * change the size, or anchor status of the image, thus we MUST * doit before we muck with the Baseline and stuff. */ if (Current->pic_data != NULL) { /* * Handle the USEMAP attribute of IMG tags. This is used for * client-side image map support. * --SWP */ Current->pic_data->map=NULL; Current->pic_data->usemap=NULL; tptr=ParseMarkTag(mptr->start, MT_IMAGE, "USEMAP"); if (tptr!=NULL) { if (*tptr) { Current->pic_data->usemap=tptr; } else { free(tptr); } } Current->pic_data->fptr = NULL; tptr = ParseMarkTag(mptr->start, MT_IMAGE, "ISMAP"); if (tptr != NULL) { free(tptr); Current->pic_data->ismap = 1; /* * SUPER SPECIAL CASE! (Thanks Marc) * If you have an ISMAP image inside a form, * And that form doesn't already have an HREF * by being inside an anchor, * (Being a DelayedHRef is considered no href) * clicking in that image will submit the form, * adding the x,y coordinates of the click as part * of the list of name/value pairs. */ if ((CurrentForm != NULL)&& ((Current->anchorHRef == NULL)|| (IsDelayedHRef(hw, Current->anchorHRef)))) { Current->pic_data->fptr = CurrentForm; Current->anchorHRef = IsMapForm(hw); Current->fg = hw->html.anchor_fg; } } else { Current->pic_data->ismap = 0; } } /* * Check if this image will be top aligned */ tptr = ParseMarkTag(mptr->start, MT_IMAGE, "ALIGN"); /*temp******/ if (!tptr) { tptr = ParseMarkTag(mptr->start, MT_FIGURE, "ALIGN"); } /*temp******/ if (caseless_equal(tptr, "TOP")) { Current->alignment = ALIGN_TOP; } else if (caseless_equal(tptr, "MIDDLE")) { Current->alignment = ALIGN_MIDDLE; } else { Current->alignment = ALIGN_BOTTOM; } /* * Clean up parsed ALIGN string */ if (tptr != NULL) { free(tptr); } /* * Advance x position, and check the max * line height. We need to follow this * image with a space. */ if (Current->pic_data != NULL) { int extra; if ((hw->html.border_images == True)|| ((Current->anchorHRef != NULL)&& (!Current->pic_data->internal))) { if (Current->pic_data->delayed) { extra=4; } else { extra = 2*border_width; } } else { extra = 0; } if (BaseLine == -100) { BaseLine = 0; } *x = *x + Current->pic_data->width + extra; if (Current->alignment == ALIGN_TOP) { Current->y_offset = 0; if ((Current->pic_data->height + extra - BaseLine) > LineBottom) { LineBottom = Current->pic_data->height + extra - BaseLine; } } else if (Current->alignment == ALIGN_MIDDLE) { int baseline; baseline = (Current->pic_data->height + extra) / 2; if (baseline <= BaseLine) { Current->y_offset = BaseLine - baseline; } else { struct ele_rec *eptr; int line, incy; Current->y_offset = 0; incy = baseline - BaseLine; BaseLine = baseline; /* * Go back over this line * and move everything down * a little. */ eptr = Current; line = eptr->line_number; while ((eptr->prev != NULL)&& (eptr->prev->line_number == line)) { eptr = eptr->prev; eptr->y_offset = eptr->y_offset + incy; } } if ((Current->pic_data->height + extra - BaseLine) > LineBottom) { LineBottom = Current->pic_data->height + extra - BaseLine; } } else if ((Current->pic_data->height + extra) <= BaseLine) { Current->y_offset = BaseLine - (Current->pic_data->height + extra); } else if ((Current->pic_data->height + extra) > BaseLine) { struct ele_rec *eptr; int line, incy; incy = Current->pic_data->height + extra - BaseLine; BaseLine = Current->pic_data->height + extra; /* * Go back over this line * and move everything down * a little. */ eptr = Current; line = eptr->line_number; while ((eptr->prev != NULL)&& (eptr->prev->line_number == line)) { eptr = eptr->prev; eptr->y_offset = eptr->y_offset + incy; } } if (BaseLine == 0) { BaseLine = -100; } } PF_LF_State = 0; NeedSpace = 1; } /* * Place a Widget. Add an element record for it. */ void WidgetPlace(hw, mptr, x, y, width) HTMLWidget hw; struct mark_up *mptr; int *x, *y; unsigned int width; { SetElement(hw, E_WIDGET, currentFont, *x, *y, mptr->start,NULL,NULL,IMAGE_DEFAULT_BORDER); /* * Only after we have placed the widget do we know its dimensions. * So now look and see if the widget is too wide, and if so go * back and insert a linebreak. */ if ((Current->widget_data != NULL)&&(!Preformat)) { int extra; extra = 2 * IMAGE_DEFAULT_BORDER; if (((*x + Current->widget_data->width + extra + MarginW) > width)&& (Current->prev != NULL)&& (Current->prev->line_number == LineNumber)) { WidgetId--; Current = Current->prev; LineFeed(hw, x, y); SetElement(hw, E_WIDGET, currentFont, *x, *y, mptr->start,NULL,NULL,IMAGE_DEFAULT_BORDER); } } /* * Advance x position, and check BaseLine and LineBottom. * We need to follow this widget with a space. */ if (Current->widget_data != NULL) { int extra; int baseline; XFontStruct *fp; extra = 2 * IMAGE_DEFAULT_BORDER; /* * Find the font used in this widget. Then find its baseline */ fp = GetWidgetFont(hw, Current->widget_data); if (fp == NULL) { baseline = Current->widget_data->height + extra; } /* * If no font, the baseline is the bottum of the widget */ else { int border; border = ((Current->widget_data->height + extra) - (fp->max_bounds.ascent + fp->max_bounds.descent)); baseline = (border / 2) + fp->max_bounds.ascent; } /* * Baseline == -100 is the special unset baseline value. */ if (BaseLine == -100) { BaseLine = baseline; Current->y_offset = 0; /* * If linebottom isn't set, set it to * whatever of the height is below the baseline. */ if (LineBottom == 0) { LineBottom = Current->widget_data->height + extra - baseline; } /* * Else, it is possible that a linebottom has been * set even when we have no baseline yet (like if * the first item in the line was a top aligned image) * It now needs to be corrected as we set a real * BaseLine. */ else { if ((Current->widget_data->height + extra - baseline) > (LineBottom - baseline)) { LineBottom = Current->widget_data->height + extra - baseline; } else { LineBottom = LineBottom - baseline; } } } /* * Else we already have a baseline, and it is greater that * the baseline for this widget. * Set y_offset, and check linebottom. */ else if (baseline <= BaseLine) { if (baseline < BaseLine) { Current->y_offset = BaseLine - baseline; } else { Current->y_offset = 0; } /* * Our line bottom may be greater than the * old one. */ if ((Current->widget_data->height + extra - baseline) > LineBottom) { LineBottom = Current->widget_data->height + extra - baseline; } } else /* * Else we have a new baseline greater than the old baseline. */ { struct ele_rec *eptr; int line, incy; /* * Figure out how much to move all the old stuff */ incy = baseline - BaseLine; BaseLine = baseline; /* * Go back over this line * and move everything down * a little. */ eptr = Current; line = eptr->line_number; while ((eptr->prev != NULL)&& (eptr->prev->line_number == line)) { eptr = eptr->prev; eptr->y_offset = eptr->y_offset + incy; } /* * Our line bottom may be greater than the * old one. */ if ((Current->widget_data->height + extra - baseline) > LineBottom) { LineBottom = Current->widget_data->height + extra - baseline; } } /* * Advance the X position. */ *x = *x + Current->widget_data->width + extra; } PF_LF_State = 0; NeedSpace = 1; } static void PushFont(font) XFontStruct *font; { FontRec *fptr; fptr = (FontRec *)malloc(sizeof(FontRec)); if (fptr == NULL) { #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr, "No memory to expand font stack!\n"); } #endif return; } fptr->font = font; fptr->next = FontStack; FontStack = fptr; } static XFontStruct * PopFont() { XFontStruct *font; FontRec *fptr; if (FontStack->next != NULL) { fptr = FontStack; FontStack = FontStack->next; font = fptr->font; free((char *)fptr); } else { #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr, "Warning, popping empty font stack!\n"); } #endif font = FontStack->font; } return(font); } /* * We've just terminated the current OPTION. * Put it in the proper place in the SelectInfo structure. * Move option_buf into options, and maybe copy into * value if is_value is set. */ static void ProcessOption(sptr) SelectInfo *sptr; { int i, cnt; char **tarray; clean_white_space(sptr->option_buf); tarray = sptr->options; cnt = sptr->option_cnt + 1; sptr->options = (char **)malloc(sizeof(char *) * cnt); for (i=0; i<(cnt - 1); i++) { sptr->options[i] = tarray[i]; } if (tarray != NULL) { free((char *)tarray); } sptr->options[cnt - 1] = sptr->option_buf; sptr->option_cnt = cnt; tarray = sptr->returns; cnt = sptr->option_cnt; sptr->returns = (char **)malloc(sizeof(char *) * cnt); for (i=0; i<(cnt - 1); i++) { sptr->returns[i] = tarray[i]; } if (tarray != NULL) { free((char *)tarray); } sptr->returns[cnt - 1] = sptr->retval_buf; if (sptr->is_value) { tarray = sptr->value; cnt = sptr->value_cnt + 1; sptr->value = (char **)malloc(sizeof(char *) * cnt); for (i=0; i<(cnt - 1); i++) { sptr->value[i] = tarray[i]; } if (tarray != NULL) { free((char *)tarray); } sptr->value[cnt - 1] = (char *)malloc( strlen(sptr->option_buf) + 1); strcpy(sptr->value[cnt - 1], sptr->option_buf); sptr->value_cnt = cnt; } } /* * Horrible code for the TEXTAREA element. Escape '\' and ''' by * putting a '\' in front of them, then replace all '"' with '''. * This lets us safely put the resultant value between double quotes. */ char * TextAreaAddValue(value, text) char *value; char *text; { int extra; char *buf; char *bptr; char *tptr; if ((text == NULL)||(text[0] == '\0')) { return(value); } extra = 0; tptr = text; while (*tptr != '\0') { if (*tptr == '\\') { extra++; } else if (*tptr == '\'') { extra++; } tptr++; } buf = (char *)malloc(strlen(value) + strlen(text) + extra + 1); if (buf == NULL) { return(value); } strcpy(buf, value); tptr = text; bptr = (char *)(buf + strlen(value)); while (*tptr != '\0') { if ((*tptr == '\\')||(*tptr == '\'')) { *bptr++ = '\\'; *bptr++ = *tptr++; } else if (*tptr == '\"') { *bptr++ = '\''; tptr++; } else { *bptr++ = *tptr++; } } *bptr = '\0'; free(value); return(buf); } /* * Make necessary changes to formatting, based on the type of the * parsed HTML text we are formatting. * Some calls create elements that are added to the formatted element list. */ static void TriggerMarkChanges(hw, mptr, x, y) HTMLWidget hw; struct mark_up **mptr; int *x, *y; { struct mark_up *mark; XFontStruct *font; int type, width, curCoord; char *tptr,*cptr,*endptr; mark = *mptr; type = mark->type; font = NULL; /* If we are not in a tag that belongs in the HEAD, end the HEAD section - amb */ if (InDocHead) if ((type != M_TITLE)&&(type != M_NONE)&&(type != M_BASE)&& (type != M_INDEX)&&(type != M_COMMENT)) { Ignore = 0; InDocHead = 0; } /* * If Ignore is set, we ignore all further elements until we get to the * end of the Ignore * Let text through so we can grab the title text. * Let title through so we can hit the end title. * Now also used for SELECT parseing * Let SELECT through so we can hit the end SELECT. * Let OPTION through so we can hit the OPTIONs. * Let TEXTAREA through so we can hit the TEXTAREAs. */ if ((Ignore)&&(!InDocHead)&&(type != M_TITLE)&&(type != M_NONE)&&(type != M_COMMENT)&& (type != M_SELECT)&&(type != M_OPTION)&& (type != M_TEXTAREA)&&(type != M_DOC_HEAD)) { return; } switch(type) { /* * Place the text. Different functions based on whether it * is pre-formatted or not. */ case M_NONE: if ((Ignore)&&(CurrentSelect == NULL)&& (TextAreaBuf == NULL)) { if (TitleText == NULL) { TitleText = (char *) malloc(strlen((*mptr)->text) + 1); strcpy(TitleText, (*mptr)->text); } else { char *tptr; tptr = (char *) malloc(strlen(TitleText) + strlen((*mptr)->text) + 1); strcpy(tptr, TitleText); strcat(tptr, (*mptr)->text); free(TitleText); TitleText = tptr; } } else if ((Ignore)&&(CurrentSelect != NULL)) { if (CurrentSelect->option_buf != NULL) { char *tptr; tptr = (char *)malloc(strlen( CurrentSelect->option_buf) + strlen((*mptr)->text) + 1); strcpy(tptr, CurrentSelect->option_buf); strcat(tptr, (*mptr)->text); free(CurrentSelect->option_buf); CurrentSelect->option_buf = tptr; } } else if ((Ignore)&&(TextAreaBuf != NULL)) { TextAreaBuf = TextAreaAddValue(TextAreaBuf, (*mptr)->text); } else if (Preformat) { PreformatPlace(hw, *mptr, x, y, Width); } else { FormatPlace(hw, *mptr, x, y, Width); } break; /* * Titles are just set into the widget for retrieval by * XtGetValues(). */ case M_TITLE: if (mark->is_end) { if (!InDocHead) Ignore = 0; hw->html.title = TitleText; TitleText = NULL; } else { Ignore = 1; TitleText = NULL; } break; /* * Formatting commands just change the current font. */ case M_CODE: case M_SAMPLE: case M_KEYBOARD: case M_FIXED: if (mark->is_end) { font = PopFont(); } else { PushFont(currentFont); font = hw->html.fixed_font; } break; case M_STRONG: case M_BOLD: if (mark->is_end) { font = PopFont(); } else { PushFont(currentFont); if (currentFont == hw->html.fixed_font || currentFont == hw->html.fixeditalic_font) font = hw->html.fixedbold_font; else if (currentFont == hw->html.plain_font || currentFont == hw->html.plainitalic_font) font = hw->html.plainbold_font; else font = hw->html.bold_font; } break; case M_EMPHASIZED: case M_VARIABLE: case M_CITATION: case M_ITALIC: if (mark->is_end) { font = PopFont(); } else { PushFont(currentFont); if (currentFont == hw->html.fixed_font || currentFont == hw->html.fixedbold_font) font = hw->html.fixeditalic_font; else if (currentFont == hw->html.plain_font || currentFont == hw->html.plainbold_font) font = hw->html.plainitalic_font; else font = hw->html.italic_font; } break; /* * Strikeout means draw a line through the text. * Right now we just set a boolean flag which gets shoved * in the element record for all elements in the * strikeout zone. */ case M_STRIKEOUT: if (mark->is_end) { Strikeout = False; } else { Strikeout = True; } break; case M_SUP: if (mark->is_end) { Superscript--; if ((Superscript==0) && (Subscript==0)) font = PopFont(); } else { Superscript++; if ((Superscript==1) && (Subscript==0)) { nonScriptFont=currentFont; PushFont(currentFont); font = hw->html.supsub_font; } } break; case M_SUB: if (mark->is_end) { Subscript--; if ((Subscript==0) && (Superscript==0)) font = PopFont(); } else { Subscript++; if ((Subscript==1) && (Superscript==0)) { nonScriptFont=currentFont; PushFont(currentFont); font = hw->html.supsub_font; } } break; case M_CENTER: if (mark->is_end) Centered = 0; else Centered = 1; break; /* amb - ignore text inside a HEAD element */ case M_DOC_HEAD: if (mark->is_end) { InDocHead = 0; Ignore = 0; } else { InDocHead = 1; Ignore = 1; } break; case M_DOC_BODY: if (mark->is_end) { /* do nothing */ } else { InDocHead = 0; /* end section */ Ignore = 0; } break; case M_UNDERLINED: if (mark->is_end) { Underlines = 0; InUnderlined = 0; } else { Underlines = 1; InUnderlined = 1; } break; /* * Headers are preceeded and followed by a linefeed, * and the change the font. */ case M_HEADER_1: ConditionalLineFeed(hw, x, y, 1); if (mark->is_end) { font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 2); PushFont(currentFont); font = hw->html.header1_font; } break; case M_HEADER_2: ConditionalLineFeed(hw, x, y, 1); if (mark->is_end) { font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 2); PushFont(currentFont); font = hw->html.header2_font; } break; case M_HEADER_3: ConditionalLineFeed(hw, x, y, 1); if (mark->is_end) { font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 2); PushFont(currentFont); font = hw->html.header3_font; } break; case M_HEADER_4: ConditionalLineFeed(hw, x, y, 1); if (mark->is_end) { font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 2); PushFont(currentFont); font = hw->html.header4_font; } break; case M_HEADER_5: ConditionalLineFeed(hw, x, y, 1); if (mark->is_end) { font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 2); PushFont(currentFont); font = hw->html.header5_font; } break; case M_HEADER_6: ConditionalLineFeed(hw, x, y, 1); if (mark->is_end) { font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 2); PushFont(currentFont); font = hw->html.header6_font; } break; case M_FRAME: break; /* * Anchors change the text color, and may set * underlineing attributes. * No linefeeds, so they can be imbedded anywhere. */ case M_ANCHOR: if (mark->is_end) { /* * Without motif we use our own foreground resource instead of * using the manager's */ #ifdef MOTIF Fg = hw->manager.foreground; #else Fg = hw->html.foreground; #endif /* MOTIF */ if (!InUnderlined) /* amb 2 */ Underlines = 0; else Underlines = 1; DashedUnderlines = False; AnchorText = NULL; } else { char *tptr; /* * Only change the color of anchors with * HREF tags, because other anchors are * not active. */ tptr = ParseMarkTag(mark->start, MT_ANCHOR, AT_HREF); if (tptr != NULL) { /* * If internal check our internal list * to change color if visited before. */ if (Internal == True) { struct ref_rec *hptr; hptr = FindHRef( hw->html.my_visited_hrefs, tptr); if (hptr != NULL) { Fg = hw->html.visitedAnchor_fg; Underlines = hw->html.num_visitedAnchor_underlines; DashedUnderlines = hw->html.dashed_visitedAnchor_lines; } else { Fg = hw->html.anchor_fg; Underlines = hw->html.num_anchor_underlines; DashedUnderlines = hw->html.dashed_anchor_lines; } } /* * Else we may want to send * the href back somewhere else and * find out if we've visited it before */ else if (hw->html.previously_visited_test != NULL) { if ((*(visitTestProc) (hw->html.previously_visited_test)) (hw, tptr)) { Fg = hw->html.visitedAnchor_fg; Underlines = hw->html.num_visitedAnchor_underlines; DashedUnderlines = hw->html.dashed_visitedAnchor_lines; } else { Fg = hw->html.anchor_fg; Underlines = hw->html.num_anchor_underlines; DashedUnderlines = hw->html.dashed_anchor_lines; } } else { Fg = hw->html.anchor_fg; Underlines = hw->html.num_anchor_underlines; DashedUnderlines = hw->html.dashed_anchor_lines; } if (tptr) free(tptr); } AnchorText = mark->start; /* amb 2 */ if (InUnderlined) { DashedUnderlines = False; if (!Underlines) Underlines = 1; } } break; /* * Just insert a linefeed, or ignore if this is prefomatted * text because the

will be followed be a linefeed. */ case M_PARAGRAPH: ConditionalLineFeed(hw, x, y, 1); ConditionalLineFeed(hw, x, y, 2); break; /* * Just insert the image for now */ case M_FIGURE: case M_IMAGE: if (mark->is_end) { /* do nothing */ } else { ImagePlace(hw, *mptr, x, y, Width); } break; /* * Can only be inside a SELECT tag. */ case M_OPTION: if (CurrentSelect != NULL) { char *tptr; if (CurrentSelect->option_buf != NULL) { ProcessOption(CurrentSelect); } CurrentSelect->option_buf = (char *)malloc(1); strcpy(CurrentSelect->option_buf, ""); /* * Check if this option starts selected */ tptr = ParseMarkTag(mark->start, MT_OPTION, "SELECTED"); if (tptr != NULL) { CurrentSelect->is_value = 1; free(tptr); } else { CurrentSelect->is_value = 0; } /* * Check if this option has an different * return value field. */ tptr = ParseMarkTag(mark->start, MT_OPTION, "VALUE"); if (tptr != NULL) { if (*tptr != '\0') { CurrentSelect->retval_buf = tptr; } else { CurrentSelect->retval_buf = NULL; free(tptr); } } else { CurrentSelect->retval_buf = NULL; } } break; /* * Special INPUT tag. Allows an option menu or * a scrolled list. * Due to a restriction in SGML, this can't just be a * subset of the INPUT markup. However, I can treat it * that way to avoid duplicating code. * As a result I combine SELECT and OPTION into a faked * up INPUT mark. */ case M_SELECT: if (CurrentForm != NULL) { if ((mark->is_end)&&(CurrentSelect != NULL)) { int len; char *buf; char *start; char *options, *returns, *value; if (CurrentSelect->option_buf != NULL) { ProcessOption(CurrentSelect); } options = ComposeCommaList( CurrentSelect->options, CurrentSelect->option_cnt); returns = ComposeCommaList( CurrentSelect->returns, CurrentSelect->option_cnt); value = ComposeCommaList( CurrentSelect->value, CurrentSelect->value_cnt); FreeCommaList( CurrentSelect->options, CurrentSelect->option_cnt); FreeCommaList( CurrentSelect->returns, CurrentSelect->option_cnt); FreeCommaList( CurrentSelect->value, CurrentSelect->value_cnt); /* * Construct a fake INPUT tag. */ len = strlen(MT_INPUT) + strlen(options) + strlen(returns) + strlen(value) + strlen( " type=select options=\"\" returns=\"\" value=\"\""); buf = (char *)malloc(len + strlen(CurrentSelect->mptr->start) + 1); strcpy(buf, MT_INPUT); strcat(buf, " type=select"); strcat(buf, " options=\""); strcat(buf, options); strcat(buf, "\" returns=\""); strcat(buf, returns); strcat(buf, "\" value=\""); strcat(buf, value); strcat(buf, "\""); strcat(buf, (char *) (CurrentSelect->mptr->start + strlen(MT_SELECT))); /* * stick the fake in, saving the * real one. */ start = CurrentSelect->mptr->start; CurrentSelect->mptr->start = buf; WidgetPlace(hw, CurrentSelect->mptr, x, y, Width); /* * free the fake, put the original back */ free(buf); free(options); free(returns); free(value); CurrentSelect->mptr->start = start; free((char *)CurrentSelect); CurrentSelect = NULL; Ignore = 0; } else if ((!mark->is_end)&&(CurrentSelect == NULL)) { CurrentSelect = (SelectInfo *)malloc( sizeof(SelectInfo)); CurrentSelect->hw = (Widget)hw; CurrentSelect->mptr = *mptr; CurrentSelect->option_cnt = 0; CurrentSelect->returns = NULL; CurrentSelect->retval_buf = NULL; CurrentSelect->options = NULL; CurrentSelect->option_buf = NULL; CurrentSelect->value_cnt = 0; CurrentSelect->value = NULL; CurrentSelect->is_value = -1; Ignore = 1; } } break; /* * TEXTAREA is a replacement for INPUT type=text size=rows,cols * because SGML will not allow an arbitrary length value * field. */ case M_TEXTAREA: if (CurrentForm != NULL) { if ((mark->is_end)&&(TextAreaBuf != NULL)) { char *start; char *buf; /* * Finish a fake INPUT tag. */ buf = (char *)malloc( strlen(TextAreaBuf) + 2); strcpy(buf, TextAreaBuf); strcat(buf, "\""); /* * stick the fake in, saving the * real one. */ start = mark->start; mark->start = buf; mark->is_end = 0; WidgetPlace(hw, mark, x, y, Width); /* * free the fake, put the original back */ free(buf); free(TextAreaBuf); mark->start = start; mark->is_end = 1; TextAreaBuf = NULL; Ignore = 0; } else if ((!mark->is_end)&&(TextAreaBuf == NULL)) { char *buf; int len; /* * Construct the start of * a fake INPUT tag. */ len = strlen(MT_INPUT) + strlen( " type=textarea value=\"\""); buf = (char *)malloc(len + strlen(mark->start) + 1); strcpy(buf, MT_INPUT); strcat(buf, (char *) (mark->start + strlen(MT_TEXTAREA))); strcat(buf, " type=textarea"); strcat(buf, " value=\""); TextAreaBuf = buf; Ignore = 1; } } break; /* * Just insert the widget. * Can only inside a FORM tag. * Special case the type=image stuff to become a special * IMG tag. */ case M_INPUT: if (CurrentForm != NULL) { char *tptr; char *tptr2; tptr = ParseMarkTag((*mptr)->start, MT_INPUT, "TYPE"); if ((tptr != NULL)&& (caseless_equal(tptr, "image"))) { free(tptr); tptr = (char *)malloc( strlen((*mptr)->start) + strlen(" ISMAP") + strlen(MT_IMAGE) - strlen(MT_INPUT) + 1); strcpy(tptr, MT_IMAGE); strcat(tptr, (char *) ((*mptr)->start + strlen(MT_INPUT)) ); strcat(tptr, " ISMAP"); tptr2 = (*mptr)->start; (*mptr)->start = tptr; ImagePlace(hw, *mptr, x, y, Width); (*mptr)->start = tptr2; free(tptr); } /* * hidden inputs have no element associated * with them, just a widget record. */ else if ((tptr != NULL)&& (caseless_equal(tptr, "hidden"))) { free(tptr); WidgetId++; (void)MakeWidget(hw, (*mptr)->start, x, y, WidgetId, CurrentForm); } else { if (tptr != NULL) { free(tptr); } WidgetPlace(hw, *mptr, x, y, Width); } } break; /* * Fillout forms. Cannot be nested. */ case M_FORM: ConditionalLineFeed(hw, x, y, 1); if ((mark->is_end)&&(CurrentForm != NULL)) { CurrentForm->end = WidgetId; ConditionalLineFeed(hw, x, y, 2); AddNewForm(hw, CurrentForm); CurrentForm = NULL; } else if ((!mark->is_end)&&(CurrentForm == NULL)) { ConditionalLineFeed(hw, x, y, 2); CurrentForm = (FormInfo *)malloc( sizeof(FormInfo)); CurrentForm->next = NULL; CurrentForm->hw = (Widget)hw; CurrentForm->action = ParseMarkTag(mark->start, MT_FORM, "ACTION"); CurrentForm->format = ParseMarkTag(mark->start, MT_FORM, "FORMAT"); CurrentForm->method = ParseMarkTag(mark->start, MT_FORM, "METHOD"); CurrentForm->enctype = ParseMarkTag(mark->start, MT_FORM, "ENCTYPE"); CurrentForm->enc_entity = ParseMarkTag( mark->start, MT_FORM, "ENCENTITY"); CurrentForm->start = WidgetId; CurrentForm->end = -1; CurrentForm->button_pressed=NULL; } break; /* * Addresses are just like headers. A linefeed before and * after, and change the font. */ case M_ADDRESS: ConditionalLineFeed(hw, x, y, 1); if (mark->is_end) { font = PopFont(); } else { PushFont(currentFont); font = hw->html.address_font; } break; /* * Blockquotes increase the margin width. * They cannot be nested. */ case M_BLOCKQUOTE: ConditionalLineFeed(hw, x, y, 1); if (mark->is_end) { MarginW = hw->html.margin_width; /* * Only unindent if we think we indented * when we started the blockquote */ if (TextIndent <= (2 * MarginW)) { TextIndent = MarginW; } ConditionalLineFeed(hw, x, y, 2); /* * The linefeed should have set x to TextIndent * but since it is conditional, it might not * have, so check it here. */ if (*x > TextIndent) { *x = TextIndent; } } else { MarginW = 2 * hw->html.margin_width; /* * Only indent if the current indent * is less than what we want. */ if (TextIndent < MarginW) { TextIndent = MarginW; } ConditionalLineFeed(hw, x, y, 2); /* * The linefeed should have set x to TextIndent * but since it is conditional, it might not * have, so check it here. */ if (*x < TextIndent) { *x = TextIndent; } } break; /* * Plain text. A single pre-formatted chunk of text * in its own font. */ case M_PLAIN_TEXT: if (mark->is_end) { Preformat = 0; /* * Properly convert the Linefeed state * variable from preformat to formatted * state. */ if (PF_LF_State == 2) { PF_LF_State = 1; } else { PF_LF_State = 0; } ConditionalLineFeed(hw, x, y, 1); font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 1); ConditionalLineFeed(hw, x, y, 2); Preformat = 1; PF_LF_State = 0; PushFont(currentFont); font = hw->html.plain_font; } break; /* * Listing text. A different pre-formatted chunk of text * in its own font. */ case M_LISTING_TEXT: if (mark->is_end) { Preformat = 0; /* * Properly convert the Linefeed state * variable from preformat to formatted * state. */ if (PF_LF_State == 2) { PF_LF_State = 1; } else { PF_LF_State = 0; } ConditionalLineFeed(hw, x, y, 1); font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 1); ConditionalLineFeed(hw, x, y, 2); Preformat = 1; PF_LF_State = 0; PushFont(currentFont); font = hw->html.listing_font; } break; /* * Plain text. The rest of the text is pre-formatted. * There is not end for this mark. */ case M_PLAIN_FILE: ConditionalLineFeed(hw, x, y, 1); ConditionalLineFeed(hw, x, y, 2); Preformat = 1; PF_LF_State = 0; PushFont(currentFont); font = hw->html.plain_font; break; /* * Numbered lists, Unnumbered lists, Menus. * Currently also lump directory listings into this. * Save state for each indent level. * Change the value of the TxtIndent (can be nested) * Linefeed at the end of the list. */ case M_NUM_LIST: case M_UNUM_LIST: case M_MENU: case M_DIRECTORY: ConditionalLineFeed(hw, x, y, 1); width = hw->html.font->max_bounds.width; /* * If this is the outermost level of indentation, * add another linefeed for more white space. */ if ((TextIndent <= MarginW)||((mark->is_end)&& ((TextIndent - ((INDENT_SPACES + 1) * width)) <= MarginW))) { ConditionalLineFeed(hw, x, y, 2); } if (mark->is_end) { TextIndent = TextIndent - ((INDENT_SPACES + 1) * width); if (TextIndent < MarginW) { TextIndent = MarginW; } IndentLevel--; if (IndentLevel < 0) { IndentLevel = 0; } /* * restore the old state if there is one */ if (ListData->next != NULL) { DescRec *dptr; dptr = ListData; ListData = ListData->next; free((char *)dptr); } } else { DescRec *dptr; dptr = (DescRec *)malloc(sizeof(DescRec)); /* * Save the old state, and start a new */ if (type == M_NUM_LIST) { dptr->type = D_OLIST; dptr->count = 1; } else { dptr->type = D_ULIST; dptr->count = 0; } dptr->next = ListData; ListData = dptr; TextIndent = TextIndent + ((INDENT_SPACES + 1) * width); IndentLevel++; } *x = TextIndent; break; /* * Place the bullet element at the beginning of this item. */ case M_LIST_ITEM: if (!mark->is_end) { ConditionalLineFeed(hw, x, y, 1); /* * for ordered/numbered lists * put numbers in place of bullets. */ if (ListData->type == D_OLIST) { ListNumberPlace(hw, x, y, ListData->count); ListData->count++; } else { BulletPlace(hw, x, y); } } break; /* * Description lists */ case M_DESC_LIST: ConditionalLineFeed(hw, x, y, 1); ConditionalLineFeed(hw, x, y, 2); width = hw->html.font->max_bounds.width; if (mark->is_end) { if (DescType->type == D_TEXT) { TextIndent = TextIndent - ((INDENT_SPACES + 1) * width); if (TextIndent < MarginW) { TextIndent = MarginW; } } /* * restore the old state if there is one */ if (DescType->next != NULL) { DescRec *dptr; dptr = DescType; DescType = DescType->next; free((char *)dptr); /* * If the old state had forced an * indent, outdent it now. */ if (DescType->type == D_TITLE) { TextIndent = TextIndent - ((INDENT_SPACES + 1) * width); if (TextIndent < MarginW) { TextIndent = MarginW; } } } } else { DescRec *dptr; char *tptr; dptr = (DescRec *)malloc(sizeof(DescRec)); /* * Check is this is a compact list */ tptr = ParseMarkTag(mark->start, MT_DESC_LIST, "COMPACT"); if (tptr != NULL) { free(tptr); dptr->compact = 1; } else { dptr->compact = 0; } /* * Description list stared after a title needs * a forced indentation here */ if (DescType->type == D_TITLE) { TextIndent = TextIndent + ((INDENT_SPACES + 1) * width); } /* * Save the old state, and start a new */ dptr->type = D_TITLE; dptr->next = DescType; DescType = dptr; } *x = TextIndent; break; case M_DESC_TITLE: ConditionalLineFeed(hw, x, y, 1); width = hw->html.font->max_bounds.width; /* * Special hack. Don't indent again for * multiple

's in a row. */ if (DescType->type == D_TEXT) { TextIndent = TextIndent - ((INDENT_SPACES + 1) * width); if (TextIndent < MarginW) { TextIndent = MarginW; } } DescType->type = D_TITLE; *x = TextIndent; break; case M_DESC_TEXT: width = hw->html.font->max_bounds.width; /* * For a compact list we want to stay on the same * line if there is room and we are the first line * after a title. */ if ((DescType->compact)&&(DescType->type == D_TITLE)&& (*x < (TextIndent + (INDENT_SPACES * width)))) { NeedSpace = 0; } else { ConditionalLineFeed(hw, x, y, 1); } /* * Special hack. Don't indent again for * multiple
's in a row. */ if (DescType->type == D_TITLE) { TextIndent = TextIndent + ((INDENT_SPACES + 1) * width); } DescType->type = D_TEXT; *x = TextIndent; break; case M_PREFORMAT: if (mark->is_end) { Preformat = 0; /* * Properly convert the Linefeed state * variable from preformat to formatted * state. */ if (PF_LF_State == 2) { PF_LF_State = 1; } else { PF_LF_State = 0; } ConditionalLineFeed(hw, x, y, 1); if (saveFont != NULL) { hw->html.font = saveFont; saveFont = NULL; } font = PopFont(); NewFont(font); currentFont = font; ConditionalLineFeed(hw, x, y, 2); } else { ConditionalLineFeed(hw, x, y, 1); ConditionalLineFeed(hw, x, y, 2); Preformat = 1; PF_LF_State = 2; if (saveFont == NULL) { saveFont = hw->html.font; hw->html.font = hw->html.plain_font; } PushFont(currentFont); font = hw->html.font; } break; /* * Now with forms, is the same as: *
*
* This is a searchable index. Enter search keywords: * *
*
* Also, will take an ACTION tag to specify a * different URL to submit the query to. */ case M_INDEX: hw->html.is_index = True; /* * No index inside a form */ if (CurrentForm == NULL) { struct mark_up mark_tmp; /* * Start the form */ ConditionalLineFeed(hw, x, y, 1); ConditionalLineFeed(hw, x, y, 2); CurrentForm = (FormInfo *)malloc( sizeof(FormInfo)); CurrentForm->next = NULL; CurrentForm->hw = (Widget)hw; CurrentForm->action = NULL; CurrentForm->action = ParseMarkTag(mark->start, MT_INDEX, "ACTION"); CurrentForm->format = ParseMarkTag(mark->start, MT_INDEX, "FORMAT"); CurrentForm->method = ParseMarkTag(mark->start, MT_INDEX, "METHOD"); CurrentForm->enctype = ParseMarkTag(mark->start, MT_INDEX, "ENCTYPE"); CurrentForm->enc_entity = ParseMarkTag( mark->start, MT_INDEX, "ENCENTITY"); CurrentForm->start = WidgetId; CurrentForm->end = -1; /* * Horizontal rule */ ConditionalLineFeed(hw, x, y, 1); HRulePlace(hw, x, y, Width); ConditionalLineFeed(hw, x, y, 1); /* * Text: "This is a searchable index. * Enter search keywords: " */ mark_tmp.text = (char *)malloc( strlen("This is a searchable index. Enter search keywords: ") + 1); strcpy(mark_tmp.text,"This is a searchable index. Enter search keywords: "); FormatPlace(hw, &mark_tmp, x, y, Width); /* * Fake up the text INPUT tag. */ mark_tmp.start = (char *)malloc( strlen("input SIZE=25 NAME=\"isindex\"") + 1); strcpy(mark_tmp.start,"input SIZE=25 NAME=\"isindex\""); WidgetPlace(hw, &mark_tmp, x, y, Width); #ifdef ISINDEX_SUBMIT /* * Text: "; press this button to submit * the query: " */ mark_tmp.text = (char *)malloc( strlen(";\n press this button to submit the query: ") + 1); strcpy(mark_tmp.text,";\n press this button to submit the query: "); FormatPlace(hw, &mark_tmp, x, y, Width); /* * Fake up the submit INPUT tag. */ mark_tmp.start = (char *)malloc( strlen("input TYPE=\"submit\"") + 1); strcpy(mark_tmp.start, "input TYPE=\"submit\""); WidgetPlace(hw, &mark_tmp, x, y, Width); /* * Text: "." */ mark_tmp.text = (char *)malloc( strlen(".\n") + 1); strcpy(mark_tmp.text, ".\n"); FormatPlace(hw, &mark_tmp, x, y, Width); #endif /* ISINDEX_SUBMIT */ /* * Horizontal rule */ ConditionalLineFeed(hw, x, y, 1); HRulePlace(hw, x, y, Width); ConditionalLineFeed(hw, x, y, 1); /* * Close the form */ ConditionalLineFeed(hw, x, y, 1); CurrentForm->end = WidgetId; ConditionalLineFeed(hw, x, y, 2); AddNewForm(hw, CurrentForm); CurrentForm = NULL; } break; case M_HRULE: ConditionalLineFeed(hw, x, y, 1); HRulePlace(hw, x, y, Width); ConditionalLineFeed(hw, x, y, 1); break; case M_LINEBREAK: LineFeed(hw, x, y); break; case M_TABLE: if (tableSupportEnabled) { TablePlace(hw, mptr, x, y, Width); } break; case M_COMMENT: Ignore = !mark->is_end; break; default: break; } if ((font != NULL)&&(font != currentFont)) { NewFont(font); currentFont = font; } } /* TriggerMarkChanges() */ /* * Format all the objects in the passed Widget's * parsed object list to fit the locally global Width. * Passes in the x,y coords of where to start placing the * formatted text. * Returns the ending x,y in same variables. * Title objects are ignored, and not formatted. * * The locally global variables are assumed to have been initialized * before this function was called. */ void FormatChunk(hw, x, y) HTMLWidget hw; int *x, *y; { struct mark_up *mptr; /* * Format all objects */ mptr = hw->html.html_objects; Last = NULL; while (mptr != NULL) { TriggerMarkChanges(hw, &mptr, x, y); /* * Save last non-text mark */ /* DDT: why is this here? it's not used anywhere? */ if (mptr->type != M_NONE) { Last = mptr; } /*****/ if (mptr) { mptr = mptr->next; } } } /* * Called by the widget to format all the objects in the * parsed object list to fit its current window size. * Returns the max_height of the entire document. * Title objects are ignored, and not formatted. */ int FormatAll(hw, Fwidth) HTMLWidget hw; int *Fwidth; { int x, y; int width; struct mark_up *msave; #ifndef DISABLE_TRACE if (htmlwTrace) { #ifndef VMS gettimeofday(&Tv, &Tz); fprintf(stderr, "FormatAll enter (%d.%d)\n", Tv.tv_sec, Tv.tv_usec); #else fprintf(stderr, "FormatAll enter (%s)\n", asctime(localtime(&clock))); #endif } #endif width = *Fwidth; MaxWidth = width; /* * Clear the is_index flag */ hw->html.is_index = False; /* * Initialize local variables, some from the widget */ MarginW = hw->html.margin_width; /* * Without motif we use our own foreground resource instead of * using the manager's */ #ifdef MOTIF Fg = hw->manager.foreground; #else Fg = hw->html.foreground; #endif /* MOTIF */ Bg = hw->core.background_pixel; Underlines = 0; DashedUnderlines = False; Width = width; TextIndent = MarginW; ElementId = 0; WidgetId = 0; LineNumber = 1; LineBottom = 0; BaseLine = -100; CharsInLine = 0; IndentLevel = 0; Ignore = 0; Preformat = 0; PF_LF_State = 0; NeedSpace = 0; Internal = False; Strikeout = False; AnchorText = NULL; DescType = &BaseDesc; ListData = &BaseDesc; DescType->type = D_NONE; DescType->count = 0; DescType->compact = 0; DescType->next = NULL; CurrentForm = NULL; CurrentSelect = NULL; TextAreaBuf = NULL; Superscript = 0; /* amb */ Subscript = 0; InDocHead = 0; InUnderlined = 0; /* * Free the old title, if there is one. */ if (hw->html.title != NULL) { free(hw->html.title); hw->html.title = NULL; } TitleText = NULL; #ifdef THROW_AWAY_OLD_LIST /* * Free up previously formatted elements */ FreeLineList(hw->html.formatted_elements); hw->html.formatted_elements = NULL; #endif /* * Clear any previous selections */ hw->html.select_start = NULL; hw->html.select_end = NULL; hw->html.new_start = NULL; hw->html.new_end = NULL; /* * Set up a starting font, and starting x, y, position */ NewFont(hw->html.font); currentFont = hw->html.font; saveFont = NULL; FontStack = &FontBase; FontStack->font = hw->html.font; x = TextIndent; y = hw->html.margin_height; /* * Start a null element list, to be filled in as we go. */ Current = NULL; /* * If we have parsed special header text, fill it in now. */ if (hw->html.html_header_objects != NULL) { msave = hw->html.html_objects; hw->html.html_objects = hw->html.html_header_objects; FormatChunk(hw, &x, &y); if (saveFont != NULL) { hw->html.font = saveFont; saveFont = NULL; } NewFont(hw->html.font); currentFont = hw->html.font; ConditionalLineFeed(hw, &x, &y, 1); hw->html.html_objects = msave; } /* * Format all objects for width */ FormatChunk(hw, &x, &y); /* * If we have parsed special footer text, fill it in now. */ if (hw->html.html_footer_objects != NULL) { if (saveFont != NULL) { hw->html.font = saveFont; saveFont = NULL; } NewFont(hw->html.font); currentFont = hw->html.font; Preformat = 0; PF_LF_State = 0; NeedSpace = 0; ConditionalLineFeed(hw, &x, &y, 1); msave = hw->html.html_objects; hw->html.html_objects = hw->html.html_footer_objects; FormatChunk(hw, &x, &y); hw->html.html_objects = msave; } /* * Ensure a linefeed after the final element. */ ConditionalLineFeed(hw, &x, &y, 1); /* * Restore the proper font from unterminated preformatted text * sequences. */ if (saveFont != NULL) { hw->html.font = saveFont; saveFont = NULL; } /* * Free any extra of the pre-allocated list. * Terminate the element list. */ if ((Current != NULL)&&(Current->next != NULL)) { FreeLineList(Current->next); Current->next = NULL; } else if ((Current == NULL)&&(hw->html.formatted_elements != NULL)) { FreeLineList(hw->html.formatted_elements); hw->html.formatted_elements = NULL; } /* * Add the bottom margin to the max height. */ y = y + hw->html.margin_height; /* * Make the line array indexed into the element list * and store it into the widget */ hw->html.line_count = LineNumber; if (hw->html.line_array != NULL) { free((char *)hw->html.line_array); } hw->html.line_array = MakeLineList(hw->html.formatted_elements, LineNumber); /* * If the passed in MaxWidth was wrong, correct it. */ if (MaxWidth != width) { *Fwidth = MaxWidth; } #ifndef DISABLE_TRACE if (htmlwTrace) { #ifndef VMS gettimeofday(&Tv, &Tz); fprintf(stderr, "FormatAll exit (%d.%d)\n", Tv.tv_sec, Tv.tv_usec); #else fprintf(stderr, "FormatAll exit (%s)\n", asctime(localtime(&clock))); #endif } #endif return(y); } /* * Redraw a linefeed. * Basically a filled rectangle at the end of a line. */ void LinefeedRefresh(hw, eptr) HTMLWidget hw; struct ele_rec *eptr; { int x1, y1; unsigned int width, height; #ifdef NO_EXTRA_FILLS /* * The actualt height of the rectangle to fill is strange, based * an a differente between eptr->font->(ascent/descent) and * eptr->font->max_bounds.(ascent/descent) which I don't quite * understand. But it works. * Deal with bad Lucidia descents. */ x1 = eptr->x; if (x1 > (int)hw->core.width) { width = 0; } else { width = hw->core.width - x1; } #ifdef SHORT_LINEFEEDS y1 = eptr->y + eptr->y_offset + eptr->font->max_bounds.ascent - eptr->font->ascent; height = eptr->font->ascent + eptr->font->descent; #else y1 = eptr->y + eptr->font->max_bounds.ascent - eptr->font->ascent; height = eptr->line_height; #endif /* SHORT_LINEFEEDS */ #else x1 = eptr->x; if (x1 > (int)hw->core.width) { width = 0; } else { width = hw->core.width - x1; } #ifdef SHORT_LINEFEEDS y1 = eptr->y + eptr->y_offset; if (eptr->font->descent > eptr->font->max_bounds.descent) { height = eptr->font->max_bounds.ascent + eptr->font->descent; } else { height = eptr->font->max_bounds.ascent + eptr->font->max_bounds.descent; } #else y1 = eptr->y; height = eptr->line_height; #endif /* SHORT_LINEFEEDS */ #endif /* NO_EXTRA_FILLS */ x1 = x1 - hw->html.scroll_x; y1 = y1 - hw->html.scroll_y; if (eptr->selected == True) { XSetForeground(XtDisplay(hw), hw->html.drawGC, eptr->fg); } else { XSetForeground(XtDisplay(hw), hw->html.drawGC, eptr->bg); } /* Don't draw out past the end of the text if selecting */ if (eptr->selected == False) { if (hw->html.bg_image) { HTMLDrawBackgroundImage((Widget)hw, x1, y1, width, height); } else { XFillRectangle(XtDisplay(hw->html.view), XtWindow(hw->html.view), hw->html.drawGC, x1, y1, width, height); } } } /* * Redraw part of a formatted text element, in the passed fg and bg */ void PartialRefresh(hw, eptr, start_pos, end_pos, fg, bg) HTMLWidget hw; struct ele_rec *eptr; int start_pos, end_pos; unsigned long fg, bg; { int ascent; char *tdata; int tlen; int x, y, width; int partial, descent; unsigned long valuemask; XGCValues values; XSetFont(XtDisplay(hw), hw->html.drawGC, eptr->font->fid); ascent = eptr->font->max_bounds.ascent; width = -1; partial = 0; if (start_pos != 0) { int dir, nascent; XCharStruct all; #ifndef ASSUME_FIXED_WIDTH_PRE if (eptr->font == hw->html.plain_font) { all.width = eptr->font->max_bounds.width * start_pos; } else { XTextExtents(eptr->font, (char *)eptr->edata, start_pos, &dir, &nascent, &descent, &all); } #else XTextExtents(eptr->font, (char *)eptr->edata, start_pos, &dir, &nascent, &descent, &all); #endif /* ASSUME_FIXED_WIDTH_PRE */ x = eptr->x + all.width; tdata = (char *)(eptr->edata + start_pos); partial = 1; } else { x = eptr->x; tdata = (char *)eptr->edata; } if (end_pos != (eptr->edata_len - 2)) { tlen = end_pos - start_pos + 1; partial = 1; } else { tlen = eptr->edata_len - start_pos - 1; } y = eptr->y + eptr->y_offset; x = x - hw->html.scroll_x; y = y - hw->html.scroll_y; #ifndef NO_EXTRA_FILLS { int dir, nascent, descent; XCharStruct all; int height; /* * May be safe to used the cached full width of this * string, and thus avoid a call to XTextExtents */ if ((!partial)&&(eptr->width != 0)) { all.width = eptr->width; } else { #ifdef ASSUME_FIXED_WIDTH_PRE if (eptr->font == hw->html.plain_font) { all.width = eptr->font->max_bounds.width * tlen; } else { XTextExtents(eptr->font, (char *)tdata, tlen, &dir, &nascent, &descent, &all); } #else XTextExtents(eptr->font, (char *)tdata, tlen, &dir, &nascent, &descent, &all); #endif /* ASSUME_FIXED_WIDTH_PRE */ } XSetForeground(XtDisplay(hw), hw->html.drawGC, bg); height = (eptr->font->max_bounds.ascent - eptr->font->ascent); if (height > 0) { if(!hw->html.bg_image) XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, y, (unsigned int)all.width, (unsigned int)height); } height = (eptr->font->max_bounds.descent - eptr->font->descent); if (height > 0) { if(!hw->html.bg_image) XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, (int)(y + eptr->font->max_bounds.ascent + eptr->font->descent), (unsigned int)all.width, (unsigned int)height); } width = all.width; } #endif /* NO_EXTRA_FILLS */ if (bg!=hw->html.view->core.background_pixel || NoBodyImages(hw) || !hw->html.bg_image) { XSetForeground(XtDisplay(hw), hw->html.drawGC, bg); XSetBackground(XtDisplay(hw), hw->html.drawGC, fg); XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, y, width, ascent+eptr->font->descent); XSetForeground(XtDisplay(hw), hw->html.drawGC, fg); XSetBackground(XtDisplay(hw), hw->html.drawGC, bg); XDrawString(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, y + ascent, (char *)tdata, tlen); } else { XSetForeground(XtDisplay(hw), hw->html.drawGC, bg); XSetBackground(XtDisplay(hw), hw->html.drawGC, fg); XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, y, width, ascent+eptr->font->descent); XSetForeground(XtDisplay(hw), hw->html.drawGC, fg); XSetBackground(XtDisplay(hw), hw->html.drawGC, bg); HTMLDrawBackgroundImage((Widget)hw, (x<0 ? 0 : x), (y<0 ? 0 : y), (x<0 ? (width+x) : width), (y<0 ? (ascent+eptr->font->descent+y) : (ascent+eptr->font->descent))); XDrawString(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, y + ascent, (char *)tdata, tlen); } if (eptr->underline_number) { int i, ly; if (eptr->dashed_underline) { XSetLineAttributes(XtDisplay(hw), hw->html.drawGC, 1, LineOnOffDash, CapButt, JoinBevel); } else { XSetLineAttributes(XtDisplay(hw), hw->html.drawGC, 1, LineSolid, CapButt, JoinBevel); } if (width == -1) { int dir, nascent, descent; XCharStruct all; #ifdef ASSUME_FIXED_WIDTH_PRE if (eptr->font == hw->html.plain_font) { all.width = eptr->font->max_bounds.width * tlen; } else { XTextExtents(eptr->font, (char *)tdata, tlen, &dir, &nascent, &descent,&all); } #else XTextExtents(eptr->font, (char *)tdata, tlen, &dir, &nascent, &descent,&all); #endif /* ASSUME_FIXED_WIDTH_PRE */ width = all.width; } ly = (int)(y + eptr->font->max_bounds.ascent + eptr->font->descent - 1); for (i=0; iunderline_number; i++) { XDrawLine(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, ly, (int)(x + width), ly); ly -= 2; } } if (eptr->strikeout == True) { int ly; XSetLineAttributes(XtDisplay(hw), hw->html.drawGC, 1, LineSolid, CapButt, JoinBevel); if (width == -1) { int dir, nascent, descent; XCharStruct all; #ifdef ASSUME_FIXED_WIDTH_PRE if (eptr->font == hw->html.plain_font) { all.width = eptr->font->max_bounds.width * tlen; } else { XTextExtents(eptr->font, (char *)tdata, tlen, &dir, &nascent, &descent,&all); } #else XTextExtents(eptr->font, (char *)tdata, tlen, &dir, &nascent, &descent,&all); #endif /* ASSUME_FIXED_WIDTH_PRE */ width = all.width; } ly = (int)(y + eptr->font->max_bounds.ascent + eptr->font->descent - 1); ly = ly - ((hw->html.font->max_bounds.ascent + hw->html.font->descent) / 2); XDrawLine(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, ly, (int)(x + width), ly); } } /* * Redraw a formatted text element */ void TextRefresh(hw, eptr, start_pos, end_pos) HTMLWidget hw; struct ele_rec *eptr; int start_pos, end_pos; { if (eptr->selected == False) { PartialRefresh(hw, eptr, start_pos, end_pos, eptr->fg, eptr->bg); } else if ((start_pos >= eptr->start_pos)&&(end_pos <= eptr->end_pos)) { PartialRefresh(hw, eptr, start_pos, end_pos, eptr->bg, eptr->fg); } else { if (start_pos < eptr->start_pos) { PartialRefresh(hw, eptr, start_pos, eptr->start_pos - 1, eptr->fg, eptr->bg); start_pos = eptr->start_pos; } if (end_pos > eptr->end_pos) { PartialRefresh(hw, eptr, eptr->end_pos + 1, end_pos, eptr->fg, eptr->bg); end_pos = eptr->end_pos; } PartialRefresh(hw, eptr, start_pos, end_pos, eptr->bg, eptr->fg); } } /* * Redraw a formatted bullet element */ void BulletRefresh(hw, eptr) HTMLWidget hw; struct ele_rec *eptr; { int width, line_height; int x1, y1; /* width = eptr->font->max_bounds.width; */ width = eptr->font->max_bounds.lbearing + eptr->font->max_bounds.rbearing; /* * Deal with bad Lucidia descents. */ if (eptr->font->descent > eptr->font->max_bounds.descent) { line_height = eptr->font->max_bounds.ascent + eptr->font->descent; } else { line_height = eptr->font->max_bounds.ascent + eptr->font->max_bounds.descent; } x1 = eptr->x; y1 = eptr->y + eptr->y_offset + (line_height / 2) - (width / 4); x1 = x1 - hw->html.scroll_x; y1 = y1 - hw->html.scroll_y; XSetFont(XtDisplay(hw), hw->html.drawGC, eptr->font->fid); XSetForeground(XtDisplay(hw), hw->html.drawGC, eptr->fg); XSetBackground(XtDisplay(hw), hw->html.drawGC, eptr->bg); /* * Rewritten to alternate circle, square and those paris alternate filled * and not filled. * --SWP */ if (eptr->indent_level && (eptr->indent_level % 2)) { /* odd & !0 */ XSetLineAttributes(XtDisplay(hw), hw->html.drawGC, 1, LineSolid, CapButt, JoinBevel); if (eptr->indent_level==1 || (eptr->indent_level % 4)==1) { XFillArc(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, (x1 - width), y1, (width / 2), (width / 2), 0, 23040); } else { XDrawArc(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, (x1 - width), y1, (width / 2), (width / 2), 0, 23040); } } else { /* even */ XSetLineAttributes(XtDisplay(hw), hw->html.drawGC, 1, LineSolid, CapButt, JoinBevel); if (eptr->indent_level==0 || (eptr->indent_level % 4)==2) { XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, (x1 - width), y1, (width / 2), (width / 2)); } else { XDrawRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, (x1 - width), y1, (width / 2), (width / 2)); } } } /* * Redraw a formatted horizontal rule element */ void HRuleRefresh(hw, eptr) HTMLWidget hw; struct ele_rec *eptr; { int width, height; int x1, y1; width = (int)hw->html.view_width - (int)(2 * hw->html.margin_width); if (width < 0) { width = 0; } x1 = eptr->x; y1 = eptr->y; x1 = x1 - hw->html.scroll_x; y1 = y1 - hw->html.scroll_y; height = eptr->line_height; /* blank out area */ XSetForeground(XtDisplay(hw), hw->html.drawGC, eptr->bg); if(!hw->html.bg_image) XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x1, y1, width, height); y1 = y1 + (height / 2) - 1; XSetLineAttributes(XtDisplay(hw), hw->html.drawGC, 1, LineSolid, CapButt, JoinBevel); #ifdef MOTIF XDrawLine(XtDisplay(hw), XtWindow(hw->html.view), hw->manager.bottom_shadow_GC, x1, y1, (int)(x1 + width), y1); XDrawLine(XtDisplay(hw), XtWindow(hw->html.view), hw->manager.top_shadow_GC, x1, y1 + 1, (int)(x1 + width), y1 + 1); #else /* changing the GC back and forth is not the most efficient way.... */ XSetForeground(XtDisplay(hw), hw->html.drawGC, eptr->fg); XDrawLine(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x1, y1, (int)(x1 + width), y1); XDrawLine(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x1, y1 + 1, (int)(x1 + width), y1 + 1); #endif } /* * Redraw a formatted image element. * The color of the image border reflects whether it is an active anchor * or not. * Actual Pixmap creation was put off until now to make sure we * had a window. If it hasn't been already created, make the Pixmap * now. */ void ImageRefresh(hw, eptr) HTMLWidget hw; struct ele_rec *eptr; { unsigned long valuemask; XGCValues values; if (eptr->pic_data != NULL) { int x, y, extra; x = eptr->x; y = eptr->y + eptr->y_offset; if ((hw->html.border_images == True)|| ((eptr->anchorHRef != NULL)&& (!eptr->pic_data->internal))) { if (eptr->pic_data->delayed) { extra=2; } else { extra = eptr->bwidth; } } else { extra = 0; } x = x - hw->html.scroll_x; y = y - hw->html.scroll_y; XSetForeground(XtDisplay(hw), hw->html.drawGC, eptr->fg); XSetBackground(XtDisplay(hw), hw->html.drawGC, eptr->bg); if (extra) { XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, y, (eptr->pic_data->width + (2 * extra)), extra); XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, (y + eptr->pic_data->height + extra), (eptr->pic_data->width + (2 * extra)), extra); XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, x, y, extra, (eptr->pic_data->height + (2 * extra))); XFillRectangle(XtDisplay(hw), XtWindow(hw->html.view), hw->html.drawGC, (x + eptr->pic_data->width + extra), y, extra, (eptr->pic_data->height + (2 * extra))); } if (eptr->pic_data->image == None) { if (eptr->pic_data->image_data != NULL) { eptr->pic_data->image = InfoToImage(hw, eptr->pic_data, 0); if (eptr->pic_data->transparent && eptr->pic_data->clip==None) { eptr->pic_data->clip = XCreatePixmapFromBitmapData (XtDisplay(hw), XtWindow(hw->html.view), eptr->pic_data->clip_data, eptr->pic_data->width, eptr->pic_data->height, 1, 0, 1); } else if (!eptr->pic_data->transparent) { eptr->pic_data->clip = None; } } else { if (eptr->pic_data->delayed) { if ((eptr->anchorHRef != NULL)&& (!IsDelayedHRef(hw, eptr->anchorHRef))&& (!IsIsMapForm(hw, eptr->anchorHRef))) { eptr->pic_data->image = DelayedImage( hw, True); } else { eptr->pic_data->image = DelayedImage( hw, False); } } else { /* * Could be that the user opened another * window, and the Pixmap was freed, and * then they overflowed the cache, * and the XImage data was freed. * If this image was ever successfully * fetched, try again before giving up. */ if ((eptr->pic_data->fetched)&& (hw->html.resolveDelayedImage != NULL)) { ImageInfo *pdata; pdata = eptr->pic_data; eptr->pic_data = (*(resolveImageProc) (hw->html.resolveDelayedImage))(hw, eptr->edata); if (eptr->pic_data != NULL) { eptr->pic_data->delayed = 0; /* * Mark images we have sucessfully * loaded at least once */ if (eptr->pic_data->image_data != NULL) { eptr->pic_data->fetched = 1; } /* * Copy over state information from * the last time we had this image */ eptr->pic_data->ismap = pdata->ismap; eptr->pic_data->fptr = pdata->fptr; eptr->pic_data->internal = pdata->internal; eptr->pic_data->text = pdata->text; } else { eptr->pic_data = NoImageData(hw); eptr->pic_data->delayed = 0; eptr->pic_data->internal = 0; } } else { eptr->pic_data->image = NoImage(hw); } } } } if (eptr->pic_data->image != None) { if (eptr->pic_data->transparent) { unsigned long valuemask; XGCValues values; values.clip_mask=eptr->pic_data->clip; values.clip_x_origin=x+extra; values.clip_y_origin=y+extra; valuemask=GCClipMask|GCClipXOrigin|GCClipYOrigin; XChangeGC(XtDisplay(hw), hw->html.drawGC, valuemask, &values); XCopyArea(XtDisplay(hw), eptr->pic_data->image, XtWindow(hw->html.view), hw->html.drawGC, 0, 0, eptr->pic_data->width, eptr->pic_data->height, (x + extra), (y + extra)); values.clip_mask=None; values.clip_x_origin=0; values.clip_y_origin=0; valuemask=GCClipMask|GCClipXOrigin|GCClipYOrigin; XChangeGC(XtDisplay(hw), hw->html.drawGC, valuemask, &values); } else { values.clip_mask=None; values.clip_x_origin=0; values.clip_y_origin=0; valuemask=GCClipMask|GCClipXOrigin|GCClipYOrigin; XChangeGC(XtDisplay(hw), hw->html.drawGC, valuemask, &values); XCopyArea(XtDisplay(hw), eptr->pic_data->image, XtWindow(hw->html.view), hw->html.drawGC, 0, 0, eptr->pic_data->width, eptr->pic_data->height, (x + extra), (y + extra)); } } if ((eptr->pic_data->delayed)&&(eptr->anchorHRef != NULL)&& (!IsDelayedHRef(hw, eptr->anchorHRef))&& (!IsIsMapForm(hw, eptr->anchorHRef))) { XSetForeground(XtDisplay(hw), hw->html.drawGC, eptr->fg); XFillRectangle(XtDisplay(hw->html.view), XtWindow(hw->html.view), hw->html.drawGC, x, (y + AnchoredHeight(hw)), (eptr->pic_data->width + (2 * extra)), extra); } } } void RefreshTextRange(hw, start, end) HTMLWidget hw; struct ele_rec *start; struct ele_rec *end; { struct ele_rec *eptr; eptr = start; while ((eptr != NULL)&&(eptr != end)) { if (eptr->type == E_TEXT) { TextRefresh(hw, eptr, 0, (eptr->edata_len - 2)); } eptr = eptr->next; } if (eptr != NULL) { if (eptr->type == E_TEXT) { TextRefresh(hw, eptr, 0, (eptr->edata_len - 2)); } } } /* * Refresh all elements on a single line into the widget's window */ void PlaceLine(hw, line) HTMLWidget hw; int line; { struct ele_rec *eptr; XGCValues values; /* * Item list for this line */ eptr = hw->html.line_array[line]; while ((eptr != NULL)&&(eptr->line_number == (line + 1))) { switch(eptr->type) { case E_TEXT: TextRefresh(hw, eptr, 0, (eptr->edata_len - 2)); break; case E_BULLET: BulletRefresh(hw, eptr); break; case E_HRULE: HRuleRefresh(hw, eptr); break; case E_LINEFEED: if(!hw->html.bg_image) LinefeedRefresh(hw, eptr); break; case E_IMAGE: ImageRefresh(hw, eptr); break; case E_WIDGET: WidgetRefresh(hw, eptr); break; case E_TABLE: TableRefresh(hw, eptr); break; } eptr = eptr->next; } } /* * Locate the element (if any) that is at the passed location * in the widget. If there is no corresponding element, return * NULL. If an element is found return the position of the character * you are at in the pos pointer passed. */ struct ele_rec * LocateElement(hw, x, y, pos) HTMLWidget hw; int x, y; int *pos; { struct ele_rec *eptr; struct ele_rec *rptr; int i, start, end, line, guess; int tx1, tx2, ty1, ty2; x = x + hw->html.scroll_x; y = y + hw->html.scroll_y; /* * Narrow the search down to a 2 line range * before beginning to search element by element */ start = -1; end = -1; /* * Heuristic to speed up redraws by guessing at the starting line. */ guess = y / (hw->html.font->max_bounds.ascent + hw->html.font->max_bounds.descent); if (guess > (hw->html.line_count - 1)) { guess = hw->html.line_count - 1; } while (guess > 0) { if ((hw->html.line_array[guess] != NULL)&& (hw->html.line_array[guess]->y <= y)) { break; } guess--; } if (guess < 0) { guess = 0; } for (i=guess; ihtml.line_count; i++) { if (hw->html.line_array[i] == NULL) { continue; } else if (hw->html.line_array[i]->y <= y) { start = i; continue; } else { end = i; break; } } /* * Search may have already failed, or it may be a one line * range. */ if ((start == -1)&&(end == -1)) { return(NULL); } else if (start == -1) { start = end; } else if (end == -1) { end = start; } /* * Search element by element, for now we only search * text elements, images, and linefeeds. */ eptr = hw->html.line_array[start]; ty1 = eptr->y; /* * Deal with bad Lucidia descents. */ if (eptr->font->descent > eptr->font->max_bounds.descent) { ty2 = eptr->y + eptr->font->max_bounds.ascent + eptr->font->descent; } else { ty2 = eptr->y + eptr->font->max_bounds.ascent + eptr->font->max_bounds.descent; } line = eptr->line_number; /* * Searches on this line should extend to the top of the * next line, if possible. Which might be far away if there * is an image on this line. */ if (((line + 1) < hw->html.line_count)&& (hw->html.line_array[line + 1] != NULL)) { ty2 = hw->html.line_array[line + 1]->y - 1; } /* * Else we are at the last line, and need to find its height. * The linefeed at the end should know the max height of the line. */ else { struct ele_rec *teptr; teptr = eptr; while (teptr != NULL) { if (teptr->type == E_LINEFEED) { break; } teptr = teptr->next; } if (teptr != NULL) { ty2 = teptr->y + teptr->line_height - 1; } } rptr = NULL; while ((eptr != NULL)&&(eptr->line_number <= (end + 1))) { if (eptr->line_number != line) { ty1 = ty2; /* * Deal with bad Lucidia descents. */ if(eptr->font->descent > eptr->font->max_bounds.descent) { ty2 = eptr->y + eptr->font->max_bounds.ascent + eptr->font->descent; } else { ty2 = eptr->y + eptr->font->max_bounds.ascent + eptr->font->max_bounds.descent; } line = eptr->line_number; /* * Searches on this line should extend to the top of * the next line, if possible. Which might be far * away if there is an image on this line. */ if (((line + 1) < hw->html.line_count)&& (hw->html.line_array[line + 1] != NULL)) { ty2 = hw->html.line_array[line + 1]->y - 1; } /* * Else we are at the last line, and need to find its * height. The linefeed at the end should know the * max height of the line. */ else { struct ele_rec *teptr; teptr = eptr; while (teptr != NULL) { if (teptr->type == E_LINEFEED) { break; } teptr = teptr->next; } if (teptr != NULL) { ty2 = teptr->y + teptr->line_height - 1; } } } if (eptr->type == E_TEXT) { int dir, ascent, descent; XCharStruct all; tx1 = eptr->x; XTextExtents(eptr->font, (char *)eptr->edata, eptr->edata_len - 1, &dir, &ascent, &descent, &all); tx2 = eptr->x + all.width; if ((x >= tx1)&&(x <= tx2)&&(y >= ty1)&&(y <= ty2)) { rptr = eptr; break; } } else if ((eptr->type == E_IMAGE)&&(eptr->pic_data != NULL)) { tx1 = eptr->x; tx2 = eptr->x + eptr->pic_data->width; if ((x >= tx1)&&(x <= tx2)&&(y >= ty1)&&(y <= ty2)) { rptr = eptr; break; } } else if (eptr->type == E_LINEFEED) { tx1 = eptr->x; if ((x >= tx1)&&(y >= ty1)&&(y <= ty2)) { rptr = eptr; break; } else if (eptr->next == NULL) { rptr = eptr; break; } else if (eptr->next != NULL) { int tmpy; tmpy = eptr->next->y + eptr->next->line_height; tx2 = eptr->next->x; if ((x < tx2)&&(y >= ty2)&&(y <= tmpy)) { rptr = eptr; break; } } } eptr = eptr->next; } /* * If we found an element, locate the exact character position within * that element. */ if (rptr != NULL) { int dir, ascent, descent; XCharStruct all; int epos; /* * Start assuming fixed width font. The real position should * always be <= to this, but just in case, start at the end * of the string if it is not. */ epos = ((x - rptr->x) / rptr->font->max_bounds.width) + 1; if (epos >= rptr->edata_len - 1) { epos = rptr->edata_len - 2; } XTextExtents(rptr->font, (char *)rptr->edata, (epos + 1), &dir, &ascent, &descent, &all); if (x > (int)(rptr->x + all.width)) { epos = rptr->edata_len - 3; } else { epos--; } while (epos >= 0) { XTextExtents(rptr->font, (char *)rptr->edata, (epos + 1), &dir, &ascent, &descent, &all); if ((int)(rptr->x + all.width) <= x) { break; } epos--; } epos++; *pos = epos; } return(rptr); } /* * Used by ParseTextToPrettyString to let it be sloppy about its * string creation, and never overflow the buffer. * It concatonates the passed string to the current string, managing * both the current string length, and the total buffer length. */ void strcpy_or_grow(str, slen, blen, add) char **str; int *slen; int *blen; char *add; { int newlen; int addlen; char *buf; /* * If necessary, initialize this string buffer */ if (*str == NULL) { *str = (char *)malloc(1024 * sizeof(char)); if (*str == NULL) { return; } *blen = 1024; strcpy(*str, ""); *slen = 0; } buf = *str; if ((buf == NULL)||(add == NULL)) { return; } addlen = strlen(add); newlen = *slen + addlen; if (newlen >= *blen) { newlen = ((newlen / 1024) + 1) * 1024; buf = (char *)malloc(newlen * sizeof(char)); if (buf == NULL) { return; } /* bcopy(*str, buf, *blen); */ memcpy(buf, *str, *blen); free((char *)*str); *str = buf; *blen = newlen; } /* bcopy(add, (char *)(buf + *slen), addlen + 1); */ memcpy((char *)(buf + *slen), add, addlen + 1); *slen = *slen + addlen; } /* * Parse all the formatted text elements from start to end * into an ascii text string, and return it. * space_width and lmargin tell us how many spaces * to indent lines. */ char * ParseTextToString(elist, startp, endp, start_pos, end_pos, space_width, lmargin) struct ele_rec *elist; struct ele_rec *startp; struct ele_rec *endp; int start_pos, end_pos; int space_width; int lmargin; { int newline; int epos; char *text; int t_slen, t_blen; struct ele_rec *eptr; struct ele_rec *start; struct ele_rec *end; if (startp == NULL) { return(NULL); } if (SwapElements(startp, endp, start_pos, end_pos)) { start = endp; end = startp; epos = start_pos; start_pos = end_pos; end_pos = epos; } else { start = startp; end = endp; } text = NULL; newline = 0; eptr = start; while ((eptr != NULL)&&(eptr != end)) { /* * Skip the special internal text */ if (eptr->internal == True) { eptr = eptr->next; continue; } if (eptr->type == E_TEXT) { int i, spaces; char *tptr; if (eptr == start) { tptr = (char *)(eptr->edata + start_pos); } else { tptr = (char *)eptr->edata; } if (newline) { spaces = (eptr->x - lmargin) / space_width; if (spaces < 0) { spaces = 0; } for (i=0; itype == E_LINEFEED) { strcpy_or_grow(&text, &t_slen, &t_blen, "\n"); newline = 1; } eptr = eptr->next; } if ((eptr != NULL)&&(eptr->internal == False)) { if (eptr->type == E_TEXT) { int i, spaces; char *tptr; char *tend, tchar; if (eptr == start) { tptr = (char *)(eptr->edata + start_pos); } else { tptr = (char *)eptr->edata; } if (eptr == end) { tend = (char *)(eptr->edata + end_pos + 1); tchar = *tend; *tend = '\0'; } if (newline) { spaces = (eptr->x - lmargin) / space_width; if (spaces < 0) { spaces = 0; } for (i=0; itype == E_LINEFEED) { strcpy_or_grow(&text, &t_slen, &t_blen, "\n"); newline = 1; } } return(text); } /* * Parse all the formatted text elements from start to end * into an ascii text string, and return it. * Very like ParseTextToString() except the text is prettied up * to show headers and the like. * space_width and lmargin tell us how many spaces * to indent lines. */ char * ParseTextToPrettyString(hw, elist, startp, endp, start_pos, end_pos, space_width, lmargin) HTMLWidget hw; struct ele_rec *elist; struct ele_rec *startp; struct ele_rec *endp; int start_pos, end_pos; int space_width; int lmargin; { int line; int newline; int lead_spaces; int epos; char *text; int t_slen, t_blen; char *line_buf; int l_slen, l_blen; char lchar; struct ele_rec *eptr; struct ele_rec *start; struct ele_rec *end; struct ele_rec *last; if (startp == NULL) { return(NULL); } if (SwapElements(startp, endp, start_pos, end_pos)) { start = endp; end = startp; epos = start_pos; start_pos = end_pos; end_pos = epos; } else { start = startp; end = endp; } text = NULL; line_buf = NULL; /* * We need to know if we should consider the indentation or bullet * that might be just before the first selected element to also be * selected. This current hack looks to see if they selected the * Whole line, and assumes if they did, they also wanted the beginning. * * If we are at the beginning of the list, or the beginning of * a line, or just behind a bullett, assume this is the start of * a line that we may want to include the indent for. */ if ((start_pos == 0)&& ((start->prev == NULL)||(start->prev->type == E_BULLET)|| (start->prev->line_number != start->line_number))) { eptr = start; while ((eptr != NULL)&&(eptr != end)&& (eptr->type != E_LINEFEED)) { eptr = eptr->next; } if ((eptr != NULL)&&(eptr->type == E_LINEFEED)) { newline = 1; if ((start->prev != NULL)&& (start->prev->type == E_BULLET)) { start = start->prev; } } else { newline = 0; } } else { newline = 0; } lead_spaces = 0; last = start; eptr = start; line = eptr->line_number; while ((eptr != NULL)&&(eptr != end)) { /* * Skip the special internal text */ if (eptr->internal == True) { eptr = eptr->next; continue; } if (eptr->type == E_BULLET) { int i, spaces; if (newline) { spaces = (eptr->x - lmargin) / space_width; spaces -= 2; if (spaces < 0) { spaces = 0; } lead_spaces = spaces; for (i=0; itype == E_TEXT) { int i, spaces; char *tptr; if (eptr == start) { tptr = (char *)(eptr->edata + start_pos); } else { tptr = (char *)eptr->edata; } if (newline) { spaces = (eptr->x - lmargin) / space_width; if (spaces < 0) { spaces = 0; } lead_spaces = spaces; for (i=0; itype == E_LINEFEED) { strcpy_or_grow(&text, &t_slen, &t_blen, line_buf); strcpy_or_grow(&text, &t_slen, &t_blen, "\n"); newline = 1; lchar = '\0'; if (eptr->font == hw->html.header1_font) { lchar = '*'; } else if (eptr->font == hw->html.header2_font) { lchar = '='; } else if (eptr->font == hw->html.header3_font) { lchar = '+'; } else if (eptr->font == hw->html.header4_font) { lchar = '-'; } else if (eptr->font == hw->html.header5_font) { lchar = '~'; } else if (eptr->font == hw->html.header6_font) { lchar = '.'; } if (lchar != '\0') { char *ptr; int cnt; cnt = 0; ptr = line_buf; while ((ptr != NULL)&&(*ptr != '\0')) { cnt++; if (cnt > lead_spaces) { *ptr = lchar; } ptr++; } strcpy_or_grow(&text,&t_slen,&t_blen, line_buf); strcpy_or_grow(&text, &t_slen, &t_blen, "\n"); } if (line_buf != NULL) { free(line_buf); line_buf = NULL; } } last = eptr; eptr = eptr->next; } if ((eptr != NULL)&&(eptr->internal == False)) { if (eptr->type == E_BULLET) { int i, spaces; if (newline) { spaces = (eptr->x - lmargin) / space_width; spaces -= 2; if (spaces < 0) { spaces = 0; } lead_spaces = spaces; for (i=0; itype == E_TEXT) { int i, spaces; char *tptr; char *tend, tchar; if (eptr == start) { tptr = (char *)(eptr->edata + start_pos); } else { tptr = (char *)eptr->edata; } if (eptr == end) { tend = (char *)(eptr->edata + end_pos + 1); tchar = *tend; *tend = '\0'; } if (newline) { spaces = (eptr->x - lmargin) / space_width; if (spaces < 0) { spaces = 0; } lead_spaces = spaces; for (i=0; itype == E_LINEFEED) { strcpy_or_grow(&text, &t_slen, &t_blen, line_buf); strcpy_or_grow(&text, &t_slen, &t_blen, "\n"); newline = 1; lchar = '\0'; if (eptr->font == hw->html.header1_font) { lchar = '*'; } else if (eptr->font == hw->html.header2_font) { lchar = '='; } else if (eptr->font == hw->html.header3_font) { lchar = '+'; } else if (eptr->font == hw->html.header4_font) { lchar = '-'; } else if (eptr->font == hw->html.header5_font) { lchar = '~'; } else if (eptr->font == hw->html.header6_font) { lchar = '.'; } if (lchar != '\0') { char *ptr; int cnt; cnt = 0; ptr = line_buf; while ((ptr != NULL)&&(*ptr != '\0')) { cnt++; if (cnt > lead_spaces) { *ptr = lchar; } ptr++; } strcpy_or_grow(&text,&t_slen,&t_blen, line_buf); strcpy_or_grow(&text, &t_slen, &t_blen, "\n"); } if (line_buf != NULL) { free(line_buf); line_buf = NULL; } } last = eptr; } if (line_buf != NULL) { strcpy_or_grow(&text, &t_slen, &t_blen, line_buf); lchar = '\0'; if (last->font == hw->html.header1_font) { lchar = '*'; } else if (last->font == hw->html.header2_font) { lchar = '='; } else if (last->font == hw->html.header3_font) { lchar = '+'; } else if (last->font == hw->html.header4_font) { lchar = '-'; } else if (last->font == hw->html.header5_font) { lchar = '~'; } else if (last->font == hw->html.header6_font) { lchar = '.'; } if (lchar != '\0') { char *ptr; int cnt; cnt = 0; ptr = line_buf; while ((ptr != NULL)&&(*ptr != '\0')) { cnt++; if (cnt > lead_spaces) { *ptr = lchar; } ptr++; } strcpy_or_grow(&text, &t_slen, &t_blen, "\n"); strcpy_or_grow(&text, &t_slen, &t_blen, line_buf); } } if (line_buf != NULL) { free(line_buf); line_buf = NULL; } return(text); } /* * Find the preferred width of a parsed HTML document * Currently unformatted plain text, unformatted listing text, plain files * and preformatted text require special width. * Preferred width = (width of longest plain text line in document) * * (width of that text's font) */ int DocumentWidth(hw, list) HTMLWidget hw; struct mark_up *list; { struct mark_up *mptr; int plain_text; int listing_text; int pcnt, lcnt, pwidth, lwidth; int width; char *ptr; /* * Loop through object list looking at the plain, preformatted, * and listing text */ width = 0; pwidth = 0; lwidth = 0; plain_text = 0; listing_text = 0; mptr = list; while (mptr != NULL) { /* * All text blocks between the starting and ending * plain and pre text markers are plain text blocks. * Manipulate flags so we recognize these blocks. */ if ((mptr->type == M_PLAIN_TEXT)|| (mptr->type == M_PLAIN_FILE)|| (mptr->type == M_PREFORMAT)) { if (mptr->is_end) { plain_text--; if (plain_text < 0) { plain_text = 0; } } else { plain_text++; } pcnt = 0; lcnt = 0; } /* * All text blocks between the starting and ending * listing markers are listing text blocks. */ else if (mptr->type == M_LISTING_TEXT) { if (mptr->is_end) { listing_text--; if (listing_text < 0) { listing_text = 0; } } else { listing_text++; } lcnt = 0; pcnt = 0; } /* * If this is a plain text block, add to line length. * Find the Max of all line lengths. */ else if ((plain_text)&&(mptr->type == M_NONE)) { ptr = mptr->text; while ((ptr != NULL)&&(*ptr != '\0')) { ptr = MaxTextWidth(ptr, &pcnt); if (pcnt > pwidth) { pwidth = pcnt; } } } /* * If this is a listing text block, add to line length. * Find the Max of all line lengths. */ else if ((listing_text)&&(mptr->type == M_NONE)) { ptr = mptr->text; while ((ptr != NULL)&&(*ptr != '\0')) { ptr = MaxTextWidth(ptr, &lcnt); if (lcnt > lwidth) { lwidth = lcnt; } } } mptr = mptr->next; } width = pwidth * hw->html.plain_font->max_bounds.width; lwidth = lwidth * hw->html.listing_font->max_bounds.width; if (lwidth > width) { width = lwidth; } return(width); }