#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
(PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
+
+
\f
/***********************************************************************
Utility functions
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.
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);
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.
SET_FRAME_GARBAGED (f);
UNBLOCK_INPUT;
+
+ return 1;
}
/* Get rid of the menu bar of frame F, and free its storage.
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
/* 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;
/* 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. */
{
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;
}
g_signal_connect (G_OBJECT (wscroll),
"button-press-event",
G_CALLBACK (scroll_bar_button_cb),
- 0);
+ (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);
}
}
+/* 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.
{
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);
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
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;
}
}
if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
- return;
+ return FALSE;
if (event->type == GDK_ENTER_NOTIFY)
{
xg_initialize ()
{
xg_ignore_gtk_scrollbar = 0;
- xg_ignore_next_thumb = 0;
xg_left_ptr_cursor = 0;
xg_did_tearoff = 0;