1 /* scrollbar implementation -- GTK interface.
2 Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
3 Copyright (C) 1994 Amdhal Corporation.
4 Copyright (C) 1995 Sun Microsystems, Inc.
5 Copyright (C) 1995 Darrell Kindred <dkindred+@cmu.edu>.
7 This file is part of XEmacs.
9 XEmacs is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by the
11 Free Software Foundation; either version 2, or (at your option) any
14 XEmacs is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License
20 along with XEmacs; see the file COPYING. If not, write to
21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA. */
24 /* Synched up with: Not in FSF. */
25 /* Gtk version by William M. Perry */
30 #include "console-gtk.h"
31 #include "glyphs-gtk.h"
33 #include "scrollbar-gtk.h"
38 static gboolean scrollbar_cb (GtkAdjustment *adj, gpointer user_data);
40 /* Used to prevent changing the size of the slider while drag
41 scrolling, under Motif. This is necessary because the Motif
42 scrollbar is incredibly stupid about updating the slider and causes
43 lots of flicker if it is done too often. */
44 static int inhibit_slider_size_change;
46 static int vertical_drag_in_progress;
49 /* A device method. */
51 gtk_inhibit_scrollbar_slider_size_change (void)
53 return inhibit_slider_size_change;
56 /* A device method. */
58 gtk_free_scrollbar_instance (struct scrollbar_instance *instance)
60 if (instance->scrollbar_data)
62 if (SCROLLBAR_GTK_WIDGET (instance))
64 gtk_widget_hide_all (SCROLLBAR_GTK_WIDGET (instance));
65 gtk_widget_destroy (SCROLLBAR_GTK_WIDGET (instance));
68 xfree (instance->scrollbar_data);
72 /* A device method. */
74 gtk_release_scrollbar_instance (struct scrollbar_instance *instance)
76 /* It won't hurt to hide it all the time, will it? */
77 gtk_widget_hide_all (SCROLLBAR_GTK_WIDGET (instance));
81 scrollbar_drag_hack_cb (GtkWidget *w, GdkEventButton *ev, gpointer v)
83 vertical_drag_in_progress = (int) v;
84 inhibit_slider_size_change = (int) v;
88 /* A device method. */
90 gtk_create_scrollbar_instance (struct frame *f, int vertical,
91 struct scrollbar_instance *instance)
93 GtkAdjustment *adj = GTK_ADJUSTMENT (gtk_adjustment_new (0,0,0,0,0,0));
94 GtkScrollbar *sb = NULL;
96 /* initialize the X specific data section. */
97 instance->scrollbar_data = xnew_and_zero (struct gtk_scrollbar_data);
99 SCROLLBAR_GTK_ID (instance) = new_gui_id ();
100 SCROLLBAR_GTK_VDRAG_ORIG_VALUE (instance) = -1;
101 SCROLLBAR_GTK_LAST_VALUE (instance) = adj->value;
103 gtk_object_set_data (GTK_OBJECT (adj), GTK_DATA_GUI_IDENTIFIER,
104 (void *) SCROLLBAR_GTK_ID (instance));
105 gtk_object_set_data (GTK_OBJECT (adj), GTK_DATA_FRAME_IDENTIFIER, f);
106 gtk_object_set_data (GTK_OBJECT (adj), "xemacs::sb_instance", instance);
108 sb = GTK_SCROLLBAR (vertical ? gtk_vscrollbar_new (adj) : gtk_hscrollbar_new (adj));
109 /* With gtk version > 1.2.8 the gtk code does not call
110 gtk_widget_request_size() on the newly created scrollbars
111 anymore (catering to theme engines).
112 #### Maybe it is better to postpone this call to just before
113 gtk_widget_show() is called on the scrollbar? */
114 #if GTK_MAJOR_VERSION == 1 && GTK_MINOR_VERSION == 2 && GTK_BINARY_AGE > 8
115 gtk_widget_size_request(GTK_WIDGET(sb), &(GTK_WIDGET(sb)->requisition));
117 SCROLLBAR_GTK_WIDGET (instance) = GTK_WIDGET (sb);
119 gtk_signal_connect (GTK_OBJECT (adj),"value-changed",
120 GTK_SIGNAL_FUNC (scrollbar_cb), (gpointer) vertical);
122 gtk_signal_connect (GTK_OBJECT (sb), "button-press-event",
123 GTK_SIGNAL_FUNC (scrollbar_drag_hack_cb), (gpointer) 1);
124 gtk_signal_connect (GTK_OBJECT (sb), "button-release-event",
125 GTK_SIGNAL_FUNC (scrollbar_drag_hack_cb), (gpointer) 0);
127 gtk_fixed_put (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)), SCROLLBAR_GTK_WIDGET (instance), 0, 0);
128 gtk_widget_hide (SCROLLBAR_GTK_WIDGET (instance));
131 #define UPDATE_DATA_FIELD(field) \
132 if (new_##field >= 0 && \
133 SCROLLBAR_GTK_POS_DATA (inst).field != new_##field) { \
134 SCROLLBAR_GTK_POS_DATA (inst).field = new_##field; \
135 inst->scrollbar_instance_changed = 1; \
138 /* A device method. */
139 /* #### The -1 check is such a hack. */
141 gtk_update_scrollbar_instance_values (struct window *w,
142 struct scrollbar_instance *inst,
143 int new_line_increment,
144 int new_page_increment,
145 int new_minimum, int new_maximum,
147 int new_slider_position,
148 int new_scrollbar_width,
149 int new_scrollbar_height,
150 int new_scrollbar_x, int new_scrollbar_y)
152 UPDATE_DATA_FIELD (line_increment);
153 UPDATE_DATA_FIELD (page_increment);
154 UPDATE_DATA_FIELD (minimum);
155 UPDATE_DATA_FIELD (maximum);
156 UPDATE_DATA_FIELD (slider_size);
157 UPDATE_DATA_FIELD (slider_position);
158 UPDATE_DATA_FIELD (scrollbar_width);
159 UPDATE_DATA_FIELD (scrollbar_height);
160 UPDATE_DATA_FIELD (scrollbar_x);
161 UPDATE_DATA_FIELD (scrollbar_y);
163 if (w && !vertical_drag_in_progress)
165 int new_vov = SCROLLBAR_GTK_POS_DATA (inst).slider_position;
166 int new_vows = marker_position (w->start[CURRENT_DISP]);
168 if (SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) != new_vov)
170 SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) = new_vov;
171 inst->scrollbar_instance_changed = 1;
173 if (SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) != new_vows)
175 SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) = new_vows;
176 inst->scrollbar_instance_changed = 1;
181 /* Used by gtk_update_scrollbar_instance_status. */
183 update_one_widget_scrollbar_pointer (struct window *w, GtkWidget *wid)
186 gtk_widget_realize (wid);
188 if (POINTER_IMAGE_INSTANCEP (w->scrollbar_pointer))
190 gdk_window_set_cursor (GET_GTK_WIDGET_WINDOW (wid),
191 XIMAGE_INSTANCE_GTK_CURSOR (w->scrollbar_pointer));
196 /* A device method. */
198 gtk_update_scrollbar_instance_status (struct window *w, int active, int size,
199 struct scrollbar_instance *instance)
201 struct frame *f = XFRAME (w->frame);
202 GtkWidget *wid = SCROLLBAR_GTK_WIDGET (instance);
203 gboolean managed = GTK_WIDGET_MAPPED (wid);
207 if (instance->scrollbar_instance_changed)
209 /* Need to set the height, width, and position of the widget */
210 GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (wid));
211 scrollbar_values *pos_data = & SCROLLBAR_GTK_POS_DATA (instance);
214 /* We do not want to update the size all the time if we can
215 help it. This cuts down on annoying flicker.
217 if ((wid->allocation.width != pos_data->scrollbar_width) ||
218 (wid->allocation.height != pos_data->scrollbar_height))
220 gtk_widget_set_usize (wid,
221 pos_data->scrollbar_width,
222 pos_data->scrollbar_height);
225 UGLY! UGLY! UGLY! Changes to wid->allocation are queued and
226 not performed until the GTK event loop. However, when the
227 fontlock progress bar is run, the vertical scrollbar's height
228 is change and then changed back before events are again
229 processed. This means that the change back is not seen and
230 the scrollbar is left too short. Fix this by making the
231 change manually so the test above sees the change. This does
232 not seem to cause problems in other cases.
235 wid->allocation.width = pos_data->scrollbar_width;
236 wid->allocation.height = pos_data->scrollbar_height;
241 /* Ditto for the x/y position. */
242 if ((wid->allocation.x != pos_data->scrollbar_x) ||
243 (wid->allocation.y != pos_data->scrollbar_y))
245 gtk_fixed_move (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
247 pos_data->scrollbar_x,
248 pos_data->scrollbar_y);
251 UGLY! UGLY! UGLY! Changes to wid->allocation are queued and
252 not performed until the GTK event loop. However, when the
253 fontlock progress bar is run, the horizontal scrollbar's
254 position is change and then changed back before events are
255 again processed. This means that the change back is not seen
256 and the scrollbar is left in the wrong position. Fix this by
257 making the change manually so the test above sees the change.
258 This does not seem to cause problems in other cases.
261 wid->allocation.x = pos_data->scrollbar_x;
262 wid->allocation.y = pos_data->scrollbar_y;
267 adj->lower = pos_data->minimum;
268 adj->upper = pos_data->maximum;
269 adj->page_increment = pos_data->slider_size + 1;
270 adj->step_increment = w->max_line_len - 1;
271 adj->page_size = pos_data->slider_size + 1;
272 adj->value = pos_data->slider_position;
274 /* But, if we didn't resize or move the scrollbar, the
275 widget will not get redrawn correctly when the user
276 scrolls around in the XEmacs frame manually. So we
277 update the slider manually here.
280 gtk_range_slider_update (GTK_RANGE (wid));
282 instance->scrollbar_instance_changed = 0;
287 gtk_widget_show (wid);
288 update_one_widget_scrollbar_pointer (w, wid);
293 gtk_widget_hide (wid);
297 enum gtk_scrollbar_loop
299 GTK_FIND_SCROLLBAR_WINDOW_MIRROR,
300 GTK_SET_SCROLLBAR_POINTER,
301 GTK_WINDOW_IS_SCROLLBAR,
302 GTK_UPDATE_FRAME_SCROLLBARS
305 static struct window_mirror *
306 gtk_scrollbar_loop (enum gtk_scrollbar_loop type, Lisp_Object window,
307 struct window_mirror *mir,
308 GUI_ID id, GdkWindow *x_win)
310 struct window_mirror *retval = NULL;
314 struct scrollbar_instance *vinstance = mir->scrollbar_vertical_instance;
315 struct scrollbar_instance *hinstance = mir->scrollbar_horizontal_instance;
316 struct window *w = XWINDOW (window);
319 retval = gtk_scrollbar_loop (type, w->vchild, mir->vchild, id, x_win);
320 else if (mir->hchild)
321 retval = gtk_scrollbar_loop (type, w->hchild, mir->hchild, id, x_win);
325 if (hinstance || vinstance)
329 case GTK_FIND_SCROLLBAR_WINDOW_MIRROR:
330 if ((vinstance && SCROLLBAR_GTK_ID (vinstance) == id) ||
331 (hinstance && SCROLLBAR_GTK_ID (hinstance) == id))
334 case GTK_UPDATE_FRAME_SCROLLBARS:
335 if (!mir->vchild && !mir->hchild)
336 update_window_scrollbars (w, mir, 1, 0);
338 case GTK_SET_SCROLLBAR_POINTER:
339 if (!mir->vchild && !mir->hchild)
343 widget = SCROLLBAR_GTK_WIDGET (hinstance);
344 if (widget && GTK_WIDGET_MAPPED (widget))
345 update_one_widget_scrollbar_pointer (w, widget);
347 widget = SCROLLBAR_GTK_WIDGET (vinstance);
348 if (widget && GTK_WIDGET_MAPPED (widget))
349 update_one_widget_scrollbar_pointer (w, widget);
352 case GTK_WINDOW_IS_SCROLLBAR:
353 if (!mir->vchild && !mir->hchild)
357 widget = SCROLLBAR_GTK_WIDGET (hinstance);
358 if (widget && GTK_WIDGET_MAPPED (widget) &&
359 GET_GTK_WIDGET_WINDOW (widget) == x_win)
360 return (struct window_mirror *) 1;
362 widget = SCROLLBAR_GTK_WIDGET (vinstance);
363 if (widget && GTK_WIDGET_MAPPED (widget) &&
364 GET_GTK_WIDGET_WINDOW (widget) == x_win)
365 return (struct window_mirror *) 1;
380 /* Used by callbacks. */
381 static struct window_mirror *
382 find_scrollbar_window_mirror (struct frame *f, GUI_ID id)
385 update_frame_window_mirror (f);
386 return gtk_scrollbar_loop (GTK_FIND_SCROLLBAR_WINDOW_MIRROR, f->root_window,
387 f->root_mirror, id, (GdkWindow *) NULL);
391 scrollbar_cb (GtkAdjustment *adj, gpointer user_data)
393 /* This function can GC */
394 int vertical = (int) user_data;
395 struct frame *f = gtk_object_get_data (GTK_OBJECT (adj), GTK_DATA_FRAME_IDENTIFIER);
396 struct scrollbar_instance *instance = gtk_object_get_data (GTK_OBJECT (adj), "xemacs::sb_instance");
397 GUI_ID id = (GUI_ID) gtk_object_get_data (GTK_OBJECT (adj), GTK_DATA_GUI_IDENTIFIER);
398 Lisp_Object win, frame;
399 struct window_mirror *mirror;
400 Lisp_Object event_type = Qnil;
401 Lisp_Object event_data = Qnil;
406 mirror = find_scrollbar_window_mirror (f, id);
410 win = real_window (mirror, 1);
414 instance = vertical ? mirror->scrollbar_vertical_instance : mirror->scrollbar_horizontal_instance;
415 frame = WINDOW_FRAME (XWINDOW (win));
417 inhibit_slider_size_change = 0;
418 switch (GTK_RANGE (SCROLLBAR_GTK_WIDGET (instance))->scroll_type)
420 case GTK_SCROLL_PAGE_BACKWARD:
421 event_type = vertical ? Qscrollbar_page_up : Qscrollbar_page_left;
422 event_data = Fcons (win, Qnil);
424 case GTK_SCROLL_PAGE_FORWARD:
425 event_type = vertical ? Qscrollbar_page_down : Qscrollbar_page_right;
426 event_data = Fcons (win, Qnil);
428 case GTK_SCROLL_STEP_FORWARD:
429 event_type = vertical ? Qscrollbar_line_down : Qscrollbar_char_right;
432 case GTK_SCROLL_STEP_BACKWARD:
433 event_type = vertical ? Qscrollbar_line_up : Qscrollbar_char_left;
436 case GTK_SCROLL_NONE:
437 case GTK_SCROLL_JUMP:
438 inhibit_slider_size_change = 1;
439 event_type = vertical ? Qscrollbar_vertical_drag : Qscrollbar_horizontal_drag;
440 event_data = Fcons (win, make_int ((int)adj->value));
446 signal_special_gtk_user_event (frame, event_type, event_data);
452 gtk_scrollbar_pointer_changed_in_window (struct window *w)
456 XSETWINDOW (window, w);
457 gtk_scrollbar_loop (GTK_SET_SCROLLBAR_POINTER, window, find_window_mirror (w),
458 0, (GdkWindow *) NULL);
461 /* #### BILL!!! This comment is not true for Gtk - should it be? */
462 /* Make sure that all scrollbars on frame are up-to-date. Called
463 directly from gtk_set_frame_properties in frame-gtk.c*/
465 gtk_update_frame_scrollbars (struct frame *f)
467 /* Consider this code to be "in_display" so that we ABORT() if Fsignal()
470 gtk_scrollbar_loop (GTK_UPDATE_FRAME_SCROLLBARS, f->root_window, f->root_mirror,
471 0, (GdkWindow *) NULL);
473 if (in_display < 0) ABORT ();
476 #ifdef MEMORY_USAGE_STATS
478 gtk_compute_scrollbar_instance_usage (struct device *d,
479 struct scrollbar_instance *inst,
480 struct overhead_stats *ovstats)
486 struct gtk_scrollbar_data *data =
487 (struct gtk_scrollbar_data *) inst->scrollbar_data;
489 total += malloced_storage_size (data, sizeof (*data), ovstats);
496 #endif /* MEMORY_USAGE_STATS */
499 /************************************************************************/
501 /************************************************************************/
504 console_type_create_scrollbar_gtk (void)
506 CONSOLE_HAS_METHOD (gtk, inhibit_scrollbar_slider_size_change);
507 CONSOLE_HAS_METHOD (gtk, free_scrollbar_instance);
508 CONSOLE_HAS_METHOD (gtk, release_scrollbar_instance);
509 CONSOLE_HAS_METHOD (gtk, create_scrollbar_instance);
510 CONSOLE_HAS_METHOD (gtk, update_scrollbar_instance_values);
511 CONSOLE_HAS_METHOD (gtk, update_scrollbar_instance_status);
512 CONSOLE_HAS_METHOD (gtk, scrollbar_pointer_changed_in_window);
513 #ifdef MEMORY_USAGE_STATS
514 CONSOLE_HAS_METHOD (gtk, compute_scrollbar_instance_usage);
515 #endif /* MEMORY_USAGE_STATS */
519 vars_of_scrollbar_gtk (void)
521 Fprovide (intern ("gtk-scrollbars"));