XEmacs 21.2.5
[chise/xemacs-chise.git.1] / src / scrollbar-x.c
1 /* scrollbar implementation -- X interface.
2    Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
3    Copyright (C) 1994 Amdahl 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
26 #include <config.h>
27 #include "lisp.h"
28
29 #include "console-x.h"
30 #include "glyphs-x.h"
31 #include "gui-x.h"
32 #include "scrollbar-x.h"
33
34 #include "frame.h"
35 #include "window.h"
36
37 static void x_update_vertical_scrollbar_callback (Widget widget, LWLIB_ID id,
38                                                   XtPointer client_data);
39 static void x_update_horizontal_scrollbar_callback (Widget widget, LWLIB_ID id,
40                                                     XtPointer client_data);
41
42 /* Used to prevent changing the size of the slider while drag
43    scrolling, under Motif.  This is necessary because the Motif
44    scrollbar is incredibly stupid about updating the slider and causes
45    lots of flicker if it is done too often.  */
46 static int inhibit_slider_size_change;
47 int stupid_vertical_scrollbar_drag_hack;
48
49 /* Doesn't work with athena */
50 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
51 static int vertical_drag_in_progress;
52 #endif
53
54 \f
55 /* A device method. */
56 static int
57 x_inhibit_scrollbar_slider_size_change (void)
58 {
59   /* Doesn't work with Athena */
60 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
61   return inhibit_slider_size_change;
62 #else
63   return 0;
64 #endif
65 }
66
67 /* A device method. */
68 static void
69 x_free_scrollbar_instance (struct scrollbar_instance *instance)
70 {
71   if (SCROLLBAR_X_NAME (instance))
72     xfree (SCROLLBAR_X_NAME (instance));
73
74   if (SCROLLBAR_X_WIDGET (instance))
75     {
76       if (XtIsManaged (SCROLLBAR_X_WIDGET (instance)))
77         XtUnmanageChild (SCROLLBAR_X_WIDGET (instance));
78
79       lw_destroy_all_widgets (SCROLLBAR_X_ID (instance));
80     }
81
82   if (instance->scrollbar_data)
83     xfree (instance->scrollbar_data);
84 }
85
86 /* A device method. */
87 static void
88 x_release_scrollbar_instance (struct scrollbar_instance *instance)
89 {
90   if (XtIsManaged (SCROLLBAR_X_WIDGET (instance)))
91     XtUnmanageChild (SCROLLBAR_X_WIDGET (instance));
92 }
93
94 /* A device method. */
95 static void
96 x_create_scrollbar_instance (struct frame *f, int vertical,
97                              struct scrollbar_instance *instance)
98 {
99   char buffer[32];
100
101   /* initialize the X specific data section. */
102   instance->scrollbar_data = xnew_and_zero (struct x_scrollbar_data);
103
104   SCROLLBAR_X_ID (instance) = new_lwlib_id ();
105   sprintf (buffer, "scrollbar_%d", SCROLLBAR_X_ID (instance));
106   SCROLLBAR_X_NAME (instance) = xstrdup (buffer);
107 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID) || \
108     defined (LWLIB_SCROLLBARS_ATHENA3D)
109   SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) = -1;
110 #endif
111
112   if (vertical)
113     {
114       SCROLLBAR_X_WIDGET (instance) =
115         lw_create_widget ("vertical-scrollbar", SCROLLBAR_X_NAME (instance),
116                           SCROLLBAR_X_ID (instance),
117                           NULL, FRAME_X_CONTAINER_WIDGET (f), 0,
118                           x_update_vertical_scrollbar_callback, NULL, NULL);
119     }
120   else
121     {
122       SCROLLBAR_X_WIDGET (instance) =
123         lw_create_widget ("horizontal-scrollbar", SCROLLBAR_X_NAME (instance),
124                           SCROLLBAR_X_ID (instance),
125                           NULL, FRAME_X_CONTAINER_WIDGET (f), 0,
126                           x_update_horizontal_scrollbar_callback, NULL, NULL);
127     }
128 }
129
130 #define UPDATE_DATA_FIELD(field)                                \
131   if (new_##field >= 0 &&                                       \
132       SCROLLBAR_X_POS_DATA (inst).field != new_##field) {       \
133     SCROLLBAR_X_POS_DATA (inst).field = new_##field;            \
134     inst->scrollbar_instance_changed = 1;                       \
135   }
136
137 /* A device method. */
138 /* #### The -1 check is such a hack. */
139 static void
140 x_update_scrollbar_instance_values (struct window *w,
141                                     struct scrollbar_instance *inst,
142                                     int new_line_increment,
143                                     int new_page_increment,
144                                     int new_minimum, int new_maximum,
145                                     int new_slider_size,
146                                     int new_slider_position,
147                                     int new_scrollbar_width,
148                                     int new_scrollbar_height,
149                                     int new_scrollbar_x, int new_scrollbar_y)
150 {
151   UPDATE_DATA_FIELD (line_increment);
152   UPDATE_DATA_FIELD (page_increment);
153   UPDATE_DATA_FIELD (minimum);
154   UPDATE_DATA_FIELD (maximum);
155   UPDATE_DATA_FIELD (slider_size);
156   UPDATE_DATA_FIELD (slider_position);
157   UPDATE_DATA_FIELD (scrollbar_width);
158   UPDATE_DATA_FIELD (scrollbar_height);
159   UPDATE_DATA_FIELD (scrollbar_x);
160   UPDATE_DATA_FIELD (scrollbar_y);
161
162   /* This doesn't work with Athena, why? */
163 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
164   if (w && !vertical_drag_in_progress)
165     {
166       int new_vov = SCROLLBAR_X_POS_DATA (inst).slider_position;
167       int new_vows = marker_position (w->start[CURRENT_DISP]);
168
169       if (SCROLLBAR_X_VDRAG_ORIG_VALUE (inst) != new_vov)
170         {
171           SCROLLBAR_X_VDRAG_ORIG_VALUE (inst) = new_vov;
172           inst->scrollbar_instance_changed = 1;
173         }
174       if (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (inst) != new_vows)
175         {
176           SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (inst) = new_vows;
177           inst->scrollbar_instance_changed = 1;
178         }
179     }
180 #endif
181 }
182
183 /* Used by x_update_scrollbar_instance_status. */
184 static void
185 update_one_scrollbar_bs (struct frame *f, Widget sb_widget)
186 {
187   Boolean use_backing_store;
188
189   Xt_GET_VALUE (FRAME_X_TEXT_WIDGET (f), XtNuseBackingStore, &use_backing_store);
190
191   if (use_backing_store && sb_widget)
192     {
193       unsigned long mask = CWBackingStore;
194       XSetWindowAttributes attrs;
195
196       attrs.backing_store = Always;
197       XChangeWindowAttributes (XtDisplay (sb_widget),
198                                XtWindow (sb_widget),
199                                mask,
200                                &attrs);
201     }
202 }
203
204 /* Create a widget value structure for passing down to lwlib so that
205    it can update the scrollbar widgets.  Used by
206    x_update_scrollbar_instance_status. */
207 static widget_value *
208 scrollbar_instance_to_widget_value (struct scrollbar_instance *instance)
209 {
210   widget_value *wv;
211
212   wv = xmalloc_widget_value ();
213   /* #### maybe should add malloc_scrollbar_values to resource these? */
214   wv->scrollbar_data = xnew (scrollbar_values);
215
216   wv->name = SCROLLBAR_X_NAME (instance);
217   wv->value = 0;
218   wv->key = 0;
219   wv->enabled = instance->scrollbar_is_active;
220   wv->selected = 0;
221   wv->call_data = NULL;
222
223   *wv->scrollbar_data = SCROLLBAR_X_POS_DATA (instance);
224
225   wv->next = NULL;
226
227   return wv;
228 }
229
230 /* Used by x_update_scrollbar_instance_status. */
231 static void
232 update_one_widget_scrollbar_pointer (struct window *w, Widget wid)
233 {
234   if (POINTER_IMAGE_INSTANCEP (w->scrollbar_pointer))
235     {
236       XDefineCursor (XtDisplay (wid), XtWindow (wid),
237                      XIMAGE_INSTANCE_X_CURSOR (w->scrollbar_pointer));
238       XSync (XtDisplay (wid), False);
239     }
240 }
241
242 /* A device method. */
243 static void
244 x_update_scrollbar_instance_status (struct window *w, int active, int size,
245                                     struct scrollbar_instance *instance)
246 {
247   struct frame *f = XFRAME (w->frame);
248   Boolean managed = XtIsManaged (SCROLLBAR_X_WIDGET (instance));
249
250   if (active && size)
251     {
252       widget_value *wv = scrollbar_instance_to_widget_value (instance);
253
254       if (instance->scrollbar_instance_changed)
255         {
256           lw_modify_all_widgets (SCROLLBAR_X_ID (instance), wv, 0);
257           instance->scrollbar_instance_changed = 0;
258         }
259
260       if (!managed)
261         {
262           XtManageChild (SCROLLBAR_X_WIDGET (instance));
263           if (XtWindow (SCROLLBAR_X_WIDGET (instance)))
264             {
265               /* Raise this window so that it's visible on top of the
266                  text window below it. */
267               XRaiseWindow (XtDisplay (SCROLLBAR_X_WIDGET (instance)),
268                             XtWindow (SCROLLBAR_X_WIDGET (instance)));
269               update_one_widget_scrollbar_pointer
270                 (w, SCROLLBAR_X_WIDGET (instance));
271               if (!SCROLLBAR_X_BACKING_STORE_INITIALIZED (instance))
272                 {
273                   update_one_scrollbar_bs (f, SCROLLBAR_X_WIDGET (instance));
274                   SCROLLBAR_X_BACKING_STORE_INITIALIZED (instance) = 1;
275                 }
276             }
277         }
278
279       if (!wv->scrollbar_data) abort ();
280       xfree (wv->scrollbar_data);
281       wv->scrollbar_data = 0;
282       free_widget_value (wv);
283     }
284   else if (managed)
285     {
286 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
287       /* This isn't needed with Athena Scrollbars.  It might not be needed */
288       /* with Motif scrollbars (it is apparently needed with Lesstif). */
289       XtUngrabKeyboard (SCROLLBAR_X_WIDGET (instance), CurrentTime);
290 #endif
291       XtUnmanageChild (SCROLLBAR_X_WIDGET (instance));
292     }
293 }
294
295 enum x_scrollbar_loop
296 {
297   X_FIND_SCROLLBAR_WINDOW_MIRROR,
298   X_SET_SCROLLBAR_POINTER,
299   X_WINDOW_IS_SCROLLBAR,
300   X_UPDATE_FRAME_SCROLLBARS
301 };
302
303 static struct window_mirror *
304 x_scrollbar_loop (enum x_scrollbar_loop type, Lisp_Object window,
305                   struct window_mirror *mir,
306                   LWLIB_ID id, Window x_win)
307 {
308   struct window_mirror *retval = NULL;
309
310   while (mir)
311     {
312       struct scrollbar_instance *vinstance = mir->scrollbar_vertical_instance;
313       struct scrollbar_instance *hinstance = mir->scrollbar_horizontal_instance;
314       struct window *w = XWINDOW (window);
315
316       if (mir->vchild)
317         retval = x_scrollbar_loop (type, w->vchild, mir->vchild, id, x_win);
318       else if (mir->hchild)
319         retval = x_scrollbar_loop (type, w->hchild, mir->hchild, id, x_win);
320       if (retval)
321         return retval;
322
323       if (hinstance || vinstance)
324         {
325           switch (type)
326             {
327             case X_FIND_SCROLLBAR_WINDOW_MIRROR:
328               if ((vinstance && SCROLLBAR_X_ID (vinstance) == id) ||
329                   (hinstance && SCROLLBAR_X_ID (hinstance) == id))
330                 return mir;
331               break;
332             case X_UPDATE_FRAME_SCROLLBARS:
333               if (!mir->vchild && !mir->hchild)
334                 update_window_scrollbars (w, mir, 1, 0);
335               break;
336             case X_SET_SCROLLBAR_POINTER:
337               if (!mir->vchild && !mir->hchild)
338                 {
339                   Widget widget;
340
341                   widget = SCROLLBAR_X_WIDGET (hinstance);
342                   if (widget && XtIsManaged (widget))
343                     update_one_widget_scrollbar_pointer (w, widget);
344
345                   widget = SCROLLBAR_X_WIDGET (vinstance);
346                   if (widget && XtIsManaged (widget))
347                     update_one_widget_scrollbar_pointer (w, widget);
348                 }
349               break;
350             case X_WINDOW_IS_SCROLLBAR:
351               if (!mir->vchild && !mir->hchild)
352                 {
353                   Widget widget;
354
355                   widget = SCROLLBAR_X_WIDGET (hinstance);
356                   if (widget && XtIsManaged (widget) &&
357                       XtWindow (widget) == x_win)
358                     return (struct window_mirror *) 1;
359
360                   widget = SCROLLBAR_X_WIDGET (vinstance);
361                   if (widget && XtIsManaged (widget) &&
362                       XtWindow (widget) == x_win)
363                     return (struct window_mirror *) 1;
364                 }
365               break;
366             default:
367               abort ();
368             }
369         }
370
371       mir = mir->next;
372       window = w->next;
373     }
374
375   return NULL;
376 }
377
378 /* Used by callbacks. */
379 static struct window_mirror *
380 find_scrollbar_window_mirror (struct frame *f, LWLIB_ID id)
381 {
382   if (f->mirror_dirty)
383     update_frame_window_mirror (f);
384   return x_scrollbar_loop (X_FIND_SCROLLBAR_WINDOW_MIRROR, f->root_window,
385                            f->root_mirror, id, (Window) NULL);
386 }
387
388 /*
389  * This is the only callback provided for vertical scrollbars.  It
390  * should be able to handle all of the scrollbar events in
391  * scroll_action (see lwlib.h).  The client data will be of type
392  * scroll_event (see lwlib.h). */
393 static void
394 x_update_vertical_scrollbar_callback (Widget widget, LWLIB_ID id,
395                                       XtPointer client_data)
396 {
397   /* This function can GC */
398   scroll_event *data = (scroll_event *) client_data;
399   struct device *d = get_device_from_display (XtDisplay (widget));
400   struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
401   Lisp_Object win, frame;
402   struct scrollbar_instance *instance;
403   struct window_mirror *mirror;
404
405   if (!f)
406     return;
407
408   mirror = find_scrollbar_window_mirror (f, id);
409   win = real_window (mirror, 1);
410
411   if (NILP (win))
412     return;
413   instance = mirror->scrollbar_vertical_instance;
414   frame = WINDOW_FRAME (XWINDOW (win));
415
416   /* It seems that this is necessary whenever signal_special_Xt_user_event()
417      is called.  #### Why??? */
418   DEVICE_X_MOUSE_TIMESTAMP (d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (d);
419
420   switch (data->action)
421     {
422     case SCROLLBAR_LINE_UP:
423       signal_special_Xt_user_event (frame, Qscrollbar_line_up, win);
424       break;
425
426     case SCROLLBAR_LINE_DOWN:
427       signal_special_Xt_user_event (frame, Qscrollbar_line_down, win);
428       break;
429
430       /* The Athena scrollbar paging behavior is that of xterms.
431          Depending on where you click the size of the page varies.
432          Motif always does a standard Emacs page. */
433     case SCROLLBAR_PAGE_UP:
434 #if !defined (LWLIB_SCROLLBARS_MOTIF) && !defined (LWLIB_SCROLLBARS_LUCID) && \
435     !defined (LWLIB_SCROLLBARS_ATHENA3D)
436       {
437         double tmp = ((double) data->slider_value /
438                       (double) SCROLLBAR_X_POS_DATA(instance).scrollbar_height);
439         double line = tmp *
440           (double) window_displayed_height (XWINDOW (win));
441
442         if (line > -1.0)
443           line = -1.0;
444         signal_special_Xt_user_event (frame, Qscrollbar_page_up,
445                                       Fcons (win, make_int ((int) line)));
446       }
447 #else
448       signal_special_Xt_user_event (frame, Qscrollbar_page_up,
449                                     Fcons (win, Qnil));
450 #endif
451       break;
452
453     case SCROLLBAR_PAGE_DOWN:
454 #if !defined (LWLIB_SCROLLBARS_MOTIF) && !defined (LWLIB_SCROLLBARS_LUCID) && \
455     !defined (LWLIB_SCROLLBARS_ATHENA3D)
456       {
457         double tmp = ((double) data->slider_value /
458                       (double) SCROLLBAR_X_POS_DATA(instance).scrollbar_height);
459         double line = tmp *
460           (double) window_displayed_height (XWINDOW (win));
461
462         if (SCROLLBAR_X_POS_DATA(instance).maximum >
463             (SCROLLBAR_X_POS_DATA(instance).slider_size + SCROLLBAR_X_POS_DATA(instance).slider_position))
464           {
465             if (line < 1.0)
466               line = 1.0;
467             signal_special_Xt_user_event (frame, Qscrollbar_page_down,
468                                           Fcons (win,
469                                                  make_int ((int) line)));
470           }
471       }
472 #else
473       signal_special_Xt_user_event (frame, Qscrollbar_page_down,
474                                     Fcons (win, Qnil));
475 #endif
476       break;
477
478     case SCROLLBAR_TOP:
479       signal_special_Xt_user_event (frame, Qscrollbar_to_top, win);
480       break;
481
482     case SCROLLBAR_BOTTOM:
483       signal_special_Xt_user_event (frame, Qscrollbar_to_bottom, win);
484       break;
485
486
487     case SCROLLBAR_CHANGE:
488       inhibit_slider_size_change = 0;
489 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
490       vertical_drag_in_progress = 0;
491       SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) = data->slider_value;
492       SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance) =
493         XINT (Fwindow_start (win));
494 #else
495       stupid_vertical_scrollbar_drag_hack = 0;
496 #endif
497       break;
498
499     case SCROLLBAR_DRAG:
500       {
501         int value;
502
503         inhibit_slider_size_change = 1;
504
505 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
506         /* Doing drags with Motif-like scrollbars is a mess, since we
507            want to avoid having the window position jump when you
508            first grab the scrollbar, but we also want to ensure that
509            you can scroll all the way to the top or bottom of the
510            buffer.  This can all be replaced with something sane when
511            we get line-based scrolling. */
512
513         vertical_drag_in_progress = 1;
514
515         if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) < 0)
516           {
517             SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) = data->slider_value;
518             SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance) =
519               XINT (Fwindow_start (win));
520           }
521
522         /* Could replace this piecewise linear scrolling with a
523            quadratic through the three points, but I'm not sure that
524            would feel any nicer in practice. */
525         if (data->slider_value < SCROLLBAR_X_VDRAG_ORIG_VALUE (instance))
526           {
527             /* We've dragged up; slide linearly from original position to
528                window-start=data.minimum, slider-value=data.minimum. */
529
530             if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
531                 <= SCROLLBAR_X_POS_DATA (instance).minimum)
532               {
533                 /* shouldn't get here, but just in case */
534                 value = SCROLLBAR_X_POS_DATA (instance).minimum;
535               }
536             else
537               {
538                 value = (int)
539                   (SCROLLBAR_X_POS_DATA (instance).minimum
540                    + (((double)
541                        (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance)
542                         - SCROLLBAR_X_POS_DATA (instance).minimum)
543                        * (data->slider_value -
544                           SCROLLBAR_X_POS_DATA (instance).minimum))
545                       / (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
546                          - SCROLLBAR_X_POS_DATA (instance).minimum)));
547               }
548           }
549         else
550           {
551             /* We've dragged down; slide linearly from original position to
552                window-start=data.maximum, slider-value=data.maximum. */
553
554             if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
555                 >= (SCROLLBAR_X_POS_DATA (instance).maximum -
556                     SCROLLBAR_X_POS_DATA (instance).slider_size))
557               {
558                 /* avoid divide by zero */
559                 value = SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance);
560               }
561             else
562               {
563                 value = (int)
564                   (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance)
565                    + (((double)
566                        (SCROLLBAR_X_POS_DATA (instance).maximum
567                         - SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance))
568                        * (data->slider_value
569                           - SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)))
570                       / (SCROLLBAR_X_POS_DATA (instance).maximum
571                          - SCROLLBAR_X_POS_DATA (instance).slider_size
572                          - SCROLLBAR_X_VDRAG_ORIG_VALUE (instance))));
573               }
574           }
575 #else
576         stupid_vertical_scrollbar_drag_hack = 0;
577         value = data->slider_value;
578 #endif
579
580         if (value >= SCROLLBAR_X_POS_DATA (instance).maximum)
581           value = SCROLLBAR_X_POS_DATA (instance).maximum - 1;
582         if (value < SCROLLBAR_X_POS_DATA (instance).minimum)
583           value = SCROLLBAR_X_POS_DATA (instance).minimum;
584
585         signal_special_Xt_user_event (frame, Qscrollbar_vertical_drag,
586                                       Fcons (win, make_int (value)));
587       }
588       break;
589
590     }
591 }
592
593 /*
594  * This is the only callback provided for horizontal scrollbars.  It
595  * should be able to handle all of the scrollbar events in
596  * scroll_action (see lwlib.h).  The client data will be of type
597  * scroll_event (see lwlib.h). */
598 static void
599 x_update_horizontal_scrollbar_callback (Widget widget, LWLIB_ID id,
600                                         XtPointer client_data)
601 {
602   scroll_event *data = (scroll_event *) client_data;
603   struct device *d = get_device_from_display (XtDisplay (widget));
604   struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
605   Lisp_Object win, frame;
606   struct window_mirror *mirror;
607
608   if (!f)
609     return;
610
611   mirror = find_scrollbar_window_mirror (f, id);
612   win = real_window (mirror, 1);
613
614   if (NILP (win))
615     return;
616   frame = WINDOW_FRAME (XWINDOW (win));
617
618   /* It seems that this is necessary whenever signal_special_Xt_user_event()
619      is called.  #### Why??? */
620   DEVICE_X_MOUSE_TIMESTAMP (d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (d);
621
622   switch (data->action)
623     {
624     case SCROLLBAR_LINE_UP:
625       signal_special_Xt_user_event (frame, Qscrollbar_char_left, win);
626       break;
627     case SCROLLBAR_LINE_DOWN:
628       signal_special_Xt_user_event (frame, Qscrollbar_char_right, win);
629       break;
630     case SCROLLBAR_PAGE_UP:
631       signal_special_Xt_user_event (frame, Qscrollbar_page_left, win);
632       break;
633     case SCROLLBAR_PAGE_DOWN:
634       signal_special_Xt_user_event (frame, Qscrollbar_page_right, win);
635       break;
636     case SCROLLBAR_TOP:
637       signal_special_Xt_user_event (frame, Qscrollbar_to_left, win);
638       break;
639     case SCROLLBAR_BOTTOM:
640       signal_special_Xt_user_event (frame, Qscrollbar_to_right, win);
641       break;
642     case SCROLLBAR_CHANGE:
643       inhibit_slider_size_change = 0;
644       break;
645     case SCROLLBAR_DRAG:
646       inhibit_slider_size_change = 1;
647       /* #### Fix the damn toolkit code so they all work the same way.
648          Lucid is the one mostly wrong.*/
649 #if defined (LWLIB_SCROLLBARS_LUCID) || defined (LWLIB_SCROLLBARS_ATHENA3D)
650       signal_special_Xt_user_event (frame, Qscrollbar_horizontal_drag,
651                                     (Fcons
652                                      (win, make_int (data->slider_value))));
653 #else
654       signal_special_Xt_user_event (frame, Qscrollbar_horizontal_drag,
655                                     (Fcons
656                                      (win,
657                                       make_int (data->slider_value - 1))));
658 #endif
659       break;
660     default:
661       break;
662     }
663 }
664
665 static void
666 x_scrollbar_pointer_changed_in_window (struct window *w)
667 {
668   Lisp_Object window;
669
670   XSETWINDOW (window, w);
671   x_scrollbar_loop (X_SET_SCROLLBAR_POINTER, window, find_window_mirror (w),
672                     0, (Window) NULL);
673 }
674
675 /* Called directly from x_any_window_to_frame in frame-x.c */
676 EMACS_INT
677 x_window_is_scrollbar (struct frame *f, Window win)
678 {
679   if (!FRAME_X_P (f))
680     return 0;
681
682   if (f->mirror_dirty)
683     update_frame_window_mirror (f);
684   return (EMACS_INT) x_scrollbar_loop (X_WINDOW_IS_SCROLLBAR, f->root_window,
685                                  f->root_mirror, 0, win);
686 }
687
688 /* Make sure that all scrollbars on frame are up-to-date.  Called
689    directly from x_set_frame_properties in frame-x.c*/
690 void
691 x_update_frame_scrollbars (struct frame *f)
692 {
693   /* Consider this code to be "in_display" so that we abort() if Fsignal()
694      gets called. */
695   in_display++;
696   x_scrollbar_loop (X_UPDATE_FRAME_SCROLLBARS, f->root_window, f->root_mirror,
697                     0, (Window) NULL);
698   in_display--;
699   if (in_display < 0) abort ();
700 }
701
702 #ifdef MEMORY_USAGE_STATS
703
704 static int
705 x_compute_scrollbar_instance_usage (struct device *d,
706                                     struct scrollbar_instance *inst,
707                                     struct overhead_stats *ovstats)
708 {
709   int total = 0;
710
711   while (inst)
712     {
713       struct x_scrollbar_data *data =
714         (struct x_scrollbar_data *) inst->scrollbar_data;
715
716       total += malloced_storage_size (data, sizeof (*data), ovstats);
717       total += malloced_storage_size (data->name, 1 + strlen (data->name),
718                                       ovstats);
719       inst = inst->next;
720     }
721
722   return total;
723 }
724
725 #endif /* MEMORY_USAGE_STATS */
726
727
728 /************************************************************************/
729 /*                            initialization                            */
730 /************************************************************************/
731
732 void
733 console_type_create_scrollbar_x (void)
734 {
735   CONSOLE_HAS_METHOD (x, inhibit_scrollbar_slider_size_change);
736   CONSOLE_HAS_METHOD (x, free_scrollbar_instance);
737   CONSOLE_HAS_METHOD (x, release_scrollbar_instance);
738   CONSOLE_HAS_METHOD (x, create_scrollbar_instance);
739   CONSOLE_HAS_METHOD (x, update_scrollbar_instance_values);
740   CONSOLE_HAS_METHOD (x, update_scrollbar_instance_status);
741   CONSOLE_HAS_METHOD (x, scrollbar_pointer_changed_in_window);
742 #ifdef MEMORY_USAGE_STATS
743   CONSOLE_HAS_METHOD (x, compute_scrollbar_instance_usage);
744 #endif /* MEMORY_USAGE_STATS */
745 }
746
747 void
748 vars_of_scrollbar_x (void)
749 {
750 #if defined (LWLIB_SCROLLBARS_LUCID)
751   Fprovide (intern ("lucid-scrollbars"));
752 #elif defined (LWLIB_SCROLLBARS_MOTIF)
753   Fprovide (intern ("motif-scrollbars"));
754 #elif defined (LWLIB_SCROLLBARS_ATHENA)
755   Fprovide (intern ("athena-scrollbars"));
756 #endif
757   stupid_vertical_scrollbar_drag_hack = 1;
758 }