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