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 (SCROLLBAR_GTK_WIDGET (instance))
62 gtk_widget_hide_all (SCROLLBAR_GTK_WIDGET (instance));
63 gtk_widget_destroy (SCROLLBAR_GTK_WIDGET (instance));
66 if (instance->scrollbar_data)
67 xfree (instance->scrollbar_data);
70 /* A device method. */
72 gtk_release_scrollbar_instance (struct scrollbar_instance *instance)
74 /* It won't hurt to hide it all the time, will it? */
75 gtk_widget_hide_all (SCROLLBAR_GTK_WIDGET (instance));
79 scrollbar_drag_hack_cb (GtkWidget *w, GdkEventButton *ev, gpointer v)
81 vertical_drag_in_progress = (int) v;
82 inhibit_slider_size_change = (int) v;
86 /* A device method. */
88 gtk_create_scrollbar_instance (struct frame *f, int vertical,
89 struct scrollbar_instance *instance)
91 GtkAdjustment *adj = GTK_ADJUSTMENT (gtk_adjustment_new (0,0,0,0,0,0));
92 GtkScrollbar *sb = NULL;
94 /* initialize the X specific data section. */
95 instance->scrollbar_data = xnew_and_zero (struct gtk_scrollbar_data);
97 SCROLLBAR_GTK_ID (instance) = new_gui_id ();
98 SCROLLBAR_GTK_VDRAG_ORIG_VALUE (instance) = -1;
99 SCROLLBAR_GTK_LAST_VALUE (instance) = adj->value;
101 gtk_object_set_data (GTK_OBJECT (adj), GTK_DATA_GUI_IDENTIFIER,
102 (void *) SCROLLBAR_GTK_ID (instance));
103 gtk_object_set_data (GTK_OBJECT (adj), GTK_DATA_FRAME_IDENTIFIER, f);
104 gtk_object_set_data (GTK_OBJECT (adj), "xemacs::sb_instance", instance);
106 sb = GTK_SCROLLBAR (vertical ? gtk_vscrollbar_new (adj) : gtk_hscrollbar_new (adj));
107 /* With gtk version > 1.2.8 the gtk code does not call
108 gtk_widget_request_size() on the newly created scrollbars
109 anymore (catering to theme engines).
110 #### Maybe it is better to postpone this call to just before
111 gtk_widget_show() is called on the scrollbar? */
112 #if GTK_MAJOR_VERSION == 1 && GTK_MINOR_VERSION == 2 && GTK_BINARY_AGE > 8
113 gtk_widget_size_request(GTK_WIDGET(sb), &(GTK_WIDGET(sb)->requisition));
115 SCROLLBAR_GTK_WIDGET (instance) = GTK_WIDGET (sb);
117 gtk_signal_connect (GTK_OBJECT (adj),"value-changed",
118 GTK_SIGNAL_FUNC (scrollbar_cb), (gpointer) vertical);
120 gtk_signal_connect (GTK_OBJECT (sb), "button-press-event",
121 GTK_SIGNAL_FUNC (scrollbar_drag_hack_cb), (gpointer) 1);
122 gtk_signal_connect (GTK_OBJECT (sb), "button-release-event",
123 GTK_SIGNAL_FUNC (scrollbar_drag_hack_cb), (gpointer) 0);
125 gtk_fixed_put (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)), SCROLLBAR_GTK_WIDGET (instance), 0, 0);
126 gtk_widget_hide (SCROLLBAR_GTK_WIDGET (instance));
129 #define UPDATE_DATA_FIELD(field) \
130 if (new_##field >= 0 && \
131 SCROLLBAR_GTK_POS_DATA (inst).field != new_##field) { \
132 SCROLLBAR_GTK_POS_DATA (inst).field = new_##field; \
133 inst->scrollbar_instance_changed = 1; \
136 /* A device method. */
137 /* #### The -1 check is such a hack. */
139 gtk_update_scrollbar_instance_values (struct window *w,
140 struct scrollbar_instance *inst,
141 int new_line_increment,
142 int new_page_increment,
143 int new_minimum, int new_maximum,
145 int new_slider_position,
146 int new_scrollbar_width,
147 int new_scrollbar_height,
148 int new_scrollbar_x, int new_scrollbar_y)
150 UPDATE_DATA_FIELD (line_increment);
151 UPDATE_DATA_FIELD (page_increment);
152 UPDATE_DATA_FIELD (minimum);
153 UPDATE_DATA_FIELD (maximum);
154 UPDATE_DATA_FIELD (slider_size);
155 UPDATE_DATA_FIELD (slider_position);
156 UPDATE_DATA_FIELD (scrollbar_width);
157 UPDATE_DATA_FIELD (scrollbar_height);
158 UPDATE_DATA_FIELD (scrollbar_x);
159 UPDATE_DATA_FIELD (scrollbar_y);
161 if (w && !vertical_drag_in_progress)
163 int new_vov = SCROLLBAR_GTK_POS_DATA (inst).slider_position;
164 int new_vows = marker_position (w->start[CURRENT_DISP]);
166 if (SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) != new_vov)
168 SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) = new_vov;
169 inst->scrollbar_instance_changed = 1;
171 if (SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) != new_vows)
173 SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) = new_vows;
174 inst->scrollbar_instance_changed = 1;
179 /* Used by gtk_update_scrollbar_instance_status. */
181 update_one_widget_scrollbar_pointer (struct window *w, GtkWidget *wid)
184 gtk_widget_realize (wid);
186 if (POINTER_IMAGE_INSTANCEP (w->scrollbar_pointer))
188 gdk_window_set_cursor (GET_GTK_WIDGET_WINDOW (wid),
189 XIMAGE_INSTANCE_GTK_CURSOR (w->scrollbar_pointer));
194 /* A device method. */
196 gtk_update_scrollbar_instance_status (struct window *w, int active, int size,
197 struct scrollbar_instance *instance)
199 struct frame *f = XFRAME (w->frame);
200 GtkWidget *wid = SCROLLBAR_GTK_WIDGET (instance);
201 gboolean managed = GTK_WIDGET_MAPPED (wid);
205 if (instance->scrollbar_instance_changed)
207 /* Need to set the height, width, and position of the widget */
208 GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (wid));
209 scrollbar_values *pos_data = & SCROLLBAR_GTK_POS_DATA (instance);
212 /* We do not want to update the size all the time if we can
213 help it. This cuts down on annoying flicker.
215 if ((wid->allocation.width != pos_data->scrollbar_width) ||
216 (wid->allocation.height != pos_data->scrollbar_height))
218 gtk_widget_set_usize (wid,
219 pos_data->scrollbar_width,
220 pos_data->scrollbar_height);
223 UGLY! UGLY! UGLY! Changes to wid->allocation are queued and
224 not performed until the GTK event loop. However, when the
225 fontlock progress bar is run, the vertical scrollbar's height
226 is change and then changed back before events are again
227 processed. This means that the change back is not seen and
228 the scrollbar is left too short. Fix this by making the
229 change manually so the test above sees the change. This does
230 not seem to cause problems in other cases.
233 wid->allocation.width = pos_data->scrollbar_width;
234 wid->allocation.height = pos_data->scrollbar_height;
239 /* Ditto for the x/y position. */
240 if ((wid->allocation.x != pos_data->scrollbar_x) ||
241 (wid->allocation.y != pos_data->scrollbar_y))
243 gtk_fixed_move (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
245 pos_data->scrollbar_x,
246 pos_data->scrollbar_y);
249 UGLY! UGLY! UGLY! Changes to wid->allocation are queued and
250 not performed until the GTK event loop. However, when the
251 fontlock progress bar is run, the horizontal scrollbar's
252 position is change and then changed back before events are
253 again processed. This means that the change back is not seen
254 and the scrollbar is left in the wrong position. Fix this by
255 making the change manually so the test above sees the change.
256 This does not seem to cause problems in other cases.
259 wid->allocation.x = pos_data->scrollbar_x;
260 wid->allocation.y = pos_data->scrollbar_y;
265 adj->lower = pos_data->minimum;
266 adj->upper = pos_data->maximum;
267 adj->page_increment = pos_data->slider_size + 1;
268 adj->step_increment = w->max_line_len - 1;
269 adj->page_size = pos_data->slider_size + 1;
270 adj->value = pos_data->slider_position;
272 /* But, if we didn't resize or move the scrollbar, the
273 widget will not get redrawn correctly when the user
274 scrolls around in the XEmacs frame manually. So we
275 update the slider manually here.
278 gtk_range_slider_update (GTK_RANGE (wid));
280 instance->scrollbar_instance_changed = 0;
285 gtk_widget_show (wid);
286 update_one_widget_scrollbar_pointer (w, wid);
291 gtk_widget_hide (wid);
295 enum gtk_scrollbar_loop
297 GTK_FIND_SCROLLBAR_WINDOW_MIRROR,
298 GTK_SET_SCROLLBAR_POINTER,
299 GTK_WINDOW_IS_SCROLLBAR,
300 GTK_UPDATE_FRAME_SCROLLBARS
303 static struct window_mirror *
304 gtk_scrollbar_loop (enum gtk_scrollbar_loop type, Lisp_Object window,
305 struct window_mirror *mir,
306 GUI_ID id, GdkWindow *x_win)
308 struct window_mirror *retval = NULL;
312 struct scrollbar_instance *vinstance = mir->scrollbar_vertical_instance;
313 struct scrollbar_instance *hinstance = mir->scrollbar_horizontal_instance;
314 struct window *w = XWINDOW (window);
317 retval = gtk_scrollbar_loop (type, w->vchild, mir->vchild, id, x_win);
318 else if (mir->hchild)
319 retval = gtk_scrollbar_loop (type, w->hchild, mir->hchild, id, x_win);
323 if (hinstance || vinstance)
327 case GTK_FIND_SCROLLBAR_WINDOW_MIRROR:
328 if ((vinstance && SCROLLBAR_GTK_ID (vinstance) == id) ||
329 (hinstance && SCROLLBAR_GTK_ID (hinstance) == id))
332 case GTK_UPDATE_FRAME_SCROLLBARS:
333 if (!mir->vchild && !mir->hchild)
334 update_window_scrollbars (w, mir, 1, 0);
336 case GTK_SET_SCROLLBAR_POINTER:
337 if (!mir->vchild && !mir->hchild)
341 widget = SCROLLBAR_GTK_WIDGET (hinstance);
342 if (widget && GTK_WIDGET_MAPPED (widget))
343 update_one_widget_scrollbar_pointer (w, widget);
345 widget = SCROLLBAR_GTK_WIDGET (vinstance);
346 if (widget && GTK_WIDGET_MAPPED (widget))
347 update_one_widget_scrollbar_pointer (w, widget);
350 case GTK_WINDOW_IS_SCROLLBAR:
351 if (!mir->vchild && !mir->hchild)
355 widget = SCROLLBAR_GTK_WIDGET (hinstance);
356 if (widget && GTK_WIDGET_MAPPED (widget) &&
357 GET_GTK_WIDGET_WINDOW (widget) == x_win)
358 return (struct window_mirror *) 1;
360 widget = SCROLLBAR_GTK_WIDGET (vinstance);
361 if (widget && GTK_WIDGET_MAPPED (widget) &&
362 GET_GTK_WIDGET_WINDOW (widget) == x_win)
363 return (struct window_mirror *) 1;
378 /* Used by callbacks. */
379 static struct window_mirror *
380 find_scrollbar_window_mirror (struct frame *f, GUI_ID id)
383 update_frame_window_mirror (f);
384 return gtk_scrollbar_loop (GTK_FIND_SCROLLBAR_WINDOW_MIRROR, f->root_window,
385 f->root_mirror, id, (GdkWindow *) NULL);
389 scrollbar_cb (GtkAdjustment *adj, gpointer user_data)
391 /* This function can GC */
392 int vertical = (int) user_data;
393 struct frame *f = gtk_object_get_data (GTK_OBJECT (adj), GTK_DATA_FRAME_IDENTIFIER);
394 struct scrollbar_instance *instance = gtk_object_get_data (GTK_OBJECT (adj), "xemacs::sb_instance");
395 GUI_ID id = (GUI_ID) gtk_object_get_data (GTK_OBJECT (adj), GTK_DATA_GUI_IDENTIFIER);
396 Lisp_Object win, frame;
397 struct window_mirror *mirror;
398 Lisp_Object event_type = Qnil;
399 Lisp_Object event_data = Qnil;
404 mirror = find_scrollbar_window_mirror (f, id);
408 win = real_window (mirror, 1);
412 instance = vertical ? mirror->scrollbar_vertical_instance : mirror->scrollbar_horizontal_instance;
413 frame = WINDOW_FRAME (XWINDOW (win));
415 inhibit_slider_size_change = 0;
416 switch (GTK_RANGE (SCROLLBAR_GTK_WIDGET (instance))->scroll_type)
418 case GTK_SCROLL_PAGE_BACKWARD:
419 event_type = vertical ? Qscrollbar_page_up : Qscrollbar_page_left;
420 event_data = Fcons (win, Qnil);
422 case GTK_SCROLL_PAGE_FORWARD:
423 event_type = vertical ? Qscrollbar_page_down : Qscrollbar_page_right;
424 event_data = Fcons (win, Qnil);
426 case GTK_SCROLL_STEP_FORWARD:
427 event_type = vertical ? Qscrollbar_line_down : Qscrollbar_char_right;
430 case GTK_SCROLL_STEP_BACKWARD:
431 event_type = vertical ? Qscrollbar_line_up : Qscrollbar_char_left;
434 case GTK_SCROLL_NONE:
435 case GTK_SCROLL_JUMP:
436 inhibit_slider_size_change = 1;
437 event_type = vertical ? Qscrollbar_vertical_drag : Qscrollbar_horizontal_drag;
438 event_data = Fcons (win, make_int ((int)adj->value));
444 signal_special_gtk_user_event (frame, event_type, event_data);
450 gtk_scrollbar_pointer_changed_in_window (struct window *w)
454 XSETWINDOW (window, w);
455 gtk_scrollbar_loop (GTK_SET_SCROLLBAR_POINTER, window, find_window_mirror (w),
456 0, (GdkWindow *) NULL);
459 /* #### BILL!!! This comment is not true for Gtk - should it be? */
460 /* Make sure that all scrollbars on frame are up-to-date. Called
461 directly from gtk_set_frame_properties in frame-gtk.c*/
463 gtk_update_frame_scrollbars (struct frame *f)
465 /* Consider this code to be "in_display" so that we ABORT() if Fsignal()
468 gtk_scrollbar_loop (GTK_UPDATE_FRAME_SCROLLBARS, f->root_window, f->root_mirror,
469 0, (GdkWindow *) NULL);
471 if (in_display < 0) ABORT ();
474 #ifdef MEMORY_USAGE_STATS
476 gtk_compute_scrollbar_instance_usage (struct device *d,
477 struct scrollbar_instance *inst,
478 struct overhead_stats *ovstats)
484 struct gtk_scrollbar_data *data =
485 (struct gtk_scrollbar_data *) inst->scrollbar_data;
487 total += malloced_storage_size (data, sizeof (*data), ovstats);
494 #endif /* MEMORY_USAGE_STATS */
497 /************************************************************************/
499 /************************************************************************/
502 console_type_create_scrollbar_gtk (void)
504 CONSOLE_HAS_METHOD (gtk, inhibit_scrollbar_slider_size_change);
505 CONSOLE_HAS_METHOD (gtk, free_scrollbar_instance);
506 CONSOLE_HAS_METHOD (gtk, release_scrollbar_instance);
507 CONSOLE_HAS_METHOD (gtk, create_scrollbar_instance);
508 CONSOLE_HAS_METHOD (gtk, update_scrollbar_instance_values);
509 CONSOLE_HAS_METHOD (gtk, update_scrollbar_instance_status);
510 CONSOLE_HAS_METHOD (gtk, scrollbar_pointer_changed_in_window);
511 #ifdef MEMORY_USAGE_STATS
512 CONSOLE_HAS_METHOD (gtk, compute_scrollbar_instance_usage);
513 #endif /* MEMORY_USAGE_STATS */
517 vars_of_scrollbar_gtk (void)
519 Fprovide (intern ("gtk-scrollbars"));