|
|
1.1 root 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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.