]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * tkScrollbar.c -- | |
3 | * | |
4 | * This module implements a scrollbar widgets for the Tk | |
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 1990-1992 Regents of the University of California. | |
10 | * Permission to use, copy, modify, and distribute this | |
11 | * software and its documentation for any purpose and without | |
12 | * fee is hereby granted, provided that the above copyright | |
13 | * notice appear in all copies. The University of California | |
14 | * makes no representations about the suitability of this | |
15 | * software for any purpose. It is provided "as is" without | |
16 | * express or implied warranty. | |
17 | */ | |
18 | ||
19 | #ifndef lint | |
20 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkScrollbar.c,v 1.35 92/05/22 16:57:27 ouster Exp $ SPRITE (Berkeley)"; | |
21 | #endif | |
22 | ||
23 | #include "tkconfig.h" | |
24 | #include "default.h" | |
25 | #include "tkint.h" | |
26 | ||
27 | /* | |
28 | * A data structure of the following type is kept for each scrollbar | |
29 | * widget managed by this file: | |
30 | */ | |
31 | ||
32 | typedef struct { | |
33 | Tk_Window tkwin; /* Window that embodies the scrollbar. NULL | |
34 | * means that the window has been destroyed | |
35 | * but the data structures haven't yet been | |
36 | * cleaned up.*/ | |
37 | Tcl_Interp *interp; /* Interpreter associated with scrollbar. */ | |
38 | Tk_Uid orientUid; /* Orientation for window ("vertical" or | |
39 | * "horizontal"). */ | |
40 | int vertical; /* Non-zero means vertical orientation | |
41 | * requested, zero means horizontal. */ | |
42 | int width; /* Desired narrow dimension of scrollbar, | |
43 | * in pixels. */ | |
44 | char *command; /* Command prefix to use when invoking | |
45 | * scrolling commands. NULL means don't | |
46 | * invoke commands. Malloc'ed. */ | |
47 | int commandSize; /* Number of non-NULL bytes in command. */ | |
48 | int repeatDelay; /* How long to wait before auto-repeating | |
49 | * on scrolling actions (in ms). */ | |
50 | int repeatInterval; /* Interval between autorepeats (in ms). */ | |
51 | ||
52 | /* | |
53 | * Information used when displaying widget: | |
54 | */ | |
55 | ||
56 | int borderWidth; /* Width of 3-D borders. */ | |
57 | Tk_3DBorder bgBorder; /* Used for drawing background. */ | |
58 | Tk_3DBorder fgBorder; /* For drawing foreground shapes. */ | |
59 | Tk_3DBorder activeBorder; /* For drawing foreground shapes when | |
60 | * active (i.e. when mouse is positioned | |
61 | * over element). NULL means use fgBorder. */ | |
62 | GC copyGC; /* Used for copying from pixmap onto screen. */ | |
63 | int relief; /* Indicates whether window as a whole is | |
64 | * raised, sunken, or flat. */ | |
65 | int offset; /* Zero if relief is TK_RELIEF_FLAT, | |
66 | * borderWidth otherwise. Indicates how | |
67 | * much interior stuff must be offset from | |
68 | * outside edges to leave room for border. */ | |
69 | int arrowLength; /* Length of arrows along long dimension of | |
70 | * scrollbar. Recomputed on window size | |
71 | * changes. */ | |
72 | int sliderFirst; /* Pixel coordinate of top or left edge | |
73 | * of slider area, including border. */ | |
74 | int sliderLast; /* Coordinate of pixel just after bottom | |
75 | * or right edge of slider area, including | |
76 | * border. */ | |
77 | int mouseField; /* Indicates which scrollbar element is | |
78 | * under mouse (e.g. TOP_ARROW; see below | |
79 | * for possible values). */ | |
80 | int pressField; /* Field in which button was pressed, or -1 | |
81 | * if no button is down. */ | |
82 | int pressPos; /* Position of mouse when button was | |
83 | * pressed (y for vertical scrollbar, x | |
84 | * for horizontal). */ | |
85 | int pressFirstUnit; /* Value of "firstUnit" when mouse button | |
86 | * was pressed. */ | |
87 | ||
88 | /* | |
89 | * Information describing the application related to the scrollbar. | |
90 | * This information is provided by the application by invoking the | |
91 | * "set" widget command. | |
92 | */ | |
93 | ||
94 | int totalUnits; /* Total dimension of application, in | |
95 | * units. */ | |
96 | int windowUnits; /* Maximum number of units that can | |
97 | * be displayed in the window at | |
98 | * once. */ | |
99 | int firstUnit; /* Number of last unit visible in | |
100 | * application's window. */ | |
101 | int lastUnit; /* Index of last unit visible in window. */ | |
102 | ||
103 | /* | |
104 | * Miscellaneous information: | |
105 | */ | |
106 | ||
107 | Cursor cursor; /* Current cursor for window, or None. */ | |
108 | Tk_TimerToken autoRepeat; /* Token for auto-repeat that's | |
109 | * currently in progress. NULL means no | |
110 | * auto-repeat in progress. */ | |
111 | int flags; /* Various flags; see below for | |
112 | * definitions. */ | |
113 | } Scrollbar; | |
114 | ||
115 | /* | |
116 | * Legal values for "mouseField" field of Scrollbar structures. These | |
117 | * are also the return values from the ScrollbarPosition procedure. | |
118 | */ | |
119 | ||
120 | #define TOP_ARROW 1 | |
121 | #define TOP_GAP 2 | |
122 | #define SLIDER 3 | |
123 | #define BOTTOM_GAP 4 | |
124 | #define BOTTOM_ARROW 5 | |
125 | #define OUTSIDE 6 | |
126 | ||
127 | /* | |
128 | * Flag bits for scrollbars: | |
129 | * | |
130 | * REDRAW_PENDING: Non-zero means a DoWhenIdle handler | |
131 | * has already been queued to redraw | |
132 | * this window. | |
133 | */ | |
134 | ||
135 | #define REDRAW_PENDING 1 | |
136 | ||
137 | /* | |
138 | * Information used for argv parsing. | |
139 | */ | |
140 | ||
141 | ||
142 | static Tk_ConfigSpec configSpecs[] = { | |
143 | {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background", | |
144 | DEF_SCROLLBAR_ACTIVE_FG_COLOR, Tk_Offset(Scrollbar, activeBorder), | |
145 | TK_CONFIG_COLOR_ONLY}, | |
146 | {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background", | |
147 | DEF_SCROLLBAR_ACTIVE_FG_MONO, Tk_Offset(Scrollbar, activeBorder), | |
148 | TK_CONFIG_MONO_ONLY}, | |
149 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
150 | DEF_SCROLLBAR_BG_COLOR, Tk_Offset(Scrollbar, bgBorder), | |
151 | TK_CONFIG_COLOR_ONLY}, | |
152 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
153 | DEF_SCROLLBAR_BG_MONO, Tk_Offset(Scrollbar, bgBorder), | |
154 | TK_CONFIG_MONO_ONLY}, | |
155 | {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, | |
156 | (char *) NULL, 0, 0}, | |
157 | {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, | |
158 | (char *) NULL, 0, 0}, | |
159 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | |
160 | DEF_SCROLLBAR_BORDER_WIDTH, Tk_Offset(Scrollbar, borderWidth), 0}, | |
161 | {TK_CONFIG_STRING, "-command", "command", "Command", | |
162 | DEF_SCROLLBAR_COMMAND, Tk_Offset(Scrollbar, command), 0}, | |
163 | {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", | |
164 | DEF_SCROLLBAR_CURSOR, Tk_Offset(Scrollbar, cursor), TK_CONFIG_NULL_OK}, | |
165 | {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, | |
166 | (char *) NULL, 0, 0}, | |
167 | {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", | |
168 | DEF_SCROLLBAR_FG_COLOR, Tk_Offset(Scrollbar, fgBorder), | |
169 | TK_CONFIG_COLOR_ONLY}, | |
170 | {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", | |
171 | DEF_SCROLLBAR_FG_MONO, Tk_Offset(Scrollbar, fgBorder), | |
172 | TK_CONFIG_MONO_ONLY}, | |
173 | {TK_CONFIG_UID, "-orient", "orient", "Orient", | |
174 | DEF_SCROLLBAR_ORIENT, Tk_Offset(Scrollbar, orientUid), 0}, | |
175 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", | |
176 | DEF_SCROLLBAR_RELIEF, Tk_Offset(Scrollbar, relief), 0}, | |
177 | {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay", | |
178 | DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(Scrollbar, repeatDelay), 0}, | |
179 | {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval", | |
180 | DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(Scrollbar, repeatInterval), 0}, | |
181 | {TK_CONFIG_PIXELS, "-width", "width", "Width", | |
182 | DEF_SCROLLBAR_WIDTH, Tk_Offset(Scrollbar, width), 0}, | |
183 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
184 | (char *) NULL, 0, 0} | |
185 | }; | |
186 | ||
187 | /* | |
188 | * Forward declarations for procedures defined later in this file: | |
189 | */ | |
190 | ||
191 | static void ComputeScrollbarGeometry _ANSI_ARGS_(( | |
192 | Scrollbar *scrollPtr)); | |
193 | static int ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp, | |
194 | Scrollbar *scrollPtr, int argc, char **argv, | |
195 | int flags)); | |
196 | static void DestroyScrollbar _ANSI_ARGS_((ClientData clientData)); | |
197 | static void DisplayScrollbar _ANSI_ARGS_((ClientData clientData)); | |
198 | static void EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr)); | |
199 | static void ScrollbarEventProc _ANSI_ARGS_((ClientData clientData, | |
200 | XEvent *eventPtr)); | |
201 | static void ScrollbarMouseProc _ANSI_ARGS_((ClientData clientData, | |
202 | XEvent *eventPtr)); | |
203 | static void ScrollbarNewField _ANSI_ARGS_((Scrollbar *scrollPtr, | |
204 | int field)); | |
205 | static int ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr, | |
206 | int x, int y)); | |
207 | static void ScrollbarTimerProc _ANSI_ARGS_(( | |
208 | ClientData clientData)); | |
209 | static int ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData, | |
210 | Tcl_Interp *, int argc, char **argv)); | |
211 | static void ScrollCmd _ANSI_ARGS_((Scrollbar *scrollPtr, | |
212 | int unit)); | |
213 | \f | |
214 | /* | |
215 | *-------------------------------------------------------------- | |
216 | * | |
217 | * Tk_ScrollbarCmd -- | |
218 | * | |
219 | * This procedure is invoked to process the "scrollbar" Tcl | |
220 | * command. See the user documentation for details on what | |
221 | * it does. | |
222 | * | |
223 | * Results: | |
224 | * A standard Tcl result. | |
225 | * | |
226 | * Side effects: | |
227 | * See the user documentation. | |
228 | * | |
229 | *-------------------------------------------------------------- | |
230 | */ | |
231 | ||
232 | int | |
233 | Tk_ScrollbarCmd(clientData, interp, argc, argv) | |
234 | ClientData clientData; /* Main window associated with | |
235 | * interpreter. */ | |
236 | Tcl_Interp *interp; /* Current interpreter. */ | |
237 | int argc; /* Number of arguments. */ | |
238 | char **argv; /* Argument strings. */ | |
239 | { | |
240 | Tk_Window tkwin = (Tk_Window) clientData; | |
241 | register Scrollbar *scrollPtr; | |
242 | Tk_Window new; | |
243 | ||
244 | if (argc < 2) { | |
245 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
246 | argv[0], " pathName ?options?\"", (char *) NULL); | |
247 | return TCL_ERROR; | |
248 | } | |
249 | ||
250 | new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL); | |
251 | if (new == NULL) { | |
252 | return TCL_ERROR; | |
253 | } | |
254 | ||
255 | /* | |
256 | * Initialize fields that won't be initialized by ConfigureScrollbar, | |
257 | * or which ConfigureScrollbar expects to have reasonable values | |
258 | * (e.g. resource pointers). | |
259 | */ | |
260 | ||
261 | scrollPtr = (Scrollbar *) ckalloc(sizeof(Scrollbar)); | |
262 | scrollPtr->tkwin = new; | |
263 | scrollPtr->interp = interp; | |
264 | scrollPtr->command = NULL; | |
265 | scrollPtr->bgBorder = NULL; | |
266 | scrollPtr->fgBorder = NULL; | |
267 | scrollPtr->activeBorder = NULL; | |
268 | scrollPtr->copyGC = None; | |
269 | scrollPtr->mouseField = OUTSIDE; | |
270 | scrollPtr->pressField = -1; | |
271 | scrollPtr->totalUnits = 0; | |
272 | scrollPtr->windowUnits = 0; | |
273 | scrollPtr->firstUnit = 0; | |
274 | scrollPtr->lastUnit = 0; | |
275 | scrollPtr->cursor = None; | |
276 | scrollPtr->autoRepeat = NULL; | |
277 | scrollPtr->flags = 0; | |
278 | ||
279 | Tk_SetClass(scrollPtr->tkwin, "Scrollbar"); | |
280 | Tk_CreateEventHandler(scrollPtr->tkwin, ExposureMask|StructureNotifyMask, | |
281 | ScrollbarEventProc, (ClientData) scrollPtr); | |
282 | Tk_CreateEventHandler(scrollPtr->tkwin, EnterWindowMask|LeaveWindowMask | |
283 | |PointerMotionMask|ButtonPressMask|ButtonReleaseMask, | |
284 | ScrollbarMouseProc, (ClientData) scrollPtr); | |
285 | Tcl_CreateCommand(interp, Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd, | |
286 | (ClientData) scrollPtr, (void (*)()) NULL); | |
287 | if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) { | |
288 | goto error; | |
289 | } | |
290 | ||
291 | interp->result = Tk_PathName(scrollPtr->tkwin); | |
292 | return TCL_OK; | |
293 | ||
294 | error: | |
295 | Tk_DestroyWindow(scrollPtr->tkwin); | |
296 | return TCL_ERROR; | |
297 | } | |
298 | \f | |
299 | /* | |
300 | *-------------------------------------------------------------- | |
301 | * | |
302 | * ScrollbarWidgetCmd -- | |
303 | * | |
304 | * This procedure is invoked to process the Tcl command | |
305 | * that corresponds to a widget managed by this module. | |
306 | * See the user documentation for details on what it does. | |
307 | * | |
308 | * Results: | |
309 | * A standard Tcl result. | |
310 | * | |
311 | * Side effects: | |
312 | * See the user documentation. | |
313 | * | |
314 | *-------------------------------------------------------------- | |
315 | */ | |
316 | ||
317 | static int | |
318 | ScrollbarWidgetCmd(clientData, interp, argc, argv) | |
319 | ClientData clientData; /* Information about scrollbar | |
320 | * widget. */ | |
321 | Tcl_Interp *interp; /* Current interpreter. */ | |
322 | int argc; /* Number of arguments. */ | |
323 | char **argv; /* Argument strings. */ | |
324 | { | |
325 | register Scrollbar *scrollPtr = (Scrollbar *) clientData; | |
326 | int result = TCL_OK; | |
327 | int length; | |
328 | char c; | |
329 | ||
330 | if (argc < 2) { | |
331 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
332 | argv[0], " option ?arg arg ...?\"", (char *) NULL); | |
333 | return TCL_ERROR; | |
334 | } | |
335 | Tk_Preserve((ClientData) scrollPtr); | |
336 | c = argv[1][0]; | |
337 | length = strlen(argv[1]); | |
338 | if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) { | |
339 | if (argc == 2) { | |
340 | result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs, | |
341 | (char *) scrollPtr, (char *) NULL, 0); | |
342 | } else if (argc == 3) { | |
343 | result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs, | |
344 | (char *) scrollPtr, argv[2], 0); | |
345 | } else { | |
346 | result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, | |
347 | TK_CONFIG_ARGV_ONLY); | |
348 | } | |
349 | } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) { | |
350 | if (argc != 2) { | |
351 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
352 | argv[0], " get\"", (char *) NULL); | |
353 | goto error; | |
354 | } | |
355 | sprintf(interp->result, "%d %d %d %d", scrollPtr->totalUnits, | |
356 | scrollPtr->windowUnits, scrollPtr->firstUnit, | |
357 | scrollPtr->lastUnit); | |
358 | } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) { | |
359 | int totalUnits, windowUnits, firstUnit, lastUnit; | |
360 | ||
361 | if (argc != 6) { | |
362 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
363 | argv[0], | |
364 | " set totalUnits windowUnits firstUnit lastUnit\"", | |
365 | (char *) NULL); | |
366 | goto error; | |
367 | } | |
368 | if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) { | |
369 | goto error; | |
370 | } | |
371 | if (totalUnits < 0) { | |
372 | sprintf(interp->result, "illegal totalUnits %d", totalUnits); | |
373 | goto error; | |
374 | } | |
375 | if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) { | |
376 | goto error; | |
377 | } | |
378 | if (windowUnits < 0) { | |
379 | sprintf(interp->result, "illegal windowUnits %d", windowUnits); | |
380 | goto error; | |
381 | } | |
382 | if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) { | |
383 | goto error; | |
384 | } | |
385 | if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) { | |
386 | goto error; | |
387 | } | |
388 | if (totalUnits > 0) { | |
389 | if (lastUnit < firstUnit) { | |
390 | sprintf(interp->result, "illegal lastUnit %d", lastUnit); | |
391 | goto error; | |
392 | } | |
393 | } else { | |
394 | firstUnit = lastUnit = 0; | |
395 | } | |
396 | scrollPtr->totalUnits = totalUnits; | |
397 | scrollPtr->windowUnits = windowUnits; | |
398 | scrollPtr->firstUnit = firstUnit; | |
399 | scrollPtr->lastUnit = lastUnit; | |
400 | ComputeScrollbarGeometry(scrollPtr); | |
401 | EventuallyRedraw(scrollPtr); | |
402 | } else { | |
403 | Tcl_AppendResult(interp, "bad option \"", argv[1], | |
404 | "\": must be configure, get, or set", (char *) NULL); | |
405 | goto error; | |
406 | } | |
407 | Tk_Release((ClientData) scrollPtr); | |
408 | return result; | |
409 | ||
410 | error: | |
411 | Tk_Release((ClientData) scrollPtr); | |
412 | return TCL_ERROR; | |
413 | } | |
414 | \f | |
415 | /* | |
416 | *---------------------------------------------------------------------- | |
417 | * | |
418 | * DestroyScrollbar -- | |
419 | * | |
420 | * This procedure is invoked by Tk_EventuallyFree or Tk_Release | |
421 | * to clean up the internal structure of a scrollbar at a safe time | |
422 | * (when no-one is using it anymore). | |
423 | * | |
424 | * Results: | |
425 | * None. | |
426 | * | |
427 | * Side effects: | |
428 | * Everything associated with the scrollbar is freed up. | |
429 | * | |
430 | *---------------------------------------------------------------------- | |
431 | */ | |
432 | ||
433 | static void | |
434 | DestroyScrollbar(clientData) | |
435 | ClientData clientData; /* Info about scrollbar widget. */ | |
436 | { | |
437 | register Scrollbar *scrollPtr = (Scrollbar *) clientData; | |
438 | ||
439 | if (scrollPtr->command != NULL) { | |
440 | ckfree(scrollPtr->command); | |
441 | } | |
442 | if (scrollPtr->bgBorder != NULL) { | |
443 | Tk_Free3DBorder(scrollPtr->bgBorder); | |
444 | } | |
445 | if (scrollPtr->fgBorder != NULL) { | |
446 | Tk_Free3DBorder(scrollPtr->fgBorder); | |
447 | } | |
448 | if (scrollPtr->activeBorder != NULL) { | |
449 | Tk_Free3DBorder(scrollPtr->activeBorder); | |
450 | } | |
451 | if (scrollPtr->copyGC != None) { | |
452 | Tk_FreeGC(scrollPtr->copyGC); | |
453 | } | |
454 | if (scrollPtr->cursor != None) { | |
455 | Tk_FreeCursor(scrollPtr->cursor); | |
456 | } | |
457 | ckfree((char *) scrollPtr); | |
458 | } | |
459 | \f | |
460 | /* | |
461 | *---------------------------------------------------------------------- | |
462 | * | |
463 | * ConfigureScrollbar -- | |
464 | * | |
465 | * This procedure is called to process an argv/argc list, plus | |
466 | * the Tk option database, in order to configure (or | |
467 | * reconfigure) a scrollbar widget. | |
468 | * | |
469 | * Results: | |
470 | * The return value is a standard Tcl result. If TCL_ERROR is | |
471 | * returned, then interp->result contains an error message. | |
472 | * | |
473 | * Side effects: | |
474 | * Configuration information, such as colors, border width, | |
475 | * etc. get set for scrollPtr; old resources get freed, | |
476 | * if there were any. | |
477 | * | |
478 | *---------------------------------------------------------------------- | |
479 | */ | |
480 | ||
481 | static int | |
482 | ConfigureScrollbar(interp, scrollPtr, argc, argv, flags) | |
483 | Tcl_Interp *interp; /* Used for error reporting. */ | |
484 | register Scrollbar *scrollPtr; /* Information about widget; may or | |
485 | * may not already have values for | |
486 | * some fields. */ | |
487 | int argc; /* Number of valid entries in argv. */ | |
488 | char **argv; /* Arguments. */ | |
489 | int flags; /* Flags to pass to | |
490 | * Tk_ConfigureWidget. */ | |
491 | { | |
492 | int length; | |
493 | XGCValues gcValues; | |
494 | ||
495 | if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, configSpecs, | |
496 | argc, argv, (char *) scrollPtr, flags) != TCL_OK) { | |
497 | return TCL_ERROR; | |
498 | } | |
499 | ||
500 | /* | |
501 | * A few options need special processing, such as parsing the | |
502 | * orientation or setting the background from a 3-D border. | |
503 | */ | |
504 | ||
505 | length = strlen(scrollPtr->orientUid); | |
506 | if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) { | |
507 | scrollPtr->vertical = 1; | |
508 | } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) { | |
509 | scrollPtr->vertical = 0; | |
510 | } else { | |
511 | Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid, | |
512 | "\": must be vertical or horizontal", (char *) NULL); | |
513 | return TCL_ERROR; | |
514 | } | |
515 | ||
516 | if (scrollPtr->command != NULL) { | |
517 | scrollPtr->commandSize = strlen(scrollPtr->command); | |
518 | } else { | |
519 | scrollPtr->commandSize = 0; | |
520 | } | |
521 | ||
522 | Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder); | |
523 | ||
524 | if (scrollPtr->copyGC == None) { | |
525 | gcValues.graphics_exposures = False; | |
526 | scrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures, | |
527 | &gcValues); | |
528 | } | |
529 | ||
530 | /* | |
531 | * Register the desired geometry for the window (leave enough space | |
532 | * for the two arrows plus a minimum-size slider, plus border around | |
533 | * the whole window, if any). Then arrange for the window to be | |
534 | * redisplayed. | |
535 | */ | |
536 | ||
537 | ComputeScrollbarGeometry(scrollPtr); | |
538 | EventuallyRedraw(scrollPtr); | |
539 | return TCL_OK; | |
540 | } | |
541 | \f | |
542 | /* | |
543 | *-------------------------------------------------------------- | |
544 | * | |
545 | * DisplayScrollbar -- | |
546 | * | |
547 | * This procedure redraws the contents of a scrollbar window. | |
548 | * It is invoked as a do-when-idle handler, so it only runs | |
549 | * when there's nothing else for the application to do. | |
550 | * | |
551 | * Results: | |
552 | * None. | |
553 | * | |
554 | * Side effects: | |
555 | * Information appears on the screen. | |
556 | * | |
557 | *-------------------------------------------------------------- | |
558 | */ | |
559 | ||
560 | static void | |
561 | DisplayScrollbar(clientData) | |
562 | ClientData clientData; /* Information about window. */ | |
563 | { | |
564 | register Scrollbar *scrollPtr = (Scrollbar *) clientData; | |
565 | register Tk_Window tkwin = scrollPtr->tkwin; | |
566 | XPoint points[7]; | |
567 | Tk_3DBorder border; | |
568 | int relief, width, fieldLength; | |
569 | Pixmap pixmap; | |
570 | ||
571 | if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { | |
572 | goto done; | |
573 | } | |
574 | ||
575 | if (scrollPtr->vertical) { | |
576 | width = Tk_Width(tkwin) - 2*scrollPtr->offset; | |
577 | } else { | |
578 | width = Tk_Height(tkwin) - 2*scrollPtr->offset; | |
579 | } | |
580 | ||
581 | /* | |
582 | * In order to avoid screen flashes, this procedure redraws | |
583 | * the scrollbar in a pixmap, then copies the pixmap to the | |
584 | * screen in a single operation. This means that there's no | |
585 | * point in time where the on-sreen image has been cleared. | |
586 | */ | |
587 | ||
588 | pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
589 | Tk_Width(tkwin), Tk_Height(tkwin), | |
590 | Tk_DefaultDepth(Tk_Screen(tkwin))); | |
591 | Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, scrollPtr->bgBorder, | |
592 | 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), | |
593 | scrollPtr->borderWidth, scrollPtr->relief); | |
594 | ||
595 | /* | |
596 | * Draw the top or left arrow. The coordinates of the polygon | |
597 | * points probably seem odd, but they were carefully chosen with | |
598 | * respect to X's rules for filling polygons. These point choices | |
599 | * cause the arrows to just fill the narrow dimension of the | |
600 | * scrollbar and be properly centered. | |
601 | */ | |
602 | ||
603 | if (scrollPtr->mouseField == TOP_ARROW) { | |
604 | border = scrollPtr->activeBorder; | |
605 | relief = scrollPtr->pressField == TOP_ARROW ? TK_RELIEF_SUNKEN | |
606 | : TK_RELIEF_RAISED; | |
607 | } else { | |
608 | border = scrollPtr->fgBorder; | |
609 | relief = TK_RELIEF_RAISED; | |
610 | } | |
611 | if (scrollPtr->vertical) { | |
612 | points[0].x = scrollPtr->offset - 1; | |
613 | points[0].y = scrollPtr->arrowLength + scrollPtr->offset; | |
614 | points[1].x = width + scrollPtr->offset; | |
615 | points[1].y = points[0].y; | |
616 | points[2].x = width/2 + scrollPtr->offset; | |
617 | points[2].y = scrollPtr->offset - 1; | |
618 | Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border, | |
619 | points, 3, scrollPtr->borderWidth, relief); | |
620 | } else { | |
621 | points[0].x = scrollPtr->arrowLength + scrollPtr->offset; | |
622 | points[0].y = scrollPtr->offset - 1; | |
623 | points[1].x = scrollPtr->offset; | |
624 | points[1].y = width/2 + scrollPtr->offset; | |
625 | points[2].x = points[0].x; | |
626 | points[2].y = width + scrollPtr->offset; | |
627 | Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border, | |
628 | points, 3, scrollPtr->borderWidth, relief); | |
629 | } | |
630 | ||
631 | /* | |
632 | * Display the bottom or right arrow. | |
633 | */ | |
634 | ||
635 | if (scrollPtr->mouseField == BOTTOM_ARROW) { | |
636 | border = scrollPtr->activeBorder; | |
637 | relief = scrollPtr->pressField == BOTTOM_ARROW ? TK_RELIEF_SUNKEN | |
638 | : TK_RELIEF_RAISED; | |
639 | } else { | |
640 | border = scrollPtr->fgBorder; | |
641 | relief = TK_RELIEF_RAISED; | |
642 | } | |
643 | if (scrollPtr->vertical) { | |
644 | points[0].x = scrollPtr->offset; | |
645 | points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength | |
646 | - scrollPtr->offset; | |
647 | points[1].x = width/2 + scrollPtr->offset; | |
648 | points[1].y = Tk_Height(tkwin) - scrollPtr->offset; | |
649 | points[2].x = width + scrollPtr->offset; | |
650 | points[2].y = points[0].y; | |
651 | Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border, | |
652 | points, 3, scrollPtr->borderWidth, relief); | |
653 | } else { | |
654 | points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength | |
655 | - scrollPtr->offset; | |
656 | points[0].y = scrollPtr->offset - 1; | |
657 | points[1].x = points[0].x; | |
658 | points[1].y = width + scrollPtr->offset; | |
659 | points[2].x = Tk_Width(tkwin) - scrollPtr->offset; | |
660 | points[2].y = width/2 + scrollPtr->offset; | |
661 | Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border, | |
662 | points, 3, scrollPtr->borderWidth, relief); | |
663 | } | |
664 | ||
665 | /* | |
666 | * Display the slider. | |
667 | */ | |
668 | ||
669 | if (scrollPtr->mouseField == SLIDER) { | |
670 | border = scrollPtr->activeBorder; | |
671 | relief = scrollPtr->pressField == SLIDER ? TK_RELIEF_SUNKEN | |
672 | : TK_RELIEF_RAISED; | |
673 | } else { | |
674 | border = scrollPtr->fgBorder; | |
675 | relief = TK_RELIEF_RAISED; | |
676 | } | |
677 | fieldLength = (scrollPtr->vertical ? Tk_Height(tkwin) : Tk_Width(tkwin)) | |
678 | - 2*(scrollPtr->arrowLength + scrollPtr->offset); | |
679 | if (fieldLength < 0) { | |
680 | fieldLength = 0; | |
681 | } | |
682 | if (scrollPtr->vertical) { | |
683 | Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border, | |
684 | 1 + scrollPtr->offset, scrollPtr->sliderFirst, | |
685 | width-2, scrollPtr->sliderLast - scrollPtr->sliderFirst, | |
686 | scrollPtr->borderWidth, relief); | |
687 | } else { | |
688 | Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border, | |
689 | scrollPtr->sliderFirst, 1 + scrollPtr->offset, | |
690 | scrollPtr->sliderLast - scrollPtr->sliderFirst, width-2, | |
691 | scrollPtr->borderWidth, relief); | |
692 | } | |
693 | ||
694 | /* | |
695 | * Copy the information from the off-screen pixmap onto the screen, | |
696 | * then delete the pixmap. | |
697 | */ | |
698 | ||
699 | XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), | |
700 | scrollPtr->copyGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0); | |
701 | XFreePixmap(Tk_Display(tkwin), pixmap); | |
702 | ||
703 | done: | |
704 | scrollPtr->flags &= ~REDRAW_PENDING; | |
705 | } | |
706 | \f | |
707 | /* | |
708 | *-------------------------------------------------------------- | |
709 | * | |
710 | * ScrollbarEventProc -- | |
711 | * | |
712 | * This procedure is invoked by the Tk dispatcher for various | |
713 | * events on scrollbars. | |
714 | * | |
715 | * Results: | |
716 | * None. | |
717 | * | |
718 | * Side effects: | |
719 | * When the window gets deleted, internal structures get | |
720 | * cleaned up. When it gets exposed, it is redisplayed. | |
721 | * | |
722 | *-------------------------------------------------------------- | |
723 | */ | |
724 | ||
725 | static void | |
726 | ScrollbarEventProc(clientData, eventPtr) | |
727 | ClientData clientData; /* Information about window. */ | |
728 | XEvent *eventPtr; /* Information about event. */ | |
729 | { | |
730 | Scrollbar *scrollPtr = (Scrollbar *) clientData; | |
731 | ||
732 | if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { | |
733 | EventuallyRedraw(scrollPtr); | |
734 | } else if (eventPtr->type == DestroyNotify) { | |
735 | Tcl_DeleteCommand(scrollPtr->interp, Tk_PathName(scrollPtr->tkwin)); | |
736 | scrollPtr->tkwin = NULL; | |
737 | if (scrollPtr->flags & REDRAW_PENDING) { | |
738 | Tk_CancelIdleCall(DisplayScrollbar, (ClientData) scrollPtr); | |
739 | } | |
740 | Tk_EventuallyFree((ClientData) scrollPtr, DestroyScrollbar); | |
741 | } else if (eventPtr->type == ConfigureNotify) { | |
742 | ComputeScrollbarGeometry(scrollPtr); | |
743 | } | |
744 | } | |
745 | \f | |
746 | /* | |
747 | *---------------------------------------------------------------------- | |
748 | * | |
749 | * ComputeScrollbarGeometry -- | |
750 | * | |
751 | * After changes in a scrollbar's size or configuration, this | |
752 | * procedure recomputes various geometry information used in | |
753 | * displaying the scrollbar. | |
754 | * | |
755 | * Results: | |
756 | * None. | |
757 | * | |
758 | * Side effects: | |
759 | * The scrollbar will be displayed differently. | |
760 | * | |
761 | *---------------------------------------------------------------------- | |
762 | */ | |
763 | ||
764 | static void | |
765 | ComputeScrollbarGeometry(scrollPtr) | |
766 | register Scrollbar *scrollPtr; /* Scrollbar whose geometry may | |
767 | * have changed. */ | |
768 | { | |
769 | int width, fieldLength; | |
770 | ||
771 | if (scrollPtr->relief == TK_RELIEF_FLAT) { | |
772 | scrollPtr->offset = 0; | |
773 | } else { | |
774 | scrollPtr->offset = scrollPtr->borderWidth; | |
775 | } | |
776 | width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin) | |
777 | : Tk_Height(scrollPtr->tkwin); | |
778 | scrollPtr->arrowLength = | |
779 | (((width - 2*scrollPtr->offset)*173) + 100) / 200; | |
780 | fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin) | |
781 | : Tk_Width(scrollPtr->tkwin)) | |
782 | - 2*(scrollPtr->arrowLength + scrollPtr->offset); | |
783 | if (fieldLength < 0) { | |
784 | fieldLength = 0; | |
785 | } | |
786 | if (scrollPtr->totalUnits <= 0) { | |
787 | scrollPtr->sliderFirst = 0; | |
788 | scrollPtr->sliderLast = fieldLength; | |
789 | } else { | |
790 | scrollPtr->sliderFirst = (fieldLength*scrollPtr->firstUnit | |
791 | + scrollPtr->totalUnits/2)/scrollPtr->totalUnits; | |
792 | scrollPtr->sliderLast = (fieldLength*(scrollPtr->lastUnit+1) | |
793 | + scrollPtr->totalUnits/2)/scrollPtr->totalUnits; | |
794 | ||
795 | /* | |
796 | * Adjust the slider so that some piece of it is always | |
797 | * displayed in the scrollbar and so that it has at least | |
798 | * a minimal width (so it can be grabbed with the mouse). | |
799 | */ | |
800 | ||
801 | if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) { | |
802 | scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth; | |
803 | } | |
804 | if (scrollPtr->sliderFirst < 0) { | |
805 | scrollPtr->sliderFirst = 0; | |
806 | } | |
807 | if (scrollPtr->sliderLast < (scrollPtr->sliderFirst | |
808 | + 2*scrollPtr->borderWidth)) { | |
809 | scrollPtr->sliderLast = scrollPtr->sliderFirst | |
810 | + 2*scrollPtr->borderWidth; | |
811 | } | |
812 | if (scrollPtr->sliderLast > fieldLength) { | |
813 | scrollPtr->sliderLast = fieldLength; | |
814 | } | |
815 | } | |
816 | scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->offset; | |
817 | scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->offset; | |
818 | ||
819 | /* | |
820 | * Register the desired geometry for the window (leave enough space | |
821 | * for the two arrows plus a minimum-size slider, plus border around | |
822 | * the whole window, if any). Then arrange for the window to be | |
823 | * redisplayed. | |
824 | */ | |
825 | ||
826 | if (scrollPtr->vertical) { | |
827 | Tk_GeometryRequest(scrollPtr->tkwin, | |
828 | scrollPtr->width + 2*scrollPtr->offset, | |
829 | 2*(scrollPtr->arrowLength + scrollPtr->borderWidth | |
830 | + scrollPtr->offset)); | |
831 | } else { | |
832 | Tk_GeometryRequest(scrollPtr->tkwin, | |
833 | 2*(scrollPtr->arrowLength + scrollPtr->borderWidth | |
834 | + scrollPtr->offset), scrollPtr->width + 2*scrollPtr->offset); | |
835 | } | |
836 | Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->borderWidth); | |
837 | ||
838 | } | |
839 | \f | |
840 | /* | |
841 | *-------------------------------------------------------------- | |
842 | * | |
843 | * ScrollbarPosition -- | |
844 | * | |
845 | * Determine the scrollbar element corresponding to a | |
846 | * given position. | |
847 | * | |
848 | * Results: | |
849 | * One of TOP_ARROW, TOP_GAP, etc., indicating which element | |
850 | * of the scrollbar covers the position given by (x, y). If | |
851 | * (x,y) is outside the scrollbar entirely, then OUTSIDE is | |
852 | * returned. | |
853 | * | |
854 | * Side effects: | |
855 | * None. | |
856 | * | |
857 | *-------------------------------------------------------------- | |
858 | */ | |
859 | ||
860 | static int | |
861 | ScrollbarPosition(scrollPtr, x, y) | |
862 | register Scrollbar *scrollPtr; /* Scrollbar widget record. */ | |
863 | int x, y; /* Coordinates within scrollPtr's | |
864 | * window. */ | |
865 | { | |
866 | int length, width, tmp; | |
867 | ||
868 | if (scrollPtr->vertical) { | |
869 | length = Tk_Height(scrollPtr->tkwin); | |
870 | width = Tk_Width(scrollPtr->tkwin); | |
871 | } else { | |
872 | tmp = x; | |
873 | x = y; | |
874 | y = tmp; | |
875 | length = Tk_Width(scrollPtr->tkwin); | |
876 | width = Tk_Height(scrollPtr->tkwin); | |
877 | } | |
878 | ||
879 | if ((x < 0) || (x > width) || (y < 0)) { | |
880 | return OUTSIDE; | |
881 | } | |
882 | ||
883 | /* | |
884 | * All of the calculations in this procedure mirror those in | |
885 | * DisplayScrollbar. Be sure to keep the two consistent. | |
886 | */ | |
887 | ||
888 | if (y < (scrollPtr->offset + scrollPtr->arrowLength)) { | |
889 | return TOP_ARROW; | |
890 | } | |
891 | if (y < scrollPtr->sliderFirst) { | |
892 | return TOP_GAP; | |
893 | } | |
894 | if (y < scrollPtr->sliderLast) { | |
895 | return SLIDER; | |
896 | } | |
897 | if (y >= (length - (scrollPtr->arrowLength + scrollPtr->offset))) { | |
898 | return BOTTOM_ARROW; | |
899 | } | |
900 | return BOTTOM_GAP; | |
901 | } | |
902 | \f | |
903 | /* | |
904 | *-------------------------------------------------------------- | |
905 | * | |
906 | * ScrollbarMouseProc -- | |
907 | * | |
908 | * This procedure is called back by Tk in response to | |
909 | * mouse events such as window entry, window exit, mouse | |
910 | * motion, and button presses. | |
911 | * | |
912 | * Results: | |
913 | * None. | |
914 | * | |
915 | * Side effects: | |
916 | * This procedure implements the "feel" of the scrollbar | |
917 | * by issuing scrolling commands in response to button presses | |
918 | * and mouse motion. | |
919 | * | |
920 | *-------------------------------------------------------------- | |
921 | */ | |
922 | ||
923 | static void | |
924 | ScrollbarMouseProc(clientData, eventPtr) | |
925 | ClientData clientData; /* Information about window. */ | |
926 | register XEvent *eventPtr; /* Information about event. */ | |
927 | { | |
928 | register Scrollbar *scrollPtr = (Scrollbar *) clientData; | |
929 | ||
930 | Tk_Preserve((ClientData) scrollPtr); | |
931 | if (eventPtr->type == EnterNotify) { | |
932 | if (scrollPtr->pressField == -1) { | |
933 | ScrollbarNewField(scrollPtr, | |
934 | ScrollbarPosition(scrollPtr, eventPtr->xcrossing.x, | |
935 | eventPtr->xcrossing.y)); | |
936 | } | |
937 | } else if (eventPtr->type == LeaveNotify) { | |
938 | if (scrollPtr->pressField == -1) { | |
939 | ScrollbarNewField(scrollPtr, OUTSIDE); | |
940 | } | |
941 | } else if (eventPtr->type == MotionNotify) { | |
942 | if (scrollPtr->pressField == SLIDER) { | |
943 | int delta, length, newFirst; | |
944 | ||
945 | if (scrollPtr->vertical) { | |
946 | delta = eventPtr->xmotion.y - scrollPtr->pressPos; | |
947 | length = Tk_Height(scrollPtr->tkwin) | |
948 | - 2*(scrollPtr->arrowLength + scrollPtr->offset); | |
949 | } else { | |
950 | delta = eventPtr->xmotion.x - scrollPtr->pressPos; | |
951 | length = Tk_Width(scrollPtr->tkwin) | |
952 | - 2*(scrollPtr->arrowLength + scrollPtr->offset); | |
953 | } | |
954 | ||
955 | /* | |
956 | * Do the division with positive numbers to avoid | |
957 | * differences in negative-number truncation on different | |
958 | * machines. | |
959 | */ | |
960 | ||
961 | if (delta >= 0) { | |
962 | newFirst = scrollPtr->pressFirstUnit | |
963 | + ((delta * scrollPtr->totalUnits) + (length/2)) | |
964 | / length; | |
965 | } else { | |
966 | newFirst = scrollPtr->pressFirstUnit | |
967 | - (((-delta) * scrollPtr->totalUnits) + (length/2)) | |
968 | / length; | |
969 | } | |
970 | ScrollCmd(scrollPtr, newFirst); | |
971 | } else if (scrollPtr->pressField == -1) { | |
972 | ScrollbarNewField(scrollPtr, | |
973 | ScrollbarPosition(scrollPtr, eventPtr->xmotion.x, | |
974 | eventPtr->xmotion.y)); | |
975 | } | |
976 | } else if ((eventPtr->type == ButtonPress) | |
977 | && (eventPtr->xbutton.state == 0)) { | |
978 | scrollPtr->pressField = scrollPtr->mouseField; | |
979 | if (scrollPtr->pressField != SLIDER) { | |
980 | scrollPtr->autoRepeat = Tk_CreateTimerHandler( | |
981 | scrollPtr->repeatDelay, | |
982 | ScrollbarTimerProc, (ClientData) scrollPtr); | |
983 | } | |
984 | if (scrollPtr->vertical) { | |
985 | scrollPtr->pressPos = eventPtr->xbutton.y; | |
986 | } else { | |
987 | scrollPtr->pressPos = eventPtr->xbutton.x; | |
988 | } | |
989 | scrollPtr->pressFirstUnit = scrollPtr->firstUnit; | |
990 | if (scrollPtr->pressFirstUnit <= -scrollPtr->windowUnits) { | |
991 | scrollPtr->pressFirstUnit = 1-scrollPtr->windowUnits; | |
992 | } | |
993 | if (scrollPtr->pressFirstUnit >= scrollPtr->totalUnits) { | |
994 | scrollPtr->pressFirstUnit = scrollPtr->totalUnits-1; | |
995 | } | |
996 | EventuallyRedraw(scrollPtr); | |
997 | } else if (eventPtr->type == ButtonRelease) { | |
998 | if (scrollPtr->pressField == scrollPtr->mouseField) { | |
999 | switch (scrollPtr->pressField) { | |
1000 | case TOP_ARROW: | |
1001 | ScrollCmd(scrollPtr, scrollPtr->firstUnit-1); | |
1002 | break; | |
1003 | case TOP_GAP: | |
1004 | if (scrollPtr->windowUnits <= 1) { | |
1005 | ScrollCmd(scrollPtr, scrollPtr->firstUnit - 1); | |
1006 | } else { | |
1007 | ScrollCmd(scrollPtr, scrollPtr->firstUnit | |
1008 | - (scrollPtr->windowUnits-1)); | |
1009 | } | |
1010 | break; | |
1011 | case BOTTOM_GAP: { | |
1012 | if (scrollPtr->windowUnits <= 1) { | |
1013 | ScrollCmd(scrollPtr, scrollPtr->firstUnit + 1); | |
1014 | } else { | |
1015 | ScrollCmd(scrollPtr, scrollPtr->firstUnit | |
1016 | + (scrollPtr->windowUnits-1)); | |
1017 | } | |
1018 | break; | |
1019 | } | |
1020 | case BOTTOM_ARROW: | |
1021 | ScrollCmd(scrollPtr, scrollPtr->firstUnit+1); | |
1022 | break; | |
1023 | } | |
1024 | } | |
1025 | if (scrollPtr->autoRepeat != NULL) { | |
1026 | Tk_DeleteTimerHandler(scrollPtr->autoRepeat); | |
1027 | scrollPtr->autoRepeat = NULL; | |
1028 | } | |
1029 | EventuallyRedraw(scrollPtr); | |
1030 | scrollPtr->pressField = -1; | |
1031 | ScrollbarNewField(scrollPtr, | |
1032 | ScrollbarPosition(scrollPtr, eventPtr->xbutton.x, | |
1033 | eventPtr->xbutton.y)); | |
1034 | } | |
1035 | Tk_Release((ClientData) scrollPtr); | |
1036 | } | |
1037 | \f | |
1038 | /* | |
1039 | *-------------------------------------------------------------- | |
1040 | * | |
1041 | * ScrollCmd -- | |
1042 | * | |
1043 | * This procedure takes care of invoking a scrolling Tcl | |
1044 | * command and reporting any error that occurs in it. | |
1045 | * | |
1046 | * Results: | |
1047 | * None. | |
1048 | * | |
1049 | * Side effects: | |
1050 | * A Tcl command is invoked, and an additional error-processing | |
1051 | * command may also be invoked. | |
1052 | * | |
1053 | *-------------------------------------------------------------- | |
1054 | */ | |
1055 | ||
1056 | static void | |
1057 | ScrollCmd(scrollPtr, unit) | |
1058 | register Scrollbar *scrollPtr; /* Scrollbar from which to issue | |
1059 | * command. */ | |
1060 | int unit; /* Unit position within thing being | |
1061 | * being displayed that should appear | |
1062 | * at top or right of screen. */ | |
1063 | { | |
1064 | char string[20]; | |
1065 | int result; | |
1066 | ||
1067 | if ((unit == scrollPtr->firstUnit) || (scrollPtr->command == NULL)) { | |
1068 | return; | |
1069 | } | |
1070 | sprintf(string, " %d", unit); | |
1071 | result = Tcl_VarEval(scrollPtr->interp, scrollPtr->command, string, | |
1072 | (char *) NULL); | |
1073 | if (result != TCL_OK) { | |
1074 | TkBindError(scrollPtr->interp); | |
1075 | } | |
1076 | } | |
1077 | \f | |
1078 | /* | |
1079 | *-------------------------------------------------------------- | |
1080 | * | |
1081 | * EventuallyRedraw -- | |
1082 | * | |
1083 | * Arrange for one or more of the fields of a scrollbar | |
1084 | * to be redrawn. | |
1085 | * | |
1086 | * Results: | |
1087 | * None. | |
1088 | * | |
1089 | * Side effects: | |
1090 | * None. | |
1091 | * | |
1092 | *-------------------------------------------------------------- | |
1093 | */ | |
1094 | ||
1095 | static void | |
1096 | EventuallyRedraw(scrollPtr) | |
1097 | register Scrollbar *scrollPtr; /* Information about widget. */ | |
1098 | { | |
1099 | if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) { | |
1100 | return; | |
1101 | } | |
1102 | if ((scrollPtr->flags & REDRAW_PENDING) == 0) { | |
1103 | Tk_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr); | |
1104 | scrollPtr->flags |= REDRAW_PENDING; | |
1105 | } | |
1106 | } | |
1107 | \f | |
1108 | /* | |
1109 | *-------------------------------------------------------------- | |
1110 | * | |
1111 | * ScrollbarNewField -- | |
1112 | * | |
1113 | * This procedure is called to declare that the mouse is in | |
1114 | * a particular field of the scrollbar (e.g. top arrow), so | |
1115 | * that the field can be highlighed and the previous field | |
1116 | * can be returned to normal display. | |
1117 | * | |
1118 | * Results: | |
1119 | * None. | |
1120 | * | |
1121 | * Side effects: | |
1122 | * Fields may be redisplayed. | |
1123 | * | |
1124 | *-------------------------------------------------------------- | |
1125 | */ | |
1126 | ||
1127 | static void | |
1128 | ScrollbarNewField(scrollPtr, field) | |
1129 | register Scrollbar *scrollPtr; /* Information about widget. */ | |
1130 | int field; /* Identifies field under mouse, | |
1131 | * e.g. TOP_ARROW. */ | |
1132 | { | |
1133 | if (field == scrollPtr->mouseField) { | |
1134 | return; | |
1135 | } | |
1136 | EventuallyRedraw(scrollPtr); | |
1137 | scrollPtr->mouseField = field; | |
1138 | } | |
1139 | \f | |
1140 | /* | |
1141 | *-------------------------------------------------------------- | |
1142 | * | |
1143 | * ScrollbarTimerProc -- | |
1144 | * | |
1145 | * This procedure is invoked as a Tk timer handler for actions | |
1146 | * that auto-repeat (mouse presses in an arrow or gap). It | |
1147 | * performs the auto-repeat action. | |
1148 | * | |
1149 | * Results: | |
1150 | * None. | |
1151 | * | |
1152 | * Side effects: | |
1153 | * Whatever action corresponds to the current mouse button | |
1154 | * is repeated, and this procedure is rescheduled to execute | |
1155 | * again later. | |
1156 | * | |
1157 | *-------------------------------------------------------------- | |
1158 | */ | |
1159 | ||
1160 | static void | |
1161 | ScrollbarTimerProc(clientData) | |
1162 | ClientData clientData; /* Information about widget. */ | |
1163 | { | |
1164 | register Scrollbar *scrollPtr = (Scrollbar *) clientData; | |
1165 | ||
1166 | Tk_Preserve((ClientData) scrollPtr); | |
1167 | switch(scrollPtr->pressField) { | |
1168 | case TOP_ARROW: | |
1169 | ScrollCmd(scrollPtr, scrollPtr->firstUnit-1); | |
1170 | break; | |
1171 | case TOP_GAP: | |
1172 | ScrollCmd(scrollPtr, scrollPtr->firstUnit | |
1173 | - (scrollPtr->windowUnits-1)); | |
1174 | break; | |
1175 | case BOTTOM_GAP: { | |
1176 | ScrollCmd(scrollPtr, scrollPtr->firstUnit | |
1177 | + (scrollPtr->windowUnits-1)); | |
1178 | break; | |
1179 | } | |
1180 | case BOTTOM_ARROW: | |
1181 | ScrollCmd(scrollPtr, scrollPtr->firstUnit+1); | |
1182 | break; | |
1183 | } | |
1184 | if (scrollPtr->tkwin != NULL) { | |
1185 | scrollPtr->autoRepeat = Tk_CreateTimerHandler( | |
1186 | scrollPtr->repeatInterval, ScrollbarTimerProc, | |
1187 | (ClientData) scrollPtr); | |
1188 | } | |
1189 | Tk_Release((ClientData) scrollPtr); | |
1190 | } |