/* * tkMacScrollbar.c -- * * This file implements the Macintosh specific portion of the scrollbar * widget. The Macintosh scrollbar may also draw a windows grow * region under certain cases. * * Copyright (c) 1996 by Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tkMacScrlbr.c,v 1.6 2001/11/23 02:06:27 das Exp $ */ #include "tkScrollbar.h" #include "tkMacInt.h" #include #include /* * The following definitions should really be in MacOS * header files. They are included here as this is the only * file that needs the declarations. */ typedef pascal void (*ThumbActionFunc)(void); #if GENERATINGCFM typedef UniversalProcPtr ThumbActionUPP; #else typedef ThumbActionFunc ThumbActionUPP; #endif enum { uppThumbActionProcInfo = kPascalStackBased }; #if GENERATINGCFM #define NewThumbActionProc(userRoutine) \ (ThumbActionUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppThumbActionProcInfo, GetCurrentArchitecture()) #else #define NewThumbActionProc(userRoutine) \ ((ThumbActionUPP) (userRoutine)) #endif /* * Minimum slider length, in pixels (designed to make sure that the slider * is always easy to grab with the mouse). */ #define MIN_SLIDER_LENGTH 5 /* * Declaration of Windows specific scrollbar structure. */ typedef struct MacScrollbar { TkScrollbar info; /* Generic scrollbar info. */ ControlRef sbHandle; /* Handle to the Scrollbar control struct. */ int macFlags; /* Various flags; see below. */ } MacScrollbar; /* * Flag bits for scrollbars on the Mac: * * ALREADY_DEAD: Non-zero means this scrollbar has been * destroyed, but has not been cleaned up. * IN_MODAL_LOOP: Non-zero means this scrollbar is in the middle * of a modal loop. * ACTIVE: Non-zero means this window is currently * active (in the foreground). * FLUSH_TOP: Flush with top of Mac window. * FLUSH_BOTTOM: Flush with bottom of Mac window. * FLUSH_RIGHT: Flush with right of Mac window. * FLUSH_LEFT: Flush with left of Mac window. * SCROLLBAR_GROW: Non-zero means this window draws the grow * region for the toplevel window. * AUTO_ADJUST: Non-zero means we automatically adjust * the size of the widget to align correctly * along a Mac window. * DRAW_GROW: Non-zero means we draw the grow region. */ #define ALREADY_DEAD 1 #define IN_MODAL_LOOP 2 #define ACTIVE 4 #define FLUSH_TOP 8 #define FLUSH_BOTTOM 16 #define FLUSH_RIGHT 32 #define FLUSH_LEFT 64 #define SCROLLBAR_GROW 128 #define AUTO_ADJUST 256 #define DRAW_GROW 512 /* * Globals uses locally in this file. */ static ControlActionUPP scrollActionProc = NULL; /* Pointer to func. */ static ThumbActionUPP thumbActionProc = NULL; /* Pointer to func. */ static TkScrollbar *activeScrollPtr = NULL; /* Non-null when in thumb */ /* proc. */ /* * Forward declarations for procedures defined later in this file: */ static pascal void ScrollbarActionProc _ANSI_ARGS_((ControlRef theControl, ControlPartCode partCode)); static int ScrollbarBindProc _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, XEvent *eventPtr, Tk_Window tkwin, KeySym keySym)); static void ScrollbarEventProc _ANSI_ARGS_(( ClientData clientData, XEvent *eventPtr)); static pascal void ThumbActionProc _ANSI_ARGS_((void)); static void UpdateControlValues _ANSI_ARGS_((MacScrollbar *macScrollPtr)); /* * The class procedure table for the scrollbar widget. Leave the proc fields * initialized to NULL, which should happen automatically because of the scope * at which the variable is declared. */ Tk_ClassProcs tkpScrollbarProcs = { sizeof(Tk_ClassProcs) /* size */ }; /* *---------------------------------------------------------------------- * * TkpCreateScrollbar -- * * Allocate a new TkScrollbar structure. * * Results: * Returns a newly allocated TkScrollbar structure. * * Side effects: * None. * *---------------------------------------------------------------------- */ TkScrollbar * TkpCreateScrollbar( Tk_Window tkwin) /* New Tk Window. */ { MacScrollbar * macScrollPtr; TkWindow *winPtr = (TkWindow *)tkwin; if (scrollActionProc == NULL) { scrollActionProc = NewControlActionProc(ScrollbarActionProc); thumbActionProc = NewThumbActionProc(ThumbActionProc); } macScrollPtr = (MacScrollbar *) ckalloc(sizeof(MacScrollbar)); macScrollPtr->sbHandle = NULL; macScrollPtr->macFlags = 0; Tk_CreateEventHandler(tkwin, ActivateMask|ExposureMask| StructureNotifyMask|FocusChangeMask, ScrollbarEventProc, (ClientData) macScrollPtr); if (!Tcl_GetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL)) { Tcl_SetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL, (ClientData)1); TkCreateBindingProcedure(winPtr->mainPtr->interp, winPtr->mainPtr->bindingTable, (ClientData)Tk_GetUid("Scrollbar"), "", ScrollbarBindProc, NULL, NULL); } return (TkScrollbar *) macScrollPtr; } /* *-------------------------------------------------------------- * * TkpDisplayScrollbar -- * * This procedure redraws the contents of a scrollbar window. * It is invoked as a do-when-idle handler, so it only runs * when there's nothing else for the application to do. * * Results: * None. * * Side effects: * Information appears on the screen. * *-------------------------------------------------------------- */ void TkpDisplayScrollbar( ClientData clientData) /* Information about window. */ { register TkScrollbar *scrollPtr = (TkScrollbar *) clientData; register MacScrollbar *macScrollPtr = (MacScrollbar *) clientData; register Tk_Window tkwin = scrollPtr->tkwin; MacDrawable *macDraw; CGrafPtr saveWorld; GDHandle saveDevice; GWorldPtr destPort; WindowRef windowRef; if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { goto done; } /* * Draw the focus or any 3D relief we may have. */ if (scrollPtr->highlightWidth != 0) { GC fgGC, bgGC; bgGC = Tk_GCForColor(scrollPtr->highlightBgColorPtr, Tk_WindowId(tkwin)); if (scrollPtr->flags & GOT_FOCUS) { fgGC = Tk_GCForColor(scrollPtr->highlightColorPtr, Tk_WindowId(tkwin)); TkpDrawHighlightBorder(tkwin, fgGC, bgGC, scrollPtr->highlightWidth, Tk_WindowId(tkwin)); } else { TkpDrawHighlightBorder(tkwin, bgGC, bgGC, scrollPtr->highlightWidth, Tk_WindowId(tkwin)); } } Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), scrollPtr->bgBorder, scrollPtr->highlightWidth, scrollPtr->highlightWidth, Tk_Width(tkwin) - 2*scrollPtr->highlightWidth, Tk_Height(tkwin) - 2*scrollPtr->highlightWidth, scrollPtr->borderWidth, scrollPtr->relief); /* * Set up port for drawing Macintosh control. */ macDraw = (MacDrawable *) Tk_WindowId(tkwin); destPort = TkMacGetDrawablePort(Tk_WindowId(tkwin)); GetGWorld(&saveWorld, &saveDevice); SetGWorld(destPort, NULL); TkMacSetUpClippingRgn(Tk_WindowId(tkwin)); if (macScrollPtr->sbHandle == NULL) { Rect r; WindowRef frontNonFloating; r.left = r.top = 0; r.right = r.bottom = 1; macScrollPtr->sbHandle = NewControl((WindowRef) destPort, &r, "\p", false, (short) 500, 0, 1000, scrollBarProc, (SInt32) scrollPtr); /* * If we are foremost than make us active. */ if (TkMacHaveAppearance() >= 0x110) { frontNonFloating = FrontNonFloatingWindow(); } else { frontNonFloating = FrontWindow(); } if ((WindowPtr) destPort == FrontWindow() || TkpIsWindowFloating((WindowPtr) destPort)) { macScrollPtr->macFlags |= ACTIVE; } } /* * Update the control values before we draw. */ windowRef = (**macScrollPtr->sbHandle).contrlOwner; UpdateControlValues(macScrollPtr); if (macScrollPtr->macFlags & ACTIVE) { Draw1Control(macScrollPtr->sbHandle); if (macScrollPtr->macFlags & DRAW_GROW) { DrawGrowIcon(windowRef); } } else { (**macScrollPtr->sbHandle).contrlHilite = 255; Draw1Control(macScrollPtr->sbHandle); if (macScrollPtr->macFlags & DRAW_GROW) { DrawGrowIcon(windowRef); Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), scrollPtr->bgBorder, Tk_Width(tkwin) - 13, Tk_Height(tkwin) - 13, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); } } SetGWorld(saveWorld, saveDevice); done: scrollPtr->flags &= ~REDRAW_PENDING; } /* *---------------------------------------------------------------------- * * TkpConfigureScrollbar -- * * This procedure is called after the generic code has finished * processing configuration options, in order to configure * platform specific options. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkpConfigureScrollbar(scrollPtr) register TkScrollbar *scrollPtr; /* Information about widget; may or * may not already have values for * some fields. */ { } /* *---------------------------------------------------------------------- * * TkpComputeScrollbarGeometry -- * * After changes in a scrollbar's size or configuration, this * procedure recomputes various geometry information used in * displaying the scrollbar. * * Results: * None. * * Side effects: * The scrollbar will be displayed differently. * *---------------------------------------------------------------------- */ void TkpComputeScrollbarGeometry( register TkScrollbar *scrollPtr) /* Scrollbar whose geometry may * have changed. */ { MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr; int width, fieldLength, adjust = 0; if (scrollPtr->highlightWidth < 0) { scrollPtr->highlightWidth = 0; } scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth; width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin) : Tk_Height(scrollPtr->tkwin); scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1; fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin) : Tk_Width(scrollPtr->tkwin)) - 2*(scrollPtr->arrowLength + scrollPtr->inset); if (fieldLength < 0) { fieldLength = 0; } scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction; scrollPtr->sliderLast = fieldLength*scrollPtr->lastFraction; /* * Adjust the slider so that some piece of it is always * displayed in the scrollbar and so that it has at least * a minimal width (so it can be grabbed with the mouse). */ if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) { scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth; } if (scrollPtr->sliderFirst < 0) { scrollPtr->sliderFirst = 0; } if (scrollPtr->sliderLast < (scrollPtr->sliderFirst + MIN_SLIDER_LENGTH)) { scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH; } if (scrollPtr->sliderLast > fieldLength) { scrollPtr->sliderLast = fieldLength; } scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset; scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset; /* * Register the desired geometry for the window (leave enough space * for the two arrows plus a minimum-size slider, plus border around * the whole window, if any). Then arrange for the window to be * redisplayed. */ if (scrollPtr->vertical) { if ((macScrollPtr->macFlags & AUTO_ADJUST) && (macScrollPtr->macFlags & (FLUSH_RIGHT|FLUSH_LEFT))) { adjust--; } Tk_GeometryRequest(scrollPtr->tkwin, scrollPtr->width + 2*scrollPtr->inset + adjust, 2*(scrollPtr->arrowLength + scrollPtr->borderWidth + scrollPtr->inset)); } else { if ((macScrollPtr->macFlags & AUTO_ADJUST) && (macScrollPtr->macFlags & (FLUSH_TOP|FLUSH_BOTTOM))) { adjust--; } Tk_GeometryRequest(scrollPtr->tkwin, 2*(scrollPtr->arrowLength + scrollPtr->borderWidth + scrollPtr->inset), scrollPtr->width + 2*scrollPtr->inset + adjust); } Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset); } /* *---------------------------------------------------------------------- * * TkpDestroyScrollbar -- * * Free data structures associated with the scrollbar control. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkpDestroyScrollbar( TkScrollbar *scrollPtr) /* Scrollbar to destroy. */ { MacScrollbar *macScrollPtr = (MacScrollbar *)scrollPtr; if (macScrollPtr->sbHandle != NULL) { if (!(macScrollPtr->macFlags & IN_MODAL_LOOP)) { DisposeControl(macScrollPtr->sbHandle); macScrollPtr->sbHandle = NULL; } } macScrollPtr->macFlags |= ALREADY_DEAD; } /* *-------------------------------------------------------------- * * TkpScrollbarPosition -- * * Determine the scrollbar element corresponding to a * given position. * * Results: * One of TOP_ARROW, TOP_GAP, etc., indicating which element * of the scrollbar covers the position given by (x, y). If * (x,y) is outside the scrollbar entirely, then OUTSIDE is * returned. * * Side effects: * None. * *-------------------------------------------------------------- */ int TkpScrollbarPosition( TkScrollbar *scrollPtr, /* Scrollbar widget record. */ int x, int y) /* Coordinates within scrollPtr's * window. */ { MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr; GWorldPtr destPort; int length, width, tmp, inactive = false; ControlPartCode part; Point where; Rect bounds; if (scrollPtr->vertical) { length = Tk_Height(scrollPtr->tkwin); width = Tk_Width(scrollPtr->tkwin); } else { tmp = x; x = y; y = tmp; length = Tk_Width(scrollPtr->tkwin); width = Tk_Height(scrollPtr->tkwin); } if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset)) || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) { return OUTSIDE; } /* * All of the calculations in this procedure mirror those in * DisplayScrollbar. Be sure to keep the two consistent. On the * Macintosh we use the OS call TestControl to do this mapping. * For TestControl to work, the scrollbar must be active and must * be in the current port. */ destPort = TkMacGetDrawablePort(Tk_WindowId(scrollPtr->tkwin)); SetGWorld(destPort, NULL); UpdateControlValues(macScrollPtr); if ((**macScrollPtr->sbHandle).contrlHilite == 255) { inactive = true; (**macScrollPtr->sbHandle).contrlHilite = 0; } TkMacWinBounds((TkWindow *) scrollPtr->tkwin, &bounds); where.h = x + bounds.left; where.v = y + bounds.top; part = TestControl(((MacScrollbar *) scrollPtr)->sbHandle, where); if (inactive) { (**macScrollPtr->sbHandle).contrlHilite = 255; } switch (part) { case kControlUpButtonPart: return TOP_ARROW; case kControlPageUpPart: return TOP_GAP; case kControlIndicatorPart: return SLIDER; case kControlPageDownPart: return BOTTOM_GAP; case kControlDownButtonPart: return BOTTOM_ARROW; default: return OUTSIDE; } } /* *-------------------------------------------------------------- * * ThumbActionProc -- * * Callback procedure used by the Macintosh toolbox call * TrackControl. This call is used to track the thumb of * the scrollbar. Unlike the ScrollbarActionProc function * this function is called once and basically takes over * tracking the scrollbar from the control. This is done * to avoid conflicts with what the control plans to draw. * * Results: * None. * * Side effects: * May change the display. * *-------------------------------------------------------------- */ static pascal void ThumbActionProc() { register TkScrollbar *scrollPtr = activeScrollPtr; register MacScrollbar *macScrollPtr = (MacScrollbar *) activeScrollPtr; Tcl_DString cmdString; Rect nullRect = {0,0,0,0}; int origValue, trackBarPin; double thumbWidth, newFirstFraction, trackBarSize; char vauleString[40]; Point currentPoint = { 0, 0 }; Point lastPoint = { 0, 0 }; Rect trackRect; Tcl_Interp *interp; if (scrollPtr == NULL) { return; } Tcl_DStringInit(&cmdString); /* * First compute values that will remain constant during the tracking * of the thumb. The variable trackBarSize is the length of the scrollbar * minus the 2 arrows and half the width of the thumb on both sides * (3 * arrowLength). The variable trackBarPin is the lower starting point * of the drag region. * * Note: the arrowLength is equal to the thumb width of a Mac scrollbar. */ origValue = GetControlValue(macScrollPtr->sbHandle); trackRect = (**macScrollPtr->sbHandle).contrlRect; if (scrollPtr->vertical == true) { trackBarSize = (double) (trackRect.bottom - trackRect.top - (scrollPtr->arrowLength * 3)); trackBarPin = trackRect.top + scrollPtr->arrowLength + (scrollPtr->arrowLength / 2); InsetRect(&trackRect, -25, -113); } else { trackBarSize = (double) (trackRect.right - trackRect.left - (scrollPtr->arrowLength * 3)); trackBarPin = trackRect.left + scrollPtr->arrowLength + (scrollPtr->arrowLength / 2); InsetRect(&trackRect, -113, -25); } /* * Track the mouse while the button is held down. If the mouse is moved, * we calculate the value that should be passed to the "command" part of * the scrollbar. */ while (StillDown()) { GetMouse(¤tPoint); if (EqualPt(currentPoint, lastPoint)) { continue; } lastPoint = currentPoint; /* * Calculating this value is a little tricky. We need to calculate a * value for where the thumb would be in a Motif widget (variable * thumb). This value is what the "command" expects and is what will * be resent to the scrollbar to update its value. */ thumbWidth = scrollPtr->lastFraction - scrollPtr->firstFraction; if (PtInRect(currentPoint, &trackRect)) { if (scrollPtr->vertical == true) { newFirstFraction = (1.0 - thumbWidth) * ((double) (currentPoint.v - trackBarPin) / trackBarSize); } else { newFirstFraction = (1.0 - thumbWidth) * ((double) (currentPoint.h - trackBarPin) / trackBarSize); } } else { newFirstFraction = ((double) origValue / 1000.0) * (1.0 - thumbWidth); } sprintf(vauleString, "%g", newFirstFraction); Tcl_DStringSetLength(&cmdString, 0); Tcl_DStringAppend(&cmdString, scrollPtr->command, scrollPtr->commandSize); Tcl_DStringAppendElement(&cmdString, "moveto"); Tcl_DStringAppendElement(&cmdString, vauleString); interp = scrollPtr->interp; Tcl_Preserve((ClientData) interp); Tcl_GlobalEval(interp, cmdString.string); Tcl_Release((ClientData) interp); Tcl_DStringSetLength(&cmdString, 0); Tcl_DStringAppend(&cmdString, "update idletasks", strlen("update idletasks")); Tcl_Preserve((ClientData) interp); Tcl_GlobalEval(interp, cmdString.string); Tcl_Release((ClientData) interp); } /* * This next bit of code is a bit of a hack - but needed. The problem is * that the control wants to draw the drag outline if the control value * changes during the drag (which it does). What we do here is change the * clip region to hide this drawing from the user. */ ClipRect(&nullRect); Tcl_DStringFree(&cmdString); return; } /* *-------------------------------------------------------------- * * ScrollbarActionProc -- * * Callback procedure used by the Macintosh toolbox call * TrackControl. This call will update the display while * the scrollbar is being manipulated by the user. * * Results: * None. * * Side effects: * May change the display. * *-------------------------------------------------------------- */ static pascal void ScrollbarActionProc( ControlRef theControl, /* Handle to scrollbat control */ ControlPartCode partCode) /* Part of scrollbar that was "hit" */ { register TkScrollbar *scrollPtr = (TkScrollbar *) GetControlReference(theControl); Tcl_DString cmdString; Tcl_DStringInit(&cmdString); Tcl_DStringAppend(&cmdString, scrollPtr->command, scrollPtr->commandSize); if (partCode == kControlUpButtonPart || partCode == kControlDownButtonPart) { Tcl_DStringAppendElement(&cmdString, "scroll"); Tcl_DStringAppendElement(&cmdString, (partCode == kControlUpButtonPart ) ? "-1" : "1"); Tcl_DStringAppendElement(&cmdString, "unit"); } else if (partCode == kControlPageUpPart || partCode == kControlPageDownPart) { Tcl_DStringAppendElement(&cmdString, "scroll"); Tcl_DStringAppendElement(&cmdString, (partCode == kControlPageUpPart ) ? "-1" : "1"); Tcl_DStringAppendElement(&cmdString, "page"); } Tcl_Preserve((ClientData) scrollPtr->interp); Tcl_DStringAppend(&cmdString, "; update idletasks", strlen("; update idletasks")); Tcl_GlobalEval(scrollPtr->interp, cmdString.string); Tcl_Release((ClientData) scrollPtr->interp); Tcl_DStringFree(&cmdString); } /* *-------------------------------------------------------------- * * ScrollbarBindProc -- * * This procedure is invoked when the default * binding on the Scrollbar bind tag fires. * * Results: * None. * * Side effects: * The event enters a modal loop. * *-------------------------------------------------------------- */ static int ScrollbarBindProc( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Interp with binding. */ XEvent *eventPtr, /* X event that triggered binding. */ Tk_Window tkwin, /* Target window for event. */ KeySym keySym) /* The KeySym if a key event. */ { TkWindow *winPtr = (TkWindow*)tkwin; TkScrollbar *scrollPtr = (TkScrollbar *) winPtr->instanceData; MacScrollbar *macScrollPtr = (MacScrollbar *) winPtr->instanceData; Tcl_Preserve((ClientData)scrollPtr); macScrollPtr->macFlags |= IN_MODAL_LOOP; if (eventPtr->type == ButtonPress) { Point where; Rect bounds; int part, x, y, dummy; unsigned int state; CGrafPtr saveWorld; GDHandle saveDevice; GWorldPtr destPort; Window window; /* * To call Macintosh control routines we must have the port * set to the window containing the control. We will then test * which part of the control was hit and act accordingly. */ destPort = TkMacGetDrawablePort(Tk_WindowId(scrollPtr->tkwin)); GetGWorld(&saveWorld, &saveDevice); SetGWorld(destPort, NULL); TkMacSetUpClippingRgn(Tk_WindowId(scrollPtr->tkwin)); TkMacWinBounds((TkWindow *) scrollPtr->tkwin, &bounds); where.h = eventPtr->xbutton.x + bounds.left; where.v = eventPtr->xbutton.y + bounds.top; part = TestControl(macScrollPtr->sbHandle, where); if (part == kControlIndicatorPart && scrollPtr->jump == false) { /* * Case 1: In thumb, no jump scrolling. Call track control * with the thumb action proc which will do most of the work. * Set the global activeScrollPtr to the current control * so the callback may have access to it. */ activeScrollPtr = scrollPtr; part = TrackControl(macScrollPtr->sbHandle, where, (ControlActionUPP) thumbActionProc); activeScrollPtr = NULL; } else if (part == kControlIndicatorPart) { /* * Case 2: in thumb with jump scrolling. Call TrackControl * with a NULL action proc. Use the new value of the control * to set update the control. */ part = TrackControl(macScrollPtr->sbHandle, where, NULL); if (part == kControlIndicatorPart) { double newFirstFraction, thumbWidth; Tcl_DString cmdString; char vauleString[TCL_DOUBLE_SPACE]; /* * The following calculation takes the new control * value and maps it to what Tk needs for its variable * thumb size representation. */ thumbWidth = scrollPtr->lastFraction - scrollPtr->firstFraction; newFirstFraction = (1.0 - thumbWidth) * ((double) GetControlValue(macScrollPtr->sbHandle) / 1000.0); sprintf(vauleString, "%g", newFirstFraction); Tcl_DStringInit(&cmdString); Tcl_DStringAppend(&cmdString, scrollPtr->command, strlen(scrollPtr->command)); Tcl_DStringAppendElement(&cmdString, "moveto"); Tcl_DStringAppendElement(&cmdString, vauleString); Tcl_DStringAppend(&cmdString, "; update idletasks", strlen("; update idletasks")); interp = scrollPtr->interp; Tcl_Preserve((ClientData) interp); Tcl_GlobalEval(interp, cmdString.string); Tcl_Release((ClientData) interp); Tcl_DStringFree(&cmdString); } } else if (part != 0) { /* * Case 3: in any other part of the scrollbar. We call * TrackControl with the scrollActionProc which will do * most all the work. */ TrackControl(macScrollPtr->sbHandle, where, scrollActionProc); HiliteControl(macScrollPtr->sbHandle, 0); } /* * The TrackControl call will "eat" the ButtonUp event. We now * generate a ButtonUp event so Tk will unset implicit grabs etc. */ GetMouse(&where); XQueryPointer(NULL, None, &window, &window, &x, &y, &dummy, &dummy, &state); window = Tk_WindowId(scrollPtr->tkwin); TkGenerateButtonEvent(x, y, window, state); SetGWorld(saveWorld, saveDevice); } if (macScrollPtr->sbHandle && (macScrollPtr->macFlags & ALREADY_DEAD)) { DisposeControl(macScrollPtr->sbHandle); macScrollPtr->sbHandle = NULL; } macScrollPtr->macFlags &= ~IN_MODAL_LOOP; Tcl_Release((ClientData)scrollPtr); return TCL_OK; } /* *-------------------------------------------------------------- * * ScrollbarEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on scrollbars. * * Results: * None. * * Side effects: * When the window gets deleted, internal structures get * cleaned up. When it gets exposed, it is redisplayed. * *-------------------------------------------------------------- */ static void ScrollbarEventProc( ClientData clientData, /* Information about window. */ XEvent *eventPtr) /* Information about event. */ { TkScrollbar *scrollPtr = (TkScrollbar *) clientData; MacScrollbar *macScrollPtr = (MacScrollbar *) clientData; if (eventPtr->type == UnmapNotify) { TkMacSetScrollbarGrow((TkWindow *) scrollPtr->tkwin, false); } else if (eventPtr->type == ActivateNotify) { macScrollPtr->macFlags |= ACTIVE; TkScrollbarEventuallyRedraw((ClientData) scrollPtr); } else if (eventPtr->type == DeactivateNotify) { macScrollPtr->macFlags &= ~ACTIVE; TkScrollbarEventuallyRedraw((ClientData) scrollPtr); } else { TkScrollbarEventProc(clientData, eventPtr); } } /* *-------------------------------------------------------------- * * UpdateControlValues -- * * This procedure updates the Macintosh scrollbar control * to display the values defined by the Tk scrollbar. * * Results: * None. * * Side effects: * The Macintosh control is updated. * *-------------------------------------------------------------- */ static void UpdateControlValues( MacScrollbar *macScrollPtr) /* Scrollbar data struct. */ { TkScrollbar *scrollPtr = (TkScrollbar *) macScrollPtr; Tk_Window tkwin = scrollPtr->tkwin; MacDrawable * macDraw = (MacDrawable *) Tk_WindowId(scrollPtr->tkwin); WindowRef windowRef = (**macScrollPtr->sbHandle).contrlOwner; double middle; int drawGrowRgn = false; int flushRight = false; int flushBottom = false; /* * We can't use the Macintosh commands SizeControl and MoveControl as these * calls will also cause a redraw which in our case will also cause * flicker. To avoid this we adjust the control record directly. The * Draw1Control command appears to just draw where ever the control says to * draw so this seems right. * * NOTE: changing the control record directly may not work when * Apple releases the Copland version of the MacOS (or when hell is cold). */ (**macScrollPtr->sbHandle).contrlRect.left = macDraw->xOff + scrollPtr->inset; (**macScrollPtr->sbHandle).contrlRect.top = macDraw->yOff + scrollPtr->inset; (**macScrollPtr->sbHandle).contrlRect.right = macDraw->xOff + Tk_Width(tkwin) - scrollPtr->inset; (**macScrollPtr->sbHandle).contrlRect.bottom = macDraw->yOff + Tk_Height(tkwin) - scrollPtr->inset; /* * To make Tk applications look more like Macintosh applications without * requiring additional work by the Tk developer we do some cute tricks. * The first trick plays with the size of the widget to get it to overlap * with the side of the window by one pixel (we don't do this if the placer * is the geometry manager). The second trick shrinks the scrollbar if it * it covers the area of the grow region ao the scrollbar can also draw * the grow region if need be. */ if (!strcmp(macDraw->winPtr->geomMgrPtr->name, "place")) { macScrollPtr->macFlags &= AUTO_ADJUST; } else { macScrollPtr->macFlags |= AUTO_ADJUST; } /* TODO: use accessor function!!! */ if (windowRef->portRect.left == (**macScrollPtr->sbHandle).contrlRect.left) { if (macScrollPtr->macFlags & AUTO_ADJUST) { (**macScrollPtr->sbHandle).contrlRect.left--; } if (!(macScrollPtr->macFlags & FLUSH_LEFT)) { macScrollPtr->macFlags |= FLUSH_LEFT; if (scrollPtr->vertical) { TkpComputeScrollbarGeometry(scrollPtr); } } } else if (macScrollPtr->macFlags & FLUSH_LEFT) { macScrollPtr->macFlags &= ~FLUSH_LEFT; if (scrollPtr->vertical) { TkpComputeScrollbarGeometry(scrollPtr); } } if (windowRef->portRect.top == (**macScrollPtr->sbHandle).contrlRect.top) { if (macScrollPtr->macFlags & AUTO_ADJUST) { (**macScrollPtr->sbHandle).contrlRect.top--; } if (!(macScrollPtr->macFlags & FLUSH_TOP)) { macScrollPtr->macFlags |= FLUSH_TOP; if (! scrollPtr->vertical) { TkpComputeScrollbarGeometry(scrollPtr); } } } else if (macScrollPtr->macFlags & FLUSH_TOP) { macScrollPtr->macFlags &= ~FLUSH_TOP; if (! scrollPtr->vertical) { TkpComputeScrollbarGeometry(scrollPtr); } } if (windowRef->portRect.right == (**macScrollPtr->sbHandle).contrlRect.right) { flushRight = true; if (macScrollPtr->macFlags & AUTO_ADJUST) { (**macScrollPtr->sbHandle).contrlRect.right++; } if (!(macScrollPtr->macFlags & FLUSH_RIGHT)) { macScrollPtr->macFlags |= FLUSH_RIGHT; if (scrollPtr->vertical) { TkpComputeScrollbarGeometry(scrollPtr); } } } else if (macScrollPtr->macFlags & FLUSH_RIGHT) { macScrollPtr->macFlags &= ~FLUSH_RIGHT; if (scrollPtr->vertical) { TkpComputeScrollbarGeometry(scrollPtr); } } if (windowRef->portRect.bottom == (**macScrollPtr->sbHandle).contrlRect.bottom) { flushBottom = true; if (macScrollPtr->macFlags & AUTO_ADJUST) { (**macScrollPtr->sbHandle).contrlRect.bottom++; } if (!(macScrollPtr->macFlags & FLUSH_BOTTOM)) { macScrollPtr->macFlags |= FLUSH_BOTTOM; if (! scrollPtr->vertical) { TkpComputeScrollbarGeometry(scrollPtr); } } } else if (macScrollPtr->macFlags & FLUSH_BOTTOM) { macScrollPtr->macFlags &= ~FLUSH_BOTTOM; if (! scrollPtr->vertical) { TkpComputeScrollbarGeometry(scrollPtr); } } /* * If the scrollbar is flush against the bottom right hand coner then * it may need to draw the grow region for the window so we let the * wm code know about this scrollbar. We don't actually draw the grow * region, however, unless we are currently resizable. */ macScrollPtr->macFlags &= ~DRAW_GROW; if (flushBottom && flushRight) { TkMacSetScrollbarGrow((TkWindow *) tkwin, true); if (TkMacResizable(macDraw->toplevel->winPtr)) { if (scrollPtr->vertical) { (**macScrollPtr->sbHandle).contrlRect.bottom -= 14; } else { (**macScrollPtr->sbHandle).contrlRect.right -= 14; } macScrollPtr->macFlags |= DRAW_GROW; } } else { TkMacSetScrollbarGrow((TkWindow *) tkwin, false); } /* * Given the Tk parameters for the fractions of the start and * end of the thumb, the following calculation determines the * location for the fixed sized Macintosh thumb. */ middle = scrollPtr->firstFraction / (scrollPtr->firstFraction + (1.0 - scrollPtr->lastFraction)); (**macScrollPtr->sbHandle).contrlValue = (short) (middle * 1000); if ((**macScrollPtr->sbHandle).contrlHilite == 0 || (**macScrollPtr->sbHandle).contrlHilite == 255) { if (scrollPtr->firstFraction == 0.0 && scrollPtr->lastFraction == 1.0) { (**macScrollPtr->sbHandle).contrlHilite = 255; } else { (**macScrollPtr->sbHandle).contrlHilite = 0; } } if ((**macScrollPtr->sbHandle).contrlVis != 255) { (**macScrollPtr->sbHandle).contrlVis = 255; } }