OSDN Git Service

Initial revision
[pf3gnuchains/sourceware.git] / tk / macosx / tkMacOSXScrlbr.c
1 /* 
2  * tkMacOSXScrollbar.c --
3  *
4  *        This file implements the Macintosh specific portion of the scrollbar
5  *        widget.  The Macintosh scrollbar may also draw a windows grow
6  *        region under certain cases.
7  *
8  * Copyright (c) 1996 by Sun Microsystems, Inc.
9  * Copyright 2001, Apple Computer, Inc.
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  *
14  * RCS: @(#) $Id$
15  */
16
17 #include "tkScrollbar.h"
18 #include "tkMacOSXInt.h"
19
20 #include <Carbon/Carbon.h>
21
22 #include "tkMacOSXDebug.h"
23 /*
24  * The following definitions should really be in MacOS
25  * header files.  They are included here as this is the only
26  * file that needs the declarations.
27  */
28 typedef pascal void (*ThumbActionFunc)(void);
29
30 typedef ThumbActionFunc ThumbActionUPP;
31
32 enum {
33     uppThumbActionProcInfo = kPascalStackBased
34 };
35
36 #define NewThumbActionProc(userRoutine) ((ThumbActionUPP) (userRoutine))
37
38 /*
39  * Minimum slider length, in pixels (designed to make sure that the slider
40  * is always easy to grab with the mouse).
41  */
42
43 #define MIN_SLIDER_LENGTH       5
44 #define MIN_SCROLLBAR_VALUE     0
45 #define MAX_SCROLLBAR_VALUE     1000
46 #define MAX_SCROLLBAR_DVALUE    1000.0
47
48 /*
49  * Declaration of Windows specific scrollbar structure.
50  */
51
52 typedef struct MacScrollbar {
53     TkScrollbar info;            /* Generic scrollbar info */
54     ControlRef  sbHandle;        /* Opaque handle to the Scrollbar contol struct */
55     int         macFlags;        /* Various flags; see below */
56 } MacScrollbar;
57
58 /* Handle to the Scrollbar control structure */
59         
60
61 /*
62  * Flag bits for scrollbars on the Mac:
63  * 
64  * ALREADY_DEAD:                Non-zero means this scrollbar has been
65  *                                destroyed, but has not been cleaned up.
66  * IN_MODAL_LOOP:                Non-zero means this scrollbar is in the middle
67  *                                of a modal loop.
68  * ACTIVE:                        Non-zero means this window is currently
69  *                                active (in the foreground).
70  * FLUSH_TOP:                        Flush with top of Mac window.
71  * FLUSH_BOTTOM:                Flush with bottom of Mac window.
72  * FLUSH_RIGHT:                        Flush with right of Mac window.
73  * FLUSH_LEFT:                        Flush with left of Mac window.
74  * SCROLLBAR_GROW:                Non-zero means this window draws the grow
75  *                                region for the toplevel window.
76  * AUTO_ADJUST:                        Non-zero means we automatically adjust
77  *                                the size of the widget to align correctly
78  *                                along a Mac window.
79  * DRAW_GROW:                        Non-zero means we draw the grow region.
80  */
81
82 #define ALREADY_DEAD             1
83 #define IN_MODAL_LOOP            2
84 #define ACTIVE                   4
85 #define FLUSH_TOP                8
86 #define FLUSH_BOTTOM             16
87 #define FLUSH_RIGHT              32
88 #define FLUSH_LEFT               64
89 #define SCROLLBAR_GROW           128
90 #define AUTO_ADJUST              256
91 #define DRAW_GROW                512
92
93 /*
94  * Globals uses locally in this file.
95  */
96 static ControlActionUPP scrollActionProc = NULL; /* Pointer to func. */
97 static ThumbActionUPP thumbActionProc = NULL;    /* Pointer to func. */
98 static TkScrollbar *activeScrollPtr = NULL;        /* Non-null when in thumb */
99                                                  /* proc. */
100 /*
101  * Forward declarations for procedures defined later in this file:
102  */
103
104 static pascal void ScrollbarActionProc _ANSI_ARGS_((ControlRef theControl, ControlPartCode partCode));
105 static int         ScrollbarBindProc _ANSI_ARGS_((ClientData clientData,
106     Tcl_Interp *interp, XEvent *eventPtr,
107     Tk_Window tkwin, KeySym keySym));
108 static void        ScrollbarEventProc _ANSI_ARGS_(( ClientData clientData, XEvent *eventPtr));
109 static pascal void ThumbActionProc _ANSI_ARGS_((void));
110 static void        UpdateControlValues _ANSI_ARGS_((MacScrollbar *macScrollPtr));
111                     
112 /*
113  * The class procedure table for the scrollbar widget.  Leave the proc fields
114  * initialized to NULL, which should happen automatically because of the scope
115  * at which the variable is declared.
116  */
117
118 Tk_ClassProcs tkpScrollbarProcs = {
119     sizeof(Tk_ClassProcs)        /* size */
120 };
121 \f
122 /*
123  *----------------------------------------------------------------------
124  *
125  * TkpCreateScrollbar --
126  *
127  *        Allocate a new TkScrollbar structure.
128  *
129  * Results:
130  *        Returns a newly allocated TkScrollbar structure.
131  *
132  * Side effects:
133  *        None.
134  *
135  *----------------------------------------------------------------------
136  */
137
138 TkScrollbar *
139 TkpCreateScrollbar(
140     Tk_Window tkwin)        /* New Tk Window. */
141 {
142     MacScrollbar * macScrollPtr;
143     TkWindow *winPtr = (TkWindow *)tkwin;
144     
145     if (scrollActionProc == NULL) {
146         scrollActionProc = NewControlActionUPP (ScrollbarActionProc);
147         thumbActionProc = NewThumbActionProc(ThumbActionProc);
148     }
149
150     macScrollPtr = (MacScrollbar *) ckalloc(sizeof(MacScrollbar));
151     macScrollPtr->sbHandle = NULL;
152     macScrollPtr->macFlags = 0;
153
154     Tk_CreateEventHandler(tkwin, ActivateMask|ExposureMask|
155         StructureNotifyMask|FocusChangeMask,
156         ScrollbarEventProc, (ClientData) macScrollPtr);
157
158     if (!Tcl_GetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL)) {
159         Tcl_SetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL, (ClientData)1);
160         TkCreateBindingProcedure(winPtr->mainPtr->interp,
161             winPtr->mainPtr->bindingTable,
162             (ClientData)Tk_GetUid("Scrollbar"), "<ButtonPress>",
163             ScrollbarBindProc, NULL, NULL);
164     }
165     return (TkScrollbar *) macScrollPtr;
166 }
167 \f
168 /*
169  *--------------------------------------------------------------
170  *
171  * TkpDisplayScrollbar --
172  *
173  *        This procedure redraws the contents of a scrollbar window.
174  *        It is invoked as a do-when-idle handler, so it only runs
175  *        when there's nothing else for the application to do.
176  *
177  * Results:
178  *        None.
179  *
180  * Side effects:
181  *        Information appears on the screen.
182  *
183  *--------------------------------------------------------------
184  */
185
186 void
187 TkpDisplayScrollbar(
188     ClientData clientData)        /* Information about window. */
189 {
190     TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
191     MacScrollbar *macScrollPtr = (MacScrollbar *) clientData;
192     Tk_Window tkwin = scrollPtr->tkwin;
193     
194     MacDrawable *macDraw;
195     CGrafPtr saveWorld;
196     GDHandle saveDevice;
197     GWorldPtr destPort;
198     WindowRef windowRef;
199     
200     if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
201         goto done;
202     }
203
204     /*
205      * Draw the focus or any 3D relief we may have.
206      */
207     if (scrollPtr->highlightWidth != 0) {
208         GC fgGC, bgGC;
209
210         bgGC = Tk_GCForColor(scrollPtr->highlightBgColorPtr,
211             Tk_WindowId(tkwin));
212
213         if (scrollPtr->flags & GOT_FOCUS) {
214             fgGC = Tk_GCForColor(scrollPtr->highlightColorPtr,
215                 Tk_WindowId(tkwin));
216             TkpDrawHighlightBorder(tkwin, fgGC, bgGC, scrollPtr->highlightWidth,
217                 Tk_WindowId(tkwin));
218         } else {
219             TkpDrawHighlightBorder(tkwin, bgGC, bgGC, scrollPtr->highlightWidth,
220                 Tk_WindowId(tkwin));
221         }
222     }
223     Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), scrollPtr->bgBorder,
224         scrollPtr->highlightWidth, scrollPtr->highlightWidth,
225         Tk_Width(tkwin) - 2*scrollPtr->highlightWidth,
226         Tk_Height(tkwin) - 2*scrollPtr->highlightWidth,
227         scrollPtr->borderWidth, scrollPtr->relief);
228
229     /*
230      * Set up port for drawing Macintosh control.
231      */
232     macDraw = (MacDrawable *) Tk_WindowId(tkwin);
233     destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin));
234     GetGWorld(&saveWorld, &saveDevice);
235     SetGWorld(destPort, NULL);
236     TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin));
237
238     if (macScrollPtr->sbHandle == NULL) {
239         Rect r;
240         SInt16 initialValue;
241         SInt16 minValue;
242         SInt16 maxValue;
243         SInt16 procID;
244         WindowRef frontNonFloating;
245         
246         r.left = r.top = 0;
247         r.right = r.bottom = 1;
248
249         minValue = MIN_SCROLLBAR_VALUE;
250         maxValue = MAX_SCROLLBAR_VALUE;
251         initialValue = (minValue + maxValue)/2;
252         procID = kControlScrollBarLiveProc;
253
254         windowRef = GetWindowFromPort(destPort);
255         macScrollPtr->sbHandle = NewControl(windowRef, &r, "\p",
256                 false, initialValue,minValue,maxValue,
257                 procID, (SInt32) scrollPtr);
258
259         /*
260          * If we are foremost then make us active.
261          */
262         
263         frontNonFloating = FrontNonFloatingWindow();
264         
265         if ((windowRef == FrontWindow()) || TkpIsWindowFloating(windowRef)) {
266             macScrollPtr->macFlags |= ACTIVE;
267         }
268     }
269
270     /*
271      * Update the control values before we draw.
272      */
273     windowRef  = GetControlOwner (macScrollPtr->sbHandle);
274     UpdateControlValues(macScrollPtr);
275     
276     if (macScrollPtr->macFlags & ACTIVE) {
277         Draw1Control(macScrollPtr->sbHandle);
278         if (macScrollPtr->macFlags & DRAW_GROW) {
279             DrawGrowIcon(windowRef);
280         }
281     } else {
282         HiliteControl (macScrollPtr->sbHandle, 255 );
283         Draw1Control(macScrollPtr->sbHandle);
284         if (macScrollPtr->macFlags & DRAW_GROW) {
285             DrawGrowIcon(windowRef);
286             Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), scrollPtr->bgBorder,
287                 Tk_Width(tkwin) - 13, Tk_Height(tkwin) - 13,
288                 Tk_Width(tkwin), Tk_Height(tkwin),
289                 0, TK_RELIEF_FLAT);
290         }
291     }
292     
293     SetGWorld(saveWorld, saveDevice);
294      
295     done:
296     scrollPtr->flags &= ~REDRAW_PENDING;
297 }
298 \f
299 /*
300  *----------------------------------------------------------------------
301  *
302  * TkpConfigureScrollbar --
303  *
304  *        This procedure is called after the generic code has finished
305  *        processing configuration options, in order to configure
306  *        platform specific options.
307  *
308  * Results:
309  *        None.
310  *
311  * Side effects:
312  *        None.
313  *
314  *----------------------------------------------------------------------
315  */
316
317 void
318 TkpConfigureScrollbar(scrollPtr)
319     register TkScrollbar *scrollPtr;        /* Information about widget;  may or
320                                              * may not already have values for
321                                              * some fields. */
322 {
323 }
324 \f
325 /*
326  *----------------------------------------------------------------------
327  *
328  * TkpComputeScrollbarGeometry --
329  *
330  *        After changes in a scrollbar's size or configuration, this
331  *        procedure recomputes various geometry information used in
332  *        displaying the scrollbar.
333  *
334  * Results:
335  *        None.
336  *
337  * Side effects:
338  *        The scrollbar will be displayed differently.
339  *
340  *----------------------------------------------------------------------
341  */
342
343 void
344 TkpComputeScrollbarGeometry(
345     register TkScrollbar *scrollPtr)        /* Scrollbar whose geometry may
346                                              * have changed. */
347 {
348     MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr;
349     int width, fieldLength, adjust = 0;
350
351     if (scrollPtr->highlightWidth < 0) {
352         scrollPtr->highlightWidth = 0;
353     }
354     scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth;
355     width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
356             : Tk_Height(scrollPtr->tkwin);
357     scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1;
358     fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
359             : Tk_Width(scrollPtr->tkwin))
360             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
361     if (fieldLength < 0) {
362         fieldLength = 0;
363     }
364     scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction;
365     scrollPtr->sliderLast = fieldLength*scrollPtr->lastFraction;
366
367     /*
368      * Adjust the slider so that some piece of it is always
369      * displayed in the scrollbar and so that it has at least
370      * a minimal width (so it can be grabbed with the mouse).
371      */
372
373     if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) {
374         scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
375     }
376     if (scrollPtr->sliderFirst < 0) {
377         scrollPtr->sliderFirst = 0;
378     }
379     if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
380             + MIN_SLIDER_LENGTH)) {
381         scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
382     }
383     if (scrollPtr->sliderLast > fieldLength) {
384         scrollPtr->sliderLast = fieldLength;
385     }
386     scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset;
387     scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset;
388
389     /*
390      * Register the desired geometry for the window (leave enough space
391      * for the two arrows plus a minimum-size slider, plus border around
392      * the whole window, if any).  Then arrange for the window to be
393      * redisplayed.
394      */
395
396     if (scrollPtr->vertical) {
397         if ((macScrollPtr->macFlags & AUTO_ADJUST) &&
398             (macScrollPtr->macFlags & (FLUSH_RIGHT|FLUSH_LEFT))) {
399             adjust--;
400         }
401         Tk_GeometryRequest(scrollPtr->tkwin,
402             scrollPtr->width + 2*scrollPtr->inset + adjust,
403             2*(scrollPtr->arrowLength + scrollPtr->borderWidth
404             + scrollPtr->inset));
405     } else {
406         if ((macScrollPtr->macFlags & AUTO_ADJUST) &&
407             (macScrollPtr->macFlags & (FLUSH_TOP|FLUSH_BOTTOM))) {
408             adjust--;
409         }
410         Tk_GeometryRequest(scrollPtr->tkwin,
411             2*(scrollPtr->arrowLength + scrollPtr->borderWidth
412             + scrollPtr->inset), scrollPtr->width + 2*scrollPtr->inset + adjust);
413     }
414     Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset);
415 }
416 \f
417 /*
418  *----------------------------------------------------------------------
419  *
420  * TkpDestroyScrollbar --
421  *
422  *        Free data structures associated with the scrollbar control.
423  *
424  * Results:
425  *        None.
426  *
427  * Side effects:
428  *        None.
429  *
430  *----------------------------------------------------------------------
431  */
432
433 void
434 TkpDestroyScrollbar(
435     TkScrollbar *scrollPtr)        /* Scrollbar to destroy. */
436 {
437     MacScrollbar *macScrollPtr = (MacScrollbar *)scrollPtr;
438
439     if (macScrollPtr->sbHandle != NULL) {
440         if (!(macScrollPtr->macFlags & IN_MODAL_LOOP)) {
441             DisposeControl(macScrollPtr->sbHandle);
442             macScrollPtr->sbHandle = NULL;
443         }
444     }
445     macScrollPtr->macFlags |= ALREADY_DEAD;
446 }
447 \f
448 /*
449  *--------------------------------------------------------------
450  *
451  * TkpScrollbarPosition --
452  *
453  *        Determine the scrollbar element corresponding to a
454  *        given position.
455  *
456  * Results:
457  *        One of TOP_ARROW, TOP_GAP, etc., indicating which element
458  *        of the scrollbar covers the position given by (x, y).  If
459  *        (x,y) is outside the scrollbar entirely, then OUTSIDE is
460  *        returned.
461  *
462  * Side effects:
463  *        None.
464  *
465  *--------------------------------------------------------------
466  */
467
468 int
469 TkpScrollbarPosition(
470     TkScrollbar *scrollPtr,        /* Scrollbar widget record. */
471     int x, int y)                /* Coordinates within scrollPtr's
472                                  * window. */
473 {
474     MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr;
475     GWorldPtr destPort;
476     int length, width, tmp, inactive = false;
477     ControlPartCode part;
478     Point where;
479     Rect bounds;
480     int  x0, y0;
481
482     x0 = x;
483     y0 = y;
484
485     if (scrollPtr->vertical) {
486         length = Tk_Height(scrollPtr->tkwin);
487         width = Tk_Width(scrollPtr->tkwin);
488     } else {
489         tmp = x;
490         x = y;
491         y = tmp;
492         length = Tk_Width(scrollPtr->tkwin);
493         width = Tk_Height(scrollPtr->tkwin);
494     }
495
496     if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset))
497             || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) {
498         return OUTSIDE;
499     }
500
501     /*
502      * All of the calculations in this procedure mirror those in
503      * DisplayScrollbar.  Be sure to keep the two consistent.  On the 
504      * Macintosh we use the OS call TestControl to do this mapping.
505      * For TestControl to work, the scrollbar must be active and must
506      * be in the current port.
507      */
508
509     destPort = TkMacOSXGetDrawablePort(Tk_WindowId(scrollPtr->tkwin));
510     SetGWorld(destPort, NULL);
511     UpdateControlValues(macScrollPtr);
512     if ( GetControlHilite(macScrollPtr->sbHandle) == 255 ) {
513         inactive = true;
514         HiliteControl(macScrollPtr->sbHandle, 0 );
515     }
516
517     TkMacOSXWinBounds((TkWindow *) scrollPtr->tkwin, &bounds);                
518     where.h = x0 + bounds.left;
519     where.v = y0 + bounds.top;
520     part = TestControl(((MacScrollbar *) scrollPtr)->sbHandle, where);
521     if (inactive) {
522         HiliteControl(macScrollPtr->sbHandle, 255 );
523     }
524     switch (part) {
525         case kControlUpButtonPart:
526             return TOP_ARROW;
527         case kControlPageUpPart:
528             return TOP_GAP;
529         case kControlIndicatorPart:
530             return SLIDER;
531         case kControlPageDownPart:
532             return BOTTOM_GAP;
533         case kControlDownButtonPart:
534             return BOTTOM_ARROW;
535         default:
536             return OUTSIDE;
537     }
538 }
539 \f
540 /*
541  *--------------------------------------------------------------
542  *
543  * ThumbActionProc --
544  *
545  *        Callback procedure used by the Macintosh toolbox call
546  *        TrackControl.  This call is used to track the thumb of
547  *        the scrollbar.  Unlike the ScrollbarActionProc function
548  *        this function is called once and basically takes over
549  *        tracking the scrollbar from the control.  This is done
550  *        to avoid conflicts with what the control plans to draw.
551  *
552  * Results:
553  *        None.
554  *
555  * Side effects:
556  *        May change the display.
557  *
558  *--------------------------------------------------------------
559  */
560
561 static pascal void
562 ThumbActionProc()
563 {
564     register TkScrollbar *scrollPtr = activeScrollPtr;
565     register MacScrollbar *macScrollPtr = (MacScrollbar *) activeScrollPtr;
566     Tcl_DString cmdString;
567     int origValue, trackBarPin;
568     double thumbWidth, newFirstFraction, trackBarSize;
569     char valueString[40];
570     Point currentPoint = { 0, 0 };
571     Rect trackRect;
572     Tcl_Interp *interp;
573     MouseTrackingResult trackingResult;
574     OSErr err;
575     
576     if (scrollPtr == NULL) {
577         return;
578     }
579
580     Tcl_DStringInit(&cmdString);
581     
582     /*
583      * First compute values that will remain constant during the tracking
584      * of the thumb.  The variable trackBarSize is the length of the scrollbar
585      * minus the 2 arrows and half the width of the thumb on both sides
586      * (3 * arrowLength).  The variable trackBarPin is the lower starting point
587      * of the drag region.
588      *
589      * Note: the arrowLength is equal to the thumb width of a Mac scrollbar.
590      */
591
592     origValue = GetControlValue(macScrollPtr->sbHandle);
593     GetControlBounds(macScrollPtr->sbHandle, &trackRect);
594     if (scrollPtr->vertical == true) {
595         trackBarSize = (double) (trackRect.bottom - trackRect.top
596             - (scrollPtr->arrowLength * 3));
597         trackBarPin = trackRect.top + scrollPtr->arrowLength
598             + (scrollPtr->arrowLength / 2);
599         InsetRect(&trackRect, -25, -113);
600         
601     } else {
602         trackBarSize = (double) (trackRect.right - trackRect.left
603             - (scrollPtr->arrowLength * 3));
604         trackBarPin = trackRect.left + scrollPtr->arrowLength
605             + (scrollPtr->arrowLength / 2);
606         InsetRect(&trackRect, -113, -25);
607     }
608
609     /*
610      * Track the mouse while the button is held down.  If the mouse is moved,
611      * we calculate the value that should be passed to the "command" part of
612      * the scrollbar.
613      */
614
615     do {
616         err = TrackMouseLocationWithOptions(NULL,
617                 kTrackMouseLocationOptionDontConsumeMouseUp,
618                 kEventDurationForever,
619                 &currentPoint,
620                 NULL,
621                 &trackingResult);
622             
623         if ((err==noErr) 
624                 && ((trackingResult == kMouseTrackingMouseDragged)
625                 || (trackingResult == kMouseTrackingMouseMoved))) {
626         /*
627          * Calculating this value is a little tricky.  We need to calculate a
628          * value for where the thumb would be in a Motif widget (variable
629          * thumb).  This value is what the "command" expects and is what will
630          * be resent to the scrollbar to update its value.
631          */
632             thumbWidth = scrollPtr->lastFraction - scrollPtr->firstFraction;
633             if (PtInRect(currentPoint, &trackRect)) {
634                 if (scrollPtr->vertical == true) {
635                     newFirstFraction =  (1.0 - thumbWidth) *
636                         ((double) (currentPoint.v - trackBarPin) / trackBarSize);
637                 } else {
638                     newFirstFraction =  (1.0 - thumbWidth) *
639                         ((double) (currentPoint.h - trackBarPin) / trackBarSize);
640                 }
641             } else {
642                 newFirstFraction = ((double) origValue / MAX_SCROLLBAR_DVALUE)
643                    * (1.0 - thumbWidth);
644             }
645             sprintf(valueString, "%g", newFirstFraction);
646             Tcl_DStringSetLength(&cmdString, 0);
647             Tcl_DStringAppend(&cmdString, scrollPtr->command,
648                 scrollPtr->commandSize);
649             Tcl_DStringAppendElement(&cmdString, "moveto");
650             Tcl_DStringAppendElement(&cmdString, valueString);
651             interp = scrollPtr->interp;
652             Tcl_Preserve((ClientData) interp);
653             Tcl_GlobalEval(interp, cmdString.string);
654             Tcl_Release((ClientData) interp);
655             Tcl_DStringSetLength(&cmdString, 0);
656             Tcl_DStringAppend(&cmdString, "update idletasks",
657                 strlen("update idletasks"));
658             Tcl_Preserve((ClientData) interp);
659             Tcl_GlobalEval(interp, cmdString.string);
660             Tcl_Release((ClientData) interp);
661         }
662     } while ((err==noErr) && trackingResult!=kMouseTrackingMouseReleased );
663
664     Tcl_DStringFree(&cmdString);
665     return;
666 }
667 \f
668 /*
669  *--------------------------------------------------------------
670  *
671  * ScrollbarActionProc --
672  *
673  *        Callback procedure used by the Macintosh toolbox call
674  *        TrackControl.  This call will update the display while
675  *        the scrollbar is being manipulated by the user.
676  *
677  * Results:
678  *        None.
679  *
680  * Side effects:
681  *        May change the display.
682  *
683  *--------------------------------------------------------------
684  */
685
686 static pascal void
687 ScrollbarActionProc(
688     ControlRef theControl,         /* Handle to scrollbat control */
689     ControlPartCode partCode)        /* Part of scrollbar that was "hit" */
690 {
691     TkScrollbar *scrollPtr = (TkScrollbar *) GetControlReference(theControl);
692     Tcl_DString cmdString;
693     
694     Tcl_DStringInit(&cmdString);
695     Tcl_DStringAppend(&cmdString, scrollPtr->command,
696             scrollPtr->commandSize);
697
698     if ( partCode == kControlUpButtonPart ||
699          partCode == kControlDownButtonPart ) {
700         Tcl_DStringAppendElement(&cmdString, "scroll");
701         Tcl_DStringAppendElement(&cmdString,
702                 (partCode == kControlUpButtonPart ) ? "-1" : "1");
703         Tcl_DStringAppendElement(&cmdString, "unit");
704     } else if (partCode == kControlPageUpPart || partCode == kControlPageDownPart ) {
705         Tcl_DStringAppendElement(&cmdString, "scroll");
706         Tcl_DStringAppendElement(&cmdString,
707                 (partCode == kControlPageUpPart ) ? "-1" : "1");
708         Tcl_DStringAppendElement(&cmdString, "page");
709     }
710     Tcl_Preserve((ClientData) scrollPtr->interp);
711     Tcl_DStringAppend(&cmdString, "; update idletasks",
712         strlen("; update idletasks"));
713     Tcl_GlobalEval(scrollPtr->interp, cmdString.string);
714     Tcl_Release((ClientData) scrollPtr->interp);
715
716     Tcl_DStringFree(&cmdString);
717 }
718 \f
719 /*
720  *--------------------------------------------------------------
721  *
722  * ScrollbarBindProc --
723  *
724  *        This procedure is invoked when the default <ButtonPress>
725  *        binding on the Scrollbar bind tag fires.
726  *
727  * Results:
728  *        None.
729  *
730  * Side effects:
731  *        The event enters a modal loop.
732  *
733  *--------------------------------------------------------------
734  */
735
736 static int
737 ScrollbarBindProc(
738     ClientData clientData,        /* Not used. */
739     Tcl_Interp *interp,           /* Interp with binding. */
740     XEvent *eventPtr,             /* X event that triggered binding. */
741     Tk_Window tkwin,              /* Target window for event. */
742     KeySym keySym)                /* The KeySym if a key event. */
743 {
744     TkWindow *winPtr = (TkWindow*)tkwin;
745     TkScrollbar *scrollPtr = (TkScrollbar *) winPtr->instanceData;
746     MacScrollbar *macScrollPtr = (MacScrollbar *) winPtr->instanceData;
747
748     Tcl_Preserve((ClientData)scrollPtr);
749     macScrollPtr->macFlags |= IN_MODAL_LOOP;
750     
751     if (eventPtr->type == ButtonPress) {
752             Point where;
753             Rect bounds;
754             int part, x, y, dummy;
755             unsigned int state;
756         CGrafPtr saveWorld;
757         GDHandle saveDevice;
758         GWorldPtr destPort;
759         Window window;
760
761         /*
762          * To call Macintosh control routines we must have the port
763          * set to the window containing the control.  We will then test
764          * which part of the control was hit and act accordingly.
765          */
766         destPort = TkMacOSXGetDrawablePort(Tk_WindowId(scrollPtr->tkwin));
767         GetGWorld(&saveWorld, &saveDevice);
768         SetGWorld(destPort, NULL);
769         TkMacOSXSetUpClippingRgn(Tk_WindowId(scrollPtr->tkwin));
770
771         TkMacOSXWinBounds((TkWindow *) scrollPtr->tkwin, &bounds);                
772             where.h = eventPtr->xbutton.x + bounds.left;
773             where.v = eventPtr->xbutton.y + bounds.top;
774         part = TestControl(macScrollPtr->sbHandle, where);
775         if (part == kControlIndicatorPart && scrollPtr->jump == false) {
776             /*
777              * Case 1: In thumb, no jump scrolling.  Call track control
778              * with the thumb action proc which will do most of the work.
779              * Set the global activeScrollPtr to the current control
780              * so the callback may have access to it.
781              */
782             activeScrollPtr = scrollPtr;
783             part = TrackControl(macScrollPtr->sbHandle, where,
784                     (ControlActionUPP) thumbActionProc);
785             activeScrollPtr = NULL;
786         } else if (part == kControlIndicatorPart) {
787             /*
788              * Case 2: in thumb with jump scrolling.  Call TrackControl
789              * with a NULL action proc.  Use the new value of the control
790              * to set update the control.
791              */
792             part = TrackControl(macScrollPtr->sbHandle, where, NULL);
793             if (part == kControlIndicatorPart) {
794                     double newFirstFraction, thumbWidth;
795                 Tcl_DString cmdString;
796                 char valueString[TCL_DOUBLE_SPACE];
797
798                 /*
799                  * The following calculation takes the new control
800                  * value and maps it to what Tk needs for its variable
801                  * thumb size representation.
802                  */
803                 thumbWidth = scrollPtr->lastFraction
804                      - scrollPtr->firstFraction;
805                 newFirstFraction = (1.0 - thumbWidth) *
806                     ((double) GetControlValue(macScrollPtr->sbHandle) / MAX_SCROLLBAR_DVALUE);
807                 sprintf(valueString, "%g", newFirstFraction);
808
809                 Tcl_DStringInit(&cmdString);
810                 Tcl_DStringAppend(&cmdString, scrollPtr->command,
811                         strlen(scrollPtr->command));
812                 Tcl_DStringAppendElement(&cmdString, "moveto");
813                 Tcl_DStringAppendElement(&cmdString, valueString);
814                 Tcl_DStringAppend(&cmdString, "; update idletasks",
815                         strlen("; update idletasks"));
816                 
817                 interp = scrollPtr->interp;
818                 Tcl_Preserve((ClientData) interp);
819                 Tcl_GlobalEval(interp, cmdString.string);
820                 Tcl_Release((ClientData) interp);
821                 Tcl_DStringFree(&cmdString);                
822             }
823         } else if (part != 0) {
824             /*
825              * Case 3: in any other part of the scrollbar.  We call
826              * TrackControl with the scrollActionProc which will do
827              * most all the work.
828              */
829             TrackControl(macScrollPtr->sbHandle, where, scrollActionProc);
830             HiliteControl(macScrollPtr->sbHandle, 0);
831         }
832         
833         /*
834          * The TrackControl call will "eat" the ButtonUp event.  We now
835          * generate a ButtonUp event so Tk will unset implicit grabs etc.
836          */
837         GetMouse(&where);
838         XQueryPointer(NULL, None, &window, &window, &x,
839             &y, &dummy, &dummy, &state);
840         window = Tk_WindowId(scrollPtr->tkwin);
841         TkGenerateButtonEvent(x, y, window, state);
842
843         SetGWorld(saveWorld, saveDevice);
844     }
845
846     if (macScrollPtr->sbHandle && (macScrollPtr->macFlags & ALREADY_DEAD)) {
847         DisposeControl(macScrollPtr->sbHandle);
848         macScrollPtr->sbHandle = NULL;
849     }
850     macScrollPtr->macFlags &= ~IN_MODAL_LOOP;
851     Tcl_Release((ClientData)scrollPtr);
852     
853     return TCL_OK;
854 }
855 \f
856 /*
857  *--------------------------------------------------------------
858  *
859  * ScrollbarEventProc --
860  *
861  *        This procedure is invoked by the Tk dispatcher for various
862  *        events on scrollbars.
863  *
864  * Results:
865  *        None.
866  *
867  * Side effects:
868  *        When the window gets deleted, internal structures get
869  *        cleaned up.  When it gets exposed, it is redisplayed.
870  *
871  *--------------------------------------------------------------
872  */
873
874 static void
875 ScrollbarEventProc(
876     ClientData clientData,        /* Information about window. */
877     XEvent *eventPtr)             /* Information about event. */
878 {
879     TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
880     MacScrollbar *macScrollPtr = (MacScrollbar *) clientData;
881
882     if (eventPtr->type == UnmapNotify) {
883         TkMacOSXSetScrollbarGrow((TkWindow *) scrollPtr->tkwin, false);
884     } else if (eventPtr->type == ActivateNotify) {
885         macScrollPtr->macFlags |= ACTIVE;
886         TkScrollbarEventuallyRedraw((ClientData) scrollPtr);
887     } else if (eventPtr->type == DeactivateNotify) {
888         macScrollPtr->macFlags &= ~ACTIVE;
889         TkScrollbarEventuallyRedraw((ClientData) scrollPtr);
890     } else {
891         TkScrollbarEventProc(clientData, eventPtr);
892     }
893 }
894 \f
895 /*
896  *--------------------------------------------------------------
897  *
898  * UpdateControlValues --
899  *
900  *        This procedure updates the Macintosh scrollbar control
901  *        to display the values defined by the Tk scrollbar.
902  *
903  * Results:
904  *        None.
905  *
906  * Side effects:
907  *        The Macintosh control is updated.
908  *
909  *--------------------------------------------------------------
910  */
911
912 static void
913 UpdateControlValues(
914     MacScrollbar *macScrollPtr)                /* Scrollbar data struct. */
915 {
916     TkScrollbar *scrollPtr = (TkScrollbar *) macScrollPtr;
917     Tk_Window tkwin = scrollPtr->tkwin;
918     MacDrawable * macDraw = (MacDrawable *) Tk_WindowId(scrollPtr->tkwin);
919     WindowRef windowRef  = GetControlOwner(macScrollPtr->sbHandle);
920     double middle;
921     SInt32 viewSize;
922     int flushRight = false;
923     int flushBottom = false;
924     Rect contrlRect, portRect;
925     UInt8 contrlHilite;
926
927     /*
928      * We can't use the Macintosh commands SizeControl and MoveControl as these
929      * calls will also cause a redraw which in our case will also cause
930      * flicker.  To avoid this we adjust the control record directly.  The
931      * Draw1Control command appears to just draw where ever the control says to
932      * draw so this seems right.
933      *
934      * NOTE: changing the control record directly may not work when
935      * Apple releases the Copland version of the MacOS (or when hell is cold).
936      */
937      
938     contrlRect.left   = macDraw->xOff + scrollPtr->inset;
939     contrlRect.top    = macDraw->yOff + scrollPtr->inset;
940     contrlRect.right  = macDraw->xOff + Tk_Width(tkwin) - scrollPtr->inset;
941     contrlRect.bottom = macDraw->yOff + Tk_Height(tkwin) - scrollPtr->inset;
942         
943     SetControlBounds(macScrollPtr->sbHandle, &contrlRect );
944     
945     /*
946      * To make Tk applications look more like Macintosh applications without 
947      * requiring additional work by the Tk developer we do some cute tricks.
948      * The first trick plays with the size of the widget to get it to overlap
949      * with the side of the window by one pixel (we don't do this if the placer
950      * is the geometry manager).  The second trick shrinks the scrollbar if it
951      * it covers the area of the grow region ao the scrollbar can also draw
952      * the grow region if need be.
953      */
954     if (!strcmp(macDraw->winPtr->geomMgrPtr->name, "place")) {
955         macScrollPtr->macFlags &= AUTO_ADJUST;
956     } else {
957         macScrollPtr->macFlags |= AUTO_ADJUST;
958     }
959     GetPortBounds ( GetWindowPort ( windowRef ), &portRect );
960     if ( portRect.left == contrlRect.left ) {
961         if (macScrollPtr->macFlags & AUTO_ADJUST) {
962             contrlRect.left--;
963             SetControlBounds ( macScrollPtr->sbHandle, &contrlRect );
964         }
965         if (!(macScrollPtr->macFlags & FLUSH_LEFT)) {
966             macScrollPtr->macFlags |= FLUSH_LEFT;
967             if (scrollPtr->vertical) {
968                 TkpComputeScrollbarGeometry(scrollPtr);
969             }
970         }
971     } else if (macScrollPtr->macFlags & FLUSH_LEFT) {
972         macScrollPtr->macFlags &= ~FLUSH_LEFT;
973         if (scrollPtr->vertical) {
974             TkpComputeScrollbarGeometry(scrollPtr);
975         }
976     }
977     
978     if (portRect.top == contrlRect.top) {
979         if (macScrollPtr->macFlags & AUTO_ADJUST) {
980             contrlRect.top--;
981         }
982         if (!(macScrollPtr->macFlags & FLUSH_TOP)) {
983             macScrollPtr->macFlags |= FLUSH_TOP;
984             if (! scrollPtr->vertical) {
985                 TkpComputeScrollbarGeometry(scrollPtr);
986             }
987         }
988     } else if (macScrollPtr->macFlags & FLUSH_TOP) {
989         macScrollPtr->macFlags &= ~FLUSH_TOP;
990         if (! scrollPtr->vertical) {
991             TkpComputeScrollbarGeometry(scrollPtr);
992         }
993     }
994         
995     if (portRect.right == contrlRect.right) {
996         flushRight = true;
997         if (macScrollPtr->macFlags & AUTO_ADJUST) {
998             contrlRect.right++;
999         }
1000         if (!(macScrollPtr->macFlags & FLUSH_RIGHT)) {
1001             macScrollPtr->macFlags |= FLUSH_RIGHT;
1002             if (scrollPtr->vertical) {
1003                 TkpComputeScrollbarGeometry(scrollPtr);
1004             }
1005         }
1006     } else if (macScrollPtr->macFlags & FLUSH_RIGHT) {
1007         macScrollPtr->macFlags &= ~FLUSH_RIGHT;
1008         if (scrollPtr->vertical) {
1009             TkpComputeScrollbarGeometry(scrollPtr);
1010         }
1011     }
1012         
1013     if (portRect.bottom == contrlRect.bottom) {
1014         flushBottom = true;
1015         if (macScrollPtr->macFlags & AUTO_ADJUST) {
1016             contrlRect.bottom++;
1017         }
1018         if (!(macScrollPtr->macFlags & FLUSH_BOTTOM)) {
1019             macScrollPtr->macFlags |= FLUSH_BOTTOM;
1020             if (! scrollPtr->vertical) {
1021                 TkpComputeScrollbarGeometry(scrollPtr);
1022             }
1023         }
1024     } else if (macScrollPtr->macFlags & FLUSH_BOTTOM) {
1025         macScrollPtr->macFlags &= ~FLUSH_BOTTOM;
1026         if (! scrollPtr->vertical) {
1027             TkpComputeScrollbarGeometry(scrollPtr);
1028         }
1029     }
1030
1031     /*
1032      * If the scrollbar is flush against the bottom right hand corner then
1033      * it may need to draw the grow region for the window so we let the
1034      * wm code know about this scrollbar.  We don't actually draw the grow
1035      * region, however, unless we are currently resizable.
1036      */
1037     macScrollPtr->macFlags &= ~DRAW_GROW;
1038     if (flushBottom && flushRight) {
1039         TkMacOSXSetScrollbarGrow((TkWindow *) tkwin, true);
1040         if (TkMacOSXResizable(macDraw->toplevel->winPtr)) {
1041             if (scrollPtr->vertical) {
1042                 contrlRect.bottom -= 14;
1043             } else {
1044                 contrlRect.right -= 14;
1045             }
1046             macScrollPtr->macFlags |= DRAW_GROW;
1047         }
1048     } else {
1049         TkMacOSXSetScrollbarGrow((TkWindow *) tkwin, false);
1050     }
1051     
1052     /*
1053      * Given the Tk parameters for the fractions of the start and
1054      * end of the thumb, the following calculation determines the
1055      * location for the fixed sized Macintosh thumb.
1056      */
1057     middle = scrollPtr->firstFraction / (scrollPtr->firstFraction +
1058             (1.0 - scrollPtr->lastFraction));
1059     viewSize = (SInt32)((scrollPtr->lastFraction-scrollPtr->firstFraction) 
1060             * MAX_SCROLLBAR_DVALUE);
1061     SetControlViewSize(macScrollPtr->sbHandle,viewSize);
1062     SetControlValue(macScrollPtr->sbHandle, 
1063             (short) (middle * MAX_SCROLLBAR_VALUE) );
1064     contrlHilite=GetControlHilite(macScrollPtr->sbHandle);
1065     if ( contrlHilite == 0 || contrlHilite == 255) {
1066         if (scrollPtr->firstFraction == 0.0 &&
1067                 scrollPtr->lastFraction == 1.0) {
1068             HiliteControl(macScrollPtr->sbHandle,255);
1069         } else {
1070             HiliteControl(macScrollPtr->sbHandle,0);
1071         }
1072     }
1073     if ( !IsControlVisible (macScrollPtr -> sbHandle) ) {
1074         SetControlVisibility(macScrollPtr->sbHandle,TRUE,FALSE);
1075     }
1076 }