/**************************************************************************** * 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 #include "HTMLP.h" #ifdef MOTIF #include #include #else #include "DrawingArea.h" #include #endif #include #define MARGIN_DEFAULT 20 #define CLICK_TIME 500 #define SELECT_THRESHOLD 3 #define MAX_UNDERLINES 3 #define DEFAULT_INCREMENT 18 #ifndef ABS #define ABS(x) (((x) > 0) ? (x) : ((x) * -1)) #endif #define W_TEXTFIELD 0 #define W_CHECKBOX 1 #define W_RADIOBOX 2 #define W_PUSHBUTTON 3 #define W_PASSWORD 4 #define W_OPTIONMENU 5 extern int FormatAll(); extern int DocumentWidth(); extern void PlaceLine(); extern void TextRefresh(); extern void ImageRefresh(); extern void LinefeedRefresh(); extern void RefreshTextRange(); extern void FreeColors(); extern void FreeImages(); extern void HideWidgets(); extern void MapWidgets(); extern int SwapElements(); extern int ElementLessThan(); extern int IsDelayedHRef(); extern int IsIsMapForm(); extern int AnchoredHeight(); extern char *ParseMarkTag(); extern char *ParseTextToString(); extern char *ParseTextToPrettyString(); extern char *ParseTextToPSString(); extern struct mark_up *HTMLParse(); extern struct ele_rec *LocateElement(); extern struct ele_rec **MakeLineList(); extern void FreeHRefs(); extern struct ref_rec *AddHRef(); extern void FreeDelayedImages(); extern struct delay_rec *AddDelayedImage(); extern ImageInfo *NoImageData(); extern void ImageSubmitForm(); static void SelectStart(); static void ExtendStart(); static void ExtendAdjust(); static void ExtendEnd(); static void TrackMotion(); static Boolean ConvertSelection(); static void LoseSelection(); static void SelectionDone(); static void Scroll(); #ifdef _NO_PROTO static void _HTMLInput() ; #ifndef MOTIF static void _HTMLpwdInput() ; #endif static void Initialize() ; static void Realize() ; static void Redisplay() ; static void Resize() ; static Boolean SetValues() ; static XtGeometryResult GeometryManager() ; static void RecolorInternalHRefs() ; static Dimension VbarWidth(); static Dimension HbarHeight(); static void ViewRedisplay(); static void ViewClearAndRefresh(); static void CallLinkCallbacks(); #else /* _NO_PROTO */ static void _HTMLInput(Widget w, XEvent *event, String *params, Cardinal *num_params); #ifndef MOTIF static void _HTMLpwdInput(Widget w, XEvent *event, String *params, Cardinal *num_params); #endif static void Initialize(HTMLWidget request, HTMLWidget new); static void Realize(HTMLWidget hw, Mask *valueMask, XSetWindowAttributes *attributes); static void Redisplay(HTMLWidget hw, XEvent *event, Region region); static void Resize(HTMLWidget hw); static Boolean SetValues(HTMLWidget current, HTMLWidget request, HTMLWidget new); static XtGeometryResult GeometryManager(Widget w, XtWidgetGeometry *request, XtWidgetGeometry *reply); static void RecolorInternalHRefs(HTMLWidget hw, char *href); static Dimension VbarWidth(HTMLWidget hw); static Dimension HbarHeight(HTMLWidget hw); static void ViewRedisplay(HTMLWidget hw, int x, int y, int width, int height); static void ViewClearAndRefresh(HTMLWidget hw); static void CallLinkCallbacks(HTMLWidget hw); #endif /* _NO_PROTO */ /* * Default translations * Selection of text, and activate anchors. * If motif, add manager translations. */ #ifdef MOTIF static char defaultTranslations[] = " \ : select-start() ManagerGadgetArm()\n\ : extend-adjust() ManagerGadgetButtonMotion()\n\ : extend-end(PRIMARY, CUT_BUFFER0) ManagerGadgetActivate()\n\ : select-start()\n\ : extend-adjust()\n\ : extend-end(PRIMARY, CUT_BUFFER0)\n\ : extend-start()\n\ : extend-adjust()\n\ : extend-end(PRIMARY, CUT_BUFFER0) \n\ f: scroll(1)\n\ b: scroll(-1)\n\ Prior: scroll(-1)\n\ Next: scroll(1)\n\ u: scroll(-0.5)\n\ d: scroll(0.5)\n\ Up: scroll(-0.5)\n\ Down: scroll(0.5)\n\ j: scroll(1ch)\n\ k: scroll(-1ch)\n\ : track-motion()\n\ : track-motion()\n\ : track-motion()\n\ : track-motion()\ "; #else static char defaultTranslations[] = " \ : select-start() \n\ : extend-adjust() \n\ : extend-end(PRIMARY, CUT_BUFFER0) \n\ : select-start() \n\ : extend-adjust() \n\ : extend-end(PRIMARY, CUT_BUFFER0) \n\ : extend-start()\n\ : extend-adjust()\n\ : extend-end(PRIMARY, CUT_BUFFER0) \n\ f: scroll(1)\n\ b: scroll(-1)\n\ Prior: scroll(-1)\n\ Next: scroll(1)\n\ u: scroll(-0.5)\n\ d: scroll(0.5)\n\ Up: scroll(-0.5)\n\ Down: scroll(0.5)\n\ j: scroll(1ch)\n\ k: scroll(-1ch)\n\ : track-motion()\n\ : track-motion()\n\ : track-motion()\n\ : track-motion()\ "; #endif /* MOTIF */ static XtActionsRec actionsList[] = { { "select-start", (XtActionProc) SelectStart }, { "extend-start", (XtActionProc) ExtendStart }, { "extend-adjust", (XtActionProc) ExtendAdjust }, { "extend-end", (XtActionProc) ExtendEnd }, { "track-motion", (XtActionProc) TrackMotion }, { "scroll", (XtActionProc) Scroll }, { "HTMLInput", (XtActionProc) _HTMLInput }, #ifndef MOTIF { "HTMLpwdInput", (XtActionProc) _HTMLpwdInput }, #endif #ifdef MOTIF #ifndef MOTIF1_2 { "Arm", (XtActionProc) _XmGadgetArm }, /* Motif 1.0 */ { "Activate", (XtActionProc) _XmGadgetActivate }, /* Motif 1.0 */ { "Enter", (XtActionProc) _XmManagerEnter }, /* Motif 1.0 */ { "FocusIn", (XtActionProc) _XmManagerFocusIn }, /* Motif 1.0 */ { "Help", (XtActionProc) _XmManagerHelp }, /* Motif 1.0 */ #endif /* not MOTIF1_2 */ #endif /* MOTIF */ }; /* * For some reason, in Motif1.2/X11R5 the actionsList above gets corrupted * When the parent HTML widget is created. This means we can't use * it later with XtAppAddActions to add to the viewing area. * So, we make a spare copy here to use with XtAppAddActions. */ static XtActionsRec SpareActionsList[] = { { "select-start", (XtActionProc) SelectStart }, { "extend-start", (XtActionProc) ExtendStart }, { "extend-adjust", (XtActionProc) ExtendAdjust }, { "extend-end", (XtActionProc) ExtendEnd }, { "track-motion", (XtActionProc) TrackMotion }, { "scroll", (XtActionProc) Scroll }, { "HTMLInput", (XtActionProc) _HTMLInput }, #ifndef MOTIF { "HTMLpwdInput", (XtActionProc) _HTMLpwdInput }, #endif }; /* * Resource definitions for HTML widget */ static XtResource resources[] = { /* Without Motif we need to override the borderWidth to 0 (from 1). */ #ifndef MOTIF { XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof (Dimension), XtOffset (HTMLWidget, core.border_width), XtRImmediate, (XtPointer) 0 }, #endif { WbNmarginWidth, WbCMarginWidth, XtRDimension, sizeof (Dimension), XtOffset (HTMLWidget, html.margin_width), XtRImmediate, (caddr_t) MARGIN_DEFAULT }, { WbNmarginHeight, WbCMarginHeight, XtRDimension, sizeof (Dimension), XtOffset (HTMLWidget, html.margin_height), XtRImmediate, (caddr_t) MARGIN_DEFAULT }, { WbNanchorCallback, XtCCallback, XtRCallback, sizeof (XtCallbackList), XtOffset (HTMLWidget, html.anchor_callback), XtRImmediate, (caddr_t) NULL }, { WbNlinkCallback, XtCCallback, XtRCallback, sizeof (XtCallbackList), XtOffset (HTMLWidget, html.link_callback), XtRImmediate, (caddr_t) NULL }, { WbNsubmitFormCallback, XtCCallback, XtRCallback, sizeof (XtCallbackList), XtOffset (HTMLWidget, html.form_callback), XtRImmediate, (caddr_t) NULL }, { WbNtext, WbCText, XtRString, sizeof (char *), XtOffset (HTMLWidget, html.raw_text), XtRString, (char *) NULL }, { WbNheaderText, WbCHeaderText, XtRString, sizeof (char *), XtOffset (HTMLWidget, html.header_text), XtRString, (char *) NULL }, { WbNfooterText, WbCFooterText, XtRString, sizeof (char *), XtOffset (HTMLWidget, html.footer_text), XtRString, (char *) NULL }, { WbNtitleText, WbCTitleText, XtRString, sizeof (char *), XtOffset (HTMLWidget, html.title), XtRString, (char *) NULL }, /* * Without motif we need our own foreground resource instead of * using the manager's */ #ifndef MOTIF { XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel), XtOffset (HTMLWidget, html.foreground), XtRString, "Black" }, #endif { WbNanchorUnderlines, WbCAnchorUnderlines, XtRInt, sizeof (int), XtOffset (HTMLWidget, html.num_anchor_underlines), XtRString, "0" }, { WbNvisitedAnchorUnderlines, WbCVisitedAnchorUnderlines, XtRInt, sizeof (int), XtOffset (HTMLWidget, html.num_visitedAnchor_underlines), XtRString, "0" }, { WbNdashedAnchorUnderlines, WbCDashedAnchorUnderlines, XtRBoolean, sizeof (Boolean), XtOffset (HTMLWidget, html.dashed_anchor_lines), XtRString, "False" }, { WbNdashedVisitedAnchorUnderlines, WbCDashedVisitedAnchorUnderlines, XtRBoolean, sizeof (Boolean), XtOffset (HTMLWidget, html.dashed_visitedAnchor_lines), XtRString, "False" }, { WbNanchorColor, XtCForeground, XtRPixel, sizeof (Pixel), XtOffset (HTMLWidget, html.anchor_fg), XtRString, "blue2" }, { WbNvisitedAnchorColor, XtCForeground, XtRPixel, sizeof (Pixel), XtOffset (HTMLWidget, html.visitedAnchor_fg), XtRString, "purple4" }, { WbNactiveAnchorFG, XtCBackground, XtRPixel, sizeof (Pixel), XtOffset (HTMLWidget, html.activeAnchor_fg), XtRString, "Red" }, { WbNactiveAnchorBG, XtCForeground, XtRPixel, sizeof (Pixel), XtOffset (HTMLWidget, html.activeAnchor_bg), XtRString, "White" }, { WbNpercentVerticalSpace, WbCPercentVerticalSpace, XtRInt, sizeof (int), XtOffset (HTMLWidget, html.percent_vert_space), XtRString, "90" }, { WbNimageBorders, WbCImageBorders, XtRBoolean, sizeof (Boolean), XtOffset (HTMLWidget, html.border_images), XtRString, "False" }, { WbNdelayImageLoads, WbCDelayImageLoads, XtRBoolean, sizeof (Boolean), XtOffset (HTMLWidget, html.delay_images), XtRString, "False" }, { WbNfancySelections, WbCFancySelections, XtRBoolean, sizeof (Boolean), XtOffset (HTMLWidget, html.fancy_selections), XtRString, "False" }, { WbNisIndex, WbCIsIndex, XtRBoolean, sizeof (Boolean), XtOffset (HTMLWidget, html.is_index), XtRString, "False" }, { WbNview, WbCView, XtRWidget, sizeof (Widget), XtOffset (HTMLWidget, html.view), XtRImmediate, NULL }, { WbNverticalScrollBar, WbCVerticalScrollBar, XtRWidget, sizeof (Widget), XtOffset (HTMLWidget, html.vbar), XtRImmediate, NULL }, { WbNhorizontalScrollBar, WbCHorizontalScrollBar, XtRWidget, sizeof (Widget), XtOffset (HTMLWidget, html.hbar), XtRImmediate, NULL }, { WbNverticalScrollOnRight, WbCVerticalScrollOnRight, XtRBoolean, sizeof (Boolean), XtOffset (HTMLWidget, html.vbar_right), XtRString, "True" }, { WbNhorizontalScrollOnTop, WbCHorizontalScrollOnTop, XtRBoolean, sizeof (Boolean), XtOffset (HTMLWidget, html.hbar_top), XtRString, "False" }, { XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.font), XtRString, "-adobe-times-medium-r-normal-*-14-*-*-*-*-*-*-*" }, { WbNitalicFont, WbCItalicFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.italic_font), XtRString, "-adobe-times-medium-i-normal-*-14-*-*-*-*-*-*-*" }, { WbNboldFont, WbCBoldFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.bold_font), XtRString, "-adobe-times-bold-r-normal-*-14-*-*-*-*-*-*-*" }, { WbNfixedFont, WbCFixedFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.fixed_font), XtRString, "-adobe-courier-medium-r-normal-*-14-*-*-*-*-*-*-*" }, { WbNfixedboldFont, WbCFixedboldFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.fixedbold_font), XtRString, "-adobe-courier-bold-r-normal-*-14-*-*-*-*-*-*-*" }, { WbNfixeditalicFont, WbCFixeditalicFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.fixeditalic_font), XtRString, "-adobe-courier-medium-o-normal-*-14-*-*-*-*-*-*-*" }, { WbNheader1Font, WbCHeader1Font, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.header1_font), XtRString, "-adobe-times-bold-r-normal-*-24-*-*-*-*-*-*-*" }, { WbNheader2Font, WbCHeader2Font, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.header2_font), XtRString, "-adobe-times-bold-r-normal-*-18-*-*-*-*-*-*-*" }, { WbNheader3Font, WbCHeader3Font, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.header3_font), XtRString, "-adobe-times-bold-r-normal-*-17-*-*-*-*-*-*-*" }, { WbNheader4Font, WbCHeader4Font, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.header4_font), XtRString, "-adobe-times-bold-r-normal-*-14-*-*-*-*-*-*-*" }, { WbNheader5Font, WbCHeader5Font, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.header5_font), XtRString, "-adobe-times-bold-r-normal-*-12-*-*-*-*-*-*-*" }, { WbNheader6Font, WbCHeader6Font, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.header6_font), XtRString, "-adobe-times-bold-r-normal-*-10-*-*-*-*-*-*-*" }, { WbNaddressFont, WbCAddressFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.address_font), XtRString, "-adobe-times-medium-i-normal-*-14-*-*-*-*-*-*-*" }, { WbNplainFont, WbCPlainFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.plain_font), XtRString, "-adobe-courier-medium-r-normal-*-14-*-*-*-*-*-*-*" }, { WbNplainboldFont, WbCPlainboldFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.plainbold_font), XtRString, "-adobe-courier-bold-r-normal-*-14-*-*-*-*-*-*-*" }, { WbNplainitalicFont, WbCPlainitalicFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.plainitalic_font), XtRString, "-adobe-courier-medium-o-normal-*-14-*-*-*-*-*-*-*" }, { WbNlistingFont, WbCListingFont, XtRFontStruct, sizeof (XFontStruct *), XtOffset (HTMLWidget, html.listing_font), XtRString, "-adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*" }, { WbNpreviouslyVisitedTestFunction, WbCPreviouslyVisitedTestFunction, XtRPointer, sizeof (XtPointer), XtOffset (HTMLWidget, html.previously_visited_test), XtRImmediate, (caddr_t) NULL }, { WbNpreviouslyVisitedTestData, WbCPreviouslyVisitedTestData, XtRPointer, sizeof (XtPointer), XtOffset (HTMLWidget, html.vt_client_data), XtRImmediate, (caddr_t) NULL }, { WbNresolveImageFunction, WbCResolveImageFunction, XtRPointer, sizeof (XtPointer), XtOffset (HTMLWidget, html.resolveImage), XtRImmediate, (caddr_t) NULL }, { WbNresolveDelayedImage, WbCResolveDelayedImage, XtRPointer, sizeof (XtPointer), XtOffset (HTMLWidget, html.resolveDelayedImage), XtRImmediate, (caddr_t) NULL }, { WbNpointerMotionCallback, WbCPointerMotionCallback, XtRPointer, sizeof (XtPointer), XtOffset (HTMLWidget, html.pointer_motion_callback), XtRImmediate, (caddr_t) NULL }, { WbNpointerMotionData, WbCPointerMotionData, XtRPointer, sizeof (XtPointer), XtOffset (HTMLWidget, html.pm_client_data), XtRImmediate, (caddr_t) NULL }, }; HTMLClassRec htmlClassRec = { { /* core class fields */ #ifdef MOTIF (WidgetClass) &xmManagerClassRec, /* superclass */ #else (WidgetClass) &constraintClassRec, /* superclass */ #endif /* MOTIF */ "HTML", /* class_name */ sizeof(HTMLRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_init */ FALSE, /* class_inited */ (XtInitProc) Initialize, /* initialize */ NULL, /* initialize_hook */ (XtRealizeProc) Realize, /* realize */ actionsList, /* actions */ XtNumber(actionsList), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ FALSE, /* compress_exposure */ TRUE, /* compress_enterlv */ FALSE, /* visible_interest */ NULL, /* destroy */ (XtWidgetProc) Resize, /* resize */ (XtExposeProc) Redisplay, /* expose */ (XtSetValuesFunc) SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ defaultTranslations, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator*/ NULL, /* extension */ }, { /* composite_class fields */ (XtGeometryHandler) GeometryManager, /* geometry_manager */ NULL, /* change_managed */ XtInheritInsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL, /* extension */ }, { /* constraint_class fields */ NULL, /* resource list */ 0, /* num resources */ 0, /* constraint size */ NULL, /* init proc */ NULL, /* destroy proc */ NULL, /* set values proc */ NULL, /* extension */ }, #ifdef MOTIF { /* manager_class fields */ XtInheritTranslations, /* translations */ NULL, /* syn_resources */ 0, /* num_syn_resources */ NULL, /* syn_cont_resources */ 0, /* num_syn_cont_resources */ XmInheritParentProcess, /* parent_process */ NULL, /* extension */ }, #endif /* MOTIF */ { /* html_class fields */ 0 /* none */ } }; WidgetClass htmlWidgetClass = (WidgetClass)&htmlClassRec; /*static Cursor in_anchor_cursor = (Cursor)NULL;*/ /* MF021 */ Cursor in_anchor_cursor = (Cursor)NULL; /* * Process an expose event in the View (or drawing area). This * Can be a regular expose event, or perhaps a GraphicsExpose Event. */ static void DrawExpose(w, data, event) Widget w; caddr_t data; XEvent *event; { XExposeEvent *ExEvent = (XExposeEvent *)event; HTMLWidget hw = (HTMLWidget)data; int x, y; int width, height; if ((event->xany.type != Expose)&&(event->xany.type != GraphicsExpose)) { return; } /* * Make sure we have a valid GC to draw with. */ if (hw->html.drawGC == NULL) { unsigned long valuemask; XGCValues values; values.function = GXcopy; values.plane_mask = AllPlanes; /* * Without motif we use our own foreground resource instead of * using the manager's */ #ifdef MOTIF values.foreground = hw->manager.foreground; #else values.foreground = hw->html.foreground; #endif /* MOTIF */ values.background = hw->core.background_pixel; valuemask = GCFunction|GCPlaneMask|GCForeground|GCBackground; hw->html.drawGC = XCreateGC(XtDisplay(hw), XtWindow(hw), valuemask, &values); } x = ExEvent->x; y = ExEvent->y; width = (int)ExEvent->width; height = (int)ExEvent->height; #ifdef DEBUG DebugHook(x, y, width, height); #endif ViewRedisplay(hw, x, y, width, height); } void ScrollWidgets(hw) HTMLWidget hw; { WidgetInfo *wptr; int xval, yval; xval = hw->html.scroll_x; yval = hw->html.scroll_y; wptr = hw->html.widget_list; while (wptr != NULL) { if (wptr->w != NULL) { Widget w; int x, y; w = wptr->w; x = wptr->x; y = wptr->y; XtMoveWidget(w, (x - xval), (y - yval)); } wptr = wptr->next; } } #ifndef MOTIF /* * Set the Athena Scrollbar's thumb position properly. */ static void setScrollBar(sb, topPosition, totalLength, currentLength) Widget sb; int topPosition; /* MF026 */ int totalLength, currentLength; /* MF026 */ { float top = (float)topPosition /(float)(totalLength); float shown = (float)currentLength/(float)(totalLength); XawScrollbarSetThumb(sb, top, shown); } #endif /* * Either the vertical or hortizontal scrollbar has been moved */ void ScrollToPos(w, hw, value) Widget w; HTMLWidget hw; int value; { /* * Special code incase the scrollbar is "moved" before we have a window * (if we have a GC we have a window) */ if (hw->html.drawGC == NULL) { if (w == hw->html.vbar) { hw->html.scroll_y = value; } else if (w == hw->html.hbar) { hw->html.scroll_x = value; } return; } /* * get our widgets out of the way (No Expose events) HideWidgets(hw); */ /* * If we've moved the vertical scrollbar */ if (w == hw->html.vbar) { /* * We've scrolled down. Copy up the untouched part of the * window. Then Clear and redraw the new area * exposed. */ if (value > hw->html.scroll_y) { int dy; dy = value - hw->html.scroll_y; if (dy > hw->html.view_height) { hw->html.scroll_y = value; XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), 0, 0, hw->html.view_width, hw->html.view_height, False); ViewRedisplay(hw, 0, 0, hw->html.view_width, hw->html.view_height); } else { XCopyArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), XtWindow(hw->html.view), hw->html.drawGC, 0, dy, hw->html.view_width, hw->html.view_height - dy, 0, 0); hw->html.scroll_y = value; XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), 0, (int)hw->html.view_height - dy, hw->html.view_width, dy, False); ViewRedisplay(hw, 0, (int)hw->html.view_height - dy, hw->html.view_width, dy); } } /* * We've scrolled up. Copy down the untouched part of the * window. Then Clear and redraw the new area * exposed. */ else if (value < hw->html.scroll_y) { int dy; dy = hw->html.scroll_y - value; if (dy > hw->html.view_height) { hw->html.scroll_y = value; XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), 0, 0, hw->html.view_width, hw->html.view_height, False); ViewRedisplay(hw, 0, 0, hw->html.view_width, hw->html.view_height); } else { XCopyArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), XtWindow(hw->html.view), hw->html.drawGC, 0, 0, hw->html.view_width, hw->html.view_height - dy, 0, dy); hw->html.scroll_y = value; XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), 0, 0, hw->html.view_width, dy, False); ViewRedisplay(hw, 0, 0, hw->html.view_width, dy); } } } /* * Else we've moved the horizontal scrollbar */ else if (w == hw->html.hbar) { /* * We've scrolled right. Copy left the untouched part of the * window. Then Clear and redraw the new area * exposed. */ if (value > hw->html.scroll_x) { int dx; dx = value - hw->html.scroll_x; if (dx > hw->html.view_width) { hw->html.scroll_x = value; XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), 0, 0, hw->html.view_width, hw->html.view_height, False); ViewRedisplay(hw, 0, 0, hw->html.view_width, hw->html.view_height); } else { XCopyArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), XtWindow(hw->html.view), hw->html.drawGC, dx, 0, hw->html.view_width - dx, hw->html.view_height, 0, 0); hw->html.scroll_x = value; XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), (int)hw->html.view_width - dx, 0, dx, hw->html.view_height, False); ViewRedisplay(hw, (int)hw->html.view_width - dx, 0, dx, hw->html.view_height); } } /* * We've scrolled left. Copy right the untouched part of the * window. Then Clear and redraw the new area * exposed. */ else if (value < hw->html.scroll_x) { int dx; dx = hw->html.scroll_x - value; if (dx > hw->html.view_width) { hw->html.scroll_x = value; XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), 0, 0, hw->html.view_width, hw->html.view_height, False); ViewRedisplay(hw, 0, 0, hw->html.view_width, hw->html.view_height); } else { XCopyArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), XtWindow(hw->html.view), hw->html.drawGC, 0, 0, hw->html.view_width - dx, hw->html.view_height, dx, 0); hw->html.scroll_x = value; XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), 0, 0, dx, hw->html.view_height, False); ViewRedisplay(hw, 0, 0, dx, hw->html.view_height); } } } /* * Move the now hidden widgets * Flush any Copyed or Cleared text first. XFlush(XtDisplay(hw)); */ ScrollWidgets(hw); /* * Remap the widgets to their new location MapWidgets(hw); */ } /* * Either the vertical or hortizontal scrollbar has been moved */ void ScrollMove(w, client_data, call_data) Widget w; caddr_t client_data; caddr_t call_data; { #ifdef MOTIF XmScrollBarCallbackStruct *sc = (XmScrollBarCallbackStruct *)call_data; ScrollToPos(w, (HTMLWidget)client_data, sc->value); #else float scrollDir = (int)call_data < 0 ? -0.3 : 0.3; HTMLWidget hw = (HTMLWidget)client_data; int value; int totalLength, currentLength; /* MF026 */ if (w == hw->html.vbar) { totalLength = hw->html.doc_height; currentLength = hw->html.view_height; value = hw->html.scroll_y + scrollDir * currentLength; } else { totalLength = hw->html.doc_width; currentLength = hw->html.view_width; value = hw->html.scroll_x + scrollDir * currentLength; } if (value > (int)totalLength) value = totalLength; if (value < 0) value = 0; setScrollBar(w, value, totalLength, currentLength); ScrollToPos(w, hw, value); #endif } #ifndef MOTIF void JumpMove(w, client_data, call_data) Widget w; caddr_t client_data; caddr_t call_data; { HTMLWidget hw = (HTMLWidget)client_data; int value = (int)(*(float *)call_data * (w == hw->html.vbar ? hw->html.doc_height : hw->html.doc_width)); ScrollToPos(w, hw, value); } #endif /* * Create the horizontal and vertical scroll bars. * Size them later. */ static void #ifdef _NO_PROTO CreateScrollbars (hw) HTMLWidget hw ; #else CreateScrollbars( HTMLWidget hw) #endif { Arg arg[20]; Cardinal argcnt; XtTranslations trans; /* * If the user hasn't provided a viewing area Widget (which they * should not most of the time) make a drawing are to use. */ if (hw->html.view == NULL) { argcnt = 0; XtSetArg(arg[argcnt], XxNwidth, 10); argcnt++; XtSetArg(arg[argcnt], XxNheight, 10); argcnt++; hw->html.view = XtCreateWidget("View", #ifdef MOTIF xmDrawingAreaWidgetClass, #else drawingAreaWidgetClass, #endif (Widget)hw, arg, argcnt); XtManageChild(hw->html.view); } /* * For the view widget catch all Expose and GraphicsExpose * events. Replace its translations with ours, and make * sure all the actions are in order. */ XtAddEventHandler((Widget)hw->html.view, ExposureMask, True, (XtEventHandler)DrawExpose, (caddr_t)hw); /* * As described previoisly, for some reason with Motif1.2/X11R5 * the list actionsList is corrupted when we get here, * so we have to use the special copy SpareActionsList */ XtAppAddActions(XtWidgetToApplicationContext(hw->html.view), SpareActionsList, XtNumber(SpareActionsList)); trans = XtParseTranslationTable(defaultTranslations); argcnt = 0; XtSetArg(arg[argcnt], XtNtranslations, trans); argcnt++; XtSetValues(hw->html.view, arg, argcnt); /* * If the user hasn't provided a vertical scrollbar (which they * should not most of the time) make one. */ if (hw->html.vbar == NULL) { argcnt = 0; #ifdef MOTIF XtSetArg(arg[argcnt], XmNorientation, XmVERTICAL); argcnt++; hw->html.vbar = XtCreateWidget("Vbar", xmScrollBarWidgetClass, (Widget)hw, arg, argcnt); #else XtSetArg(arg[argcnt],XtNorientation,XtorientVertical); argcnt++; hw->html.vbar = XtCreateWidget("Vbar", scrollbarWidgetClass, (Widget)hw, arg, argcnt); #endif XtManageChild(hw->html.vbar); } /* * Add callbacks to catch scrollbar changes */ #ifdef MOTIF XtAddCallback(hw->html.vbar, XmNvalueChangedCallback, (XtCallbackProc)ScrollMove, (caddr_t)hw); XtAddCallback(hw->html.vbar, XmNdragCallback, (XtCallbackProc)ScrollMove, (caddr_t)hw); #else XtAddCallback(hw->html.vbar, XtNjumpProc, (XtCallbackProc)JumpMove, (caddr_t)hw); XtAddCallback(hw->html.vbar, XtNscrollProc, (XtCallbackProc)ScrollMove, (caddr_t)hw); #endif /* * If the user hasn't provided a horizontal scrollbar (which they * should not most of the time) make one. */ if (hw->html.hbar == NULL) { argcnt = 0; #ifdef MOTIF XtSetArg(arg[argcnt], XmNorientation, XmHORIZONTAL); argcnt++; hw->html.hbar = XtCreateWidget("Hbar", xmScrollBarWidgetClass, (Widget)hw, arg, argcnt); #else XtSetArg(arg[argcnt], XtNorientation, XtorientHorizontal); argcnt++; hw->html.hbar = XtCreateWidget("Hbar", scrollbarWidgetClass, (Widget)hw, arg, argcnt); #endif XtManageChild(hw->html.hbar); } /* * Add callbacks to catch scrollbar changes */ #ifdef MOTIF XtAddCallback(hw->html.hbar, XmNvalueChangedCallback, (XtCallbackProc)ScrollMove, (caddr_t)hw); XtAddCallback(hw->html.hbar, XmNdragCallback, (XtCallbackProc)ScrollMove, (caddr_t)hw); #else XtAddCallback(hw->html.hbar, XtNjumpProc, (XtCallbackProc)JumpMove, (caddr_t)hw); XtAddCallback(hw->html.hbar, XtNscrollProc, (XtCallbackProc)ScrollMove, (caddr_t)hw); #endif } /* * Return the width of the vertical scrollbar */ static Dimension #ifdef _NO_PROTO VbarWidth (hw) HTMLWidget hw ; #else VbarWidth( HTMLWidget hw) #endif { Arg arg[4]; Cardinal argcnt; Dimension width; width = 0; if (hw->html.vbar != NULL) { argcnt = 0; XtSetArg(arg[argcnt], XxNwidth, &width); argcnt++; XtGetValues(hw->html.vbar, arg, argcnt); } return(width); } /* * Return the height of the horizontal scrollbar */ static Dimension #ifdef _NO_PROTO HbarHeight (hw) HTMLWidget hw ; #else HbarHeight( HTMLWidget hw) #endif { Arg arg[4]; Cardinal argcnt; Dimension height; height = 0; if (hw->html.hbar != NULL) { argcnt = 0; XtSetArg(arg[argcnt], XxNheight, &height); argcnt++; XtGetValues(hw->html.hbar, arg, argcnt); } return(height); } /* * Resize and set the min and max values of the scrollbars. Position viewing * area based on scrollbar locations. */ static void #ifdef _NO_PROTO ConfigScrollBars (hw) HTMLWidget hw ; #else ConfigScrollBars( HTMLWidget hw) #endif { #ifdef MOTIF Arg arg[20]; Cardinal argcnt; #endif int vx, vy; /* * Move and size the viewing area */ #ifdef MOTIF vx = hw->manager.shadow_thickness; vy = hw->manager.shadow_thickness; #else vx = vy = 0; #endif if ((hw->html.use_vbar == True)&&(hw->html.vbar_right == False)) { vx += VbarWidth(hw); } if ((hw->html.use_hbar == True)&&(hw->html.hbar_top == True)) { vy += HbarHeight(hw); } XtMoveWidget(hw->html.view, vx, vy); XtResizeWidget(hw->html.view, hw->html.view_width, hw->html.view_height, hw->html.view->core.border_width); /* * Set up vertical scrollbar */ if (hw->html.use_vbar == True) { int maxv; int ss; /* * Size the vertical scrollbar to the height of * the viewing area */ XtResizeWidget(hw->html.vbar, hw->html.vbar->core.width, hw->html.view_height + (2 * #ifdef MOTIF hw->manager.shadow_thickness #else 0 #endif ), hw->html.vbar->core.border_width); /* * Set the slider size to be the percentage of the * viewing area that the viewing area is of the * document area. Or set it to 1 if that isn't possible. */ if (hw->html.doc_height == 0) { ss = 1; } else { #ifdef DEBUG fprintf (stderr, "view_height %d, doc_height %d\n", hw->html.view_height, hw->html.doc_height); #endif #ifdef NOT_RIGHT /* Eric -- your previous equation wasn't doing it. This isn't either... */ ss = (int)((float)hw->html.view_height * ((float)hw->html.view_height / (float)(hw->html.doc_height - (int)hw->html.view_height))); if (ss > hw->html.view_height) { ss = hw->html.view_height; } #endif /* Added by marca: this produces results *very* close (~1 pixel) to the original scrolled window behavior. */ ss = hw->html.view_height; } if (ss < 1) { ss = 1; } #ifdef DEBUG fprintf (stderr, "computed ss to be %d\n", ss); #endif /* * If resizing of the document has made scroll_y * greater than the max, we want to hold it at the max. */ maxv = hw->html.doc_height - (int)hw->html.view_height; if (maxv < 0) { maxv = 0; } if (hw->html.scroll_y > maxv) { hw->html.scroll_y = maxv; } /* * Prevent the Motif max value and slider size * from going to zero, which is illegal */ maxv = maxv + ss; if (maxv < 1) { maxv = 1; } /* * Motif will not allow the actual value to be equal to * its max value. Adjust accordingly. * Since we might decrease scroll_y, cap it at zero. */ if (hw->html.scroll_y >= maxv) { hw->html.scroll_y = maxv - 1; } if (hw->html.scroll_y < 0) { hw->html.scroll_y = 0; } #ifdef MOTIF argcnt = 0; XtSetArg(arg[argcnt], XmNminimum, 0); argcnt++; XtSetArg(arg[argcnt], XmNmaximum, maxv); argcnt++; XtSetArg(arg[argcnt], XmNvalue, hw->html.scroll_y); argcnt++; XtSetArg(arg[argcnt], XmNsliderSize, ss); argcnt++; XtSetArg(arg[argcnt], XmNincrement, DEFAULT_INCREMENT); argcnt++; XtSetArg(arg[argcnt], XmNpageIncrement, hw->html.view_height > DEFAULT_INCREMENT ? hw->html.view_height - DEFAULT_INCREMENT : 1); argcnt++; XtSetValues(hw->html.vbar, arg, argcnt); #else setScrollBar(hw->html.vbar, hw->html.scroll_y, hw->html.doc_height, (int)hw->html.view_height); #endif /* MOTIF */ #ifdef DEBUG XtVaGetValues(hw->html.vbar, XmNsliderSize, &ss, NULL); fprintf (stderr, "real slider size %d\n", ss); #endif } /* * Set up horizontal scrollbar */ if (hw->html.use_hbar == True) { int maxv; int ss; /* * Size the horizontal scrollbar to the width of * the viewing area */ XtResizeWidget(hw->html.hbar, hw->html.view_width + (2 * #ifdef MOTIF hw->manager.shadow_thickness #else 0 #endif /* MOTIF */ ), hw->html.hbar->core.height, hw->html.hbar->core.border_width); /* * Set the slider size to be the percentage of the * viewing area that the viewing area is of the * document area. Or set it to 1 if that isn't possible. */ if (hw->html.doc_width == 0) { ss = 1; } else { #ifdef NOT_RIGHT ss = hw->html.view_width * hw->html.view_width / hw->html.doc_width; if (ss > hw->html.view_width) { ss = hw->html.view_width; } #endif /* Added by marca: this produces results *very* close (~1 pixel) to the original scrolled window behavior. */ ss = hw->html.view_width; } if (ss < 1) { ss = 1; } /* * If resizing of the document has made scroll_x * greater than the max, we want to hold it at the max. */ maxv = hw->html.doc_width - (int)hw->html.view_width; if (maxv < 0) { maxv = 0; } if (hw->html.scroll_x > maxv) { hw->html.scroll_x = maxv; } /* * Prevent the Motif max value and slider size * from going to zero, which is illegal */ maxv = maxv + ss; if (maxv < 1) { maxv = 1; } /* * Motif will not allow the actual value to be equal to * its max value. Adjust accordingly. * Since we might decrease scroll_x, cap it at zero. */ if (hw->html.scroll_x >= maxv) { hw->html.scroll_x = maxv - 1; } if (hw->html.scroll_x < 0) { hw->html.scroll_x = 0; } #ifdef MOTIF argcnt = 0; XtSetArg(arg[argcnt], XmNminimum, 0); argcnt++; XtSetArg(arg[argcnt], XmNmaximum, maxv); argcnt++; XtSetArg(arg[argcnt], XmNvalue, hw->html.scroll_x); argcnt++; XtSetArg(arg[argcnt], XmNsliderSize, ss); argcnt++; XtSetArg(arg[argcnt], XmNincrement, DEFAULT_INCREMENT); argcnt++; XtSetArg(arg[argcnt], XmNpageIncrement, hw->html.view_width > DEFAULT_INCREMENT ? hw->html.view_width - DEFAULT_INCREMENT : 1); argcnt++; XtSetValues(hw->html.hbar, arg, argcnt); #else setScrollBar(hw->html.hbar, hw->html.scroll_x, hw->html.doc_width, (int)hw->html.view_width); #endif /* MOTIF */ } #ifdef DEBUG { int ss; XtVaGetValues(hw->html.vbar, XmNsliderSize, &ss, NULL); fprintf (stderr, "real slider size %d\n", ss); } #endif } /* * Reformat the window and scrollbars. * May be called because of a changed document, or because of a changed * window size. */ static void #ifdef _NO_PROTO ReformatWindow (hw) HTMLWidget hw ; #else ReformatWindow( HTMLWidget hw) #endif { int temp; int new_width; Dimension swidth, sheight; Dimension st; /* * Find the current scrollbar sizes, and shadow thickness and format * the document to the current window width * (assume a vertical scrollbar) */ swidth = VbarWidth(hw); sheight = HbarHeight(hw); #ifdef MOTIF st = hw->manager.shadow_thickness; #else st = 0; #endif /* MOTIF */ if (hw->core.width <= swidth) { hw->core.width = swidth + 10; } new_width = hw->core.width - swidth - (2 * st); temp = FormatAll(hw, &new_width); /* * If we need the vertical scrollbar, place and manage it, * and store the current viewing area width. */ if (temp > hw->core.height - sheight) { hw->html.use_vbar = True; if (hw->html.vbar_right == True) { XtMoveWidget(hw->html.vbar, (hw->core.width - swidth), 0); } else { XtMoveWidget(hw->html.vbar, 0, 0); } XtManageChild(hw->html.vbar); hw->html.view_width = hw->core.width - swidth - (2 * st); } /* * Else we were wrong to assume a vertical scrollbar. * Remove it, and reformat the document to the wider width. * Save the as the current viewing are width. */ else { hw->html.use_vbar = False; XtUnmanageChild(hw->html.vbar); hw->html.scroll_y = 0; new_width = hw->core.width - (2 * st); temp = FormatAll(hw, &new_width); hw->html.view_width = hw->core.width - (2 * st); /* fake out later horizontal scrollbars */ swidth = 0; } /* * Calculate the actual max width and height of the complete * formatted document. * The max width may exceed the preformatted width due to special * factors in the formatting of the widget. * Use the max of the 2 here, but leave max_pre_width unchanged * for future formatting calls. */ /* * new_width includes the margins, and hw->html.max_pre_width * does not, fix that here. */ new_width = new_width - (2 * hw->html.margin_width); if (hw->html.max_pre_width > new_width) { new_width = hw->html.max_pre_width; } /* * If the maximum width derives from a formatted, as opposed to * unformatted piece of text, allow a 20% of margin width slop * over into the margin to cover up a minor glick with terminaing * punctuation after anchors at the end of the line. */ else { new_width = new_width - (20 * hw->html.margin_width / 100); } hw->html.doc_height = temp; hw->html.doc_width = new_width + (2 * hw->html.margin_width); if (hw->html.view_width > hw->html.doc_width) { hw->html.doc_width = hw->html.view_width; } /* * If we need a horizontal scrollbar * Place it and manage it. Save the height of the current * viewing area. */ if (hw->html.doc_width > hw->html.view_width) { hw->html.use_hbar = True; if (hw->html.hbar_top == True) { if (hw->html.use_vbar == True) { XtMoveWidget(hw->html.vbar, hw->html.vbar->core.x, sheight); } if (hw->html.vbar_right == True) { XtMoveWidget(hw->html.hbar, 0, 0); } else { XtMoveWidget(hw->html.hbar, swidth, 0); } } else { if (hw->html.vbar_right == True) { XtMoveWidget(hw->html.hbar, 0, (hw->core.height - sheight)); } else { XtMoveWidget(hw->html.hbar, swidth, (hw->core.height - sheight)); } } XtManageChild(hw->html.hbar); hw->html.view_height = hw->core.height - sheight - (2 * st); } /* * Else we don't need a horizontal scrollbar. * Remove it and save the current viewing area height. */ else { hw->html.use_hbar = False; XtUnmanageChild(hw->html.hbar); hw->html.scroll_x = 0; hw->html.view_height = hw->core.height - (2 * st); } /* * Configure the scrollbar min, max, and slider sizes */ #ifdef DEBUG fprintf (stderr, "calling in ReformatWindow\n"); #endif ConfigScrollBars(hw); } /* * We're a happy widget. We let any child move or resize themselves * however they want, we don't care. */ static XtGeometryResult #ifdef _NO_PROTO GeometryManager (w, request, reply) Widget w; XtWidgetGeometry * request; XtWidgetGeometry * reply; #else GeometryManager ( Widget w, XtWidgetGeometry * request, XtWidgetGeometry * reply) #endif { reply->x = request->x; reply->y = request->y; reply->width = request->width; reply->height = request->height; reply->border_width = request->border_width; reply->request_mode = request->request_mode; return (XtGeometryYes); } /* * Initialize is called when the widget is first initialized. * Check to see that all the starting resources are valid. */ static void #ifdef _NO_PROTO Initialize (request, new) HTMLWidget request ; HTMLWidget new ; #else Initialize( HTMLWidget request, HTMLWidget new) #endif { /* * Make sure height and width are not zero. */ if (new->core.width == 0) { new->core.width = new->html.margin_width << 1 ; } if (new->core.width == 0) { new->core.width = 10 ; } if (new->core.height == 0) { new->core.height = new->html.margin_height << 1 ; } if (new->core.height == 0) { new->core.height = 10 ; } /* * Make sure the underline numbers are within bounds. */ if (new->html.num_anchor_underlines < 0) { new->html.num_anchor_underlines = 0; } if (new->html.num_anchor_underlines > MAX_UNDERLINES) { new->html.num_anchor_underlines = MAX_UNDERLINES; } if (new->html.num_visitedAnchor_underlines < 0) { new->html.num_visitedAnchor_underlines = 0; } if (new->html.num_visitedAnchor_underlines > MAX_UNDERLINES) { new->html.num_visitedAnchor_underlines = MAX_UNDERLINES; } /* * Parse the raw text with the HTML parser. And set the formatted * element list to NULL. */ new->html.html_objects = HTMLParse(NULL, request->html.raw_text); CallLinkCallbacks(new); new->html.html_header_objects = HTMLParse(NULL, request->html.header_text); new->html.html_footer_objects = HTMLParse(NULL, request->html.footer_text); new->html.formatted_elements = NULL; new->html.my_visited_hrefs = NULL; new->html.my_delayed_images = NULL; new->html.widget_list = NULL; new->html.form_list = NULL; /* * Blank document */ new->html.line_array = NULL; new->html.line_count = 0; /* * Find the max width of a preformatted * line in this document. */ new->html.max_pre_width = DocumentWidth(new, new->html.html_objects); /* * Create the scrollbars. * Find their dimensions and then decide which scrollbars you * will need, and what the dimensions of the viewing area are. * Start assuming a vertical scrollbar and a horizontal one. * The remove vertical if short enough, and remove horizontal * if narrow enough. */ CreateScrollbars(new); new->html.scroll_x = 0; new->html.scroll_y = 0; ReformatWindow(new); /* * Initialize private widget resources */ new->html.drawGC = NULL; new->html.select_start = NULL; new->html.select_end = NULL; new->html.sel_start_pos = 0; new->html.sel_end_pos = 0; new->html.new_start = NULL; new->html.new_end = NULL; new->html.new_start_pos = 0; new->html.new_end_pos = 0; new->html.active_anchor = NULL; new->html.press_x = 0; new->html.press_y = 0; new->html.cached_tracked_ele = NULL; /* Initialize cursor used when pointer is inside anchor. if (in_anchor_cursor == (Cursor)NULL) */ in_anchor_cursor = XCreateFontCursor (XtDisplay (new), XC_hand2); return; } /* * Realize is called when the widget is realized to create its window. * We call the Realize method of our superclass and then create the drawGC * to be used by the HTML widget. (This was added at NOAO, the NCSA * version does not use a custom Realize method, and the drawGC is not * created until the first Expose event occurs. This causes the widget to * crash if a drawing routine is called before the application becomes idle * and processes the queued input from the display server.) */ static void #ifdef _NO_PROTO Realize (hw, valueMask, attributes) HTMLWidget hw ; Mask *valueMask ; XSetWindowAttributes *attributes ; #else Realize ( HTMLWidget hw , Mask *valueMask , XSetWindowAttributes *attributes ) #endif { unsigned long valuemask; XGCValues values; /* Call the Realize method of the superclass to create the window. */ (*htmlWidgetClass->core_class.superclass->core_class.realize) ((Widget)hw, valueMask, attributes); /* Create the drawGC for the HTML window (copied from Expose). */ values.function = GXcopy; values.plane_mask = AllPlanes; /* * Without motif we use our own foreground resource instead of * using the manager's */ #ifdef MOTIF values.foreground = hw->manager.foreground; #else values.foreground = hw->html.foreground; #endif /* MOTIF */ values.background = hw->core.background_pixel; valuemask = GCFunction|GCPlaneMask|GCForeground|GCBackground; hw->html.drawGC = XCreateGC(XtDisplay(hw), XtWindow(hw), valuemask, &values); } #ifdef DEBUG void DebugHook(x, y, width, height) int x, y, width, height; { fprintf(stderr, "Redrawing (%d,%d) %dx%d\n", x, y, width, height); } #endif /* * This is called by redisplay. It is passed a rectangle * in the viewing area, and it redisplays that portion of the * underlying document area. */ static void #ifdef _NO_PROTO ViewRedisplay (hw, x, y, width, height) HTMLWidget hw; int x, y; int width, height; #else ViewRedisplay( HTMLWidget hw, int x, int y, int width, int height) #endif { int sx, sy; int doc_x, doc_y; int i, start, end, guess; /* * Use scrollbar values to map from view space to document space. */ sx = sy = 0; if (hw->html.use_vbar == True) { sy += hw->html.scroll_y; } if (hw->html.use_hbar == True) { sx += hw->html.scroll_x; } doc_x = x + sx; doc_y = y + sy; /* * Find the lines that overlap the exposed area. */ start = 0; end = hw->html.line_count - 1; /* * Heuristic to speed up redraws by guessing at the starting line. */ guess = doc_y / (hw->html.font->max_bounds.ascent + hw->html.font->max_bounds.descent); if (guess > end) { guess = end; } while (guess > 0) { if ((hw->html.line_array[guess] != NULL)&& (hw->html.line_array[guess]->y < doc_y)) { break; } guess--; } if (guess < start) { guess = start; } for (i=guess; ihtml.line_count; i++) { if (hw->html.line_array[i] == NULL) { continue; } if (hw->html.line_array[i]->y < doc_y) { start = i; } if (hw->html.line_array[i]->y > (doc_y + height)) { end = i; break; } } /* * If we have a GC draw the lines that overlap the exposed area. */ if (hw->html.drawGC != NULL) { for (i=start; i<=end; i++) { PlaceLine(hw, i); } #ifdef EXTRA_FLUSH XFlush(XtDisplay(hw)); #endif } } static void #ifdef _NO_PROTO ViewClearAndRefresh (hw) HTMLWidget hw; #else ViewClearAndRefresh( HTMLWidget hw) #endif { /* * Only refresh if we have a window already. * (if we have a GC we have a window) */ if (hw->html.drawGC != NULL) { XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view), 0, 0, 0, 0, False); ViewRedisplay(hw, 0, 0, hw->html.view_width, hw->html.view_height); /* * This is a fake deal to make an Expose event tocall Redisplay * to redraw the shadow around the view area */ XClearArea(XtDisplay(hw), XtWindow(hw), 0, 0, 1, 1, True); } } /* * The Redisplay function is what you do with an expose event. * Right now we call user callbacks, and then call the CompositeWidget's * Redisplay routine. */ static void #ifdef _NO_PROTO Redisplay (hw, event, region) HTMLWidget hw; XEvent * event; Region region; #else Redisplay( HTMLWidget hw, XEvent * event, Region region) #endif { XExposeEvent *ExEvent = (XExposeEvent *)event; int dx, dy; #ifdef MOTIF /* * find out where the shadow is based on scrollbars */ Dimension st = hw->manager.shadow_thickness; #endif /* MOTIF */ dx = dy = 0; if ((hw->html.use_vbar == True)&&(hw->html.vbar_right == False)) { dx += VbarWidth(hw); } if ((hw->html.use_hbar == True)&&(hw->html.hbar_top == True)) { dy += HbarHeight(hw); } #ifdef MOTIF /* * Redraw the shadow around the scrolling area which may have been * messed up. */ _XmDrawShadow(XtDisplay(hw), XtWindow(hw), hw->manager.bottom_shadow_GC, hw->manager.top_shadow_GC, hw->manager.shadow_thickness, dx, dy, hw->html.view_width + (2 * st),hw->html.view_height + (2 * st)); #endif /* MOTIF */ #ifdef MOTIF #ifdef MOTIF1_2 _XmRedisplayGadgets ((Widget)hw, (XEvent*)event, region); #else _XmRedisplayGadgets ((CompositeWidget)hw, (XExposeEvent*)event, region); #endif /* MOTIF1_2 */ #endif /* MOTIF */ return; } /* * Resize is called when the widget changes size. * Mostly any resize causes a reformat, except for the special case * where the width doesn't change, and the height doesn't change * enought to affect the vertical scrollbar. * It is too complex to guess exactly what needs to be redrawn, so refresh the * whole window on any resize. */ static void #ifdef _NO_PROTO Resize (hw) HTMLWidget hw; #else Resize( HTMLWidget hw) #endif { int tempw; Dimension swidth, sheight; Dimension st; /* * Find the new widht of the viewing area. */ swidth = VbarWidth(hw); sheight = HbarHeight(hw); #ifdef MOTIF st = hw->manager.shadow_thickness; #else st = 0; #endif /* MOTIF */ if (hw->core.width <= swidth) { hw->core.width = swidth + 10 ; } if (hw->html.use_vbar == True) { tempw = hw->core.width - swidth - (2 * st); } else { tempw = hw->core.width - (2 * st); /* fool positioning of horz scrollbar later */ swidth = 0; } /* * Special case where we don't have to reformat to a new width. * The width has not changed, and the height has not changed * significantly to change the state of the vertical scrollbar. */ if ((tempw == hw->html.view_width)&& (((hw->html.use_vbar == True)&& ((hw->core.height - sheight - (2 * st)) < hw->html.doc_height))|| ((hw->html.use_vbar == False)&& ((hw->core.height - sheight - (2 * st)) >= hw->html.doc_height)))) { /* * Super special case where the size of the window hasn't * changed at ALL! */ if (((hw->html.use_hbar == True)&&(hw->html.view_height == (hw->core.height - sheight - (2 * st))))|| ((hw->html.use_hbar == False)&&(hw->html.view_height == (hw->core.height - (2 * st))))) { return; } if (hw->html.use_hbar == True) { if (hw->html.hbar_top == True) { if (hw->html.vbar_right == True) { XtMoveWidget(hw->html.hbar, 0, 0); } else { XtMoveWidget(hw->html.hbar, swidth, 0); } } else { if (hw->html.vbar_right == True) { XtMoveWidget(hw->html.hbar, 0, (hw->core.height - sheight)); } else { XtMoveWidget(hw->html.hbar, swidth, (hw->core.height - sheight)); } } hw->html.view_height = hw->core.height - sheight - (2 * st); } else { hw->html.view_height = hw->core.height - (2 * st); } #ifdef DEBUG fprintf (stderr, "calling in Resize\n"); #endif ConfigScrollBars(hw); ScrollWidgets(hw); ViewClearAndRefresh(hw); } /* * Otherwise we have to do a full reformat on every resize. */ else { ReformatWindow(hw); ScrollWidgets(hw); ViewClearAndRefresh(hw); } #ifdef DEBUG { int ss; XtVaGetValues(hw->html.vbar, XmNsliderSize, &ss, NULL); fprintf (stderr, "leaving; slider size %d\n", ss); } #endif return; } /* * Find the complete text for this the anchor that aptr is a part of * and set it into the selection. */ static void FindSelectAnchor(hw, aptr) HTMLWidget hw; struct ele_rec *aptr; { struct ele_rec *eptr; eptr = aptr; while ((eptr->prev != NULL)&& (eptr->prev->anchorHRef != NULL)&& (strcmp(eptr->prev->anchorHRef, eptr->anchorHRef) == 0)) { eptr = eptr->prev; } hw->html.select_start = eptr; hw->html.sel_start_pos = 0; eptr = aptr; while ((eptr->next != NULL)&& (eptr->next->anchorHRef != NULL)&& (strcmp(eptr->next->anchorHRef, eptr->anchorHRef) == 0)) { eptr = eptr->next; } hw->html.select_end = eptr; hw->html.sel_end_pos = eptr->edata_len - 2; } /* * Set as active all elements in the widget that are part of the anchor * in the widget's start ptr. */ static void SetAnchor(hw) HTMLWidget hw; { struct ele_rec *eptr; struct ele_rec *start; struct ele_rec *end; unsigned long fg, bg; unsigned long old_fg, old_bg; eptr = hw->html.active_anchor; if ((eptr == NULL)||(eptr->anchorHRef == NULL)) { return; } fg = hw->html.activeAnchor_fg; bg = hw->html.activeAnchor_bg; FindSelectAnchor(hw, eptr); start = hw->html.select_start; end = hw->html.select_end; eptr = start; while ((eptr != NULL)&&(eptr != end)) { if (eptr->type == E_TEXT) { old_fg = eptr->fg; old_bg = eptr->bg; eptr->fg = fg; eptr->bg = bg; TextRefresh(hw, eptr, 0, (eptr->edata_len - 2)); eptr->fg = old_fg; eptr->bg = old_bg; } else if (eptr->type == E_IMAGE) { old_fg = eptr->fg; old_bg = eptr->bg; eptr->fg = fg; eptr->bg = bg; ImageRefresh(hw, eptr); eptr->fg = old_fg; eptr->bg = old_bg; } /* * Linefeeds in anchor spanning multiple lines should NOT * be highlighted! else if (eptr->type == E_LINEFEED) { old_fg = eptr->fg; old_bg = eptr->bg; eptr->fg = fg; eptr->bg = bg; LinefeedRefresh(hw, eptr); eptr->fg = old_fg; eptr->bg = old_bg; } */ eptr = eptr->next; } if (eptr != NULL) { if (eptr->type == E_TEXT) { old_fg = eptr->fg; old_bg = eptr->bg; eptr->fg = fg; eptr->bg = bg; TextRefresh(hw, eptr, 0, (eptr->edata_len - 2)); eptr->fg = old_fg; eptr->bg = old_bg; } else if (eptr->type == E_IMAGE) { old_fg = eptr->fg; old_bg = eptr->bg; eptr->fg = fg; eptr->bg = bg; ImageRefresh(hw, eptr); eptr->fg = old_fg; eptr->bg = old_bg; } /* * Linefeeds in anchor spanning multiple lines should NOT * be highlighted! else if (eptr->type == E_LINEFEED) { old_fg = eptr->fg; old_bg = eptr->bg; eptr->fg = fg; eptr->bg = bg; LinefeedRefresh(hw, eptr); eptr->fg = old_fg; eptr->bg = old_bg; } */ } } /* * Draw selection for all elements in the widget * from start to end. */ static void DrawSelection(hw, start, end, start_pos, end_pos) HTMLWidget hw; struct ele_rec *start; struct ele_rec *end; int start_pos, end_pos; { struct ele_rec *eptr; int epos; if ((start == NULL)||(end == NULL)) { return; } /* * Keep positions within bounds (allows us to be sloppy elsewhere) */ if (start_pos < 0) { start_pos = 0; } if (start_pos >= start->edata_len - 1) { start_pos = start->edata_len - 2; } if (end_pos < 0) { end_pos = 0; } if (end_pos >= end->edata_len - 1) { end_pos = end->edata_len - 2; } if (SwapElements(start, end, start_pos, end_pos)) { eptr = start; start = end; end = eptr; epos = start_pos; start_pos = end_pos; end_pos = epos; } eptr = start; while ((eptr != NULL)&&(eptr != end)) { int p1, p2; if (eptr == start) { p1 = start_pos; } else { p1 = 0; } p2 = eptr->edata_len - 2; if (eptr->type == E_TEXT) { eptr->selected = True; eptr->start_pos = p1; eptr->end_pos = p2; TextRefresh(hw, eptr, p1, p2); } else if (eptr->type == E_LINEFEED) { eptr->selected = True; LinefeedRefresh(hw, eptr); } eptr = eptr->next; } if (eptr != NULL) { int p1, p2; if (eptr == start) { p1 = start_pos; } else { p1 = 0; } if (eptr == end) { p2 = end_pos; } else { p2 = eptr->edata_len - 2; } if (eptr->type == E_TEXT) { eptr->selected = True; eptr->start_pos = p1; eptr->end_pos = p2; TextRefresh(hw, eptr, p1, p2); } else if (eptr->type == E_LINEFEED) { eptr->selected = True; LinefeedRefresh(hw, eptr); } } } /* * Set selection for all elements in the widget's * start to end list. */ static void SetSelection(hw) HTMLWidget hw; { struct ele_rec *start; struct ele_rec *end; int start_pos, end_pos; start = hw->html.select_start; end = hw->html.select_end; start_pos = hw->html.sel_start_pos; end_pos = hw->html.sel_end_pos; DrawSelection(hw, start, end, start_pos, end_pos); } /* * Erase the selection from start to end */ static void EraseSelection(hw, start, end, start_pos, end_pos) HTMLWidget hw; struct ele_rec *start; struct ele_rec *end; int start_pos, end_pos; { struct ele_rec *eptr; int epos; if ((start == NULL)||(end == NULL)) { return; } /* * Keep positoins within bounds (allows us to be sloppy elsewhere) */ if (start_pos < 0) { start_pos = 0; } if (start_pos >= start->edata_len - 1) { start_pos = start->edata_len - 2; } if (end_pos < 0) { end_pos = 0; } if (end_pos >= end->edata_len - 1) { end_pos = end->edata_len - 2; } if (SwapElements(start, end, start_pos, end_pos)) { eptr = start; start = end; end = eptr; epos = start_pos; start_pos = end_pos; end_pos = epos; } eptr = start; while ((eptr != NULL)&&(eptr != end)) { int p1, p2; if (eptr == start) { p1 = start_pos; } else { p1 = 0; } p2 = eptr->edata_len - 2; if (eptr->type == E_TEXT) { eptr->selected = False; TextRefresh(hw, eptr, p1, p2); } else if (eptr->type == E_LINEFEED) { eptr->selected = False; LinefeedRefresh(hw, eptr); } eptr = eptr->next; } if (eptr != NULL) { int p1, p2; if (eptr == start) { p1 = start_pos; } else { p1 = 0; } if (eptr == end) { p2 = end_pos; } else { p2 = eptr->edata_len - 2; } if (eptr->type == E_TEXT) { eptr->selected = False; TextRefresh(hw, eptr, p1, p2); } else if (eptr->type == E_LINEFEED) { eptr->selected = False; LinefeedRefresh(hw, eptr); } } } /* * Clear the current selection (if there is one) */ static void ClearSelection(hw) HTMLWidget hw; { struct ele_rec *start; struct ele_rec *end; int start_pos, end_pos; start = hw->html.select_start; end = hw->html.select_end; start_pos = hw->html.sel_start_pos; end_pos = hw->html.sel_end_pos; EraseSelection(hw, start, end, start_pos, end_pos); if ((start == NULL)||(end == NULL)) { hw->html.select_start = NULL; hw->html.select_end = NULL; hw->html.sel_start_pos = 0; hw->html.sel_end_pos = 0; hw->html.active_anchor = NULL; return; } hw->html.select_start = NULL; hw->html.select_end = NULL; hw->html.sel_start_pos = 0; hw->html.sel_end_pos = 0; hw->html.active_anchor = NULL; } /* * clear from active all elements in the widget that are part of the anchor. * (These have already been previously set into the start and end of the * selection. */ static void UnsetAnchor(hw) HTMLWidget hw; { struct ele_rec *eptr; /* * Clear any activated images */ eptr = hw->html.select_start; while ((eptr != NULL)&&(eptr != hw->html.select_end)) { if (eptr->type == E_IMAGE) { ImageRefresh(hw, eptr); } eptr = eptr->next; } if ((eptr != NULL)&&(eptr->type == E_IMAGE)) { ImageRefresh(hw, eptr); } /* * Clear the activated anchor */ ClearSelection(hw); } /* * Erase the old selection, and draw the new one in such a way * that advantage is taken of overlap, and there is no obnoxious * flashing. */ static void ChangeSelection(hw, start, end, start_pos, end_pos) HTMLWidget hw; struct ele_rec *start; struct ele_rec *end; int start_pos, end_pos; { struct ele_rec *old_start; struct ele_rec *old_end; struct ele_rec *new_start; struct ele_rec *new_end; struct ele_rec *eptr; int epos; int new_start_pos, new_end_pos; int old_start_pos, old_end_pos; old_start = hw->html.new_start; old_end = hw->html.new_end; old_start_pos = hw->html.new_start_pos; old_end_pos = hw->html.new_end_pos; new_start = start; new_end = end; new_start_pos = start_pos; new_end_pos = end_pos; if ((new_start == NULL)||(new_end == NULL)) { return; } if ((old_start == NULL)||(old_end == NULL)) { DrawSelection(hw, new_start, new_end, new_start_pos, new_end_pos); return; } if (SwapElements(old_start, old_end, old_start_pos, old_end_pos)) { eptr = old_start; old_start = old_end; old_end = eptr; epos = old_start_pos; old_start_pos = old_end_pos; old_end_pos = epos; } if (SwapElements(new_start, new_end, new_start_pos, new_end_pos)) { eptr = new_start; new_start = new_end; new_end = eptr; epos = new_start_pos; new_start_pos = new_end_pos; new_end_pos = epos; } /* * Deal with all possible intersections of the 2 selection sets. * ******************************************************** * * * * |-- * |-- * * old--| * new--| * * |-- * |-- * * * * * |-- * |-- * * new--| * old--| * * |-- * |-- * * * * ******************************************************** * * * * |---- * |-- * * old--| * new--| * * | |-- * | * * |-+-- * |-+-- * * | * | |-- * * new--| * old--| * * |-- * |---- * * * * ******************************************************** * * * * |--------- * |--------- * * | * | * * | |-- * | |-- * * new--| old--| * old--| new--| * * | |-- * | |-- * * | * | * * |--------- * |--------- * * * * ******************************************************** * */ if ((ElementLessThan(old_end, new_start, old_end_pos, new_start_pos))|| (ElementLessThan(new_end, old_start, new_end_pos, old_start_pos))) { EraseSelection(hw, old_start, old_end, old_start_pos, old_end_pos); DrawSelection(hw, new_start, new_end, new_start_pos, new_end_pos); } else if ((ElementLessThan(old_start, new_start, old_start_pos, new_start_pos))&& (ElementLessThan(old_end, new_end, old_end_pos, new_end_pos))) { if (new_start_pos != 0) { EraseSelection(hw, old_start, new_start, old_start_pos, new_start_pos - 1); } else { EraseSelection(hw, old_start, new_start->prev, old_start_pos, new_start->prev->edata_len - 2); } if (old_end_pos < (old_end->edata_len - 2)) { DrawSelection(hw, old_end, new_end, old_end_pos + 1, new_end_pos); } else { DrawSelection(hw, old_end->next, new_end, 0, new_end_pos); } } else if ((ElementLessThan(new_start, old_start, new_start_pos, old_start_pos))&& (ElementLessThan(new_end, old_end, new_end_pos, old_end_pos))) { if (old_start_pos != 0) { DrawSelection(hw, new_start, old_start, new_start_pos, old_start_pos - 1); } else { DrawSelection(hw, new_start, old_start->prev, new_start_pos, old_start->prev->edata_len - 2); } if (new_end_pos < (new_end->edata_len - 2)) { EraseSelection(hw, new_end, old_end, new_end_pos + 1, old_end_pos); } else { EraseSelection(hw, new_end->next, old_end, 0, old_end_pos); } } else if ((ElementLessThan(new_start, old_start, new_start_pos, old_start_pos))|| (ElementLessThan(old_end, new_end, old_end_pos, new_end_pos))) { if ((new_start != old_start)||(new_start_pos != old_start_pos)) { if (old_start_pos != 0) { DrawSelection(hw, new_start, old_start, new_start_pos, old_start_pos - 1); } else { DrawSelection(hw, new_start, old_start->prev, new_start_pos, old_start->prev->edata_len - 2); } } if ((old_end != new_end)||(old_end_pos != new_end_pos)) { if (old_end_pos < (old_end->edata_len - 2)) { DrawSelection(hw, old_end, new_end, old_end_pos + 1, new_end_pos); } else { DrawSelection(hw, old_end->next, new_end, 0, new_end_pos); } } } else { if ((old_start != new_start)||(old_start_pos != new_start_pos)) { if (new_start_pos != 0) { EraseSelection(hw, old_start, new_start, old_start_pos, new_start_pos - 1); } else { EraseSelection(hw, old_start, new_start->prev, old_start_pos, new_start->prev->edata_len - 2); } } if ((new_end != old_end)||(new_end_pos != old_end_pos)) { if (new_end_pos < (new_end->edata_len - 2)) { EraseSelection(hw, new_end, old_end, new_end_pos + 1, old_end_pos); } else { EraseSelection(hw, new_end->next, old_end, 0, old_end_pos); } } } } static void SelectStart(w, event, params, num_params) Widget w; XEvent *event; String *params; /* unused */ Cardinal *num_params; /* unused */ { HTMLWidget hw = (HTMLWidget)XtParent(w); XButtonPressedEvent *BuEvent = (XButtonPressedEvent *)event; struct ele_rec *eptr; int epos; if (XtClass(XtParent(w)) != htmlWidgetClass) { return; } #ifdef NOT_RIGHT XUndefineCursor(XtDisplay(hw->html.view), XtWindow(hw->html.view)); #endif XUndefineCursor(XtDisplay(hw), XtWindow(hw)); /* * Because X sucks, we can get the button pressed in the window, but * released out of the window. This will highlight some text, but * never complete the selection. Now on the next button press we * have to clean up this mess. */ EraseSelection(hw, hw->html.new_start, hw->html.new_end, hw->html.new_start_pos, hw->html.new_end_pos); /* * We want to erase the currently selected text, but still save the * selection internally in case we don't create a new one. */ EraseSelection(hw, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos); hw->html.new_start = hw->html.select_start; hw->html.new_end = NULL; hw->html.new_start_pos = hw->html.sel_start_pos; hw->html.new_end_pos = 0; eptr = LocateElement(hw, BuEvent->x, BuEvent->y, &epos); if (eptr != NULL) { /* * If this is an anchor assume for now we are activating it * and not selecting it. */ if (eptr->anchorHRef != NULL) { hw->html.active_anchor = eptr; hw->html.press_x = BuEvent->x; hw->html.press_y = BuEvent->y; SetAnchor(hw); } /* * Else if we are on an image we can't select text so * pretend we got eptr==NULL, and exit here. */ else if (eptr->type == E_IMAGE) { hw->html.new_start = NULL; hw->html.new_end = NULL; hw->html.new_start_pos = 0; hw->html.new_end_pos = 0; hw->html.press_x = BuEvent->x; hw->html.press_y = BuEvent->y; hw->html.but_press_time = BuEvent->time; return; } /* * Else if we used button2, we can't select text, so exit * here. */ else if (BuEvent->button == Button2) { hw->html.press_x = BuEvent->x; hw->html.press_y = BuEvent->y; hw->html.but_press_time = BuEvent->time; return; } /* * Else a single click will not select a new object * but it will prime that selection on the next mouse * move. * Ignore special internal text */ else if (eptr->internal == False) { hw->html.new_start = eptr; hw->html.new_start_pos = epos; hw->html.new_end = NULL; hw->html.new_end_pos = 0; hw->html.press_x = BuEvent->x; hw->html.press_y = BuEvent->y; } else { hw->html.new_start = NULL; hw->html.new_end = NULL; hw->html.new_start_pos = 0; hw->html.new_end_pos = 0; hw->html.press_x = BuEvent->x; hw->html.press_y = BuEvent->y; } } else { hw->html.new_start = NULL; hw->html.new_end = NULL; hw->html.new_start_pos = 0; hw->html.new_end_pos = 0; hw->html.press_x = BuEvent->x; hw->html.press_y = BuEvent->y; } hw->html.but_press_time = BuEvent->time; } static void ExtendStart(w, event, params, num_params) Widget w; XEvent *event; String *params; /* unused */ Cardinal *num_params; /* unused */ { HTMLWidget hw = (HTMLWidget)XtParent(w); XButtonPressedEvent *BuEvent = (XButtonPressedEvent *)event; struct ele_rec *eptr; struct ele_rec *start, *end; struct ele_rec *old_start, *old_end; int old_start_pos, old_end_pos; int start_pos, end_pos; int epos; if (XtClass(XtParent(w)) != htmlWidgetClass) { return; } eptr = LocateElement(hw, BuEvent->x, BuEvent->y, &epos); /* * Ignore IMAGE elements. */ if ((eptr != NULL)&&(eptr->type == E_IMAGE)) { eptr = NULL; } /* * Ignore NULL elements. * Ignore special internal text * documents. */ if ((eptr != NULL)&&(eptr->internal == False)) { old_start = hw->html.new_start; old_start_pos = hw->html.new_start_pos; old_end = hw->html.new_end; old_end_pos = hw->html.new_end_pos; if (hw->html.new_start == NULL) { hw->html.new_start = hw->html.select_start; hw->html.new_start_pos = hw->html.sel_start_pos; hw->html.new_end = hw->html.select_end; hw->html.new_end_pos = hw->html.sel_end_pos; } else { hw->html.new_end = eptr; hw->html.new_end_pos = epos; } if (SwapElements(hw->html.new_start, hw->html.new_end, hw->html.new_start_pos, hw->html.new_end_pos)) { if (SwapElements(eptr, hw->html.new_end, epos, hw->html.new_end_pos)) { start = hw->html.new_end; start_pos = hw->html.new_end_pos; end = eptr; end_pos = epos; } else { start = hw->html.new_start; start_pos = hw->html.new_start_pos; end = eptr; end_pos = epos; } } else { if (SwapElements(eptr, hw->html.new_start, epos, hw->html.new_start_pos)) { start = hw->html.new_start; start_pos = hw->html.new_start_pos; end = eptr; end_pos = epos; } else { start = hw->html.new_end; start_pos = hw->html.new_end_pos; end = eptr; end_pos = epos; } } if (start == NULL) { start = eptr; start_pos = epos; } if (old_start == NULL) { hw->html.new_start = hw->html.select_start; hw->html.new_end = hw->html.select_end; hw->html.new_start_pos = hw->html.sel_start_pos; hw->html.new_end_pos = hw->html.sel_end_pos; } else { hw->html.new_start = old_start; hw->html.new_end = old_end; hw->html.new_start_pos = old_start_pos; hw->html.new_end_pos = old_end_pos; } ChangeSelection(hw, start, end, start_pos, end_pos); hw->html.new_start = start; hw->html.new_end = end; hw->html.new_start_pos = start_pos; hw->html.new_end_pos = end_pos; } else { if (hw->html.new_start == NULL) { hw->html.new_start = hw->html.select_start; hw->html.new_start_pos = hw->html.sel_start_pos; hw->html.new_end = hw->html.select_end; hw->html.new_end_pos = hw->html.sel_end_pos; } } hw->html.press_x = BuEvent->x; hw->html.press_y = BuEvent->y; } static void ExtendAdjust(w, event, params, num_params) Widget w; XEvent *event; String *params; /* unused */ Cardinal *num_params; /* unused */ { HTMLWidget hw = (HTMLWidget)XtParent(w); XPointerMovedEvent *MoEvent = (XPointerMovedEvent *)event; struct ele_rec *eptr; struct ele_rec *start, *end; int start_pos, end_pos; int epos; if (XtClass(XtParent(w)) != htmlWidgetClass) { return; } /* * Very small mouse motion immediately after button press * is ignored. */ if ((ABS((hw->html.press_x - MoEvent->x)) <= SELECT_THRESHOLD)&& (ABS((hw->html.press_y - MoEvent->y)) <= SELECT_THRESHOLD)) { return; } /* * If we have an active anchor and we got here, we have moved the * mouse too far. Deactivate anchor, and prime a selection. * If the anchor is internal text, don't * prime a selection. */ if (hw->html.active_anchor != NULL) { eptr = hw->html.active_anchor; UnsetAnchor(hw); if (eptr->internal == False) { hw->html.new_start = NULL; hw->html.new_start_pos = 0; hw->html.new_end = NULL; hw->html.new_end_pos = 0; } } /* * If we used button2, we can't select text, so * clear selection and exit here. */ if ((MoEvent->state & Button2Mask) != 0) { hw->html.select_start = NULL; hw->html.select_end = NULL; hw->html.sel_start_pos = 0; hw->html.sel_end_pos = 0; hw->html.new_start = NULL; hw->html.new_end = NULL; hw->html.new_start_pos = 0; hw->html.new_end_pos = 0; return; } eptr = LocateElement(hw, MoEvent->x, MoEvent->y, &epos); /* * If we are on an image pretend we are nowhere * and just return; */ if ((eptr != NULL)&&(eptr->type == E_IMAGE)) { return; } /* * Ignore NULL items. * Ignore if the same as last selected item and position. * Ignore special internal text */ if ((eptr != NULL)&& ((eptr != hw->html.new_end)||(epos != hw->html.new_end_pos))&& (eptr->internal == False)) { start = hw->html.new_start; start_pos = hw->html.new_start_pos; end = eptr; end_pos = epos; if (start == NULL) { start = eptr; start_pos = epos; } ChangeSelection(hw, start, end, start_pos, end_pos); hw->html.new_start = start; hw->html.new_end = end; hw->html.new_start_pos = start_pos; hw->html.new_end_pos = end_pos; } } static void ExtendEnd(w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { HTMLWidget hw = (HTMLWidget)XtParent(w); XButtonReleasedEvent *BuEvent = (XButtonReleasedEvent *)event; struct ele_rec *eptr; struct ele_rec *start, *end; Atom *atoms; int i, buffer; int start_pos, end_pos; int epos; char *text; if (XtClass(XtParent(w)) != htmlWidgetClass) { return; } eptr = LocateElement(hw, BuEvent->x, BuEvent->y, &epos); /* * If we just released button one or two, and we are on an object, * and we have an active anchor, and we are on the active anchor, * and if we havn't waited too long. Activate that anchor. */ if (((BuEvent->button == Button1)||(BuEvent->button == Button2))&& (eptr != NULL)&& (hw->html.active_anchor != NULL)&& (eptr == hw->html.active_anchor)&& ((BuEvent->time - hw->html.but_press_time) < CLICK_TIME)) { _HTMLInput(w, event, params, num_params); return; } else if (hw->html.active_anchor != NULL) { start = hw->html.active_anchor; UnsetAnchor(hw); if (start->internal == False) { hw->html.new_start = eptr; hw->html.new_start_pos = epos; hw->html.new_end = NULL; hw->html.new_end_pos = 0; } } /* * If we used button2, we can't select text, so clear * selection and exit here. */ if (BuEvent->button == Button2) { hw->html.new_start = hw->html.select_start; hw->html.new_end = NULL; hw->html.new_start_pos = hw->html.sel_start_pos; hw->html.new_end_pos = 0; return; } /* * If we are on an image, pretend we are nowhere * and NULL out the eptr */ if ((eptr != NULL)&&(eptr->type == E_IMAGE)) { eptr = NULL; } /* * If button released on a NULL item, take the last non-NULL * item that we highlighted. */ if ((eptr == NULL)&&(hw->html.new_end != NULL)) { eptr = hw->html.new_end; epos = hw->html.new_end_pos; } if ((eptr != NULL)&&(eptr->internal == False)&& (hw->html.new_end != NULL)) { start = hw->html.new_start; start_pos = hw->html.new_start_pos; end = eptr; end_pos = epos; if (start == NULL) { start = eptr; start_pos = epos; } ChangeSelection(hw, start, end, start_pos, end_pos); hw->html.select_start = start; hw->html.sel_start_pos = start_pos; hw->html.select_end = end; hw->html.sel_end_pos = end_pos; SetSelection(hw); hw->html.new_start = NULL; hw->html.new_end = NULL; hw->html.new_start_pos = 0; hw->html.new_end_pos = 0; atoms = (Atom *)malloc(*num_params * sizeof(Atom)); if (atoms == NULL) { fprintf(stderr, "cannot allocate atom list\n"); return; } XmuInternStrings(XtDisplay((Widget)hw), params, *num_params, atoms); hw->html.selection_time = BuEvent->time; for (i=0; i< *num_params; i++) { switch (atoms[i]) { case XA_CUT_BUFFER0: buffer = 0; break; case XA_CUT_BUFFER1: buffer = 1; break; case XA_CUT_BUFFER2: buffer = 2; break; case XA_CUT_BUFFER3: buffer = 3; break; case XA_CUT_BUFFER4: buffer = 4; break; case XA_CUT_BUFFER5: buffer = 5; break; case XA_CUT_BUFFER6: buffer = 6; break; case XA_CUT_BUFFER7: buffer = 7; break; default: buffer = -1; break; } if (buffer >= 0) { if (hw->html.fancy_selections == True) { text = ParseTextToPrettyString(hw, hw->html.formatted_elements, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); } else { text = ParseTextToString( hw->html.formatted_elements, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); } XStoreBuffer(XtDisplay((Widget)hw), text, strlen(text), buffer); if (text != NULL) { free(text); } } else { XtOwnSelection((Widget)hw, atoms[i], BuEvent->time, (XtConvertSelectionProc )ConvertSelection, (XtLoseSelectionProc )LoseSelection, (XtSelectionDoneProc )SelectionDone); } } free((char *)atoms); } else if (eptr == NULL) { hw->html.select_start = NULL; hw->html.sel_start_pos = 0; hw->html.select_end = NULL; hw->html.sel_end_pos = 0; hw->html.new_start = NULL; hw->html.new_start_pos = 0; hw->html.new_end = NULL; hw->html.new_end_pos = 0; } } #define LEAVING_ANCHOR(hw) \ hw->html.cached_tracked_ele = NULL; \ (*(pointerTrackProc) \ (hw->html.pointer_motion_callback))(hw, hw->html.pm_client_data, ""); \ XUndefineCursor (XtDisplay (hw), XtWindow (hw)); /* KNOWN PROBLEM: We never get LeaveNotify or FocusOut events, despite the fact we've requested them. Bummer. */ static void TrackMotion(w, event, params, num_params) Widget w; XEvent *event; String *params; /* unused */ Cardinal *num_params; /* unused */ { HTMLWidget hw = (HTMLWidget)XtParent(w); struct ele_rec *eptr; int epos, x, y; if (XtClass(XtParent(w)) != htmlWidgetClass) { return; } if (!hw->html.pointer_motion_callback) return; if (event->type == MotionNotify) { x = ((XMotionEvent *)event)->x; y = ((XMotionEvent *)event)->y; } else if (event->type == LeaveNotify || event->type == FocusOut || event->type == Expose) { /* Wipe out. */ if (hw->html.cached_tracked_ele) { LEAVING_ANCHOR (hw); } return; } else { return; } eptr = LocateElement(hw, x, y, &epos); /* We're hitting a new anchor if eptr exists and eptr != cached tracked element and anchorHRef != NULL. */ if (eptr != NULL && eptr != hw->html.cached_tracked_ele && eptr->anchorHRef != NULL) { hw->html.cached_tracked_ele = eptr; (*(pointerTrackProc) (hw->html.pointer_motion_callback)) (hw, hw->html.pm_client_data, eptr->anchorHRef); XDefineCursor (XtDisplay (hw), XtWindow (hw), in_anchor_cursor); } /* We're leaving an anchor if eptr exists and a cached ele exists and we're not entering a new anchor. */ else if (eptr != NULL && hw->html.cached_tracked_ele != NULL && eptr->anchorHRef == NULL) { LEAVING_ANCHOR (hw); } return; } /* * Scroll display vertically. */ static void Scroll (w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { register HTMLWidget hw = (HTMLWidget)XtParent(w); #ifdef MOTIF int val, size, inc, pageinc; #endif int offset, newy; if (XtClass(XtParent(w)) != htmlWidgetClass) return; if (*num_params > 0) { char *s = params[0]; double fraction; double atof(); int ch; if (strcmp (s + strlen(s) - 2, "ch") == 0) { fraction = atof (s); ch = (hw->html.font->max_bounds.ascent + hw->html.font->max_bounds.descent); offset = fraction * ch; } else offset = (int)hw->html.view_height * atof(s); } else offset = (int)hw->html.view_height / 10; newy = hw->html.scroll_y + offset; if (newy < 0) newy = 0; if (newy > (hw->html.doc_height - (int)hw->html.view_height)) newy = hw->html.doc_height - (int)hw->html.view_height; if (newy < 0) newy = 0; #ifdef MOTIF XmScrollBarGetValues(hw->html.vbar, &val, &size, &inc, &pageinc); XmScrollBarSetValues(hw->html.vbar, newy, size, inc, pageinc, True); XmScrollBarGetValues(hw->html.hbar, &val, &size, &inc, &pageinc); XmScrollBarSetValues(hw->html.hbar, 0, size, inc, pageinc, True); #else ScrollToPos(hw->html.vbar, hw, newy); ScrollToPos(hw->html.hbar, hw, 0); setScrollBar(hw->html.vbar, newy, hw->html.doc_height, (int)hw->html.view_height); #endif } /* * Process mouse input to the HTML widget * Currently only processes an anchor-activate when Button1 * is pressed */ static void #ifdef _NO_PROTO _HTMLInput( w, event, params, num_params) Widget w ; XEvent *event ; String *params; /* unused */ Cardinal *num_params; /* unused */ #else _HTMLInput( Widget w, XEvent *event, String *params, /* unused */ Cardinal *num_params) /* unused */ #endif { HTMLWidget hw = (HTMLWidget)XtParent(w); struct ele_rec *eptr; WbAnchorCallbackData cbdata; int epos; #ifdef MOTIF Boolean on_gadget; #endif /* MOTIF */ if (XtClass(XtParent(w)) != htmlWidgetClass) { return; } #ifdef MOTIF /* * If motif is defined, we don't want to process this button press * if it is on a gadget */ #ifdef MOTIF1_2 on_gadget = (_XmInputForGadget((Widget)hw, #else on_gadget = (_XmInputForGadget((CompositeWidget)hw, #endif /* MOTIF1_2 */ event->xbutton.x, event->xbutton.y) != NULL); if (on_gadget) { return; } #endif /* MOTIF */ if (event->type == ButtonRelease) { eptr = LocateElement(hw, event->xbutton.x, event->xbutton.y, &epos); if (eptr != NULL) { if (eptr->anchorHRef != NULL) { char *tptr, *ptr; /* * Save the anchor text, replace newlines with * spaces. */ tptr = ParseTextToString(hw->html.select_start, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); ptr = tptr; while ((ptr != NULL)&&(*ptr != '\0')) { if (*ptr == '\n') { *ptr = ' '; } ptr++; } /* * Clear the activated anchor */ UnsetAnchor(hw); #ifdef EXTRA_FLUSH XFlush(XtDisplay(hw)); #endif if ((IsDelayedHRef(hw, eptr->anchorHRef))&& (hw->html.resolveDelayedImage != NULL)) { eptr->pic_data = (*(resolveImageProc) (hw->html.resolveDelayedImage))(hw, eptr->edata); if (eptr->pic_data == NULL) { eptr->pic_data = NoImageData(hw); eptr->pic_data->delayed = 0; eptr->pic_data->internal = 0; } else { 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; } /* * See if this is a special * internal image */ if ((eptr->edata != NULL)&& (strncmp(eptr->edata, INTERNAL_IMAGE, strlen(INTERNAL_IMAGE)) == 0)) { eptr->pic_data->internal = 1; } else { eptr->pic_data->internal = 0; } } ReformatWindow(hw); ScrollWidgets(hw); ViewClearAndRefresh(hw); } else if ((eptr->pic_data != NULL)&& (eptr->pic_data->delayed)&& (eptr->anchorHRef != NULL)&& (IsIsMapForm(hw, eptr->anchorHRef))) { eptr->pic_data = (*(resolveImageProc) (hw->html.resolveDelayedImage))(hw, eptr->edata); if (eptr->pic_data == NULL) { eptr->pic_data = NoImageData(hw); eptr->pic_data->delayed = 0; eptr->pic_data->internal = 0; } else { 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; } /* * See if this is a special * internal image */ if ((eptr->edata != NULL)&& (strncmp(eptr->edata, INTERNAL_IMAGE, strlen(INTERNAL_IMAGE)) == 0)) { eptr->pic_data->internal = 1; } else { eptr->pic_data->internal = 0; } } ReformatWindow(hw); ScrollWidgets(hw); ViewClearAndRefresh(hw); } else if ((eptr->pic_data != NULL)&& (eptr->pic_data->delayed)&& (eptr->anchorHRef != NULL)&& (!IsDelayedHRef(hw, eptr->anchorHRef))&& (!IsIsMapForm(hw, eptr->anchorHRef))&& (((event->xbutton.y + hw->html.scroll_y) - (eptr->y + eptr->y_offset)) > AnchoredHeight(hw))) { eptr->pic_data = (*(resolveImageProc) (hw->html.resolveDelayedImage))(hw, eptr->edata); if (eptr->pic_data == NULL) { eptr->pic_data = NoImageData(hw); eptr->pic_data->delayed = 0; eptr->pic_data->internal = 0; } else { /* * Mark images we have sucessfully * loaded at least once */ if (eptr->pic_data->image_data != NULL) { eptr->pic_data->fetched = 1; } /* * See if this is a special * internal image */ if ((eptr->edata != NULL)&& (strncmp(eptr->edata, INTERNAL_IMAGE, strlen(INTERNAL_IMAGE)) == 0)) { eptr->pic_data->internal = 1; } else { eptr->pic_data->internal = 0; } } eptr->pic_data->delayed = 0; ReformatWindow(hw); ScrollWidgets(hw); ViewClearAndRefresh(hw); } else if ((eptr->pic_data != NULL)&& (eptr->pic_data->ismap)&& (eptr->anchorHRef != NULL)&& (IsIsMapForm(hw, eptr->anchorHRef))) { int form_x, form_y; form_x = event->xbutton.x + hw->html.scroll_x - eptr->x; form_y = event->xbutton.y + hw->html.scroll_y - eptr->y; ImageSubmitForm(eptr->pic_data->fptr, event, eptr->pic_data->text, form_x, form_y); } else { /* The following is a hack to send the * selection location along with the HRef * for images. This allows you to * point at a location on a map and have * the server send you the related document. * Tony Sanders, April 1993 */ int alloced = 0; char *buf = eptr->anchorHRef; if (eptr->type == E_IMAGE && eptr->pic_data && eptr->pic_data->ismap) { buf = (char *) malloc(strlen(eptr->anchorHRef) + 256); alloced = 1; sprintf(buf, "%s?%d,%d", eptr->anchorHRef, event->xbutton.x + hw->html.scroll_x - eptr->x, event->xbutton.y + hw->html.scroll_y - eptr->y); } /* * XXX: should call a media dependent * function that decides how to munge the * HRef. For example mpeg data will want * to know on what frame the event occured. * * cddata.href = *(eptr->eventf)(eptr, event); */ cbdata.event = event; cbdata.element_id = eptr->ele_id; cbdata.href = buf; /* cbdata.href = eptr->anchorHRef; */ cbdata.text = tptr; XtCallCallbackList ((Widget)hw, hw->html.anchor_callback, (XtPointer)&cbdata); if (alloced) free(buf); if (tptr != NULL) free(tptr); } } } } return; } #ifndef MOTIF #include /* * Process key input passwd widgets */ static void #ifdef _NO_PROTO _HTMLpwdInput( w, event, params, num_params) Widget w ; XEvent *event ; String *params; /* unused */ Cardinal *num_params; /* unused */ #else _HTMLpwdInput( Widget w, XEvent *event, String *params, /* unused */ Cardinal *num_params) /* unused */ #endif { char buffer[50]; KeySym ks; char *keySymString; char *star = "*"; int length, passwdLength, i, insertPos, maxLength; Boolean stringInPlace; if (event->type == KeyPress) { HTMLWidget hw = (HTMLWidget)XtParent(XtParent(w)); WidgetInfo *wptr; if (XtClass((Widget)hw) != htmlWidgetClass) return; /* it was not for us */ /* * find the structure for this widget */ wptr = hw->html.widget_list; while (wptr != NULL) { if (wptr->w == w) break; wptr = wptr->next; } if (wptr == NULL) return; passwdLength = wptr->password ? strlen(wptr->password) : 0; length = XLookupString((XKeyEvent *)event, buffer, 50, &ks, NULL); keySymString = XKeysymToString(ks); XtVaGetValues(w, XtNinsertPosition,&insertPos, XtNuseStringInPlace, &stringInPlace, XtNlength, &maxLength, NULL); if (maxLength<1) maxLength = 1000000; if (!strcmp("Left",keySymString)) { if (insertPos > 0) XtVaSetValues(w,XtNinsertPosition,--insertPos,NULL); return; } else if (!strcmp("Right",keySymString)) { if (insertPos < passwdLength) XtVaSetValues(w,XtNinsertPosition,++insertPos,NULL); return; } if ((!strcmp("BackSpace",keySymString)) || (!strcmp("Backspace",keySymString)) || (!strcmp("Delete",keySymString)) ) { insertPos --; if (passwdLength>0) { char *pwd = &(wptr->password[insertPos]); for (i = passwdLength - insertPos; i>0; i--,pwd++) *pwd = *(pwd+1); /* fprintf(stderr,"modified passwd <%s>\n", wptr->password);*/ XtCallActionProc(w, insertPos>-1 ? "delete-previous-character" : "delete-next-character", event, NULL,0); } /* else nothing to erase */ return; } if (length == 1) { buffer[1] = '\0'; if (passwdLength>0) { if (passwdLength < maxLength) { char *pwd = wptr->password = (char *)realloc(wptr->password, sizeof(char)*(passwdLength+2)); for (i=passwdLength+1; i>insertPos; i--) pwd[i] = pwd[i-1]; pwd[insertPos] = buffer[0]; } } else { if (wptr->password == NULL) wptr->password = (char *)malloc(sizeof(char)*2); wptr->password[0] = buffer[0]; wptr->password[1] = '\0'; } if (stringInPlace && passwdLength\n", wptr->password); */ } } } #endif /* not MOTIF */ /* * SetValues is called when XtSetValues is used to change resources in this * widget. */ static Boolean #ifdef _NO_PROTO SetValues (current, request, new) HTMLWidget current ; HTMLWidget request ; HTMLWidget new ; #else SetValues( HTMLWidget current, HTMLWidget request, HTMLWidget new) #endif { int reformatted; /* * Make sure the underline numbers are within bounds. */ if (request->html.num_anchor_underlines < 0) { new->html.num_anchor_underlines = 0; } if (request->html.num_anchor_underlines > MAX_UNDERLINES) { new->html.num_anchor_underlines = MAX_UNDERLINES; } if (request->html.num_visitedAnchor_underlines < 0) { new->html.num_visitedAnchor_underlines = 0; } if (request->html.num_visitedAnchor_underlines > MAX_UNDERLINES) { new->html.num_visitedAnchor_underlines = MAX_UNDERLINES; } reformatted = 0; if ((request->html.raw_text != current->html.raw_text)|| (request->html.header_text != current->html.header_text)|| (request->html.footer_text != current->html.footer_text)) { /* * Free up the old visited href list. */ FreeHRefs(current->html.my_visited_hrefs); new->html.my_visited_hrefs = NULL; /* * Free up the old visited delayed images list. */ FreeDelayedImages(current->html.my_delayed_images); new->html.my_delayed_images = NULL; /* * Free any old colors and pixmaps */ FreeColors(XtDisplay(current), DefaultColormapOfScreen(XtScreen(current))); FreeImages(current); /* * Hide any old widgets */ HideWidgets(current); new->html.widget_list = NULL; new->html.form_list = NULL; /* * Parse the raw text with the HTML parser */ new->html.html_objects = HTMLParse(current->html.html_objects, request->html.raw_text); CallLinkCallbacks(new); new->html.html_header_objects = HTMLParse(current->html.html_header_objects, request->html.header_text); new->html.html_footer_objects = HTMLParse(current->html.html_footer_objects, request->html.footer_text); /* * Redisplay for the changed data. */ { new->html.scroll_x = 0; new->html.scroll_y = 0; new->html.max_pre_width = DocumentWidth(new, new->html.html_objects); ReformatWindow(new); ViewClearAndRefresh(new); reformatted = 1; } /* * Clear any previous selection */ new->html.select_start = NULL; new->html.select_end = NULL; new->html.sel_start_pos = 0; new->html.sel_end_pos = 0; new->html.new_start = NULL; new->html.new_end = NULL; new->html.new_start_pos = 0; new->html.new_end_pos = 0; new->html.active_anchor = NULL; } else if ((request->html.font != current->html.font)|| (request->html.italic_font != current->html.italic_font)|| (request->html.bold_font != current->html.bold_font)|| (request->html.fixed_font != current->html.fixed_font)|| (request->html.fixedbold_font != current->html.fixedbold_font)|| (request->html.fixeditalic_font != current->html.fixeditalic_font)|| (request->html.header1_font != current->html.header1_font)|| (request->html.header2_font != current->html.header2_font)|| (request->html.header3_font != current->html.header3_font)|| (request->html.header4_font != current->html.header4_font)|| (request->html.header5_font != current->html.header5_font)|| (request->html.header6_font != current->html.header6_font)|| (request->html.address_font != current->html.address_font)|| (request->html.plain_font != current->html.plain_font)|| (request->html.plainbold_font != current->html.plainbold_font)|| (request->html.plainitalic_font != current->html.plainitalic_font)|| (request->html.listing_font != current->html.listing_font)|| (request->html.activeAnchor_fg != current->html.activeAnchor_fg)|| (request->html.activeAnchor_bg != current->html.activeAnchor_bg)|| (request->html.anchor_fg != current->html.anchor_fg)|| (request->html.visitedAnchor_fg != current->html.visitedAnchor_fg)|| (request->html.dashed_anchor_lines != current->html.dashed_anchor_lines)|| (request->html.dashed_visitedAnchor_lines != current->html.dashed_visitedAnchor_lines)|| (request->html.num_anchor_underlines != current->html.num_anchor_underlines)|| (request->html.num_visitedAnchor_underlines != current->html.num_visitedAnchor_underlines)) { if ((request->html.plain_font != current->html.plain_font)|| (request->html.listing_font != current->html.listing_font)) { new->html.max_pre_width = DocumentWidth(new, new->html.html_objects); } ReformatWindow(new); ScrollWidgets(new); ViewClearAndRefresh(new); reformatted = 1; } /* * image borders have been changed */ if (request->html.border_images != current->html.border_images) { ReformatWindow(new); ScrollWidgets(new); ViewClearAndRefresh(new); reformatted = 1; } /* * vertical space has been changed */ if(request->html.percent_vert_space != current->html.percent_vert_space) { ReformatWindow(new); ScrollWidgets(new); ViewClearAndRefresh(new); reformatted = 1; } /* * Set client data for retest anchors callback. */ if(request->html.vt_client_data != current->html.vt_client_data) { new->html.vt_client_data = request->html.vt_client_data; } /* * Set client data for pointer motion callback. */ if(request->html.pm_client_data != current->html.pm_client_data) { new->html.pm_client_data = request->html.pm_client_data; } return(False); } /* * Go through the parsed marks and for all the tags in the document * call the LinkCallback. */ static void #ifdef _NO_PROTO CallLinkCallbacks(hw) HTMLWidget hw; #else CallLinkCallbacks(HTMLWidget hw) #endif { struct mark_up *mptr; LinkInfo l_info; mptr = hw->html.html_objects; while (mptr != NULL) { if (mptr->type == M_BASE) { l_info.href = ParseMarkTag(mptr->start, MT_BASE, "HREF"); l_info.role = ParseMarkTag(mptr->start, MT_BASE, "ROLE"); XtCallCallbackList ((Widget)hw, hw->html.link_callback, (XtPointer)&l_info); if (l_info.href != NULL) { free(l_info.href); } if (l_info.role != NULL) { free(l_info.role); } } mptr = mptr->next; } } /* * Search through the whole document, and recolor the internal elements with * the passed HREF. */ static void #ifdef _NO_PROTO RecolorInternalHRefs(hw, href) HTMLWidget hw; char *href; #else RecolorInternalHRefs(HTMLWidget hw, char *href) #endif { struct ele_rec *start; unsigned long fg; fg = hw->html.visitedAnchor_fg; start = hw->html.formatted_elements; while (start != NULL) { /* * This one is internal * This one has an href * This is the href we want */ if ((start->internal == True)&& (start->anchorHRef != NULL)&& (strcmp(start->anchorHRef, href) == 0)) { start->fg = fg; start->underline_number = hw->html.num_visitedAnchor_underlines; start->dashed_underline = hw->html.dashed_visitedAnchor_lines; } start = start->next; } } static Boolean ConvertSelection(w, selection, target, type, value, length, format) Widget w; Atom *selection, *target, *type; caddr_t *value; unsigned long *length; int *format; { Display *d = XtDisplay(w); HTMLWidget hw = (HTMLWidget)w; char *text; if (hw->html.select_start == NULL) { return False; } if (*target == XA_TARGETS(d)) { Atom *targetP; Atom *std_targets; unsigned long std_length; XmuConvertStandardSelection( w, hw->html.selection_time, selection, target, type, (caddr_t*)&std_targets, &std_length, format); *length = std_length + 5; *value = (caddr_t)XtMalloc(sizeof(Atom)*(*length)); targetP = *(Atom**)value; *targetP++ = XA_STRING; *targetP++ = XA_TEXT(d); *targetP++ = XA_COMPOUND_TEXT(d); *targetP++ = XA_LENGTH(d); *targetP++ = XA_LIST_LENGTH(d); bcopy((char*)std_targets, (char*)targetP, sizeof(Atom)*std_length); XtFree((char*)std_targets); *type = XA_ATOM; *format = 32; return True; } if (*target == XA_STRING || *target == XA_TEXT(d) || *target == XA_COMPOUND_TEXT(d)) { if (*target == XA_COMPOUND_TEXT(d)) { *type = *target; } else { *type = XA_STRING; } if (hw->html.fancy_selections == True) { text = ParseTextToPrettyString(hw, hw->html.formatted_elements, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); } else { text = ParseTextToString(hw->html.formatted_elements, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); } *value = text; *length = strlen(*value); *format = 8; return True; } if (*target == XA_LIST_LENGTH(d)) { *value = XtMalloc(4); if (sizeof(long) == 4) { *(long*)*value = 1; } else { long temp = 1; bcopy( ((char*)&temp)+sizeof(long)-4, (char*)*value, 4); } *type = XA_INTEGER; *length = 1; *format = 32; return True; } if (*target == XA_LENGTH(d)) { if (hw->html.fancy_selections == True) { text = ParseTextToPrettyString(hw, hw->html.formatted_elements, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); } else { text = ParseTextToString(hw->html.formatted_elements, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); } *value = XtMalloc(4); if (sizeof(long) == 4) { *(long*)*value = strlen(text); } else { long temp = strlen(text); bcopy( ((char*)&temp)+sizeof(long)-4, (char*)*value, 4); } free(text); *type = XA_INTEGER; *length = 1; *format = 32; return True; } if (XmuConvertStandardSelection(w, hw->html.selection_time, selection, target, type, value, length, format)) { return True; } return False; } static void LoseSelection(w, selection) Widget w; Atom *selection; { HTMLWidget hw = (HTMLWidget)w; ClearSelection(hw); } static void SelectionDone(w, selection, target) Widget w; Atom *selection, *target; { /* empty proc so Intrinsics know we want to keep storage */ } /* ************************************************************************* ******************************* PUBLIC FUNCTIONS ************************ ************************************************************************* */ /* * Convenience function to return the text of the HTML document as a plain * ascii text string. * This function allocates memory for the returned string, that it is up * to the user to free. * Extra option flags "pretty" text to be returned. * when pretty is two or larger, Postscript is returned. The font used is * encoded in the pretty parameter: * pretty = 2: Times * pretty = 3: Helvetica * pretty = 4: New century schoolbook * pretty = 5: Lucida Bright */ char * #ifdef _NO_PROTO HTMLGetText (w, pretty) Widget w; int pretty; #else HTMLGetText(Widget w, int pretty) #endif { HTMLWidget hw = (HTMLWidget)w; char *text; char *tptr, *buf; struct ele_rec *start; struct ele_rec *end; text = NULL; start = hw->html.formatted_elements; end = start; while (end != NULL) { end = end->next; } if (pretty >= 2) { tptr = ParseTextToPSString(hw, start, start, end, 0, 0, hw->html.font->max_bounds.width, hw->html.margin_width , pretty-2); } else if (pretty) { tptr = ParseTextToPrettyString(hw, start, start, end, 0, 0, hw->html.font->max_bounds.width, hw->html.margin_width); } else { tptr = ParseTextToString(start, start, end, 0, 0, hw->html.font->max_bounds.width, hw->html.margin_width); } if (tptr != NULL) { if (text == NULL) { text = tptr; } else { buf = (char *)malloc(strlen(text) + strlen(tptr) + 1); strcpy(buf, text); strcat(buf, tptr); free(text); free(tptr); text = buf; } } return(text); } /* * Convenience function to return the element id of the element * nearest to the x,y coordinates passed in. * If there is no element there, return the first element in the * line we are on. If there we are on no line, either return the * beginning, or the end of the document. */ int #ifdef _NO_PROTO HTMLPositionToId(w, x, y) Widget w; int x, y; #else HTMLPositionToId(Widget w, int x, int y) #endif { HTMLWidget hw = (HTMLWidget)w; int i; int epos; struct ele_rec *eptr; eptr = LocateElement(hw, x, y, &epos); if (eptr == NULL) { x = x + hw->html.scroll_x; y = y + hw->html.scroll_y; eptr = hw->html.line_array[0]; for (i=0; ihtml.line_count; i++) { if (hw->html.line_array[i] == NULL) { continue; } else if (hw->html.line_array[i]->y <= y) { eptr = hw->html.line_array[i]; continue; } else { break; } } } /* * 0 means the very top of the document. We put you there for * unfound elements. * We also special case for when the scrollbar is at the * absolute top. */ if ((eptr == NULL)||(hw->html.scroll_y == 0)) { return(0); } else { return(eptr->ele_id); } } /* * Convenience function to return the position of the element * based on the element id passed in. * Function returns 1 on success and fills in x,y pixel values. * If there is no such element, x=0, y=0 and -1 is returned. */ int #ifdef _NO_PROTO HTMLIdToPosition(w, element_id, x, y) Widget w; int element_id; int *x, *y; #else HTMLIdToPosition(Widget w, int element_id, int *x, int *y) #endif { HTMLWidget hw = (HTMLWidget)w; struct ele_rec *start; struct ele_rec *eptr; eptr = NULL; start = hw->html.formatted_elements; while (start != NULL) { if (start->ele_id == element_id) { eptr = start; break; } start = start->next; } if (eptr == NULL) { *x = 0; *y = 0; return(-1); } else { *x = eptr->x; *y = eptr->y; return(1); } } /* * Convenience function to position the element * based on the element id passed at the top of the viewing area. * A passed in id of 0 means goto the top. */ void #ifdef _NO_PROTO HTMLGotoId(w, element_id) Widget w; int element_id; #else HTMLGotoId(Widget w, int element_id) #endif { HTMLWidget hw = (HTMLWidget)w; struct ele_rec *start; struct ele_rec *eptr; int newy; #ifdef MOTIF int val, size, inc, pageinc; #endif /* * If we have no scrollbar, just return. */ if (hw->html.use_vbar == False) { return; } /* * Find the element corrsponding to the id passed in. */ eptr = NULL; start = hw->html.formatted_elements; while (start != NULL) { if (start->ele_id == element_id) { eptr = start; break; } start = start->next; } /* * No such element, do nothing. */ if ((element_id != 0)&&(eptr == NULL)) { return; } if (element_id == 0) { newy = 0; } else { newy = eptr->y - 2; } if (newy < 0) { newy = 0; } if (newy > (hw->html.doc_height - (int)hw->html.view_height)) { newy = hw->html.doc_height - (int)hw->html.view_height; } if (newy < 0) { newy = 0; } #ifdef MOTIF XmScrollBarGetValues(hw->html.vbar, &val, &size, &inc, &pageinc); XmScrollBarSetValues(hw->html.vbar, newy, size, inc, pageinc, True); XmScrollBarGetValues(hw->html.hbar, &val, &size, &inc, &pageinc); XmScrollBarSetValues(hw->html.hbar, 0, size, inc, pageinc, True); #else ScrollToPos(hw->html.vbar, hw, newy); ScrollToPos(hw->html.hbar, hw, 0); setScrollBar(hw->html.vbar, newy, hw->html.doc_height, (int)hw->html.view_height); #endif } /* * Convenience function to return the position of the anchor * based on the anchor NAME passed. * Function returns 1 on success and fills in x,y pixel values. * If there is no such element, x=0, y=0 and -1 is returned. */ int #ifdef _NO_PROTO HTMLAnchorToPosition(w, name, x, y) Widget w; char *name; int *x, *y; #else HTMLAnchorToPosition(Widget w, char *name, int *x, int *y) #endif { HTMLWidget hw = (HTMLWidget)w; struct ele_rec *start; struct ele_rec *eptr; eptr = NULL; start = hw->html.formatted_elements; while (start != NULL) { if ((start->anchorName)&& (strcmp(start->anchorName, name) == 0)) { eptr = start; break; } start = start->next; } if (eptr == NULL) { *x = 0; *y = 0; return(-1); } else { *x = eptr->x; *y = eptr->y; return(1); } } /* * Convenience function to return the element id of the anchor * based on the anchor NAME passed. * Function returns id on success. * If there is no such element, 0 is returned. */ int #ifdef _NO_PROTO HTMLAnchorToId(w, name) Widget w; char *name; #else HTMLAnchorToId(Widget w, char *name) #endif { HTMLWidget hw = (HTMLWidget)w; struct ele_rec *start; struct ele_rec *eptr; /* * Find the passed anchor name */ eptr = NULL; start = hw->html.formatted_elements; while (start != NULL) { if ((start->anchorName)&& /* MF023 */ ((strcmp(start->anchorName, name) == 0) || (*name == '#' && strcmp(start->anchorName, &(name[1])) == 0)) ) { eptr = start; break; } start = start->next; } if (eptr == NULL) { return(0); } else { return(eptr->ele_id); } } /* * Convenience function to return the HREFs of all active anchors in the * document. * Function returns an array of strings and fills num_hrefs passed. * If there are no HREFs NULL returned. */ char ** #ifdef _NO_PROTO HTMLGetHRefs(w, num_hrefs) Widget w; int *num_hrefs; #else HTMLGetHRefs(Widget w, int *num_hrefs) #endif { HTMLWidget hw = (HTMLWidget)w; int cnt; struct ele_rec *start; struct ele_rec *list; struct ele_rec *eptr; char **harray; list = NULL; cnt = 0; /* * Construct a linked list of all the diffeent hrefs, counting * then as we go. */ start = hw->html.formatted_elements; while (start != NULL) { /* * This one has an HREF */ if (start->anchorHRef != NULL) { /* * Check to see if we already have * this HREF in our list. */ eptr = list; while (eptr != NULL) { if (strcmp(eptr->anchorHRef, start->anchorHRef) == 0) { break; } eptr = eptr->next; } /* * This HREF is not, in our list. Add it. * That is, if it's not an internal reference. */ if ((eptr == NULL)&&(start->internal == False)) { eptr = (struct ele_rec *) malloc(sizeof(struct ele_rec)); eptr->anchorHRef = start->anchorHRef; eptr->next = list; list = eptr; cnt++; } } start = start->next; } if (cnt == 0) { *num_hrefs = 0; return(NULL); } else { *num_hrefs = cnt; harray = (char **)malloc(sizeof(char *) * cnt); eptr = list; cnt--; while (eptr != NULL) { harray[cnt] = (char *) malloc(strlen(eptr->anchorHRef) + 1); strcpy(harray[cnt], eptr->anchorHRef); start = eptr; eptr = eptr->next; free((char *)start); cnt--; } return(harray); } } /* * Convenience function to return the SRCs of all images in the * document. * Function returns an array of strings and fills num_srcs passed. * If there are no SRCs NULL returned. */ char ** #ifdef _NO_PROTO HTMLGetImageSrcs(w, num_srcs) Widget w; int *num_srcs; #else HTMLGetImageSrcs(Widget w, int *num_srcs) #endif { HTMLWidget hw = (HTMLWidget)w; struct mark_up *mptr; int cnt; char *tptr; char **harray; cnt = 0; mptr = hw->html.html_objects; while (mptr != NULL) { if (mptr->type == M_IMAGE) { tptr = ParseMarkTag(mptr->start, MT_IMAGE, "SRC"); if ((tptr != NULL)&&(*tptr != '\0')) { cnt++; free(tptr); } } mptr = mptr->next; } if (cnt == 0) { *num_srcs = 0; return(NULL); } else { *num_srcs = cnt; harray = (char **)malloc(sizeof(char *) * cnt); mptr = hw->html.html_objects; cnt = 0; while (mptr != NULL) { if (mptr->type == M_IMAGE) { tptr = ParseMarkTag(mptr->start,MT_IMAGE,"SRC"); if ((tptr != NULL)&&(*tptr != '\0')) { harray[cnt] = tptr; cnt++; } } mptr = mptr->next; } return(harray); } } /* * Convenience function to return the link information * for all the tags in the document. * Function returns an array of LinkInfo structures and fills * num_links passed. * If there are no LINKs NULL returned. */ LinkInfo * #ifdef _NO_PROTO HTMLGetLinks(w, num_links) Widget w; int *num_links; #else HTMLGetLinks(Widget w, int *num_links) #endif { HTMLWidget hw = (HTMLWidget)w; struct mark_up *mptr; int cnt; char *tptr; LinkInfo *larray; cnt = 0; mptr = hw->html.html_objects; while (mptr != NULL) { if (mptr->type == M_BASE) { cnt++; } mptr = mptr->next; } if (cnt == 0) { *num_links = 0; return(NULL); } else { *num_links = cnt; larray = (LinkInfo *)malloc(sizeof(LinkInfo) * cnt); mptr = hw->html.html_objects; cnt = 0; while (mptr != NULL) { if (mptr->type == M_BASE) { tptr = ParseMarkTag(mptr->start, MT_BASE, "HREF"); larray[cnt].href = tptr; tptr = ParseMarkTag(mptr->start, MT_BASE, "ROLE"); larray[cnt].role = tptr; cnt++; } mptr = mptr->next; } return(larray); } } void * #ifdef _NO_PROTO HTMLGetWidgetInfo(w) Widget w; #else HTMLGetWidgetInfo(Widget w) #endif { HTMLWidget hw = (HTMLWidget)w; return((void *)hw->html.widget_list); } void #ifdef _NO_PROTO HTMLFreeImageInfo(w) Widget w; #else HTMLFreeImageInfo(Widget w) #endif { HTMLWidget hw = (HTMLWidget)w; FreeColors(XtDisplay(w), DefaultColormapOfScreen(XtScreen(w))); FreeImages(hw); } void #ifdef _NO_PROTO HTMLFreeWidgetInfo(ptr) void *ptr; #else HTMLFreeWidgetInfo(void *ptr) #endif { WidgetInfo *wptr = (WidgetInfo *)ptr; WidgetInfo *tptr; while (wptr != NULL) { tptr = wptr; wptr = wptr->next; if (tptr->w != NULL) { /* * This is REALLY DUMB, but X generates an expose event * for the destruction of the Widgte, even if it isn't * mapped at the time it is destroyed. * So I move the invisible widget to -1000,-1000 * before destroying it, to avoid a visible flash. */ XtMoveWidget(tptr->w, -1000, -1000); XtDestroyWidget(tptr->w); } if (tptr->name != NULL) { free(tptr->name); } if ((tptr->value != NULL)&&(tptr->type != W_OPTIONMENU)) { free(tptr->value); } free((char *)tptr); } } /* * Convenience function to redraw all active anchors in the * document. * Can also pass a new predicate function to check visited * anchors. If NULL passed for function, uses default predicate * function. */ void #ifdef _NO_PROTO HTMLRetestAnchors(w, testFunc, client_data) Widget w; visitTestProc testFunc; XtPointer client_data; #else HTMLRetestAnchors(Widget w, visitTestProc testFunc, XtPointer client_data) #endif { HTMLWidget hw = (HTMLWidget)w; struct ele_rec *start; int stat = 0; if (testFunc == NULL) { testFunc = (visitTestProc)hw->html.previously_visited_test; client_data = hw->html.vt_client_data; } /* * Search all elements */ start = hw->html.formatted_elements; while (start != NULL) { if ((start->internal == True)|| (start->anchorHRef == NULL)) { start = start->next; continue; } if (testFunc != NULL) { if ((stat=(*testFunc)(hw, client_data, start->anchorHRef))) { start->fg = hw->html.visitedAnchor_fg; start->underline_number = hw->html.num_visitedAnchor_underlines; start->dashed_underline = hw->html.dashed_visitedAnchor_lines; } else { start->fg = hw->html.anchor_fg; start->underline_number = hw->html.num_anchor_underlines; start->dashed_underline = hw->html.dashed_anchor_lines; } } else { start->fg = hw->html.anchor_fg; start->underline_number = hw->html.num_anchor_underlines; start->dashed_underline = hw->html.dashed_anchor_lines; } /* * Since the element may have changed, redraw it */ if (stat) { switch(start->type) { case E_TEXT: TextRefresh(hw, start, 0, (start->edata_len - 2)); break; case E_IMAGE: ImageRefresh(hw, start); break; case E_BULLET: BulletRefresh(hw, start); break; case E_LINEFEED: LinefeedRefresh(hw, start); break; } } start = start->next; } } void #ifdef _NO_PROTO HTMLClearSelection (w) Widget w; #else HTMLClearSelection(Widget w) #endif { LoseSelection (w, NULL); } /* * Set the current selection based on the ElementRefs passed in. * Both refs must be valid. */ void #ifdef _NO_PROTO HTMLSetSelection (w, start, end) Widget w; ElementRef *start; ElementRef *end; #else HTMLSetSelection(Widget w, ElementRef *start, ElementRef *end) #endif { HTMLWidget hw = (HTMLWidget)w; int found; struct ele_rec *eptr; struct ele_rec *e_start; struct ele_rec *e_end; int start_pos, end_pos; Atom *atoms; int i, buffer; char *text; char *params[2]; /* * If the starting position is not valid, fail the selection */ if ((start->id > 0)&&(start->pos >= 0)) { found = 0; eptr = hw->html.formatted_elements; while (eptr != NULL) { if (eptr->ele_id == start->id) { e_start = eptr; start_pos = start->pos; found = 1; break; } eptr = eptr->next; } if (!found) { return; } } /* * If the ending position is not valid, fail the selection */ if ((end->id > 0)&&(end->pos >= 0)) { found = 0; eptr = hw->html.formatted_elements; while (eptr != NULL) { if (eptr->ele_id == end->id) { e_end = eptr; end_pos = end->pos; found = 1; break; } eptr = eptr->next; } if (!found) { return; } } LoseSelection (w, NULL); /* * We expect the ElementRefs came from HTMLSearchText, so we know * that the end_pos is one past what we want to select. */ end_pos = end_pos - 1; /* * Sanify the position data */ if ((start_pos > 0)&&(start_pos >= e_start->edata_len - 1)) { start_pos = e_start->edata_len - 2; } if ((end_pos > 0)&&(end_pos >= e_end->edata_len - 1)) { end_pos = e_end->edata_len - 2; } hw->html.select_start = e_start; hw->html.sel_start_pos = start_pos; hw->html.select_end = e_end; hw->html.sel_end_pos = end_pos; SetSelection(hw); hw->html.new_start = NULL; hw->html.new_end = NULL; hw->html.new_start_pos = 0; hw->html.new_end_pos = 0; /* * Do all the gunk from the end of the ExtendEnd function */ params[0] = "PRIMARY"; params[1] = "CUT_BUFFER0"; atoms = (Atom *)malloc(2 * sizeof(Atom)); if (atoms == NULL) { fprintf(stderr, "cannot allocate atom list\n"); return; } XmuInternStrings(XtDisplay((Widget)hw), params, 2, atoms); hw->html.selection_time = CurrentTime; for (i=0; i< 2; i++) { switch (atoms[i]) { case XA_CUT_BUFFER0: buffer = 0; break; case XA_CUT_BUFFER1: buffer = 1; break; case XA_CUT_BUFFER2: buffer = 2; break; case XA_CUT_BUFFER3: buffer = 3; break; case XA_CUT_BUFFER4: buffer = 4; break; case XA_CUT_BUFFER5: buffer = 5; break; case XA_CUT_BUFFER6: buffer = 6; break; case XA_CUT_BUFFER7: buffer = 7; break; default: buffer = -1; break; } if (buffer >= 0) { if (hw->html.fancy_selections == True) { text = ParseTextToPrettyString(hw, hw->html.formatted_elements, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); } else { text = ParseTextToString( hw->html.formatted_elements, hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos, hw->html.font->max_bounds.width, hw->html.margin_width); } XStoreBuffer(XtDisplay((Widget)hw), text, strlen(text), buffer); free(text); } else { XtOwnSelection((Widget)hw, atoms[i], CurrentTime, (XtConvertSelectionProc )ConvertSelection, (XtLoseSelectionProc )LoseSelection, (XtSelectionDoneProc )SelectionDone); } } free((char *)atoms); } /* * Convenience function to return the text of the HTML document as a single * white space separated string, with pointers to the various start and * end points of selections. * This function allocates memory for the returned string, that it is up * to the user to free. */ char * #ifdef _NO_PROTO HTMLGetTextAndSelection (w, startp, endp, insertp) Widget w; char **startp; char **endp; char **insertp; #else HTMLGetTextAndSelection(Widget w, char **startp, char **endp, char **insertp) #endif { HTMLWidget hw = (HTMLWidget)w; int length; char *text; char *tptr; struct ele_rec *eptr; struct ele_rec *sel_start; struct ele_rec *sel_end; struct ele_rec *insert_start; int start_pos, end_pos, insert_pos; if (SwapElements(hw->html.select_start, hw->html.select_end, hw->html.sel_start_pos, hw->html.sel_end_pos)) { sel_end = hw->html.select_start; end_pos = hw->html.sel_start_pos; sel_start = hw->html.select_end; start_pos = hw->html.sel_end_pos; } else { sel_start = hw->html.select_start; start_pos = hw->html.sel_start_pos; sel_end = hw->html.select_end; end_pos = hw->html.sel_end_pos; } insert_start = hw->html.new_start; insert_pos = hw->html.new_start_pos; *startp = NULL; *endp = NULL; *insertp = NULL; length = 0; eptr = hw->html.formatted_elements; while (eptr != NULL) { /* * Skip the special internal text */ if (eptr->internal == True) { eptr = eptr->next; continue; } if (eptr->type == E_TEXT) { length = length + eptr->edata_len - 1; } else if (eptr->type == E_LINEFEED) { length = length + 1; } eptr = eptr->next; } text = (char *)malloc(length + 1); if (text == NULL) { fprintf(stderr, "No space for return string\n"); return(NULL); } strcpy(text, ""); tptr = text; eptr = hw->html.formatted_elements; while (eptr != NULL) { /* * Skip the special internal text */ if (eptr->internal == True) { eptr = eptr->next; continue; } if (eptr->type == E_TEXT) { if (eptr == sel_start) { *startp = (char *)(tptr + start_pos); } if (eptr == sel_end) { *endp = (char *)(tptr + end_pos); } if (eptr == insert_start) { *insertp = (char *)(tptr + insert_pos); } strcat(text, (char *)eptr->edata); tptr = tptr + eptr->edata_len - 1; } else if (eptr->type == E_LINEFEED) { if (eptr == sel_start) { *startp = tptr; } if (eptr == sel_end) { *endp = tptr; } if (eptr == insert_start) { *insertp = tptr; } strcat(text, " "); tptr = tptr + 1; } eptr = eptr->next; } return(text); } /* * Convenience function to set the raw text into the widget. * Forces a reparse and a reformat. * If any pointer is passed in as NULL that text is unchanged, * if a pointer points to an empty string, that text is set to NULL; * Also pass an element ID to set the view area to that section of the new * text. Finally pass an anchor NAME to set position of the new text * to that anchor. */ void #ifdef _NO_PROTO HTMLSetText (w, text, header_text, footer_text, element_id, target_anchor, ptr) Widget w; char *text; char *header_text; char *footer_text; int element_id; char *target_anchor; void *ptr; #else HTMLSetText(Widget w, char *text, char *header_text, char *footer_text, int element_id, char *target_anchor, void *ptr) #endif { HTMLWidget hw = (HTMLWidget)w; WidgetInfo *wptr = (WidgetInfo *)ptr; struct ele_rec *start; struct ele_rec *eptr; int newy; if ((text == NULL)&&(header_text == NULL)&&(footer_text == NULL)) { return; } /* * Free up the old visited href list. */ FreeHRefs(hw->html.my_visited_hrefs); hw->html.my_visited_hrefs = NULL; /* * Free up the old visited delayed images list. */ FreeDelayedImages(hw->html.my_delayed_images); hw->html.my_delayed_images = NULL; /* * Hide any old widgets */ HideWidgets(hw); hw->html.widget_list = wptr; hw->html.form_list = NULL; if (text != NULL) { if (*text == '\0') { text = NULL; } hw->html.raw_text = text; /* * Free any old colors and pixmaps */ FreeColors(XtDisplay(hw),DefaultColormapOfScreen(XtScreen(hw))); FreeImages(hw); /* * Parse the raw text with the HTML parser */ hw->html.html_objects = HTMLParse(hw->html.html_objects, hw->html.raw_text); CallLinkCallbacks(hw); } if (header_text != NULL) { if (*header_text == '\0') { header_text = NULL; } hw->html.header_text = header_text; /* * Parse the header text with the HTML parser */ hw->html.html_header_objects = HTMLParse(hw->html.html_header_objects, hw->html.header_text); } if (footer_text != NULL) { if (*footer_text == '\0') { footer_text = NULL; } hw->html.footer_text = footer_text; /* * Parse the footer text with the HTML parser */ hw->html.html_footer_objects = HTMLParse(hw->html.html_footer_objects, hw->html.footer_text); } /* * Reformat the new text */ hw->html.max_pre_width = DocumentWidth(hw, hw->html.html_objects); ReformatWindow(hw); /* * If a target anchor is passed, override the element id * with the id of that anchor. */ if (target_anchor != NULL) { int id; id = HTMLAnchorToId(w, target_anchor); if (id != 0) { element_id = id; } } /* * Position text at id specified, or at top if no position * specified. * Find the element corrsponding to the id passed in. */ eptr = NULL; if (element_id != 0) { start = hw->html.formatted_elements; while (start != NULL) { if (start->ele_id == element_id) { eptr = start; break; } start = start->next; } } if (eptr == NULL) { newy = 0; } else { newy = eptr->y - 2; } if (newy < 0) { newy = 0; } if (newy > (hw->html.doc_height - (int)hw->html.view_height)) { newy = hw->html.doc_height - (int)hw->html.view_height; } if (newy < 0) { newy = 0; } hw->html.scroll_x = 0; hw->html.scroll_y = newy; #ifdef DEBUG fprintf (stderr, "calling in HTMLSetText\n"); #endif ConfigScrollBars(hw); ScrollWidgets(hw); /* * Display the new text */ ViewClearAndRefresh(hw); /* * Clear any previous selection */ hw->html.select_start = NULL; hw->html.select_end = NULL; hw->html.sel_start_pos = 0; hw->html.sel_end_pos = 0; hw->html.new_start = NULL; hw->html.new_end = NULL; hw->html.new_start_pos = 0; hw->html.new_end_pos = 0; hw->html.active_anchor = NULL; hw->html.cached_tracked_ele = NULL; } /* * To use faster TOLOWER as set up in HTMLparse.c */ #ifdef NOT_ASCII #define TOLOWER(x) (tolower(x)) #else extern char map_table[]; #define TOLOWER(x) (map_table[x]) #endif /* NOT_ASCII */ /* * Convenience function to search the text of the HTML document as a single * white space separated string. Linefeeds are converted into spaces. * * Takes a pattern, pointers to the start and end blocks to store the * start and end of the match into. Start is also used as the location to * start the search from for incremental searching. If start is an invalid * position (id = 0). Default start is the beginning of the document for * forward searching, and the end of the document for backwards searching. * The backward and caseless parameters I hope are self-explanatory. * * returns 1 on success * (and the start and end positions of the match). * returns -1 otherwise (and start and end are unchanged). */ int #ifdef _NO_PROTO HTMLSearchText (w, pattern, m_start, m_end, backward, caseless) Widget w; char *pattern; ElementRef *m_start; ElementRef *m_end; int backward; int caseless; #else HTMLSearchText (Widget w, char *pattern, ElementRef *m_start, ElementRef *m_end, int backward, int caseless) #endif { HTMLWidget hw = (HTMLWidget)w; int found, equal; char *match; char *tptr; char *mptr; char cval; struct ele_rec *eptr; int s_pos; struct ele_rec *s_eptr; ElementRef s_ref, e_ref; ElementRef *start, *end; /* * If bad parameters are passed, just fail the search */ if ((pattern == NULL)||(*pattern == '\0')|| (m_start == NULL)||(m_end == NULL)) { return(-1); } /* * If we are caseless, make a lower case copy of the pattern to * match to use in compares. * * remember to free this before returning */ if (caseless) { match = (char *)malloc(strlen(pattern) + 1); tptr = pattern; mptr = match; while (*tptr != '\0') { *mptr = (char)TOLOWER((int)*tptr); mptr++; tptr++; } *mptr = '\0'; } else { match = pattern; } /* * Slimy coding. I later decided I didn't want to change start and * end if the search failed. Rather than changing all the code, * I just copy it into locals here, and copy it out again if a match * is found. */ start = &s_ref; end = &e_ref; start->id = m_start->id; start->pos = m_start->pos; end->id = m_end->id; end->pos = m_end->pos; /* * Find the user specified start position. */ if (start->id > 0) { found = 0; eptr = hw->html.formatted_elements; while (eptr != NULL) { /* if (eptr->ele_id > start->id) {*/ /* MF031 */ if ((!backward && eptr->ele_id > start->id) || ( backward && eptr->ele_id == start->id)) { s_eptr = eptr; found = 1; break; } eptr = eptr->next; } /* * Bad start position, fail them out. */ if (!found) { if (caseless) { free(match); } return(-1); } /* * Sanify the start position */ s_pos = start->pos; if (s_pos >= s_eptr->edata_len - 1) { s_pos = s_eptr->edata_len - 2; } if (s_pos < 0) { s_pos = 0; } } else { /* * Default search starts at end for backward, and * beginning for forwards. */ if (backward) { s_eptr = hw->html.formatted_elements; while (s_eptr->next != NULL) { s_eptr = s_eptr->next; } s_pos = s_eptr->edata_len - 2; } else { s_eptr = hw->html.formatted_elements; s_pos = 0; } } if (backward) { char *mend; /* * Save the end of match here for easy end to start searching */ mend = match; while (*mend != '\0') { mend++; } if (mend > match) { mend--; } found = 0; equal = 0; mptr = mend; if (s_eptr != NULL) { eptr = s_eptr; } else { eptr = hw->html.formatted_elements; while (eptr->next != NULL) { eptr = eptr->next; } } while (eptr != NULL) { /* * Skip the special internal text */ if (eptr->internal == True) { eptr = eptr->prev; continue; } if (eptr->type == E_TEXT) { tptr = (char *)(eptr->edata + eptr->edata_len - 2); if (eptr == s_eptr) { tptr = (char *)(eptr->edata + s_pos); } while (tptr >= eptr->edata) { if (equal) { if (caseless) { cval =(char)TOLOWER((int)*tptr); } else { cval = *tptr; } while ((mptr >= match)&& (tptr >= eptr->edata)&& (cval == *mptr)) { tptr--; mptr--; if (tptr >= eptr->edata) { if (caseless) { cval =(char)TOLOWER((int)*tptr); } else { cval = *tptr; } } } if (mptr < match) { found = 1; start->id = eptr->ele_id; start->pos = (int) (tptr - eptr->edata + 1); break; } else if (tptr < eptr->edata) { break; } else { equal = 0; } } else { mptr = mend; if (caseless) { cval =(char)TOLOWER((int)*tptr); } else { cval = *tptr; } while ((tptr >= eptr->edata)&& (cval != *mptr)) { tptr--; if (tptr >= eptr->edata) { if (caseless) { cval =(char)TOLOWER((int)*tptr); } else { cval = *tptr; } } } if ((tptr >= eptr->edata)&& (cval == *mptr)) { equal = 1; end->id = eptr->ele_id; end->pos = (int) (tptr - eptr->edata + 1); } } } } /* * Linefeeds match to single space characters. */ else if (eptr->type == E_LINEFEED) { if (equal) { if (*mptr == ' ') { mptr--; if (mptr < match) { found = 1; start->id =eptr->ele_id; start->pos = 0; } } else { equal = 0; } } else { mptr = mend; if (*mptr == ' ') { equal = 1; end->id = eptr->ele_id; end->pos = 0; mptr--; if (mptr < match) { found = 1; start->id =eptr->ele_id; start->pos = 0; } } } } if (found) { break; } eptr = eptr->prev; } } else /* forward */ { found = 0; equal = 0; mptr = match; if (s_eptr != NULL) { eptr = s_eptr; } else { eptr = hw->html.formatted_elements; } while (eptr != NULL) { /* * Skip the special internal text */ if (eptr->internal == True) { eptr = eptr->next; continue; } if (eptr->type == E_TEXT) { tptr = eptr->edata; if (eptr == s_eptr) { tptr = (char *)(tptr + s_pos); } while (*tptr != '\0') { if (equal) { if (caseless) { cval =(char)TOLOWER((int)*tptr); } else { cval = *tptr; } while ((*mptr != '\0')&& (cval == *mptr)) { tptr++; mptr++; if (caseless) { cval =(char)TOLOWER((int)*tptr); } else { cval = *tptr; } } if (*mptr == '\0') { found = 1; end->id = eptr->ele_id; end->pos = (int) (tptr - eptr->edata); break; } else if (*tptr == '\0') { break; } else { equal = 0; } } else { mptr = match; if (caseless) { cval =(char)TOLOWER((int)*tptr); } else { cval = *tptr; } while ((*tptr != '\0')&& (cval != *mptr)) { tptr++; if (caseless) { cval =(char)TOLOWER((int)*tptr); } else { cval = *tptr; } } if (cval == *mptr) { equal = 1; start->id = eptr->ele_id; start->pos = (int) (tptr - eptr->edata); } } } } else if (eptr->type == E_LINEFEED) { if (equal) { if (*mptr == ' ') { mptr++; if (*mptr == '\0') { found = 1; end->id = eptr->ele_id; end->pos = 0; } } else { equal = 0; } } else { mptr = match; if (*mptr == ' ') { equal = 1; start->id = eptr->ele_id; start->pos = 0; mptr++; if (*mptr == '\0') { found = 1; end->id = eptr->ele_id; end->pos = 0; } } } } if (found) { break; } eptr = eptr->next; } } if (found) { m_start->id = start->id; m_start->pos = start->pos; m_end->id = end->id; m_end->pos = end->pos; } if (caseless) { free(match); } if (found) { return(1); } else { return(-1); } }