Initial git import
[sxemacs] / src / ui / scrollbar.c
1 /* Generic scrollbar implementation.
2    Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
3    Copyright (C) 1995 Free Software Foundation, Inc.
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 has been Mule-ized. */
26
27 #include <config.h>
28 #include "lisp.h"
29
30 #include "buffer.h"
31 #include "commands.h"
32 #include "scrollbar.h"
33 #include "device.h"
34 #include "frame.h"
35 #include "glyphs.h"
36 #include "gutter.h"
37 #include "window.h"
38
39 Lisp_Object Qinit_scrollbar_from_resources;
40
41 Lisp_Object Qscrollbar_line_up;
42 Lisp_Object Qscrollbar_line_down;
43 Lisp_Object Qscrollbar_page_up;
44 Lisp_Object Qscrollbar_page_down;
45 Lisp_Object Qscrollbar_to_top;
46 Lisp_Object Qscrollbar_to_bottom;
47 Lisp_Object Qscrollbar_vertical_drag;
48
49 Lisp_Object Qscrollbar_char_left;
50 Lisp_Object Qscrollbar_char_right;
51 Lisp_Object Qscrollbar_page_left;
52 Lisp_Object Qscrollbar_page_right;
53 Lisp_Object Qscrollbar_to_left;
54 Lisp_Object Qscrollbar_to_right;
55 Lisp_Object Qscrollbar_horizontal_drag;
56
57 #define DEFAULT_SCROLLBAR_WIDTH 15
58 #define DEFAULT_SCROLLBAR_HEIGHT 15
59
60 /* Width and height of the scrollbar. */
61 Lisp_Object Vscrollbar_width;
62 Lisp_Object Vscrollbar_height;
63
64 /* Scrollbar visibility specifiers */
65 Lisp_Object Vhorizontal_scrollbar_visible_p;
66 Lisp_Object Vvertical_scrollbar_visible_p;
67
68 /* Scrollbar location specifiers */
69 Lisp_Object Vscrollbar_on_left_p;
70 Lisp_Object Vscrollbar_on_top_p;
71
72 Lisp_Object Vscrollbar_pointer_glyph;
73
74 EXFUN(Fcenter_to_window_line, 2);
75
76 static void update_scrollbar_instance(struct window *w, int vertical,
77                                       struct scrollbar_instance *instance);
78 \f
79 static void
80 free_scrollbar_instance(struct scrollbar_instance *instance,
81                         struct frame *frame)
82 {
83         if (!instance)
84                 return;
85         else {
86                 struct device *d = XDEVICE(frame->device);
87
88                 MAYBE_DEVMETH(d, free_scrollbar_instance, (instance));
89                 xfree(instance);
90         }
91 }
92
93 static void free_window_mirror_scrollbars(struct window_mirror *mir)
94 {
95         free_scrollbar_instance(mir->scrollbar_vertical_instance, mir->frame);
96         mir->scrollbar_vertical_instance = 0;
97
98         free_scrollbar_instance(mir->scrollbar_horizontal_instance, mir->frame);
99         mir->scrollbar_horizontal_instance = 0;
100 }
101
102 static struct window_mirror *free_scrollbars_loop(Lisp_Object window,
103                                                   struct window_mirror *mir)
104 {
105         struct window_mirror *retval = NULL;
106
107         while (mir) {
108                 assert(!NILP(window));
109
110                 if (mir->vchild) {
111                         retval = free_scrollbars_loop(XWINDOW(window)->vchild,
112                                                       mir->vchild);
113                 } else if (mir->hchild) {
114                         retval = free_scrollbars_loop(XWINDOW(window)->hchild,
115                                                       mir->hchild);
116                 }
117
118                 if (retval != NULL)
119                         return retval;
120
121                 if (mir->scrollbar_vertical_instance ||
122                     mir->scrollbar_horizontal_instance)
123                         free_window_mirror_scrollbars(mir);
124
125                 mir = mir->next;
126                 window = XWINDOW(window)->next;
127         }
128
129         return NULL;
130 }
131
132 /* Destroy all scrollbars associated with FRAME.  Only called from
133    delete_frame_internal. */
134 void free_frame_scrollbars(struct frame *f)
135 {
136         if (!HAS_FRAMEMETH_P(f, create_scrollbar_instance))
137                 return;
138
139         if (f->mirror_dirty)
140                 update_frame_window_mirror(f);
141
142         free_scrollbars_loop(f->root_window, f->root_mirror);
143
144         while (FRAME_SB_VCACHE(f)) {
145                 struct scrollbar_instance *tofree = FRAME_SB_VCACHE(f);
146                 FRAME_SB_VCACHE(f) = FRAME_SB_VCACHE(f)->next;
147                 tofree->next = NULL;
148                 free_scrollbar_instance(tofree, f);
149         }
150
151         while (FRAME_SB_HCACHE(f)) {
152                 struct scrollbar_instance *tofree = FRAME_SB_HCACHE(f);
153                 FRAME_SB_HCACHE(f) = FRAME_SB_HCACHE(f)->next;
154                 tofree->next = NULL;
155                 free_scrollbar_instance(tofree, f);
156         }
157 }
158 \f
159 static struct scrollbar_instance *create_scrollbar_instance(struct frame *f,
160                                                             int vertical)
161 {
162         struct device *d = XDEVICE(f->device);
163         struct scrollbar_instance *instance =
164             xnew_and_zero(struct scrollbar_instance);
165
166         MAYBE_DEVMETH(d, create_scrollbar_instance, (f, vertical, instance));
167
168         return instance;
169 }
170 \f
171 #define GET_SCROLLBAR_INSTANCE_INTERNAL(cache)                          \
172   do {                                                                  \
173     if (FRAME_SB_##cache (f))                                           \
174       {                                                                 \
175         struct scrollbar_instance *retval = FRAME_SB_##cache (f);       \
176         FRAME_SB_##cache (f) = FRAME_SB_##cache (f)->next;              \
177         retval->next = NULL;                                            \
178         return retval;                                                  \
179       }                                                                 \
180   } while (0)
181
182 static struct scrollbar_instance *get_scrollbar_instance(struct frame *f,
183                                                          int vertical)
184 {
185         /* Check if there are any available scrollbars already in existence. */
186         if (vertical)
187                 GET_SCROLLBAR_INSTANCE_INTERNAL(VCACHE);
188         else
189                 GET_SCROLLBAR_INSTANCE_INTERNAL(HCACHE);
190
191         return create_scrollbar_instance(f, vertical);
192 }
193
194 #undef GET_SCROLLBAR_INSTANCE_INTERNAL
195
196 #define RELEASE_SCROLLBAR_INSTANCE_INTERNAL(cache)                      \
197   do {                                                                  \
198     if (!FRAME_SB_##cache (f))                                          \
199       {                                                                 \
200         instance->next = NULL;                                          \
201         FRAME_SB_##cache (f) = instance;                                \
202       }                                                                 \
203     else                                                                \
204       {                                                                 \
205         instance->next = FRAME_SB_##cache (f);                          \
206         FRAME_SB_##cache (f) = instance;                                \
207       }                                                                 \
208   } while (0)
209
210 static void
211 release_scrollbar_instance(struct frame *f, int vertical,
212                            struct scrollbar_instance *instance)
213 {
214         /* #### should we do "instance->mir = 0;" for safety? */
215         if (vertical)
216                 RELEASE_SCROLLBAR_INSTANCE_INTERNAL(VCACHE);
217         else
218                 RELEASE_SCROLLBAR_INSTANCE_INTERNAL(HCACHE);
219 }
220
221 #undef RELEASE_SCROLLBAR_INSTANCE_INTERNAL
222
223 #if defined MEMORY_USAGE_STATS && !(defined HAVE_BDWGC && defined EF_USE_BDWGC)
224
225 int
226 compute_scrollbar_instance_usage(struct device *d,
227                                  struct scrollbar_instance *inst,
228                                  struct overhead_stats *ovstats)
229 {
230         int total = 0;
231
232         if (HAS_DEVMETH_P(d, compute_scrollbar_instance_usage))
233                 total += DEVMETH (d, compute_scrollbar_instance_usage, (d, inst, ovstats));
234
235         while (inst) {
236                 total += malloced_storage_size(inst, sizeof(*inst), ovstats);
237                 inst = inst->next;
238         }
239
240         return total;
241 }
242
243 #endif                          /* MEMORY_USAGE_STATS */
244
245 void
246 update_window_scrollbars(struct window *w, struct window_mirror *mirror,
247                          int active, int horiz_only)
248 {
249         struct frame *f = XFRAME(w->frame);
250         struct device *d = XDEVICE(f->device);
251
252         if (!HAS_DEVMETH_P(d, create_scrollbar_instance))
253                 return;
254
255         in_display++;
256
257         /* It is possible for this to get called from the mirror update
258            routines.  In that case the structure is in an indeterminate
259            state but we know exactly what struct we are working with.  So we
260            pass it in in that case.  We also take advantage of it at some
261            other points where we know what the mirror struct is. */
262         if (!mirror)
263                 mirror = find_window_mirror(w);
264
265         if (!mirror->scrollbar_vertical_instance && active)
266                 mirror->scrollbar_vertical_instance =
267                     get_scrollbar_instance(f, 1);
268
269         if (!mirror->scrollbar_horizontal_instance && active)
270                 mirror->scrollbar_horizontal_instance =
271                     get_scrollbar_instance(f, 0);
272
273         if (!horiz_only && mirror->scrollbar_vertical_instance) {
274                 int size = (active ? window_scrollbar_width(w) : 0);
275                 struct scrollbar_instance *instance;
276
277                 instance = mirror->scrollbar_vertical_instance;
278                 instance->scrollbar_is_active = active;
279                 instance->mirror = mirror;
280
281                 if (active && size)
282                         update_scrollbar_instance(w, 1, instance);
283                 MAYBE_DEVMETH(d, update_scrollbar_instance_status,
284                               (w, active, size, instance));
285
286                 if (!active) {
287                         release_scrollbar_instance(f, 1, instance);
288                         mirror->scrollbar_vertical_instance = NULL;
289                 }
290         }
291
292         if (mirror->scrollbar_horizontal_instance) {
293                 int size = (active ? window_scrollbar_height(w) : 0);
294                 struct scrollbar_instance *instance;
295
296                 instance = mirror->scrollbar_horizontal_instance;
297                 instance->scrollbar_is_active = active;
298                 instance->mirror = mirror;
299
300                 if (active && size)
301                         update_scrollbar_instance(w, 0, instance);
302                 MAYBE_DEVMETH(d, update_scrollbar_instance_status,
303                               (w, active, size, instance));
304
305                 if (!active) {
306                         release_scrollbar_instance(f, 0, instance);
307                         mirror->scrollbar_horizontal_instance = NULL;
308                 }
309         }
310
311         in_display--;
312 }
313
314 void release_window_mirror_scrollbars(struct window_mirror *mir)
315 {
316         struct device *d = XDEVICE(mir->frame->device);
317
318         if (!HAS_DEVMETH_P(d, create_scrollbar_instance))
319                 return;
320
321         if (mir->scrollbar_vertical_instance) {
322                 release_scrollbar_instance(mir->frame, 1,
323                                            mir->scrollbar_vertical_instance);
324                 MAYBE_DEVMETH(d, release_scrollbar_instance,
325                               (mir->scrollbar_vertical_instance));
326         }
327         mir->scrollbar_vertical_instance = 0;
328
329         if (mir->scrollbar_horizontal_instance) {
330                 release_scrollbar_instance(mir->frame, 0,
331                                            mir->scrollbar_horizontal_instance);
332                 MAYBE_DEVMETH(d, release_scrollbar_instance,
333                               (mir->scrollbar_horizontal_instance));
334         }
335         mir->scrollbar_horizontal_instance = 0;
336 }
337
338 /*
339  * If w->sb_point is on the top line then return w->sb_point else
340  * return w->start.  If flag, then return beginning point of line
341  * which w->sb_point lies on.
342  */
343 static Bufpos scrollbar_point(struct window *w, int flag)
344 {
345         Bufpos start_pos, end_pos, sb_pos;
346         Lisp_Object buf;
347         struct buffer *b;
348
349         if (NILP(w->buffer))    /* non-leaf window */
350                 return 0;
351
352         start_pos = marker_position(w->start[CURRENT_DISP]);
353         sb_pos = marker_position(w->sb_point);
354
355         if (!flag && sb_pos < start_pos)
356                 return start_pos;
357
358         buf = emacs_get_buffer(w->buffer, 0);
359         if (!NILP(buf))
360                 b = XBUFFER(buf);
361         else
362                 return start_pos;
363
364         if (flag)
365                 end_pos = find_next_newline_no_quit(b, sb_pos, -1);
366         else
367                 end_pos = find_next_newline_no_quit(b, start_pos, 1);
368
369         if (flag)
370                 return end_pos;
371         else if (sb_pos > end_pos)
372                 return start_pos;
373         else
374                 return sb_pos;
375 }
376
377 /*
378  * Update a window's horizontal or vertical scrollbar.
379  */
380 static void
381 update_scrollbar_instance(struct window *w, int vertical,
382                           struct scrollbar_instance *instance)
383 {
384         struct frame *f = XFRAME(w->frame);
385         struct device *d = XDEVICE(f->device);
386         struct buffer *b = XBUFFER(w->buffer);
387         Bufpos start_pos, end_pos, sb_pos;
388         int scrollbar_width = window_scrollbar_width(w);
389         int scrollbar_height = window_scrollbar_height(w);
390
391         int new_line_increment = -1, new_page_increment = -1;
392         int new_minimum = -1, new_maximum = -1;
393         int new_slider_size = -1, new_slider_position = -1;
394         int new_width = -1, new_height = -1, new_x = -1, new_y = -1;
395         struct window *new_window = 0;  /* #### currently unused */
396
397         end_pos = BUF_Z(b) - w->window_end_pos[CURRENT_DISP];
398         sb_pos = scrollbar_point(w, 0);
399         start_pos = sb_pos;
400
401         /* The end position must be strictly greater than the start
402            position, at least for the Motify scrollbar.  It shouldn't hurt
403            anything for other scrollbar implementations. */
404         if (end_pos <= start_pos)
405                 end_pos = start_pos + 1;
406
407         if (vertical) {
408                 new_height = WINDOW_TEXT_HEIGHT(w);
409                 new_width = scrollbar_width;
410         } else {
411                 new_height = scrollbar_height;
412                 new_width = WINDOW_TEXT_WIDTH(w);
413         }
414
415         /* If the height and width are not greater than 0, then later on the
416            Motif widgets will bitch and moan. */
417         if (new_height <= 0)
418                 new_height = 1;
419         if (new_width <= 0)
420                 new_width = 1;
421
422         {
423                 /* fuck, just for an assertion? */
424                 Lisp_Object tmp = real_window(instance->mirror, 0);
425                 assert(instance->mirror && XWINDOW(tmp) == w);
426         }
427
428         /* Only character-based scrollbars are implemented at the moment.
429            Line-based will be implemented in the future. */
430
431         instance->scrollbar_is_active = 1;
432         new_line_increment = 1;
433         new_page_increment = 1;
434
435         /* We used to check for inhibit_scrollbar_slider_size_change here,
436            but that seems bogus.  */
437         {
438                 int x_offset, y_offset;
439
440                 /* Scrollbars are always the farthest from the text area, barring
441                    gutters. */
442                 if (vertical) {
443                         if (!NILP(w->scrollbar_on_left_p)) {
444                                 x_offset = WINDOW_LEFT(w);
445                         } else {
446                                 x_offset = WINDOW_RIGHT(w) - scrollbar_width;
447                                 if (window_needs_vertical_divider(w))
448                                         x_offset -= window_divider_width(w);
449                         }
450                         y_offset = WINDOW_TEXT_TOP(w) + f->scrollbar_y_offset;
451                 } else {
452                         x_offset = WINDOW_TEXT_LEFT(w);
453                         y_offset = f->scrollbar_y_offset;
454
455                         if (!NILP(w->scrollbar_on_top_p)) {
456                                 y_offset += WINDOW_TOP(w);
457                         } else {
458                                 y_offset += WINDOW_TEXT_BOTTOM(w);
459                         }
460                 }
461
462                 new_x = x_offset;
463                 new_y = y_offset;
464         }
465
466         /* A disabled scrollbar has its slider sized to the entire height of
467            the scrollbar.  Currently the minibuffer scrollbar is
468            disabled. */
469         if (!MINI_WINDOW_P(w) && vertical) {
470                 if (!DEVMETH_OR_GIVEN
471                     (d, inhibit_scrollbar_slider_size_change, (), 0)) {
472                         new_minimum = BUF_BEGV(b);
473                         new_maximum = max(BUF_ZV(b), new_minimum + 1);
474                         new_slider_size = min((end_pos - start_pos),
475                                               (new_maximum - new_minimum));
476                         new_slider_position = sb_pos;
477                         new_window = w;
478                 }
479         } else if (!MINI_WINDOW_P(w)) {
480                 /* The minus one is to account for the truncation glyph. */
481                 int wcw = window_char_width(w, 0) - 1;
482                 int max_width, max_slide;
483
484                 if (w->max_line_len < wcw) {
485                         max_width = 1;
486                         max_slide = 1;
487                         wcw = 1;
488                 } else {
489                         max_width = w->max_line_len + 2;
490                         max_slide = max_width - wcw;
491                 }
492
493                 new_minimum = 0;
494                 new_maximum = max_width;
495                 new_slider_size = wcw;
496                 new_slider_position = min(w->hscroll, max_slide);
497         } else {                /* MINI_WINDOW_P (w) */
498
499                 new_minimum = 1;
500                 new_maximum = 2;
501                 new_slider_size = 1;
502                 new_slider_position = 1;
503                 instance->scrollbar_is_active = 0;
504         }
505
506         DEVMETH(d, update_scrollbar_instance_values, (w, instance,
507                                                       new_line_increment,
508                                                       new_page_increment,
509                                                       new_minimum,
510                                                       new_maximum,
511                                                       new_slider_size,
512                                                       new_slider_position,
513                                                       new_width, new_height,
514                                                       new_x, new_y));
515 }
516
517 void init_frame_scrollbars(struct frame *f)
518 {
519         struct device *d = XDEVICE(f->device);
520
521         if (HAS_DEVMETH_P(d, create_scrollbar_instance)) {
522                 int depth = unlock_ghost_specifiers_protected();
523                 Lisp_Object frame;
524                 XSETFRAME(frame, f);
525                 call_critical_lisp_code(XDEVICE(FRAME_DEVICE(f)),
526                                         Qinit_scrollbar_from_resources, frame);
527                 unbind_to(depth, Qnil);
528         }
529 }
530
531 void init_device_scrollbars(struct device *d)
532 {
533         if (HAS_DEVMETH_P(d, create_scrollbar_instance)) {
534                 int depth = unlock_ghost_specifiers_protected();
535                 Lisp_Object device;
536                 XSETDEVICE(device, d);
537                 call_critical_lisp_code(d,
538                                         Qinit_scrollbar_from_resources, device);
539                 unbind_to(depth, Qnil);
540         }
541 }
542
543 void init_global_scrollbars(struct device *d)
544 {
545         if (HAS_DEVMETH_P(d, create_scrollbar_instance)) {
546                 int depth = unlock_ghost_specifiers_protected();
547                 call_critical_lisp_code(d,
548                                         Qinit_scrollbar_from_resources,
549                                         Qglobal);
550                 unbind_to(depth, Qnil);
551         }
552 }
553
554 static void
555 vertical_scrollbar_changed_in_window(Lisp_Object specifier,
556                                      struct window *w, Lisp_Object oldval)
557 {
558         /* Hold on your cerebella guys. If we always show the dividers,
559            changing scrollbar affects only how the text and scrollbar are
560            laid out in the window. If we do not want the dividers to show up
561            always, then we mark more drastic change, because changing
562            divider appearance changes lotta things. Although we actually need
563            to do this only if the scrollbar has appeared or disappeared
564            completely at either window edge, we do this always, as users
565            usually do not reposition scrollbars 200 times a second or so. Do
566            you? */
567         if (NILP(w->vertical_divider_always_visible_p))
568                 MARK_FRAME_WINDOWS_STRUCTURE_CHANGED(XFRAME(WINDOW_FRAME(w)));
569         else
570                 MARK_WINDOWS_CHANGED(w);
571 }
572
573 /* This function is called as a result of a change to the
574    `scrollbar-pointer' glyph.  */
575 static void
576 scrollbar_pointer_changed_in_window(Lisp_Object specifier, struct window *w,
577                                     Lisp_Object oldval)
578 {
579         struct frame *f = XFRAME(WINDOW_FRAME(w));
580
581         if (f->init_finished)
582                 MAYBE_FRAMEMETH(f, scrollbar_pointer_changed_in_window, (w));
583 }
584
585 /* ####
586
587    All of the following stuff is functions that handle scrollbar
588    actions.  All of it should be moved into Lisp.  This may require
589    adding some badly-needed primitives. */
590
591 /********** vertical scrollbar stuff **********/
592
593 /*
594  * If the original point is still visible, put the cursor back there.
595  * Otherwise, when scrolling down stick it at the beginning of the
596  * first visible line and when scrolling up stick it at the beginning
597  * of the last visible line.
598  */
599
600 /* #### This function should be moved into Lisp */
601 static void scrollbar_reset_cursor(Lisp_Object win, Lisp_Object orig_pt)
602 {
603         /* When this function is called we know that start is already
604            accurate.  We know this because either set-window-start or
605            recenter was called immediately prior to it being called. */
606         Lisp_Object buf;
607         Bufpos start_pos = XINT(Fwindow_start(win));
608         Bufpos ptint = XINT(orig_pt);
609         struct window *w = XWINDOW(win);
610         Lisp_Object tmp_win = Fselected_window(XFRAME(w->frame)->device);
611         int selected = ((w == XWINDOW(tmp_win)) ? 1 : 0);
612
613         buf = Fwindow_buffer(win);
614         if (NILP(buf))
615                 return;         /* the window was deleted out from under us */
616
617         if (ptint < XINT(Fwindow_start(win))) {
618                 if (selected)
619                         Fgoto_char(make_int(start_pos), buf);
620                 else
621                         Fset_window_point(win, make_int(start_pos));
622         } else if (!point_would_be_visible(XWINDOW(win), start_pos, ptint)) {
623                 Fmove_to_window_line(make_int(-1), win);
624
625                 if (selected)
626                         Fbeginning_of_line(Qnil, buf);
627                 else {
628                         /* #### Taken from forward-line. */
629                         Bufpos pos;
630
631                         pos = find_next_newline(XBUFFER(buf),
632                                                 marker_position(w->
633                                                                 pointm
634                                                                 [CURRENT_DISP]),
635                                                 -1);
636                         Fset_window_point(win, make_int(pos));
637                 }
638         } else {
639                 if (selected)
640                         Fgoto_char(orig_pt, buf);
641                 else
642                         Fset_window_point(win, orig_pt);
643         }
644 }
645
646 DEFUN("scrollbar-line-up", Fscrollbar_line_up, 1, 1, 0, /*
647 Function called when the line-up arrow on the scrollbar is clicked.
648 This is the little arrow at the top of the scrollbar.  One argument, the
649 scrollbar's window.  You can advise this function to change the scrollbar
650 behavior.
651 */
652       (window))
653 {
654         CHECK_LIVE_WINDOW(window);
655         window_scroll(window, make_int(1), -1, ERROR_ME_NOT);
656         zmacs_region_stays = 1;
657         return Qnil;
658 }
659
660 DEFUN("scrollbar-line-down", Fscrollbar_line_down, 1, 1, 0,     /*
661 Function called when the line-down arrow on the scrollbar is clicked.
662 This is the little arrow at the bottom of the scrollbar.  One argument, the
663 scrollbar's window.  You can advise this function to change the scrollbar
664 behavior.
665 */
666       (window))
667 {
668         CHECK_LIVE_WINDOW(window);
669         window_scroll(window, make_int(1), 1, ERROR_ME_NOT);
670         zmacs_region_stays = 1;
671         return Qnil;
672 }
673
674 DEFUN("scrollbar-page-up", Fscrollbar_page_up, 1, 1, 0, /*
675 Function called when the user gives the "page-up" scrollbar action.
676 \(The way this is done can vary from scrollbar to scrollbar.) One argument,
677 a cons containing the scrollbar's window and a value (#### document me!
678 This value is nil for Motif/Lucid scrollbars and a number for Athena
679 scrollbars).  You can advise this function to change the scrollbar
680 behavior.
681 */
682       (object))
683 {
684         Lisp_Object window = Fcar(object);
685
686         CHECK_LIVE_WINDOW(window);
687         /* Motif and Athena scrollbars behave differently, but in accordance
688            with their standard behaviors.  It is not possible to hide the
689            differences down in lwlib because knowledge of SXEmacs buffer and
690            cursor motion routines is necessary. */
691
692         if (NILP(XCDR(object)))
693                 window_scroll(window, Qnil, -1, ERROR_ME_NOT);
694         else {
695                 Bufpos bufpos;
696                 Lisp_Object value = Fcdr(object);
697
698                 CHECK_INT(value);
699                 Fmove_to_window_line(Qzero, window);
700                 /* can't use Fvertical_motion() because it moves the buffer point
701                    rather than the window's point.
702
703                    #### It does?  Why does it take a window argument then? */
704                 bufpos = vmotion(XWINDOW(window), XINT(Fwindow_point(window)),
705                                  XINT(value), 0);
706                 Fset_window_point(window, make_int(bufpos));
707                 Fcenter_to_window_line(Qzero, window);
708         }
709
710         zmacs_region_stays = 1;
711         return Qnil;
712 }
713
714 DEFUN("scrollbar-page-down", Fscrollbar_page_down, 1, 1, 0,     /*
715 Function called when the user gives the "page-down" scrollbar action.
716 \(The way this is done can vary from scrollbar to scrollbar.) One argument,
717 a cons containing the scrollbar's window and a value (#### document me!
718 This value is nil for Motif/Lucid scrollbars and a number for Athena
719 scrollbars).  You can advise this function to change the scrollbar
720 behavior.
721 */
722       (object))
723 {
724         Lisp_Object window = Fcar(object);
725
726         CHECK_LIVE_WINDOW(window);
727         /* Motif and Athena scrollbars behave differently, but in accordance
728            with their standard behaviors.  It is not possible to hide the
729            differences down in lwlib because knowledge of SXEmacs buffer and
730            cursor motion routines is necessary. */
731
732         if (NILP(XCDR(object)))
733                 window_scroll(window, Qnil, 1, ERROR_ME_NOT);
734         else {
735                 Lisp_Object value = Fcdr(object);
736                 CHECK_INT(value);
737                 Fmove_to_window_line(value, window);
738                 Fcenter_to_window_line(Qzero, window);
739         }
740
741         zmacs_region_stays = 1;
742         return Qnil;
743 }
744
745 DEFUN("scrollbar-to-top", Fscrollbar_to_top, 1, 1, 0,   /*
746 Function called when the user invokes the "to-top" scrollbar action.
747 The way this is done can vary from scrollbar to scrollbar, but
748 C-button1 on the up-arrow is very common. One argument, the
749 scrollbar's window.  You can advise this function to change the
750 scrollbar behavior.
751 */
752       (window))
753 {
754         Lisp_Object orig_pt = Fwindow_point(window);
755         Fset_window_point(window, Fpoint_min(Fwindow_buffer(window)));
756         Fcenter_to_window_line(Qzero, window);
757         scrollbar_reset_cursor(window, orig_pt);
758         zmacs_region_stays = 1;
759         return Qnil;
760 }
761
762 DEFUN("scrollbar-to-bottom", Fscrollbar_to_bottom, 1, 1, 0,     /*
763 Function called when the user invokes the "to-bottom" scrollbar action.
764 The way this is done can vary from scrollbar to scrollbar, but
765 C-button1 on the down-arrow is very common. One argument, the
766 scrollbar's window.  You can advise this function to change the
767 scrollbar behavior.
768 */
769       (window))
770 {
771         Lisp_Object orig_pt = Fwindow_point(window);
772         Fset_window_point(window, Fpoint_max(Fwindow_buffer(window)));
773         Fcenter_to_window_line(make_int(-3), window);
774         scrollbar_reset_cursor(window, orig_pt);
775         zmacs_region_stays = 1;
776         return Qnil;
777 }
778
779 DEFUN("scrollbar-vertical-drag", Fscrollbar_vertical_drag, 1, 1, 0,     /*
780 Function called when the user drags the vertical scrollbar slider.
781 One argument, a cons containing the scrollbar's window and a value
782 between point-min and point-max.  You can advise this function to
783 change the scrollbar behavior.
784 */
785       (object))
786 {
787         Bufpos start_pos;
788         Lisp_Object orig_pt;
789         Lisp_Object window = Fcar(object);
790         Lisp_Object value = Fcdr(object);
791
792         orig_pt = Fwindow_point(window);
793         Fset_marker(XWINDOW(window)->sb_point, value, Fwindow_buffer(window));
794         start_pos = scrollbar_point(XWINDOW(window), 1);
795         Fset_window_start(window, make_int(start_pos), Qnil);
796         scrollbar_reset_cursor(window, orig_pt);
797         Fsit_for(Qzero, Qnil);
798         zmacs_region_stays = 1;
799         return Qnil;
800 }
801
802 DEFUN("scrollbar-set-hscroll", Fscrollbar_set_hscroll, 2, 2, 0, /*
803 Set WINDOW's hscroll position to VALUE.
804 This ensures that VALUE is in the proper range for the horizontal scrollbar.
805 */
806       (window, value))
807 {
808         struct window *w;
809         int hscroll, wcw, max_len;
810
811         CHECK_LIVE_WINDOW(window);
812         if (!EQ(value, Qmax))
813                 CHECK_INT(value);
814
815         w = XWINDOW(window);
816         wcw = window_char_width(w, 0) - 1;
817         /* #### We should be able to scroll further right as long as there is
818            a visible truncation glyph.  This calculation for max is bogus.  */
819         max_len = w->max_line_len + 2;
820
821         if (EQ(value, Qmax) || (XINT(value) > (max_len - wcw)))
822                 hscroll = max_len - wcw;
823         else
824                 hscroll = XINT(value);
825
826         /* Can't allow this out of set-window-hscroll's acceptable range. */
827         /* #### What hell on the earth this code limits scroll size to the
828            machine-dependent SHORT size? -- kkm */
829         if (hscroll < 0)
830                 hscroll = 0;
831         else if (hscroll >= (1 << (SXE_SHORTBITS - 1)) - 1)
832                 hscroll = (1 << (SXE_SHORTBITS - 1)) - 1;
833
834         if (hscroll != w->hscroll)
835                 Fset_window_hscroll(window, make_int(hscroll));
836
837         return Qnil;
838 }
839 \f
840 /************************************************************************/
841 /*                            initialization                            */
842 /************************************************************************/
843
844 void syms_of_scrollbar(void)
845 {
846         defsymbol(&Qscrollbar_line_up, "scrollbar-line-up");
847         defsymbol(&Qscrollbar_line_down, "scrollbar-line-down");
848         defsymbol(&Qscrollbar_page_up, "scrollbar-page-up");
849         defsymbol(&Qscrollbar_page_down, "scrollbar-page-down");
850         defsymbol(&Qscrollbar_to_top, "scrollbar-to-top");
851         defsymbol(&Qscrollbar_to_bottom, "scrollbar-to-bottom");
852         defsymbol(&Qscrollbar_vertical_drag, "scrollbar-vertical-drag");
853
854         defsymbol(&Qscrollbar_char_left, "scrollbar-char-left");
855         defsymbol(&Qscrollbar_char_right, "scrollbar-char-right");
856         defsymbol(&Qscrollbar_page_left, "scrollbar-page-left");
857         defsymbol(&Qscrollbar_page_right, "scrollbar-page-right");
858         defsymbol(&Qscrollbar_to_left, "scrollbar-to-left");
859         defsymbol(&Qscrollbar_to_right, "scrollbar-to-right");
860         defsymbol(&Qscrollbar_horizontal_drag, "scrollbar-horizontal-drag");
861
862         defsymbol(&Qinit_scrollbar_from_resources,
863                   "init-scrollbar-from-resources");
864
865         /* #### All these functions should be moved into Lisp.
866            See comment above. */
867         DEFSUBR(Fscrollbar_line_up);
868         DEFSUBR(Fscrollbar_line_down);
869         DEFSUBR(Fscrollbar_page_up);
870         DEFSUBR(Fscrollbar_page_down);
871         DEFSUBR(Fscrollbar_to_top);
872         DEFSUBR(Fscrollbar_to_bottom);
873         DEFSUBR(Fscrollbar_vertical_drag);
874
875         DEFSUBR(Fscrollbar_set_hscroll);
876 }
877
878 void vars_of_scrollbar(void)
879 {
880         DEFVAR_LISP("scrollbar-pointer-glyph", &Vscrollbar_pointer_glyph        /*
881 *The shape of the mouse-pointer when over a scrollbar.
882 This is a glyph; use `set-glyph-image' to change it.
883 If unspecified in a particular domain, the window-system-provided
884 default pointer is used.
885                                                                                  */ );
886
887         Fprovide(intern("scrollbar"));
888 }
889
890 void specifier_vars_of_scrollbar(void)
891 {
892         DEFVAR_SPECIFIER("scrollbar-width", &Vscrollbar_width   /*
893 *Width of vertical scrollbars.
894 This is a specifier; use `set-specifier' to change it.
895                                                                  */ );
896         Vscrollbar_width = make_magic_specifier(Qnatnum);
897         set_specifier_fallback
898             (Vscrollbar_width,
899              list1(Fcons(Qnil, make_int(DEFAULT_SCROLLBAR_WIDTH))));
900         set_specifier_caching(Vscrollbar_width,
901                               offsetof(struct window, scrollbar_width),
902                               vertical_scrollbar_changed_in_window,
903                               offsetof(struct frame, scrollbar_width),
904                               frame_size_slipped, 0);
905
906         DEFVAR_SPECIFIER("scrollbar-height", &Vscrollbar_height /*
907 *Height of horizontal scrollbars.
908 This is a specifier; use `set-specifier' to change it.
909                                                                  */ );
910         Vscrollbar_height = make_magic_specifier(Qnatnum);
911         set_specifier_fallback
912             (Vscrollbar_height,
913              list1(Fcons(Qnil, make_int(DEFAULT_SCROLLBAR_HEIGHT))));
914         set_specifier_caching(Vscrollbar_height,
915                               offsetof(struct window, scrollbar_height),
916                               some_window_value_changed,
917                               offsetof(struct frame, scrollbar_height),
918                               frame_size_slipped, 0);
919
920         DEFVAR_SPECIFIER("horizontal-scrollbar-visible-p", &Vhorizontal_scrollbar_visible_p     /*
921 *Whether the horizontal scrollbar is visible.
922 This is a specifier; use `set-specifier' to change it.
923                                                                                                  */ );
924         Vhorizontal_scrollbar_visible_p = Fmake_specifier(Qboolean);
925         set_specifier_fallback(Vhorizontal_scrollbar_visible_p,
926                                list1(Fcons(Qnil, Qt)));
927         set_specifier_caching(Vhorizontal_scrollbar_visible_p,
928                               offsetof(struct window,
929                                        horizontal_scrollbar_visible_p),
930                               some_window_value_changed,
931                               offsetof(struct frame,
932                                        horizontal_scrollbar_visible_p),
933                               frame_size_slipped, 0);
934
935         DEFVAR_SPECIFIER("vertical-scrollbar-visible-p", &Vvertical_scrollbar_visible_p /*
936 *Whether the vertical scrollbar is visible.
937 This is a specifier; use `set-specifier' to change it.
938                                                                                          */ );
939         Vvertical_scrollbar_visible_p = Fmake_specifier(Qboolean);
940         set_specifier_fallback(Vvertical_scrollbar_visible_p,
941                                list1(Fcons(Qnil, Qt)));
942         set_specifier_caching(Vvertical_scrollbar_visible_p,
943                               offsetof(struct window,
944                                        vertical_scrollbar_visible_p),
945                               vertical_scrollbar_changed_in_window,
946                               offsetof(struct frame,
947                                        vertical_scrollbar_visible_p),
948                               frame_size_slipped, 0);
949
950         DEFVAR_SPECIFIER("scrollbar-on-left-p", &Vscrollbar_on_left_p   /*
951 *Whether the vertical scrollbar is on the left side of window or frame.
952 This is a specifier; use `set-specifier' to change it.
953                                                                          */ );
954         Vscrollbar_on_left_p = Fmake_specifier(Qboolean);
955
956         {
957                 /* Kludge. Under X, we want athena scrollbars on the left,
958                    while all other scrollbars go on the right by default. */
959                 Lisp_Object fallback = list1(Fcons(Qnil, Qnil));
960 #if defined (HAVE_X_WINDOWS)                    \
961     && !defined (LWLIB_SCROLLBARS_MOTIF)        \
962     && !defined (LWLIB_SCROLLBARS_LUCID)        \
963     && !defined (LWLIB_SCROLLBARS_ATHENA3D)
964
965                 fallback = Fcons(Fcons(list1(Qx), Qt), fallback);
966 #endif
967                 set_specifier_fallback(Vscrollbar_on_left_p, fallback);
968         }
969
970         set_specifier_caching(Vscrollbar_on_left_p,
971                               offsetof(struct window, scrollbar_on_left_p),
972                               vertical_scrollbar_changed_in_window,
973                               offsetof(struct frame, scrollbar_on_left_p),
974                               frame_size_slipped, 0);
975
976         DEFVAR_SPECIFIER("scrollbar-on-top-p", &Vscrollbar_on_top_p     /*
977 *Whether the horizontal scrollbar is on the top side of window or frame.
978 This is a specifier; use `set-specifier' to change it.
979                                                                          */ );
980         Vscrollbar_on_top_p = Fmake_specifier(Qboolean);
981         set_specifier_fallback(Vscrollbar_on_top_p, list1(Fcons(Qnil, Qnil)));
982         set_specifier_caching(Vscrollbar_on_top_p,
983                               offsetof(struct window, scrollbar_on_top_p),
984                               some_window_value_changed,
985                               offsetof(struct frame, scrollbar_on_top_p),
986                               frame_size_slipped, 0);
987 }
988
989 void complex_vars_of_scrollbar(void)
990 {
991         Vscrollbar_pointer_glyph = Fmake_glyph_internal(Qpointer);
992
993         set_specifier_caching(XGLYPH(Vscrollbar_pointer_glyph)->image,
994                               offsetof(struct window, scrollbar_pointer),
995                               scrollbar_pointer_changed_in_window, 0, 0, 0);
996 }