Merge remote-tracking branch 'origin/master' into for-steve
[sxemacs] / src / ui / X11 / 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 SXEmacs
8
9 SXEmacs is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 SXEmacs is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
21
22
23 /* Synched up with: Not in FSF. */
24
25 /* This file Mule-ized (more like Mule-verified) by Ben Wing, 7-8-00. */
26
27 #include <config.h>
28 #include "lisp.h"
29
30 #include "console-x.h"
31 #include "EmacsFrame.h"
32 #include "glyphs-x.h"
33 #include "gui-x.h"
34 #include "scrollbar-x.h"
35
36 #include "ui/frame.h"
37 #include "ui/window.h"
38
39 static void x_update_vertical_scrollbar_callback(Widget widget, LWLIB_ID id,
40                                                  XtPointer client_data);
41 static void x_update_horizontal_scrollbar_callback(Widget widget, LWLIB_ID id,
42                                                    XtPointer client_data);
43
44 /* Used to prevent changing the size of the slider while drag
45    scrolling, under Motif.  This is necessary because the Motif
46    scrollbar is incredibly stupid about updating the slider and causes
47    lots of flicker if it is done too often.  */
48 static int inhibit_slider_size_change;
49 int stupid_vertical_scrollbar_drag_hack;
50
51 /* Doesn't work with athena */
52 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
53 static int vertical_drag_in_progress;
54 #endif
55 \f
56 /* A device method. */
57 static int x_inhibit_scrollbar_slider_size_change(void)
58 {
59         /* Doesn't work with Athena */
60 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
61         return inhibit_slider_size_change;
62 #else
63         return 0;
64 #endif
65 }
66
67 /* A device method. */
68 static void x_free_scrollbar_instance(struct scrollbar_instance *instance)
69 {
70         if ( instance && instance->scrollbar_data ) {
71
72                 if (SCROLLBAR_X_NAME(instance))
73                         xfree(SCROLLBAR_X_NAME(instance));
74
75                 if (SCROLLBAR_X_WIDGET(instance)) {
76                         if (XtIsManaged(SCROLLBAR_X_WIDGET(instance)))
77                                 XtUnmanageChild(SCROLLBAR_X_WIDGET(instance));
78
79                         lw_destroy_all_widgets(SCROLLBAR_X_ID(instance));
80                 }
81                 xfree(instance->scrollbar_data);
82         }
83 }
84
85 /* A device method. */
86 static void x_release_scrollbar_instance(struct scrollbar_instance *instance)
87 {
88         if (XtIsManaged(SCROLLBAR_X_WIDGET(instance)))
89                 XtUnmanageChild(SCROLLBAR_X_WIDGET(instance));
90 }
91
92 /* A device method. */
93 static void
94 x_create_scrollbar_instance(struct frame *f, int vertical,
95                             struct scrollbar_instance *instance)
96 {
97         char buffer[32];
98         int sz;
99
100         /* initialize the X specific data section. */
101         instance->scrollbar_data = xnew_and_zero(struct x_scrollbar_data);
102
103         SCROLLBAR_X_ID(instance) = new_lwlib_id();
104         sz = snprintf(buffer, sizeof(buffer), "scrollbar_%d", SCROLLBAR_X_ID(instance));
105         assert(sz >= 0 && (size_t)sz < sizeof(buffer));
106         SCROLLBAR_X_NAME(instance) = xstrdup(buffer);
107 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID) || \
108     defined (LWLIB_SCROLLBARS_ATHENA3D)
109         SCROLLBAR_X_VDRAG_ORIG_VALUE(instance) = -1;
110 #endif
111
112         if (vertical) {
113                 SCROLLBAR_X_WIDGET(instance) =
114                     lw_create_widget("vertical-scrollbar",
115                                      SCROLLBAR_X_NAME(instance),
116                                      SCROLLBAR_X_ID(instance), NULL,
117                                      FRAME_X_CONTAINER_WIDGET(f), 0,
118                                      x_update_vertical_scrollbar_callback, NULL,
119                                      NULL);
120         } else {
121                 SCROLLBAR_X_WIDGET(instance) =
122                     lw_create_widget("horizontal-scrollbar",
123                                      SCROLLBAR_X_NAME(instance),
124                                      SCROLLBAR_X_ID(instance), NULL,
125                                      FRAME_X_CONTAINER_WIDGET(f), 0,
126                                      x_update_horizontal_scrollbar_callback,
127                                      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                 int new_vov = SCROLLBAR_X_POS_DATA(inst).slider_position;
167                 int new_vows = marker_position(w->start[CURRENT_DISP]);
168
169                 if (SCROLLBAR_X_VDRAG_ORIG_VALUE(inst) != new_vov) {
170                         SCROLLBAR_X_VDRAG_ORIG_VALUE(inst) = new_vov;
171                         inst->scrollbar_instance_changed = 1;
172                 }
173                 if (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START(inst) != new_vows) {
174                         SCROLLBAR_X_VDRAG_ORIG_WINDOW_START(inst) = new_vows;
175                         inst->scrollbar_instance_changed = 1;
176                 }
177         }
178 #endif
179 }
180
181 /* Used by x_update_scrollbar_instance_status. */
182 static void update_one_scrollbar_bs(struct frame *f, Widget sb_widget)
183 {
184         Boolean use_backing_store;
185
186         Xt_GET_VALUE(FRAME_X_TEXT_WIDGET(f), XtNuseBackingStore,
187                      &use_backing_store);
188
189         if (use_backing_store && sb_widget) {
190                 unsigned long mask = CWBackingStore;
191                 XSetWindowAttributes attrs;
192
193                 attrs.backing_store = Always;
194                 XChangeWindowAttributes(XtDisplay(sb_widget),
195                                         XtWindow(sb_widget), mask, &attrs);
196         }
197 }
198
199 /* Create a widget value structure for passing down to lwlib so that
200    it can update the scrollbar widgets.  Used by
201    x_update_scrollbar_instance_status. */
202 static widget_value *scrollbar_instance_to_widget_value(struct
203                                                         scrollbar_instance
204                                                         *instance)
205 {
206         widget_value *wv;
207
208         wv = xmalloc_widget_value();
209         /* #### maybe should add malloc_scrollbar_values to resource these? */
210         wv->scrollbar_data = xnew(scrollbar_values);
211
212         wv->name = SCROLLBAR_X_NAME(instance);
213         wv->name = xstrdup(wv->name);
214         wv->value = 0;
215         wv->key = 0;
216         wv->enabled = instance->scrollbar_is_active;
217         wv->selected = 0;
218         wv->call_data = NULL;
219
220         *wv->scrollbar_data = SCROLLBAR_X_POS_DATA(instance);
221
222         wv->next = NULL;
223
224         return wv;
225 }
226
227 /* Used by x_update_scrollbar_instance_status. */
228 static void update_one_widget_scrollbar_pointer(struct window *w, Widget wid)
229 {
230         if (POINTER_IMAGE_INSTANCEP(w->scrollbar_pointer)) {
231                 XDefineCursor(XtDisplay(wid), XtWindow(wid),
232                               XIMAGE_INSTANCE_X_CURSOR(w->scrollbar_pointer));
233                 XSync(XtDisplay(wid), False);
234         }
235 }
236
237 /* A device method. */
238 static void
239 x_update_scrollbar_instance_status(struct window *w, int active, int size,
240                                    struct scrollbar_instance *instance)
241 {
242         struct frame *f = XFRAME(w->frame);
243         Boolean managed = XtIsManaged(SCROLLBAR_X_WIDGET(instance));
244
245         if (active && size) {
246                 widget_value *wv = scrollbar_instance_to_widget_value(instance);
247
248                 if (instance->scrollbar_instance_changed) {
249                         lw_modify_all_widgets(SCROLLBAR_X_ID(instance), wv, 0);
250                         instance->scrollbar_instance_changed = 0;
251                 }
252
253                 if (!managed) {
254                         XtManageChild(SCROLLBAR_X_WIDGET(instance));
255                         if (XtWindow(SCROLLBAR_X_WIDGET(instance))) {
256                                 /* Raise this window so that it's visible on top of the
257                                    text window below it. */
258                                 XRaiseWindow(XtDisplay
259                                              (SCROLLBAR_X_WIDGET(instance)),
260                                              XtWindow(SCROLLBAR_X_WIDGET
261                                                       (instance)));
262                                 update_one_widget_scrollbar_pointer(w,
263                                                                     SCROLLBAR_X_WIDGET
264                                                                     (instance));
265                                 if (!SCROLLBAR_X_BACKING_STORE_INITIALIZED
266                                     (instance)) {
267                                         update_one_scrollbar_bs(f,
268                                                                 SCROLLBAR_X_WIDGET
269                                                                 (instance));
270                                         SCROLLBAR_X_BACKING_STORE_INITIALIZED
271                                             (instance) = 1;
272                                 }
273                         }
274                 }
275
276                 if (!wv->scrollbar_data)
277                         abort();
278                 free_widget_value_tree(wv);
279         } else if (managed) {
280 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
281                 /* This isn't needed with Athena Scrollbars.  It might not be needed */
282                 /* with Motif scrollbars (it is apparently needed with Lesstif). */
283                 XtUngrabKeyboard(SCROLLBAR_X_WIDGET(instance), CurrentTime);
284 #endif
285                 XtUnmanageChild(SCROLLBAR_X_WIDGET(instance));
286         }
287 }
288
289 enum x_scrollbar_loop {
290         X_FIND_SCROLLBAR_WINDOW_MIRROR,
291         X_SET_SCROLLBAR_POINTER,
292         X_WINDOW_IS_SCROLLBAR,
293         X_UPDATE_FRAME_SCROLLBARS
294 };
295
296 static struct window_mirror *x_scrollbar_loop(enum x_scrollbar_loop type,
297                                               Lisp_Object window,
298                                               struct window_mirror *mir,
299                                               LWLIB_ID id, Window x_win)
300 {
301         struct window_mirror *retval = NULL;
302
303         while (mir) {
304                 struct scrollbar_instance *vinstance =
305                     mir->scrollbar_vertical_instance;
306                 struct scrollbar_instance *hinstance =
307                     mir->scrollbar_horizontal_instance;
308                 struct window *w = XWINDOW(window);
309
310                 if (mir->vchild)
311                         retval =
312                             x_scrollbar_loop(type, w->vchild, mir->vchild, id,
313                                              x_win);
314                 else if (mir->hchild)
315                         retval =
316                             x_scrollbar_loop(type, w->hchild, mir->hchild, id,
317                                              x_win);
318                 if (retval)
319                         return retval;
320
321                 if (hinstance || vinstance) {
322                         switch (type) {
323                         case X_FIND_SCROLLBAR_WINDOW_MIRROR:
324                                 if ((vinstance
325                                      && SCROLLBAR_X_ID(vinstance) == id)
326                                     || (hinstance
327                                         && SCROLLBAR_X_ID(hinstance) == id))
328                                         return mir;
329                                 break;
330                         case X_UPDATE_FRAME_SCROLLBARS:
331                                 if (!mir->vchild && !mir->hchild)
332                                         update_window_scrollbars(w, mir, 1, 0);
333                                 break;
334                         case X_SET_SCROLLBAR_POINTER:
335                                 if (!mir->vchild && !mir->hchild) {
336                                         Widget widget;
337
338                                         widget = SCROLLBAR_X_WIDGET(hinstance);
339                                         if (widget && XtIsManaged(widget))
340                                                 update_one_widget_scrollbar_pointer
341                                                     (w, widget);
342
343                                         widget = SCROLLBAR_X_WIDGET(vinstance);
344                                         if (widget && XtIsManaged(widget))
345                                                 update_one_widget_scrollbar_pointer
346                                                     (w, widget);
347                                 }
348                                 break;
349                         case X_WINDOW_IS_SCROLLBAR:
350                                 if (!mir->vchild && !mir->hchild) {
351                                         Widget widget;
352
353                                         widget = SCROLLBAR_X_WIDGET(hinstance);
354                                         if (widget && XtIsManaged(widget) &&
355                                             XtWindow(widget) == x_win)
356                                                 return (struct window_mirror *)
357                                                     1;
358
359                                         widget = SCROLLBAR_X_WIDGET(vinstance);
360                                         if (widget && XtIsManaged(widget) &&
361                                             XtWindow(widget) == x_win)
362                                                 return (struct window_mirror *)
363                                                     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 *find_scrollbar_window_mirror(struct frame *f,
380                                                           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         case SCROLLBAR_LINE_UP:
425                 signal_special_Xt_user_event(frame, Qscrollbar_line_up, win);
426                 break;
427
428         case SCROLLBAR_LINE_DOWN:
429                 signal_special_Xt_user_event(frame, Qscrollbar_line_down, win);
430                 break;
431
432                 /* The Athena scrollbar paging behavior is that of xterms.
433                    Depending on where you click the size of the page varies.
434                    Motif always does a standard Emacs page. */
435         case SCROLLBAR_PAGE_UP: {
436 #if !defined (LWLIB_SCROLLBARS_MOTIF) && !defined (LWLIB_SCROLLBARS_LUCID) && \
437         !defined (LWLIB_SCROLLBARS_ATHENA3D)
438                 double tmp = ((double)data->slider_value /
439                               (double)SCROLLBAR_X_POS_DATA(instance).
440                               scrollbar_height);
441                 double line =
442                         tmp * (double)window_displayed_height(XWINDOW(win));
443
444                 if (line > -1.0) {
445                         line = -1.0;
446                 }
447                 signal_special_Xt_user_event(frame, Qscrollbar_page_up,
448                                              Fcons(win, make_int((int)line)));
449 #else
450                 signal_special_Xt_user_event(frame, Qscrollbar_page_up,
451                                              Fcons(win, Qnil));
452 #endif
453         }
454                 break;
455
456         case SCROLLBAR_PAGE_DOWN: {
457 #if !defined (LWLIB_SCROLLBARS_MOTIF) && !defined (LWLIB_SCROLLBARS_LUCID) && \
458         !defined (LWLIB_SCROLLBARS_ATHENA3D)
459                 double tmp = ((double)data->slider_value /
460                               (double)SCROLLBAR_X_POS_DATA(instance).
461                               scrollbar_height);
462                 double line =
463                         tmp * (double)window_displayed_height(XWINDOW(win));
464
465                 if (SCROLLBAR_X_POS_DATA(instance).maximum >
466                     (SCROLLBAR_X_POS_DATA(instance).slider_size +
467                      SCROLLBAR_X_POS_DATA(instance).slider_position)) {
468                         if (line < 1.0) {
469                                 line = 1.0;
470                         }
471                         signal_special_Xt_user_event(
472                                 frame,
473                                 Qscrollbar_page_down,
474                                 Fcons(win, make_int((int)line)));
475                 }
476 #else
477                 signal_special_Xt_user_event(frame, Qscrollbar_page_down,
478                                              Fcons(win, Qnil));
479 #endif
480         }
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         case SCROLLBAR_CHANGE:
492                 inhibit_slider_size_change = 0;
493 #if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
494                 vertical_drag_in_progress = 0;
495                 SCROLLBAR_X_VDRAG_ORIG_VALUE(instance) = data->slider_value;
496                 SCROLLBAR_X_VDRAG_ORIG_WINDOW_START(instance) =
497                     XINT(Fwindow_start(win));
498 #else
499                 stupid_vertical_scrollbar_drag_hack = 0;
500 #endif
501                 break;
502
503         case SCROLLBAR_DRAG: {
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                         SCROLLBAR_X_VDRAG_ORIG_VALUE(instance) =
520                                 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 <
529                     SCROLLBAR_X_VDRAG_ORIG_VALUE(instance)) {
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                                 /* shouldn't get here, but just in case */
536                                 value =
537                                         SCROLLBAR_X_POS_DATA(instance).
538                                         minimum;
539                         } else {
540                                 value = (int)
541                                         (SCROLLBAR_X_POS_DATA(instance).
542                                          minimum + (((double)
543                                                      (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START(instance)
544                                                       -
545                                                       SCROLLBAR_X_POS_DATA
546                                                       (instance).minimum)
547                                                      * (data->slider_value -
548                                                         SCROLLBAR_X_POS_DATA
549                                                         (instance).minimum))
550                                                     /
551                                                     (SCROLLBAR_X_VDRAG_ORIG_VALUE
552                                                      (instance)
553                                                      -
554                                                      SCROLLBAR_X_POS_DATA
555                                                      (instance).minimum)));
556                         }
557                 } else {
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).
564                                 slider_size)) {
565                                 /* avoid divide by zero */
566                                 value =
567                                         SCROLLBAR_X_VDRAG_ORIG_WINDOW_START
568                                         (instance);
569                         } else {
570                                 value = (int)
571                                         (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START
572                                          (instance)
573                                          + (((double)
574                                              (SCROLLBAR_X_POS_DATA
575                                               (instance).maximum -
576                                               SCROLLBAR_X_VDRAG_ORIG_WINDOW_START
577                                               (instance))
578                                              * (data->slider_value -
579                                                 SCROLLBAR_X_VDRAG_ORIG_VALUE
580                                                 (instance)))
581                                             /
582                                             (SCROLLBAR_X_POS_DATA(instance).
583                                              maximum -
584                                              SCROLLBAR_X_POS_DATA(instance).
585                                              slider_size -
586                                              SCROLLBAR_X_VDRAG_ORIG_VALUE
587                                              (instance))));
588                         }
589                 }
590 #else
591                 stupid_vertical_scrollbar_drag_hack = 0;
592                 value = data->slider_value;
593 #endif
594
595                 if (value >= SCROLLBAR_X_POS_DATA(instance).maximum)
596                         value =
597                                 SCROLLBAR_X_POS_DATA(instance).maximum - 1;
598                 if (value < SCROLLBAR_X_POS_DATA(instance).minimum)
599                         value = SCROLLBAR_X_POS_DATA(instance).minimum;
600
601                 signal_special_Xt_user_event(frame,
602                                              Qscrollbar_vertical_drag,
603                                              Fcons(win,
604                                                    make_int(value)));
605         }
606                 break;
607         default:
608                 /* maybe punish the user here? */
609                 /* abort(); */
610                 break;
611         }
612 }
613
614 /*
615  * This is the only callback provided for horizontal scrollbars.  It
616  * should be able to handle all of the scrollbar events in
617  * scroll_action (see lwlib.h).  The client data will be of type
618  * scroll_event (see lwlib.h). */
619 static void
620 x_update_horizontal_scrollbar_callback(Widget widget, LWLIB_ID id,
621                                        XtPointer client_data)
622 {
623         scroll_event *data = (scroll_event *) client_data;
624         struct device *d = get_device_from_display(XtDisplay(widget));
625         struct frame *f = x_any_window_to_frame(d, XtWindow(widget));
626         Lisp_Object win, frame;
627         struct window_mirror *mirror;
628
629         if (!f)
630                 return;
631
632         mirror = find_scrollbar_window_mirror(f, id);
633         if (!mirror)
634                 return;
635
636         win = real_window(mirror, 1);
637
638         if (NILP(win))
639                 return;
640         frame = WINDOW_FRAME(XWINDOW(win));
641
642         /* It seems that this is necessary whenever signal_special_Xt_user_event()
643            is called.  #### Why??? */
644         DEVICE_X_MOUSE_TIMESTAMP(d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP(d);
645
646         switch (data->action) {
647         case SCROLLBAR_LINE_UP:
648                 signal_special_Xt_user_event(frame, Qscrollbar_char_left, win);
649                 break;
650         case SCROLLBAR_LINE_DOWN:
651                 signal_special_Xt_user_event(frame, Qscrollbar_char_right, win);
652                 break;
653         case SCROLLBAR_PAGE_UP:
654                 signal_special_Xt_user_event(frame, Qscrollbar_page_left, win);
655                 break;
656         case SCROLLBAR_PAGE_DOWN:
657                 signal_special_Xt_user_event(frame, Qscrollbar_page_right, win);
658                 break;
659         case SCROLLBAR_TOP:
660                 signal_special_Xt_user_event(frame, Qscrollbar_to_left, win);
661                 break;
662         case SCROLLBAR_BOTTOM:
663                 signal_special_Xt_user_event(frame, Qscrollbar_to_right, win);
664                 break;
665         case SCROLLBAR_CHANGE:
666                 inhibit_slider_size_change = 0;
667                 break;
668         case SCROLLBAR_DRAG:
669                 inhibit_slider_size_change = 1;
670                 /* #### Fix the damn toolkit code so they all work the same way.
671                    Lucid is the one mostly wrong. */
672 #if defined (LWLIB_SCROLLBARS_LUCID) || defined (LWLIB_SCROLLBARS_ATHENA3D)
673                 signal_special_Xt_user_event(frame, Qscrollbar_horizontal_drag,
674                                              (Fcons
675                                               (win,
676                                                make_int(data->slider_value))));
677 #else
678                 signal_special_Xt_user_event(frame, Qscrollbar_horizontal_drag,
679                                              (Fcons
680                                               (win,
681                                                make_int(data->slider_value -
682                                                         1))));
683 #endif
684                 break;
685         default:
686                 break;
687         }
688 }
689
690 static void x_scrollbar_pointer_changed_in_window(struct window *w)
691 {
692         Lisp_Object window;
693
694         XSETWINDOW(window, w);
695         x_scrollbar_loop(X_SET_SCROLLBAR_POINTER, window, find_window_mirror(w),
696                          0, (Window) NULL);
697 }
698
699 /* Make sure that all scrollbars on frame are up-to-date.  Called
700    directly from x_set_frame_properties in frame-x.c*/
701 void x_update_frame_scrollbars(struct frame *f)
702 {
703         /* Consider this code to be "in_display" so that we abort() if Fsignal()
704            gets called. */
705         in_display++;
706         x_scrollbar_loop(X_UPDATE_FRAME_SCROLLBARS, f->root_window,
707                          f->root_mirror, 0, (Window) NULL);
708         in_display--;
709         if (in_display < 0)
710                 abort();
711 }
712
713 #if defined MEMORY_USAGE_STATS && !(defined HAVE_BDWGC && defined EF_USE_BDWGC)
714
715 static int
716 x_compute_scrollbar_instance_usage(struct device *d,
717                                    struct scrollbar_instance *inst,
718                                    struct overhead_stats *ovstats)
719 {
720         int total = 0;
721
722         while (inst) {
723                 struct x_scrollbar_data *data =
724                     (struct x_scrollbar_data *)inst->scrollbar_data;
725
726                 total += malloced_storage_size(data, sizeof(*data), ovstats);
727                 total +=
728                     malloced_storage_size(data->name, 1 + strlen(data->name),
729                                           ovstats);
730                 inst = inst->next;
731         }
732
733         return total;
734 }
735
736 #endif                          /* MEMORY_USAGE_STATS */
737
738 /************************************************************************/
739 /*                            initialization                            */
740 /************************************************************************/
741
742 void console_type_create_scrollbar_x(void)
743 {
744         CONSOLE_HAS_METHOD(x, inhibit_scrollbar_slider_size_change);
745         CONSOLE_HAS_METHOD(x, free_scrollbar_instance);
746         CONSOLE_HAS_METHOD(x, release_scrollbar_instance);
747         CONSOLE_HAS_METHOD(x, create_scrollbar_instance);
748         CONSOLE_HAS_METHOD(x, update_scrollbar_instance_values);
749         CONSOLE_HAS_METHOD(x, update_scrollbar_instance_status);
750         CONSOLE_HAS_METHOD(x, scrollbar_pointer_changed_in_window);
751 #if defined MEMORY_USAGE_STATS && !(defined HAVE_BDWGC && defined EF_USE_BDWGC)
752         CONSOLE_HAS_METHOD(x, compute_scrollbar_instance_usage);
753 #endif                          /* MEMORY_USAGE_STATS */
754 }
755
756 void reinit_vars_of_scrollbar_x(void)
757 {
758         stupid_vertical_scrollbar_drag_hack = 1;
759 }
760
761 void vars_of_scrollbar_x(void)
762 {
763         reinit_vars_of_scrollbar_x();
764
765 #if defined (LWLIB_SCROLLBARS_LUCID)
766         Fprovide(intern("lucid-scrollbars"));
767 #elif defined (LWLIB_SCROLLBARS_MOTIF)
768         Fprovide(intern("motif-scrollbars"));
769 #elif defined (LWLIB_SCROLLBARS_ATHENA)
770         Fprovide(intern("athena-scrollbars"));
771 #endif
772 }