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 SXEmacs
9 SXEmacs is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 SXEmacs is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 /* Synched up with: Not in FSF. */
24 /* Gtk version by William M. Perry */
29 #include "console-gtk.h"
30 #include "glyphs-gtk.h"
32 #include "scrollbar-gtk.h"
35 #include "ui/window.h"
37 static gboolean scrollbar_cb(GtkAdjustment * adj, gpointer user_data);
39 /* Used to prevent changing the size of the slider while drag
40 scrolling, under Motif. This is necessary because the Motif
41 scrollbar is incredibly stupid about updating the slider and causes
42 lots of flicker if it is done too often. */
43 static int inhibit_slider_size_change;
45 static int vertical_drag_in_progress;
47 /* A device method. */
48 static int gtk_inhibit_scrollbar_slider_size_change(void)
50 return inhibit_slider_size_change;
53 /* A device method. */
54 static void gtk_free_scrollbar_instance(struct scrollbar_instance *instance)
56 if (SCROLLBAR_GTK_WIDGET(instance)) {
57 gtk_widget_hide_all(SCROLLBAR_GTK_WIDGET(instance));
58 gtk_widget_destroy(SCROLLBAR_GTK_WIDGET(instance));
61 if (instance->scrollbar_data)
62 xfree(instance->scrollbar_data);
65 /* A device method. */
66 static void gtk_release_scrollbar_instance(struct scrollbar_instance *instance)
68 /* It won't hurt to hide it all the time, will it? */
69 gtk_widget_hide_all(SCROLLBAR_GTK_WIDGET(instance));
73 scrollbar_drag_hack_cb(GtkWidget * w, GdkEventButton * ev, gpointer v)
75 vertical_drag_in_progress = (int)v;
76 inhibit_slider_size_change = (int)v;
80 /* A device method. */
82 gtk_create_scrollbar_instance(struct frame *f, int vertical,
83 struct scrollbar_instance *instance)
86 GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0));
87 GtkScrollbar *sb = NULL;
89 /* initialize the X specific data section. */
90 instance->scrollbar_data = xnew_and_zero(struct gtk_scrollbar_data);
92 SCROLLBAR_GTK_ID(instance) = new_gui_id();
93 SCROLLBAR_GTK_VDRAG_ORIG_VALUE(instance) = -1;
94 SCROLLBAR_GTK_LAST_VALUE(instance) = adj->value;
96 gtk_object_set_data(GTK_OBJECT(adj), GTK_DATA_GUI_IDENTIFIER,
97 (void *)SCROLLBAR_GTK_ID(instance));
98 gtk_object_set_data(GTK_OBJECT(adj), GTK_DATA_FRAME_IDENTIFIER, f);
99 gtk_object_set_data(GTK_OBJECT(adj), "xemacs::sb_instance", instance);
101 sb = GTK_SCROLLBAR(vertical ? gtk_vscrollbar_new(adj) :
102 gtk_hscrollbar_new(adj));
103 /* With gtk version > 1.2.8 the gtk code does not call
104 gtk_widget_request_size() on the newly created scrollbars
105 anymore (catering to theme engines).
106 #### Maybe it is better to postpone this call to just before
107 gtk_widget_show() is called on the scrollbar? */
108 #if GTK_MAJOR_VERSION == 1 && GTK_MINOR_VERSION == 2 && GTK_BINARY_AGE > 8
109 gtk_widget_size_request(GTK_WIDGET(sb), &(GTK_WIDGET(sb)->requisition));
111 SCROLLBAR_GTK_WIDGET(instance) = GTK_WIDGET(sb);
113 gtk_signal_connect(GTK_OBJECT(adj), "value-changed",
114 GTK_SIGNAL_FUNC(scrollbar_cb), (gpointer) vertical);
116 gtk_signal_connect(GTK_OBJECT(sb), "button-press-event",
117 GTK_SIGNAL_FUNC(scrollbar_drag_hack_cb),
119 gtk_signal_connect(GTK_OBJECT(sb), "button-release-event",
120 GTK_SIGNAL_FUNC(scrollbar_drag_hack_cb),
123 gtk_fixed_put(GTK_FIXED(FRAME_GTK_TEXT_WIDGET(f)),
124 SCROLLBAR_GTK_WIDGET(instance), 0, 0);
125 gtk_widget_hide(SCROLLBAR_GTK_WIDGET(instance));
128 #define UPDATE_DATA_FIELD(field) \
129 if (new_##field >= 0 && \
130 SCROLLBAR_GTK_POS_DATA (inst).field != new_##field) { \
131 SCROLLBAR_GTK_POS_DATA (inst).field = new_##field; \
132 inst->scrollbar_instance_changed = 1; \
135 /* A device method. */
136 /* #### The -1 check is such a hack. */
138 gtk_update_scrollbar_instance_values(struct window *w,
139 struct scrollbar_instance *inst,
140 int new_line_increment,
141 int new_page_increment,
142 int new_minimum, int new_maximum,
144 int new_slider_position,
145 int new_scrollbar_width,
146 int new_scrollbar_height,
147 int new_scrollbar_x, int new_scrollbar_y)
149 UPDATE_DATA_FIELD(line_increment);
150 UPDATE_DATA_FIELD(page_increment);
151 UPDATE_DATA_FIELD(minimum);
152 UPDATE_DATA_FIELD(maximum);
153 UPDATE_DATA_FIELD(slider_size);
154 UPDATE_DATA_FIELD(slider_position);
155 UPDATE_DATA_FIELD(scrollbar_width);
156 UPDATE_DATA_FIELD(scrollbar_height);
157 UPDATE_DATA_FIELD(scrollbar_x);
158 UPDATE_DATA_FIELD(scrollbar_y);
160 if (w && !vertical_drag_in_progress) {
161 int new_vov = SCROLLBAR_GTK_POS_DATA(inst).slider_position;
162 int new_vows = marker_position(w->start[CURRENT_DISP]);
164 if (SCROLLBAR_GTK_VDRAG_ORIG_VALUE(inst) != new_vov) {
165 SCROLLBAR_GTK_VDRAG_ORIG_VALUE(inst) = new_vov;
166 inst->scrollbar_instance_changed = 1;
168 if (SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START(inst) != new_vows) {
169 SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START(inst) = new_vows;
170 inst->scrollbar_instance_changed = 1;
175 /* Used by gtk_update_scrollbar_instance_status. */
177 update_one_widget_scrollbar_pointer(struct window *w, GtkWidget * wid)
180 gtk_widget_realize(wid);
182 if (POINTER_IMAGE_INSTANCEP(w->scrollbar_pointer)) {
183 gdk_window_set_cursor(GET_GTK_WIDGET_WINDOW(wid),
184 XIMAGE_INSTANCE_GTK_CURSOR(w->
190 /* A device method. */
192 gtk_update_scrollbar_instance_status(struct window *w, int active, int size,
193 struct scrollbar_instance *instance)
195 struct frame *f = XFRAME(w->frame);
196 GtkWidget *wid = SCROLLBAR_GTK_WIDGET(instance);
197 gboolean managed = GTK_WIDGET_MAPPED(wid);
199 if (active && size) {
200 if (instance->scrollbar_instance_changed) {
201 /* Need to set the height, width, and position of the widget */
203 gtk_range_get_adjustment(GTK_RANGE(wid));
204 scrollbar_values *pos_data =
205 &SCROLLBAR_GTK_POS_DATA(instance);
208 /* We do not want to update the size all the time if we can
209 help it. This cuts down on annoying flicker.
211 if ((wid->allocation.width != pos_data->scrollbar_width)
212 || (wid->allocation.height !=
213 pos_data->scrollbar_height)) {
214 gtk_widget_set_usize(wid,
215 pos_data->scrollbar_width,
220 UGLY! UGLY! UGLY! Changes to wid->allocation are queued and
221 not performed until the GTK event loop. However, when the
222 fontlock progress bar is run, the vertical scrollbar's height
223 is change and then changed back before events are again
224 processed. This means that the change back is not seen and
225 the scrollbar is left too short. Fix this by making the
226 change manually so the test above sees the change. This does
227 not seem to cause problems in other cases.
230 wid->allocation.width =
231 pos_data->scrollbar_width;
232 wid->allocation.height =
233 pos_data->scrollbar_height;
238 /* Ditto for the x/y position. */
239 if ((wid->allocation.x != pos_data->scrollbar_x) ||
240 (wid->allocation.y != pos_data->scrollbar_y)) {
241 gtk_fixed_move(GTK_FIXED
242 (FRAME_GTK_TEXT_WIDGET(f)), wid,
243 pos_data->scrollbar_x,
244 pos_data->scrollbar_y);
247 UGLY! UGLY! UGLY! Changes to wid->allocation are queued and
248 not performed until the GTK event loop. However, when the
249 fontlock progress bar is run, the horizontal scrollbar's
250 position is change and then changed back before events are
251 again processed. This means that the change back is not seen
252 and the scrollbar is left in the wrong position. Fix this by
253 making the change manually so the test above sees the change.
254 This does not seem to cause problems in other cases.
257 wid->allocation.x = pos_data->scrollbar_x;
258 wid->allocation.y = pos_data->scrollbar_y;
263 adj->lower = pos_data->minimum;
264 adj->upper = pos_data->maximum;
265 adj->page_increment = pos_data->slider_size + 1;
266 adj->step_increment = w->max_line_len - 1;
267 adj->page_size = pos_data->slider_size + 1;
268 adj->value = pos_data->slider_position;
270 /* But, if we didn't resize or move the scrollbar, the
271 widget will not get redrawn correctly when the user
272 scrolls around in the XEmacs frame manually. So we
273 update the slider manually here.
276 gtk_range_slider_update(GTK_RANGE(wid));
278 instance->scrollbar_instance_changed = 0;
282 gtk_widget_show(wid);
283 update_one_widget_scrollbar_pointer(w, wid);
285 } else if (managed) {
286 gtk_widget_hide(wid);
290 enum gtk_scrollbar_loop {
291 GTK_FIND_SCROLLBAR_WINDOW_MIRROR,
292 GTK_SET_SCROLLBAR_POINTER,
293 GTK_WINDOW_IS_SCROLLBAR,
294 GTK_UPDATE_FRAME_SCROLLBARS
297 static struct window_mirror *gtk_scrollbar_loop(enum gtk_scrollbar_loop type,
299 struct window_mirror *mir,
300 GUI_ID id, GdkWindow * x_win)
302 struct window_mirror *retval = NULL;
305 struct scrollbar_instance *vinstance =
306 mir->scrollbar_vertical_instance;
307 struct scrollbar_instance *hinstance =
308 mir->scrollbar_horizontal_instance;
309 struct window *w = XWINDOW(window);
313 gtk_scrollbar_loop(type, w->vchild, mir->vchild, id,
315 else if (mir->hchild)
317 gtk_scrollbar_loop(type, w->hchild, mir->hchild, id,
322 if (hinstance || vinstance) {
324 case GTK_FIND_SCROLLBAR_WINDOW_MIRROR:
326 && SCROLLBAR_GTK_ID(vinstance) == id)
328 && SCROLLBAR_GTK_ID(hinstance) == id))
331 case GTK_UPDATE_FRAME_SCROLLBARS:
332 if (!mir->vchild && !mir->hchild)
333 update_window_scrollbars(w, mir, 1, 0);
335 case GTK_SET_SCROLLBAR_POINTER:
336 if (!mir->vchild && !mir->hchild) {
340 SCROLLBAR_GTK_WIDGET(hinstance);
341 if (widget && GTK_WIDGET_MAPPED(widget))
342 update_one_widget_scrollbar_pointer
346 SCROLLBAR_GTK_WIDGET(vinstance);
347 if (widget && GTK_WIDGET_MAPPED(widget))
348 update_one_widget_scrollbar_pointer
352 case GTK_WINDOW_IS_SCROLLBAR:
353 if (!mir->vchild && !mir->hchild) {
357 SCROLLBAR_GTK_WIDGET(hinstance);
358 if (widget && GTK_WIDGET_MAPPED(widget)
359 && GET_GTK_WIDGET_WINDOW(widget) ==
361 return (struct window_mirror *)
365 SCROLLBAR_GTK_WIDGET(vinstance);
366 if (widget && GTK_WIDGET_MAPPED(widget)
367 && GET_GTK_WIDGET_WINDOW(widget) ==
369 return (struct window_mirror *)
385 /* Used by callbacks. */
386 static struct window_mirror *find_scrollbar_window_mirror(struct frame *f,
390 update_frame_window_mirror(f);
391 return gtk_scrollbar_loop(GTK_FIND_SCROLLBAR_WINDOW_MIRROR,
392 f->root_window, f->root_mirror, id,
396 static gboolean scrollbar_cb(GtkAdjustment * adj, gpointer user_data)
398 /* This function can GC */
399 int vertical = (int)user_data;
401 gtk_object_get_data(GTK_OBJECT(adj), GTK_DATA_FRAME_IDENTIFIER);
402 struct scrollbar_instance *instance =
403 gtk_object_get_data(GTK_OBJECT(adj), "xemacs::sb_instance");
405 (GUI_ID) gtk_object_get_data(GTK_OBJECT(adj),
406 GTK_DATA_GUI_IDENTIFIER);
407 Lisp_Object win, frame;
408 struct window_mirror *mirror;
409 Lisp_Object event_type = Qnil;
410 Lisp_Object event_data = Qnil;
415 mirror = find_scrollbar_window_mirror(f, id);
419 win = real_window(mirror, 1);
424 vertical ? mirror->scrollbar_vertical_instance : mirror->
425 scrollbar_horizontal_instance;
426 frame = WINDOW_FRAME(XWINDOW(win));
428 inhibit_slider_size_change = 0;
429 switch (GTK_RANGE(SCROLLBAR_GTK_WIDGET(instance))->scroll_type) {
430 case GTK_SCROLL_PAGE_BACKWARD:
432 vertical ? Qscrollbar_page_up : Qscrollbar_page_left;
433 event_data = Fcons(win, Qnil);
435 case GTK_SCROLL_PAGE_FORWARD:
437 vertical ? Qscrollbar_page_down : Qscrollbar_page_right;
438 event_data = Fcons(win, Qnil);
440 case GTK_SCROLL_STEP_FORWARD:
442 vertical ? Qscrollbar_line_down : Qscrollbar_char_right;
445 case GTK_SCROLL_STEP_BACKWARD:
447 vertical ? Qscrollbar_line_up : Qscrollbar_char_left;
450 case GTK_SCROLL_NONE:
451 case GTK_SCROLL_JUMP:
452 inhibit_slider_size_change = 1;
454 vertical ? Qscrollbar_vertical_drag :
455 Qscrollbar_horizontal_drag;
456 event_data = Fcons(win, make_int((int)adj->value));
462 signal_special_gtk_user_event(frame, event_type, event_data);
467 static void gtk_scrollbar_pointer_changed_in_window(struct window *w)
471 XSETWINDOW(window, w);
472 gtk_scrollbar_loop(GTK_SET_SCROLLBAR_POINTER, window,
473 find_window_mirror(w), 0, (GdkWindow *) NULL);
476 /* #### BILL!!! This comment is not true for Gtk - should it be? */
477 /* Make sure that all scrollbars on frame are up-to-date. Called
478 directly from gtk_set_frame_properties in frame-gtk.c*/
479 void gtk_update_frame_scrollbars(struct frame *f)
481 /* Consider this code to be "in_display" so that we abort() if Fsignal()
484 gtk_scrollbar_loop(GTK_UPDATE_FRAME_SCROLLBARS, f->root_window,
485 f->root_mirror, 0, (GdkWindow *) NULL);
491 #ifdef MEMORY_USAGE_STATS
493 gtk_compute_scrollbar_instance_usage(struct device *d,
494 struct scrollbar_instance *inst,
495 struct overhead_stats *ovstats)
500 struct gtk_scrollbar_data *data =
501 (struct gtk_scrollbar_data *)inst->scrollbar_data;
503 total += malloced_storage_size(data, sizeof(*data), ovstats);
510 #endif /* MEMORY_USAGE_STATS */
512 /************************************************************************/
514 /************************************************************************/
516 void console_type_create_scrollbar_gtk(void)
518 CONSOLE_HAS_METHOD(gtk, inhibit_scrollbar_slider_size_change);
519 CONSOLE_HAS_METHOD(gtk, free_scrollbar_instance);
520 CONSOLE_HAS_METHOD(gtk, release_scrollbar_instance);
521 CONSOLE_HAS_METHOD(gtk, create_scrollbar_instance);
522 CONSOLE_HAS_METHOD(gtk, update_scrollbar_instance_values);
523 CONSOLE_HAS_METHOD(gtk, update_scrollbar_instance_status);
524 CONSOLE_HAS_METHOD(gtk, scrollbar_pointer_changed_in_window);
525 #ifdef MEMORY_USAGE_STATS
526 CONSOLE_HAS_METHOD(gtk, compute_scrollbar_instance_usage);
527 #endif /* MEMORY_USAGE_STATS */
530 void vars_of_scrollbar_gtk(void)
532 Fprovide(intern("gtk-scrollbars"));