chimera/libhtmlw/HTML.c
2025-03-12 01:54:34 +09:00

5959 lines
129 KiB
C

/****************************************************************************
* 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 <stdlib.h>
#include "HTMLP.h"
#include "DrawingArea.h"
#include <X11/Xaw/Scrollbar.h>
#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 ScrollUp();
static void ScrollDown();
#ifdef _NO_PROTO
static void _HTMLInput() ;
static void _HTMLpwdInput() ;
static void Initialize() ;
static void Redisplay() ;
static void Resize() ;
static Boolean SetValues() ;
static XtGeometryResult GeometryManager() ;
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);
static void _HTMLpwdInput(Widget w, XEvent *event,
String *params, Cardinal *num_params);
static void Initialize(HTMLWidget request, HTMLWidget new);
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 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.
*/
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\
<Motion>: track-motion()\n\
<Leave>: track-motion()\n\
<FocusOut>: track-motion()\n\
<Expose>: track-motion()\n\
<Key>space: scroll-up(\"Halfscreen\")\n\
<Key>b: scroll-down(\"Halfscreen\")\n\
<Key>j: scroll-up(\"Oneline\")\n\
<Key>k: scroll-down(\"Oneline\")\n\
<Key>Down: scroll-up(\"Oneline\")\n\
<Key>Up: scroll-down(\"Oneline\")\n\
";
/*
* Changes above by Satoshi ASAMI <asami@cory.EECS.Berkeley.EDU>
*/
static XtActionsRec actionsList[] =
{
{ "select-start", (XtActionProc) SelectStart },
{ "extend-start", (XtActionProc) ExtendStart },
{ "extend-adjust", (XtActionProc) ExtendAdjust },
{ "extend-end", (XtActionProc) ExtendEnd },
{ "track-motion", (XtActionProc) TrackMotion },
{ "HTMLInput", (XtActionProc) _HTMLInput },
{ "HTMLpwdInput", (XtActionProc) _HTMLpwdInput },
{ "scroll-up", (XtActionProc) ScrollUp },
{ "scroll-down", (XtActionProc) ScrollDown },
};
/*
* 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 },
{ "HTMLInput", (XtActionProc) _HTMLInput },
{ "HTMLpwdInput", (XtActionProc) _HTMLpwdInput },
{ "scroll-up", (XtActionProc) ScrollUp },
{ "scroll-down", (XtActionProc) ScrollDown },
};
/*
* Resource definitions for HTML widget
*/
static XtResource resources[] =
{
{ XtNborderWidth,
XtCBorderWidth, XtRDimension, sizeof (Dimension),
XtOffset (HTMLWidget, core.border_width),
XtRImmediate, (XtPointer) 0
},
{ WbNverticalScrollBarPos,
WbCVerticalScrollBarPos, XtRInt, sizeof (int),
XtOffset (HTMLWidget, html.scroll_y),
XtRInt, 0
},
{ WbNhorizontalScrollBarPos,
WbCHorizontalScrollBarPos, XtRInt, sizeof (int),
XtOffset (HTMLWidget, html.scroll_x),
XtRInt, 0
},
{ 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
},
{ XtNforeground,
XtCForeground, XtRPixel, sizeof (Pixel),
XtOffset (HTMLWidget, html.foreground),
XtRString, "Black"
},
{ 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, "False"
},
{ 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
},
{ 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
},
};
HTMLClassRec htmlClassRec = {
{ /* core class fields */
(WidgetClass) &constraintClassRec, /* superclass */
"HTML", /* class_name */
sizeof(HTMLRec), /* widget_size */
NULL, /* class_initialize */
NULL, /* class_part_init */
FALSE, /* class_inited */
(XtInitProc) Initialize, /* initialize */
NULL, /* initialize_hook */
XtInheritRealize, /* 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 */
},
{ /* html_class fields */
0 /* none */
}
};
WidgetClass htmlWidgetClass = (WidgetClass)&htmlClassRec;
static 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;
values.foreground = hw->html.foreground;
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;
}
}
/*
* Set the Athena Scrollbar's thumb position properly.
*/
static void
setScrollBar(sb, topPosition, totalLength, currentLength)
Widget sb;
Position topPosition;
Dimension totalLength, currentLength;
{
float top = (float)topPosition /(float)(totalLength);
float shown = (float)currentLength/(float)(totalLength);
#if !defined(ultrix)
#if defined(XRELEASE)
#if XRELEASE > 4
XawScrollbarSetThumb(sb, top, shown);
#endif
#endif
#else
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;
{
float scrollDir = (int)call_data < 0 ? -0.3 : 0.3;
HTMLWidget hw = (HTMLWidget)client_data;
int value, maxv;
Dimension totalLength, currentLength;
if (w == hw->html.vbar)
{
totalLength = hw->html.doc_height;
currentLength = hw->html.view_height;
/* value = hw->html.scroll_y + scrollDir * currentLength; */
/*
* patch by Satoshi Asami <asami@cs.berkeley.edu>
*/
if ((int)call_data == 1 || (int)call_data == -1)
value = hw->html.scroll_y + scrollDir * currentLength;
else
value = hw->html.scroll_y + (int)call_data;
}
else
{
totalLength = hw->html.doc_width;
currentLength = hw->html.view_width;
value = hw->html.scroll_x + scrollDir * currentLength;
}
/*
* If resizing of the document has made the scroll value
* greater than the max, we want to hold it at the max.
*
* Moved this from someplace else in the code to keep the scrollbar
* from going too far. john.
*/
maxv = totalLength - currentLength;
if (value > maxv) value = maxv;
if (value < 0) value = 0;
setScrollBar(w, value, totalLength, currentLength);
ScrollToPos(w, hw, value);
}
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);
}
/*
* 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",
drawingAreaWidgetClass,
(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;
XtSetArg(arg[argcnt],XtNorientation,XtorientVertical); argcnt++;
hw->html.vbar = XtCreateWidget("Vbar", scrollbarWidgetClass,
(Widget)hw, arg, argcnt);
XtManageChild(hw->html.vbar);
}
/*
* Add callbacks to catch scrollbar changes
*/
XtAddCallback(hw->html.vbar, XtNjumpProc,
(XtCallbackProc)JumpMove, (caddr_t)hw);
XtAddCallback(hw->html.vbar, XtNscrollProc,
(XtCallbackProc)ScrollMove, (caddr_t)hw);
/*
* 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;
XtSetArg(arg[argcnt], XtNorientation, XtorientHorizontal);
argcnt++;
hw->html.hbar = XtCreateWidget("Hbar", scrollbarWidgetClass,
(Widget)hw, arg, argcnt);
XtManageChild(hw->html.hbar);
}
/*
* Add callbacks to catch scrollbar changes
*/
XtAddCallback(hw->html.hbar, XtNjumpProc,
(XtCallbackProc)JumpMove, (caddr_t)hw);
XtAddCallback(hw->html.hbar, XtNscrollProc,
(XtCallbackProc)ScrollMove, (caddr_t)hw);
}
/*
* 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
{
int vx, vy;
/*
* Move and size the viewing area
*/
vx = vy = 0;
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 * 0),
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;
}
setScrollBar(hw->html.vbar,
hw->html.scroll_y,
hw->html.doc_height,
hw->html.view_height);
#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,
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;
}
setScrollBar(hw->html.hbar,
hw->html.scroll_x,
hw->html.doc_width,
hw->html.view_width);
}
#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);
st = 0;
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;
}
#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
{
#ifdef FAKE_EXPOSE
/*
* This is some scary shit. The first argument in DrawExpose is
* not used so 0 is OK but I am still getting queasy. The reason
* this is being done is because there are problems on some machines
* with refreshes. This may be bad because at this point there
* may not be a window. This seemed to be the easiest way to solve it
* but I will try to figure out a better way. john.
*/
if (hw->html.drawGC == NULL)
{
XEvent xe;
xe.xany.type = Expose;
DrawExpose(0, (caddr_t)hw, &xe);
}
#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
{
int dx, dy;
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);
}
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);
st = 0;
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 & Button1Mask) == 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, ""); \
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, 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;
}
static void
ScrollUp(w, event, params, num_params)
Widget w;
XEvent *event;
String *params; /* unused */
Cardinal *num_params; /* unused */
{
HTMLWidget hw = (HTMLWidget)XtParent(w);
if (XtClass(XtParent(w)) == htmlWidgetClass)
{
/*
* Added by Satoshi ASAMI <asami@cory.EECS.Berkeley.EDU>
*/
if (*num_params == 1)
{
if (strcmp(params[0], "Oneline") == 0)
ScrollMove(hw->html.vbar, (caddr_t)hw, DEFAULT_INCREMENT);
else
ScrollMove(hw->html.vbar, (caddr_t)hw, 1);
}
}
}
static void
ScrollDown(w, event, params, num_params)
Widget w;
XEvent *event;
String *params; /* unused */
Cardinal *num_params; /* unused */
{
HTMLWidget hw = (HTMLWidget)XtParent(w);
if (XtClass(XtParent(w)) == htmlWidgetClass)
{
/*
* Added by Satoshi ASAMI <asami@cory.EECS.Berkeley.EDU>
*/
if (*num_params == 1)
{
if (strcmp(params[0], "Oneline") == 0)
ScrollMove(hw->html.vbar, (caddr_t)hw, -DEFAULT_INCREMENT);
else
ScrollMove(hw->html.vbar, (caddr_t)hw, -1);
}
}
}
/*
* 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;
if (XtClass(XtParent(w)) != htmlWidgetClass)
{
return;
}
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;
}
#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); */
}
}
}
/*
* 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;
}
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;
}
}
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;
/*
* 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 - (int)hw->html.view_height / 2;
}
if (newy < 0)
{
newy = 0;
}
if (newy > (hw->html.doc_height - (int)hw->html.view_height / 2))
{
newy = hw->html.doc_height - (int)hw->html.view_height / 2;
}
if (newy < 0)
{
newy = 0;
}
ScrollToPos(hw->html.vbar, hw, newy);
ScrollToPos(hw->html.hbar, hw, 0);
setScrollBar(hw->html.vbar, newy,
hw->html.doc_height,
hw->html.view_height);
}
/*
* 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)&&
(strcmp(start->anchorName, name) == 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)
Widget w;
visitTestProc testFunc;
#else
HTMLRetestAnchors(Widget w, visitTestProc testFunc)
#endif
{
HTMLWidget hw = (HTMLWidget)w;
struct ele_rec *start;
if (testFunc == NULL)
{
testFunc = (visitTestProc)hw->html.previously_visited_test;
}
/*
* 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 ((*testFunc)(hw, 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
*/
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;
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;
}
}
/*
* Took this out because it screwed up the scrollbar positioning which
* I hacked on. It sucks but seems to work. john.
*/
/*
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)
{
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);
}
}