/***** * XmHTML.c : XmHTML main routines * * This file Version $Revision: 1.28 $ * * Creation date: Thu Nov 21 05:02:44 GMT+0100 1996 * Last modification: $Date: 1999/07/29 01:26:28 $ * By: $Author: sopwith $ * Current State: $State: Exp $ * * Author: newt * (C)Copyright 1995-1996 Ripley Software Development * All Rights Reserved * * This file is part of the XmHTML Widget Library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****/ /***** * ChangeLog * $Log: XmHTML.c,v $ * Revision 1.28 1999/07/29 01:26:28 sopwith * * * Fix all warnings. * * Revision 1.27 1999/06/02 01:00:35 unammx * * 1999-06-01 Akira Higuchi * * * libgnomeui/gnome-canvas-text.c: * * libgnomeui/gnome-icon-item.c: * * libgnomeui/gnome-less.c: Replace some gdk_font_load() calls with * gdk_fontset_load. Use a more open fontset rule to load the fonts. * * 1999-06-01 Akira Higuchi * * * gtk-xmhtml/XmHTMLP.h: Add three members lbearing, rbearing, * and width. These members are computed in allocFont(). * * * gtk-xmhtml/toolkit.h: Remove Toolkit_XFont() macro. * * * gtk-xmhtml/XmHTML.c: * * gtk-xmhtml/fonts.c: * * gtk-xmhtml/format.c: * * gtk-xmhtml/gtk-xmhtml.c: * * gtk-xmhtml/layout.c: * * gtk-xmhtml/paint.c: Add fontset support. We use gdk_fontset_load() * instead of gdk_font_load() iff a fontset is supplied for label * widgets. * * * gtk-xmhtml/test.c: Add gtk_set_locale() call before gtk_init(). * * Revision 1.26 1998/11/29 00:06:34 unammx * 1998-11-28 Ronald de Man * * * XmHTML.c (_XmHTMLMoveToPos): Fix to the repaint. * * Revision 1.25 1998/05/09 17:05:20 kmaraas * 1998-05-09 Kjartan Maraas * * * XmHTML.c: Added declaration of DestroyPhaseZero to get * rid of compiler warning. * * Revision 1.24 1998/04/08 14:45:35 jpaint * Reverted border changes (finally, sorry miguel!) * * Revision 1.22 1998/02/21 00:13:13 unammx * Fri Feb 20 18:14:15 1998 Miguel de Icaza * * * XmHTML.c (_XmHTMLMoveToPos): Implement missing Gtk functionality * for updating the scrollbar position. * * * gtk-xmhtml.c (AdjustVerticalScrollValue): Implement Gtk version * of this routine. * * Revision 1.21 1998/02/12 03:08:09 unammx * Merge to Koen's XmHTML 1.1.2 + following fixes: * * Wed Feb 11 20:27:19 1998 Miguel de Icaza * * * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed * with the refcounting changes. * * * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was * adding the just removed widget. * * Revision 1.20 1998/02/11 10:47:00 cwryu * Wed Feb 11 19:22:32 1998 Changwoo Ryu * * * gtk-xmhtml.c (gtk_xmhtml_focus): Use gtk_signal_handler_pending * instead of gtk_signal_get_handlers which doesn't exist anymore. * * toolkit.h (CHECK_CALLBACK): Likewise. * * * XmHTML.c (TPROTO): Remove a comparison warning. * * Revision 1.19 1998/01/14 05:49:43 unammx * Tue Jan 13 22:04:43 1998 Federico Mena * * * gtk-xmhtml.c (gtk_xmhtml_new): The widget starts up frozen and * thaws itself when it is realized. This fixes all of the problems * regarding realization, gc creation, and window background setting. * * (Federico and Miguel) * * Revision 1.18 1998/01/14 04:11:44 unammx * Tue Jan 13 22:04:43 1998 Federico Mena * * * Lots of changes all over the place to fix colors. Things are * *almost* working right now. I think I'm only missing setting the * window backgrounds appropriately. Several things were done: * * - Motif's color and gc fields from Core and XmManager were * replicated inside the GtkXmHTML widget structure. * * - Macros were created in toolkit.h to use these fields. * * - Instead of the old kludgy set_{fore,back}ground_internal * functions, we now set the window background directly. * This does not work perfectly; I'll look into it. * * - I created a shade_color() function in colors.c (ok, ok, * I stole it from gtkstyle.c) which mimics XmGetColors() * -- it calculates shaded colors for the 3D look. * * I hope to fix the remaining problems with window backgrounds real * soon now. * * Revision 1.17 1998/01/09 06:10:22 unammx * Fixed (?) background colors of the HTML widget. I'm not 100% sure I did it * the right way, but it seems to work. * * - Federico * * Revision 1.16 1998/01/07 01:45:35 unammx * Gtk/XmHTML is ready to be used by the Gnome hackers now! * Weeeeeee! * * This afternoon: * * - Changes to integrate gtk-xmhtml into an autoconf setup. * * - Changes to make gtk-xmhtml a library to be used by Gnome * (simply include tags, * so this means that the program is crashing if you pass a document * withou the BODY. I bet this is caused by some mistake of mine in * the handling of the old/new/current widgets as created by Motif. * * Miguel. * * Revision 1.13 1997/12/25 01:34:09 unammx * Good news for the day: * * I have upgraded our XmHTML sources to XmHTML 1.1.1. * * This basically means that we got table support :-) * * Still left to do: * * - Set/Get gtk interface for all of the toys in the widget. * - Frame support is broken, dunno why. * - Form support (ie adding widgets to it) * * Miguel. * * Revision 1.12 1997/12/24 17:53:53 unammx * Fun stuff: * * The widget now handles mouse motion, mouse clicks, anchors can * be clicked. * * The widget emits signals for all of the interesting events * (the same events that were used by the Motif port, we just use * signals instead of XtCallbacks). * * Boring stuff: * * The widget now handles focusin/focusout/enternotif/leavenotify * * More code sharing between the Motif frontend an the Gtk * frontned; More portability macros; * * Cleaned up some more the privte widget header files. * * Revision 1.11 1997/12/23 04:44:27 unammx * Ok kiddies, news for the day: * * It scrolls nicely. * It now displays GIFs. * It now displays animated GIFs. * It now displays JPEGs. * Colors work. * * Weeeeee! The beginning on an XmHTML era is here ;-) * * The rendering engine is pretty amazing, very accurate, looks like * Netscape on equivalent pages :-). * * Miguel and Federico. * * Revision 1.10 1997/12/19 07:06:54 unammx * My pretty pathetic attempt at getting scrollbars displayed on the XmHTML widget is here, make fun of me - miguel * * Revision 1.9 1997/12/19 03:58:10 unammx * Simple test program works! -mig&fed * * Revision 1.8 1997/12/18 00:39:20 unammx * It compiles and links -miguel * * Revision 1.7 1997/12/17 04:40:27 unammx * Your daily XmHTML code is here. It almost links. Only the * images.c file is left to port. Once this is ported we are all * set to start debugging this baby. * * btw, Dickscrape is a Motif based web browser that is entirely * based on this widget, I just tested it today, very impressive. * * Miguel. * * Revision 1.6 1997/12/16 00:34:47 unammx * More progress on the XmHTML work. This time, I did frames.c, not * as nice as I would like it to be. * * It still does not link. * * Revision 1.5 1997/12/14 23:20:59 unammx * it compiles. Now we need to make it link and work -mig * * Revision 1.4 1997/12/13 01:49:11 unammx * your daily dose of ported XmHTML code, non functional as usual -mig * * Revision 1.3 1997/12/12 00:58:40 unammx * Most of the Motif dependant code is now splitted. * * There are some bits still remaining in XmHTML.c, but will be * soonish gone. * * As usual, it does not compile yet :-) * * Miguel * * Revision 1.2 1997/12/11 21:20:19 unammx * Step 2: more gtk/xmhtml code, still non-working - mig * * Revision 1.17 1997/10/26 23:49:18 newt * Bugfixes 10/22/97-01, 10/26/97-01 * * Revision 1.16 1997/08/31 17:32:33 newt * Again some fixes in _XmHTMLMoveToPos * * Revision 1.15 1997/08/30 00:38:58 newt * Anchor Highlighting, form component traversal action routines, alpha * channel support, loads of fixes in _XmHTMLMoveToPos, DrawRedisplay and * SetValues. * * Revision 1.14 1997/08/01 12:54:48 newt * Progressive image loading changes. * * Revision 1.13 1997/05/28 01:38:30 newt * Added a check on the value of the XmNmaxImageColors resource. Added a check * on the scrollbar dimensions. Modified the SetValues method to properly deal * with the XmNbodyImage resource. Modified XmHTMLImageFreeImageInfo to call * _XmHTMLFreeImageInfo. * * Revision 1.12 1997/04/29 14:22:14 newt * A lot of changes in SetValues, Layout, XmHTMLTextSetString. * Added XmHTMLXYToInfo. * * Revision 1.11 1997/04/03 05:31:02 newt * Modified XmHTMLImageReplace and XmHTMLImageUpdate to immediatly render an * image whenever possible. Added the XmHTMLFrameGetChild convenience function. * * Revision 1.10 1997/03/28 07:05:28 newt * Frame interface added. * Horizontal scrolling bugfix. * Added all remaining action routines and adjusted translation table. * * Revision 1.9 1997/03/20 08:04:38 newt * XmHTMLImageFreeImageInfo, XmNrepeatDelay, changes in public image functions * * Revision 1.8 1997/03/11 19:49:53 newt * Fix 03/11/97-01, SetValues. * Added XmHTMLImageGetType, updated XmHTMLImageReplace and XmHTMLImageUpdate * * Revision 1.7 1997/03/04 18:45:15 newt * ? * * Revision 1.6 1997/03/04 00:55:54 newt * Delayed Image Loading changes: XmHTMLReplaceImage, XmHTMLUpdateImage * and XmHTMLRedisplay added * * Revision 1.5 1997/03/02 23:06:35 newt * Added image/imagemap support; changed expose method; added background * image painting * * Revision 1.4 1997/02/11 02:05:44 newt * Changes related to autosizing, exposure and scrolling * * Revision 1.3 1997/01/09 06:54:53 newt * expanded copyright marker * * Revision 1.2 1997/01/09 06:42:40 newt * XmNresizeWidth and XmNresizeHeight updates * * Revision 1.1 1996/12/19 02:17:03 newt * Initial Revision * ****/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef WITH_MOTIF # include /* Fast macros */ # include /* for keycodes in HTMLProcessInput */ # include # include /* XmField, XmPartOffset and private motif funcs. */ # include # include /* For motif XmN macros */ /***** * Test if these macros are present. They are required for conversion of the * enumeration resource values. I don't know if they are present in Motif 1.2.X. * If not here, spit out an error message. * If you can't compile this because this error occurs, please contact us * at the address given below and state what version of Motif you are using. * Note: these are explicit syntax errors... *****/ #ifndef XmPartOffset XmPartOffset macro undefined. Please contact ripley@xs4all.nl #endif #ifndef XmField XmField macro undefined. Please contact ripley@xs4all.nl #endif #include #include #include #include #endif /* Our private header files */ #include "XmHTMLP.h" #include "XmHTMLfuncs.h" /* This is the anchor cursor */ #include "bitmaps/fingers.xbm" #include "bitmaps/fingers_m.xbm" /*** External Function Prototype Declarations ***/ /* * undocumented motif functions used: (declared in XmP.h): * _XmRedisplayGadgets (Widget, XEvent*, Region); */ static void DestroyPhaseZero (XmHTMLWidget html); /*** Public Variable Declarations ***/ /*** Private Datatype Declarations ****/ static void XmHTML_Initialize (XmHTMLWidget html, XmHTMLWidget init, char *html_source); static void Layout(XmHTMLWidget html); static void CheckMaxColorSetting(XmHTMLWidget html); static void CheckPLCIntervals(XmHTMLWidget html); static void Refresh(XmHTMLWidget html, int x, int y, int width, int height); static void Resize(TWidget w); void _XmHTMLClearArea(XmHTMLWidget html, int x, int y, int width, int height); static void AnchorTrack (XmHTMLWidget html, TEvent *event, int x, int y); static void LeaveAnchor(XmHTMLWidget html); static void FreeExpendableResources(XmHTMLWidget html, Boolean free_img); static void ResetWidget(XmHTMLWidget html, Boolean free_img); static void ScrollToLine(XmHTMLWidget html, int line); #ifdef WITH_MOTIF # include "XmHTML-motif.c" #else # include "gtk-xmhtml.c" #endif /***** * Name: XmHTML_Initialize * Return Type: void * Description: Called when the TWidget is instantiated * In: * html: Widget * init: Optionally the original widget (Motifish support) * html_source: html source code. * Returns: * nothing, but init is updated with checked/updated resource values. *****/ static void XmHTML_Initialize (XmHTMLWidget html, XmHTMLWidget init, char *html_source) { /* Initialize the global HTMLpart */ _XmHTMLDebug(1, ("XmHTML.c: Initialize Start\n")); /* private TWidget resources */ html->html.needs_vsb = False; html->html.needs_hsb = False; html->html.scroll_x = 0; html->html.scroll_y = 0; #ifdef WITH_MOTIF CheckAnchorUnderlining(html, html); #endif /* repeat delay. Must be positive */ if(html->html.repeat_delay < 1) { _XmHTMLWarning(__WFUNC__(html, "Initialize"), "The specified value for XmNrepeatDelay (%i) is too small.\n" " Reset to 25", html->html.repeat_delay); html->html.repeat_delay = 25; } #ifdef WITH_MOTIF /* Set default text alignment */ CheckAlignment(html, html); #endif /**** * Initialize private resources. ****/ /* Formatted document resources */ html->html.formatted_width = 1; html->html.formatted_height = 1; html->html.elements = (XmHTMLObject*)NULL; html->html.formatted = (XmHTMLObjectTable*)NULL; html->html.paint_start = (XmHTMLObjectTable*)NULL; html->html.paint_end = (XmHTMLObjectTable*)NULL; html->html.nlines = 0; /* body image */ html->html.body_image = (XmHTMLImage*)NULL; html->html.body_image_url = (String)NULL; /* layout & paint engine resources */ html->html.in_layout = False; html->html.paint_x = 0; html->html.paint_y = 0; html->html.paint_width = 0; html->html.paint_height = 0; #ifdef WITH_MOTIF html->html.body_fg = html->manager.foreground; html->html.body_bg = html->core.background_pixel; #else html->html.body_fg = GTK_WIDGET(html)->style->fg [GTK_STATE_NORMAL].pixel; html->html.body_bg = GTK_WIDGET(html)->style->bg [GTK_STATE_NORMAL].pixel; #endif html->html.images = (XmHTMLImage*)NULL; html->html.image_maps = (XmHTMLImageMap*)NULL; html->html.xcc = (XCC)NULL; html->html.bg_gc = (TGC)NULL; html->html.form_data = (XmHTMLFormData*)NULL; html->html.delayed_creation = False; /* no delayed image creation */ /***** * Original colors must be stored. They can be altered by the * element, so if we get a body without any or some of these * colors specified, we can use the proper default values for the * unspecified elements. *****/ html->html.body_fg_save = html->html.body_fg; html->html.body_bg_save = html->html.body_bg; html->html.anchor_fg_save = html->html.anchor_fg; html->html.anchor_target_fg_save = html->html.anchor_target_fg; html->html.anchor_visited_fg_save = html->html.anchor_visited_fg; html->html.anchor_activated_fg_save = html->html.anchor_activated_fg; html->html.anchor_activated_bg_save = html->html.anchor_activated_bg; /* anchor resources */ html->html.anchor_position_x = 0; html->html.anchor_position_y = 0; html->html.anchor_current_cursor_element = (XmHTMLAnchor*)NULL; html->html.armed_anchor = (XmHTMLObjectTable*)NULL; html->html.current_anchor = (XmHTMLObjectTable*)NULL; html->html.num_anchors = 0; html->html.num_named_anchors = 0; html->html.anchors = (XmHTMLWord*)NULL; html->html.anchor_words = 0; html->html.named_anchors = (XmHTMLObjectTable*)NULL; html->html.anchor_data = (XmHTMLAnchor*)NULL; html->html.press_x = 0; html->html.press_y = 0; html->html.pressed_time = 0; html->html.selected_time = 0; html->html.selected = (XmHTMLAnchor*)NULL; /* Text selection resources */ html->html.selection = (XmHTMLObjectTable*)NULL; html->html.select_start = 0; html->html.select_end = 0; /* HTML Frame resources */ html->html.nframes = 0; html->html.frames = NULL; html->html.is_frame = False; /* PLC resources */ html->html.plc_buffer = (PLCPtr)NULL; html->html.num_plcs = 0; html->html.plc_proc_id = None; html->html.plc_suspended = True; html->html.plc_gc = (TGC)NULL; /* Table resources */ html->html.tables = (XmHTMLTable*)NULL; /* HTML4.0 Event database */ html->html.events = (HTEvent*)NULL; html->html.nevents = 0; /* initial mimetype */ if(!(strcasecmp(html->html.mime_type, "text/html"))) html->html.mime_id = XmPLC_DOCUMENT; else if(!(strcasecmp(html->html.mime_type, "text/html-perfect"))) html->html.mime_id = XmPLC_DOCUMENT; else if(!(strcasecmp(html->html.mime_type, "text/plain"))) html->html.mime_id = XmHTML_NONE; else if(!(strncasecmp(html->html.mime_type, "image/", 6))) html->html.mime_id = XmPLC_IMAGE; /* alpha channel stuff */ html->html.alpha_buffer = (AlphaPtr)NULL; /* FIXME: Gtk: we need to set need_tracking correctly */ if(!html->html.anchor_track_callback && !html->html.anchor_cursor && !html->html.highlight_on_enter && !html->html.motion_track_callback && !html->html.focus_callback && !html->html.losing_focus_callback) html->html.need_tracking = False; else html->html.need_tracking = True; /* verify plc timing intervals */ CheckPLCIntervals(html); /* Misc. resources */ html->html.gc = (TGC)NULL; /* set maximum amount of colors for this display (also creates the XCC) */ CheckMaxColorSetting(html); /* Create the anchor cursor (if any) */ if(html->html.anchor_display_cursor && !(html->html.anchor_cursor)) CreateAnchorCursor(XmHTML(init)); /* set cursor to None if we don't have to use or have a cursor */ if(!html->html.anchor_display_cursor || !html->html.anchor_cursor) html->html.anchor_cursor = None; /* Select & initialize appropriate font cache */ html->html.default_font = _XmHTMLSelectFontCache(html, True); /***** * if no width or height was specified, default to the width of 20 em * (TeX measure for average character width) in the default font and the * height of a single line. We need to do this check since the Core * initialize() method doesn't do it. *****/ if(Toolkit_Widget_Dim (html).width <= 0) { unsigned long value = 0; #ifdef WITH_MOTIF if(!(XGetFontProperty(XF (html->html.default_font->xfont), XA_QUAD_WIDTH, &value))) { XCharStruct ch; int dir, ascent, descent; XTextExtents (html->html.default_font->xfont, "m", 1, &dir, &ascent, &descent, &ch); value = (Cardinal)ch.width; } #else GdkFontPrivate *gfn; gfn = (GdkFontPrivate *)html->html.default_font->xfont; if ((gfn->font.type != GDK_FONT_FONT) && (!XGetFontProperty( (XFontStruct *)gfn->xfont, XA_QUAD_WIDTH, &value))) value = gdk_char_width (&gfn->font, 'm'); #endif /* sanity for non-ISO fonts */ if(value <= 0) value = 16; Toolkit_Widget_Dim (html).width = (Dimension)(20*(Dimension)value + 2*html->html.margin_width); } if(Toolkit_Widget_Dim (html).height <= 0) Toolkit_Widget_Dim (html).height = html->html.default_font->lineheight + 2*html->html.margin_height; /***** * Now create all private TWidgets: drawing area and scrollbars. * We couldn't do this until we knew for sure the TWidget dimensions were * set; creation of the work_area uses them. *****/ CreateHTMLWidget(html); /* Parse the raw HTML text */ if(html_source) { html->html.source = strdup(html_source); html->html.elements = _XmHTMLparseHTML(html, NULL, html_source, NULL); /* check for frames */ html->html.nframes = _XmHTMLCheckForFrames(html, html->html.elements); /* and create them */ if(!_XmHTMLCreateFrames(NULL, html)) { html->html.frames = NULL; html->html.nframes = 0; } /* Trigger link callback */ if(CHECK_CALLBACK (html, link_callback, LINK)) _XmHTMLLinkCallback(html); /* do initial document markup */ _XmHTMLformatObjects(html, html); /* check for possible delayed external imagemaps */ _XmHTMLCheckImagemaps(html); } else { html->html.source = (String)NULL; html->html.elements = (XmHTMLObject*)NULL; html->html.nframes = 0; html->html.formatted = (XmHTMLObjectTable*)NULL; } /* reset scrollbars (this will also resize the work_area) */ CheckScrollBars(html); /* Final step: add a palette if we must dither */ if(html->html.map_to_palette != XmDISABLED) _XmHTMLAddPalette(html); _XmHTMLDebug(1, ("XmHTML.c: Initialize End.\n")); } /***** * Name: _XmHTMLGetLineObject * Return Type: void * Description: get the object located at the given y position. * In: * html: XmHTMLWidget * y_pos: current text y position. * Returns: * located element. *****/ static XmHTMLObjectTableElement _XmHTMLGetLineObject(XmHTMLWidget html, int y_pos) { register XmHTMLObjectTableElement tmp = NULL; /* * y_pos given must fall in the bounding box of an element. * We try to be a little bit smart here: * If we have a paint engine end and it's y position is below the * requested position, walk forwards until we find a match. * If we have a paint engine start and it's y position is below the * requested position, walk forwards. If it's above the requested position, * walk backwards. We are always bound to find a matching element. */ if(html->html.paint_end || html->html.paint_start) { /* located above paint engine end, walk forwards */ if(html->html.paint_end && html->html.paint_end->y < y_pos) { for(tmp = html->html.paint_end; tmp != NULL; tmp = tmp->next) if(y_pos >= tmp->y && y_pos < tmp->y + tmp->height) break; } /* not found or no paint engine end */ else if(html->html.paint_start) { /* located above paint engine start, walk forwards */ if(html->html.paint_start->y < y_pos) { for(tmp = html->html.paint_start; tmp != NULL; tmp = tmp->next) if(y_pos >= tmp->y && y_pos < tmp->y + tmp->height) break; } /* located under paint engine start, walk backwards */ else { for(tmp = html->html.paint_start; tmp != NULL; tmp = tmp->prev) if(y_pos >= tmp->y && y_pos < tmp->y + tmp->height) break; } } } /* neither paint engine start or end */ else for(tmp = html->html.formatted; tmp != NULL; tmp = tmp->next) if(y_pos >= tmp->y && y_pos < tmp->y + tmp->height) break; /* top or bottom element */ if(tmp == NULL || tmp->prev == NULL) { /* bottom element */ if(tmp == NULL) return(html->html.formatted); /* top element otherwise */ return(NULL); } return((tmp->y > y_pos ? tmp->prev : tmp)); } /***** * Name: SetCurrentLineNumber * Return Type: void * Description: get & set the linenumber of the line at the top of the * working area. * In: * html: XmHTMLWidget * y_pos: current text y position. * Returns: * nothing, but the top_line field of the htmlRec is updated. *****/ static void SetCurrentLineNumber(XmHTMLWidget html, int y_pos) { XmHTMLObjectTableElement tmp; _XmHTMLDebug(1, ("XmHTML.c: SetCurrentLineNumber, y_pos = %i\n", y_pos)); if((tmp = _XmHTMLGetLineObject(html, y_pos)) != NULL) { _XmHTMLDebug(1, ("XmHTML.c: SetCurrentLineNumber, object found, " "y_pos = %i, linenumber = %i\n", tmp->y, tmp->line)); /* set line number for the found object */ html->html.top_line = tmp->line; /* * If the current element has got more than one word in it, and these * words span accross a number of lines, adjust the linenumber. */ if(tmp->n_words > 1 && tmp->words[0].y != tmp->words[tmp->n_words-1].y) { int i; for(i = 0 ; i < tmp->n_words && tmp->words[i].y < y_pos; i++); if(i != tmp->n_words) html->html.top_line = tmp->words[i].line; } } else html->html.top_line = 0; _XmHTMLDebug(1, ("XmHTML.c: SetCurrentLineNumber, top_line = %i\n", html->html.top_line)); } /***** * Name: Layout * Return Type: void * Description: main layout algorithm. * computes text layout and configures the scrollbars. * Also does handles image recreation. * In: * html: TWidget to layout * Returns: * nothing *****/ static void Layout(XmHTMLWidget html) { XmHTMLObjectTableElement curr_ele = NULL; _XmHTMLDebug(1, ("XmHTML.c: Layout Start\n")); /* set blocking flag */ html->html.in_layout = True; /* remember current vertical position if we have been scrolled */ if(html->html.scroll_y) curr_ele = _XmHTMLGetLineObject(html, html->html.scroll_y); /* make a resize request if we have to do auto-sizing in either direction */ if(html->html.resize_width || html->html.resize_height) autoSizeWidget(html); else _XmHTMLComputeLayout(html); /* set new vertical scrollbar positions */ if(curr_ele != NULL) html->html.scroll_y = curr_ele->y; else html->html.scroll_y = 0; /* configure the scrollbars, will also resize work_area */ CheckScrollBars(html); html->html.in_layout = False; _XmHTMLDebug(1, ("XmHTML.c: Layout End\n")); return; } /***** * Name: Resize * Return Type: void * Description: xmHTMLWidgetClass resize method. * In: * w: resized TWidget. * Returns: * nothing *****/ static void Resize(TWidget w) { Boolean do_expose; XmHTMLWidget html = XmHTML(w); int foo, vsb_width; Display *dpy; TWindow win; _XmHTMLDebug(1, ("XmHTML.c: Resize Start\n")); /* No needless resizing */ if(!Toolkit_Widget_Is_Realized(w)) { _XmHTMLDebug(1, ("XmHTML.c: Resize end, TWidget not realized.\n")); return; } if(html->html.in_layout) { _XmHTMLDebug(1, ("XmHTML.c: Resize end, layout flag is set.\n")); return; } GetScrollDim(html, &foo, &vsb_width); /* No change in size, return */ if((Toolkit_Widget_Dim (html).height == html->html.work_height) && (Toolkit_Widget_Dim (html).width == (html->html.work_width + html->html.margin_width + vsb_width))) { _XmHTMLDebug(1, ("XmHTML.c: Resize End, no change in size\n")); return; } dpy = Toolkit_Display (html->html.work_area); win = Toolkit_Widget_Window (html->html.work_area); /* * Check if we have to do layout and generate an expose event. * When the TWidget shrinks, X does not generate an expose event. * We want to recompute layout and generate an expose event when the * width changes. * When the height increases, we only want to generate a partial * exposure (this gets handled in Redisplay). */ do_expose = (Toolkit_Widget_Dim (html).width != (html->html.work_width + html->html.margin_width + vsb_width)); _XmHTMLDebug(1, ("XmHTML.c: Resize, new window dimensions: %ix%i.\n", Toolkit_Widget_Dim (html).width - html->html.margin_width, html->html.work_height)); _XmHTMLDebug(1, ("XmHTML.c: Resize, generating expose event : %s.\n", (do_expose ? "yes" : "no"))); /* Clear current visible text */ if(do_expose) { /* * save new height & width of visible area. * subtract margin_width once to minimize number of calcs in * the paint routines: every thing rendered starts at an x position * of margin_width. */ html->html.work_width = Toolkit_Widget_Dim (html).width - html->html.margin_width - vsb_width; html->html.work_height= Toolkit_Widget_Dim (html).height; /* Recompute layout */ Layout(html); /* Clear current text area and generate an expose event */ Toolkit_Widget_Repaint (html); } /* change in height */ else { /* * Get new start & end points for the paint engine * We have two cases: shrink or stretch. * When stretched, we generate an exposure event for the added * area and let DrawRedisplay figure it out. If shrunk, adjust * end point for the paint engine. */ /* Window has been stretched */ if(html->html.work_height < Toolkit_Widget_Dim (html).height) { /* * formatted_height has some formatting offsets in it. Need * to subtract them first. */ int max = html->html.formatted_height - html->html.margin_height - html->html.default_font->xfont->descent; /* * If the stretch is so large that the entire text will fit * in the new window height, remove the scrollbars by resetting * the vertical scrollbar position. */ if(Toolkit_Widget_Dim (html).height > max) html->html.scroll_y = 0; /* save new height */ html->html.work_height = Toolkit_Widget_Dim (html).height; /* reset scrollbars (this will also resize the work_area) */ CheckScrollBars(html); /* * just clear the entire area. Will generate a double exposure * but everything will be painted as it should. */ Toolkit_Widget_Repaint (html); } /* window has been shrunk */ else { XmHTMLObjectTable *start, *end; int y; /* get new y maximum */ y = html->html.scroll_y + Toolkit_Widget_Dim (html).height; /* Starting point is end of previous stream */ start = (html->html.paint_end == NULL ? html->html.formatted: html->html.paint_end); /* Walk backwards until we reach the desired height */ for(end = start; end != NULL && y >= end->y; end = end->prev); /* save end point */ html->html.paint_end = end; /* save new height */ html->html.work_height = Toolkit_Widget_Dim (html).height; /* reset scrollbars (this will also resize the work_area) */ CheckScrollBars(html); /* no need to paint */ } } /* resize XmHTML's frame childs */ if(html->html.nframes) { _XmHTMLDebug(1, ("XmHTML.c: Resize, calling ReconfigureFrames\n")); _XmHTMLReconfigureFrames(html); } SetScrollBars(html); _XmHTMLDebug(1, ("XmHTML.c: Resize End\n")); return; } /***** * Name: PaintBackground * Return Type: void * Description: update background with the given region * In: * html: XmHTMLWidget for which to do background painting. * x,y: origin of region to update * width,height: dimensions of region to update. * Returns: * nothing. * Note: * A simple and beautiful routine that does it's job perfectly! *****/ static void PaintBackground(XmHTMLWidget html, int x, int y, int width, int height) { #ifdef WITH_MOTIF XGCValues values; unsigned long valuemask; #endif Display *dpy; int tile_width, tile_height, x_dist, y_dist, ntiles_x, ntiles_y; int x_offset, y_offset, tsx, tsy; _XmHTMLDebug(1, ("XmHTML.c: PaintBackground start, x = %i, y = %i, " "width = %i, height = %i\n", x, y, width, height)); /***** * We need to figure out a correct starting point for the first * tile to be drawn (ts_[x,y]_origin in the GC). * We know the region to update. First we need to get the number of tiles * drawn so far. Since we want the *total* number of tiles drawn, we must * add the scroll offsets to the region origin. *****/ tile_width = html->html.body_image->width; tile_height = html->html.body_image->height; _XmHTMLDebug(1, ("XmHTML.c: PaintBackground, tile width = %i, " "tile height = %i\n", tile_width, tile_height)); x_dist = html->html.scroll_x + x; y_dist = html->html.scroll_y + y; ntiles_x = (int)(x_dist/tile_width); ntiles_y = (int)(y_dist/tile_height); _XmHTMLDebug(1, ("XmHTML.c: PaintBackground, no of full horizontal " "tiles: %i (x_dist = %i)\n", ntiles_x, x_dist)); _XmHTMLDebug(1, ("XmHTML.c: PaintBackground, no of full vertical " "tiles : %i (y_dist = %i)\n", ntiles_y, y_dist)); /* * Now we know how many full tiles have been drawn, we can calculate * the horizontal and vertical shifts required to start tiling on a * tile boundary. */ x_offset = x_dist - ntiles_x * tile_width; y_offset = y_dist - ntiles_y * tile_height; _XmHTMLDebug(1, ("XmHTML.c: PaintBackground, computed horizontal " "offset: %i\n", x_offset)); _XmHTMLDebug(1, ("XmHTML.c: PaintBackground, computed vertical " "offset : %i\n", y_offset)); /* * Now we can compute the x and y tile origins. Note that these can * be negative. */ tsx = x - x_offset; tsy = y - y_offset; _XmHTMLDebug(1, ("XmHTML.c: PaintBackground, computed horizontal tile " "origin: %i (x = %i)\n", tsx, x)); _XmHTMLDebug(1, ("XmHTML.c: PaintBackground, computed vertical tile " "origin : %i (y = %i)\n", tsy, y)); dpy = Toolkit_Display (html->html.work_area); #if WITH_MOTIF valuemask = GCTile | GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin; values.fill_style = FillTiled; values.tile = html->html.body_image->pixmap; values.ts_x_origin = tsx; values.ts_y_origin = tsy; XChangeGC(dpy, html->html.bg_gc, valuemask, &values); #else gdk_gc_set_fill (html->html.bg_gc, GDK_TILED); gdk_gc_set_tile (html->html.bg_gc, html->html.body_image->pixmap); gdk_gc_set_ts_origin (html->html.bg_gc, tsx, tsy); #endif /* a plain fillrect will redraw the background portion */ Toolkit_Fill_Rectangle(dpy, Toolkit_Widget_Window(html->html.work_area), html->html.bg_gc, x, y, width, height); _XmHTMLDebug(1, ("XmHTML.c: PaintBackground end\n")); } /***** * Name: Refresh * Return Type: void * Description: main screen refresher: given an exposure rectangle, this * routine determines the proper paint engine start and end * points and calls the painter. * In: * html: XmHTMLWidget id * x,y: upper-left corner of exposure region * width: width of exposure region * height: height of exposure region * Returns: * nothing. *****/ static void Refresh(XmHTMLWidget html, int x, int y, int width, int height) { int x1, x2, y1, y2, dy; XmHTMLObjectTable *start, *end; _XmHTMLDebug(1, ("XmHTML.c: Refresh Start\n")); x1 = x; x2 = x1 + width; /* * Update background with the given region. Must check if body image * hasn't been delayed or is being loaded progressively or we will get * some funny effects... */ if(html->html.body_image && !ImageDelayedCreation(html->html.body_image) && BodyImageLoaded(html->html.body_image->html_image)) PaintBackground(html, x, y, width, height); /* * We add the fontheight to the height of the exposure region. This will * ensure that the line right under the exposure region is also redrawn * properly. Same for topmost position, but subtraction instead of addition. */ dy = html->html.default_font->lineheight; y1 = (y - dy > 0 ? y - dy : y); y2 = y1 + height + 1.5*dy; _XmHTMLDebug(1, ("XmHTML.c: Refresh, initial y1: %i, y2: %i\n", y1, y2)); /* add vertical scroll and core offsets */ y1 += html->html.scroll_y - Toolkit_Widget_Dim (html).y; y2 += html->html.scroll_y + Toolkit_Widget_Dim (html).y; _XmHTMLDebug(1, ("XmHTML.c: Refresh, using y1: %i, y2: %i (scroll_y = %i, " "core.y = %i)\n", y1, y2, html->html.scroll_y, Toolkit_Widget_Dim (html).y)); /* * If the offset of the top of the exposed region is higher than * the max height of the text to display, the exposure region * is empty, so we just return here and leave everything untouched. */ if(y1 > html->html.formatted_height) { _XmHTMLDebug(1, ("XmHTML.c: Refresh End, y1 > maximum document " " height\n")); html->html.top_line = html->html.nlines; return; } /* * Get paint stream start & end for the obtained exposure region * We have to take the height of the object into account as well. * We try to be a little bit smart here. * paint_start == NULL is a valid stream command, so check it. */ start = (html->html.paint_start ? html->html.paint_start : html->html.formatted); /* below current paint engine start, scrolling down */ if(y1 > start->y) { /* already in region, get first object in it */ if(y1 < (start->y + start->height)) { _XmHTMLDebug(1, ("XmHTML.c: Refresh, walking bottom-up, " "y_start = %i\n", start->y)); while(start && y1 > start->y && y1 < (start->y + start->height)) start = start->prev; } /* below region, walk forward until we hit first object */ else { _XmHTMLDebug(1, ("XmHTML.c: Refresh, walking bottom-down, " "y_start = %i\n", start->y)); while(start) { if(y1 > start->y && y1 < (start->y + start->height)) break; start = start->next; } } } /* above current paint engine start, scrolling up */ else { _XmHTMLDebug(1, ("XmHTML.c: Refresh, walking top-up, " "y_start = %i\n", start->y)); while(start && y1 <= start->y) start = start->prev; /* get first object with same y position */ while(start && start->prev && start->y == start->prev->y) start = start->prev; } /* sanity check */ if(start == NULL) start = html->html.formatted; end = start; /* get first point at bottom of exposed region */ while(end && y2 > end->y) end = end->next; /* now walk to the last point still inside the region */ while(end && y2 > end->y && y2 < (end->y + end->height)) end = end->next; /* set proper paint engine start & end */ html->html.paint_start = start; html->html.paint_end = end; /* Set horizontal painting positions */ html->html.paint_x = x1 + html->html.scroll_x - Toolkit_Widget_Dim (html).x; html->html.paint_width = x2 + html->html.scroll_x + Toolkit_Widget_Dim (html).x; /* Set vertical painting positions */ html->html.paint_y = y1; html->html.paint_height = y2; _XmHTMLDebug(1, ("XmHTML.c: Refresh, x1 = %i, x2 = %i\n", x1, x2)); _XmHTMLDebug(1, ("XmHTML.c: Refresh, y1 = %i, y2 = %i\n", y1, y2)); _XmHTMLDebug(1, ("XmHTML.c: Refresh, paint_start->x = %i, paint_start->y " "= %i\n", start->x, start->y)); #ifdef DEBUG if(end) _XmHTMLDebug(1, ("XmHTML.c: Refresh, paint_end->x = %i, paint_end->y " "= %i\n", end->x, end->y)); else _XmHTMLDebug(1, ("XmHTML.c: Refresh, paint_end is NULL!\n")); #endif if(html->html.gc == NULL) return; _XmHTMLDebug(1, ("XmHTML.c: Refresh, calling _XmHTMLPaint\n")); _XmHTMLPaint(html, html->html.paint_start, html->html.paint_end); #if 0 /* doesn't work yet */ if(html->html.is_frame && html->html.frame_border) _XmHTMLDrawFrameBorder(html); #endif /* display scrollbars */ SetScrollBars(html); } /***** * Name: ResetWidget * Return Type: void * Description: resets all non-persistent resources to their defaults * In: * html: XmHTMLWidget id * free_img: true when images should be freed. This will only be True * when the TWidget has received a new source. * Returns: * nothing *****/ static void ResetWidget(XmHTMLWidget html, Boolean free_img) { /* reset some important vars */ html->html.anchors = (XmHTMLWord*)NULL; html->html.anchor_words = 0; html->html.named_anchors = (XmHTMLObjectTable*)NULL; html->html.num_named_anchors = 0; html->html.anchor_current_cursor_element = (XmHTMLAnchor*)NULL; html->html.armed_anchor = (XmHTMLObjectTable*)NULL; html->html.current_anchor = (XmHTMLObjectTable*)NULL; html->html.selected = (XmHTMLAnchor*)NULL; html->html.selection = (XmHTMLObjectTable*)NULL; html->html.select_start = 0; html->html.select_end = 0; html->html.scroll_x = 0; html->html.scroll_y = 0; html->html.formatted_width = 1; html->html.formatted_height = 1; html->html.paint_start = (XmHTMLObjectTable*)NULL; html->html.paint_end = (XmHTMLObjectTable*)NULL; html->html.paint_x = 0; html->html.paint_y = 0; html->html.paint_width = 0; html->html.paint_height = 0; html->html.scroll_x = 0; html->html.scroll_y = 0; html->html.top_line = 0; /* fix 02/26/97-01, kdh */ html->html.paint_start = (XmHTMLObjectTable*)NULL; html->html.paint_end = (XmHTMLObjectTable*)NULL; /* Reset all colors */ html->html.body_fg = html->html.body_fg_save; html->html.body_bg = html->html.body_bg_save; html->html.anchor_fg = html->html.anchor_fg_save; html->html.anchor_target_fg = html->html.anchor_target_fg_save; html->html.anchor_visited_fg = html->html.anchor_visited_fg_save; html->html.anchor_activated_fg = html->html.anchor_activated_fg_save; html->html.anchor_activated_bg = html->html.anchor_activated_bg_save; html->html.image_maps = (XmHTMLImageMap*)NULL; /* and reset image stuff if it was freed */ if(free_img) { /* must reset body_image as well since it also has been freed */ html->html.body_image = (XmHTMLImage*)NULL; html->html.images = (XmHTMLImage*)NULL; html->html.body_image_url = (String)NULL; html->html.alpha_buffer = (AlphaPtr)NULL; /* only reset when we aren't dithering */ if(html->html.map_to_palette == XmDISABLED) html->html.xcc = (XCC)NULL; } } /***** * Name: FreeExpendableResources * Return Type: void * Description: frees all non-persistent resources of a TWidget * In: * html: XmHTMLWidget id * free_img: true when images should be freed. This will only be True * when the TWidget has received a new source. * Returns: * nothing *****/ static void FreeExpendableResources(XmHTMLWidget html, Boolean free_img) { /* Free anchor worddata */ if(html->html.anchor_words) free(html->html.anchors); html->html.anchors = (XmHTMLWord*)NULL; /* Free named anchor data */ if(html->html.num_named_anchors) free(html->html.named_anchors); html->html.named_anchors = (XmHTMLObjectTable*)NULL; /***** * Always free imagemaps, anchor data becomes invalid!! * (fix 09/17/97-02, kdh) *****/ _XmHTMLFreeImageMaps(html); html->html.image_maps = (XmHTMLImageMap*)NULL; /* clear the images if we have to */ if(free_img) { /* Free all images (also clears xcc & alpha channel stuff) */ XmHTMLImageFreeAllImages((TWidget)html); /* must reset body_image as well since it also has been freed */ html->html.body_image = (XmHTMLImage*)NULL; html->html.images = (XmHTMLImage*)NULL; html->html.delayed_creation = False; /* no delayed image creation */ html->html.alpha_buffer = (AlphaPtr)NULL; /* only reset when we aren't dithering */ if(html->html.map_to_palette == XmDISABLED) html->html.xcc = (XCC)NULL; } else { /***** * We need to orphan all images: the formatter will be called shortly * after this routine returns and as a result of that the owner * of each image will become invalid. Not orphanizing them would * lead to a lot of image copying. * Info structures with the XmIMAGE_DELAYED_CREATION bit need to * propagate this info to their parent, or chances are that alpha * channeling will *not* be redone when required. * Must not forget to set the global delayed_creation flag or nothing * will happen. *****/ register XmHTMLImage *img; for(img = html->html.images; img != NULL; img = img->next) { img->owner = NULL; /* safety */ img->options |= IMG_ORPHANED; if(!ImageInfoFreed(img) && ImageInfoDelayedCreation(img->html_image)) { img->options |= IMG_DELAYED_CREATION; html->html.delayed_creation = True; } } } /* Free any allocated document colors */ _XmHTMLFreeColors(html); } /***** * Name: DestroyPhaseZero * Return Type: void * Description: discard all toolkit independent resources * In: * html: XmHTMLWidget id being destroyed * Returns: * nothing *****/ static void DestroyPhaseZero(XmHTMLWidget html) { /* First kill any outstanding PLC's */ _XmHTMLKillPLCCycler(html); /* release event database */ _XmHTMLFreeEventDatabase(html, html); /* Free list of parsed HTML elements */ html->html.elements = _XmHTMLparseHTML(html, html->html.elements, NULL, NULL); /* Free list of formatted HTML elements */ _XmHTMLformatObjects(html, html); /* Free list of form data */ _XmHTMLFreeForm(html, html->html.form_data); html->html.form_data = (XmHTMLFormData*)NULL; /* free all non-persitent resources and destroy the images */ FreeExpendableResources(html, True); /* free the XColorContext (can be here if we are using a fixed palette) */ XCCFree(html->html.xcc); /***** * Free list of frames. It is important that the images are destroyed * *before* the frames themselves get destroyed: frames can also have * images, and destroying the frame before destroying the image data * causes havoc. _XmHTMLDestroyFrames invokes XtDestroyWidget to destroy * each of XmHTML's frame childs, which invokes this routine and so on. *****/ if(html->html.nframes) _XmHTMLDestroyFrames(html, html->html.nframes); /* free all fonts for this TWidget's instance */ _XmHTMLUnloadFonts(html); /* free cursors */ if(html->html.anchor_cursor != None) Toolkit_Free_Cursor (XtDisplay(w), html->html.anchor_cursor); /* Free GC's */ if(html->html.gc) Toolkit_GC_Free (XtDisplay(html), html->html.gc); if(html->html.bg_gc) Toolkit_GC_Free (XtDisplay(html), html->html.bg_gc); _XmHTMLDebug(1, ("XmHTML.c: XmHTML_Destroy End\n")); return; } /***** * Name: GetAnchor * Return Type: XmHTMLWord* * Description: determines if the given x and y positions are within the * bounding rectangle of an anchor. * In: * w: HTML TWidget to check * x,y: position to validate * Returns: * A ptr. to the anchor data or NULL. * Note: * anchor_words is an array that _only_ contains anchor data. Although * it requires a bit more memory, it's worth it since it will be a fast * lookup. *****/ static XmHTMLWord* GetAnchor(XmHTMLWidget html, int x, int y) { XmHTMLWord *anchor_word = NULL; int ys, xs; register int i; xs = x + html->html.scroll_x; ys = y + html->html.scroll_y; for(i = 0 ; i < html->html.anchor_words; i++) { anchor_word = &(html->html.anchors[i]); if((xs >= anchor_word->x && xs<=(anchor_word->x+anchor_word->width)) && (ys>=anchor_word->y && ys<=(anchor_word->y+anchor_word->height))) { _XmHTMLFullDebug(1, ("XmHTML.c: GetAnchor, anchor is: %s\n", anchor_word->owner->anchor->href)); /* store line number */ anchor_word->owner->anchor->line = anchor_word->line; return(anchor_word); } } _XmHTMLFullDebug(1, ("XmHTML.c: GetAnchor, no match found\n")); return(NULL); } /***** * Name: GetImageAnchor * Return Type: XmHTMLAnchor* * Description: determines if the given x and y positions lie upon an image * that has an imagemap * In: * html: HTML TWidget to check * x,y: position to validate * Returns: * A ptr. to the anchor data or NULL. *****/ static XmHTMLAnchor* GetImageAnchor(XmHTMLWidget html, int x, int y) { XmHTMLImage *image = html->html.images; XmHTMLAnchor *anchor = NULL; int ys, xs; XmHTMLImageMap *imagemap = NULL; xs = x + html->html.scroll_x; ys = y + html->html.scroll_y; /* don't do this if we haven't got any imagemaps */ if(html->html.image_maps == NULL) return(NULL); _XmHTMLFullDebug(1, ("XmHTML.c: GetImageAnchor, xs = %i, ys = %i\n", xs, ys)); for(image = html->html.images; image != NULL; image = image->next) { #ifdef DEBUG if(image->owner) { _XmHTMLFullDebug(1, ("XmHTML.c: GetImageAnchor, checking %s, " "x = %i, y = %i\n", image->url, image->owner->x, image->owner->y)); } else { _XmHTMLFullDebug(1, ("XmHTML.c: GetImageAnchor, checking %s " "(no owner).", image->url)); } #endif if(image->owner && (xs >= image->owner->x && xs <= (image->owner->x + image->owner->width)) && (ys >= image->owner->y && ys <= (image->owner->y + image->owner->height))) { if(image->map_type != XmMAP_NONE) { if(image->map_type == XmMAP_SERVER) { _XmHTMLWarning(__WFUNC__(html, "GetImageAnchor"), "server side imagemaps not supported yet."); return(NULL); } if((imagemap = _XmHTMLGetImagemap(html, image->map_url)) != NULL) { if((anchor = _XmHTMLGetAnchorFromMap(html, x, y, image, imagemap)) != NULL) { _XmHTMLDebug(1, ("XmHTML.c: GetImageAnchor, anchor " "is: %s\n", anchor->href)); return(anchor); } } } } } _XmHTMLFullDebug(1, ("XmHTML.c: GetImageAnchor, no match found\n")); return(NULL); } /***** * Name: PaintAnchorUnselected * Return Type: void * Description: paints the currently active anchor in an unactivated state. * _XmHTMLPaint will do the proper rendering. * In: * w: HTML TWidget of which to unset the current anchor * Returns: * nothing. *****/ static void PaintAnchorUnSelected(XmHTMLWidget html) { XmHTMLObjectTable *start, *end; start = html->html.current_anchor; /* pick up the anchor end. An anchor ends when the raw worddata changes. */ for (end = start; end != NULL && end->object == start->object; end = end->next) end->anchor_state = ANCHOR_UNSELECTED; _XmHTMLFullDebug(1, ("XmHTML.c: PaintAnchorUnselected, unselecting " "anchor: %s\n", start->anchor->href)); /* paint it... */ _XmHTMLPaint(html, start, end); /* ...and invalidate current selection */ html->html.current_anchor = NULL; } /***** * Name: PaintAnchorSelected * Return Type: void * Description: paints the current active in an activated state. * _XmHTMLPaint will do the proper rendering. * In: * html: HTML TWidget of which to set the current anchor * Returns: * nothing. *****/ static void PaintAnchorSelected(XmHTMLWidget html, XmHTMLWord *anchor) { XmHTMLObjectTable *start, *end; /* save as the current active anchor */ start = html->html.current_anchor = anchor->owner; start = anchor->owner; /* pick up anchor end. */ for(end = start; end != NULL && end->object == start->object; end = end->next) end->anchor_state = ANCHOR_SELECTED; _XmHTMLFullDebug(1, ("XmHTML.c: PaintAnchorSelected, selecting anchor: " "%s\n", start->anchor->href)); /* paint it */ _XmHTMLPaint(html, start, end); } /***** * Name: LeaveAnchor * Return Type: void * Description: remove anchor highlighting. * In: * html: HTML TWidget of which to set the current anchor * Returns: * nothing. *****/ static void LeaveAnchor(XmHTMLWidget html) { XmHTMLObjectTable *start, *end; /* save as the current active anchor */ start = html->html.armed_anchor; /* pick up the anchor end. */ for(end = start; end != NULL && end->object == start->object; end = end->next) end->anchor_state = ANCHOR_UNSELECTED; _XmHTMLFullDebug(1, ("XmHTML.c: LeaveAnchor, leaving anchor: %s\n", start->anchor->href)); /* paint it */ _XmHTMLPaint(html, start, end); html->html.armed_anchor = NULL; } /***** * Name: EnterAnchor * Return Type: void * Description: paints a highlight on the given anchor. * In: * html: XmHTMLWidget id; * anchor: anchor to receive highlighting. * Returns: * nothing. *****/ static void EnterAnchor(XmHTMLWidget html, XmHTMLObjectTable *anchor) { XmHTMLObjectTable *start, *end; /* save as the current active anchor */ start = html->html.armed_anchor = anchor; /* pick up anchor end */ for(end = start; end != NULL && end->object == start->object; end = end->next) end->anchor_state = ANCHOR_INSELECT; _XmHTMLFullDebug(1, ("XmHTML.c: EnterAnchor, entering anchor: %s\n", start->anchor->href)); /* paint it */ _XmHTMLPaint(html, start, end); } /***** * Name: OnImage * Return Type: XmHTMLImage* * Description: checks whether the given positions fall within an image * In: * html: XmHTMLWidget id * x: pointer x-position * y: pointer y-position * Returns: * The selected image if a match was found, NULL if not. *****/ static XmHTMLImage* OnImage(XmHTMLWidget html, int x, int y) { XmHTMLImage *image; int xs, ys; xs = x + html->html.scroll_x; ys = y + html->html.scroll_y; _XmHTMLDebug(1, ("XmHTML.c: OnImage, xs = %i, ys = %i\n", xs, ys)); for(image = html->html.images; image != NULL; image = image->next) { if(image->owner && (xs >= image->owner->x && xs <= (image->owner->x + image->owner->width)) && (ys >= image->owner->y && ys <= (image->owner->y + image->owner->height))) { _XmHTMLDebug(1, ("XmHTML.c: OnImage, image selected: %s\n", image->url)); return(image); } } _XmHTMLDebug(1, ("XmHTML.c: OnImage, no match found\n")); return(NULL); } /***** * Name: CheckMaxColorSetting * Return Type: void * Description: checks value of the XmNmaxImageColors resource against * maximum number of colors allowed for this display. * In: * html: XmHTMLWidget * Returns: * nothing; *****/ static void CheckMaxColorSetting(XmHTMLWidget html) { int max_colors; /* check for an XCC */ if(html->html.xcc == NULL) _XmHTMLCheckXCC(html); /* get maximum allowable colors */ max_colors = html->html.xcc->num_colors; /* limit to 256 colors */ if(max_colors > MAX_IMAGE_COLORS) max_colors = MAX_IMAGE_COLORS; /* verify */ if(html->html.max_image_colors > max_colors) { _XmHTMLWarning(__WFUNC__(html, "CheckMaxColorSetting"), "Bad value for XmNmaxImageColors: %i colors selected while " "display only\n supports %i colors. Reset to %i", html->html.max_image_colors, max_colors, max_colors); html->html.max_image_colors = max_colors; } /* plop maximum colors in */ else if(html->html.max_image_colors == 0) html->html.max_image_colors = max_colors; } /***** * Name: CheckPLCIntervals * Return Type: void * Description: validates the delays for the PLC Cycler. * In: * html: XmHTMLWidget id * Returns: * nothing, but the PLC delay values can have changed when this function * returns. *****/ static void CheckPLCIntervals(XmHTMLWidget html) { int delay, min_delay, max_delay, new_delay; Boolean delay_reset = False; delay = html->html.plc_delay; min_delay = html->html.plc_min_delay; max_delay = html->html.plc_max_delay; if(min_delay <= 0) { _XmHTMLWarning(__WFUNC__(html, "CheckPLCIntervals"), "Invalid value for XmNprogressiveMinimumDelay (%i)\n" " Reset to %i", min_delay, PLC_MIN_DELAY); min_delay = PLC_MIN_DELAY; } if(delay < min_delay) { if(min_delay < PLC_DEFAULT_DELAY) new_delay = PLC_DEFAULT_DELAY; else new_delay = min_delay * 50; _XmHTMLWarning(__WFUNC__(html, "CheckPLCIntervals"), "Invalid value for XmNprogressiveInitialDelay (%i).\n" " Set to %i", delay, new_delay); delay = new_delay; delay_reset = True; } if(max_delay <= min_delay) { new_delay = min_delay <= PLC_MAX_DELAY ? PLC_MAX_DELAY : min_delay * 100; _XmHTMLWarning(__WFUNC__(html, "CheckPLCIntervals"), "XmNprogressiveMaximumDelay (%i) less than " "XmNprogressiveMinimumDelay (%i).\n Set to %i", max_delay, min_delay, new_delay); max_delay = new_delay; } /* can't do anything with this, reset to default values */ if(max_delay <= delay && !delay_reset) { _XmHTMLWarning(__WFUNC__(html, "CheckPLCIntervals"), "XmNprogressiveMaximumDelay (%i) smaller than " "XmNprogressiveInitialDelay (%i).\n Reset to default values", max_delay, min_delay); delay = PLC_DEFAULT_DELAY; min_delay = PLC_MIN_DELAY; max_delay = PLC_MAX_DELAY; } html->html.plc_delay = html->html.plc_def_delay = delay; html->html.plc_min_delay = min_delay; html->html.plc_max_delay = max_delay; } /***** * Name: _XmHTMLGetAnchorByName * Return Type: XmHTMLObjectTableElement * Description: returns the named anchor data. * In: * html: XmHTMLWidget * anchor: anchor to locate, with a leading hash sign. * Returns: * anchor data upon success, NULL on failure. *****/ XmHTMLObjectTableElement _XmHTMLGetAnchorByName(XmHTMLWidget html, String anchor) { XmHTMLObjectTableElement anchor_data; int i; String chPtr = NULL; /* see if it is indeed a named anchor */ if(!anchor || !*anchor || anchor[0] != '#') return(NULL); /* fix 02/03/97-04, kdh */ /* we start right after the leading hash sign */ chPtr = &anchor[1]; _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLGetAnchorByName Start\n")); for(i = 0 ; i < html->html.num_named_anchors; i++) { anchor_data = &(html->html.named_anchors[i]); if(anchor_data->anchor && anchor_data->anchor->name && strstr(anchor_data->anchor->name, chPtr)) { _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLGetAnchorByName End, " "match found.\n")); return(anchor_data); } } _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLGetAnchorByName End\n")); return(NULL); } /***** * Name: _XmHTMLGetAnchorByValue * Return Type: XmHTMLObjectTableElement * Description: returns the named anchor data. * In: * w: XmHTMLWidget * anchor_id: internal anchor id. * Returns: * anchor data upon success, NULL on failure. *****/ XmHTMLObjectTableElement _XmHTMLGetAnchorByValue(XmHTMLWidget html, int anchor_id) { XmHTMLObjectTableElement anchor_data; int i; _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLGetAnchorByValue Start\n")); /* this should always match */ anchor_data = &(html->html.named_anchors[anchor_id]); if(anchor_data->id == anchor_id) return(anchor_data); /* hmm, something went wrong, search the whole list of named anchors */ _XmHTMLWarning(__WFUNC__(html, "_XmHTMLGetAnchorByValue"), "Misaligned anchor stack (id=%i), trying to recover.", anchor_id); for(i = 0 ; i < html->html.num_named_anchors; i++) { anchor_data = &(html->html.named_anchors[i]); if(anchor_data->id == anchor_id) { _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLGetAnchorByValue End, " "match found.\n")); return(anchor_data); } } _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLGetAnchorByValue End\n")); return(NULL); } /***** * Name: VerticalPosToLine * Return Type: int * Description: translates a vertical position to the current line number * in the currently displayed document. * In: * html: XmHTMLWidget id; * y: absolute document y position. * Returns: * line number of the object found at the given position. nlines if not found. *****/ static int VerticalPosToLine(XmHTMLWidget html, int y) { XmHTMLObjectTableElement tmp; /* sanity check */ if(!html->html.formatted) return(0); if((tmp = _XmHTMLGetLineObject(html, y)) != NULL) { _XmHTMLDebug(1, ("XmHTML.c: VerticalPosToLine, object found, " "y_pos = %i, linenumber = %i\n", tmp->y, tmp->line)); /* * If the current element has got more than one word in it, and these * words span accross a number of lines, adjust the linenumber. */ if(tmp->n_words > 1 && tmp->words[0].y != tmp->words[tmp->n_words-1].y) { int i; for(i = 0 ; i < tmp->n_words && tmp->words[i].y < y; i++); if(i != tmp->n_words) return(tmp->words[i].line); else return(tmp->line); } else return(tmp->line); } return(0); } /***** * Name: ScrollToLine * Return Type: void * Description: scrolls the TWidget to the given line number. * In: * html: XmHTMLWidget id * line: line number to scroll to. * Returns: * nothing. *****/ static void ScrollToLine(XmHTMLWidget html, int line) { XmHTMLObjectTableElement tmp = NULL; if(line > html->html.nlines) { int value; _XmHTMLDebug(1, ("XmHTML.c: ScrollToLine, " "calling _XmHTMLMoveToPos\n")); html->html.top_line = html->html.nlines; value = html->html.formatted_height; /* fix 01/30/97-04, kdh */ AdjustVerticalScrollValue(html->html.vsb, value); _XmHTMLMoveToPos(html->html.vsb, html, value); return; } if(line <= 0) { html->html.top_line = 0; _XmHTMLDebug(1, ("XmHTML.c: ScrollToLine, " "calling _XmHTMLMoveToPos\n")); _XmHTMLMoveToPos(html->html.vsb, html, 0); return; } for(tmp = html->html.formatted; tmp != NULL && line > tmp->line; tmp = tmp->next); /* get vertical position */ if(tmp) { int i, value; /* position to scroll to */ /* we might have gone one object to far. Check and adjust */ tmp = (line != tmp->line ? (tmp->prev ? tmp->prev : tmp) : tmp); value = tmp->y - tmp->height; html->html.top_line = tmp->line; /* * Not exactly the requested line. Now check the line numbers of * the text inside this object. We need to subtract the height of this * object if we want to have it displayed properly. */ if(tmp->line != line) { if(tmp->n_words) { /* fix 11/11/97-01, dbl */ for(i = 0; i < tmp->n_words && line > tmp->words[i].line; i++); /* if found, we need to take y position of the previous word */ if(i != tmp->n_words && i != 0) { html->html.top_line = tmp->words[i-1].line; value = tmp->words[i-1].y - tmp->words[i-1].height; } } } _XmHTMLDebug(1, ("XmHTML.c: ScrollToLine, " "requested line: %i, lineno found: %i, y_pos: %i\n", line, tmp->line, value)); /* fix 01/30/97-04, kdh */ AdjustVerticalScrollValue(html->html.vsb, value); _XmHTMLMoveToPos(html->html.vsb, html, value); } else { _XmHTMLDebug(1, ("XmHTML.c: ScrollToLine, " "failed to detect requested line number!\n")); } } /******** ****** Public XmHTML Functions ********/ /***** * Name: XmHTMLAnchorGetId * Return Type: int * Description: returns the internal id of an anchor * In: * w: XmHTMLWidget * anchor: anchor to locate * Returns: * the id upon success, -1 if not found. *****/ int XmHTMLAnchorGetId(TWidget w, String anchor) { XmHTMLObjectTableElement anchor_data = NULL; /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLAnchorGetId"); return(-1); } if((anchor_data = _XmHTMLGetAnchorByName(XmHTML (w), anchor)) != NULL) return(anchor_data->id); else /* not found */ return(-1); } /***** * Name: XmHTMLScrollToAnchorById * Return Type: void * Description: moves the text with the current anchor on top. * In: * w: XmHTMLWidget * anchor_id: internal anchor id to scroll to. * Returns: * nothing. *****/ void XmHTMLAnchorScrollToId(TWidget w, int anchor_id) { XmHTMLWidget html; XmHTMLObjectTableElement anchor_data = NULL; /* sanity check */ if(!w || !XmIsHTML(w) || anchor_id < 0) { _XmHTMLWarning(__WFUNC__(w, "XmHTMLAnchorScrollToId"), "%s passed to XmHTMLAnchorScrollToId.", w ? (anchor_id < 0 ? "Invalid id" : "Invalid parent") : "NULL parent"); return; } html = XmHTML (w); /* only scroll when we have a vertical scrollbar */ /* fix 10/22/97-01, kdh */ if((anchor_data = _XmHTMLGetAnchorByValue(html, anchor_id)) != NULL && html->html.needs_vsb) { int value; _XmHTMLDebug(1, ("XmHTML.c: XmHTMLAnchorScrollToId, " "calling _XmHTMLMoveToPos\n")); value = anchor_data->y - anchor_data->height; /* fix 01/30/97-04, kdh */ AdjustVerticalScrollValue(html->html.vsb, value); _XmHTMLMoveToPos(html->html.vsb, html, value); } } /***** * Name: XmHTMLAnchorScrollToName * Return Type: void * Description: moves the text with the current anchor on top. * In: * w: XmHTMLWidget * anchor: anchor to scroll to. * Returns: * nothing. *****/ void XmHTMLAnchorScrollToName(TWidget w, String anchor) { XmHTMLWidget html; XmHTMLObjectTableElement anchor_data = NULL; /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLAnchorScrollToName"); return; } html = XmHTML (w); /* only scroll when we have a vertical scrollbar */ /* fix 10/22/97-01, kdh */ if((anchor_data = _XmHTMLGetAnchorByName(html, anchor)) != NULL && html->html.needs_vsb) { int value; _XmHTMLDebug(1, ("XmHTML.c: XmHTMLAnchorScrollToName, " "calling _XmHTMLMoveToPos\n")); value = anchor_data->y - anchor_data->height; /* fix 01/30/97-04, kdh */ AdjustVerticalScrollValue(html->html.vsb, value); _XmHTMLMoveToPos(html->html.vsb, html, value); } return; } /***** * Name: XmHTMLTextScrollToLine * Return Type: void * Description: scrolls the TWidget to the given line number. * In: * w: TWidget to scroll * line: line number to scroll to. * Returns: * nothing. *****/ void XmHTMLTextScrollToLine(TWidget w, int line) { /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLAnchorScrollToLine"); return; } if(line == (XmHTML (w))->html.top_line) return; /* scroll to the requested line */ ScrollToLine(XmHTML(w), line); } /***** * Name: XmHTMLTextGetSource * Return Type: String * Description: returns a copy of the original, unmodified document. * In: * w: XmHTMLWidget in question * Returns: * a *pointer* to the original text, or NULL when w isn't a subclass of XmHTML * or there wasn't a current document. *****/ String XmHTMLTextGetSource(TWidget w) { if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLTextGetSource"); return(NULL); } return((XmHTML (w))->html.source); } /***** * Name: XmHTMLTextGetString * Return Type: String * Description: composes a text buffer consisting of the parser output. * This return buffer is not necessarely equal to the original * document as the document verification and repair routines * are capable of modifying the original rather heavily. * In: * w: XmHTMLWidget in question * Returns: * An allocated buffer containing the composed text upon success, NULL on * failure. * Note: * The return value from this function must be freed by the caller. * Typical use of this function is to set this buffer into the TWidget when * the parser failed to verify the document as it might well be that a next * parser pass on the original document does produce a HTML3.2 conforming * and verified document. *****/ String XmHTMLTextGetString(TWidget w) { if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLTextGetString"); return(NULL); } return(_XmHTMLTextGetString((XmHTML(w))->html.elements)); } /***** * Name: XmHTMLGetVersion * Return Type: int * Description: returns the version number of XmHTML * In: * nothing * Returns: * version number of this library. *****/ int XmHTMLGetVersion(void) { return(XmHTMLVersion); } /***** * Name: XmHTMLGetTitle * Return Type: String * Description: returns the value of the element * In: * w: XmHTMLWidget in question * Returns: * value of the title upon success, NULL on failure. *****/ String XmHTMLGetTitle(TWidget w) { XmHTMLWidget html; XmHTMLObject *tmp; static String ret_val; String start, end; /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLGetTitle"); return(NULL); } html = XmHTML(w); for(tmp = html->html.elements; tmp != NULL && tmp->id != HT_TITLE && tmp->id != HT_BODY; tmp = tmp->next); /* sanity check */ if(!tmp || !tmp->next || tmp->id == HT_BODY) return(NULL); /* ok, we have reached the title element, pick up the text */ tmp = tmp->next; /* another sanity check */ if(!tmp->element) return(NULL); /* skip leading... */ for(start = tmp->element; *start != '\0' && isspace(*start); start++); /* ...and trailing whitespace */ for(end = &start[strlen(start)-1]; *end != '\0' && isspace(*end); end--); /* always backs up one to many */ end++; /* sanity */ if(*start == '\0' || (end - start) <= 0) return(NULL); /* duplicate the title */ ret_val = my_strndup(start, end - start); /* expand escape sequences */ _XmHTMLExpandEscapes(ret_val, html->html.bad_html_warnings); /* and return to caller */ return(ret_val); } /***** * Name: XmHTMLRedisplay * Return Type: void * Description: forces a layout recomputation of the currently loaded document * and triggers a redisplay. * In: * w: TWidget for which to redo layout computation. * Returns: * nothing * Note: * This function is mostly useful in combination with the image updating * and/or replacing routines. *****/ void XmHTMLRedisplay(TWidget w) { XmHTMLWidget html; /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLRedisplay"); return; } html = XmHTML (w); /* recompute screen layout */ Layout(html); if(html->html.gc != NULL) XmHTML_Frontend_Redisplay (html); } /***** * Name: XmHTMLTextSetString * Return Type: void * Description: sets the given text into the given HTML TWidget * In: * w: XmHTMLWidget in question * value: text to set * Returns: * clears any previous text and sets the new text. *****/ void XmHTMLTextSetString(TWidget w, String text) { /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLTextSetString"); return; } XmHTMLTextSetStringWithLength(w, text, text ? strlen (text) : 0); } /***** * Name: XmHTMLTextSetStringWithLength * Return Type: void * Description: sets the given text into the given HTML widget * In: * w: XmHTMLWidget in question * value: text to set. Doesn't have to be NULL terminated * len: size of input string. * Returns: * clears any previous text and sets the new text. *****/ void XmHTMLTextSetStringWithLength(TWidget w, String text, size_t len) { XmHTMLWidget html; /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLTextSetStringWithLength"); return; } _XmHTMLDebug(1, ("XmHTML.c: XmHTMLTextSetStringWithLength, start\n")); html = XmHTML (w); /* almost impossible */ if(html->html.value == text) return; /* check if the new value is different from the current source */ if(text && html->html.source && len && !(strncmp(html->html.source, text, len))) return; /* First kill any outstanding PLC's */ _XmHTMLKillPLCCycler(html); /* release event database */ _XmHTMLFreeEventDatabase(html, html); /* now destroy any forms */ _XmHTMLFreeForm(html, html->html.form_data); html->html.form_data = (XmHTMLFormData*)NULL; /* clear the current display area. Prevents color flashing etc. */ if(html->html.gc != NULL) { Toolkit_Clear_Area (XtDisplay(html->html.work_area), Toolkit_Widget_Window(html->html.work_area), 0, 0, Toolkit_Widget_Dim (html).width, Toolkit_Widget_Dim (html).height, False); } /* clear current source */ if(html->html.source) { free(html->html.source); html->html.source = NULL; html->html.value = NULL; } /* set new source text */ if(text && len) { /* strndup returns a NULL terminated string */ html->html.source = my_strndup(text, len); html->html.value = html->html.source; } /* destroy any existing frames */ if(html->html.nframes) _XmHTMLDestroyFrames(html, html->html.nframes); /* free all non-persistent resources and images */ FreeExpendableResources(html, True); /* reset some important vars */ ResetWidget(html, True); /* Parse the raw HTML text */ html->html.elements = _XmHTMLparseHTML(html, html->html.elements, html->html.source, html); /* Trigger link callback */ if(CHECK_CALLBACK (html, link_callback, LINK)) _XmHTMLLinkCallback(html); /* reset topline */ html->html.top_line = 0; /* check for frames */ html->html.nframes = _XmHTMLCheckForFrames(html, html->html.elements); #ifdef WITH_MOTIF /* set appropriate background color */ XtVaSetValues(html->html.work_area, XmNbackground, html->html.body_bg, NULL); #else { /* FIXME: is setting the window background the same as setting * the XmNbackground resource on the work_area? */ GdkColor c; c.pixel = html->html.body_bg; gdk_window_set_background(html->html.work_area->window, &c); } #endif /* get new values for top, bottom & highlight */ _XmHTMLRecomputeColors(html); /* create frames */ if(!_XmHTMLCreateFrames(NULL, html)) { html->html.frames = NULL; html->html.nframes = 0; /* keep current frame setting */ } /* do initial markup */ _XmHTMLformatObjects(html, html); /* check for delayed external imagemaps */ _XmHTMLCheckImagemaps(html); /* compute new screen layout */ Layout(html); /* and clear the display, causing an Expose event */ if(html->html.gc != NULL) _XmHTMLClearArea (html, 0, 0, Toolkit_Widget_Dim (html).width, Toolkit_Widget_Dim(html).height); /* and start up the PLC cycler */ html->html.plc_suspended = False; #ifdef WITH_MOTIF _XmHTMLPLCCycler((XtPointer)html, None); #else _XmHTMLPLCCycler((XtPointer)html); #endif } /***** * Name: XmHTMLXYToInfo * Return Type: XmHTMLInfoStructure* * Description: Retrieves the contents of an image and/or anchor at the * given cursor position. * In: * w: XmHTMLWidget id; * x: x-location of pointer, relative to left side of the workArea * y: y-location of pointer, relative to top side of the workArea * Returns: * A filled XmHTMLInfoStructure when the pointer was pressed on an image * and/or anchor. NULL if not. * Note: * The return value, nor one of its members may be freed by the caller. *****/ XmHTMLInfoPtr XmHTMLXYToInfo(TWidget w, int x, int y) { static XmHTMLInfoStructure cbs; static XmHTMLImage *image; static XmHTMLAnchorCallbackStruct anchor_cbs; static XmImageInfo info; long line = -1; XmHTMLAnchor *anchor; XmHTMLWord *anchor_word; XmHTMLWidget html; /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLXYToInfo"); return(NULL); } html = XmHTML (w); /* default fields */ #ifdef WITH_MOTIF cbs.x = x - html->core.x; cbs.y = y - html->core.y; #else cbs.x = x; cbs.y = y; #endif cbs.is_map = XmMAP_NONE; cbs.image = NULL; cbs.anchor = NULL; line = -1; /* pick up a possible anchor or imagemap location */ anchor = NULL; if((anchor_word = GetAnchor(html, x, y)) == NULL) anchor = GetImageAnchor(html, x, y); /* no regular anchor, see if it's an imagemap */ if(anchor == NULL && anchor_word) anchor = anchor_word->owner->anchor; /* * Final check: if this anchor is a form component it can't be followed * as this is an internal-only anchor. */ if(anchor && anchor->url_type == ANCHOR_FORM_IMAGE) anchor = NULL; /* check if we have an anchor */ if(anchor != NULL) { /* set to zero */ (void)memset(&anchor_cbs, 0, sizeof(XmHTMLAnchorCallbackStruct)); /* initialize callback fields */ anchor_cbs.reason = XmCR_ACTIVATE; anchor_cbs.event = NULL; anchor_cbs.url_type = anchor->url_type; anchor_cbs.line = anchor->line; anchor_cbs.href = anchor->href; anchor_cbs.target = anchor->target; anchor_cbs.rel = anchor->rel; anchor_cbs.rev = anchor->rev; anchor_cbs.title = anchor->title; anchor_cbs.doit = False; anchor_cbs.visited = anchor->visited; cbs.anchor = &anchor_cbs; line = anchor->line; } /* check if we have an image */ if((image = OnImage(html, x, y)) != NULL) { /* set map type */ cbs.is_map = (image->map_type != XmMAP_NONE); if(image->html_image != NULL) { /* no image info if this image is being loaded progressively */ if(!ImageInfoProgressive(image->html_image)) { /* use real url but link all other members */ info.url = image->url; info.data = image->html_image->data; info.clip = image->html_image->clip; info.width = image->html_image->width; info.height = image->html_image->height; info.reds = image->html_image->reds; info.greens = image->html_image->greens; info.blues = image->html_image->blues; info.bg = image->html_image->bg; info.ncolors = image->html_image->ncolors; info.options = image->html_image->options; info.type = image->html_image->type; info.depth = image->html_image->depth; info.colorspace = image->html_image->colorspace; info.transparency = image->html_image->transparency; info.swidth = image->html_image->swidth; info.sheight = image->html_image->sheight; info.scolors = image->html_image->scolors; info.alpha = image->html_image->alpha; info.fg_gamma = image->html_image->fg_gamma; info.x = image->html_image->x; info.y = image->html_image->y; info.loop_count = image->html_image->loop_count; info.dispose = image->html_image->dispose; info.timeout = image->html_image->timeout; info.nframes = image->html_image->nframes; info.frame = image->html_image->frame; info.user_data = image->html_image->user_data; /* set it */ cbs.image = &info; } } else { /* XmImageInfo has been freed, construct one */ /* set to zero */ memset(&info, 0, sizeof(XmImageInfo)); /* fill in the fields we know */ info.url = image->url; info.type = IMAGE_UNKNOWN; info.width = image->swidth; info.height = image->sheight; info.swidth = image->width; info.sheight = image->height; info.ncolors = image->npixels; info.nframes = image->nframes; /* set it */ cbs.image = &info; } if(line == -1) line = (image->owner ? image->owner->line : -1); } /* no line number yet, get one */ if(line == -1) cbs.line = VerticalPosToLine(html, y + html->html.scroll_y); else cbs.line = line; return(&cbs); } /***** * Name: XmHTMLTextGetFormatted * Return Type: String * Description: returns a formatted copy of the current document. * In: * w: XmHTMLWidget id; * papertype: type of paper to use (any of the XmHTMLTEXT_PAPERSIZE enums); * papersize: size of paper for custom stuff, or default overrides; * type: type of output wanted, plain, formatted or PS; * PSoptions: options to use when creating postscript output. * Returns: * a string which needs to be freed by the caller. *****/ String XmHTMLTextGetFormatted(TWidget w, unsigned char papertype, XmHTMLPaperSize *paperdef, unsigned char type, unsigned char PSoptions) { XmHTMLWidget html; #if 0 XmHTMLPaperSize *pdef, pbase; #endif /* sanity check */ if(!w || !XmIsHTML(w)) { _XmHTMLBadParent(w, "XmHTMLTextGetFormatted"); return(NULL); } /***** * Check args: we only allow a papersize of XmHTML_NONE for plain and * formatted output. PS requires a papersize. *****/ if(paperdef == NULL && type == XmHTMLTEXT_POSTSCRIPT) { _XmHTMLWarning(__WFUNC__(w, "XmHTMLTextGetFormatted"), "Formatted text output: postscript requires a papertype."); return(NULL); } /* custom papersize requires a paper definition. */ if(papertype == XmHTMLTEXT_PAPERSIZE_CUSTOM && paperdef == NULL) { _XmHTMLWarning(__WFUNC__(w, "XmHTMLTextGetFormatted"), "Formatted text output: custom papersize misses a " "papersize definition."); return(NULL); } /* TWidget ptr */ html = XmHTML (w); #if 0 /***** * get appropriate papersize definitions if not given. *****/ if(papertype != XmHTMLTEXT_PAPERSIZE_CUSTOM && paperdef == NULL) { /* formatting routines use point size */ if(papertype == XmHTMLTEXT_PAPERSIZE_A4) { pbase.unit_type = XmHTML_POINT; pbase.paper_type = XmHTMLTEXT_PAPERSIZE_A4; pbase.width = 845; /* 297mm */ pbase.height = 597; /* 210mm */ pbase.left_margin = 57; /* 20mm */ pbase.right_margin = 57; pbase.top_margin = 57; pbase.bottom_margin = 57; } else /* XmHTMLTEXT_PAPERSIZE_LETTER */ { pbase.unit_type = XmHTML_POINT; pbase.paper_type = XmHTMLTEXT_PAPERSIZE_LETTER; pbase.width = 795; /* 11in */ pbase.height = 614; /* 8.5in */ pbase.left_margin = 65; /* 0.9in */ pbase.right_margin = 65; pbase.top_margin = 65; pbase.bottom_margin = 51; /* 0.7in */ } /* convert to correct output type */ pdef = _XmHTMLTextCheckAndConvertPaperDef(html, &paperdef, type); } else /* check validity of paper definition and convert to correct type */ if((pdef = _XmHTMLTextCheckAndConvertPaperDef(html, paperdef, type)) == NULL) return(NULL); if(type == XmHTMLTEXT_PLAIN) return(_XmHTMLTextGetPlain(html, pdef, html->html.formatted, NULL, 0)); else if(type == XmHTMLTEXT_FORMATTED) return(_XmHTMLTextGetFormatted(html, pdef, html->html.formatted, NULL, 0)); else if(type == XmHTMLTEXT_POSTSCRIPT) return(_XmHTMLTextGetPS(html, pdef, html->html.formatted, NULL, PSoptions)); else _XmHTMLWarning(__WFUNC__(w, "XmHTMLTextGetFormatted"), "Formatted text output: Invalid type selected."); #endif return(NULL); } /***** * Name: _XmHTMLMoveToPos * Return Type: void * Description: scroll the working area with the given value * In: * w: originator * html: XmHTMLWidget * value: position to scroll to * Returns: * nothing *****/ void _XmHTMLMoveToPos(TWidget w, XmHTMLWidget html, int value) { int inc, x, y, width, height; TWindow win = Toolkit_Widget_Window(html->html.work_area); TGC gc = html->html.gc; int vsb_width = 0, hsb_height = 0; /* sanity check */ if(value < 0) return; /* default exposure region */ x = y = 0; width = Toolkit_Widget_Dim (html).width; height = Toolkit_Widget_Dim (html).height; #ifdef WITH_MOTIF /* * need to adjust slider position since we may not be called from * the scrollbar callback handler. */ XtVaSetValues(w, XmNvalue, value, NULL); #else { GtkAdjustment *wa; if (w == html->html.vsb) wa = GTK_ADJUSTMENT (html->vsba); else if (w == html->html.hsb) wa = GTK_ADJUSTMENT (html->hsba); else wa = 0; if (wa){ wa->value = value; gtk_range_slider_update (GTK_RANGE (w)); } } #endif /* vertical scrolling */ if(w == html->html.vsb) { /* * clicking on the slider causes activation of the scrollbar * callbacks. Since there is no real movement, just return. * Not doing this will cause an entire redraw of the window. */ if(value == html->html.scroll_y) return; /* fix 01/20/97-01 kdh */ /* save line number */ SetCurrentLineNumber(html, value); /* moving down (text moving up) */ if(value > html->html.scroll_y) { inc = value - html->html.scroll_y; /* save new value */ html->html.scroll_y = value; /* save new paint engine start */ /* html->html.paint_start = html->html.paint_end; */ html->html.paint_end = html->html.paint_start; /* small increment */ if(inc < html->html.work_height) { /***** * See if we have a hsb. If we have one, we need to add * the height of the hsb to the region requiring updating. *****/ if(html->html.needs_hsb) #ifdef NO_XLIB_ILLEGAL_ACCESS GetScrollDim(html, &hsb_height, &vsb_width); #else hsb_height = Toolkit_Widget_Dim (html->html.hsb).height; #endif /* copy visible part upward */ Toolkit_Copy_Area (dpy, win, win, gc, 0, inc, html->html.work_width + html->html.margin_width, html->html.work_height - inc - hsb_height, 0, 0); /* clear area below */ x = 0; y = html->html.work_height - inc - hsb_height; width = Toolkit_Widget_Dim (html).width; height = inc + hsb_height; } /* large increment, use default area */ } /* moving up (text moving down) */ else { inc = html->html.scroll_y - value; /* save new value */ html->html.scroll_y = value; /* small increment */ if(inc < html->html.work_height) { /* copy area down */ Toolkit_Copy_Area (dpy, win, win, gc, 0, 0, html->html.work_width + html->html.margin_width, html->html.work_height - inc, 0, inc); /* save paint engine end */ html->html.paint_end = html->html.paint_start; /* clear area above */ x = y = 0; width = Toolkit_Widget_Dim (html).width; height = inc; } /* large increment, use default area */ } } /* horizontal scrolling */ else if(w == html->html.hsb) { /* * clicking on the slider causes activation of the scrollbar * callbacks. Since there is no real movement, just return. * Not doing this will cause an entire redraw of the window. */ if(value == html->html.scroll_x) return; /* fix 01/20/97-01 kdh */ /* moving right (text moving left) */ if (value > html->html.scroll_x) { inc = value - html->html.scroll_x; /* save new value */ html->html.scroll_x = value; /* small increment */ if(inc < html->html.work_width) { /* * See if we have a vsb. If we have, no additional offset * required, otherwise we also have to clear the space that * has been reserved for it. */ if(!html->html.needs_vsb) #ifdef NO_XLIB_ILLEGAL_ACCESS GetScrollDim(html, &hsb_height, &vsb_width); #else vsb_width = Toolkit_Widget_Dim (html->html.vsb).width; #endif /* copy area to the left */ Toolkit_Copy_Area(dpy, win, win, gc, inc, 0, html->html.work_width - inc, html->html.work_height, 0, 0); /* clear area on right */ x = html->html.work_width - inc; y = 0; width = inc + html->html.margin_width + vsb_width; height = html->html.work_height; } /* large increment, use default area */ } /* moving left (text moving right) */ else { inc = html->html.scroll_x - value; /* save new value */ html->html.scroll_x = value; /* small increment */ if(inc < html->html.work_width) { if(!html->html.needs_vsb) #ifdef NO_XLIB_ILLEGAL_ACCESS GetScrollDim(html, &hsb_height, &vsb_width); #else vsb_width = Toolkit_Widget_Dim (html->html.vsb).width; #endif /* copy area to the right */ /* fix 01/24/97-01, kdh */ Toolkit_Copy_Area (dpy, win, win, gc, 0, 0, html->html.work_width - inc + html->html.margin_width + vsb_width, html->html.work_height, inc, 0); /* clear area on left */ x = y = 0; width = inc; height = html->html.work_height; } /* large increment, use default area */ } } else { _XmHTMLWarning(__WFUNC__(html, "_XmHTMLMoveToPos"), "Internal Error: unknown scrollbar!"); return; } /* update display */ _XmHTMLClearArea(html, x, y, width, height); } /***** * Name: _XmHTMLClearArea * Return Type: void * Description: XClearArea wrapper. Does form component updating as well. * In: * html: XmHTMLWidget id; * x,y: upper left corner of region to be updated; * width: width of region; * height: height of region; * Returns: * *****/ void _XmHTMLClearArea(XmHTMLWidget html, int x, int y, int width, int height) { TWindow win = Toolkit_Widget_Window(html->html.work_area); _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLClearArea Start, x: %i, y: %i, width: %i " "height: %i.\n", x, y, width, height)); /* first scroll form TWidgets if we have them */ if(html->html.form_data) { FormScroll(html); Toolkit_Clear_Area(dpy, win, x, y, width, height, False); Refresh(html, x, y, width, height); } else Toolkit_Clear_Area(dpy, win, x, y, width, height, True); _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLClearArea End.\n")); } static void AnchorTrack (XmHTMLWidget html, TEvent *event, int x, int y) { XmHTMLAnchor *anchor = NULL; XmHTMLWord *anchor_word = NULL; /* try to get current anchor element (if any) */ if(((anchor_word = GetAnchor(html, x, y)) == NULL) && ((anchor = GetImageAnchor(html, x, y)) == NULL)) { _XmHTMLFullDebug(1, ("XmHTML.c: TrackMotion, no current anchor.\n")); /* invalidate current selection if there is one */ if(CHECK_CALLBACK (html, anchor_track_callback, ANCHOR_TRACK) && html->html.anchor_current_cursor_element) _XmHTMLTrackCallback(html, event, NULL); if(html->html.highlight_on_enter && html->html.armed_anchor) LeaveAnchor(html); html->html.armed_anchor = NULL; html->html.anchor_current_cursor_element = NULL; Toolkit_Undefine_Cursor(Toolkit_Display(html), Toolkit_Widget_Window((TWidget)html)); return; } if(anchor == NULL) anchor = anchor_word->owner->anchor; /* Trigger callback and set cursor if we are entering a new element */ if(anchor != html->html.anchor_current_cursor_element) { _XmHTMLFullDebug(1, ("XmHTML.c: TrackMotion, new anchor.\n")); /* remove highlight of previous anchor */ if(html->html.highlight_on_enter) { if(anchor_word) { /* unarm previous selection */ if(html->html.armed_anchor && html->html.armed_anchor != anchor_word->owner) LeaveAnchor(html); /* highlight new selection */ EnterAnchor(html, anchor_word->owner); } else /* unarm previous selection */ if(html->html.armed_anchor) LeaveAnchor(html); } html->html.anchor_current_cursor_element = anchor; _XmHTMLTrackCallback(html, event, anchor); Toolkit_Define_Cursor (Toolkit_Display(w), Toolkit_Widget_Window((TWidget)html), html->html.anchor_cursor); return; } } /***** * Name: ExtendStart * Return Type: void * Description: buttonPress action routine. Initializes a selection when * not over an anchor, else paints the anchor as being selected. * In: * * Returns: * nothing. *****/ static void TPROTO (ExtendStart, TWidget w, TEvent *event, String *params, Cardinal *num_params) { /* need to use XtParent since we only get button events from work_area */ XmHTMLWidget html = XmHTML (Toolkit_Widget_Parent (w)); TButtonPressedEvent *pressed= (TButtonPressedEvent*)event; XmHTMLAnchor *anchor = NULL; XmHTMLWord *anchor_word = NULL; int x,y; #ifdef WITH_MOTIF /* no needless lingering in this routine */ if(XtClass(XtParent(w)) != xmHTMLWidgetClass) return; #endif /* we don't do a thing with events generated by button3 */ if(pressed->button == Button3 && (! CHECK_CALLBACK (html, arm_callback, ARM))) return; _XmHTMLFullDebug(1, ("XmHTML.c: ExtendStart Start\n")); /* Get coordinates of button event and add core offsets */ x = pressed->x; y = pressed->y; /* try to get current anchor element */ if(pressed->button != Button3 && (((anchor_word = GetAnchor(html, x, y)) != NULL) || ((anchor = GetImageAnchor(html, x, y)) != NULL))) { /***** * User has selected an anchor. Get the text for this anchor. * Note: if anchor is NULL it means the user was over a real anchor * (regular anchor, anchored image or a form image button) and * anchor_word is non-NULL (this object the referenced URL). If it * is non-NULL the mouse was over an imagemap, in which case we * may not show visual feedback to the user. * I admit, the names of the variables is rather confusing. ******/ if(anchor == NULL) { /* store anchor & paint as selected */ anchor = anchor_word->owner->anchor; /***** * uncheck currently selected anchor if it's not the same as * the current anchor (mouse dragging) *****/ if(html->html.current_anchor != NULL && html->html.current_anchor != anchor_word->owner) PaintAnchorUnSelected(html); PaintAnchorSelected(html, anchor_word); } else if(html->html.selected != NULL && html->html.selected != anchor) PaintAnchorUnSelected(html); /* check for the onMouseDown event */ if(anchor->events && anchor->events->onMouseDown) _XmHTMLProcessEvent(html, event, anchor->events->onMouseDown); html->html.selected = anchor; _XmHTMLFullDebug(1, ("XmHTML.c: ExtendStart, anchor selected is %s\n", anchor->href)); } else if(html->html.current_anchor != NULL) { /* not over an anchor, unselect current anchor and reset cursor */ PaintAnchorUnSelected(html); Toolkit_Undefine_Cursor(XtDisplay(w), Toolkit_Widget_Window(w)); } /* remember pointer position and time */ html->html.press_x = pressed->x; html->html.press_y = pressed->y; html->html.pressed_time = pressed->time; if(anchor_word == NULL && anchor == NULL && CHECK_CALLBACK (html, arm_callback, ARM)) { XmAnyCallbackStruct cbs; cbs.reason = XmCR_ARM; cbs.event = event; Toolkit_Call_Callback((TWidget)html, html->html.arm_callback, ARM, &cbs); } _XmHTMLFullDebug(1, ("XmHTML.c: ExtendStart End\n")); return; } /***** * Name: ExtendEnd * Return Type: void * Description: buttonrelease tracking action routine. Terminates the selection * initiated by ExtendStart. When over an anchor, paints the * anchor as being deselected. XmNactivateCallback or * XmNarmCallback callback resources are only called if the * buttonpress and release occur within a certain time limit * (XmHTML_MAX_BUTTON_RELEASE_TIME, defined XmHTMLfuncs.h) * In: * * Returns: * nothing *****/ static void TPROTO (ExtendEnd, TWidget w, TEvent *event, String *params, Cardinal *num_params) { /* need to use XtParent since we only get button events from work_area */ XmHTMLWidget html = XmHTML (Toolkit_Widget_Parent (w)); TButtonReleasedEvent *release = (TButtonReleasedEvent*)event; XmHTMLAnchor *anchor = NULL; XmHTMLWord *anchor_word = NULL; int x,y; #ifdef WITH_MOTIF /* no needless lingering in this routine */ if(XtClass(XtParent(w)) != xmHTMLWidgetClass) return; #endif /* we don't do a thing with events generated by button3 */ if(release->button == Button3) return; _XmHTMLFullDebug(1, ("XmHTML.c: ExtendEnd Start\n")); /* Get coordinates of button event */ x = release->x; y = release->y; /* try to get current anchor element */ if(((anchor_word = GetAnchor(html, x, y)) != NULL) || ((anchor = GetImageAnchor(html, x, y)) != NULL)) { /* * OK, release took place over an anchor, see if it falls within the * allowable time limit and we are still over the anchor selected by * ExtendStart. */ if(anchor == NULL) anchor = anchor_word->owner->anchor; /* * If we already have an active anchor and it's different from the * current anchor, deselect it. */ if(html->html.current_anchor && html->html.current_anchor != anchor_word->owner) PaintAnchorUnSelected(html); /* see if we need to serve the mouseUp event */ if(anchor->events && anchor->events->onMouseUp) _XmHTMLProcessEvent(html, event, anchor->events->onMouseUp); /* this anchor is still in selection */ if(anchor_word) EnterAnchor(html, anchor_word->owner); _XmHTMLFullDebug(1, ("XmHTML.c: ExtendEnd, anchor selected is %s\n", anchor->href)); /* * if we had a selected anchor and it's equal to the current anchor * and the button was released in time, trigger the activation callback. */ if(html->html.selected != NULL && anchor == html->html.selected && (release->time - html->html.pressed_time) < XmHTML_BUTTON_RELEASE_TIME) { /* check for the onClick event */ if(anchor->events && anchor->events->onClick) _XmHTMLProcessEvent(html, event, anchor->events->onClick); if(anchor->url_type == ANCHOR_FORM_IMAGE) _XmHTMLFormActivate(html, event, anchor_word->form); else { if(CHECK_CALLBACK (html, activate_callback, ACTIVATE)) { /* trigger activation callback */ _XmHTMLActivateCallback(html, event, anchor); _XmHTMLFullDebug(1, ("XmHTML.c: ExtendEnd End\n")); } } return; } } /* unset any previously selected anchor */ if(html->html.current_anchor != NULL) { /* keep current anchor selection or unset it */ if(anchor_word) EnterAnchor(html, anchor_word->owner); else PaintAnchorUnSelected(html); } _XmHTMLFullDebug(1, ("XmHTML.c: ExtendEnd End\n")); return; }