XEmacs 21.2.33 "Melpomene".
[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->name = xstrdup (wv->name);
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       free_widget_value_tree (wv);
283     }
284   else if (managed)
285     {
286 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
287       /* This isn't needed with Athena Scrollbars.  It might not be needed */
288       /* with Motif scrollbars (it is apparently needed with Lesstif). */
289       XtUngrabKeyboard (SCROLLBAR_X_WIDGET (instance), CurrentTime);
290 #endif
291       XtUnmanageChild (SCROLLBAR_X_WIDGET (instance));
292     }
293 }
294
295 enum x_scrollbar_loop
296 {
297   X_FIND_SCROLLBAR_WINDOW_MIRROR,
298   X_SET_SCROLLBAR_POINTER,
299   X_WINDOW_IS_SCROLLBAR,
300   X_UPDATE_FRAME_SCROLLBARS
301 };
302
303 static struct window_mirror *
304 x_scrollbar_loop (enum x_scrollbar_loop type, Lisp_Object window,
305                   struct window_mirror *mir,
306                   LWLIB_ID id, Window x_win)
307 {
308   struct window_mirror *retval = NULL;
309
310   while (mir)
311     {
312       struct scrollbar_instance *vinstance = mir->scrollbar_vertical_instance;
313       struct scrollbar_instance *hinstance = mir->scrollbar_horizontal_instance;
314       struct window *w = XWINDOW (window);
315
316       if (mir->vchild)
317         retval = x_scrollbar_loop (type, w->vchild, mir->vchild, id, x_win);
318       else if (mir->hchild)
319         retval = x_scrollbar_loop (type, w->hchild, mir->hchild, id, x_win);
320       if (retval)
321         return retval;
322
323       if (hinstance || vinstance)
324         {
325           switch (type)
326             {
327             case X_FIND_SCROLLBAR_WINDOW_MIRROR:
328               if ((vinstance && SCROLLBAR_X_ID (vinstance) == id) ||
329                   (hinstance && SCROLLBAR_X_ID (hinstance) == id))
330                 return mir;
331               break;
332             case X_UPDATE_FRAME_SCROLLBARS:
333               if (!mir->vchild && !mir->hchild)
334                 update_window_scrollbars (w, mir, 1, 0);
335               break;
336             case X_SET_SCROLLBAR_POINTER:
337               if (!mir->vchild && !mir->hchild)
338                 {
339                   Widget widget;
340
341                   widget = SCROLLBAR_X_WIDGET (hinstance);
342                   if (widget && XtIsManaged (widget))
343                     update_one_widget_scrollbar_pointer (w, widget);
344
345                   widget = SCROLLBAR_X_WIDGET (vinstance);
346                   if (widget && XtIsManaged (widget))
347                     update_one_widget_scrollbar_pointer (w, widget);
348                 }
349               break;
350             case X_WINDOW_IS_SCROLLBAR:
351               if (!mir->vchild && !mir->hchild)
352                 {
353                   Widget widget;
354
355                   widget = SCROLLBAR_X_WIDGET (hinstance);
356                   if (widget && XtIsManaged (widget) &&
357                       XtWindow (widget) == x_win)
358                     return (struct window_mirror *) 1;
359
360                   widget = SCROLLBAR_X_WIDGET (vinstance);
361                   if (widget && XtIsManaged (widget) &&
362                       XtWindow (widget) == x_win)
363                     return (struct window_mirror *) 1;
364                 }
365               break;
366             default:
367               abort ();
368             }
369         }
370
371       mir = mir->next;
372       window = w->next;
373     }
374
375   return NULL;
376 }
377
378 /* Used by callbacks. */
379 static struct window_mirror *
380 find_scrollbar_window_mirror (struct frame *f, LWLIB_ID id)
381 {
382   if (f->mirror_dirty)
383     update_frame_window_mirror (f);
384   return x_scrollbar_loop (X_FIND_SCROLLBAR_WINDOW_MIRROR, f->root_window,
385                            f->root_mirror, id, (Window) NULL);
386 }
387
388 /*
389  * This is the only callback provided for vertical scrollbars.  It
390  * should be able to handle all of the scrollbar events in
391  * scroll_action (see lwlib.h).  The client data will be of type
392  * scroll_event (see lwlib.h). */
393 static void
394 x_update_vertical_scrollbar_callback (Widget widget, LWLIB_ID id,
395                                       XtPointer client_data)
396 {
397   /* This function can GC */
398   scroll_event *data = (scroll_event *) client_data;
399   struct device *d = get_device_from_display (XtDisplay (widget));
400   struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
401   Lisp_Object win, frame;
402   struct scrollbar_instance *instance;
403   struct window_mirror *mirror;
404
405   if (!f)
406     return;
407
408   mirror = find_scrollbar_window_mirror (f, id);
409   if (!mirror)
410     return;
411   
412   win = real_window (mirror, 1);
413
414   if (NILP (win))
415     return;
416   instance = mirror->scrollbar_vertical_instance;
417   frame = WINDOW_FRAME (XWINDOW (win));
418
419   /* It seems that this is necessary whenever signal_special_Xt_user_event()
420      is called.  #### Why??? */
421   DEVICE_X_MOUSE_TIMESTAMP (d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (d);
422
423   switch (data->action)
424     {
425     case SCROLLBAR_LINE_UP:
426       signal_special_Xt_user_event (frame, Qscrollbar_line_up, win);
427       break;
428
429     case SCROLLBAR_LINE_DOWN:
430       signal_special_Xt_user_event (frame, Qscrollbar_line_down, win);
431       break;
432
433       /* The Athena scrollbar paging behavior is that of xterms.
434          Depending on where you click the size of the page varies.
435          Motif always does a standard Emacs page. */
436     case SCROLLBAR_PAGE_UP:
437 #if !defined (LWLIB_SCROLLBARS_MOTIF) && !defined (LWLIB_SCROLLBARS_LUCID) && \
438     !defined (LWLIB_SCROLLBARS_ATHENA3D)
439       {
440         double tmp = ((double) data->slider_value /
441                       (double) SCROLLBAR_X_POS_DATA(instance).scrollbar_height);
442         double line = tmp *
443           (double) window_displayed_height (XWINDOW (win));
444
445         if (line > -1.0)
446           line = -1.0;
447         signal_special_Xt_user_event (frame, Qscrollbar_page_up,
448                                       Fcons (win, make_int ((int) line)));
449       }
450 #else
451       signal_special_Xt_user_event (frame, Qscrollbar_page_up,
452                                     Fcons (win, Qnil));
453 #endif
454       break;
455
456     case SCROLLBAR_PAGE_DOWN:
457 #if !defined (LWLIB_SCROLLBARS_MOTIF) && !defined (LWLIB_SCROLLBARS_LUCID) && \
458     !defined (LWLIB_SCROLLBARS_ATHENA3D)
459       {
460         double tmp = ((double) data->slider_value /
461                       (double) SCROLLBAR_X_POS_DATA(instance).scrollbar_height);
462         double line = tmp *
463           (double) window_displayed_height (XWINDOW (win));
464
465         if (SCROLLBAR_X_POS_DATA(instance).maximum >
466             (SCROLLBAR_X_POS_DATA(instance).slider_size + SCROLLBAR_X_POS_DATA(instance).slider_position))
467           {
468             if (line < 1.0)
469               line = 1.0;
470             signal_special_Xt_user_event (frame, Qscrollbar_page_down,
471                                           Fcons (win,
472                                                  make_int ((int) line)));
473           }
474       }
475 #else
476       signal_special_Xt_user_event (frame, Qscrollbar_page_down,
477                                     Fcons (win, Qnil));
478 #endif
479       break;
480
481     case SCROLLBAR_TOP:
482       signal_special_Xt_user_event (frame, Qscrollbar_to_top, win);
483       break;
484
485     case SCROLLBAR_BOTTOM:
486       signal_special_Xt_user_event (frame, Qscrollbar_to_bottom, win);
487       break;
488
489
490     case SCROLLBAR_CHANGE:
491       inhibit_slider_size_change = 0;
492 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
493       vertical_drag_in_progress = 0;
494       SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) = data->slider_value;
495       SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance) =
496         XINT (Fwindow_start (win));
497 #else
498       stupid_vertical_scrollbar_drag_hack = 0;
499 #endif
500       break;
501
502     case SCROLLBAR_DRAG:
503       {
504         int value;
505
506         inhibit_slider_size_change = 1;
507
508 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
509         /* Doing drags with Motif-like scrollbars is a mess, since we
510            want to avoid having the window position jump when you
511            first grab the scrollbar, but we also want to ensure that
512            you can scroll all the way to the top or bottom of the
513            buffer.  This can all be replaced with something sane when
514            we get line-based scrolling. */
515
516         vertical_drag_in_progress = 1;
517
518         if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) < 0)
519           {
520             SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) = data->slider_value;
521             SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance) =
522               XINT (Fwindow_start (win));
523           }
524
525         /* Could replace this piecewise linear scrolling with a
526            quadratic through the three points, but I'm not sure that
527            would feel any nicer in practice. */
528         if (data->slider_value < SCROLLBAR_X_VDRAG_ORIG_VALUE (instance))
529           {
530             /* We've dragged up; slide linearly from original position to
531                window-start=data.minimum, slider-value=data.minimum. */
532
533             if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
534                 <= SCROLLBAR_X_POS_DATA (instance).minimum)
535               {
536                 /* shouldn't get here, but just in case */
537                 value = SCROLLBAR_X_POS_DATA (instance).minimum;
538               }
539             else
540               {
541                 value = (int)
542                   (SCROLLBAR_X_POS_DATA (instance).minimum
543                    + (((double)
544                        (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance)
545                         - SCROLLBAR_X_POS_DATA (instance).minimum)
546                        * (data->slider_value -
547                           SCROLLBAR_X_POS_DATA (instance).minimum))
548                       / (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
549                          - SCROLLBAR_X_POS_DATA (instance).minimum)));
550               }
551           }
552         else
553           {
554             /* We've dragged down; slide linearly from original position to
555                window-start=data.maximum, slider-value=data.maximum. */
556
557             if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
558                 >= (SCROLLBAR_X_POS_DATA (instance).maximum -
559                     SCROLLBAR_X_POS_DATA (instance).slider_size))
560               {
561                 /* avoid divide by zero */
562                 value = SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance);
563               }
564             else
565               {
566                 value = (int)
567                   (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance)
568                    + (((double)
569                        (SCROLLBAR_X_POS_DATA (instance).maximum
570                         - SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance))
571                        * (data->slider_value
572                           - SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)))
573                       / (SCROLLBAR_X_POS_DATA (instance).maximum
574                          - SCROLLBAR_X_POS_DATA (instance).slider_size
575                          - SCROLLBAR_X_VDRAG_ORIG_VALUE (instance))));
576               }
577           }
578 #else
579         stupid_vertical_scrollbar_drag_hack = 0;
580         value = data->slider_value;
581 #endif
582
583         if (value >= SCROLLBAR_X_POS_DATA (instance).maximum)
584           value = SCROLLBAR_X_POS_DATA (instance).maximum - 1;
585         if (value < SCROLLBAR_X_POS_DATA (instance).minimum)
586           value = SCROLLBAR_X_POS_DATA (instance).minimum;
587
588         signal_special_Xt_user_event (frame, Qscrollbar_vertical_drag,
589                                       Fcons (win, make_int (value)));
590       }
591       break;
592
593     }
594 }
595
596 /*
597  * This is the only callback provided for horizontal scrollbars.  It
598  * should be able to handle all of the scrollbar events in
599  * scroll_action (see lwlib.h).  The client data will be of type
600  * scroll_event (see lwlib.h). */
601 static void
602 x_update_horizontal_scrollbar_callback (Widget widget, LWLIB_ID id,
603                                         XtPointer client_data)
604 {
605   scroll_event *data = (scroll_event *) client_data;
606   struct device *d = get_device_from_display (XtDisplay (widget));
607   struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
608   Lisp_Object win, frame;
609   struct window_mirror *mirror;
610
611   if (!f)
612     return;
613
614   mirror = find_scrollbar_window_mirror (f, id);
615   if (!mirror)
616     return;
617   
618   win = real_window (mirror, 1);
619
620   if (NILP (win))
621     return;
622   frame = WINDOW_FRAME (XWINDOW (win));
623
624   /* It seems that this is necessary whenever signal_special_Xt_user_event()
625      is called.  #### Why??? */
626   DEVICE_X_MOUSE_TIMESTAMP (d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (d);
627
628   switch (data->action)
629     {
630     case SCROLLBAR_LINE_UP:
631       signal_special_Xt_user_event (frame, Qscrollbar_char_left, win);
632       break;
633     case SCROLLBAR_LINE_DOWN:
634       signal_special_Xt_user_event (frame, Qscrollbar_char_right, win);
635       break;
636     case SCROLLBAR_PAGE_UP:
637       signal_special_Xt_user_event (frame, Qscrollbar_page_left, win);
638       break;
639     case SCROLLBAR_PAGE_DOWN:
640       signal_special_Xt_user_event (frame, Qscrollbar_page_right, win);
641       break;
642     case SCROLLBAR_TOP:
643       signal_special_Xt_user_event (frame, Qscrollbar_to_left, win);
644       break;
645     case SCROLLBAR_BOTTOM:
646       signal_special_Xt_user_event (frame, Qscrollbar_to_right, win);
647       break;
648     case SCROLLBAR_CHANGE:
649       inhibit_slider_size_change = 0;
650       break;
651     case SCROLLBAR_DRAG:
652       inhibit_slider_size_change = 1;
653       /* #### Fix the damn toolkit code so they all work the same way.
654          Lucid is the one mostly wrong.*/
655 #if defined (LWLIB_SCROLLBARS_LUCID) || defined (LWLIB_SCROLLBARS_ATHENA3D)
656       signal_special_Xt_user_event (frame, Qscrollbar_horizontal_drag,
657                                     (Fcons
658                                      (win, make_int (data->slider_value))));
659 #else
660       signal_special_Xt_user_event (frame, Qscrollbar_horizontal_drag,
661                                     (Fcons
662                                      (win,
663                                       make_int (data->slider_value - 1))));
664 #endif
665       break;
666     default:
667       break;
668     }
669 }
670
671 static void
672 x_scrollbar_pointer_changed_in_window (struct window *w)
673 {
674   Lisp_Object window;
675
676   XSETWINDOW (window, w);
677   x_scrollbar_loop (X_SET_SCROLLBAR_POINTER, window, find_window_mirror (w),
678                     0, (Window) NULL);
679 }
680
681 /* Make sure that all scrollbars on frame are up-to-date.  Called
682    directly from x_set_frame_properties in frame-x.c*/
683 void
684 x_update_frame_scrollbars (struct frame *f)
685 {
686   /* Consider this code to be "in_display" so that we abort() if Fsignal()
687      gets called. */
688   in_display++;
689   x_scrollbar_loop (X_UPDATE_FRAME_SCROLLBARS, f->root_window, f->root_mirror,
690                     0, (Window) NULL);
691   in_display--;
692   if (in_display < 0) abort ();
693 }
694
695 #ifdef MEMORY_USAGE_STATS
696
697 static int
698 x_compute_scrollbar_instance_usage (struct device *d,
699                                     struct scrollbar_instance *inst,
700                                     struct overhead_stats *ovstats)
701 {
702   int total = 0;
703
704   while (inst)
705     {
706       struct x_scrollbar_data *data =
707         (struct x_scrollbar_data *) inst->scrollbar_data;
708
709       total += malloced_storage_size (data, sizeof (*data), ovstats);
710       total += malloced_storage_size (data->name, 1 + strlen (data->name),
711                                       ovstats);
712       inst = inst->next;
713     }
714
715   return total;
716 }
717
718 #endif /* MEMORY_USAGE_STATS */
719
720
721 /************************************************************************/
722 /*                            initialization                            */
723 /************************************************************************/
724
725 void
726 console_type_create_scrollbar_x (void)
727 {
728   CONSOLE_HAS_METHOD (x, inhibit_scrollbar_slider_size_change);
729   CONSOLE_HAS_METHOD (x, free_scrollbar_instance);
730   CONSOLE_HAS_METHOD (x, release_scrollbar_instance);
731   CONSOLE_HAS_METHOD (x, create_scrollbar_instance);
732   CONSOLE_HAS_METHOD (x, update_scrollbar_instance_values);
733   CONSOLE_HAS_METHOD (x, update_scrollbar_instance_status);
734   CONSOLE_HAS_METHOD (x, scrollbar_pointer_changed_in_window);
735 #ifdef MEMORY_USAGE_STATS
736   CONSOLE_HAS_METHOD (x, compute_scrollbar_instance_usage);
737 #endif /* MEMORY_USAGE_STATS */
738 }
739
740 void
741 reinit_vars_of_scrollbar_x (void)
742 {
743   stupid_vertical_scrollbar_drag_hack = 1;
744 }
745
746 void
747 vars_of_scrollbar_x (void)
748 {
749   reinit_vars_of_scrollbar_x ();
750
751 #if defined (LWLIB_SCROLLBARS_LUCID)
752   Fprovide (intern ("lucid-scrollbars"));
753 #elif defined (LWLIB_SCROLLBARS_MOTIF)
754   Fprovide (intern ("motif-scrollbars"));
755 #elif defined (LWLIB_SCROLLBARS_ATHENA)
756   Fprovide (intern ("athena-scrollbars"));
757 #endif
758 }