(<DENTISTRY SYMBOL *>): Add missing `general-category'.
[chise/xemacs-chise.git.1] / src / scrollbar-gtk.c
1 /* scrollbar implementation -- X 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>.
6
7 This file is part of XEmacs.
8
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
12 later version.
13
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
17 for more details.
18
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.  */
23
24 /* Synched up with: Not in FSF. */
25 /* Gtk version by William M. Perry */
26
27 #include <config.h>
28 #include "lisp.h"
29
30 #include "console-gtk.h"
31 #include "glyphs-gtk.h"
32 #include "gui-gtk.h"
33 #include "scrollbar-gtk.h"
34
35 #include "frame.h"
36 #include "window.h"
37
38 static gboolean scrollbar_cb (GtkAdjustment *adj, gpointer user_data);
39
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;
45
46 static int vertical_drag_in_progress;
47
48 \f
49 /* A device method. */
50 static int
51 gtk_inhibit_scrollbar_slider_size_change (void)
52 {
53   return inhibit_slider_size_change;
54 }
55
56 /* A device method. */
57 static void
58 gtk_free_scrollbar_instance (struct scrollbar_instance *instance)
59 {
60   if (SCROLLBAR_GTK_WIDGET (instance))
61     {
62       gtk_widget_hide_all (SCROLLBAR_GTK_WIDGET (instance));
63       gtk_widget_destroy (SCROLLBAR_GTK_WIDGET (instance));
64     }
65
66   if (instance->scrollbar_data)
67     xfree (instance->scrollbar_data);
68 }
69
70 /* A device method. */
71 static void
72 gtk_release_scrollbar_instance (struct scrollbar_instance *instance)
73 {
74     /* It won't hurt to hide it all the time, will it? */
75     gtk_widget_hide_all (SCROLLBAR_GTK_WIDGET (instance));
76 }
77
78 static gboolean
79 scrollbar_drag_hack_cb (GtkWidget *w, GdkEventButton *ev, gpointer v)
80 {
81   vertical_drag_in_progress = (int) v;
82   inhibit_slider_size_change = (int) v;
83   return (FALSE);
84 }
85
86 /* A device method. */
87 static void
88 gtk_create_scrollbar_instance (struct frame *f, int vertical,
89                                struct scrollbar_instance *instance)
90 {
91   GtkAdjustment *adj = GTK_ADJUSTMENT (gtk_adjustment_new (0,0,0,0,0,0));
92   GtkScrollbar *sb = NULL;
93
94   /* initialize the X specific data section. */
95   instance->scrollbar_data = xnew_and_zero (struct gtk_scrollbar_data);
96
97   SCROLLBAR_GTK_ID (instance) = new_gui_id ();
98   SCROLLBAR_GTK_VDRAG_ORIG_VALUE (instance) = -1;
99   SCROLLBAR_GTK_LAST_VALUE (instance) = adj->value;
100
101   gtk_object_set_data (GTK_OBJECT (adj), "xemacs::gui_id", (void *) SCROLLBAR_GTK_ID (instance));
102   gtk_object_set_data (GTK_OBJECT (adj), "xemacs::frame", f);
103   gtk_object_set_data (GTK_OBJECT (adj), "xemacs::sb_instance", instance);
104
105   sb = GTK_SCROLLBAR (vertical ? gtk_vscrollbar_new (adj) : gtk_hscrollbar_new (adj));
106   /* With gtk version > 1.2.8 the gtk code does not call
107      gtk_widget_request_size() on the newly created scrollbars
108      anymore (catering to theme engines).
109      #### Maybe it is better to postpone this call to just before
110      gtk_widget_show() is called on the scrollbar? */
111 #if GTK_MAJOR_VERSION == 1 && GTK_MINOR_VERSION == 2 && GTK_BINARY_AGE > 8
112   gtk_widget_size_request(GTK_WIDGET(sb), &(GTK_WIDGET(sb)->requisition));
113 #endif  
114   SCROLLBAR_GTK_WIDGET (instance) = GTK_WIDGET (sb);
115
116   gtk_signal_connect (GTK_OBJECT (adj),"value-changed",
117                       GTK_SIGNAL_FUNC (scrollbar_cb), (gpointer) vertical);
118
119   gtk_signal_connect (GTK_OBJECT (sb), "button-press-event",
120                       GTK_SIGNAL_FUNC (scrollbar_drag_hack_cb), (gpointer) 1);
121   gtk_signal_connect (GTK_OBJECT (sb), "button-release-event",
122                       GTK_SIGNAL_FUNC (scrollbar_drag_hack_cb), (gpointer) 0);
123
124   gtk_fixed_put (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)), SCROLLBAR_GTK_WIDGET (instance), 0, 0);
125   gtk_widget_hide (SCROLLBAR_GTK_WIDGET (instance));
126 }
127
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;                       \
133   }
134
135 /* A device method. */
136 /* #### The -1 check is such a hack. */
137 static void
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,
143                                       int new_slider_size,
144                                       int new_slider_position,
145                                       int new_scrollbar_width,
146                                       int new_scrollbar_height,
147                                       int new_scrollbar_x, int new_scrollbar_y)
148 {
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);
159
160   if (w && !vertical_drag_in_progress)
161     {
162       int new_vov = SCROLLBAR_GTK_POS_DATA (inst).slider_position;
163       int new_vows = marker_position (w->start[CURRENT_DISP]);
164
165       if (SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) != new_vov)
166         {
167           SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) = new_vov;
168           inst->scrollbar_instance_changed = 1;
169         }
170       if (SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) != new_vows)
171         {
172           SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) = new_vows;
173           inst->scrollbar_instance_changed = 1;
174         }
175     }
176 }
177
178 /* Used by gtk_update_scrollbar_instance_status. */
179 static void
180 update_one_widget_scrollbar_pointer (struct window *w, GtkWidget *wid)
181 {
182   if (!wid->window)
183     gtk_widget_realize (wid);
184
185   if (POINTER_IMAGE_INSTANCEP (w->scrollbar_pointer))
186     {
187       gdk_window_set_cursor (GET_GTK_WIDGET_WINDOW (wid),
188                              XIMAGE_INSTANCE_GTK_CURSOR (w->scrollbar_pointer));
189       gdk_flush ();
190     }
191 }
192
193 /* A device method. */
194 static void
195 gtk_update_scrollbar_instance_status (struct window *w, int active, int size,
196                                       struct scrollbar_instance *instance)
197 {
198   struct frame *f = XFRAME (w->frame);
199   GtkWidget *wid = SCROLLBAR_GTK_WIDGET (instance);
200   gboolean managed = GTK_WIDGET_MAPPED (wid);
201
202   if (active && size)
203     {
204       if (instance->scrollbar_instance_changed)
205         {
206           /* Need to set the height, width, and position of the widget */
207           GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (wid));
208           scrollbar_values *pos_data = & SCROLLBAR_GTK_POS_DATA (instance);
209           int modified_p = 0;
210
211           /* We do not want to update the size all the time if we can
212              help it.  This cuts down on annoying flicker.
213           */
214           if ((wid->allocation.width != pos_data->scrollbar_width) ||
215               (wid->allocation.height != pos_data->scrollbar_height))
216             {
217               gtk_widget_set_usize (wid,
218                                     pos_data->scrollbar_width,
219                                     pos_data->scrollbar_height);
220               modified_p = 1;
221             }
222
223           /* Ditto for the x/y position. */
224           if ((wid->allocation.x != pos_data->scrollbar_x) ||
225               (wid->allocation.y != pos_data->scrollbar_y))
226             {
227               gtk_fixed_move (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
228                               wid,
229                               pos_data->scrollbar_x,
230                               pos_data->scrollbar_y);
231               modified_p = 1;
232             }
233
234           adj->lower = pos_data->minimum;
235           adj->upper = pos_data->maximum;
236           adj->page_increment = pos_data->slider_size + 1;
237           adj->step_increment = w->max_line_len - 1;
238           adj->page_size = pos_data->slider_size + 1;
239           adj->value = pos_data->slider_position;
240
241           /* But, if we didn't resize or move the scrollbar, the
242              widget will not get redrawn correctly when the user
243              scrolls around in the XEmacs frame manually.  So we
244              update the slider manually here.
245           */
246           if (!modified_p)
247             gtk_range_slider_update (GTK_RANGE (wid));
248
249           instance->scrollbar_instance_changed = 0;
250         }
251
252       if (!managed)
253         {
254           gtk_widget_show (wid);
255           update_one_widget_scrollbar_pointer (w, wid);
256         }
257     }
258   else if (managed)
259     {
260       gtk_widget_hide (wid);
261     }
262 }
263
264 enum gtk_scrollbar_loop
265 {
266   GTK_FIND_SCROLLBAR_WINDOW_MIRROR,
267   GTK_SET_SCROLLBAR_POINTER,
268   GTK_WINDOW_IS_SCROLLBAR,
269   GTK_UPDATE_FRAME_SCROLLBARS
270 };
271
272 static struct window_mirror *
273 gtk_scrollbar_loop (enum gtk_scrollbar_loop type, Lisp_Object window,
274                     struct window_mirror *mir,
275                     GUI_ID id, GdkWindow *x_win)
276 {
277   struct window_mirror *retval = NULL;
278
279   while (mir)
280     {
281       struct scrollbar_instance *vinstance = mir->scrollbar_vertical_instance;
282       struct scrollbar_instance *hinstance = mir->scrollbar_horizontal_instance;
283       struct window *w = XWINDOW (window);
284
285       if (mir->vchild)
286         retval = gtk_scrollbar_loop (type, w->vchild, mir->vchild, id, x_win);
287       else if (mir->hchild)
288         retval = gtk_scrollbar_loop (type, w->hchild, mir->hchild, id, x_win);
289       if (retval)
290         return retval;
291
292       if (hinstance || vinstance)
293         {
294           switch (type)
295             {
296             case GTK_FIND_SCROLLBAR_WINDOW_MIRROR:
297               if ((vinstance && SCROLLBAR_GTK_ID (vinstance) == id) ||
298                   (hinstance && SCROLLBAR_GTK_ID (hinstance) == id))
299                 return mir;
300               break;
301             case GTK_UPDATE_FRAME_SCROLLBARS:
302               if (!mir->vchild && !mir->hchild)
303                 update_window_scrollbars (w, mir, 1, 0);
304               break;
305             case GTK_SET_SCROLLBAR_POINTER:
306               if (!mir->vchild && !mir->hchild)
307                 {
308                   GtkWidget *widget;
309
310                   widget = SCROLLBAR_GTK_WIDGET (hinstance);
311                   if (widget && GTK_WIDGET_MAPPED (widget))
312                     update_one_widget_scrollbar_pointer (w, widget);
313
314                   widget = SCROLLBAR_GTK_WIDGET (vinstance);
315                   if (widget && GTK_WIDGET_MAPPED (widget))
316                     update_one_widget_scrollbar_pointer (w, widget);
317                 }
318               break;
319             case GTK_WINDOW_IS_SCROLLBAR:
320               if (!mir->vchild && !mir->hchild)
321                 {
322                   GtkWidget *widget;
323
324                   widget = SCROLLBAR_GTK_WIDGET (hinstance);
325                   if (widget && GTK_WIDGET_MAPPED (widget) &&
326                       GET_GTK_WIDGET_WINDOW (widget) == x_win)
327                     return (struct window_mirror *) 1;
328
329                   widget = SCROLLBAR_GTK_WIDGET (vinstance);
330                   if (widget && GTK_WIDGET_MAPPED (widget) &&
331                       GET_GTK_WIDGET_WINDOW (widget) == x_win)
332                     return (struct window_mirror *) 1;
333                 }
334               break;
335             default:
336               abort ();
337             }
338         }
339
340       mir = mir->next;
341       window = w->next;
342     }
343
344   return NULL;
345 }
346
347 /* Used by callbacks. */
348 static struct window_mirror *
349 find_scrollbar_window_mirror (struct frame *f, GUI_ID id)
350 {
351   if (f->mirror_dirty)
352     update_frame_window_mirror (f);
353   return gtk_scrollbar_loop (GTK_FIND_SCROLLBAR_WINDOW_MIRROR, f->root_window,
354                              f->root_mirror, id, (GdkWindow *) NULL);
355 }
356
357 static gboolean
358 scrollbar_cb (GtkAdjustment *adj, gpointer user_data)
359 {
360   /* This function can GC */
361   int vertical = (int) user_data;
362   struct frame *f = gtk_object_get_data (GTK_OBJECT (adj), "xemacs::frame");
363   struct scrollbar_instance *instance = gtk_object_get_data (GTK_OBJECT (adj), "xemacs::sb_instance");
364   GUI_ID id = (GUI_ID) gtk_object_get_data (GTK_OBJECT (adj), "xemacs::gui_id");
365   Lisp_Object win, frame;
366   struct window_mirror *mirror;
367   Lisp_Object event_type = Qnil;
368   Lisp_Object event_data = Qnil;
369
370   if (!f)
371     return(FALSE);
372
373   mirror = find_scrollbar_window_mirror (f, id);
374   if (!mirror)
375     return(FALSE);
376   
377   win = real_window (mirror, 1);
378
379   if (NILP (win))
380     return(FALSE);
381   instance = vertical ? mirror->scrollbar_vertical_instance : mirror->scrollbar_horizontal_instance;
382   frame = WINDOW_FRAME (XWINDOW (win));
383
384   inhibit_slider_size_change = 0;
385   switch (GTK_RANGE (SCROLLBAR_GTK_WIDGET (instance))->scroll_type)
386     {
387     case GTK_SCROLL_PAGE_BACKWARD:
388       event_type = vertical ? Qscrollbar_page_up : Qscrollbar_page_left;
389       event_data = Fcons (win, Qnil);
390       break;
391     case GTK_SCROLL_PAGE_FORWARD:
392       event_type = vertical ? Qscrollbar_page_down : Qscrollbar_page_right;
393       event_data = Fcons (win, Qnil);
394       break;
395     case GTK_SCROLL_STEP_FORWARD:
396       event_type = vertical ? Qscrollbar_line_down : Qscrollbar_char_right;
397       event_data = win;
398       break;
399     case GTK_SCROLL_STEP_BACKWARD:
400       event_type = vertical ? Qscrollbar_line_up : Qscrollbar_char_left;
401       event_data = win;
402       break;
403     case GTK_SCROLL_NONE:
404     case GTK_SCROLL_JUMP:
405       inhibit_slider_size_change = 1;
406       event_type = vertical ? Qscrollbar_vertical_drag : Qscrollbar_horizontal_drag;
407       event_data = Fcons (win, make_int ((int)adj->value));
408       break;
409     default:
410       abort();
411     }
412
413   signal_special_gtk_user_event (frame, event_type, event_data);
414
415   return (TRUE);
416 }
417
418 static void
419 gtk_scrollbar_pointer_changed_in_window (struct window *w)
420 {
421   Lisp_Object window;
422
423   XSETWINDOW (window, w);
424   gtk_scrollbar_loop (GTK_SET_SCROLLBAR_POINTER, window, find_window_mirror (w),
425                       0, (GdkWindow *) NULL);
426 }
427
428 /* #### BILL!!! This comment is not true for Gtk - should it be? */
429 /* Make sure that all scrollbars on frame are up-to-date.  Called
430    directly from gtk_set_frame_properties in frame-gtk.c*/
431 void
432 gtk_update_frame_scrollbars (struct frame *f)
433 {
434   /* Consider this code to be "in_display" so that we abort() if Fsignal()
435      gets called. */
436   in_display++;
437   gtk_scrollbar_loop (GTK_UPDATE_FRAME_SCROLLBARS, f->root_window, f->root_mirror,
438                       0, (GdkWindow *) NULL);
439   in_display--;
440   if (in_display < 0) abort ();
441 }
442
443 #ifdef MEMORY_USAGE_STATS
444 static int
445 gtk_compute_scrollbar_instance_usage (struct device *d,
446                                       struct scrollbar_instance *inst,
447                                       struct overhead_stats *ovstats)
448 {
449   int total = 0;
450
451   while (inst)
452     {
453       struct gtk_scrollbar_data *data =
454         (struct gtk_scrollbar_data *) inst->scrollbar_data;
455
456       total += malloced_storage_size (data, sizeof (*data), ovstats);
457       inst = inst->next;
458     }
459
460   return total;
461 }
462
463 #endif /* MEMORY_USAGE_STATS */
464
465
466 /************************************************************************/
467 /*                            initialization                            */
468 /************************************************************************/
469
470 void
471 console_type_create_scrollbar_gtk (void)
472 {
473   CONSOLE_HAS_METHOD (gtk, inhibit_scrollbar_slider_size_change);
474   CONSOLE_HAS_METHOD (gtk, free_scrollbar_instance);
475   CONSOLE_HAS_METHOD (gtk, release_scrollbar_instance);
476   CONSOLE_HAS_METHOD (gtk, create_scrollbar_instance);
477   CONSOLE_HAS_METHOD (gtk, update_scrollbar_instance_values);
478   CONSOLE_HAS_METHOD (gtk, update_scrollbar_instance_status);
479   CONSOLE_HAS_METHOD (gtk, scrollbar_pointer_changed_in_window);
480 #ifdef MEMORY_USAGE_STATS
481   CONSOLE_HAS_METHOD (gtk, compute_scrollbar_instance_usage);
482 #endif /* MEMORY_USAGE_STATS */
483 }
484
485 void
486 vars_of_scrollbar_gtk (void)
487 {
488   Fprovide (intern ("gtk-scrollbars"));
489 }