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