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