diff options
Diffstat (limited to 'vendor/x11iraf/obm/ObmW/HTML.c')
-rw-r--r-- | vendor/x11iraf/obm/ObmW/HTML.c | 6177 |
1 files changed, 6177 insertions, 0 deletions
diff --git a/vendor/x11iraf/obm/ObmW/HTML.c b/vendor/x11iraf/obm/ObmW/HTML.c new file mode 100644 index 00000000..546bfbc0 --- /dev/null +++ b/vendor/x11iraf/obm/ObmW/HTML.c @@ -0,0 +1,6177 @@ +/**************************************************************************** + * 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 <stdio.h> +#include "HTMLP.h" +#ifdef MOTIF +#include <Xm/DrawingA.h> +#include <Xm/ScrollBar.h> +#else +#include "DrawingArea.h" +#include <X11/Xaw/Scrollbar.h> +#endif +#include <X11/cursorfont.h> + + +#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[] = +" \ +<Btn1Down>: select-start() ManagerGadgetArm()\n\ +<Btn1Motion>: extend-adjust() ManagerGadgetButtonMotion()\n\ +<Btn1Up>: extend-end(PRIMARY, CUT_BUFFER0) ManagerGadgetActivate()\n\ +<Btn2Down>: select-start()\n\ +<Btn2Motion>: extend-adjust()\n\ +<Btn2Up>: extend-end(PRIMARY, CUT_BUFFER0)\n\ +<Btn3Down>: extend-start()\n\ +<Btn3Motion>: extend-adjust()\n\ +<Btn3Up>: extend-end(PRIMARY, CUT_BUFFER0) \n\ +<Key>f: scroll(1)\n\ +<Key>b: scroll(-1)\n\ +<Key>Prior: scroll(-1)\n\ +<Key>Next: scroll(1)\n\ +<Key>u: scroll(-0.5)\n\ +<Key>d: scroll(0.5)\n\ +<Key>Up: scroll(-0.5)\n\ +<Key>Down: scroll(0.5)\n\ +<Key>j: scroll(1ch)\n\ +<Key>k: scroll(-1ch)\n\ +<Motion>: track-motion()\n\ +<Leave>: track-motion()\n\ +<FocusOut>: track-motion()\n\ +<Expose>: track-motion()\ +"; +#else +static char defaultTranslations[] = +" \ +<Btn1Down>: select-start() \n\ +<Btn1Motion>: extend-adjust() \n\ +<Btn1Up>: extend-end(PRIMARY, CUT_BUFFER0) \n\ +<Btn2Down>: select-start() \n\ +<Btn2Motion>: extend-adjust() \n\ +<Btn2Up>: extend-end(PRIMARY, CUT_BUFFER0) \n\ +<Btn3Down>: extend-start()\n\ +<Btn3Motion>: extend-adjust()\n\ +<Btn3Up>: extend-end(PRIMARY, CUT_BUFFER0) \n\ +<Key>f: scroll(1)\n\ +<Key>b: scroll(-1)\n\ +<Key>Prior: scroll(-1)\n\ +<Key>Next: scroll(1)\n\ +<Key>u: scroll(-0.5)\n\ +<Key>d: scroll(0.5)\n\ +<Key>Up: scroll(-0.5)\n\ +<Key>Down: scroll(0.5)\n\ +<Key>j: scroll(1ch)\n\ +<Key>k: scroll(-1ch)\n\ +<Motion>: track-motion()\n\ +<Leave>: track-motion()\n\ +<FocusOut>: track-motion()\n\ +<Expose>: 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; i<hw->html.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 <sanders@bsdi.com> + */ + 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 <X11/Xaw/AsciiText.h> +/* + * 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<maxLength) + { + char *txt; + /* insert string dumps core when string in place is true */ + + XtVaGetValues(w,XtNstring,&txt,NULL); + txt[passwdLength] = star[0]; + txt[passwdLength+1] = '\0'; + + /* the combined set values command does not work */ + XtVaSetValues(w, XtNstring,txt, NULL); + XtVaSetValues(w, XtNinsertPosition,insertPos+1, NULL); + } + else + XtCallActionProc(w, "insert-string", event, &star, 1); +/* fprintf(stderr,"modified passwd <%s>\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 <LINK> 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; i<hw->html.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 <LINK> 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); + } +} |