X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/26b74a0bd022bc62c7babf7d12e2fd001a0f31f0..f9e65eb300c487a85de743edc0bafd6434d6db5e:/src/gtkutil.c diff --git a/src/gtkutil.c b/src/gtkutil.c index bae2196965..61e7974935 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -35,6 +35,8 @@ Boston, MA 02111-1307, USA. */ #define FRAME_TOTAL_PIXEL_HEIGHT(f) \ (PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)) + + /*********************************************************************** Utility functions @@ -285,6 +287,24 @@ xg_resize_outer_widget (f, columns, rows) gdk_window_process_all_updates (); } +/* This gets called after the frame F has been cleared. Since that is + done with X calls, we need to redraw GTK widget (scroll bars). */ +void +xg_frame_cleared (f) + FRAME_PTR f; +{ + GtkWidget *w = f->output_data.x->widget; + + if (w) + { + gtk_container_set_reallocate_redraws (GTK_CONTAINER (w), TRUE); + gtk_container_foreach (GTK_CONTAINER (w), + (GtkCallback) gtk_widget_queue_draw, + 0); + gdk_window_process_all_updates (); + } +} + /* Function to handle resize of our widgets. Since Emacs has some layouts that does not fit well with GTK standard containers, we do most layout manually. @@ -314,7 +334,8 @@ xg_resize_widgets (f, pixelwidth, pixelheight) all.height = pixelheight - mbheight - tbheight; gtk_widget_size_allocate (x->edit_widget, &all); - gdk_window_process_all_updates (); + + xg_frame_cleared (f); change_frame_size (f, rows, columns, 0, 1, 0); SET_FRAME_GARBAGED (f); @@ -345,7 +366,7 @@ xg_frame_set_char_size (f, cols, rows) : (FRAME_SCROLL_BAR_COLS (f) * FONT_WIDTH (f->output_data.x->font))); - x_compute_fringe_widths (f, 0); + compute_fringe_widths (f, 0); /* Must resize our top level widget. Font size may have changed, but not rows/cols. */ @@ -1344,6 +1365,10 @@ xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group) return w; } +static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback, + GCallback, GCallback, int, int, int, + GtkWidget *, xg_menu_cb_data *, char *)); + /* Create a full menu tree specified by DATA. F is the frame the created menu belongs to. SELECT_CB is the callback to use when a menu item is selected. @@ -2180,6 +2205,8 @@ xg_update_frame_menubar (f) SET_FRAME_GARBAGED (f); UNBLOCK_INPUT; + + return 1; } /* Get rid of the menu bar of frame F, and free its storage. @@ -2221,11 +2248,6 @@ free_frame_menubar (f) to indicate that callback should do nothing. */ int xg_ignore_gtk_scrollbar; -/* After we send a scroll bar event, x_set_toolkit_scroll_bar_thumb will - be called. For some reason that needs to be debugged, it gets called - with bad values. Thus, we set this variable to ignore those calls. */ -int xg_ignore_next_thumb; - /* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in 32 bits. But we want to store pointers, and they may be larger than 32 bits. Keep a mapping from integer index to widget pointers @@ -2301,9 +2323,30 @@ xg_get_widget_from_map (idx) return 0; } +/* Return the scrollbar id for X Window WID. + Return -1 if WID not in id_to_widget. */ +int +xg_get_scroll_id_for_window (wid) + Window wid; +{ + int idx; + GtkWidget *w; + + w = xg_win_to_widget (wid); + + if (w) + { + for (idx = 0; idx < id_to_widget.max_size; ++idx) + if (id_to_widget.widgets[idx] == w) + return idx; + } + + return -1; +} + /* Callback invoked when scroll bar WIDGET is destroyed. DATA is the index into id_to_widget for WIDGET. - We free pointer to last scroll bar value here and remove the index. */ + We free pointer to last scroll bar values here and remove the index. */ static void xg_gtk_scroll_destroy (widget, data) GtkWidget *widget; @@ -2319,9 +2362,10 @@ xg_gtk_scroll_destroy (widget, data) /* Callback for button press/release events. Used to start timer so that the scroll bar repetition timer in GTK gets handeled. + Also, sets bar->dragging to Qnil when dragging (button release) is done. WIDGET is the scroll bar widget the event is for (not used). EVENT contains the event. - USER_DATA is 0 (not used). + USER_DATA points to the struct scrollbar structure. Returns FALSE to tell GTK that it shall continue propagate the event to widgets. */ @@ -2333,9 +2377,13 @@ scroll_bar_button_cb (widget, event, user_data) { if (event->type == GDK_BUTTON_PRESS && ! xg_timer) xg_start_timer (); - else if (event->type == GDK_BUTTON_RELEASE && xg_timer) - xg_stop_timer (); - + else if (event->type == GDK_BUTTON_RELEASE) + { + struct scroll_bar *bar = (struct scroll_bar *) user_data; + if (xg_timer) xg_stop_timer (); + bar->dragging = Qnil; + } + return FALSE; } @@ -2381,14 +2429,14 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name) g_signal_connect (G_OBJECT (wscroll), "button-press-event", G_CALLBACK (scroll_bar_button_cb), - (gpointer)1); + (gpointer)bar); g_signal_connect (G_OBJECT (wscroll), "button-release-event", G_CALLBACK (scroll_bar_button_cb), - 0); + (gpointer)bar); gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), - wscroll, 0, 0); + wscroll, -1, -1); /* Set the cursor to an arrow. */ xg_set_cursor (wscroll, &xg_left_ptr_cursor); @@ -2420,6 +2468,29 @@ xg_remove_scroll_bar (f, scrollbar_id) } } +/* Find left/top for widget W in GtkFixed widget WFIXED. */ +static void +xg_find_top_left_in_fixed (w, wfixed, left, top) + GtkWidget *w, *wfixed; + int *left, *top; +{ + GList *iter; + + for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter)) + { + GtkFixedChild *child = (GtkFixedChild *) iter->data; + + if (child->widget == w) + { + *left = child->x; + *top = child->y; + return; + } + } + + /* Shall never end up here. */ + abort (); +} /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID in frame F. @@ -2436,28 +2507,101 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height) { GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id); - + if (wscroll) { + GtkWidget *wfixed = f->output_data.x->edit_widget; int gheight = max (height, 1); + int canon_width = FRAME_SCROLL_BAR_COLS (f) * CANON_X_UNIT (f); + int winextra = canon_width > width ? (canon_width - width) / 2 : 0; + int bottom = top + gheight; - gtk_fixed_move (GTK_FIXED (f->output_data.x->edit_widget), - wscroll, left, top); + gint slider_width; + int oldtop, oldleft, oldbottom; + GtkRequisition req; + + /* Get old values. */ + xg_find_top_left_in_fixed (wscroll, wfixed, &oldleft, &oldtop); + gtk_widget_size_request (wscroll, &req); + oldbottom = oldtop + req.height; + + /* Scroll bars in GTK has a fixed width, so if we say width 16, it + will only be its fixed width (14 is default) anyway, the rest is + blank. We are drawing the mode line across scroll bars when + the frame is split: + |bar| |fringe| + ---------------- + mode line + ---------------- + |bar| |fringe| + + When we "unsplit" the frame: + + |bar| |fringe| + -| |-| | + m¦ |i| | + -| |-| | + | | | | + + + the remains of the mode line can be seen in these blank spaces. + So we must clear them explicitly. + GTK scroll bars should do that, but they don't. + Also, the scroll bar canonical width may be wider than the width + passed in here. */ + + if (oldtop != -1 && oldleft != -1) + { + int gtkextra; + int xl, xr, wblank; + int bottomdiff, topdiff; + + gtk_widget_style_get (wscroll, "slider_width", &slider_width, NULL); + gtkextra = width > slider_width ? (width - slider_width) / 2 : 0; + + xl = left - winextra; + wblank = gtkextra + winextra; + xr = left + gtkextra + slider_width; + bottomdiff = abs (oldbottom - bottom); + topdiff = abs (oldtop - top); + + if (oldtop > top) + { + gdk_window_clear_area (wfixed->window, xl, top, wblank, topdiff); + gdk_window_clear_area (wfixed->window, xr, top, wblank, topdiff); + } + else if (oldtop < top) + { + gdk_window_clear_area (wfixed->window, xl, oldtop, wblank, + topdiff); + gdk_window_clear_area (wfixed->window, xr, oldtop, wblank, + topdiff); + } - gtk_widget_set_size_request (wscroll, width, gheight); + if (oldbottom > bottom) + { + gdk_window_clear_area (wfixed->window, xl, bottom, wblank, + bottomdiff); + gdk_window_clear_area (wfixed->window, xr, bottom, wblank, + bottomdiff); + } + else if (oldbottom < bottom) + { + gdk_window_clear_area (wfixed->window, xl, oldbottom, wblank, + bottomdiff); + gdk_window_clear_area (wfixed->window, xr, oldbottom, wblank, + bottomdiff); + } + } - /* Must force out update so wscroll gets the resize. - Otherwise, the gdk_window_clear clears the old window size. */ - gdk_window_process_all_updates (); + /* Move and resize to new values. */ + gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top); + gtk_widget_set_size_request (wscroll, width, gheight); - /* The scroll bar doesn't explicitly redraw the whole window - when a resize occurs. Since the scroll bar seems to be fixed - in width it doesn't fill the space reserved, so we must clear - the whole window. */ - gdk_window_clear (wscroll->window); + gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE); - /* Since we are not using a pure gtk event loop, we must force out - pending update events with this call. */ + /* Make GTK draw the new sizes. We are not using a pure GTK event + loop so we need to do this. */ gdk_window_process_all_updates (); SET_FRAME_GARBAGED (f); @@ -2477,15 +2621,25 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole) FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); BLOCK_INPUT; - if (wscroll && ! xg_ignore_next_thumb) + + if (wscroll && NILP (bar->dragging)) { GtkAdjustment *adj; gdouble shown; gdouble top; int size, value; + int new_upper, new_step; adj = gtk_range_get_adjustment (GTK_RANGE (wscroll)); + /* We do the same as for MOTIF in xterm.c, assume 30 chars per line + rather than the real portion value. This makes the thumb less likely + to resize and that looks better. */ + portion = XFASTINT (XWINDOW (bar->window)->height) * 30; + /* When the thumb is at the bottom, position == whole. + So we need to increase `whole' to make space for the thumb. */ + whole += portion; + if (whole <= 0) top = 0, shown = 1; else @@ -2502,26 +2656,40 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole) value = min (value, whole - size); value = max (value, XG_SB_MIN); - adj->upper = max (whole, size); - adj->page_size = (int)size; - - /* Assume a page increment is about 95% of the page size */ - adj->page_increment = (int) (0.95*adj->page_size); - - /* Assume all lines are equal. */ - adj->step_increment = portion / max (1, FRAME_HEIGHT (f)); - /* gtk_range_set_value invokes the callback. Set ignore_gtk_scrollbar to make the callback do nothing */ xg_ignore_gtk_scrollbar = 1; - gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value); + + new_upper = max (whole, size); + new_step = portion / max (1, FRAME_HEIGHT (f)); + + if ((int) adj->page_size != size + || (int) adj->upper != new_upper + || (int) adj->step_increment != new_step) + { + adj->page_size = (int) size; + + gtk_range_set_range (GTK_RANGE (wscroll), adj->lower, + (gdouble) new_upper); + + /* Assume all lines are of equal size. */ + /* Assume a page increment is about 95% of the page size */ + gtk_range_set_increments (GTK_RANGE (wscroll), + portion / max (1, FRAME_HEIGHT (f)), + (int) (0.95*adj->page_size)); + + } + + if ((int) gtk_range_get_value (GTK_RANGE (wscroll)) != value) + gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value); + xg_ignore_gtk_scrollbar = 0; + + /* Make GTK draw the new thumb. We are not using a pure GTK event + loop so we need to do this. */ + gdk_window_process_all_updates (); } - /* Make sure the scroll bar is redrawn with new thumb */ - gtk_widget_queue_draw (wscroll); - gdk_window_process_all_updates (); - xg_ignore_next_thumb = 0; UNBLOCK_INPUT; } @@ -2646,7 +2814,7 @@ xg_tool_bar_help_callback (w, event, client_data) } if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items)) - return; + return FALSE; if (event->type == GDK_ENTER_NOTIFY) { @@ -2676,7 +2844,7 @@ xg_tool_bar_help_callback (w, event, client_data) Returns FALSE to tell GTK to keep processing this event. */ static gboolean -xg_tool_bar_expose_callback (w, event, client_data) +xg_tool_bar_item_expose_callback (w, event, client_data) GtkWidget *w; GdkEventExpose *event; gpointer client_data; @@ -2686,6 +2854,24 @@ xg_tool_bar_expose_callback (w, event, client_data) return FALSE; } +/* This callback is called when a tool bar shall be redrawn. + We need to update the tool bar from here in case the image cache + has deleted the pixmaps used in the tool bar. + W is the GtkToolbar to be redrawn. + EVENT is the expose event for W. + CLIENT_DATA is pointing to the frame for this tool bar. + + Returns FALSE to tell GTK to keep processing this event. */ +static gboolean +xg_tool_bar_expose_callback (w, event, client_data) + GtkWidget *w; + GdkEventExpose *event; + gpointer client_data; +{ + update_frame_tool_bar((FRAME_PTR)client_data); + return FALSE; +} + static void xg_create_tool_bar (f) FRAME_PTR f; @@ -2722,6 +2908,10 @@ xg_create_tool_bar (f) G_CALLBACK (xg_tool_bar_detach_callback), f); g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached", G_CALLBACK (xg_tool_bar_attach_callback), f); + g_signal_connect (G_OBJECT (x->toolbar_widget), + "expose-event", + G_CALLBACK (xg_tool_bar_expose_callback), + f); gtk_widget_show_all (x->handlebox_widget); @@ -2828,10 +3018,10 @@ update_frame_tool_bar (f) (gpointer)img->pixmap); /* Catch expose events to overcome an annoying redraw bug, see - comment for xg_tool_bar_expose_callback. */ + comment for xg_tool_bar_item_expose_callback. */ g_signal_connect (G_OBJECT (w), "expose-event", - G_CALLBACK (xg_tool_bar_expose_callback), + G_CALLBACK (xg_tool_bar_item_expose_callback), 0); /* We must set sensitive on the button that is the parent @@ -2947,7 +3137,6 @@ void xg_initialize () { xg_ignore_gtk_scrollbar = 0; - xg_ignore_next_thumb = 0; xg_left_ptr_cursor = 0; xg_did_tearoff = 0;