]> git.zerfleddert.de Git - micropolis/blob - src/tk/tkscrbar.c
6cec27aa12f146ea0d2c66f217ad9dfac929e0fa
[micropolis] / src / tk / tkscrbar.c
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 }
Impressum, Datenschutz