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