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