]> www.wagner.pp.ru Git - oss/ck.git/blob - ckScrollbar.c
Ck console graphics toolkit
[oss/ck.git] / ckScrollbar.c
1 /* 
2  * ckScrollbar.c --
3  *
4  *      This module implements a scrollbar widgets for the
5  *      toolkit.  A scrollbar displays a slider and two arrows;
6  *      mouse clicks on features within the scrollbar cause
7  *      scrolling commands to be invoked.
8  *
9  * Copyright (c) 1990-1994 The Regents of the University of California.
10  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
11  * Copyright (c) 1995 Christian Werner
12  *
13  * See the file "license.terms" for information on usage and redistribution
14  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15  */
16
17 #include "ckPort.h"
18 #include "ck.h"
19 #include "default.h"
20
21 /*
22  * A data structure of the following type is kept for each scrollbar
23  * widget managed by this file:
24  */
25
26 typedef struct {
27     CkWindow *winPtr;           /* Window that embodies the scrollbar.  NULL
28                                  * means that the window has been destroyed
29                                  * but the data structures haven't yet been
30                                  * cleaned up.*/
31     Tcl_Interp *interp;         /* Interpreter associated with scrollbar. */
32     Tcl_Command widgetCmd;      /* Token for scrollbar's widget command. */
33     Ck_Uid orientUid;           /* Orientation for window ("vertical" or
34                                  * "horizontal"). */
35     int vertical;               /* Non-zero means vertical orientation
36                                  * requested, zero means horizontal. */
37     char *command;              /* Command prefix to use when invoking
38                                  * scrolling commands.  NULL means don't
39                                  * invoke commands.  Malloc'ed. */
40     int commandSize;            /* Number of non-NULL bytes in command. */
41
42     /*
43      * Information used when displaying widget:
44      */
45
46     int normalBg;               /* Used for drawing background. */
47     int normalFg;               /* Used for drawing foreground. */
48     int normalAttr;             /* Video attributes for normal mode. */
49     int activeBg;               /* Background in active mode. */
50     int activeFg;               /* Foreground in active mode. */
51     int activeAttr;             /* Video attributes for active mode. */
52
53     int sliderFirst;            /* Coordinate of top or left edge
54                                  * of slider area. */
55     int sliderLast;             /* Coordinate just after bottom
56                                  * or right edge of slider area. */
57     /*
58      * Information describing the application related to the scrollbar.
59      * This information is provided by the application by invoking the
60      * "set" widget command.
61      */
62
63     double firstFraction;       /* Position of first visible thing in window,
64                                  * specified as a fraction between 0 and
65                                  * 1.0. */
66     double lastFraction;        /* Position of last visible thing in window,
67                                  * specified as a fraction between 0 and
68                                  * 1.0. */
69
70     /*
71      * Miscellaneous information:
72      */
73
74     char *takeFocus;            /* Value of -takefocus option;  not used in
75                                  * the C code, but used by keyboard traversal
76                                  * scripts.  Malloc'ed, but may be NULL. */
77     int flags;                  /* Various flags;  see below for
78                                  * definitions. */
79 } Scrollbar;
80
81 /*
82  * Flag bits for scrollbars:
83  * 
84  * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler
85  *                              has already been queued to redraw
86  *                              this window.
87  * ACTIVATED:                   1 means draw in activated mode,
88  *                              0 means draw in normal mode
89  */
90
91 #define REDRAW_PENDING          1
92 #define ACTIVATED               2
93
94 /*
95  * Legal values for identifying position in scrollbar. These
96  * are the return values from the ScrollbarPosition procedure.
97  */
98
99 #define OUTSIDE         0
100 #define TOP_ARROW       1
101 #define TOP_GAP         2
102 #define SLIDER          3
103 #define BOTTOM_GAP      4
104 #define BOTTOM_ARROW    5
105
106 /*
107  * Minimum slider length, in pixels (designed to make sure that the slider
108  * is always easy to grab with the mouse).
109  */
110
111 #define MIN_SLIDER_LENGTH       1
112
113 /*
114  * Information used for argv parsing.
115  */
116
117 static Ck_ConfigSpec configSpecs[] = {
118     {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes", "Attributes",
119         DEF_SCROLLBAR_ACTIVE_ATTR_COLOR, Ck_Offset(Scrollbar, activeAttr),
120         CK_CONFIG_COLOR_ONLY},
121     {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes", "Attributes",
122         DEF_SCROLLBAR_ACTIVE_ATTR_MONO, Ck_Offset(Scrollbar, activeAttr),
123         CK_CONFIG_MONO_ONLY},
124     {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
125         DEF_SCROLLBAR_ACTIVE_BG_COLOR, Ck_Offset(Scrollbar, activeBg),
126         CK_CONFIG_COLOR_ONLY},
127     {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
128         DEF_SCROLLBAR_ACTIVE_BG_MONO, Ck_Offset(Scrollbar, activeBg),
129         CK_CONFIG_MONO_ONLY},
130     {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
131         DEF_SCROLLBAR_ACTIVE_FG_COLOR, Ck_Offset(Scrollbar, activeFg),
132         CK_CONFIG_COLOR_ONLY},
133     {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
134         DEF_SCROLLBAR_ACTIVE_FG_MONO, Ck_Offset(Scrollbar, activeBg),
135         CK_CONFIG_MONO_ONLY},
136     {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
137         DEF_SCROLLBAR_ATTR, Ck_Offset(Scrollbar, normalAttr), 0},
138     {CK_CONFIG_COLOR, "-background", "background", "Background",
139         DEF_SCROLLBAR_BG_COLOR, Ck_Offset(Scrollbar, normalBg),
140         CK_CONFIG_COLOR_ONLY},
141     {CK_CONFIG_COLOR, "-background", "background", "Background",
142         DEF_SCROLLBAR_BG_MONO, Ck_Offset(Scrollbar, normalBg),
143         CK_CONFIG_MONO_ONLY},
144     {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
145         (char *) NULL, 0, 0},
146     {CK_CONFIG_STRING, "-command", "command", "Command",
147         DEF_SCROLLBAR_COMMAND, Ck_Offset(Scrollbar, command),
148         CK_CONFIG_NULL_OK},
149     {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
150         DEF_SCROLLBAR_FG_COLOR, Ck_Offset(Scrollbar, normalFg),
151         CK_CONFIG_COLOR_ONLY},
152     {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
153         DEF_SCROLLBAR_FG_MONO, Ck_Offset(Scrollbar, normalFg),
154         CK_CONFIG_MONO_ONLY},
155     {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
156         (char *) NULL, 0, 0},
157     {CK_CONFIG_UID, "-orient", "orient", "Orient",
158         DEF_SCROLLBAR_ORIENT, Ck_Offset(Scrollbar, orientUid), 0},
159     {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
160         DEF_SCROLLBAR_TAKE_FOCUS, Ck_Offset(Scrollbar, takeFocus),
161         CK_CONFIG_NULL_OK},
162     {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
163         (char *) NULL, 0, 0}
164 };
165
166 /*
167  * Forward declarations for procedures defined later in this file:
168  */
169
170 static void             ComputeScrollbarGeometry _ANSI_ARGS_((
171                             Scrollbar *scrollPtr));
172 static int              ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
173                             Scrollbar *scrollPtr, int argc, char **argv,
174                             int flags));
175 static void             DestroyScrollbar _ANSI_ARGS_((ClientData clientData));
176 static void             DisplayScrollbar _ANSI_ARGS_((ClientData clientData));
177 static void             EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr));
178 static void             ScrollbarEventProc _ANSI_ARGS_((ClientData clientData,
179                             CkEvent *eventPtr));
180 static void             ScrollbarCmdDeletedProc _ANSI_ARGS_((
181                             ClientData clientData));
182 static int              ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr,
183                             int x, int y));
184 static int              ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
185                             Tcl_Interp *, int argc, char **argv));
186 \f
187 /*
188  *--------------------------------------------------------------
189  *
190  * Ck_ScrollbarCmd --
191  *
192  *      This procedure is invoked to process the "scrollbar" Tcl
193  *      command.  See the user documentation for details on what
194  *      it does.
195  *
196  * Results:
197  *      A standard Tcl result.
198  *
199  * Side effects:
200  *      See the user documentation.
201  *
202  *--------------------------------------------------------------
203  */
204
205 int
206 Ck_ScrollbarCmd(clientData, interp, argc, argv)
207     ClientData clientData;      /* Main window associated with
208                                  * interpreter. */
209     Tcl_Interp *interp;         /* Current interpreter. */
210     int argc;                   /* Number of arguments. */
211     char **argv;                /* Argument strings. */
212 {
213     CkWindow *mainPtr = (CkWindow *) clientData;
214     register Scrollbar *scrollPtr;
215     CkWindow *new;
216
217     if (argc < 2) {
218         Tcl_AppendResult(interp, "wrong # args:  should be \"",
219                 argv[0], " pathName ?options?\"", (char *) NULL);
220         return TCL_ERROR;
221     }
222
223     new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
224     if (new == NULL) {
225         return TCL_ERROR;
226     }
227
228     /*
229      * Initialize fields that won't be initialized by ConfigureScrollbar,
230      * or which ConfigureScrollbar expects to have reasonable values
231      * (e.g. resource pointers).
232      */
233
234     scrollPtr = (Scrollbar *) ckalloc(sizeof (Scrollbar));
235     scrollPtr->winPtr = new;
236     scrollPtr->interp = interp;
237     scrollPtr->widgetCmd = Tcl_CreateCommand(interp,
238         scrollPtr->winPtr->pathName, ScrollbarWidgetCmd,
239             (ClientData) scrollPtr, ScrollbarCmdDeletedProc);
240     scrollPtr->orientUid = NULL;
241     scrollPtr->vertical = 0;
242     scrollPtr->command = NULL;
243     scrollPtr->commandSize = 0;
244     scrollPtr->normalBg = 0;
245     scrollPtr->normalFg = 0;
246     scrollPtr->normalAttr = 0;
247     scrollPtr->activeBg = 0;
248     scrollPtr->activeFg = 0;
249     scrollPtr->activeAttr = 0;
250     scrollPtr->firstFraction = 0.0;
251     scrollPtr->lastFraction = 0.0;
252     scrollPtr->takeFocus = NULL;
253     scrollPtr->flags = 0;
254
255     Ck_SetClass(scrollPtr->winPtr, "Scrollbar");
256     Ck_CreateEventHandler(scrollPtr->winPtr,
257             CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
258             ScrollbarEventProc, (ClientData) scrollPtr);
259     if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
260         goto error;
261     }
262
263     interp->result = scrollPtr->winPtr->pathName;
264     return TCL_OK;
265
266 error:
267     Ck_DestroyWindow(scrollPtr->winPtr);
268     return TCL_ERROR;
269 }
270 \f
271 /*
272  *--------------------------------------------------------------
273  *
274  * ScrollbarWidgetCmd --
275  *
276  *      This procedure is invoked to process the Tcl command
277  *      that corresponds to a widget managed by this module.
278  *      See the user documentation for details on what it does.
279  *
280  * Results:
281  *      A standard Tcl result.
282  *
283  * Side effects:
284  *      See the user documentation.
285  *
286  *--------------------------------------------------------------
287  */
288
289 static int
290 ScrollbarWidgetCmd(clientData, interp, argc, argv)
291     ClientData clientData;      /* Information about scrollbar
292                                          * widget. */
293     Tcl_Interp *interp;                 /* Current interpreter. */
294     int argc;                           /* Number of arguments. */
295     char **argv;                        /* Argument strings. */
296 {
297     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
298     int result = TCL_OK;
299     size_t length;
300     int c;
301
302     if (argc < 2) {
303         Tcl_AppendResult(interp, "wrong # args: should be \"",
304                 argv[0], " option ?arg arg ...?\"", (char *) NULL);
305         return TCL_ERROR;
306     }
307     Ck_Preserve((ClientData) scrollPtr);
308     c = argv[1][0];
309     length = strlen(argv[1]);
310     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
311         if (argc != 2) {
312             Tcl_AppendResult(interp, "wrong # args: should be \"",
313                     argv[0], " activate\"", (char *) NULL);
314             goto error;
315         }
316         if (!(scrollPtr->flags & ACTIVATED)) {
317             scrollPtr->flags |= ACTIVATED;
318             EventuallyRedraw(scrollPtr);
319         }
320     } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
321         && (length >= 2)) {
322         if (argc != 3) {
323             Tcl_AppendResult(interp, "wrong # args: should be \"",
324                     argv[0], " cget option\"",
325                     (char *) NULL);
326             goto error;
327         }
328         result = Ck_ConfigureValue(interp, scrollPtr->winPtr, configSpecs,
329                 (char *) scrollPtr, argv[2], 0);
330     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
331             && (length >= 2)) {
332         if (argc == 2) {
333             result = Ck_ConfigureInfo(interp, scrollPtr->winPtr, configSpecs,
334                     (char *) scrollPtr, (char *) NULL, 0);
335         } else if (argc == 3) {
336             result = Ck_ConfigureInfo(interp, scrollPtr->winPtr, configSpecs,
337                     (char *) scrollPtr, argv[2], 0);
338         } else {
339             result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
340                     CK_CONFIG_ARGV_ONLY);
341         }
342    } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)) {
343         if (argc != 2) {
344             Tcl_AppendResult(interp, "wrong # args: should be \"",
345                     argv[0], " deactivate\"", (char *) NULL);
346             goto error;
347         }
348         if (scrollPtr->flags & ACTIVATED) {
349             scrollPtr->flags &= ~ACTIVATED;
350             EventuallyRedraw(scrollPtr);
351         }
352     } else if ((c == 'f') && (strncmp(argv[1], "fraction", length) == 0)) {
353         int x, y, pos, length;
354         double fraction;
355
356         if (argc != 4) {
357             Tcl_AppendResult(interp, "wrong # args: should be \"",
358                     argv[0], " fraction x y\"", (char *) NULL);
359             goto error;
360         }
361         if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
362                 || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
363             goto error;
364         }
365         if (scrollPtr->vertical) {
366             pos = y - 1;
367             length = scrollPtr->winPtr->height - 1 - 2;
368         } else {
369             pos = x - 1;
370             length = scrollPtr->winPtr->width - 1 - 2;
371         }
372         if (length == 0) {
373             fraction = 0.0;
374         } else {
375             fraction = ((double) pos / (double) length);
376         }
377         if (fraction < 0) {
378             fraction = 0;
379         } else if (fraction > 1.0) {
380             fraction = 1.0;
381         }
382         sprintf(interp->result, "%g", fraction);
383     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
384         char first[TCL_DOUBLE_SPACE], last[TCL_DOUBLE_SPACE];
385         if (argc != 2) {
386             Tcl_AppendResult(interp, "wrong # args: should be \"",
387                     argv[0], " get\"", (char *) NULL);
388             goto error;
389         }
390         Tcl_PrintDouble(interp, scrollPtr->firstFraction, first);
391         Tcl_PrintDouble(interp, scrollPtr->lastFraction, last);
392         Tcl_AppendResult(interp, first, " ", last, (char *) NULL);
393     } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
394         int x, y, thing;
395
396         if (argc != 4) {
397             Tcl_AppendResult(interp, "wrong # args: should be \"",
398                     argv[0], " identify x y\"", (char *) NULL);
399             goto error;
400         }
401         if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
402             || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
403             goto error;
404         }
405         thing = ScrollbarPosition(scrollPtr, x, y);
406         switch (thing) {
407             case TOP_ARROW:     interp->result = "arrow1";      break;
408             case TOP_GAP:       interp->result = "trough1";     break;
409             case SLIDER:        interp->result = "slider";      break;
410             case BOTTOM_GAP:    interp->result = "trough2";     break;
411             case BOTTOM_ARROW:  interp->result = "arrow2";      break;
412         }
413     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
414         if (argc == 4) {
415             double first, last;
416
417             if (Tcl_GetDouble(interp, argv[2], &first) != TCL_OK) {
418                 goto error;
419             }
420             if (Tcl_GetDouble(interp, argv[3], &last) != TCL_OK) {
421                 goto error;
422             }
423             if (first < 0) {
424                 scrollPtr->firstFraction = 0;
425             } else if (first > 1.0) {
426                 scrollPtr->firstFraction = 1.0;
427             } else {
428                 scrollPtr->firstFraction = first;
429             }
430             if (last < scrollPtr->firstFraction) {
431                 scrollPtr->lastFraction = scrollPtr->firstFraction;
432             } else if (last > 1.0) {
433                 scrollPtr->lastFraction = 1.0;
434             } else {
435                 scrollPtr->lastFraction = last;
436             }
437         } else {
438             Tcl_AppendResult(interp, "wrong # args: should be \"",
439                     argv[0], " set firstFraction lastFraction\"",
440                     (char *) NULL);
441             goto error;
442         }
443         ComputeScrollbarGeometry(scrollPtr);
444         EventuallyRedraw(scrollPtr);
445     } else {
446         Tcl_AppendResult(interp, "bad option \"", argv[1],
447                 "\": must be activate, cget, configure, deactivate, ",
448                 "fraction, get, or set", (char *) NULL);
449         goto error;
450     }
451     Ck_Release((ClientData) scrollPtr);
452     return result;
453
454 error:
455     Ck_Release((ClientData) scrollPtr);
456     return TCL_ERROR;
457 }
458 \f
459 /*
460  *----------------------------------------------------------------------
461  *
462  * DestroyScrollbar --
463  *
464  *      This procedure is invoked by Ck_EventuallyFree or Ck_Release
465  *      to clean up the internal structure of a scrollbar at a safe time
466  *      (when no-one is using it anymore).
467  *
468  * Results:
469  *      None.
470  *
471  * Side effects:
472  *      Everything associated with the scrollbar is freed up.
473  *
474  *----------------------------------------------------------------------
475  */
476
477 static void
478 DestroyScrollbar(clientData)
479     ClientData clientData;      /* Info about scrollbar widget. */
480 {
481     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
482
483     /*
484      * Free up all the stuff that requires special handling, then
485      * let Ck_FreeOptions handle all the standard option-related
486      * stuff.
487      */
488
489     Ck_FreeOptions(configSpecs, (char *) scrollPtr, 0);
490     ckfree((char *) scrollPtr);
491 }
492 \f
493 /*
494  *----------------------------------------------------------------------
495  *
496  * ScrollbarCmdDeletedProc --
497  *
498  *      This procedure is invoked when a widget command is deleted.  If
499  *      the widget isn't already in the process of being destroyed,
500  *      this command destroys it.
501  *
502  * Results:
503  *      None.
504  *
505  * Side effects:
506  *      The widget is destroyed.
507  *
508  *----------------------------------------------------------------------
509  */
510
511 static void
512 ScrollbarCmdDeletedProc(clientData)
513     ClientData clientData;      /* Pointer to widget record for widget. */
514 {
515     Scrollbar *scrollPtr = (Scrollbar *) clientData;
516     CkWindow *winPtr = scrollPtr->winPtr;
517
518     /*
519      * This procedure could be invoked either because the window was
520      * destroyed and the command was then deleted (in which case winPtr
521      * is NULL) or because the command was deleted, and then this procedure
522      * destroys the widget.
523      */
524
525     if (winPtr != NULL) {
526         scrollPtr->winPtr = NULL;
527         Ck_DestroyWindow(winPtr);
528     }
529 }
530 \f
531 /*
532  *----------------------------------------------------------------------
533  *
534  * ConfigureScrollbar --
535  *
536  *      This procedure is called to process an argv/argc list, plus
537  *      the option database, in order to configure (or
538  *      reconfigure) a scrollbar widget.
539  *
540  * Results:
541  *      The return value is a standard Tcl result.  If TCL_ERROR is
542  *      returned, then interp->result contains an error message.
543  *
544  * Side effects:
545  *      Configuration information, such as colors, border width,
546  *      etc. get set for scrollPtr;  old resources get freed,
547  *      if there were any.
548  *
549  *----------------------------------------------------------------------
550  */
551
552 static int
553 ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
554     Tcl_Interp *interp;                 /* Used for error reporting. */
555     register Scrollbar *scrollPtr;      /* Information about widget;  may or
556                                          * may not already have values for
557                                          * some fields. */
558     int argc;                           /* Number of valid entries in argv. */
559     char **argv;                        /* Arguments. */
560     int flags;                          /* Flags to pass to
561                                          * Ck_ConfigureWidget. */
562 {
563     size_t length;
564
565     if (Ck_ConfigureWidget(interp, scrollPtr->winPtr, configSpecs,
566             argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
567         return TCL_ERROR;
568     }
569
570     /*
571      * A few options need special processing, such as parsing the
572      * orientation or setting the background from a 3-D border.
573      */
574
575     length = strlen(scrollPtr->orientUid);
576     if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
577         scrollPtr->vertical = 1;
578     } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
579         scrollPtr->vertical = 0;
580     } else {
581         Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
582                 "\": must be vertical or horizontal", (char *) NULL);
583         return TCL_ERROR;
584     }
585
586     if (scrollPtr->command != NULL) {
587         scrollPtr->commandSize = strlen(scrollPtr->command);
588     } else {
589         scrollPtr->commandSize = 0;
590     }
591
592     /*
593      * Register the desired geometry for the window (leave enough space
594      * for the two arrows plus a minimum-size slider, plus border around
595      * the whole window, if any).  Then arrange for the window to be
596      * redisplayed.
597      */
598
599     ComputeScrollbarGeometry(scrollPtr);
600     EventuallyRedraw(scrollPtr);
601     return TCL_OK;
602 }
603 \f
604 /*
605  *--------------------------------------------------------------
606  *
607  * DisplayScrollbar --
608  *
609  *      This procedure redraws the contents of a scrollbar window.
610  *      It is invoked as a do-when-idle handler, so it only runs
611  *      when there's nothing else for the application to do.
612  *
613  * Results:
614  *      None.
615  *
616  * Side effects:
617  *      Information appears on the screen.
618  *
619  *--------------------------------------------------------------
620  */
621
622 static void
623 DisplayScrollbar(clientData)
624     ClientData clientData;      /* Information about window. */
625 {
626     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
627     register CkWindow *winPtr = scrollPtr->winPtr;
628     int width, i, gchar;
629
630     if ((scrollPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
631         goto done;
632     }
633
634     width = (scrollPtr->vertical ? winPtr->height : winPtr->width);
635
636     if (scrollPtr->flags & ACTIVATED) {
637         Ck_SetWindowAttr(winPtr, scrollPtr->activeFg,
638             scrollPtr->activeBg, scrollPtr->activeAttr);
639     } else {
640         Ck_SetWindowAttr(winPtr, scrollPtr->normalFg,
641             scrollPtr->normalBg, scrollPtr->normalAttr);
642     }
643
644     /*
645      * Fill space left with blanks.
646      */
647
648     if (scrollPtr->vertical) {
649         for (i = 0; i < width; i++) {
650             wmove(winPtr->window, i, 0);
651             waddch(winPtr->window, ' ');
652         }
653     } else {
654         wmove(winPtr->window, 0, 0);
655         for (i = 0; i < width; i++) {
656             waddch(winPtr->window, ' ');
657         }
658     }
659
660     /*
661      * Display the slider.
662      */
663
664     Ck_GetGChar(scrollPtr->interp, "ckboard", &gchar);
665     if (scrollPtr->vertical) {
666         for (i = scrollPtr->sliderFirst; i < scrollPtr->sliderLast; i++) {
667             mvwaddch(winPtr->window, i, 0, gchar);
668         }
669     } else {
670         wmove(winPtr->window, 0, scrollPtr->sliderFirst);
671         for (i = scrollPtr->sliderFirst; i < scrollPtr->sliderLast; i++) {
672             waddch(winPtr->window, gchar);
673         }
674     }
675
676     /*
677      * Display top or left arrow.
678      */
679
680     Ck_GetGChar(scrollPtr->interp, scrollPtr->vertical ? "uarrow" : "larrow",
681         &gchar);
682     mvwaddch(winPtr->window, 0, 0, gchar);
683
684     /*
685      * Display the bottom or right arrow.
686      */
687
688     Ck_GetGChar(scrollPtr->interp, scrollPtr->vertical ? "darrow" : "rarrow",
689         &gchar);
690     scrollPtr->vertical ? wmove(winPtr->window, width - 1, 0) :
691         wmove(winPtr->window, 0, width - 1);
692     waddch(winPtr->window, gchar);
693
694     Ck_EventuallyRefresh(winPtr);
695
696 done:
697     scrollPtr->flags &= ~REDRAW_PENDING;
698 }
699 \f
700 /*
701  *--------------------------------------------------------------
702  *
703  * ScrollbarEventProc --
704  *
705  *      This procedure is invoked by the dispatcher for various
706  *      events on scrollbars.
707  *
708  * Results:
709  *      None.
710  *
711  * Side effects:
712  *      When the window gets deleted, internal structures get
713  *      cleaned up.  When it gets exposed, it is redisplayed.
714  *
715  *--------------------------------------------------------------
716  */
717
718 static void
719 ScrollbarEventProc(clientData, eventPtr)
720     ClientData clientData;      /* Information about window. */
721     CkEvent *eventPtr;          /* Information about event. */
722 {
723     Scrollbar *scrollPtr = (Scrollbar *) clientData;
724
725     if (eventPtr->type == CK_EV_EXPOSE) {
726         ComputeScrollbarGeometry(scrollPtr);
727         EventuallyRedraw(scrollPtr);
728     } else if (eventPtr->type == CK_EV_DESTROY) {
729         if (scrollPtr->winPtr != NULL) {
730             scrollPtr->winPtr = NULL;
731             Tcl_DeleteCommand(scrollPtr->interp,
732                 Tcl_GetCommandName(scrollPtr->interp, scrollPtr->widgetCmd));
733         }
734         if (scrollPtr->flags & REDRAW_PENDING) {
735             Tk_CancelIdleCall(DisplayScrollbar, (ClientData) scrollPtr);
736         }
737         Ck_EventuallyFree((ClientData) scrollPtr,
738             (Ck_FreeProc *) DestroyScrollbar);
739     }
740 }
741 \f
742 /*
743  *----------------------------------------------------------------------
744  *
745  * ComputeScrollbarGeometry --
746  *
747  *      After changes in a scrollbar's size or configuration, this
748  *      procedure recomputes various geometry information used in
749  *      displaying the scrollbar.
750  *
751  * Results:
752  *      None.
753  *
754  * Side effects:
755  *      The scrollbar will be displayed differently.
756  *
757  *----------------------------------------------------------------------
758  */
759
760 static void
761 ComputeScrollbarGeometry(scrollPtr)
762     register Scrollbar *scrollPtr;      /* Scrollbar whose geometry may
763                                          * have changed. */
764 {
765     int fieldLength;
766
767     fieldLength = (scrollPtr->vertical ? scrollPtr->winPtr->height
768             : scrollPtr->winPtr->width) - 2;
769     if (fieldLength < 0) {
770         fieldLength = 0;
771     }
772     scrollPtr->sliderFirst = (int) (fieldLength * scrollPtr->firstFraction);
773     scrollPtr->sliderLast = (int) (fieldLength * scrollPtr->lastFraction);
774
775     /*
776      * Adjust the slider so that some piece of it is always
777      * displayed in the scrollbar and so that it has at least
778      * a minimal width (so it can be grabbed with the mouse).
779      */
780
781     if (scrollPtr->sliderFirst > fieldLength) {
782         scrollPtr->sliderFirst = fieldLength;
783     }
784     if (scrollPtr->sliderFirst < 0) {
785         scrollPtr->sliderFirst = 0;
786     }
787     if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
788             + MIN_SLIDER_LENGTH)) {
789         scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
790     }
791     if (scrollPtr->sliderLast > fieldLength) {
792         scrollPtr->sliderLast = fieldLength;
793     }
794     scrollPtr->sliderFirst += 1;
795     scrollPtr->sliderLast += 1;
796
797     /*
798      * Register the desired geometry for the window (leave enough space
799      * for the two arrows plus a minimum-size slider, plus border around
800      * the whole window, if any).  Then arrange for the window to be
801      * redisplayed.
802      */
803
804     if (scrollPtr->vertical) {
805         Ck_GeometryRequest(scrollPtr->winPtr, 1, 2 + MIN_SLIDER_LENGTH);
806     } else {
807         Ck_GeometryRequest(scrollPtr->winPtr, 2 + MIN_SLIDER_LENGTH, 1);
808     }
809 }
810 \f
811 /*
812  *--------------------------------------------------------------
813  *
814  * ScrollbarPosition --
815  *
816  *      Determine the scrollbar element corresponding to a
817  *      given position.
818  *
819  * Results:
820  *      One of TOP_ARROW, TOP_GAP, etc., indicating which element
821  *      of the scrollbar covers the position given by (x, y).  If
822  *      (x,y) is outside the scrollbar entirely, then OUTSIDE is
823  *      returned.
824  *
825  * Side effects:
826  *      None.
827  *
828  *--------------------------------------------------------------
829  */
830
831 static int
832 ScrollbarPosition(scrollPtr, x, y)
833     register Scrollbar *scrollPtr;      /* Scrollbar widget record. */
834     int x, y;                           /* Coordinates within scrollPtr's
835                                          * window. */
836 {
837     int length, width, tmp;
838
839     if (scrollPtr->vertical) {
840         length = scrollPtr->winPtr->height;
841         width = scrollPtr->winPtr->width;
842     } else {
843         tmp = x;
844         x = y;
845         y = tmp;
846         length = scrollPtr->winPtr->width;
847         width = scrollPtr->winPtr->height;
848     }
849
850     if (x < 0 || x >= width || y < 0 || y >= length)
851         return OUTSIDE;
852
853     if (y == 0)
854         return TOP_ARROW;
855     if (y < scrollPtr->sliderFirst)
856         return TOP_GAP;
857     if (y < scrollPtr->sliderLast)
858         return SLIDER;
859     if (y == length - 1)
860         return BOTTOM_ARROW;
861     return BOTTOM_GAP;
862 }
863 \f
864 /*
865  *--------------------------------------------------------------
866  *
867  * EventuallyRedraw --
868  *
869  *      Arrange for one or more of the fields of a scrollbar
870  *      to be redrawn.
871  *
872  * Results:
873  *      None.
874  *
875  * Side effects:
876  *      None.
877  *
878  *--------------------------------------------------------------
879  */
880
881 static void
882 EventuallyRedraw(scrollPtr)
883     register Scrollbar *scrollPtr;      /* Information about widget. */
884 {
885     if ((scrollPtr->winPtr == NULL) ||
886         !(scrollPtr->winPtr->flags & CK_MAPPED)) {
887         return;
888     }
889     if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
890         Tk_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr);
891         scrollPtr->flags |= REDRAW_PENDING;
892     }
893 }