]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tkscrbar.c
Fixes for compilation with gcc 15
[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 (
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 {
241 Tk_Window tkwin = (Tk_Window) clientData;
242 register Scrollbar *scrollPtr;
243 Tk_Window new;
244
245 if (argc < 2) {
246 Tcl_AppendResult(interp, "wrong # args: should be \"",
247 argv[0], " pathName ?options?\"", (char *) NULL);
248 return TCL_ERROR;
249 }
250
251 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
252 if (new == NULL) {
253 return TCL_ERROR;
254 }
255
256 /*
257 * Initialize fields that won't be initialized by ConfigureScrollbar,
258 * or which ConfigureScrollbar expects to have reasonable values
259 * (e.g. resource pointers).
260 */
261
262 scrollPtr = (Scrollbar *) ckalloc(sizeof(Scrollbar));
263 scrollPtr->tkwin = new;
264 scrollPtr->interp = interp;
265 scrollPtr->command = NULL;
266 scrollPtr->bgBorder = NULL;
267 scrollPtr->fgBorder = NULL;
268 scrollPtr->activeBorder = NULL;
269 scrollPtr->copyGC = None;
270 scrollPtr->mouseField = OUTSIDE;
271 scrollPtr->pressField = -1;
272 scrollPtr->totalUnits = 0;
273 scrollPtr->windowUnits = 0;
274 scrollPtr->firstUnit = 0;
275 scrollPtr->lastUnit = 0;
276 scrollPtr->cursor = None;
277 scrollPtr->autoRepeat = NULL;
278 scrollPtr->flags = 0;
279
280 Tk_SetClass(scrollPtr->tkwin, "Scrollbar");
281 Tk_CreateEventHandler(scrollPtr->tkwin, ExposureMask|StructureNotifyMask,
282 ScrollbarEventProc, (ClientData) scrollPtr);
283 Tk_CreateEventHandler(scrollPtr->tkwin, EnterWindowMask|LeaveWindowMask
284 |PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
285 ScrollbarMouseProc, (ClientData) scrollPtr);
286 Tcl_CreateCommand(interp, Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
287 (ClientData) scrollPtr, (void (*)(int *)) NULL);
288 if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
289 goto error;
290 }
291
292 interp->result = Tk_PathName(scrollPtr->tkwin);
293 return TCL_OK;
294
295 error:
296 Tk_DestroyWindow(scrollPtr->tkwin);
297 return TCL_ERROR;
298 }
299 \f
300 /*
301 *--------------------------------------------------------------
302 *
303 * ScrollbarWidgetCmd --
304 *
305 * This procedure is invoked to process the Tcl command
306 * that corresponds to a widget managed by this module.
307 * See the user documentation for details on what it does.
308 *
309 * Results:
310 * A standard Tcl result.
311 *
312 * Side effects:
313 * See the user documentation.
314 *
315 *--------------------------------------------------------------
316 */
317
318 static int
319 ScrollbarWidgetCmd (
320 ClientData clientData, /* Information about scrollbar
321 * widget. */
322 Tcl_Interp *interp, /* Current interpreter. */
323 int argc, /* Number of arguments. */
324 char **argv /* Argument strings. */
325 )
326 {
327 register Scrollbar *scrollPtr = (Scrollbar *) clientData;
328 int result = TCL_OK;
329 int length;
330 char c;
331
332 if (argc < 2) {
333 Tcl_AppendResult(interp, "wrong # args: should be \"",
334 argv[0], " option ?arg arg ...?\"", (char *) NULL);
335 return TCL_ERROR;
336 }
337 Tk_Preserve((ClientData) scrollPtr);
338 c = argv[1][0];
339 length = strlen(argv[1]);
340 if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
341 if (argc == 2) {
342 result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
343 (char *) scrollPtr, (char *) NULL, 0);
344 } else if (argc == 3) {
345 result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
346 (char *) scrollPtr, argv[2], 0);
347 } else {
348 result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
349 TK_CONFIG_ARGV_ONLY);
350 }
351 } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
352 if (argc != 2) {
353 Tcl_AppendResult(interp, "wrong # args: should be \"",
354 argv[0], " get\"", (char *) NULL);
355 goto error;
356 }
357 sprintf(interp->result, "%d %d %d %d", scrollPtr->totalUnits,
358 scrollPtr->windowUnits, scrollPtr->firstUnit,
359 scrollPtr->lastUnit);
360 } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
361 int totalUnits, windowUnits, firstUnit, lastUnit;
362
363 if (argc != 6) {
364 Tcl_AppendResult(interp, "wrong # args: should be \"",
365 argv[0],
366 " set totalUnits windowUnits firstUnit lastUnit\"",
367 (char *) NULL);
368 goto error;
369 }
370 if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
371 goto error;
372 }
373 if (totalUnits < 0) {
374 sprintf(interp->result, "illegal totalUnits %d", totalUnits);
375 goto error;
376 }
377 if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
378 goto error;
379 }
380 if (windowUnits < 0) {
381 sprintf(interp->result, "illegal windowUnits %d", windowUnits);
382 goto error;
383 }
384 if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
385 goto error;
386 }
387 if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
388 goto error;
389 }
390 if (totalUnits > 0) {
391 if (lastUnit < firstUnit) {
392 sprintf(interp->result, "illegal lastUnit %d", lastUnit);
393 goto error;
394 }
395 } else {
396 firstUnit = lastUnit = 0;
397 }
398 scrollPtr->totalUnits = totalUnits;
399 scrollPtr->windowUnits = windowUnits;
400 scrollPtr->firstUnit = firstUnit;
401 scrollPtr->lastUnit = lastUnit;
402 ComputeScrollbarGeometry(scrollPtr);
403 EventuallyRedraw(scrollPtr);
404 } else {
405 Tcl_AppendResult(interp, "bad option \"", argv[1],
406 "\": must be configure, get, or set", (char *) NULL);
407 goto error;
408 }
409 Tk_Release((ClientData) scrollPtr);
410 return result;
411
412 error:
413 Tk_Release((ClientData) scrollPtr);
414 return TCL_ERROR;
415 }
416 \f
417 /*
418 *----------------------------------------------------------------------
419 *
420 * DestroyScrollbar --
421 *
422 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
423 * to clean up the internal structure of a scrollbar at a safe time
424 * (when no-one is using it anymore).
425 *
426 * Results:
427 * None.
428 *
429 * Side effects:
430 * Everything associated with the scrollbar is freed up.
431 *
432 *----------------------------------------------------------------------
433 */
434
435 static void
436 DestroyScrollbar (
437 ClientData clientData /* Info about scrollbar widget. */
438 )
439 {
440 register Scrollbar *scrollPtr = (Scrollbar *) clientData;
441
442 if (scrollPtr->command != NULL) {
443 ckfree(scrollPtr->command);
444 }
445 if (scrollPtr->bgBorder != NULL) {
446 Tk_Free3DBorder(scrollPtr->bgBorder);
447 }
448 if (scrollPtr->fgBorder != NULL) {
449 Tk_Free3DBorder(scrollPtr->fgBorder);
450 }
451 if (scrollPtr->activeBorder != NULL) {
452 Tk_Free3DBorder(scrollPtr->activeBorder);
453 }
454 if (scrollPtr->copyGC != None) {
455 Tk_FreeGC(scrollPtr->copyGC);
456 }
457 if (scrollPtr->cursor != None) {
458 Tk_FreeCursor(scrollPtr->cursor);
459 }
460 ckfree((char *) scrollPtr);
461 }
462 \f
463 /*
464 *----------------------------------------------------------------------
465 *
466 * ConfigureScrollbar --
467 *
468 * This procedure is called to process an argv/argc list, plus
469 * the Tk option database, in order to configure (or
470 * reconfigure) a scrollbar widget.
471 *
472 * Results:
473 * The return value is a standard Tcl result. If TCL_ERROR is
474 * returned, then interp->result contains an error message.
475 *
476 * Side effects:
477 * Configuration information, such as colors, border width,
478 * etc. get set for scrollPtr; old resources get freed,
479 * if there were any.
480 *
481 *----------------------------------------------------------------------
482 */
483
484 static int
485 ConfigureScrollbar (
486 Tcl_Interp *interp, /* Used for error reporting. */
487 register Scrollbar *scrollPtr, /* Information about widget; may or
488 * may not already have values for
489 * some fields. */
490 int argc, /* Number of valid entries in argv. */
491 char **argv, /* Arguments. */
492 int flags /* Flags to pass to
493 * Tk_ConfigureWidget. */
494 )
495 {
496 int length;
497 XGCValues gcValues;
498
499 if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, configSpecs,
500 argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
501 return TCL_ERROR;
502 }
503
504 /*
505 * A few options need special processing, such as parsing the
506 * orientation or setting the background from a 3-D border.
507 */
508
509 length = strlen(scrollPtr->orientUid);
510 if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
511 scrollPtr->vertical = 1;
512 } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
513 scrollPtr->vertical = 0;
514 } else {
515 Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
516 "\": must be vertical or horizontal", (char *) NULL);
517 return TCL_ERROR;
518 }
519
520 if (scrollPtr->command != NULL) {
521 scrollPtr->commandSize = strlen(scrollPtr->command);
522 } else {
523 scrollPtr->commandSize = 0;
524 }
525
526 Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
527
528 if (scrollPtr->copyGC == None) {
529 gcValues.graphics_exposures = False;
530 scrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
531 &gcValues);
532 }
533
534 /*
535 * Register the desired geometry for the window (leave enough space
536 * for the two arrows plus a minimum-size slider, plus border around
537 * the whole window, if any). Then arrange for the window to be
538 * redisplayed.
539 */
540
541 ComputeScrollbarGeometry(scrollPtr);
542 EventuallyRedraw(scrollPtr);
543 return TCL_OK;
544 }
545 \f
546 /*
547 *--------------------------------------------------------------
548 *
549 * DisplayScrollbar --
550 *
551 * This procedure redraws the contents of a scrollbar window.
552 * It is invoked as a do-when-idle handler, so it only runs
553 * when there's nothing else for the application to do.
554 *
555 * Results:
556 * None.
557 *
558 * Side effects:
559 * Information appears on the screen.
560 *
561 *--------------------------------------------------------------
562 */
563
564 static void
565 DisplayScrollbar (
566 ClientData clientData /* Information about window. */
567 )
568 {
569 register Scrollbar *scrollPtr = (Scrollbar *) clientData;
570 register Tk_Window tkwin = scrollPtr->tkwin;
571 XPoint points[7];
572 Tk_3DBorder border;
573 int relief, width, fieldLength;
574 Pixmap pixmap;
575
576 if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
577 goto done;
578 }
579
580 if (scrollPtr->vertical) {
581 width = Tk_Width(tkwin) - 2*scrollPtr->offset;
582 } else {
583 width = Tk_Height(tkwin) - 2*scrollPtr->offset;
584 }
585
586 /*
587 * In order to avoid screen flashes, this procedure redraws
588 * the scrollbar in a pixmap, then copies the pixmap to the
589 * screen in a single operation. This means that there's no
590 * point in time where the on-sreen image has been cleared.
591 */
592
593 pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
594 Tk_Width(tkwin), Tk_Height(tkwin),
595 Tk_DefaultDepth(Tk_Screen(tkwin)));
596 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, scrollPtr->bgBorder,
597 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
598 scrollPtr->borderWidth, scrollPtr->relief);
599
600 /*
601 * Draw the top or left arrow. The coordinates of the polygon
602 * points probably seem odd, but they were carefully chosen with
603 * respect to X's rules for filling polygons. These point choices
604 * cause the arrows to just fill the narrow dimension of the
605 * scrollbar and be properly centered.
606 */
607
608 if (scrollPtr->mouseField == TOP_ARROW) {
609 border = scrollPtr->activeBorder;
610 relief = scrollPtr->pressField == TOP_ARROW ? TK_RELIEF_SUNKEN
611 : TK_RELIEF_RAISED;
612 } else {
613 border = scrollPtr->fgBorder;
614 relief = TK_RELIEF_RAISED;
615 }
616 if (scrollPtr->vertical) {
617 points[0].x = scrollPtr->offset - 1;
618 points[0].y = scrollPtr->arrowLength + scrollPtr->offset;
619 points[1].x = width + scrollPtr->offset;
620 points[1].y = points[0].y;
621 points[2].x = width/2 + scrollPtr->offset;
622 points[2].y = scrollPtr->offset - 1;
623 Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border,
624 points, 3, scrollPtr->borderWidth, relief);
625 } else {
626 points[0].x = scrollPtr->arrowLength + scrollPtr->offset;
627 points[0].y = scrollPtr->offset - 1;
628 points[1].x = scrollPtr->offset;
629 points[1].y = width/2 + scrollPtr->offset;
630 points[2].x = points[0].x;
631 points[2].y = width + scrollPtr->offset;
632 Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border,
633 points, 3, scrollPtr->borderWidth, relief);
634 }
635
636 /*
637 * Display the bottom or right arrow.
638 */
639
640 if (scrollPtr->mouseField == BOTTOM_ARROW) {
641 border = scrollPtr->activeBorder;
642 relief = scrollPtr->pressField == BOTTOM_ARROW ? TK_RELIEF_SUNKEN
643 : TK_RELIEF_RAISED;
644 } else {
645 border = scrollPtr->fgBorder;
646 relief = TK_RELIEF_RAISED;
647 }
648 if (scrollPtr->vertical) {
649 points[0].x = scrollPtr->offset;
650 points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength
651 - scrollPtr->offset;
652 points[1].x = width/2 + scrollPtr->offset;
653 points[1].y = Tk_Height(tkwin) - scrollPtr->offset;
654 points[2].x = width + scrollPtr->offset;
655 points[2].y = points[0].y;
656 Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border,
657 points, 3, scrollPtr->borderWidth, relief);
658 } else {
659 points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength
660 - scrollPtr->offset;
661 points[0].y = scrollPtr->offset - 1;
662 points[1].x = points[0].x;
663 points[1].y = width + scrollPtr->offset;
664 points[2].x = Tk_Width(tkwin) - scrollPtr->offset;
665 points[2].y = width/2 + scrollPtr->offset;
666 Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, border,
667 points, 3, scrollPtr->borderWidth, relief);
668 }
669
670 /*
671 * Display the slider.
672 */
673
674 if (scrollPtr->mouseField == SLIDER) {
675 border = scrollPtr->activeBorder;
676 relief = scrollPtr->pressField == SLIDER ? TK_RELIEF_SUNKEN
677 : TK_RELIEF_RAISED;
678 } else {
679 border = scrollPtr->fgBorder;
680 relief = TK_RELIEF_RAISED;
681 }
682 fieldLength = (scrollPtr->vertical ? Tk_Height(tkwin) : Tk_Width(tkwin))
683 - 2*(scrollPtr->arrowLength + scrollPtr->offset);
684 if (fieldLength < 0) {
685 fieldLength = 0;
686 }
687 if (scrollPtr->vertical) {
688 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
689 1 + scrollPtr->offset, scrollPtr->sliderFirst,
690 width-2, scrollPtr->sliderLast - scrollPtr->sliderFirst,
691 scrollPtr->borderWidth, relief);
692 } else {
693 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
694 scrollPtr->sliderFirst, 1 + scrollPtr->offset,
695 scrollPtr->sliderLast - scrollPtr->sliderFirst, width-2,
696 scrollPtr->borderWidth, relief);
697 }
698
699 /*
700 * Copy the information from the off-screen pixmap onto the screen,
701 * then delete the pixmap.
702 */
703
704 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
705 scrollPtr->copyGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
706 XFreePixmap(Tk_Display(tkwin), pixmap);
707
708 done:
709 scrollPtr->flags &= ~REDRAW_PENDING;
710 }
711 \f
712 /*
713 *--------------------------------------------------------------
714 *
715 * ScrollbarEventProc --
716 *
717 * This procedure is invoked by the Tk dispatcher for various
718 * events on scrollbars.
719 *
720 * Results:
721 * None.
722 *
723 * Side effects:
724 * When the window gets deleted, internal structures get
725 * cleaned up. When it gets exposed, it is redisplayed.
726 *
727 *--------------------------------------------------------------
728 */
729
730 static void
731 ScrollbarEventProc (
732 ClientData clientData, /* Information about window. */
733 XEvent *eventPtr /* Information about event. */
734 )
735 {
736 Scrollbar *scrollPtr = (Scrollbar *) clientData;
737
738 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
739 EventuallyRedraw(scrollPtr);
740 } else if (eventPtr->type == DestroyNotify) {
741 Tcl_DeleteCommand(scrollPtr->interp, Tk_PathName(scrollPtr->tkwin));
742 scrollPtr->tkwin = NULL;
743 if (scrollPtr->flags & REDRAW_PENDING) {
744 Tk_CancelIdleCall(DisplayScrollbar, (ClientData) scrollPtr);
745 }
746 Tk_EventuallyFree((ClientData) scrollPtr, DestroyScrollbar);
747 } else if (eventPtr->type == ConfigureNotify) {
748 ComputeScrollbarGeometry(scrollPtr);
749 }
750 }
751 \f
752 /*
753 *----------------------------------------------------------------------
754 *
755 * ComputeScrollbarGeometry --
756 *
757 * After changes in a scrollbar's size or configuration, this
758 * procedure recomputes various geometry information used in
759 * displaying the scrollbar.
760 *
761 * Results:
762 * None.
763 *
764 * Side effects:
765 * The scrollbar will be displayed differently.
766 *
767 *----------------------------------------------------------------------
768 */
769
770 static void
771 ComputeScrollbarGeometry (
772 register Scrollbar *scrollPtr /* Scrollbar whose geometry may
773 * have changed. */
774 )
775 {
776 int width, fieldLength;
777
778 if (scrollPtr->relief == TK_RELIEF_FLAT) {
779 scrollPtr->offset = 0;
780 } else {
781 scrollPtr->offset = scrollPtr->borderWidth;
782 }
783 width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
784 : Tk_Height(scrollPtr->tkwin);
785 scrollPtr->arrowLength =
786 (((width - 2*scrollPtr->offset)*173) + 100) / 200;
787 fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
788 : Tk_Width(scrollPtr->tkwin))
789 - 2*(scrollPtr->arrowLength + scrollPtr->offset);
790 if (fieldLength < 0) {
791 fieldLength = 0;
792 }
793 if (scrollPtr->totalUnits <= 0) {
794 scrollPtr->sliderFirst = 0;
795 scrollPtr->sliderLast = fieldLength;
796 } else {
797 scrollPtr->sliderFirst = (fieldLength*scrollPtr->firstUnit
798 + scrollPtr->totalUnits/2)/scrollPtr->totalUnits;
799 scrollPtr->sliderLast = (fieldLength*(scrollPtr->lastUnit+1)
800 + scrollPtr->totalUnits/2)/scrollPtr->totalUnits;
801
802 /*
803 * Adjust the slider so that some piece of it is always
804 * displayed in the scrollbar and so that it has at least
805 * a minimal width (so it can be grabbed with the mouse).
806 */
807
808 if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) {
809 scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
810 }
811 if (scrollPtr->sliderFirst < 0) {
812 scrollPtr->sliderFirst = 0;
813 }
814 if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
815 + 2*scrollPtr->borderWidth)) {
816 scrollPtr->sliderLast = scrollPtr->sliderFirst
817 + 2*scrollPtr->borderWidth;
818 }
819 if (scrollPtr->sliderLast > fieldLength) {
820 scrollPtr->sliderLast = fieldLength;
821 }
822 }
823 scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->offset;
824 scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->offset;
825
826 /*
827 * Register the desired geometry for the window (leave enough space
828 * for the two arrows plus a minimum-size slider, plus border around
829 * the whole window, if any). Then arrange for the window to be
830 * redisplayed.
831 */
832
833 if (scrollPtr->vertical) {
834 Tk_GeometryRequest(scrollPtr->tkwin,
835 scrollPtr->width + 2*scrollPtr->offset,
836 2*(scrollPtr->arrowLength + scrollPtr->borderWidth
837 + scrollPtr->offset));
838 } else {
839 Tk_GeometryRequest(scrollPtr->tkwin,
840 2*(scrollPtr->arrowLength + scrollPtr->borderWidth
841 + scrollPtr->offset), scrollPtr->width + 2*scrollPtr->offset);
842 }
843 Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->borderWidth);
844
845 }
846 \f
847 /*
848 *--------------------------------------------------------------
849 *
850 * ScrollbarPosition --
851 *
852 * Determine the scrollbar element corresponding to a
853 * given position.
854 *
855 * Results:
856 * One of TOP_ARROW, TOP_GAP, etc., indicating which element
857 * of the scrollbar covers the position given by (x, y). If
858 * (x,y) is outside the scrollbar entirely, then OUTSIDE is
859 * returned.
860 *
861 * Side effects:
862 * None.
863 *
864 *--------------------------------------------------------------
865 */
866
867 static int
868 ScrollbarPosition (
869 register Scrollbar *scrollPtr, /* Scrollbar widget record. */
870 int x,
871 int y /* Coordinates within scrollPtr's
872 * window. */
873 )
874 {
875 int length, width, tmp;
876
877 if (scrollPtr->vertical) {
878 length = Tk_Height(scrollPtr->tkwin);
879 width = Tk_Width(scrollPtr->tkwin);
880 } else {
881 tmp = x;
882 x = y;
883 y = tmp;
884 length = Tk_Width(scrollPtr->tkwin);
885 width = Tk_Height(scrollPtr->tkwin);
886 }
887
888 if ((x < 0) || (x > width) || (y < 0)) {
889 return OUTSIDE;
890 }
891
892 /*
893 * All of the calculations in this procedure mirror those in
894 * DisplayScrollbar. Be sure to keep the two consistent.
895 */
896
897 if (y < (scrollPtr->offset + scrollPtr->arrowLength)) {
898 return TOP_ARROW;
899 }
900 if (y < scrollPtr->sliderFirst) {
901 return TOP_GAP;
902 }
903 if (y < scrollPtr->sliderLast) {
904 return SLIDER;
905 }
906 if (y >= (length - (scrollPtr->arrowLength + scrollPtr->offset))) {
907 return BOTTOM_ARROW;
908 }
909 return BOTTOM_GAP;
910 }
911 \f
912 /*
913 *--------------------------------------------------------------
914 *
915 * ScrollbarMouseProc --
916 *
917 * This procedure is called back by Tk in response to
918 * mouse events such as window entry, window exit, mouse
919 * motion, and button presses.
920 *
921 * Results:
922 * None.
923 *
924 * Side effects:
925 * This procedure implements the "feel" of the scrollbar
926 * by issuing scrolling commands in response to button presses
927 * and mouse motion.
928 *
929 *--------------------------------------------------------------
930 */
931
932 static void
933 ScrollbarMouseProc (
934 ClientData clientData, /* Information about window. */
935 register XEvent *eventPtr /* Information about event. */
936 )
937 {
938 register Scrollbar *scrollPtr = (Scrollbar *) clientData;
939
940 Tk_Preserve((ClientData) scrollPtr);
941 if (eventPtr->type == EnterNotify) {
942 if (scrollPtr->pressField == -1) {
943 ScrollbarNewField(scrollPtr,
944 ScrollbarPosition(scrollPtr, eventPtr->xcrossing.x,
945 eventPtr->xcrossing.y));
946 }
947 } else if (eventPtr->type == LeaveNotify) {
948 if (scrollPtr->pressField == -1) {
949 ScrollbarNewField(scrollPtr, OUTSIDE);
950 }
951 } else if (eventPtr->type == MotionNotify) {
952 if (scrollPtr->pressField == SLIDER) {
953 int delta, length, newFirst;
954
955 if (scrollPtr->vertical) {
956 delta = eventPtr->xmotion.y - scrollPtr->pressPos;
957 length = Tk_Height(scrollPtr->tkwin)
958 - 2*(scrollPtr->arrowLength + scrollPtr->offset);
959 } else {
960 delta = eventPtr->xmotion.x - scrollPtr->pressPos;
961 length = Tk_Width(scrollPtr->tkwin)
962 - 2*(scrollPtr->arrowLength + scrollPtr->offset);
963 }
964
965 /*
966 * Do the division with positive numbers to avoid
967 * differences in negative-number truncation on different
968 * machines.
969 */
970
971 if (delta >= 0) {
972 newFirst = scrollPtr->pressFirstUnit
973 + ((delta * scrollPtr->totalUnits) + (length/2))
974 / length;
975 } else {
976 newFirst = scrollPtr->pressFirstUnit
977 - (((-delta) * scrollPtr->totalUnits) + (length/2))
978 / length;
979 }
980 ScrollCmd(scrollPtr, newFirst);
981 } else if (scrollPtr->pressField == -1) {
982 ScrollbarNewField(scrollPtr,
983 ScrollbarPosition(scrollPtr, eventPtr->xmotion.x,
984 eventPtr->xmotion.y));
985 }
986 } else if ((eventPtr->type == ButtonPress)
987 && ((eventPtr->xbutton.state & ALL_BUTTONS) == 0)) {
988 scrollPtr->pressField = scrollPtr->mouseField;
989 if (scrollPtr->pressField != SLIDER) {
990 scrollPtr->autoRepeat = Tk_CreateTimerHandler(
991 scrollPtr->repeatDelay,
992 ScrollbarTimerProc, (ClientData) scrollPtr);
993 }
994 if (scrollPtr->vertical) {
995 scrollPtr->pressPos = eventPtr->xbutton.y;
996 } else {
997 scrollPtr->pressPos = eventPtr->xbutton.x;
998 }
999 scrollPtr->pressFirstUnit = scrollPtr->firstUnit;
1000 if (scrollPtr->pressFirstUnit <= -scrollPtr->windowUnits) {
1001 scrollPtr->pressFirstUnit = 1-scrollPtr->windowUnits;
1002 }
1003 if (scrollPtr->pressFirstUnit >= scrollPtr->totalUnits) {
1004 scrollPtr->pressFirstUnit = scrollPtr->totalUnits-1;
1005 }
1006 EventuallyRedraw(scrollPtr);
1007 } else if (eventPtr->type == ButtonRelease) {
1008 if (scrollPtr->pressField == scrollPtr->mouseField) {
1009 switch (scrollPtr->pressField) {
1010 case TOP_ARROW:
1011 ScrollCmd(scrollPtr, scrollPtr->firstUnit-1);
1012 break;
1013 case TOP_GAP:
1014 if (scrollPtr->windowUnits <= 1) {
1015 ScrollCmd(scrollPtr, scrollPtr->firstUnit - 1);
1016 } else {
1017 ScrollCmd(scrollPtr, scrollPtr->firstUnit
1018 - (scrollPtr->windowUnits-1));
1019 }
1020 break;
1021 case BOTTOM_GAP: {
1022 if (scrollPtr->windowUnits <= 1) {
1023 ScrollCmd(scrollPtr, scrollPtr->firstUnit + 1);
1024 } else {
1025 ScrollCmd(scrollPtr, scrollPtr->firstUnit
1026 + (scrollPtr->windowUnits-1));
1027 }
1028 break;
1029 }
1030 case BOTTOM_ARROW:
1031 ScrollCmd(scrollPtr, scrollPtr->firstUnit+1);
1032 break;
1033 }
1034 }
1035 if (scrollPtr->autoRepeat != NULL) {
1036 Tk_DeleteTimerHandler(scrollPtr->autoRepeat);
1037 scrollPtr->autoRepeat = NULL;
1038 }
1039 EventuallyRedraw(scrollPtr);
1040 scrollPtr->pressField = -1;
1041 ScrollbarNewField(scrollPtr,
1042 ScrollbarPosition(scrollPtr, eventPtr->xbutton.x,
1043 eventPtr->xbutton.y));
1044 }
1045 Tk_Release((ClientData) scrollPtr);
1046 }
1047 \f
1048 /*
1049 *--------------------------------------------------------------
1050 *
1051 * ScrollCmd --
1052 *
1053 * This procedure takes care of invoking a scrolling Tcl
1054 * command and reporting any error that occurs in it.
1055 *
1056 * Results:
1057 * None.
1058 *
1059 * Side effects:
1060 * A Tcl command is invoked, and an additional error-processing
1061 * command may also be invoked.
1062 *
1063 *--------------------------------------------------------------
1064 */
1065
1066 static void
1067 ScrollCmd (
1068 register Scrollbar *scrollPtr, /* Scrollbar from which to issue
1069 * command. */
1070 int unit /* Unit position within thing being
1071 * being displayed that should appear
1072 * at top or right of screen. */
1073 )
1074 {
1075 char string[20];
1076 int result;
1077
1078 if ((unit == scrollPtr->firstUnit) || (scrollPtr->command == NULL)) {
1079 return;
1080 }
1081 sprintf(string, " %d", unit);
1082 result = Tcl_VarEval(scrollPtr->interp, scrollPtr->command, string,
1083 (char *) NULL);
1084 if (result != TCL_OK) {
1085 TkBindError(scrollPtr->interp);
1086 }
1087 }
1088 \f
1089 /*
1090 *--------------------------------------------------------------
1091 *
1092 * EventuallyRedraw --
1093 *
1094 * Arrange for one or more of the fields of a scrollbar
1095 * to be redrawn.
1096 *
1097 * Results:
1098 * None.
1099 *
1100 * Side effects:
1101 * None.
1102 *
1103 *--------------------------------------------------------------
1104 */
1105
1106 static void
1107 EventuallyRedraw (
1108 register Scrollbar *scrollPtr /* Information about widget. */
1109 )
1110 {
1111 if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
1112 return;
1113 }
1114 if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
1115 Tk_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr);
1116 scrollPtr->flags |= REDRAW_PENDING;
1117 }
1118 }
1119 \f
1120 /*
1121 *--------------------------------------------------------------
1122 *
1123 * ScrollbarNewField --
1124 *
1125 * This procedure is called to declare that the mouse is in
1126 * a particular field of the scrollbar (e.g. top arrow), so
1127 * that the field can be highlighed and the previous field
1128 * can be returned to normal display.
1129 *
1130 * Results:
1131 * None.
1132 *
1133 * Side effects:
1134 * Fields may be redisplayed.
1135 *
1136 *--------------------------------------------------------------
1137 */
1138
1139 static void
1140 ScrollbarNewField (
1141 register Scrollbar *scrollPtr, /* Information about widget. */
1142 int field /* Identifies field under mouse,
1143 * e.g. TOP_ARROW. */
1144 )
1145 {
1146 if (field == scrollPtr->mouseField) {
1147 return;
1148 }
1149 EventuallyRedraw(scrollPtr);
1150 scrollPtr->mouseField = field;
1151 }
1152 \f
1153 /*
1154 *--------------------------------------------------------------
1155 *
1156 * ScrollbarTimerProc --
1157 *
1158 * This procedure is invoked as a Tk timer handler for actions
1159 * that auto-repeat (mouse presses in an arrow or gap). It
1160 * performs the auto-repeat action.
1161 *
1162 * Results:
1163 * None.
1164 *
1165 * Side effects:
1166 * Whatever action corresponds to the current mouse button
1167 * is repeated, and this procedure is rescheduled to execute
1168 * again later.
1169 *
1170 *--------------------------------------------------------------
1171 */
1172
1173 static void
1174 ScrollbarTimerProc (
1175 ClientData clientData /* Information about widget. */
1176 )
1177 {
1178 register Scrollbar *scrollPtr = (Scrollbar *) clientData;
1179
1180 Tk_Preserve((ClientData) scrollPtr);
1181 switch(scrollPtr->pressField) {
1182 case TOP_ARROW:
1183 ScrollCmd(scrollPtr, scrollPtr->firstUnit-1);
1184 break;
1185 case TOP_GAP:
1186 ScrollCmd(scrollPtr, scrollPtr->firstUnit
1187 - (scrollPtr->windowUnits-1));
1188 break;
1189 case BOTTOM_GAP: {
1190 ScrollCmd(scrollPtr, scrollPtr->firstUnit
1191 + (scrollPtr->windowUnits-1));
1192 break;
1193 }
1194 case BOTTOM_ARROW:
1195 ScrollCmd(scrollPtr, scrollPtr->firstUnit+1);
1196 break;
1197 }
1198 if (scrollPtr->tkwin != NULL) {
1199 scrollPtr->autoRepeat = Tk_CreateTimerHandler(
1200 scrollPtr->repeatInterval, ScrollbarTimerProc,
1201 (ClientData) scrollPtr);
1202 }
1203 Tk_Release((ClientData) scrollPtr);
1204 }
Impressum, Datenschutz