#include /* * Gtk/XmHTML widget port. * * Miguel de Icaza, 1997 * * FIXME: * - Check all widget repaints against original source, many are * bogus, they should be calls to ClearArea() */ #define SetScrollBars(HTML) #define AdjustVerticalScrollValue(VSB,VAL) do { \ int max = 0, size = 0; \ max = (int) (GTK_RANGE (VSB)->adjustment->upper); \ size = (int) (GTK_RANGE (VSB)->adjustment->page_size);\ if (VAL > (max - size)) \ VAL = (max - size); \ } while (0) gint gtk_xmhtml_signals [GTK_XMHTML_LAST_SIGNAL] = { 0, }; /* * Gdk does not have a visibility mask thingie *yet* */ #ifndef GDK_VISIBILITY_MASK # define GDK_VISIBILITY_MASK 0 #endif #define SCROLLBAR_SPACING 0 static GtkContainer *parent_class = NULL; /* prototypes for functions defined here */ static void gtk_xmhtml_realize (GtkWidget *widget) G_GNUC_UNUSED; static void gtk_xmhtml_unrealize (GtkWidget *widget) G_GNUC_UNUSED; static void gtk_xmhtml_forall (GtkContainer *we, gboolean include_internals, GtkCallback callback, gpointer callback_data); static void gtk_xmhtml_map (GtkWidget *widget); static void gtk_xmhtml_draw (GtkWidget *widget, GdkRectangle *area); static gint gtk_xmhtml_expose (GtkWidget *widget, GdkEventExpose *event); static void gtk_xmhtml_add (GtkContainer *container, GtkWidget *widget); static void gtk_xmhtml_remove (GtkContainer *container, GtkWidget *widget); static void gtk_xmhtml_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gtk_xmhtml_size_request (GtkWidget *widget, GtkRequisition *requisition); static void CheckScrollBars(XmHTMLWidget html); static void GetScrollDim(XmHTMLWidget html, int *hsb_height, int *vsb_width); static void ExtendStart(TWidget w, TEvent *event); static void ExtendEnd (TWidget w, TEvent *event); typedef gint (*GtkXmHTMLSignal1) (GtkObject *object, gpointer arg1, gpointer data); typedef gint (*GtkXmHTMLSignal2) (GtkObject *object, gpointer arg1, gpointer arg2, gpointer data); static void gtk_xmthml_marshall_1 (GtkObject *object, GtkSignalFunc func, gpointer data, GtkArg *args) { GtkXmHTMLSignal1 rfunc; rfunc = (GtkXmHTMLSignal1) func; (* rfunc) (object, GTK_VALUE_POINTER (args[0]), data); } static void gtk_xmthml_marshall_2 (GtkObject *object, GtkSignalFunc func, gpointer data, GtkArg *args) { GtkXmHTMLSignal2 rfunc; gint *return_val = GTK_RETLOC_INT(args[2]); rfunc = (GtkXmHTMLSignal2) func; *return_val = (* rfunc) (object, GTK_VALUE_STRING (args[0]), GTK_VALUE_POINTER (args [1]), data); } static Pixel pixel_color (GtkXmHTML *html, char *color_name) { GdkColor c; int n; _XmHTMLCheckXCC (html); gdk_color_parse (color_name, &c); c.pixel = 0; n = 0; XCCGetPixels (html->html.xcc, &c.red, &c.green, &c.blue, 1, &c.pixel, &n); return c.pixel; } /* These are initialized in the Motif sources with the resources */ static void gtk_xmhtml_resource_init (GtkXmHTML *html) { /* The strings */ html->html.mime_type = g_strdup ("text/html"); html->html.charset = g_strdup ("iso8859-1"); html->html.font_family = g_strdup ("adobe-times-normal-*"); html->html.font_family_fixed = g_strdup ("adobe-courier-normal-*"); html->html.font_sizes = g_strdup (XmHTML_DEFAULT_FONT_SCALABLE_SIZES); html->html.font_sizes_fixed = g_strdup (XmHTML_DEFAULT_FONT_FIXED_SIZES); html->html.zCmd = g_strdup ("gunzip"); html->html.event_proc = NULL; html->html.needs_vsb = FALSE; html->html.needs_hsb = FALSE; html->html.scroll_x = 0; html->html.scroll_y = 0; html->html.alignment = TALIGNMENT_BEGINNING; html->html.anchor_cursor = NULL; html->html.anchor_display_cursor = TRUE; html->html.anchor_buttons = TRUE; html->html.anchor_fg = pixel_color (html, "blue1"); html->html.anchor_visited_fg = pixel_color (html, "red"); html->html.anchor_target_fg = pixel_color (html, "blue1"); html->html.anchor_activated_fg = pixel_color (html, "red"); html->html.anchor_activated_bg = pixel_color (html, "white"); html->html.highlight_on_enter = TRUE; html->html.anchor_underline_type = LINE_SOLID | LINE_UNDER | LINE_SINGLE; html->html.anchor_target_underline_type = LINE_DASHED | LINE_UNDER | LINE_SINGLE; html->html.anchor_visited_underline_type = LINE_SOLID | LINE_UNDER | LINE_SINGLE; html->html.anchor_visited_proc = NULL; html->html.image_proc = NULL; html->html.gif_proc = NULL; #if 0 /* The Gtk edition of the code does not actually use these, * it uses the signal mechanism instead */ html->html.anchor_track_callback = NULL; html->html.activate_callback = NULL; html->html.arm_callback = NULL; html->html.frame_callback = g_list_append(NULL, donothing); html->html.form_callback = NULL; html->html.focus_callback = NULL; html->html.losing_focus_callback = NULL; html->html.link_callback = NULL; html->html.input_callback = NULL; html->html.motion_track_callback = NULL; html->html.imagemap_callback = NULL; html->html.document_callback = NULL; html->html.event_callback = NULL; #endif html->html.hsb = NULL; html->html.vsb = NULL; html->html.images_enabled = TRUE; html->html.max_image_colors = 0; html->html.screen_gamma = 2.2; html->html.get_data = NULL; html->html.end_data = NULL; html->html.plc_delay = PLC_DEFAULT_DELAY; html->html.plc_min_delay = PLC_MIN_DELAY; html->html.plc_max_delay = PLC_MAX_DELAY; html->html.perfect_colors = 0; /* XmAUTOMATIC; */ html->html.resize_height = FALSE; html->html.resize_width = FALSE; html->html.sb_policy = GTK_POLICY_AUTOMATIC; html->html.sb_placement = 0; /* FIXME */ html->html.margin_height = XmHTML_DEFAULT_MARGIN; html->html.margin_width = XmHTML_DEFAULT_MARGIN; html->html.string_direction = TSTRING_DIRECTION_L_TO_R; html->html.strict_checking = FALSE; html->html.enable_outlining = FALSE; html->html.top_line = 0; html->html.value = NULL; html->html.work_area = NULL; html->html.bad_html_warnings = TRUE; html->html.body_colors_enabled = TRUE; html->html.body_images_enabled = TRUE; html->html.allow_color_switching = TRUE; html->html.allow_font_switching = TRUE; html->html.allow_form_coloring = TRUE; html->html.imagemap_fg = pixel_color (html, "White"); html->html.imagemap_draw = FALSE; html->html.repeat_delay = 25; html->html.freeze_animations = FALSE; html->html.map_to_palette = XmDISABLED; html->html.palette = NULL; html->html.def_body_image_url = NULL; html->html.alpha_processing = XmALWAYS; html->html.rgb_conv_mode = XmBEST; html->html.client_data = NULL; #ifdef DEBUG html->html.debug_disable_warnings= FALSE; html->html.debug_full_output = FALSE; html->html.debug_save_clipmasks = FALSE; html->html.debug_prefix = NULL; html->html.debug_no_loopcount = FALSE; html->html.debug_levels = NULL; #endif } static void gtk_xmhtml_reset_pending_flags (GtkXmHTML *html) { html->parse_needed = 0; html->reformat_needed = 0; html->redraw_needed = 0; html->free_images_needed = 0; html->layout_needed = 0; } static void gtk_xmhtml_create_widgets (GtkXmHTML *html); static void gtk_xmhtml_init (GtkXmHTML *html) { GTK_WIDGET_SET_FLAGS (html, GTK_NO_WINDOW); gtk_xmhtml_resource_init (html); gtk_xmhtml_reset_pending_flags (html); /* Gtk port bits */ html->children = NULL; html->background_pixel = 0; html->foreground_pixel = 0; html->bottom_shadow_gc = NULL; html->top_shadow_gc = NULL; html->highlight_gc = NULL; html->highlight_color = 0; /* moved here from gtk_xmhtml_new() so one can subclass */ /* GtkXmHTML widget properly */ GTK_WIDGET(html)->allocation.width = 200; GTK_WIDGET(html)->allocation.height = 200; html->initialized = 0; html->frozen = 0; gtk_xmhtml_create_widgets (html); gtk_xmhtml_source (html, ""); html->frozen = 1; } GtkWidget * gtk_xmhtml_new (void) { GtkXmHTML *html; html = gtk_type_new (gtk_xmhtml_get_type ()); return GTK_WIDGET (html); } static void gtk_xmhtml_destroy (GtkObject *object) { GtkXmHTML *html = GTK_XMHTML (object); DestroyPhaseZero (html); } static void gtk_xmhtml_class_init (GtkXmHTMLClass *class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkContainerClass *container_class; extern FILE *__rsd_debug_file; __rsd_debug_file = stdout; object_class = (GtkObjectClass *) class; widget_class = (GtkWidgetClass *) class; container_class = (GtkContainerClass *) class; parent_class = gtk_type_class (gtk_container_get_type ()); gtk_xmhtml_signals [GTK_XMHTML_ACTIVATE] = gtk_signal_new ("activate", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, activate), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_ARM] = gtk_signal_new ("arm", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, rearm), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_ANCHOR_TRACK] = gtk_signal_new ("anchor_track", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, anchor_track), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_FRAME] = gtk_signal_new ("frame", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, frame), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_FORM] = gtk_signal_new ("form", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, form), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_INPUT] = gtk_signal_new ("input", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, input), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_LINK] = gtk_signal_new ("link", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, link), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_MOTION] = gtk_signal_new ("motion", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, motion), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_IMAGEMAP] = gtk_signal_new ("imagemap", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, imagemap), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_DOCUMENT] = gtk_signal_new ("document", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, document), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_FOCUS] = gtk_signal_new ("_focus", /* there is already a focus signal */ GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, focus), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_LOSING_FOCUS] = gtk_signal_new ("losing_focus", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, losing_focus), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_MOTION_TRACK] = gtk_signal_new ("motion_track", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, motion_track), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_HTML_EVENT] = gtk_signal_new ("html_event", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, motion_track), gtk_xmthml_marshall_1, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_xmhtml_signals [GTK_XMHTML_ANCHOR_VISITED] = gtk_signal_new ("anchor_visited", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (GtkXmHTMLClass, anchor_visited), gtk_xmthml_marshall_2, GTK_TYPE_INT, 2, GTK_TYPE_STRING, GTK_TYPE_POINTER); gtk_object_class_add_signals (object_class, gtk_xmhtml_signals, GTK_XMHTML_LAST_SIGNAL); object_class->destroy = gtk_xmhtml_destroy; widget_class->map = gtk_xmhtml_map; /* widget_class->unmap = gtk_xmhtml_unmap; */ widget_class->draw = gtk_xmhtml_draw; widget_class->size_request = gtk_xmhtml_size_request; widget_class->size_allocate = gtk_xmhtml_size_allocate; widget_class->expose_event = gtk_xmhtml_expose; container_class->add = gtk_xmhtml_add; container_class->remove = gtk_xmhtml_remove; container_class->forall = gtk_xmhtml_forall; } static void gtk_xmhtml_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkXmHTML *html = GTK_XMHTML (widget); int extra_width, extra_height; GtkRequisition child_requisition; g_return_if_fail (widget != NULL); g_return_if_fail (requisition != NULL); requisition->width = 0; requisition->height = 0; if (GTK_WIDGET_VISIBLE (html->html.work_area)){ gtk_widget_size_request (html->html.work_area, &child_requisition); requisition->width += child_requisition.width; requisition->height += child_requisition.height; } extra_width = extra_height = 0; /* Horizontal scroll bar */ gtk_widget_size_request (html->html.hsb, &child_requisition); requisition->width = MAX (requisition->width, child_requisition.width); extra_height = SCROLLBAR_SPACING + child_requisition.height; /* Vertical scrollbarl */ gtk_widget_size_request (html->html.vsb, &child_requisition); requisition->height = MAX (requisition->height, child_requisition.height); extra_width = SCROLLBAR_SPACING + child_requisition.width; requisition->width += GTK_CONTAINER (widget)->border_width * 2 + extra_width; requisition->height += GTK_CONTAINER (widget)->border_width * 2 + extra_height; } static void gtk_xmhtml_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { widget->allocation = *allocation; /* printf ("Size Allocate: (%d,%d) %d %d\n", allocation->x, allocation->y, allocation->height, allocation->width); */ Resize (widget); } guint gtk_xmhtml_get_type (void) { static guint gtk_xmhtml_type = 0; if (!gtk_xmhtml_type){ GtkTypeInfo gtk_xmhtml_info = { "GtkXmHTML", sizeof (GtkXmHTML), sizeof (GtkXmHTMLClass), (GtkClassInitFunc) gtk_xmhtml_class_init, (GtkObjectInitFunc) gtk_xmhtml_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; gtk_xmhtml_type = gtk_type_unique (gtk_container_get_type (), >k_xmhtml_info); } return gtk_xmhtml_type; } /***** * Name: CreateAnchorCursor * Return Type: void * Description: creates the built-in anchor cursor * In: * html: XmHTMLWidget for which to create a cursor * Returns: * nothing. *****/ static void CreateAnchorCursor(XmHTMLWidget html) { TPixmap shape, mask; TWindow window = Toolkit_Widget_Window((TWidget)html); GdkColormap *colormap; GdkColor fg, bg, white, black; fg.pixel = 1; bg.pixel = 0; if (html->html.anchor_cursor != None) return; if(!window) window = Toolkit_Default_Root_Window (display); shape = gdk_pixmap_create_from_data (window, fingers_bits, fingers_width, fingers_height, 1, &fg, &bg); mask = gdk_pixmap_create_from_data (window, fingers_m_bits, fingers_m_width, fingers_m_height, 1, &fg, &bg); colormap = gtk_widget_get_colormap (GTK_WIDGET (html)); gdk_color_white (colormap, &white); gdk_color_black (colormap, &black); html->html.anchor_cursor = gdk_cursor_new_from_pixmap (shape, mask, &white, &black, fingers_x_hot, fingers_y_hot); } /***** * Name: FormScroll * Return Type: void * Description: scrolls all TWidgets of all forms in the current document. * In: * html: XmHTML TWidget id * Returns: * nothing. *****/ static void FormScroll (XmHTMLWidget html) { int x, y, xs, ys; XmHTMLFormData *form; XmHTMLForm *entry; Boolean did_anything = False; _XmHTMLDebug(1, ("XmHTML.c: FormScroll, Start\n")); for(form = html->html.form_data; form != NULL; form = form->next) { for(entry = form->components; entry != NULL; entry = entry->next) { if(entry->w) { /* save current TWidget position */ x = entry->x; y = entry->y; /* compute new TWidget position */ xs = entry->data->x - html->html.scroll_x; ys = entry->data->y - html->html.scroll_y; /* check if we need to show this TWidget */ if(xs + entry->width > 0 && xs < html->html.work_width && ys + entry->height > 0 && ys < html->html.work_height) { _XmHTMLDebug(1, ("XmHTML.c: FormScroll, moving " "TWidget %s to %ix%i\n", entry->name, xs, ys)); /* save new TWidget position */ entry->x = xs; entry->y = ys; /* and move to it */ gtk_xmhtml_set_geometry (entry->w, xs, ys, entry->width, entry->height); gtk_widget_show (entry->w); /* show it */ if(!entry->mapped) { entry->mapped = True; } /* restore background at previously obscured position */ Refresh(html, x, y, entry->width, entry->height); did_anything = True; } else { /* hide by unmapping it */ if(entry->mapped) { _XmHTMLDebug(1, ("XmHTML.c: FormScroll, hiding " "TWidget %s\n", entry->name)); gtk_widget_hide (entry->w); entry->mapped = False; /* restore background at previously obscured position */ Refresh(html, x, y, entry->width, entry->height); did_anything = True; } } } } } if (did_anything) gdk_flush (); } static void gtk_xmhtml_draw (GtkWidget *widget, GdkRectangle *area) { GtkXmHTML *html = GTK_XMHTML (widget); GdkRectangle na; GList *children; int i; if (!GTK_WIDGET_DRAWABLE (widget)) return; if (gtk_widget_intersect (html->html.vsb, area, &na)) gtk_widget_draw (html->html.vsb, &na); if (gtk_widget_intersect (html->html.hsb, area, &na)) gtk_widget_draw (html->html.hsb, &na); if (gtk_widget_intersect (html->html.work_area, area, &na)) gtk_widget_draw (html->html.work_area, &na); /* frames */ for (i = 0; i < html->html.nframes; i++) if (gtk_widget_intersect (html->html.frames [i]->frame, area, &na)) gtk_widget_draw (html->html.frames [i]->frame, &na); /* forms */ for (children = html->children; children; children = children->next){ GtkWidget *w = GTK_WIDGET (children->data); if (gtk_widget_intersect (w, area, &na)) gtk_widget_draw (w, &na); } } static gint gtk_xmhtml_expose (GtkWidget *widget, GdkEventExpose *event) { return FALSE; } /***** * Name: CheckGC * Return Type: void * Description: creates a Graphics Context to be used for rendering * In: * html: XmHTMLWidget * Returns: * nothing, but a GC is created and stored in the TWidget's internal data * structure. If background images are allowed, a seperate GC is created * which is used in PaintBackground to do tiling of the background with an * image. *****/ static void CheckGC(XmHTMLWidget html) { _XmHTMLDebug(1, ("XmHTML.c: CheckGC Start\n")); /* sanity check */ if(!GTK_WIDGET_REALIZED (GTK_WIDGET (html))) return; /* main gc */ if (html->html.gc == NULL){ GdkGCValues xgc; xgc.function = GDK_COPY; xgc.foreground.pixel = Toolkit_StyleColor_Foreground(html); xgc.background.pixel = Toolkit_StyleColor_Background(html); html->html.gc = gdk_gc_new_with_values (GTK_WIDGET(html)->window, &xgc, GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_FUNCTION); _XmHTMLRecomputeColors(html); _XmHTMLDebug(1, ("XmHTML.c: CheckGC, gc created\n")); } /* background image gc */ if(html->html.body_images_enabled && html->html.bg_gc == NULL) { html->html.bg_gc = gdk_gc_new (GTK_WIDGET(html)->window); gdk_gc_copy (html->html.bg_gc, html->html.gc); } _XmHTMLDebug(1, ("XmHTML.c: CheckGC End\n")); } static gint gtk_xmhtml_expose_event (GtkWidget *widget, GdkEvent *event, gpointer closure) { GtkXmHTML *html = closure; GdkEventExpose *e = (GdkEventExpose *) event; if (html->html.formatted == NULL || html->html.nframes) return FALSE; /* FIXME: The code in the Motif port does event coalescing */ Refresh(html, e->area.x, e->area.y, e->area.width, e->area.height); return FALSE; } /***** * Name: TrackMotion * Return Type: void * Description: mouse tracker; calls XmNanchorTrackCallback if * entering/leaving an anchor. * Also calls XmNmotionTrackCallback when installed. * In: * w: XmHTMLWidget * event: MotionEvent structure * params: additional args, unused * num_parmas: no of additional args, unused * Returns: * nothing *****/ static gint gtk_xmhtml_motion_event (GtkWidget *widget, GdkEvent *event, gpointer closure) { GtkXmHTML *html = GTK_XMHTML (closure); GdkEventMotion *motion = (GdkEventMotion *) event; XmAnyCallbackStruct cbs; int x, y; /* ignore if we don't have to make any more feedback to the user */ if (!html->html.need_tracking) return TRUE; /* we are already on the correct anchor, just return */ _XmHTMLFullDebug(1, ("XmHTML.c: TrackMotion Start.\n")); /* pass down to motion tracker callback if installed */ cbs.reason = XmCR_HTML_MOTIONTRACK; cbs.event = event; gtk_signal_emit (GTK_OBJECT (html), gtk_xmhtml_signals [GTK_XMHTML_MOTION_TRACK], &cbs); x = motion->x; y = motion->y; AnchorTrack (html, event, x, y); return TRUE; } /* * Handles focus_in, focus_out, leave_notify, enter_notify */ static int gtk_xmhtml_focus (GtkWidget *widget, GdkEvent *event, gpointer closure) { GtkXmHTML *html = GTK_XMHTML (closure); GtkObject *htmlo = GTK_OBJECT (closure); GdkEventFocus *focus = (GdkEventFocus *) event; XmAnyCallbackStruct cbs; gint focusing_in = 0; if (event->type == GDK_FOCUS_CHANGE) focusing_in = (focus->window == html->html.work_area->window); if (event->type == GDK_FOCUS_CHANGE && focusing_in){ cbs.reason = XmCR_FOCUS; cbs.event = event; gtk_signal_emit (htmlo, gtk_xmhtml_signals [GTK_XMHTML_FOCUS], &cbs); gdk_window_set_cursor (html->html.work_area->window, NULL); return TRUE; } /* Both Leave notify and focus out are handled here */ /* FIXME, from the Motif code: * LeaveNotify Events occur when the pointer focus is transferred * from the DrawingArea child to another window. This can occur * when the pointer is moved outside the Widget *OR* when a * ButtonPress event occurs ON the drawingArea. When that happens, * the pointer focus is transferred from the drawingArea to it's * parent, being the Widget itself. In this case the detail * detail member of the XEnterWindowEvent will be NotifyAncestor, * and we would want to ignore this event (as it will cause a * flicker of the screen or an unnecessary call to any installed * callbacks). * * * The correct code is thus: * * if(event->type == LeaveNotify && * ((XEnterWindowEvent*)event)->detail == NotifyAncestor) * then { * ignore this one to avoid calling callbacks and flicker * } */ /* invalidate current selection if there is one */ if (gtk_signal_handler_pending (GTK_OBJECT(html), gtk_xmhtml_signals [GTK_XMHTML_ANCHOR_TRACK], TRUE) && html->html.anchor_current_cursor_element) _XmHTMLTrackCallback (html, event, NULL); /* loses focus, remove anchor highlight */ if(html->html.highlight_on_enter && html->html.armed_anchor) LeaveAnchor(html); html->html.armed_anchor = NULL; html->html.anchor_current_cursor_element = NULL; gdk_window_set_cursor (html->html.work_area->window, NULL); /* final step: call focusOut callback */ if(event->type == FocusOut && CHECK_CALLBACK (html, losing_focus_callback, LOSING_FOCUS)){ cbs.reason = XmCR_LOSING_FOCUS; cbs.event = event; gtk_signal_emit (htmlo, gtk_xmhtml_signals [GTK_XMHTML_LOSING_FOCUS], &cbs); } return TRUE; } static int horizontal_scroll (GtkAdjustment *adj, gpointer user_data) { GtkXmHTML *html = GTK_XMHTML (user_data); _XmHTMLDebug(1, ("XmHTML.c: ScrollCB, calling _XmHTMLMoveToPos\n")); _XmHTMLMoveToPos (html->html.hsb, html, adj->value); return 0; } static int vertical_scroll (GtkAdjustment *adj, gpointer user_data) { GtkXmHTML *html = GTK_XMHTML (user_data); _XmHTMLDebug(1, ("XmHTML.c: ScrollCB, calling _XmHTMLMoveToPos\n")); _XmHTMLMoveToPos (html->html.vsb, html, adj->value); return 0; } static void drawing_area_realized (GtkWidget *widget, gpointer data) { GtkXmHTML *html = data; html->background_pixel = widget->style->bg[GTK_STATE_NORMAL].pixel; html->foreground_pixel = widget->style->fg[GTK_STATE_NORMAL].pixel; html->bottom_shadow_gc = gdk_gc_new (widget->window); gdk_gc_copy (html->bottom_shadow_gc, widget->style->dark_gc[GTK_STATE_NORMAL]); html->top_shadow_gc = gdk_gc_new (widget->window); gdk_gc_copy (html->top_shadow_gc, widget->style->light_gc[GTK_STATE_NORMAL]); html->highlight_gc = gdk_gc_new (widget->window); gdk_gc_copy (html->highlight_gc, widget->style->bg_gc[GTK_STATE_PRELIGHT]); html->highlight_color = widget->style->bg[GTK_STATE_PRELIGHT].pixel; gtk_xmhtml_thaw (html); /* FIXME: remember to destroy the GCs later */ } static void gtk_xmhtml_create_widgets (GtkXmHTML *html) { GtkWidget *draw_area; int events; /* Check if user provided a work area */ if(html->html.work_area == NULL) { draw_area = html->html.work_area = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (draw_area), 40, 40); gtk_xmhtml_manage (GTK_CONTAINER (html), draw_area); gtk_signal_connect (GTK_OBJECT (draw_area), "realize", (GtkSignalFunc) drawing_area_realized, html); gtk_signal_connect (GTK_OBJECT (draw_area), "expose_event", (GtkSignalFunc) gtk_xmhtml_expose_event, html); gtk_signal_connect (GTK_OBJECT (draw_area), "motion_notify_event", (GtkSignalFunc) gtk_xmhtml_motion_event, html); gtk_signal_connect (GTK_OBJECT (draw_area), "focus_in_event", (GtkSignalFunc) gtk_xmhtml_focus, html); gtk_signal_connect (GTK_OBJECT (draw_area), "focus_out_event", (GtkSignalFunc) gtk_xmhtml_focus, html); gtk_signal_connect (GTK_OBJECT (draw_area), "leave_notify_event", (GtkSignalFunc) gtk_xmhtml_focus, html); gtk_signal_connect (GTK_OBJECT (draw_area), "enter_notify_event", (GtkSignalFunc) gtk_xmhtml_focus, html); gtk_signal_connect (GTK_OBJECT (draw_area), "button_press_event", (GtkSignalFunc) ExtendStart, html); gtk_signal_connect (GTK_OBJECT (draw_area), "button_release_event", (GtkSignalFunc) ExtendEnd, html); events = gtk_widget_get_events (draw_area); gtk_widget_set_events (draw_area, events | GDK_EXPOSURE_MASK | GDK_FOCUS_CHANGE_MASK | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK /* not yet handled */ | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); gtk_widget_show (draw_area); } /* * Scrollbars for the widget */ if (html->html.hsb == NULL){ html->hsba = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); html->html.hsb = gtk_hscrollbar_new (GTK_ADJUSTMENT (html->hsba)); gtk_xmhtml_manage (GTK_CONTAINER(html), GTK_WIDGET (html->html.hsb)); gtk_widget_show (html->html.hsb); gtk_signal_connect (GTK_OBJECT(html->hsba), "value_changed", (GtkSignalFunc) horizontal_scroll, html); } if (html->html.vsb == NULL){ html->vsba = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); html->html.vsb = gtk_vscrollbar_new (GTK_ADJUSTMENT (html->vsba)); gtk_xmhtml_manage (GTK_CONTAINER(html), GTK_WIDGET (html->html.vsb)); gtk_widget_show (html->html.vsb); gtk_signal_connect (GTK_OBJECT(html->vsba), "value_changed", (GtkSignalFunc) vertical_scroll, html); } } /***** * Name: CreateHTMLWidget * Return Type: void * Description: creates the HTML TWidget * The actual area we use to draw into is a drawingAreaWidget. * In: * html: TWidget to be created. * Returns: * nothing *****/ static void CreateHTMLWidget(XmHTMLWidget html) { int vsb_width, hsb_height; _XmHTMLDebug(1, ("XmHTML.c: CreateHTMLWidget Start\n")); /* * subtract margin_width once to minimize number of calcs in * the paint routines: every thing rendered starts at an x position * of margin_width. */ GetScrollDim(html, &hsb_height, &vsb_width); html->html.work_width = GTK_WIDGET (html)->allocation.width - html->html.margin_width-vsb_width; html->html.work_height= GTK_WIDGET (html)->allocation.height; _XmHTMLDebug(1, ("XmHTML.c: CreateHTMLWidget End\n")); return; } static void gtk_xmhtml_realize (GtkWidget *widget) { GdkWindowAttr attributes; gint attributes_mask; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_XMHTML (widget)); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes.event_mask = gtk_widget_get_events (widget); attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (widget->window, widget); widget->style = gtk_style_attach (widget->style, widget->window); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); } static void gtk_xmhtml_unrealize (GtkWidget *widget) { g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_XMHTML (widget)); GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED | GTK_MAPPED); gtk_style_detach (widget->style); gdk_window_destroy (widget->window); widget->window = NULL; } void gtk_xmhtml_manage (GtkContainer *container, GtkWidget *widget) { GtkXmHTML *html; g_return_if_fail (container != NULL); g_return_if_fail (widget != NULL); html = GTK_XMHTML (container); gtk_widget_set_parent (widget, GTK_WIDGET (container)); if (GTK_WIDGET_REALIZED (html) && !GTK_WIDGET_REALIZED (widget)) gtk_widget_realize (widget); if (GTK_WIDGET_MAPPED (html) && !GTK_WIDGET_MAPPED (widget)) gtk_widget_map (widget); if (GTK_WIDGET_VISIBLE (html) && GTK_WIDGET_VISIBLE (widget)) gtk_widget_queue_resize (GTK_WIDGET (html)); } /* * Add a widget to this container, used by the forms code */ static void gtk_xmhtml_add (GtkContainer *container, GtkWidget *widget) { GtkXmHTML *html = GTK_XMHTML (container); gtk_xmhtml_manage (container, widget); html->children = g_list_append (html->children, widget); } /* * Remove a widget to this container, used by the forms code */ static void gtk_xmhtml_remove (GtkContainer *container, GtkWidget *widget) { GtkXmHTML *html = GTK_XMHTML (container); GList *children; GtkWidget *child; children = html->children; while (children){ child = children->data; if (child == widget){ gtk_widget_unparent (widget); html->children = g_list_remove_link (html->children, children); g_list_free (children); g_free (child); if (GTK_WIDGET_VISIBLE (container)) gtk_widget_queue_resize (GTK_WIDGET (container)); break; } children = children->next; } } static void gtk_xmhtml_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { GtkXmHTML *html; g_return_if_fail (container != NULL); g_return_if_fail (GTK_IS_XMHTML (container)); g_return_if_fail (callback != NULL); html = (GtkXmHTML *) container; if (include_internals) { /* First the scrollbars */ (*callback)(html->html.vsb, callback_data); (*callback)(html->html.hsb, callback_data); /* The drawing area */ (*callback)(html->html.work_area, callback_data); } /* Any childs */ g_list_foreach (html->children, (GFunc) callback, callback_data); } static void gtk_map_item (GtkWidget *w) { if (GTK_WIDGET_VISIBLE (w) && !GTK_WIDGET_MAPPED (w)) gtk_widget_map (w); } static void gtk_xmhtml_map (GtkWidget *widget) { GtkXmHTML *html = GTK_XMHTML (widget); GtkWidget *scrollbar; GList *children; int i; g_return_if_fail (widget != NULL); GTK_WIDGET_SET_FLAGS(widget, GTK_MAPPED); if (GTK_WIDGET_CLASS (parent_class)->map) (*GTK_WIDGET_CLASS (parent_class)->map)(widget); gtk_map_item (html->html.work_area); _XmHTMLDebug(1, ("XmHTML.c: Mapped start\n")); _XmHTMLDebug(1, ("XmHTML.c: Mapped, work area dimensions: %ix%i\n", html->html.work_width, html->html.work_height)); CheckGC (html); scrollbar = html->html.vsb; html->html.work_height = widget->allocation.height; html->html.work_width = widget->allocation.width - (html->html.margin_width + (scrollbar ? scrollbar->allocation.width : 0)); _XmHTMLDebug(1, ("XmHTML.c: Mapped, new work area dimensions: %ix%i\n", html->html.work_width, html->html.work_height)); /* configure the scrollbars, will also resize work_area */ CheckScrollBars (html); gtk_map_item (html->html.vsb); gtk_map_item (html->html.hsb); /* Frames */ for (i = 0; i < html->html.nframes; i++) gtk_map_item (html->html.frames [i]->frame); /* Form widgets */ for (children = html->children; children; children = children->next) gtk_map_item (GTK_WIDGET (children->data)); Layout(html); _XmHTMLDebug(1, ("XmHTML.c: Mapped end.\n")); } void gtk_xmhtml_set_geometry (GtkWidget *widget, int x, int y, int width, int height) { GtkAllocation allocation; allocation.x = x; allocation.y = y; allocation.width = width; allocation.height = height; gtk_widget_size_allocate (widget, &allocation); } /***** * Name: CheckScrollBars * Return Type: void * Description: (re)configures scrollbars * In: * html: HTML TWidget to configure * Returns: * nothing. *****/ static void CheckScrollBars(XmHTMLWidget html) { XmHTMLfont *f = html->html.default_font ? html->html.default_font : NULL; int dx, dy, hsb_height, vsb_width, st, nx, ny, sx, sy; int hsb_on_top, vsb_on_left; /* forced display of scrollbars: XmSTATIC or frames with scrolling = yes */ int force_vsb = FALSE, force_hsb = FALSE; GtkAdjustment *hsba = GTK_ADJUSTMENT (html->hsba); GtkAdjustment *vsba = GTK_ADJUSTMENT (html->vsba); /* don't do a thing if we aren't managed yet */ if (!GTK_WIDGET_MAPPED (html)) return; /* Initial work area offset */ st = dx = dy = 0; sx = Toolkit_Widget_Dim (html).x; sy = Toolkit_Widget_Dim (html).y; GetScrollDim (html, &hsb_height, &vsb_width); /* 1. Vertical scrollbar */ /* check if we need a vertical scrollbar */ if(html->html.formatted_height < Toolkit_Widget_Dim (html).height){ html->html.needs_vsb = False; /* don't forget! */ html->html.scroll_y = 0; gtk_widget_hide (html->html.vsb); } else html->html.needs_vsb = TRUE; /* FIXME: Here: should we support Scrollbar policies, ie, always present, * or only on demand? The XmHTML Motif code supports it. */ /* 2. Horizontal scrollbar */ /* * check if we need a horizontal scrollbar. If we have a vertical * scrollbar, we must add it's width or text might be lost. */ if(html->html.formatted_width < Toolkit_Widget_Dim (html).width - (html->html.needs_vsb ? vsb_width : 0)) { html->html.needs_hsb = False; /* don't forget! */ html->html.scroll_x = 0; gtk_widget_hide (html->html.hsb); } else html->html.needs_hsb = TRUE; /* FIXME: same. Should we support scrollbar policies? */ /* if this is a frame, check what type of scrolling is requested */ if(html->html.is_frame) { if(html->html.scroll_type == FRAME_SCROLL_NONE) { html->html.needs_hsb = False; html->html.needs_vsb = False; html->html.scroll_x = 0; html->html.scroll_y = 0; gtk_widget_hide (html->html.hsb); gtk_widget_hide (html->html.vsb); } else if(html->html.scroll_type == FRAME_SCROLL_YES) { html->html.needs_vsb = TRUE; html->html.needs_hsb = TRUE; force_vsb = TRUE; force_hsb = TRUE; } /* else scrolling is auto, just proceed */ } /* return if we don't need any scrollbars */ if(!html->html.needs_hsb && !html->html.needs_vsb) { _XmHTMLDebug(1, ("XmHTML.c: CheckScrollBars, end, no bars needed.\n")); gtk_xmhtml_set_geometry (html->html.work_area, sx+dx, sy+dy, Toolkit_Widget_Dim (html).width, Toolkit_Widget_Dim (html).height); return; } /* For now: we dont support this */ hsb_on_top = 0; vsb_on_left = 0; /* horizontal sb on top */ if(html->html.needs_hsb && hsb_on_top) dy += hsb_height; /* vertical sb on left */ if(html->html.needs_vsb && vsb_on_left) dx += vsb_width; nx = dx; ny = dy; /* See what space we have to reserve for the scrollbars */ if(html->html.needs_hsb && hsb_on_top == FALSE) dy += hsb_height; if(html->html.needs_vsb && vsb_on_left == FALSE) dx += vsb_width; gtk_xmhtml_set_geometry (html->html.work_area, sx+nx, sy+ny, Toolkit_Widget_Dim (html).width - dx, Toolkit_Widget_Dim (html).height - dy); if(html->html.needs_hsb == TRUE) { int pinc; int sb_width, sb_height; GtkRequisition child_requisition; _XmHTMLDebug(1, ("XmHTML.c: CheckScrollBars, setting hsb\n")); /* Set hsb size; adjust x-position if we have a vsb */ dx = (html->html.needs_vsb ? vsb_width : 0); sb_width = Toolkit_Widget_Dim (html).width - dx - 2*st; gtk_widget_get_child_requisition (html->html.hsb, &child_requisition); sb_height = child_requisition.height; /* pageIncrement == sliderSize */ pinc = html->html.work_width - 2*XmHTML_HORIZONTAL_SCROLL_INCREMENT; /* sanity check */ if(pinc < 1) pinc = XmHTML_HORIZONTAL_SCROLL_INCREMENT; /* adjust horizontal scroll if necessary */ if (html->html.scroll_x > html->html.formatted_width - pinc) html->html.scroll_x = html->html.formatted_width - pinc; /* * Adjust if a horizontal scrollbar has been forced * (can only happen for frames with scrolling = yes) */ if(force_hsb && pinc > html->html.formatted_width) { pinc = html->html.formatted_width; html->html.scroll_x = 0; } hsba->upper = (gfloat) html->html.formatted_width; hsba->value = (gfloat) html->html.scroll_x; hsba->page_size = (gfloat) pinc; hsba->page_increment = (gfloat) pinc; hsba->step_increment = (f ? f->width : XmHTML_HORIZONTAL_SCROLL_INCREMENT); gtk_signal_emit_by_name (GTK_OBJECT (html->hsba), "changed"); /* adjust x-position if vsb is on left */ dx = (html->html.needs_vsb && vsb_on_left ? vsb_width : 0); /* place it */ gtk_xmhtml_set_geometry (html->html.hsb, sx+dx, sy+ (hsb_on_top ? 0 : Toolkit_Widget_Dim(html).height - hsb_height), sb_width, sb_height); gtk_widget_show (html->html.hsb); } if(html->html.needs_vsb == TRUE) { int pinc; int sb_width, sb_height; GtkRequisition child_requisition; _XmHTMLDebug(1, ("XmHTML.c: CheckScrollBars, setting vsb\n")); /* Set vsb size; adjust y-position if we have a hsb */ dy = (html->html.needs_hsb ? hsb_height : 0); gtk_widget_get_child_requisition (html->html.vsb, &child_requisition); sb_width = child_requisition.width; sb_height = Toolkit_Widget_Dim (html).height - dy - 2*st; /* pageIncrement == sliderSize */ pinc = html->html.work_height - 2*(html->html.default_font ? html->html.default_font->height : XmHTML_VERTICAL_SCROLL_INCREMENT); /* sanity check */ if(pinc < 1) pinc = XmHTML_VERTICAL_SCROLL_INCREMENT; /* adjust vertical scroll if necessary */ if(html->html.scroll_y > html->html.formatted_height - pinc) html->html.scroll_y = html->html.formatted_height - pinc; /* * Adjust if a vertical scrollbar has been forced * (can only happen if scrollBarDisplayPolicy == XmSTATIC) */ if(force_vsb && pinc > html->html.formatted_height) { pinc = html->html.formatted_height; html->html.scroll_y = 0; } vsba->upper = (gfloat) html->html.formatted_height; vsba->value = (gfloat) html->html.scroll_y; vsba->page_size = (gfloat) pinc; vsba->page_increment = (gfloat) pinc; vsba->step_increment = (html->html.default_font ? html->html.default_font->height : XmHTML_VERTICAL_SCROLL_INCREMENT); gtk_signal_emit_by_name (GTK_OBJECT (html->vsba), "changed"); /* adjust y-position if hsb is on top */ dy = (html->html.needs_hsb && hsb_on_top ? hsb_height : 0); gtk_xmhtml_set_geometry (html->html.vsb, sx + (vsb_on_left ? 0 : Toolkit_Widget_Dim(html).width - vsb_width), sy, sb_width, sb_height); gtk_widget_show (html->html.vsb); } _XmHTMLDebug(1, ("XmHTML.c: CheckScrollBars, end\n")); } static void XmHTML_Frontend_Redisplay (XmHTMLWidget html) { gtk_widget_draw (GTK_WIDGET (html), NULL); if (GTK_WIDGET_MAPPED (html->html.vsb)) /* update_display html->html.vsb */; if (GTK_WIDGET_MAPPED (html->html.hsb)) /* update_display html->html.hsb */; } /***** * Name: GetScrollDim * Return Type: void * Description: retrieves width & height of the scrollbars * In: * html: XmHTMLWidget for which to retrieve these values * hsb_height: thickness of horizontal scrollbar, filled upon return * vsb_width: thickness of vertical scrollbar, filled upon return * Returns: * nothing *****/ static void GetScrollDim(XmHTMLWidget html, int *hsb_height, int *vsb_width) { int height = 0, width = 0; GtkRequisition child_requisition; if(html->html.hsb) { gtk_widget_get_child_requisition (html->html.hsb, &child_requisition); height = child_requisition.height; } /* * Sanity check if the scrollbar dimensions exceed the TWidget dimensions * Not doing this would lead to X Protocol errors whenever text is set * into the TWidget: the size of the workArea will be less than or equal * to zero when scrollbars are required. * We need always need to do this check since it's possible that some * user has been playing with the dimensions of the scrollbars. */ if(height >= GTK_WIDGET (html)->allocation.height){ _XmHTMLWarning(__WFUNC__(html->html.hsb, "GetScrollDim"), "Height of horizontal scrollbar (%i) exceeds height of parent " "TWidget (%i).\n Reset to 15.", height, GTK_WIDGET(html)->allocation.height); height = 15; /* fprintf (stderr, "Die boy boy die\n"); exit (1); */ } if(html->html.vsb){ gtk_widget_get_child_requisition (html->html.vsb, &child_requisition); width = child_requisition.width; if(width >= GTK_WIDGET (html)->allocation.width){ _XmHTMLWarning(__WFUNC__(html->html.vsb, "GetScrollDim"), "Width of vertical scrollbar (%i) exceeds width of parent " "TWidget (%i).\n Reset to 15.", width, GTK_WIDGET (html)->allocation.width); width = 15; fprintf (stderr, "Die vertical boy boy die!\n"); exit (1); } } _XmHTMLDebug(1, ("XmHTML.c: GetScrollDim; height = %i, width = %i\n", height, width)); *hsb_height = height; *vsb_width = width; } void _XmHTMLCheckXCC(XmHTMLWidget html) { GtkWidget *htmlw = GTK_WIDGET (html); _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLCheckXCC Start\n")); /* * CheckXCC is called each time an image is loaded, so it's quite * usefull if we have a GC around by the time the TWidget is being * mapped to the display. * Our SubstructureNotify event handler can fail in some cases leading to * a situation where we don't have a GC when images are about to be * rendered (especially background images can cause a problem, they * are at the top of the text). */ CheckGC (html); /* * Create an XCC. * XmHTML never decides whether or not to use a private or standard * colormap. A private colormap can be supplied by setting it on the * TWidget's parent, we know how to deal with that. */ if(!html->html.xcc) { TVisual *visual = NULL; TColormap cmap; _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLCheckXCC: creating an XCC\n")); cmap = gtk_widget_get_colormap (htmlw); visual = gtk_widget_get_visual (htmlw); /* walk TWidget tree or get default visual */ if(visual == NULL){ /* visual = XCCGetParentVisual((TWidget)html);*/ fprintf(stderr, "XmHTML.c: _XmHTMLCheckXCC:: should not happen\n"); exit (1); } /* create an xcc for this TWidget */ html->html.xcc = XCCCreate((TWidget)html, visual, cmap); } _XmHTMLDebug(1, ("XmHTML.c: _XmHTMLCheckXCC End\n")); } /***** * Name: autoSizeWidget * Return Type: void * Description: computes XmHTML's TWidget dimensions if we have to autosize * in either direction. * In: * html: XmHTMLWidget id * Returns: * nothing. * Note: * This routine is fairly complicated due to the fact that the dimensions * of the work area are partly determined by the presence of possible * scrollbars. *****/ static void autoSizeWidget (XmHTMLWidget html) { } /* XmImage configuration hook */ XmImageConfig *_xmimage_cfg; /* * Configurability of the XmHTML widget * */ static void gtk_xmhtml_sync_parse (GtkXmHTML *html) { int ov; _XmHTMLKillPLCCycler (html); /* new text has been set, kill of any existing PLC's */ _XmHTMLKillPLCCycler(html); /* release event database */ _XmHTMLFreeEventDatabase(html, html); /* destroy any form data */ _XmHTMLFreeForm(html, html->html.form_data); html->html.form_data = (XmHTMLFormData*)NULL; /* Parse the raw HTML text */ ov = html->html.in_layout; html->html.elements = _XmHTMLparseHTML(html, html->html.elements, html->html.source, html); html->html.in_layout = ov; /* reset topline */ html->html.top_line = 0; /* keep current frame setting and check if new frames are allowed */ html->html.nframes = _XmHTMLCheckForFrames(html, html->html.elements); /* Trigger link callback */ if(CHECK_CALLBACK (html, link_callback, LINK)) _XmHTMLLinkCallback(html); html->reformat_needed = TRUE; html->redraw_needed = TRUE; html->free_images_needed = TRUE; } static void gtk_xmhtml_sync_reformat (GtkXmHTML *html) { int is_frame; /* * Now format the list of parsed objects. * Don't do a bloody thing if we are already in layout as this will * cause unnecessary reloading and screen flickering. */ if (html->html.in_layout) return; /***** * It the current document makes heavy use of images we first need * to clear it. Not doing this would cause a shift in the colors of * the current document (as they are being released) which does not * look nice. Therefore first clear the entire display* area *before* * freeing anything at all. *****/ if(html->html.gc != NULL){ Toolkit_Clear_Area (Toolkit_Display (html->html.work_area), Toolkit_Widget_Window(html->html.work_area), 0, 0, Toolkit_Widget_Dim(html).width, Toolkit_Widget_Dim(html).height, False); } /* destroy any form data */ _XmHTMLFreeForm(html, html->html.form_data); html->html.form_data = (XmHTMLFormData*)NULL; /* Free all non-persistent resources */ FreeExpendableResources(html, html->free_images_needed); /* reset some important vars */ ResetWidget(html, html->free_images_needed); { /* FIXME: is setting the window background semantically equivalent * to 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); } /* get new values for top, bottom & highlight */ _XmHTMLRecomputeColors(html); is_frame = html->html.is_frame; /* go and format the parsed HTML data */ if(!_XmHTMLCreateFrames(html, html)){ html->html.frames = NULL; html->html.nframes = 0; html->html.is_frame = is_frame; } _XmHTMLformatObjects(html, html); /* and check for possible external imagemaps */ _XmHTMLCheckImagemaps(html); /* compute new screen layout */ Layout(html); /* if new text has been set, fire up the PLCCycler */ if(html->parse_needed){ html->html.plc_suspended = False; _XmHTMLPLCCycler((TPointer)html); } html->free_images_needed = 0; html->redraw_needed = 1; html->layout_needed = 0; } static void gtk_xmhtml_sync_redraw (GtkXmHTML *html) { if (html->free_images_needed){ XmHTMLImage *img; for(img = html->html.images; img != NULL; img = img->next) { if(!ImageInfoFreed(img) && ImageInfoDelayedCreation(img->html_image)) { img->options |= IMG_DELAYED_CREATION; html->html.delayed_creation = True; } } if(html->html.delayed_creation) _XmHTMLImageCheckDelayedCreation(html); } if (html->html.gc != NULL) _XmHTMLClearArea (html, 0, 0, Toolkit_Widget_Dim (html).width, Toolkit_Widget_Dim (html).height); gtk_widget_draw (GTK_WIDGET (html), NULL); } static void gtk_xmhtml_sync (GtkXmHTML *html) { if (!html->initialized){ html->initialized = 1; XmHTML_Initialize (html, html, html->html.source); } if (html->parse_needed) gtk_xmhtml_sync_parse (html); if (html->reformat_needed) gtk_xmhtml_sync_reformat (html); if (html->redraw_needed) gtk_xmhtml_sync_redraw (html); /* FIXME: compute the need_tracking variable depending on the following settings: * anchor_track_callback * anchor_cursor * highlight_on_enter * motion_track_callback * focus_callback * losing_focus_callback */ gtk_xmhtml_reset_pending_flags (html); } static void gtk_xmhtml_try_sync (GtkXmHTML *html) { if (html->frozen) return; gtk_xmhtml_sync (html); } void gtk_xmhtml_freeze (GtkXmHTML *html) { html->frozen++; } void gtk_xmhtml_thaw (GtkXmHTML *html) { if (!html->frozen) return; html->frozen--; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_source (GtkXmHTML *html, char *html_source) { int parse = FALSE; /* If we already have some HTML source code */ if (html->html.source){ if (html_source){ /* new text supplied */ if (strcmp (html_source, html->html.source)){ parse = TRUE; free (html->html.source); html->html.source = strdup (html_source); } else parse = FALSE; } else { /* have to clear current text */ parse = TRUE; free (html->html.source); html->html.source = NULL; } } else { /* we did not have any source */ if (html_source){ parse = TRUE; html->html.source = strdup (html_source); } else parse = FALSE; /* still empty */ } html->html.value = html->html.source; html->parse_needed = parse; if (html->initialized) gtk_xmhtml_try_sync (html); } /* XXX: This function does an XQueryColors() the hard way, because there is * no corresponding function in Gdk. */ void my_x_query_colors(GdkColormap *colormap, GdkColor *colors, gint ncolors) { XColor *xcolors; gint i; xcolors = g_new(XColor, ncolors); for (i = 0; i < ncolors; i++) xcolors[i].pixel = colors[i].pixel; XQueryColors(gdk_display, GDK_COLORMAP_XCOLORMAP(colormap), xcolors, ncolors); for (i = 0; i < ncolors; i++) { colors[i].red = xcolors[i].red; colors[i].green = xcolors[i].green; colors[i].blue = xcolors[i].blue; } g_free(xcolors); } void gtk_xmhtml_set_string_direction (GtkXmHTML *html, int direction) { if (html->html.string_direction == direction) return; html->html.string_direction = direction; html->parse_needed = 1; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_alignment (GtkXmHTML *html, int alignment) { if (html->html.enable_outlining) html->html.default_halign = XmHALIGN_JUSTIFY; else { /* default alignment depends on string direction */ if(html->html.string_direction == TSTRING_DIRECTION_R_TO_L) html->html.default_halign = XmHALIGN_RIGHT; else html->html.default_halign = XmHALIGN_LEFT; html->html.alignment = alignment; if(alignment == TALIGNMENT_BEGINNING) html->html.default_halign = XmHALIGN_LEFT; if(alignment == TALIGNMENT_END) html->html.default_halign = XmHALIGN_RIGHT; else if(alignment == TALIGNMENT_CENTER) html->html.default_halign = XmHALIGN_CENTER; } html->parse_needed = 1; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_outline (GtkXmHTML *html, int flag) { html->html.enable_outlining = flag; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } static void gtk_xmhtml_fonts_changed (GtkXmHTML *html) { html->html.default_font = _XmHTMLSelectFontCache (html, TRUE); html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_font_familty (GtkXmHTML *html, char *family, char *sizes) { g_free (html->html.font_family); g_free (html->html.font_sizes); html->html.font_family = g_strdup (family); html->html.font_sizes = g_strdup (sizes); gtk_xmhtml_fonts_changed (html); } void gtk_xmhtml_set_font_familty_fixed (GtkXmHTML *html, char *family, char *sizes) { g_free (html->html.font_family_fixed); g_free (html->html.font_sizes); html->html.font_family_fixed = g_strdup (family); html->html.font_sizes_fixed = g_strdup (sizes); gtk_xmhtml_fonts_changed (html); } void gtk_xmhtml_set_font_charset (GtkXmHTML *html, char *charset) { g_free (html->html.charset); html->html.charset = g_strdup (charset); gtk_xmhtml_fonts_changed (html); } void gtk_xmhtml_set_allow_body_colors (GtkXmHTML *html, int enable) { if (enable == html->html.body_colors_enabled) return; 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_visited_fg = html->html.anchor_visited_fg_save; html->html.anchor_activated_fg = html->html.anchor_activated_fg_save; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_colors (GtkXmHTML *html, Pixel foreground, Pixel background, Pixel anchor_fg, Pixel anchor_target_fg, Pixel anchor_visited_fg, Pixel anchor_activated_fg, Pixel anchor_activated_bg) { /* FIXME: Not so sure about the api, what to do? */ /* FIXME; put the code to update our colors with the new values here */ _XmHTMLRecomputeColors (html); html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_hilight_on_enter (GtkXmHTML *html, int flag) { html->html.armed_anchor = NULL; html->html.highlight_on_enter = flag; } static void gtk_xmhtml_check_underline_type (int underline_type, int *type, int *value) { switch (underline_type){ case GTK_ANCHOR_NOLINE: *value = NO_LINE; break; case GTK_ANCHOR_DASHED_LINE: *value = LINE_DASHED|LINE_UNDER|LINE_SINGLE; break; case GTK_ANCHOR_DOUBLE_LINE: *value = LINE_SOLID|LINE_UNDER|LINE_DOUBLE;; break; case GTK_ANCHOR_DOUBLE_DASHED_LINE: *value = LINE_DASHED|LINE_UNDER|LINE_DOUBLE;; break; case GTK_ANCHOR_SINGLE_LINE: default: *value = LINE_SOLID | LINE_UNDER | LINE_SINGLE; underline_type = GTK_ANCHOR_SINGLE_LINE; break; } *type = underline_type; } void gtk_xmhtml_set_anchor_underline_type (GtkXmHTML *html, int underline_type) { int type, value; gtk_xmhtml_check_underline_type (underline_type, &type, &value); html->html.anchor_underline_type = type; html->html.anchor_line = value; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_anchor_visited_underline_type (GtkXmHTML *html, int underline_type) { int type, value; gtk_xmhtml_check_underline_type (underline_type, &type, &value); html->html.anchor_visited_underline_type = type; html->html.anchor_visited_line = value; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_anchor_target_underline_type (GtkXmHTML *html, int underline_type) { int type, value; gtk_xmhtml_check_underline_type (underline_type, &type, &value); html->html.anchor_target_underline_type = type; html->html.anchor_target_line = value; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_allow_color_switching (GtkXmHTML *html, int flag) { if (html->html.allow_color_switching == flag) return; html->html.allow_color_switching = flag; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_allow_font_switching (GtkXmHTML *html, int flag) { if (html->html.allow_font_switching == flag) return; html->html.allow_font_switching = flag; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } /* See documentation for XmNimageMapToPalette/XmNimageRGBConversion for possible values */ void gtk_xmhtml_set_dithering (GtkXmHTML *html, XmHTMLDitherType flag) { if (html->html.map_to_palette == flag) return; /* from on to off or off to on */ if (html->html.map_to_palette == XmDISABLED || flag == XmDISABLED ){ /* free current stuff */ XCCFree(html->html.xcc); /* and create a new one */ html->html.xcc = NULL; _XmHTMLCheckXCC (html); /* add palette if necessary */ if(flag != XmDISABLED) _XmHTMLAddPalette(html); } else { /* fast & best methods require precomputed error matrices */ if(flag == XmBEST || flag == XmFAST) XCCInitDither(html->html.xcc); else XCCFreeDither(html->html.xcc); } html->html.map_to_palette = flag; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_max_image_colors (GtkXmHTML *html, int max_colors) { int prev_max = html->html.max_image_colors; int new_max = max_colors; int free_images = html->free_images_needed; html->html.max_image_colors = max_colors; CheckMaxColorSetting(html); /* * check if we have any images with more colors than allowed or * we had images that were dithered. If so we need to redo the layout */ if(html->reformat_needed){ XmHTMLImage *image; for(image = html->html.images; image != NULL && !free_images; image = image->next){ /* ImageInfo is still available. Compare against it */ if(!ImageInfoFreed(image)) { /* * redo image composition if any of the following * conditions is True: * - current image has more colors than allowed; * - current image has less colors than allowed but the * original image had more colors than allowed previously. */ if(image->html_image->ncolors > new_max || (image->html_image->scolors < new_max && image->html_image->scolors > prev_max)) html->free_images_needed = True; } /* info no longer available. Check against allocated colors */ else if(image->npixels > new_max) free_images = True; } /* need to redo the layout if we are to redo the images */ html->reformat_needed = free_images; } html->free_images_needed = free_images; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_allow_images (GtkXmHTML *html, int flag) { if (html->html.images_enabled == flag) return; html->html.images_enabled = flag; html->free_images_needed = 1; html->reformat_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_plc_intervals (GtkXmHTML *html, int min_delay, int max_delay, int def_delay) { if ((html->html.plc_min_delay == min_delay) && (html->html.plc_max_delay == max_delay) && (html->html.plc_delay == def_delay)) return; html->html.plc_min_delay = min_delay; html->html.plc_max_delay = max_delay; html->html.plc_def_delay = def_delay; html->html.plc_delay = def_delay; CheckPLCIntervals (html); } void gtk_xmhtml_set_def_body_image_url (GtkXmHTML *html, char *url) { /* FIXME, need to write this routien based on the Motif code */ } void gtk_xmhtml_set_anchor_buttons (GtkXmHTML *html, int flag) { if (html->html.anchor_buttons == flag) return; html->html.anchor_buttons = flag; html->redraw_needed = 1; gtk_xmhtml_try_sync (html); } void gtk_xmhtml_set_anchor_cursor (GtkXmHTML *html, GdkCursor *cursor, int flag) { html->html.anchor_display_cursor = flag; if (!flag){ Toolkit_Free_Cursor (Toolkit_Display(html), html->html.anchor_cursor); html->html.anchor_cursor = None; return; } html->html.anchor_cursor = (TCursor) cursor; } void gtk_xmhtml_set_topline (GtkXmHTML *html, int line) { html->html.top_line = line; ScrollToLine (html, line); html->redraw_needed = 1; gtk_xmhtml_try_sync (html); } int gtk_xmhtml_get_topline (GtkXmHTML *html) { return html->html.top_line; } /* FIXME: should we support setting the scroll repeat value? Motif code does it */ void gtk_xmhtml_set_freeze_animations (GtkXmHTML *html, int flag) { if (html->html.freeze_animations == flag) return; _XmHTMLRestartAnimations (html); } void gtk_xmhtml_set_screen_gamma (GtkXmHTML *html, float screen_gamma) { html->html.screen_gamma = screen_gamma; } /* Use for progressive image loading */ void gtk_xmhtml_set_image_procs (GtkXmHTML *html, XmImageProc image_proc, XmImageGifProc gif_proc, XmHTMLGetDataProc get_data, XmHTMLEndDataProc end_data) { html->html.image_proc = image_proc; html->html.gif_proc = gif_proc; html->html.get_data = get_data; html->html.end_data = end_data; } void gtk_xmhtml_set_event_proc (GtkXmHTML *html, XmHTMLEventProc event_proc) { html->html.event_proc = event_proc; } void gtk_xmhtml_set_perfect_colors (GtkXmHTML *html, int flag) { html->html.perfect_colors = flag; } void gtk_xmhtml_set_uncompress_command (GtkXmHTML *html, char *cmd) { g_free (html->html.zCmd); html->html.zCmd = g_strdup (cmd); } void gtk_xmhtml_set_strict_checking (GtkXmHTML *html, int flag) { html->html.strict_checking = flag; } void gtk_xmhtml_set_bad_html_warnings (GtkXmHTML *html, int flag) { html->html.bad_html_warnings = flag; } void gtk_xmhtml_set_allow_form_coloring (GtkXmHTML *html, int flag) { html->html.allow_form_coloring = flag; } void gtk_xmhtml_set_imagemap_draw (GtkXmHTML *html, int flag) { html->html.imagemap_draw = flag; } void gtk_xmhtml_set_mime_type (GtkXmHTML *html, char *mime_type) { g_free (html->html.mime_type); html->html.mime_type = g_strdup (mime_type); } void gtk_xmhtml_set_alpha_processing (GtkXmHTML *html, int flag) { html->html.alpha_processing = flag; } void gtk_xmhtml_set_rgb_conv_mode (GtkXmHTML *html, int val) { html->html.rgb_conv_mode = val; } /* These two functions can be removed when XmHTML is converted to use * 16-bit color information. The corresponding XCCGetPixels() and * XCCGetPixelsIncremental() macros in toolkit.h should be changed to * use the un-wrapped Gdk functions. */ #define UPSCALE(c) (((c) << 8) | (c)) void wrap_gdk_cc_get_pixels (int incremental, GdkColorContext *cc, gushort *reds, gushort *greens, gushort *blues, gint ncolors, gint *used, gulong *colors, gint *nallocated) { gushort *wreds, *wgreens, *wblues; int i; wreds = g_new (gushort, ncolors); wgreens = g_new (gushort, ncolors); wblues = g_new (gushort, ncolors); for (i = 0; i < ncolors; i++) { wreds[i] = UPSCALE (reds[i]); wgreens[i] = UPSCALE (greens[i]); wblues[i] = UPSCALE (blues[i]); } if (incremental) gdk_color_context_get_pixels_incremental (cc, wreds, wgreens, wblues, ncolors, used, colors, nallocated); else gdk_color_context_get_pixels (cc, wreds, wgreens, wblues, ncolors, colors, nallocated); g_free (wreds); g_free (wgreens); g_free (wblues); } #ifndef HAVE_GTK_SIGNAL_HANDLER_PENDING guint gtk_signal_handler_pending (GtkObject *object, gint signal_type, gboolean may_be_blocked) { static int warning_printed; /* On old Gtks, we return 1 and force all of the signals to do the full processing */ if (!warning_printed){ warning_printed = 1; fprintf (stderr, "GtkXmHTML widget running in slow mode due to old Gtk version\n"); } return 1; } #endif void Toolkit_Draw_Shadows(XmHTMLWidget html, TGC top_shadow, TGC bottom_shadow, gint xs, gint ys, gint w, gint h, gint shadow_thick, gint shadow_type) { switch (shadow_type) { case XmSHADOW_IN: gtk_draw_shadow(GTK_WIDGET(html)->style, GTK_WIDGET(html)->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, xs, ys, w, h); break; case XmSHADOW_OUT: gtk_draw_shadow(GTK_WIDGET(html)->style, GTK_WIDGET(html)->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, xs, ys, w, h); break; default: break; ; } } void _XmHTMLRecomputeShadowColors(XmHTMLWidget html, Pixel base) { /* * We can only compute the colors when we have a GC. If we don't * have a GC, the widget is not yet realized. Use managers defaults * then. */ #if 0 if(html->html.gc != NULL) { Pixel top = None, bottom = None; Arg args[2]; XmGetColors(XtScreen((Widget)html), html->core.colormap, base, NULL, &top, &bottom, NULL); XtSetArg(args[0], XmNtopShadowColor, top); XtSetArg(args[1], XmNbottomShadowColor, bottom); XtSetValues((Widget)html, args, 2); } #endif }