tkMacOSXScrlbr.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:32k
源码类别:

通讯编程

开发平台:

Visual 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.  * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net>
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * RCS: @(#) $Id: tkMacOSXScrlbr.c,v 1.5.2.15 2007/06/29 03:22:02 das Exp $
  16.  */
  17. #include "tkMacOSXPrivate.h"
  18. #include "tkScrollbar.h"
  19. #include "tkMacOSXDebug.h"
  20. #define MIN_SCROLLBAR_VALUE 0
  21. #define SCROLLBAR_SCALING_VALUE ((double)(LONG_MAX>>1))
  22. /*
  23.  * Declaration of Mac specific scrollbar structure.
  24.  */
  25. typedef struct MacScrollbar {
  26.     TkScrollbar info; /* Generic scrollbar info */
  27.     ControlRef sbHandle; /* Scrollbar control */
  28.     int macFlags; /* Various flags; see below */
  29.     Rect eraseRect; /* Rect to erase before drawing control */
  30. } MacScrollbar;
  31. /*
  32.  * Flag bits for scrollbars on the Mac:
  33.  *
  34.  * ALREADY_DEAD: Non-zero means this scrollbar has been
  35.  * destroyed, but has not been cleaned up.
  36.  * IN_MODAL_LOOP: Non-zero means this scrollbar is in the middle
  37.  * of a modal loop.
  38.  * ACTIVE: Non-zero means this window is currently
  39.  * active (in the foreground).
  40.  */
  41. #define ALREADY_DEAD 1
  42. #define IN_MODAL_LOOP 2
  43. #define ACTIVE 4
  44. /*
  45.  * Globals uses locally in this file.
  46.  */
  47. static ControlActionUPP scrollActionProc = NULL; /* Pointer to func. */
  48. static ControlActionUPP thumbActionProc = NULL; /* Pointer to func. */
  49. static Point mouseDownPoint; /* Used to store the coordinates where the  */
  50. /* mouse was first pressed to begin     */
  51. /* dragging the thumb, because     */
  52. /* ThumbActionProc can't take any args.     */
  53. typedef struct ScrollbarMetrics {
  54.     SInt32 width, minHeight, minThumbHeight;
  55.     short topArrowHeight, bottomArrowHeight;
  56.     ControlSize size;
  57. } ScrollbarMetrics;
  58. static ScrollbarMetrics metrics[2] = {
  59.     {15, 54, 26, 14, 14, kControlSizeNormal}, /* kThemeScrollBarMedium */
  60.     {11, 40, 20, 10, 10, kControlSizeSmall},  /* kThemeScrollBarSmall  */
  61. };
  62. /*
  63.  * This variable holds the default width for a scrollbar in string form for
  64.  * use in a Tk_ConfigSpec.
  65.  */
  66. static char defWidth[TCL_INTEGER_SPACE];
  67. /*
  68.  * Forward declarations for procedures defined later in this file:
  69.  */
  70. static pascal void ScrollbarActionProc(ControlRef theControl,
  71. ControlPartCode partCode);
  72. static pascal void ThumbActionProc(ControlRef theControl,
  73. ControlPartCode partCode);
  74. static int ScrollbarBindProc(ClientData clientData, Tcl_Interp *interp,
  75. XEvent *eventPtr, Tk_Window tkwin, KeySym keySym);
  76. static void ScrollbarEventProc(ClientData clientData, XEvent *eventPtr);
  77. static void UpdateControlValues(MacScrollbar *macScrollPtr);
  78. /*
  79.  * The class procedure table for the scrollbar widget. Leave the proc fields
  80.  * initialized to NULL, which should happen automatically because of the scope
  81.  * at which the variable is declared.
  82.  */
  83. Tk_ClassProcs tkpScrollbarProcs = {
  84.     sizeof(Tk_ClassProcs) /* size */
  85. };
  86. /*
  87.  *----------------------------------------------------------------------
  88.  *
  89.  * TkMacOSXInitScrollbarMetrics --
  90.  *
  91.  * This function initializes the current system metrics for a
  92.  * scrollbar.
  93.  *
  94.  * Results:
  95.  * None.
  96.  *
  97.  * Side effects:
  98.  * Updates the geometry cache info for all scrollbars.
  99.  *
  100.  *----------------------------------------------------------------------
  101.  */
  102. void
  103. TkMacOSXInitScrollbarMetrics(void)
  104. {
  105.     const short height = 100, width = 50;
  106.     ThemeTrackDrawInfo info = {0, {0, 0, height, width}, 0, 1, 0, 0,
  107.     kThemeTrackShowThumb, kThemeTrackActive, 0, {{1, 0}}};
  108.     Rect bounds;
  109.     Tk_ConfigSpec *specPtr;
  110.     ChkErr(GetThemeMetric, kThemeMetricScrollBarWidth, &metrics[0].width);
  111.     ChkErr(GetThemeMetric, kThemeMetricScrollBarMinThumbHeight,
  112.     &metrics[0].minThumbHeight);
  113.     info.kind = kThemeScrollBarMedium;
  114.     ChkErr(GetThemeTrackDragRect, &info, &bounds);
  115.     metrics[0].topArrowHeight = bounds.top;
  116.     metrics[0].bottomArrowHeight = height - bounds.bottom;
  117.     metrics[0].minHeight = metrics[0].minThumbHeight +
  118.     metrics[0].topArrowHeight + metrics[0].bottomArrowHeight;
  119.     ChkErr(GetThemeMetric, kThemeMetricSmallScrollBarWidth, &metrics[1].width);
  120.     ChkErr(GetThemeMetric, kThemeMetricSmallScrollBarMinThumbHeight,
  121.     &metrics[1].minThumbHeight);
  122.     info.kind = kThemeScrollBarSmall;
  123.     ChkErr(GetThemeTrackDragRect, &info, &bounds);
  124.     metrics[1].topArrowHeight = bounds.top;
  125.     metrics[1].bottomArrowHeight = height - bounds.bottom;
  126.     metrics[1].minHeight = metrics[1].minThumbHeight +
  127.     metrics[1].topArrowHeight + metrics[1].bottomArrowHeight;
  128.     sprintf(defWidth, "%ld", metrics[0].width);
  129.     for (specPtr = tkpScrollbarConfigSpecs; specPtr->type != TK_CONFIG_END;
  130.     specPtr++) {
  131. if (specPtr->offset == Tk_Offset(TkScrollbar, width)) {
  132.     specPtr->defValue = defWidth;
  133. }
  134.     }
  135. }
  136. /*
  137.  *----------------------------------------------------------------------
  138.  *
  139.  * TkpCreateScrollbar --
  140.  *
  141.  * Allocate a new TkScrollbar structure.
  142.  *
  143.  * Results:
  144.  * Returns a newly allocated TkScrollbar structure.
  145.  *
  146.  * Side effects:
  147.  * None.
  148.  *
  149.  *----------------------------------------------------------------------
  150.  */
  151. TkScrollbar *
  152. TkpCreateScrollbar(
  153.     Tk_Window tkwin) /* New Tk Window. */
  154. {
  155.     static int initialized = 0;
  156.     MacScrollbar * macScrollPtr;
  157.     TkWindow *winPtr = (TkWindow *)tkwin;
  158.     if (scrollActionProc == NULL) {
  159. scrollActionProc = NewControlActionUPP(ScrollbarActionProc);
  160. thumbActionProc =  NewControlActionUPP(ThumbActionProc);
  161.     }
  162.     if (!initialized) {
  163. TkMacOSXInitScrollbarMetrics();
  164. initialized = 1;
  165.     }
  166.     macScrollPtr = (MacScrollbar *) ckalloc(sizeof(MacScrollbar));
  167.     macScrollPtr->sbHandle = NULL;
  168.     macScrollPtr->macFlags = 0;
  169.     SetRect(&macScrollPtr->eraseRect, 0, 0, 0, 0);
  170.     Tk_CreateEventHandler(tkwin, ActivateMask|ExposureMask|
  171. StructureNotifyMask|FocusChangeMask,
  172. ScrollbarEventProc, (ClientData) macScrollPtr);
  173.     if (!Tcl_GetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL)) {
  174. Tcl_SetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL,
  175. (ClientData)1);
  176. TkCreateBindingProcedure(winPtr->mainPtr->interp,
  177.     winPtr->mainPtr->bindingTable,
  178.     (ClientData)Tk_GetUid("Scrollbar"), "<ButtonPress>",
  179.     ScrollbarBindProc, NULL, NULL);
  180.     }
  181.     return (TkScrollbar *) macScrollPtr;
  182. }
  183. /*
  184.  *--------------------------------------------------------------
  185.  *
  186.  * TkpDisplayScrollbar --
  187.  *
  188.  * This procedure redraws the contents of a scrollbar window.
  189.  * It is invoked as a do-when-idle handler, so it only runs
  190.  * when there's nothing else for the application to do.
  191.  *
  192.  * Results:
  193.  * None.
  194.  *
  195.  * Side effects:
  196.  * Information appears on the screen.
  197.  *
  198.  *--------------------------------------------------------------
  199.  */
  200. void
  201. TkpDisplayScrollbar(
  202.     ClientData clientData) /* Information about window. */
  203. {
  204.     TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
  205.     MacScrollbar *macScrollPtr = (MacScrollbar *) clientData;
  206.     Tk_Window tkwin = scrollPtr->tkwin;
  207.     CGrafPtr destPort, savePort;
  208.     Boolean portChanged;
  209.     WindowRef windowRef;
  210.     if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  211. goto done;
  212.     }
  213.     /*
  214.      * Draw the focus or any 3D relief we may have.
  215.      */
  216.     if (scrollPtr->highlightWidth != 0) {
  217. GC fgGC, bgGC;
  218. bgGC = Tk_GCForColor(scrollPtr->highlightBgColorPtr,
  219.     Tk_WindowId(tkwin));
  220. if (scrollPtr->flags & GOT_FOCUS) {
  221.     fgGC = Tk_GCForColor(scrollPtr->highlightColorPtr,
  222. Tk_WindowId(tkwin));
  223.     TkpDrawHighlightBorder(tkwin, fgGC, bgGC, scrollPtr->highlightWidth,
  224. Tk_WindowId(tkwin));
  225. } else {
  226.     TkpDrawHighlightBorder(tkwin, bgGC, bgGC, scrollPtr->highlightWidth,
  227. Tk_WindowId(tkwin));
  228. }
  229.     }
  230.     Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), scrollPtr->bgBorder,
  231. scrollPtr->highlightWidth, scrollPtr->highlightWidth,
  232. Tk_Width(tkwin) - 2*scrollPtr->highlightWidth,
  233. Tk_Height(tkwin) - 2*scrollPtr->highlightWidth,
  234. scrollPtr->borderWidth, scrollPtr->relief);
  235.     if (macScrollPtr->sbHandle == NULL) {
  236. Rect r = {0, 0, 1, 1};
  237. windowRef = TkMacOSXDrawableWindow(Tk_WindowId(tkwin));
  238. CreateScrollBarControl(windowRef, &r, 0, 0, 0, 0, true, NULL,
  239. &(macScrollPtr->sbHandle));
  240. SetControlReference(macScrollPtr->sbHandle, (SInt32) scrollPtr);
  241. if (IsWindowActive(windowRef)) {
  242.     macScrollPtr->macFlags |= ACTIVE;
  243. }
  244.     }
  245.     /*
  246.      * Update the control values before we draw.
  247.      */
  248.     UpdateControlValues(macScrollPtr);
  249.     /*
  250.      * Set up port for drawing Macintosh control.
  251.      */
  252.     destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin));
  253.     portChanged = QDSwapPort(destPort, &savePort);
  254.     TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin));
  255.     /*
  256.      * Scrollbars do not erase the complete control bounds if they are wider
  257.      * than the standard width, so manually erase the extra space.
  258.      */
  259.     if (!EmptyRect(&macScrollPtr->eraseRect)) {
  260. EraseRect(&macScrollPtr->eraseRect);
  261.     }
  262.     Draw1Control(macScrollPtr->sbHandle);
  263.     if (portChanged) {
  264. QDSwapPort(savePort, NULL);
  265.     }
  266.     done:
  267.     scrollPtr->flags &= ~REDRAW_PENDING;
  268. }
  269. /*
  270.  *----------------------------------------------------------------------
  271.  *
  272.  * TkpConfigureScrollbar --
  273.  *
  274.  * This procedure is called after the generic code has finished
  275.  * processing configuration options, in order to configure
  276.  * platform specific options.
  277.  *
  278.  * Results:
  279.  * None.
  280.  *
  281.  * Side effects:
  282.  * None.
  283.  *
  284.  *----------------------------------------------------------------------
  285.  */
  286. void
  287. TkpConfigureScrollbar(scrollPtr)
  288.     register TkScrollbar *scrollPtr; /* Information about widget; may or
  289.  * may not already have values for
  290.  * some fields. */
  291. {
  292. }
  293. /*
  294.  *----------------------------------------------------------------------
  295.  *
  296.  * TkpComputeScrollbarGeometry --
  297.  *
  298.  * After changes in a scrollbar's size or configuration, this
  299.  * procedure recomputes various geometry information used in
  300.  * displaying the scrollbar.
  301.  *
  302.  * Results:
  303.  * None.
  304.  *
  305.  * Side effects:
  306.  * The scrollbar will be displayed differently.
  307.  *
  308.  *----------------------------------------------------------------------
  309.  */
  310. void
  311. TkpComputeScrollbarGeometry(
  312.     register TkScrollbar *scrollPtr) /* Scrollbar whose geometry may
  313.  * have changed. */
  314. {
  315.     int variant, fieldLength;
  316.     if (scrollPtr->highlightWidth < 0) {
  317. scrollPtr->highlightWidth = 0;
  318.     }
  319.     scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth;
  320.     variant = ((scrollPtr->vertical ? Tk_Width(scrollPtr->tkwin) :
  321.     Tk_Height(scrollPtr->tkwin)) - 2 * scrollPtr->inset
  322.     < metrics[0].width) ? 1 : 0;
  323.     scrollPtr->arrowLength = (metrics[variant].topArrowHeight +
  324.     metrics[variant].bottomArrowHeight) / 2;
  325.     fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
  326.     : Tk_Width(scrollPtr->tkwin))
  327.     - 2 * (scrollPtr->arrowLength + scrollPtr->inset);
  328.     if (fieldLength < 0) {
  329. fieldLength = 0;
  330.     }
  331.     scrollPtr->sliderFirst = fieldLength * scrollPtr->firstFraction;
  332.     scrollPtr->sliderLast = fieldLength * scrollPtr->lastFraction;
  333.     /*
  334.      * Adjust the slider so that some piece of it is always
  335.      * displayed in the scrollbar and so that it has at least
  336.      * a minimal width (so it can be grabbed with the mouse).
  337.      */
  338.     if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) {
  339. scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
  340.     }
  341.     if (scrollPtr->sliderFirst < 0) {
  342. scrollPtr->sliderFirst = 0;
  343.     }
  344.     if (scrollPtr->sliderLast < (scrollPtr->sliderFirst +
  345.     metrics[variant].minThumbHeight)) {
  346. scrollPtr->sliderLast = scrollPtr->sliderFirst +
  347. metrics[variant].minThumbHeight;
  348.     }
  349.     if (scrollPtr->sliderLast > fieldLength) {
  350. scrollPtr->sliderLast = fieldLength;
  351.     }
  352.     scrollPtr->sliderFirst += scrollPtr->inset +
  353.     metrics[variant].topArrowHeight;
  354.     scrollPtr->sliderLast += scrollPtr->inset +
  355.     metrics[variant].bottomArrowHeight;
  356.     /*
  357.      * Register the desired geometry for the window (leave enough space
  358.      * for the two arrows plus a minimum-size slider, plus border around
  359.      * the whole window, if any). Then arrange for the window to be
  360.      * redisplayed.
  361.      */
  362.     if (scrollPtr->vertical) {
  363. Tk_GeometryRequest(scrollPtr->tkwin, scrollPtr->width +
  364. 2 * scrollPtr->inset, 2 * (scrollPtr->arrowLength +
  365. scrollPtr->borderWidth + scrollPtr->inset) +
  366. metrics[variant].minThumbHeight);
  367.     } else {
  368. Tk_GeometryRequest(scrollPtr->tkwin, 2 * (scrollPtr->arrowLength +
  369. scrollPtr->borderWidth + scrollPtr->inset) +
  370. metrics[variant].minThumbHeight, scrollPtr->width +
  371. 2 * scrollPtr->inset);
  372.     }
  373.     Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset);
  374. }
  375. /*
  376.  *----------------------------------------------------------------------
  377.  *
  378.  * TkpDestroyScrollbar --
  379.  *
  380.  * Free data structures associated with the scrollbar control.
  381.  *
  382.  * Results:
  383.  * None.
  384.  *
  385.  * Side effects:
  386.  * None.
  387.  *
  388.  *----------------------------------------------------------------------
  389.  */
  390. void
  391. TkpDestroyScrollbar(
  392.     TkScrollbar *scrollPtr) /* Scrollbar to destroy. */
  393. {
  394.     MacScrollbar *macScrollPtr = (MacScrollbar *)scrollPtr;
  395.     if (macScrollPtr->sbHandle != NULL) {
  396. if (!(macScrollPtr->macFlags & IN_MODAL_LOOP)) {
  397.     DisposeControl(macScrollPtr->sbHandle);
  398.     macScrollPtr->sbHandle = NULL;
  399. }
  400.     }
  401.     macScrollPtr->macFlags |= ALREADY_DEAD;
  402. }
  403. /*
  404.  *--------------------------------------------------------------
  405.  *
  406.  * TkpScrollbarPosition --
  407.  *
  408.  * Determine the scrollbar element corresponding to a
  409.  * given position.
  410.  *
  411.  * Results:
  412.  * One of TOP_ARROW, TOP_GAP, etc., indicating which element
  413.  * of the scrollbar covers the position given by (x, y). If
  414.  * (x,y) is outside the scrollbar entirely, then OUTSIDE is
  415.  * returned.
  416.  *
  417.  * Side effects:
  418.  * None.
  419.  *
  420.  *--------------------------------------------------------------
  421.  */
  422. int
  423. TkpScrollbarPosition(
  424.     TkScrollbar *scrollPtr, /* Scrollbar widget record. */
  425.     int x, int y) /* Coordinates within scrollPtr's
  426.  * window. */
  427. {
  428.     MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr;
  429.     CGrafPtr destPort, savePort;
  430.     Boolean portChanged;
  431.     int inactive = 0;
  432.     ControlPartCode part;
  433.     Point where = {y, x};
  434.     Rect bounds;
  435.     if ((x < scrollPtr->inset) || (x >= (Tk_Width(scrollPtr->tkwin) -
  436.     scrollPtr->inset)) || (y < scrollPtr->inset) ||
  437.     (y >= (Tk_Height(scrollPtr->tkwin) - scrollPtr->inset))) {
  438. return OUTSIDE;
  439.     }
  440.     /*
  441.      * All of the calculations in this procedure mirror those in
  442.      * DisplayScrollbar. Be sure to keep the two consistent. On the
  443.      * Macintosh we use the OS call TestControl to do this mapping.
  444.      * For TestControl to work, the scrollbar must be active and must
  445.      * be in the current port.
  446.      */
  447.     destPort = TkMacOSXGetDrawablePort(Tk_WindowId(scrollPtr->tkwin));
  448.     portChanged = QDSwapPort(destPort, &savePort);
  449.     UpdateControlValues(macScrollPtr);
  450.     if (!IsControlActive(macScrollPtr->sbHandle)) {
  451. inactive = true;
  452. ActivateControl(macScrollPtr->sbHandle);
  453.     }
  454.     TkMacOSXWinBounds((TkWindow *) scrollPtr->tkwin, &bounds);
  455.     where.h += bounds.left;
  456.     where.v += bounds.top;
  457.     part = TestControl(((MacScrollbar *) scrollPtr)->sbHandle, where);
  458.     if (inactive) {
  459. DeactivateControl(macScrollPtr->sbHandle);
  460.     }
  461.     if (portChanged) {
  462. QDSwapPort(savePort, NULL);
  463.     }
  464.     switch (part) {
  465. case kAppearancePartUpButton:
  466.     return TOP_ARROW;
  467. case kAppearancePartPageUpArea:
  468.     return TOP_GAP;
  469. case kAppearancePartIndicator:
  470.     return SLIDER;
  471. case kAppearancePartPageDownArea:
  472.     return BOTTOM_GAP;
  473. case kAppearancePartDownButton:
  474.     return BOTTOM_ARROW;
  475. default:
  476.     return OUTSIDE;
  477.     }
  478. }
  479. /*
  480.  *--------------------------------------------------------------
  481.  *
  482.  * ThumbActionProc --
  483.  *
  484.  * Callback procedure used by the Macintosh toolbox call
  485.  * HandleControlClick. This call is used to track the
  486.  * thumb of the scrollbar. Unlike the
  487.  * ScrollbarActionProc function this function is called
  488.  * once and basically takes over tracking the scrollbar
  489.  * from the control. This is done to avoid conflicts with
  490.  * what the control plans to draw.
  491.  *
  492.  * Results:
  493.  * None.
  494.  *
  495.  * Side effects:
  496.  * May change the display.
  497.  *
  498.  *--------------------------------------------------------------
  499.  */
  500. static pascal void
  501. ThumbActionProc(ControlRef theControl, ControlPartCode partCode)
  502. {
  503.     TkScrollbar *scrollPtr = (TkScrollbar *)(intptr_t)GetControlReference(
  504.     theControl);
  505.     MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr;
  506.     Tcl_DString cmdString;
  507.     int origValue, variant;
  508.     short trackBarSize;
  509.     double oldFirstFraction, newFirstFraction;
  510.     char valueString[40];
  511.     Point currentPoint = { 0, 0 };
  512.     Rect trackRect;
  513.     Tcl_Interp *interp;
  514.     MouseTrackingResult trackingResult;
  515.     OSStatus err;
  516.     if (scrollPtr == NULL) {
  517. return;
  518.     }
  519.     Tcl_DStringInit(&cmdString);
  520.     origValue = GetControl32BitValue(macScrollPtr->sbHandle);
  521.     GetControlBounds(macScrollPtr->sbHandle, &trackRect);
  522.     if (scrollPtr->vertical) {
  523. variant = (trackRect.right - trackRect.left) < metrics[0].width ? 1 : 0;
  524. trackBarSize = trackRect.bottom - trackRect.top -
  525. metrics[variant].topArrowHeight -
  526. metrics[variant].bottomArrowHeight;
  527. InsetRect(&trackRect, -25, -113);
  528.     } else {
  529. variant = (trackRect.bottom - trackRect.top) < metrics[0].width ? 1 : 0;
  530. trackBarSize = trackRect.right - trackRect.left -
  531. metrics[variant].topArrowHeight -
  532. metrics[variant].bottomArrowHeight;
  533. InsetRect(&trackRect, -113, -25);
  534.     }
  535.     /*
  536.      * Track the mouse while the button is held down. If the mouse is moved,
  537.      * we calculate the value that should be passed to the "command" part of
  538.      * the scrollbar. Since the mouse may move a distance too small to
  539.      * cause a change to the first fraction, each calculation must be done
  540.      * versus what the first fraction was when the mouse button was
  541.      * initially pressed. Otherwise, moving the mouse too slowly will
  542.      * cause the calculated fraction delta to be zero and the scrollbar
  543.      * won't respond.
  544.      */
  545.     oldFirstFraction = scrollPtr->firstFraction;
  546.     TkMacOSXTrackingLoop(1);
  547.     do {
  548. err = ChkErr(TrackMouseLocationWithOptions, NULL,
  549. kTrackMouseLocationOptionDontConsumeMouseUp,
  550. kEventDurationForever, &currentPoint, NULL, &trackingResult);
  551. if ((err == noErr) && ((trackingResult == kMouseTrackingMouseDragged)
  552. || (trackingResult == kMouseTrackingMouseMoved))) {
  553.    /*
  554.     * Calculate where the scrollbar should move to, based on
  555.     * where the mouse button was pressed and where the scrollbar
  556.     * initially was at that time. Note that PtInRect() will
  557.     * return false if currentPoint or trackRect are not in
  558.     * is not in current graphics port, which may happen if any
  559.     * of the waiting idle events change the port (e.g. with
  560.     * SetPort()) but fail to restore it before returning and the
  561.     * scrollbar will lock in place.
  562.     */
  563.     newFirstFraction = oldFirstFraction;
  564.     if (PtInRect(currentPoint, &trackRect)) {
  565. short pixDiff;
  566. if (scrollPtr->vertical) {
  567.     pixDiff = currentPoint.v - mouseDownPoint.v;
  568. } else {
  569.     pixDiff = currentPoint.h - mouseDownPoint.h;
  570. }
  571. newFirstFraction += (double)pixDiff / trackBarSize;
  572. if (newFirstFraction > 1.0) {
  573.     newFirstFraction = 1.0;
  574. } else if (newFirstFraction < 0.0) {
  575.     newFirstFraction = 0.0;
  576. }
  577.     }
  578.     /*
  579.      * Move the scrollbar thumb to the new first fraction given
  580.      * its position when initially pressed and how far the mouse
  581.      * has moved. Process waiting idle tasks afterward to allow
  582.      * for the display to update.
  583.      */
  584.     sprintf(valueString, "%g", newFirstFraction);
  585.     Tcl_DStringSetLength(&cmdString, 0);
  586.     Tcl_DStringAppend(&cmdString, scrollPtr->command,
  587. scrollPtr->commandSize);
  588.     Tcl_DStringAppendElement(&cmdString, "moveto");
  589.     Tcl_DStringAppendElement(&cmdString, valueString);
  590.     interp = scrollPtr->interp;
  591.     Tcl_Preserve((ClientData) interp);
  592.     Tcl_EvalEx(interp, Tcl_DStringValue(&cmdString),
  593.     Tcl_DStringLength(&cmdString), TCL_EVAL_GLOBAL);
  594.     Tcl_Release((ClientData) interp);
  595.     TkMacOSXRunTclEventLoop();
  596. }
  597.     } while ((err == noErr) && trackingResult != kMouseTrackingMouseReleased);
  598.     TkMacOSXTrackingLoop(0);
  599.     Tcl_DStringFree(&cmdString);
  600.     return;
  601. }
  602. /*
  603.  *--------------------------------------------------------------
  604.  *
  605.  * ScrollbarActionProc --
  606.  *
  607.  * Callback procedure used by the Macintosh toolbox call
  608.  * HandleControlClick. This call will update the display
  609.  * while the scrollbar is being manipulated by the user.
  610.  *
  611.  * Results:
  612.  * None.
  613.  *
  614.  * Side effects:
  615.  * May change the display.
  616.  *
  617.  *--------------------------------------------------------------
  618.  */
  619. static pascal void
  620. ScrollbarActionProc(
  621.     ControlRef theControl, /* Handle to scrollbat control */
  622.     ControlPartCode partCode) /* Part of scrollbar that was "hit" */
  623. {
  624.     TkScrollbar *scrollPtr = (TkScrollbar *)(intptr_t)GetControlReference(
  625.     theControl);
  626.     MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr;
  627.     Tcl_DString cmdString;
  628.     Tcl_DStringInit(&cmdString);
  629.     Tcl_DStringAppend(&cmdString, scrollPtr->command,
  630.     scrollPtr->commandSize);
  631.     if ( partCode == kAppearancePartUpButton ||
  632.     partCode == kAppearancePartDownButton ) {
  633. Tcl_DStringAppendElement(&cmdString, "scroll");
  634. Tcl_DStringAppendElement(&cmdString,
  635. (partCode == kAppearancePartUpButton) ? "-1" : "1");
  636. Tcl_DStringAppendElement(&cmdString, "unit");
  637.     } else if (partCode == kAppearancePartPageUpArea ||
  638.     partCode == kAppearancePartPageDownArea ) {
  639. Tcl_DStringAppendElement(&cmdString, "scroll");
  640. Tcl_DStringAppendElement(&cmdString,
  641. (partCode == kAppearancePartPageUpArea) ? "-1" : "1");
  642. Tcl_DStringAppendElement(&cmdString, "page");
  643.     } else if (partCode == kAppearancePartIndicator) {
  644. char valueString[TCL_DOUBLE_SPACE];
  645. sprintf(valueString, "%g",
  646. (GetControl32BitValue(macScrollPtr->sbHandle) -
  647. MIN_SCROLLBAR_VALUE) / SCROLLBAR_SCALING_VALUE);
  648. Tcl_DStringAppendElement(&cmdString, "moveto");
  649. Tcl_DStringAppendElement(&cmdString, valueString);
  650.     }
  651.     Tcl_Preserve((ClientData) scrollPtr->interp);
  652.     Tcl_EvalEx(scrollPtr->interp, Tcl_DStringValue(&cmdString),
  653.     Tcl_DStringLength(&cmdString), TCL_EVAL_GLOBAL);
  654.     Tcl_Release((ClientData) scrollPtr->interp);
  655.     Tcl_DStringFree(&cmdString);
  656.     TkMacOSXRunTclEventLoop();
  657. }
  658. /*
  659.  *--------------------------------------------------------------
  660.  *
  661.  * ScrollbarBindProc --
  662.  *
  663.  * This procedure is invoked when the default <ButtonPress>
  664.  * binding on the Scrollbar bind tag fires.
  665.  *
  666.  * Results:
  667.  * None.
  668.  *
  669.  * Side effects:
  670.  * The event enters a modal loop.
  671.  *
  672.  *--------------------------------------------------------------
  673.  */
  674. static int
  675. ScrollbarBindProc(
  676.     ClientData clientData, /* Not used. */
  677.     Tcl_Interp *interp, /* Interp with binding. */
  678.     XEvent *eventPtr, /* X event that triggered binding. */
  679.     Tk_Window tkwin, /* Target window for event. */
  680.     KeySym keySym) /* The KeySym if a key event. */
  681. {
  682.     TkWindow *winPtr = (TkWindow*)tkwin;
  683.     TkScrollbar *scrollPtr = (TkScrollbar *) winPtr->instanceData;
  684.     MacScrollbar *macScrollPtr = (MacScrollbar *) winPtr->instanceData;
  685.     Tcl_Preserve((ClientData)scrollPtr);
  686.     macScrollPtr->macFlags |= IN_MODAL_LOOP;
  687.     if (eventPtr->type == ButtonPress) {
  688. Point where;
  689. Rect bounds;
  690. ControlPartCode part;
  691. CGrafPtr destPort, savePort;
  692. Boolean portChanged;
  693. Window window;
  694. /*
  695.  * To call Macintosh control routines we must have the port
  696.  * set to the window containing the control. We will then test
  697.  * which part of the control was hit and act accordingly.
  698.  */
  699. destPort = TkMacOSXGetDrawablePort(Tk_WindowId(scrollPtr->tkwin));
  700. portChanged = QDSwapPort(destPort, &savePort);
  701. TkMacOSXSetUpClippingRgn(Tk_WindowId(scrollPtr->tkwin));
  702. TkMacOSXWinBounds((TkWindow *) scrollPtr->tkwin, &bounds);
  703. where.h = eventPtr->xbutton.x + bounds.left;
  704. where.v = eventPtr->xbutton.y + bounds.top;
  705. part = TestControl(macScrollPtr->sbHandle, where);
  706. TkMacOSXTrackingLoop(1);
  707. if (part == kAppearancePartIndicator && scrollPtr->jump == false) {
  708.     /*
  709.      * Case 1: In thumb, no jump scrolling. Call track control
  710.      * with the thumb action proc which will do most of the work.
  711.      */
  712.     mouseDownPoint.h = where.h;
  713.     mouseDownPoint.v = where.v;
  714.     part = HandleControlClick(macScrollPtr->sbHandle, where,
  715.     TkMacOSXModifierState(), thumbActionProc);
  716. } else if (part == kAppearancePartIndicator) {
  717.     /*
  718.      * Case 2: in thumb with jump scrolling. Call HandleControlClick
  719.      * with a NULL action proc. Use the new value of the control
  720.      * to set update the control.
  721.      */
  722.     part = HandleControlClick(macScrollPtr->sbHandle, where,
  723.     TkMacOSXModifierState(), NULL);
  724.     if (part == kAppearancePartIndicator) {
  725. Tcl_DString cmdString;
  726. char valueString[TCL_DOUBLE_SPACE];
  727. sprintf(valueString, "%g",
  728. (GetControl32BitValue(macScrollPtr->sbHandle) -
  729. MIN_SCROLLBAR_VALUE) / SCROLLBAR_SCALING_VALUE);
  730. Tcl_DStringInit(&cmdString);
  731. Tcl_DStringAppend(&cmdString, scrollPtr->command,
  732. strlen(scrollPtr->command));
  733. Tcl_DStringAppendElement(&cmdString, "moveto");
  734. Tcl_DStringAppendElement(&cmdString, valueString);
  735. interp = scrollPtr->interp;
  736. Tcl_Preserve((ClientData) interp);
  737. Tcl_EvalEx(interp, Tcl_DStringValue(&cmdString),
  738. Tcl_DStringLength(&cmdString), TCL_EVAL_GLOBAL);
  739. Tcl_Release((ClientData) interp);
  740. Tcl_DStringFree(&cmdString);
  741. TkMacOSXRunTclEventLoop();
  742.     }
  743. } else if (part != 0) {
  744.     /*
  745.      * Case 3: in any other part of the scrollbar. We call
  746.      * HandleControlClick with the scrollActionProc which will do
  747.      * most all the work.
  748.      */
  749.     HandleControlClick(macScrollPtr->sbHandle, where,
  750.     TkMacOSXModifierState(), scrollActionProc);
  751.     /*
  752.      * Workaround for Carbon bug where the scrollbar down arrow
  753.      * sometimes gets "stuck" after the mousebutton has been released.
  754.      */
  755.     if (scrollPtr->tkwin) {
  756. TkMacOSXSetUpClippingRgn(Tk_WindowId(scrollPtr->tkwin));
  757.     }
  758.     Draw1Control(macScrollPtr->sbHandle);
  759. }
  760. TkMacOSXTrackingLoop(0);
  761. /*
  762.  * The HandleControlClick call will "eat" the ButtonUp event. We now
  763.  * generate a ButtonUp event so Tk will unset implicit grabs etc.
  764.  */
  765. if (scrollPtr->tkwin) {
  766.     window = Tk_WindowId(scrollPtr->tkwin);
  767.     TkGenerateButtonEventForXPointer(window);
  768. }
  769. if (portChanged) {
  770.     QDSwapPort(savePort, NULL);
  771. }
  772.     }
  773.     if (macScrollPtr->sbHandle && (macScrollPtr->macFlags & ALREADY_DEAD)) {
  774. DisposeControl(macScrollPtr->sbHandle);
  775. macScrollPtr->sbHandle = NULL;
  776.     }
  777.     macScrollPtr->macFlags &= ~IN_MODAL_LOOP;
  778.     Tcl_Release((ClientData)scrollPtr);
  779.     return TCL_OK;
  780. }
  781. /*
  782.  *--------------------------------------------------------------
  783.  *
  784.  * ScrollbarEventProc --
  785.  *
  786.  * This procedure is invoked by the Tk dispatcher for various
  787.  * events on scrollbars.
  788.  *
  789.  * Results:
  790.  * None.
  791.  *
  792.  * Side effects:
  793.  * When the window gets deleted, internal structures get
  794.  * cleaned up. When it gets exposed, it is redisplayed.
  795.  *
  796.  *--------------------------------------------------------------
  797.  */
  798. static void
  799. ScrollbarEventProc(
  800.     ClientData clientData, /* Information about window. */
  801.     XEvent *eventPtr) /* Information about event. */
  802. {
  803.     TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
  804.     MacScrollbar *macScrollPtr = (MacScrollbar *) clientData;
  805.     if (eventPtr->type == UnmapNotify) {
  806. TkMacOSXSetScrollbarGrow((TkWindow *) scrollPtr->tkwin, false);
  807.     } else if (eventPtr->type == ActivateNotify) {
  808. macScrollPtr->macFlags |= ACTIVE;
  809. TkScrollbarEventuallyRedraw((ClientData) scrollPtr);
  810.     } else if (eventPtr->type == DeactivateNotify) {
  811. macScrollPtr->macFlags &= ~ACTIVE;
  812. TkScrollbarEventuallyRedraw((ClientData) scrollPtr);
  813.     } else {
  814. TkScrollbarEventProc(clientData, eventPtr);
  815.     }
  816. }
  817. /*
  818.  *--------------------------------------------------------------
  819.  *
  820.  * UpdateControlValues --
  821.  *
  822.  * This procedure updates the Macintosh scrollbar control
  823.  * to display the values defined by the Tk scrollbar.
  824.  *
  825.  * Results:
  826.  * None.
  827.  *
  828.  * Side effects:
  829.  * The Macintosh control is updated.
  830.  *
  831.  *--------------------------------------------------------------
  832.  */
  833. static void
  834. UpdateControlValues(
  835.     MacScrollbar *macScrollPtr) /* Scrollbar data struct. */
  836. {
  837.     TkScrollbar *scrollPtr = (TkScrollbar *) macScrollPtr;
  838.     Tk_Window tkwin = scrollPtr->tkwin;
  839.     MacDrawable * macDraw = (MacDrawable *) Tk_WindowId(scrollPtr->tkwin);
  840.     double dViewSize;
  841.     Rect contrlRect, portRect;
  842.     int variant, active;
  843.     short width, height;
  844.     contrlRect.left   = macDraw->xOff + scrollPtr->inset;
  845.     contrlRect.top    = macDraw->yOff + scrollPtr->inset;
  846.     contrlRect.right  = macDraw->xOff + Tk_Width(tkwin) - scrollPtr->inset;
  847.     contrlRect.bottom = macDraw->yOff + Tk_Height(tkwin) - scrollPtr->inset;
  848.     GetPortBounds (GetWindowPort(GetControlOwner(macScrollPtr->sbHandle)),
  849.     &portRect);
  850.     /*
  851.      * If the scrollbar is flush against the bottom right hand corner then
  852.      * we leave space to draw the grow region for the window.
  853.      */
  854.     if (portRect.bottom == contrlRect.bottom &&
  855.     portRect.right == contrlRect.right) {
  856. TkMacOSXSetScrollbarGrow((TkWindow *) tkwin, true);
  857. if (macDraw->toplevel &&
  858. TkMacOSXResizable(macDraw->toplevel->winPtr)) {
  859.     int growSize;
  860.     switch (TkMacOSXWindowClass(macDraw->toplevel->winPtr)) {
  861. case kFloatingWindowClass:
  862. case kUtilityWindowClass:
  863.     growSize = metrics[1].width - 1;
  864.     break;
  865. case kDocumentWindowClass:
  866. case kMovableAlertWindowClass:
  867. case kMovableModalWindowClass:
  868. default:
  869.     growSize = metrics[0].width - 1;
  870.     break;
  871.     }
  872.     if (scrollPtr->vertical) {
  873. contrlRect.bottom -= growSize;
  874.     } else {
  875. contrlRect.right -= growSize;
  876.     }
  877. }
  878.     } else {
  879. TkMacOSXSetScrollbarGrow((TkWindow *) tkwin, false);
  880.     }
  881.     if (IsControlVisible (macScrollPtr->sbHandle)) {
  882. SetControlVisibility(macScrollPtr->sbHandle, false, false);
  883.     }
  884.     if (scrollPtr->vertical) {
  885. width  = contrlRect.right - contrlRect.left;
  886. height = contrlRect.bottom - contrlRect.top;
  887.     } else {
  888. width  = contrlRect.bottom - contrlRect.top;
  889. height = contrlRect.right - contrlRect.left;
  890.     }
  891.     variant = width < metrics[0].width ? 1 : 0;
  892.     SetControlData(macScrollPtr->sbHandle, kControlEntireControl,
  893.     kControlSizeTag, sizeof(ControlSize),
  894.     &(metrics[variant].size));
  895.     macScrollPtr->eraseRect = contrlRect;
  896.     if (scrollPtr->vertical) {
  897. macScrollPtr->eraseRect.left += metrics[variant].width;
  898.     } else {
  899. macScrollPtr->eraseRect.top  += metrics[variant].width;
  900.     }
  901.     /*
  902.      * Ensure we set scrollbar control bounds only once all size
  903.      * adjustments have been computed.
  904.      */
  905.     SetControlBounds(macScrollPtr->sbHandle, &contrlRect);
  906.     /*
  907.      * Given the Tk parameters for the fractions of the start and
  908.      * end of the thumb, the following calculation determines the
  909.      * location for the Macintosh thumb.
  910.      * The Aqua scroll control works as follows.
  911.      * The scrollbar's value is the position of the left (or top) side of
  912.      * the view area in the content area being scrolled.
  913.      * The maximum value of the control is therefore the dimension of
  914.      * the content area less the size of the view area.
  915.      * Since these values are all integers, and Tk gives the thumb position
  916.      * as fractions, we have introduced a scaling factor.
  917.      */
  918.     dViewSize = (scrollPtr->lastFraction - scrollPtr->firstFraction)
  919.     * SCROLLBAR_SCALING_VALUE;
  920.     SetControl32BitMinimum(macScrollPtr->sbHandle, MIN_SCROLLBAR_VALUE);
  921.     SetControl32BitMaximum(macScrollPtr->sbHandle, MIN_SCROLLBAR_VALUE +
  922.     SCROLLBAR_SCALING_VALUE - dViewSize);
  923.     SetControlViewSize(macScrollPtr->sbHandle, dViewSize);
  924.     SetControl32BitValue(macScrollPtr->sbHandle, MIN_SCROLLBAR_VALUE +
  925.     SCROLLBAR_SCALING_VALUE * scrollPtr->firstFraction);
  926.     if((scrollPtr->firstFraction <= 0.0 && scrollPtr->lastFraction >= 1.0)
  927.     || height <= metrics[variant].minHeight) {
  928. /* Disable scrollbar */
  929. SetControl32BitMaximum(macScrollPtr->sbHandle, MIN_SCROLLBAR_VALUE);
  930.     }
  931.     active = ((macScrollPtr->macFlags & ACTIVE) != 0);
  932.     if (active != IsControlActive(macScrollPtr->sbHandle)) {
  933. if (active) {
  934.     ActivateControl(macScrollPtr->sbHandle);
  935. } else {
  936.     DeactivateControl(macScrollPtr->sbHandle);
  937. }
  938.     }
  939.     SetControlVisibility(macScrollPtr->sbHandle, true, false);
  940. }