update.
[chise/xemacs-chise.git.1] / src / scrollbar-gtk.c
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>.
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 (instance->scrollbar_data)
61     {
62       if (SCROLLBAR_GTK_WIDGET (instance))
63         {
64           gtk_widget_hide_all (SCROLLBAR_GTK_WIDGET (instance));
65           gtk_widget_destroy (SCROLLBAR_GTK_WIDGET (instance));
66         }
67
68       xfree (instance->scrollbar_data);
69     }
70 }
71
72 /* A device method. */
73 static void
74 gtk_release_scrollbar_instance (struct scrollbar_instance *instance)
75 {
76     /* It won't hurt to hide it all the time, will it? */
77     gtk_widget_hide_all (SCROLLBAR_GTK_WIDGET (instance));
78 }
79
80 static gboolean
81 scrollbar_drag_hack_cb (GtkWidget *w, GdkEventButton *ev, gpointer v)
82 {
83   vertical_drag_in_progress = (int) v;
84   inhibit_slider_size_change = (int) v;
85   return (FALSE);
86 }
87
88 /* A device method. */
89 static void
90 gtk_create_scrollbar_instance (struct frame *f, int vertical,
91                                struct scrollbar_instance *instance)
92 {
93   GtkAdjustment *adj = GTK_ADJUSTMENT (gtk_adjustment_new (0,0,0,0,0,0));
94   GtkScrollbar *sb = NULL;
95
96   /* initialize the X specific data section. */
97   instance->scrollbar_data = xnew_and_zero (struct gtk_scrollbar_data);
98
99   SCROLLBAR_GTK_ID (instance) = new_gui_id ();
100   SCROLLBAR_GTK_VDRAG_ORIG_VALUE (instance) = -1;
101   SCROLLBAR_GTK_LAST_VALUE (instance) = adj->value;
102
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);
107
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));
116 #endif  
117   SCROLLBAR_GTK_WIDGET (instance) = GTK_WIDGET (sb);
118
119   gtk_signal_connect (GTK_OBJECT (adj),"value-changed",
120                       GTK_SIGNAL_FUNC (scrollbar_cb), (gpointer) vertical);
121
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);
126
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));
129 }
130
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;                       \
136   }
137
138 /* A device method. */
139 /* #### The -1 check is such a hack. */
140 static void
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,
146                                       int new_slider_size,
147                                       int new_slider_position,
148                                       int new_scrollbar_width,
149                                       int new_scrollbar_height,
150                                       int new_scrollbar_x, int new_scrollbar_y)
151 {
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);
162
163   if (w && !vertical_drag_in_progress)
164     {
165       int new_vov = SCROLLBAR_GTK_POS_DATA (inst).slider_position;
166       int new_vows = marker_position (w->start[CURRENT_DISP]);
167
168       if (SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) != new_vov)
169         {
170           SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) = new_vov;
171           inst->scrollbar_instance_changed = 1;
172         }
173       if (SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) != new_vows)
174         {
175           SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) = new_vows;
176           inst->scrollbar_instance_changed = 1;
177         }
178     }
179 }
180
181 /* Used by gtk_update_scrollbar_instance_status. */
182 static void
183 update_one_widget_scrollbar_pointer (struct window *w, GtkWidget *wid)
184 {
185   if (!wid->window)
186     gtk_widget_realize (wid);
187
188   if (POINTER_IMAGE_INSTANCEP (w->scrollbar_pointer))
189     {
190       gdk_window_set_cursor (GET_GTK_WIDGET_WINDOW (wid),
191                              XIMAGE_INSTANCE_GTK_CURSOR (w->scrollbar_pointer));
192       gdk_flush ();
193     }
194 }
195
196 /* A device method. */
197 static void
198 gtk_update_scrollbar_instance_status (struct window *w, int active, int size,
199                                       struct scrollbar_instance *instance)
200 {
201   struct frame *f = XFRAME (w->frame);
202   GtkWidget *wid = SCROLLBAR_GTK_WIDGET (instance);
203   gboolean managed = GTK_WIDGET_MAPPED (wid);
204
205   if (active && size)
206     {
207       if (instance->scrollbar_instance_changed)
208         {
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);
212           int modified_p = 0;
213
214           /* We do not want to update the size all the time if we can
215              help it.  This cuts down on annoying flicker.
216           */
217           if ((wid->allocation.width != pos_data->scrollbar_width) ||
218               (wid->allocation.height != pos_data->scrollbar_height))
219             {
220               gtk_widget_set_usize (wid,
221                                     pos_data->scrollbar_width,
222                                     pos_data->scrollbar_height);
223
224               /*
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.
233                */
234
235               wid->allocation.width = pos_data->scrollbar_width;
236               wid->allocation.height = pos_data->scrollbar_height;
237
238               modified_p = 1;
239             }
240
241           /* Ditto for the x/y position. */
242           if ((wid->allocation.x != pos_data->scrollbar_x) ||
243               (wid->allocation.y != pos_data->scrollbar_y))
244             {
245               gtk_fixed_move (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
246                               wid,
247                               pos_data->scrollbar_x,
248                               pos_data->scrollbar_y);
249
250               /*
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.
259                */
260
261               wid->allocation.x = pos_data->scrollbar_x;
262               wid->allocation.y = pos_data->scrollbar_y;
263
264               modified_p = 1;
265             }
266
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;
273
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.
278           */
279           if (!modified_p)
280             gtk_range_slider_update (GTK_RANGE (wid));
281
282           instance->scrollbar_instance_changed = 0;
283         }
284
285       if (!managed)
286         {
287           gtk_widget_show (wid);
288           update_one_widget_scrollbar_pointer (w, wid);
289         }
290     }
291   else if (managed)
292     {
293       gtk_widget_hide (wid);
294     }
295 }
296
297 enum gtk_scrollbar_loop
298 {
299   GTK_FIND_SCROLLBAR_WINDOW_MIRROR,
300   GTK_SET_SCROLLBAR_POINTER,
301   GTK_WINDOW_IS_SCROLLBAR,
302   GTK_UPDATE_FRAME_SCROLLBARS
303 };
304
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)
309 {
310   struct window_mirror *retval = NULL;
311
312   while (mir)
313     {
314       struct scrollbar_instance *vinstance = mir->scrollbar_vertical_instance;
315       struct scrollbar_instance *hinstance = mir->scrollbar_horizontal_instance;
316       struct window *w = XWINDOW (window);
317
318       if (mir->vchild)
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);
322       if (retval)
323         return retval;
324
325       if (hinstance || vinstance)
326         {
327           switch (type)
328             {
329             case GTK_FIND_SCROLLBAR_WINDOW_MIRROR:
330               if ((vinstance && SCROLLBAR_GTK_ID (vinstance) == id) ||
331                   (hinstance && SCROLLBAR_GTK_ID (hinstance) == id))
332                 return mir;
333               break;
334             case GTK_UPDATE_FRAME_SCROLLBARS:
335               if (!mir->vchild && !mir->hchild)
336                 update_window_scrollbars (w, mir, 1, 0);
337               break;
338             case GTK_SET_SCROLLBAR_POINTER:
339               if (!mir->vchild && !mir->hchild)
340                 {
341                   GtkWidget *widget;
342
343                   widget = SCROLLBAR_GTK_WIDGET (hinstance);
344                   if (widget && GTK_WIDGET_MAPPED (widget))
345                     update_one_widget_scrollbar_pointer (w, widget);
346
347                   widget = SCROLLBAR_GTK_WIDGET (vinstance);
348                   if (widget && GTK_WIDGET_MAPPED (widget))
349                     update_one_widget_scrollbar_pointer (w, widget);
350                 }
351               break;
352             case GTK_WINDOW_IS_SCROLLBAR:
353               if (!mir->vchild && !mir->hchild)
354                 {
355                   GtkWidget *widget;
356
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;
361
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;
366                 }
367               break;
368             default:
369               ABORT ();
370             }
371         }
372
373       mir = mir->next;
374       window = w->next;
375     }
376
377   return NULL;
378 }
379
380 /* Used by callbacks. */
381 static struct window_mirror *
382 find_scrollbar_window_mirror (struct frame *f, GUI_ID id)
383 {
384   if (f->mirror_dirty)
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);
388 }
389
390 static gboolean
391 scrollbar_cb (GtkAdjustment *adj, gpointer user_data)
392 {
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;
402
403   if (!f)
404     return(FALSE);
405
406   mirror = find_scrollbar_window_mirror (f, id);
407   if (!mirror)
408     return(FALSE);
409   
410   win = real_window (mirror, 1);
411
412   if (NILP (win))
413     return(FALSE);
414   instance = vertical ? mirror->scrollbar_vertical_instance : mirror->scrollbar_horizontal_instance;
415   frame = WINDOW_FRAME (XWINDOW (win));
416
417   inhibit_slider_size_change = 0;
418   switch (GTK_RANGE (SCROLLBAR_GTK_WIDGET (instance))->scroll_type)
419     {
420     case GTK_SCROLL_PAGE_BACKWARD:
421       event_type = vertical ? Qscrollbar_page_up : Qscrollbar_page_left;
422       event_data = Fcons (win, Qnil);
423       break;
424     case GTK_SCROLL_PAGE_FORWARD:
425       event_type = vertical ? Qscrollbar_page_down : Qscrollbar_page_right;
426       event_data = Fcons (win, Qnil);
427       break;
428     case GTK_SCROLL_STEP_FORWARD:
429       event_type = vertical ? Qscrollbar_line_down : Qscrollbar_char_right;
430       event_data = win;
431       break;
432     case GTK_SCROLL_STEP_BACKWARD:
433       event_type = vertical ? Qscrollbar_line_up : Qscrollbar_char_left;
434       event_data = win;
435       break;
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));
441       break;
442     default:
443       ABORT();
444     }
445
446   signal_special_gtk_user_event (frame, event_type, event_data);
447
448   return (TRUE);
449 }
450
451 static void
452 gtk_scrollbar_pointer_changed_in_window (struct window *w)
453 {
454   Lisp_Object window;
455
456   XSETWINDOW (window, w);
457   gtk_scrollbar_loop (GTK_SET_SCROLLBAR_POINTER, window, find_window_mirror (w),
458                       0, (GdkWindow *) NULL);
459 }
460
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*/
464 void
465 gtk_update_frame_scrollbars (struct frame *f)
466 {
467   /* Consider this code to be "in_display" so that we ABORT() if Fsignal()
468      gets called. */
469   in_display++;
470   gtk_scrollbar_loop (GTK_UPDATE_FRAME_SCROLLBARS, f->root_window, f->root_mirror,
471                       0, (GdkWindow *) NULL);
472   in_display--;
473   if (in_display < 0) ABORT ();
474 }
475
476 #ifdef MEMORY_USAGE_STATS
477 static int
478 gtk_compute_scrollbar_instance_usage (struct device *d,
479                                       struct scrollbar_instance *inst,
480                                       struct overhead_stats *ovstats)
481 {
482   int total = 0;
483
484   while (inst)
485     {
486       struct gtk_scrollbar_data *data =
487         (struct gtk_scrollbar_data *) inst->scrollbar_data;
488
489       total += malloced_storage_size (data, sizeof (*data), ovstats);
490       inst = inst->next;
491     }
492
493   return total;
494 }
495
496 #endif /* MEMORY_USAGE_STATS */
497
498
499 /************************************************************************/
500 /*                            initialization                            */
501 /************************************************************************/
502
503 void
504 console_type_create_scrollbar_gtk (void)
505 {
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 */
516 }
517
518 void
519 vars_of_scrollbar_gtk (void)
520 {
521   Fprovide (intern ("gtk-scrollbars"));
522 }