This commit was manufactured by cvs2svn to create branch 'XEmacs-21_4'.
[chise/xemacs-chise.git-] / 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   SCROLLBAR_GTK_WIDGET (instance) = GTK_WIDGET (sb);
107
108   gtk_signal_connect (GTK_OBJECT (adj),"value-changed",
109                       GTK_SIGNAL_FUNC (scrollbar_cb), (gpointer) vertical);
110
111   gtk_signal_connect (GTK_OBJECT (sb), "button-press-event",
112                       GTK_SIGNAL_FUNC (scrollbar_drag_hack_cb), (gpointer) 1);
113   gtk_signal_connect (GTK_OBJECT (sb), "button-release-event",
114                       GTK_SIGNAL_FUNC (scrollbar_drag_hack_cb), (gpointer) 0);
115
116   gtk_fixed_put (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)), SCROLLBAR_GTK_WIDGET (instance), 0, 0);
117   gtk_widget_hide (SCROLLBAR_GTK_WIDGET (instance));
118 }
119
120 #define UPDATE_DATA_FIELD(field)                                \
121   if (new_##field >= 0 &&                                       \
122       SCROLLBAR_GTK_POS_DATA (inst).field != new_##field) {     \
123     SCROLLBAR_GTK_POS_DATA (inst).field = new_##field;          \
124     inst->scrollbar_instance_changed = 1;                       \
125   }
126
127 /* A device method. */
128 /* #### The -1 check is such a hack. */
129 static void
130 gtk_update_scrollbar_instance_values (struct window *w,
131                                       struct scrollbar_instance *inst,
132                                       int new_line_increment,
133                                       int new_page_increment,
134                                       int new_minimum, int new_maximum,
135                                       int new_slider_size,
136                                       int new_slider_position,
137                                       int new_scrollbar_width,
138                                       int new_scrollbar_height,
139                                       int new_scrollbar_x, int new_scrollbar_y)
140 {
141   UPDATE_DATA_FIELD (line_increment);
142   UPDATE_DATA_FIELD (page_increment);
143   UPDATE_DATA_FIELD (minimum);
144   UPDATE_DATA_FIELD (maximum);
145   UPDATE_DATA_FIELD (slider_size);
146   UPDATE_DATA_FIELD (slider_position);
147   UPDATE_DATA_FIELD (scrollbar_width);
148   UPDATE_DATA_FIELD (scrollbar_height);
149   UPDATE_DATA_FIELD (scrollbar_x);
150   UPDATE_DATA_FIELD (scrollbar_y);
151
152   if (w && !vertical_drag_in_progress)
153     {
154       int new_vov = SCROLLBAR_GTK_POS_DATA (inst).slider_position;
155       int new_vows = marker_position (w->start[CURRENT_DISP]);
156
157       if (SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) != new_vov)
158         {
159           SCROLLBAR_GTK_VDRAG_ORIG_VALUE (inst) = new_vov;
160           inst->scrollbar_instance_changed = 1;
161         }
162       if (SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) != new_vows)
163         {
164           SCROLLBAR_GTK_VDRAG_ORIG_WINDOW_START (inst) = new_vows;
165           inst->scrollbar_instance_changed = 1;
166         }
167     }
168 }
169
170 /* Used by gtk_update_scrollbar_instance_status. */
171 static void
172 update_one_widget_scrollbar_pointer (struct window *w, GtkWidget *wid)
173 {
174   if (!wid->window)
175     gtk_widget_realize (wid);
176
177   if (POINTER_IMAGE_INSTANCEP (w->scrollbar_pointer))
178     {
179       gdk_window_set_cursor (GET_GTK_WIDGET_WINDOW (wid),
180                              XIMAGE_INSTANCE_GTK_CURSOR (w->scrollbar_pointer));
181       gdk_flush ();
182     }
183 }
184
185 /* A device method. */
186 static void
187 gtk_update_scrollbar_instance_status (struct window *w, int active, int size,
188                                       struct scrollbar_instance *instance)
189 {
190   struct frame *f = XFRAME (w->frame);
191   GtkWidget *wid = SCROLLBAR_GTK_WIDGET (instance);
192   gboolean managed = GTK_WIDGET_MAPPED (wid);
193
194   if (active && size)
195     {
196       if (instance->scrollbar_instance_changed)
197         {
198           /* Need to set the height, width, and position of the widget */
199           GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (wid));
200           scrollbar_values *pos_data = & SCROLLBAR_GTK_POS_DATA (instance);
201           int modified_p = 0;
202
203           /* We do not want to update the size all the time if we can
204              help it.  This cuts down on annoying flicker.
205           */
206           if ((wid->allocation.width != pos_data->scrollbar_width) ||
207               (wid->allocation.height != pos_data->scrollbar_height))
208             {
209               gtk_widget_set_usize (wid,
210                                     pos_data->scrollbar_width,
211                                     pos_data->scrollbar_height);
212               modified_p = 1;
213             }
214
215           /* Ditto for the x/y position. */
216           if ((wid->allocation.x != pos_data->scrollbar_x) ||
217               (wid->allocation.y != pos_data->scrollbar_y))
218             {
219               gtk_fixed_move (GTK_FIXED (FRAME_GTK_TEXT_WIDGET (f)),
220                               wid,
221                               pos_data->scrollbar_x,
222                               pos_data->scrollbar_y);
223               modified_p = 1;
224             }
225
226           adj->lower = pos_data->minimum;
227           adj->upper = pos_data->maximum;
228           adj->page_increment = pos_data->slider_size + 1;
229           adj->step_increment = w->max_line_len - 1;
230           adj->page_size = pos_data->slider_size + 1;
231           adj->value = pos_data->slider_position;
232
233           /* But, if we didn't resize or move the scrollbar, the
234              widget will not get redrawn correctly when the user
235              scrolls around in the XEmacs frame manually.  So we
236              update the slider manually here.
237           */
238           if (!modified_p)
239             gtk_range_slider_update (GTK_RANGE (wid));
240
241           instance->scrollbar_instance_changed = 0;
242         }
243
244       if (!managed)
245         {
246           gtk_widget_show (wid);
247           update_one_widget_scrollbar_pointer (w, wid);
248         }
249     }
250   else if (managed)
251     {
252       gtk_widget_hide (wid);
253     }
254 }
255
256 enum gtk_scrollbar_loop
257 {
258   GTK_FIND_SCROLLBAR_WINDOW_MIRROR,
259   GTK_SET_SCROLLBAR_POINTER,
260   GTK_WINDOW_IS_SCROLLBAR,
261   GTK_UPDATE_FRAME_SCROLLBARS
262 };
263
264 static struct window_mirror *
265 gtk_scrollbar_loop (enum gtk_scrollbar_loop type, Lisp_Object window,
266                     struct window_mirror *mir,
267                     GUI_ID id, GdkWindow *x_win)
268 {
269   struct window_mirror *retval = NULL;
270
271   while (mir)
272     {
273       struct scrollbar_instance *vinstance = mir->scrollbar_vertical_instance;
274       struct scrollbar_instance *hinstance = mir->scrollbar_horizontal_instance;
275       struct window *w = XWINDOW (window);
276
277       if (mir->vchild)
278         retval = gtk_scrollbar_loop (type, w->vchild, mir->vchild, id, x_win);
279       else if (mir->hchild)
280         retval = gtk_scrollbar_loop (type, w->hchild, mir->hchild, id, x_win);
281       if (retval)
282         return retval;
283
284       if (hinstance || vinstance)
285         {
286           switch (type)
287             {
288             case GTK_FIND_SCROLLBAR_WINDOW_MIRROR:
289               if ((vinstance && SCROLLBAR_GTK_ID (vinstance) == id) ||
290                   (hinstance && SCROLLBAR_GTK_ID (hinstance) == id))
291                 return mir;
292               break;
293             case GTK_UPDATE_FRAME_SCROLLBARS:
294               if (!mir->vchild && !mir->hchild)
295                 update_window_scrollbars (w, mir, 1, 0);
296               break;
297             case GTK_SET_SCROLLBAR_POINTER:
298               if (!mir->vchild && !mir->hchild)
299                 {
300                   GtkWidget *widget;
301
302                   widget = SCROLLBAR_GTK_WIDGET (hinstance);
303                   if (widget && GTK_WIDGET_MAPPED (widget))
304                     update_one_widget_scrollbar_pointer (w, widget);
305
306                   widget = SCROLLBAR_GTK_WIDGET (vinstance);
307                   if (widget && GTK_WIDGET_MAPPED (widget))
308                     update_one_widget_scrollbar_pointer (w, widget);
309                 }
310               break;
311             case GTK_WINDOW_IS_SCROLLBAR:
312               if (!mir->vchild && !mir->hchild)
313                 {
314                   GtkWidget *widget;
315
316                   widget = SCROLLBAR_GTK_WIDGET (hinstance);
317                   if (widget && GTK_WIDGET_MAPPED (widget) &&
318                       GET_GTK_WIDGET_WINDOW (widget) == x_win)
319                     return (struct window_mirror *) 1;
320
321                   widget = SCROLLBAR_GTK_WIDGET (vinstance);
322                   if (widget && GTK_WIDGET_MAPPED (widget) &&
323                       GET_GTK_WIDGET_WINDOW (widget) == x_win)
324                     return (struct window_mirror *) 1;
325                 }
326               break;
327             default:
328               abort ();
329             }
330         }
331
332       mir = mir->next;
333       window = w->next;
334     }
335
336   return NULL;
337 }
338
339 /* Used by callbacks. */
340 static struct window_mirror *
341 find_scrollbar_window_mirror (struct frame *f, GUI_ID id)
342 {
343   if (f->mirror_dirty)
344     update_frame_window_mirror (f);
345   return gtk_scrollbar_loop (GTK_FIND_SCROLLBAR_WINDOW_MIRROR, f->root_window,
346                              f->root_mirror, id, (GdkWindow *) NULL);
347 }
348
349 static gboolean
350 scrollbar_cb (GtkAdjustment *adj, gpointer user_data)
351 {
352   /* This function can GC */
353   int vertical = (int) user_data;
354   struct frame *f = gtk_object_get_data (GTK_OBJECT (adj), "xemacs::frame");
355   struct scrollbar_instance *instance = gtk_object_get_data (GTK_OBJECT (adj), "xemacs::sb_instance");
356   GUI_ID id = (GUI_ID) gtk_object_get_data (GTK_OBJECT (adj), "xemacs::gui_id");
357   Lisp_Object win, frame;
358   struct window_mirror *mirror;
359   Lisp_Object event_type = Qnil;
360   Lisp_Object event_data = Qnil;
361
362   if (!f)
363     return(FALSE);
364
365   mirror = find_scrollbar_window_mirror (f, id);
366   if (!mirror)
367     return(FALSE);
368   
369   win = real_window (mirror, 1);
370
371   if (NILP (win))
372     return(FALSE);
373   instance = vertical ? mirror->scrollbar_vertical_instance : mirror->scrollbar_horizontal_instance;
374   frame = WINDOW_FRAME (XWINDOW (win));
375
376   inhibit_slider_size_change = 0;
377   switch (GTK_RANGE (SCROLLBAR_GTK_WIDGET (instance))->scroll_type)
378     {
379     case GTK_SCROLL_PAGE_BACKWARD:
380       event_type = vertical ? Qscrollbar_page_up : Qscrollbar_page_left;
381       event_data = Fcons (win, Qnil);
382       break;
383     case GTK_SCROLL_PAGE_FORWARD:
384       event_type = vertical ? Qscrollbar_page_down : Qscrollbar_page_right;
385       event_data = Fcons (win, Qnil);
386       break;
387     case GTK_SCROLL_STEP_FORWARD:
388       event_type = vertical ? Qscrollbar_line_down : Qscrollbar_char_right;
389       event_data = win;
390       break;
391     case GTK_SCROLL_STEP_BACKWARD:
392       event_type = vertical ? Qscrollbar_line_up : Qscrollbar_char_left;
393       event_data = win;
394       break;
395     case GTK_SCROLL_NONE:
396     case GTK_SCROLL_JUMP:
397       inhibit_slider_size_change = 1;
398       event_type = vertical ? Qscrollbar_vertical_drag : Qscrollbar_horizontal_drag;
399       event_data = Fcons (win, make_int ((int)adj->value));
400       break;
401     default:
402       abort();
403     }
404
405   signal_special_gtk_user_event (frame, event_type, event_data);
406
407   return (TRUE);
408 }
409
410 static void
411 gtk_scrollbar_pointer_changed_in_window (struct window *w)
412 {
413   Lisp_Object window;
414
415   XSETWINDOW (window, w);
416   gtk_scrollbar_loop (GTK_SET_SCROLLBAR_POINTER, window, find_window_mirror (w),
417                       0, (GdkWindow *) NULL);
418 }
419
420 /* #### BILL!!! This comment is not true for Gtk - should it be? */
421 /* Make sure that all scrollbars on frame are up-to-date.  Called
422    directly from gtk_set_frame_properties in frame-gtk.c*/
423 void
424 gtk_update_frame_scrollbars (struct frame *f)
425 {
426   /* Consider this code to be "in_display" so that we abort() if Fsignal()
427      gets called. */
428   in_display++;
429   gtk_scrollbar_loop (GTK_UPDATE_FRAME_SCROLLBARS, f->root_window, f->root_mirror,
430                       0, (GdkWindow *) NULL);
431   in_display--;
432   if (in_display < 0) abort ();
433 }
434
435 #ifdef MEMORY_USAGE_STATS
436 static int
437 gtk_compute_scrollbar_instance_usage (struct device *d,
438                                       struct scrollbar_instance *inst,
439                                       struct overhead_stats *ovstats)
440 {
441   int total = 0;
442
443   while (inst)
444     {
445       struct gtk_scrollbar_data *data =
446         (struct gtk_scrollbar_data *) inst->scrollbar_data;
447
448       total += malloced_storage_size (data, sizeof (*data), ovstats);
449       inst = inst->next;
450     }
451
452   return total;
453 }
454
455 #endif /* MEMORY_USAGE_STATS */
456
457
458 /************************************************************************/
459 /*                            initialization                            */
460 /************************************************************************/
461
462 void
463 console_type_create_scrollbar_gtk (void)
464 {
465   CONSOLE_HAS_METHOD (gtk, inhibit_scrollbar_slider_size_change);
466   CONSOLE_HAS_METHOD (gtk, free_scrollbar_instance);
467   CONSOLE_HAS_METHOD (gtk, release_scrollbar_instance);
468   CONSOLE_HAS_METHOD (gtk, create_scrollbar_instance);
469   CONSOLE_HAS_METHOD (gtk, update_scrollbar_instance_values);
470   CONSOLE_HAS_METHOD (gtk, update_scrollbar_instance_status);
471   CONSOLE_HAS_METHOD (gtk, scrollbar_pointer_changed_in_window);
472 #ifdef MEMORY_USAGE_STATS
473   CONSOLE_HAS_METHOD (gtk, compute_scrollbar_instance_usage);
474 #endif /* MEMORY_USAGE_STATS */
475 }
476
477 void
478 vars_of_scrollbar_gtk (void)
479 {
480   Fprovide (intern ("gtk-scrollbars"));
481 }