Mercurial > hg > xemacs-beta
comparison src/scrollbar-x.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | 080151679be2 |
comparison
equal
deleted
inserted
replaced
427:0a0253eac470 | 428:3ecd8885ac67 |
---|---|
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 | |
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 } |