home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / mac / tkMacButton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  24.0 KB  |  826 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkMacButton.c --
  3.  *
  4.  *    This file implements the Macintosh specific portion of the
  5.  *    button widgets.
  6.  *
  7.  * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkMacButton.c 1.16 97/06/26 21:23:35
  13.  */
  14.  
  15. #include "tkButton.h"
  16. #include "tkMacInt.h"
  17. #include <Controls.h>
  18. #include <LowMem.h>
  19.  
  20. /*
  21.  * Some defines used to control what type of control is drawn.
  22.  */
  23.  
  24. #define DRAW_LABEL    0        /* Labels are treated genericly. */
  25. #define DRAW_CONTROL    1        /* Draw using the Native control. */
  26. #define DRAW_CUSTOM    2        /* Make our own button drawing. */
  27.  
  28. /*
  29.  * The following structures are used to draw our controls.  Rather than
  30.  * having many Mac controls we just use one control of each type and
  31.  * reuse them for all Tk widgets.  When the windowRef variable is NULL
  32.  * it means none of the data structures have been allocated.
  33.  */
  34.  
  35. static WindowRef windowRef = NULL;
  36. static CWindowRecord windowRecord;
  37. static ControlRef buttonHandle;
  38. static ControlRef checkHandle;
  39. static ControlRef radioHandle;
  40. static CCTabHandle buttonTabHandle;
  41. static CCTabHandle checkTabHandle;
  42. static CCTabHandle radioTabHandle;
  43. static PixMapHandle oldPixPtr;
  44.  
  45. /*
  46.  * Forward declarations for procedures defined later in this file:
  47.  */
  48.  
  49. static int        UpdateControlColors _ANSI_ARGS_((TkButton *butPtr,
  50.                 ControlRef controlHandle, CCTabHandle ccTabHandle,
  51.                 RGBColor *saveColorPtr));
  52. static void        DrawBufferedControl _ANSI_ARGS_((TkButton *butPtr,
  53.                 GWorldPtr destPort));
  54. static void        ChangeBackgroundWindowColor _ANSI_ARGS_((
  55.                 WindowRef macintoshWindow, RGBColor rgbColor,
  56.                 RGBColor *oldColor));
  57. static void        ButtonExitProc _ANSI_ARGS_((ClientData clientData));
  58.  
  59. /*
  60.  * The class procedure table for the button widgets.
  61.  */
  62.  
  63. TkClassProcs tkpButtonProcs = { 
  64.     NULL,            /* createProc. */
  65.     TkButtonWorldChanged,    /* geometryProc. */
  66.     NULL            /* modalProc. */
  67. };
  68.  
  69. /*
  70.  *----------------------------------------------------------------------
  71.  *
  72.  * TkpCreateButton --
  73.  *
  74.  *    Allocate a new TkButton structure.
  75.  *
  76.  * Results:
  77.  *    Returns a newly allocated TkButton structure.
  78.  *
  79.  * Side effects:
  80.  *    Registers an event handler for the widget.
  81.  *
  82.  *----------------------------------------------------------------------
  83.  */
  84.  
  85. TkButton *
  86. TkpCreateButton(
  87.     Tk_Window tkwin)
  88. {
  89.     return (TkButton *) ckalloc(sizeof(TkButton));
  90. }
  91.  
  92. /*
  93.  *----------------------------------------------------------------------
  94.  *
  95.  * TkpDisplayButton --
  96.  *
  97.  *    This procedure is invoked to display a button widget.  It is
  98.  *    normally invoked as an idle handler.
  99.  *
  100.  * Results:
  101.  *    None.
  102.  *
  103.  * Side effects:
  104.  *    Commands are output to X to display the button in its
  105.  *    current mode.  The REDRAW_PENDING flag is cleared.
  106.  *
  107.  *----------------------------------------------------------------------
  108.  */
  109.  
  110. void
  111. TkpDisplayButton(
  112.     ClientData clientData)    /* Information about widget. */
  113. {
  114.     TkButton *butPtr = (TkButton *) clientData;
  115.     Pixmap pixmap;
  116.     GC gc;
  117.     Tk_3DBorder border;
  118.     int x = 0;            /* Initialization only needed to stop
  119.                  * compiler warning. */
  120.     int y, relief;
  121.     register Tk_Window tkwin = butPtr->tkwin;
  122.     int width, height;
  123.     int offset;            /* 0 means this is a normal widget.  1 means
  124.                  * it is an image button, so we offset the
  125.                  * image to make the button appear to move
  126.                  * up and down as the relief changes. */
  127.     CGrafPtr saveWorld;
  128.     GDHandle saveDevice;
  129.     GWorldPtr destPort;
  130.     int drawType, borderWidth;
  131.     
  132.     GetGWorld(&saveWorld, &saveDevice);
  133.  
  134.     butPtr->flags &= ~REDRAW_PENDING;
  135.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  136.     return;
  137.     }
  138.  
  139.     border = butPtr->normalBorder;
  140.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
  141.     gc = butPtr->disabledGC;
  142.     } else if ((butPtr->type == TYPE_BUTTON) && (butPtr->state == tkActiveUid)) {
  143.     gc = butPtr->activeTextGC;
  144.     border = butPtr->activeBorder;
  145.     } else {
  146.     gc = butPtr->normalTextGC;
  147.     }
  148.     if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
  149.         && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
  150.     border = butPtr->selectBorder;
  151.     }
  152.  
  153.     /*
  154.      * Override the relief specified for the button if this is a
  155.      * checkbutton or radiobutton and there's no indicator.
  156.      */
  157.  
  158.     relief = butPtr->relief;
  159.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  160.     relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
  161.         : TK_RELIEF_RAISED;
  162.     }
  163.  
  164.     offset = ((butPtr->type == TYPE_BUTTON) && 
  165.     ((butPtr->image != NULL) || (butPtr->bitmap != None)));
  166.  
  167.     /*
  168.      * In order to avoid screen flashes, this procedure redraws
  169.      * the button in a pixmap, then copies the pixmap to the
  170.      * screen in a single operation.  This means that there's no
  171.      * point in time where the on-sreen image has been cleared.
  172.      */
  173.  
  174.     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
  175.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  176.     Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, 0, 0,
  177.         Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  178.  
  179.    
  180.     if (butPtr->type == TYPE_LABEL) {
  181.     drawType = DRAW_LABEL;
  182.     } else if (butPtr->type == TYPE_BUTTON) {
  183.     if ((butPtr->image == None) && (butPtr->bitmap == None)) {
  184.         drawType = DRAW_CONTROL;
  185.     } else {
  186.         drawType = DRAW_CUSTOM;
  187.     }
  188.     } else {
  189.     if (butPtr->indicatorOn) {
  190.         drawType = DRAW_CONTROL;
  191.     } else {
  192.         drawType = DRAW_CUSTOM;
  193.     }
  194.     }
  195.  
  196.     /*
  197.      * Draw the native portion of the buttons.  Start by creating the control
  198.      * if it doesn't already exist.  Then configure the Macintosh control from
  199.      * the Tk info.  Finally, we call Draw1Control to draw to the screen.
  200.      */
  201.  
  202.     if (drawType == DRAW_CONTROL) {
  203.     borderWidth = 0;
  204.     
  205.     /*
  206.      * This part uses Macintosh rather than Tk calls to draw
  207.      * to the screen.  Make sure the ports etc. are set correctly.
  208.      */
  209.     
  210.     destPort = TkMacGetDrawablePort(pixmap);
  211.     SetGWorld(destPort, NULL);
  212.     DrawBufferedControl(butPtr, destPort);
  213.     }
  214.  
  215.     if ((drawType == DRAW_CUSTOM) || (drawType == DRAW_LABEL)) {
  216.     borderWidth = butPtr->borderWidth;
  217.     }
  218.  
  219.     /*
  220.      * Display image or bitmap or text for button.
  221.      */
  222.  
  223.     if (butPtr->image != None) {
  224.     Tk_SizeOfImage(butPtr->image, &width, &height);
  225.  
  226.     imageOrBitmap:
  227.     TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
  228.         butPtr->indicatorSpace + width, height, &x, &y);
  229.     x += butPtr->indicatorSpace;
  230.  
  231.     x += offset;
  232.     y += offset;
  233.     if (relief == TK_RELIEF_RAISED) {
  234.         x -= offset;
  235.         y -= offset;
  236.     } else if (relief == TK_RELIEF_SUNKEN) {
  237.         x += offset;
  238.         y += offset;
  239.     }
  240.     if (butPtr->image != NULL) {
  241.         if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
  242.         Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height,
  243.             pixmap, x, y);
  244.         } else {
  245.         Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
  246.             x, y);
  247.         }
  248.     } else {
  249.         XSetClipOrigin(butPtr->display, gc, x, y);
  250.         XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
  251.             (unsigned int) width, (unsigned int) height, x, y, 1);
  252.         XSetClipOrigin(butPtr->display, gc, 0, 0);
  253.     }
  254.     y += height/2;
  255.     } else if (butPtr->bitmap != None) {
  256.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  257.     goto imageOrBitmap;
  258.     } else {
  259.     TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
  260.         butPtr->indicatorSpace + butPtr->textWidth, butPtr->textHeight,
  261.         &x, &y);
  262.  
  263.     x += butPtr->indicatorSpace;
  264.  
  265.     Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
  266.         x, y, 0, -1);
  267.     y += butPtr->textHeight/2;
  268.     }
  269.  
  270.     /*
  271.      * If the button is disabled with a stipple rather than a special
  272.      * foreground color, generate the stippled effect.  If the widget
  273.      * is selected and we use a different background color when selected,
  274.      * must temporarily modify the GC.
  275.      */
  276.  
  277.     if ((butPtr->state == tkDisabledUid)
  278.         && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
  279.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  280.         && (butPtr->selectBorder != NULL)) {
  281.         XSetForeground(butPtr->display, butPtr->disabledGC,
  282.             Tk_3DBorderColor(butPtr->selectBorder)->pixel);
  283.     }
  284.     XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
  285.         butPtr->inset, butPtr->inset,
  286.         (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
  287.         (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
  288.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  289.         && (butPtr->selectBorder != NULL)) {
  290.         XSetForeground(butPtr->display, butPtr->disabledGC,
  291.             Tk_3DBorderColor(butPtr->normalBorder)->pixel);
  292.     }
  293.     }
  294.  
  295.     /*
  296.      * Draw the border and traversal highlight last.  This way, if the
  297.      * button's contents overflow they'll be covered up by the border.
  298.      */
  299.  
  300.     if (relief != TK_RELIEF_FLAT) {
  301.     int inset = butPtr->highlightWidth;
  302.     Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
  303.         Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
  304.         butPtr->borderWidth, relief);
  305.     }
  306.  
  307.     /*
  308.      * Copy the information from the off-screen pixmap onto the screen,
  309.      * then delete the pixmap.
  310.      */
  311.  
  312.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  313.         butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  314.         (unsigned) Tk_Height(tkwin), 0, 0);
  315.     Tk_FreePixmap(butPtr->display, pixmap);
  316.  
  317.     SetGWorld(saveWorld, saveDevice);
  318. }
  319.  
  320. /*
  321.  *----------------------------------------------------------------------
  322.  *
  323.  * TkpComputeButtonGeometry --
  324.  *
  325.  *    After changes in a button's text or bitmap, this procedure
  326.  *    recomputes the button's geometry and passes this information
  327.  *    along to the geometry manager for the window.
  328.  *
  329.  * Results:
  330.  *    None.
  331.  *
  332.  * Side effects:
  333.  *    The button's window may change size.
  334.  *
  335.  *----------------------------------------------------------------------
  336.  */
  337.  
  338. void
  339. TkpComputeButtonGeometry(
  340.     TkButton *butPtr)    /* Button whose geometry may have changed. */
  341. {
  342.     int width, height, avgWidth;
  343.     Tk_FontMetrics fm;
  344.  
  345.     if (butPtr->highlightWidth < 0) {
  346.     butPtr->highlightWidth = 0;
  347.     }
  348.     if ((butPtr->type == TYPE_BUTTON) && (butPtr->image == None)
  349.         && (butPtr->bitmap == None)) {
  350.     butPtr->inset = 0;
  351.     } else if ((butPtr->type != TYPE_LABEL) && butPtr->indicatorOn) {
  352.     butPtr->inset = 0;
  353.     } else {
  354.     butPtr->inset = butPtr->borderWidth;
  355.     }
  356.  
  357.     /*
  358.      * The highlight width corresponds to the default ring on the Macintosh.
  359.      * As such, the highlight width is only added if the button is the default
  360.      * button.  The actual width of the default ring is one less than the
  361.      * highlight width as there is also one pixel of spacing.
  362.      */
  363.  
  364.     if (butPtr->defaultState != tkDisabledUid) {
  365.     butPtr->inset += butPtr->highlightWidth;
  366.     }
  367.     butPtr->indicatorSpace = 0;
  368.     if (butPtr->image != NULL) {
  369.     Tk_SizeOfImage(butPtr->image, &width, &height);
  370.     imageOrBitmap:
  371.     if (butPtr->width > 0) {
  372.         width = butPtr->width;
  373.     }
  374.     if (butPtr->height > 0) {
  375.         height = butPtr->height;
  376.     }
  377.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  378.         butPtr->indicatorSpace = height;
  379.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  380.         butPtr->indicatorDiameter = (65*height)/100;
  381.         } else {
  382.         butPtr->indicatorDiameter = (75*height)/100;
  383.         }
  384.     }
  385.     } else if (butPtr->bitmap != None) {
  386.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  387.     goto imageOrBitmap;
  388.     } else {
  389.     Tk_FreeTextLayout(butPtr->textLayout);
  390.     butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
  391.         butPtr->text, -1, butPtr->wrapLength, butPtr->justify, 0,
  392.         &butPtr->textWidth, &butPtr->textHeight);
  393.  
  394.     width = butPtr->textWidth;
  395.     height = butPtr->textHeight;
  396.     avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
  397.     Tk_GetFontMetrics(butPtr->tkfont, &fm);
  398.  
  399.     if (butPtr->width > 0) {
  400.         width = butPtr->width * avgWidth;
  401.     }
  402.     if (butPtr->height > 0) {
  403.         height = butPtr->height * fm.linespace;
  404.     }
  405.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  406.         butPtr->indicatorDiameter = fm.linespace;
  407.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  408.         butPtr->indicatorDiameter = (80*butPtr->indicatorDiameter)/100;
  409.         }
  410.         butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
  411.     }
  412.     }
  413.  
  414.     /*
  415.      * When issuing the geometry request, add extra space for the indicator,
  416.      * if any, and for the border and padding, plus if this is an image two 
  417.      * extra pixels so the display can be offset by 1 pixel in either
  418.      * direction for the raised or lowered effect.
  419.      */
  420.  
  421.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
  422.     width += 2*butPtr->padX;
  423.     height += 2*butPtr->padY;
  424.     }
  425.     if ((butPtr->type == TYPE_BUTTON) && 
  426.     ((butPtr->image != NULL) || (butPtr->bitmap != None))) {
  427.     width += 2;
  428.     height += 2;
  429.     }
  430.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
  431.         + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
  432.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
  433. }
  434.  
  435. /*
  436.  *----------------------------------------------------------------------
  437.  *
  438.  * TkpDestroyButton --
  439.  *
  440.  *    Free data structures associated with the button control.
  441.  *
  442.  * Results:
  443.  *    None.
  444.  *
  445.  * Side effects:
  446.  *    Restores the default control state.
  447.  *
  448.  *----------------------------------------------------------------------
  449.  */
  450.  
  451. void
  452. TkpDestroyButton(
  453.     TkButton *butPtr)
  454. {
  455.     /* Do nothing. */
  456. }
  457.  
  458. /*
  459.  *--------------------------------------------------------------
  460.  *
  461.  * DrawBufferedControl --
  462.  *
  463.  *    This function uses a dummy Macintosh window to allow
  464.  *    drawing Mac controls to any GWorld (including off-screen
  465.  *    bitmaps).  In addition, this code may apply custom
  466.  *    colors passed in the TkButton.
  467.  *
  468.  * Results:
  469.  *    None.
  470.  *
  471.  * Side effects:
  472.  *    Control is to the GWorld.  Static state created on
  473.  *    first invocation of this routine.
  474.  *
  475.  *--------------------------------------------------------------
  476.  */
  477.  
  478. static void
  479. DrawBufferedControl(
  480.     TkButton *butPtr,        /* Tk button. */
  481.     GWorldPtr destPort)        /* Off screen GWorld. */
  482. {
  483.     ControlRef controlHandle;
  484.     CCTabHandle ccTabHandle;
  485.     int windowColorChanged = false;
  486.     RGBColor saveBackColor;
  487.  
  488.     if (windowRef == NULL) {
  489.     Rect geometry = {0, 0, 10, 10};
  490.     CWindowPeek windowList;
  491.  
  492.     /*
  493.      * Create a dummy window that we can draw to.  We will
  494.      * actually replace this windows bitmap with a the one
  495.      * we want to draw to at a later time.  This window and
  496.      * the data structures attached to it are only deallocated
  497.      * on exit of the application.
  498.      */
  499.     
  500.     windowRef = NewCWindow(NULL, &geometry, "\pempty", false, 
  501.         zoomDocProc, (WindowRef) -1, true, 0);
  502.     if (windowRef == NULL) {
  503.         panic("Can't allocate buffer window.");
  504.     }
  505.     
  506.     /*
  507.      * Now add the three standard controls to hidden window.  We
  508.      * only create one of each and reuse them for every widget in
  509.      * Tk.
  510.      */
  511.     
  512.     SetPort(windowRef);
  513.     buttonHandle = NewControl(windowRef, &geometry, "\p",
  514.         false, 1, 0, 1, pushButProc, (SInt32) 0);
  515.     checkHandle = NewControl(windowRef, &geometry, "\p",
  516.         false, 1, 0, 1, checkBoxProc, (SInt32) 0);
  517.     radioHandle = NewControl(windowRef, &geometry, "\p",
  518.         false, 1, 0, 1, radioButProc, (SInt32) 0);
  519.     ((CWindowPeek) windowRef)->visible = true;
  520.  
  521.     buttonTabHandle = (CCTabHandle) NewHandle(sizeof(CtlCTab));
  522.     checkTabHandle = (CCTabHandle) NewHandle(sizeof(CtlCTab));
  523.     radioTabHandle = (CCTabHandle) NewHandle(sizeof(CtlCTab));
  524.  
  525.     /*
  526.      * Remove our window from the window list.  This way our
  527.      * applications and others will not be confused that this
  528.      * window exists - but no one knows about it.
  529.      */
  530.  
  531.     windowList = (CWindowPeek) LMGetWindowList();
  532.     if (windowList == (CWindowPeek) windowRef) {
  533.         LMSetWindowList((WindowRef) windowList->nextWindow);
  534.     } else {
  535.         while ((windowList != NULL) 
  536.             && (windowList->nextWindow != (CWindowPeek) windowRef)) {
  537.         windowList = windowList->nextWindow;
  538.         }
  539.         if (windowList != NULL) {
  540.         windowList->nextWindow = windowList->nextWindow->nextWindow;
  541.         }
  542.     }
  543.     ((CWindowPeek) windowRef)->nextWindow = NULL;
  544.  
  545.     /* 
  546.      * Create an exit handler to clean up this mess if we our
  547.      * unloaded etc.  We need to remember the windows portPixMap
  548.      * so it isn't leaked.
  549.      *
  550.      * TODO: The ButtonExitProc doesn't currently work and the
  551.      * code it includes will crash the Mac on exit from Tk.
  552.      
  553.      oldPixPtr = ((CWindowPeek) windowRef)->port.portPixMap;
  554.      Tcl_CreateExitHandler(ButtonExitProc, (ClientData) NULL);
  555.      */
  556.     }
  557.     
  558.     /*
  559.      * Set up control in hidden window to match what we need
  560.      * to draw in the buffered window.
  561.      */
  562.  
  563.     switch (butPtr->type) {
  564.     case TYPE_BUTTON:
  565.         controlHandle = buttonHandle;
  566.         ccTabHandle = buttonTabHandle;
  567.         break;
  568.     case TYPE_RADIO_BUTTON:
  569.         controlHandle = radioHandle;
  570.         ccTabHandle = radioTabHandle;
  571.         break;
  572.     case TYPE_CHECK_BUTTON:
  573.         controlHandle = checkHandle;
  574.         ccTabHandle = checkTabHandle;
  575.         break;
  576.     }
  577.     (**controlHandle).contrlRect.left = butPtr->inset;
  578.     (**controlHandle).contrlRect.top = butPtr->inset;
  579.     (**controlHandle).contrlRect.right = Tk_Width(butPtr->tkwin) 
  580.         - butPtr->inset;
  581.     (**controlHandle).contrlRect.bottom = Tk_Height(butPtr->tkwin) 
  582.         - butPtr->inset;
  583.     if ((**controlHandle).contrlVis != 255) {
  584.     (**controlHandle).contrlVis = 255;
  585.     }
  586.     if (butPtr->flags & SELECTED) {
  587.     (**controlHandle).contrlValue = 1;
  588.     } else {
  589.     (**controlHandle).contrlValue = 0;
  590.     }
  591.     if (butPtr->state == tkActiveUid) {
  592.     switch (butPtr->type) {
  593.         case TYPE_BUTTON:
  594.         (**controlHandle).contrlHilite = kControlButtonPart;
  595.         break;
  596.         case TYPE_RADIO_BUTTON:
  597.         (**controlHandle).contrlHilite = kControlRadioButtonPart;
  598.         break;
  599.         case TYPE_CHECK_BUTTON:
  600.         (**controlHandle).contrlHilite = kControlCheckBoxPart;
  601.         break;
  602.     }
  603.     } else if (butPtr->state == tkDisabledUid) {
  604.     (**controlHandle).contrlHilite = kControlInactivePart;
  605.     } else {
  606.     (**controlHandle).contrlHilite = kControlNoPart;
  607.     }
  608.  
  609.     /*
  610.      * Now swap in the passed in GWorld for the portBits of our fake
  611.      * window.  We also adjust various fields in the WindowRecord to make
  612.      * the system think this is a normal window.
  613.      */
  614.  
  615.     ((CWindowPeek) windowRef)->port.portPixMap = destPort->portPixMap;
  616.     ((CWindowPeek) windowRef)->port.portRect = destPort->portRect;
  617.     RectRgn(((CWindowPeek) windowRef)->port.visRgn, &destPort->portRect);
  618.     RectRgn(((CWindowPeek) windowRef)->strucRgn, &destPort->portRect);
  619.     RectRgn(((CWindowPeek) windowRef)->updateRgn, &destPort->portRect);
  620.     RectRgn(((CWindowPeek) windowRef)->contRgn, &destPort->portRect);
  621.     PortChanged(windowRef);
  622.     
  623.     /*
  624.      * Before we draw the control we must add the hidden window back to the
  625.      * main window list.  Otherwise, radiobuttons and checkbuttons will draw
  626.      * incorrectly.  I don't really know why - but clearly the control draw
  627.      * proc needs to have the controls window in the window list.
  628.      */
  629.  
  630.     ((CWindowPeek) windowRef)->nextWindow = (CWindowPeek) LMGetWindowList();
  631.     LMSetWindowList(windowRef);
  632.  
  633.     /*
  634.      * Now we can set the port to our doctered up window.  We next need
  635.      * to muck with the colors for the port & window to draw the control
  636.      * with the proper Tk colors.  If we need to we also draw a default
  637.      * ring for buttons.
  638.      */
  639.  
  640.     SetPort(windowRef);
  641.     windowColorChanged = UpdateControlColors(butPtr, controlHandle, 
  642.     ccTabHandle, &saveBackColor);
  643.     Draw1Control(controlHandle);
  644.     if ((butPtr->type == TYPE_BUTTON) && 
  645.         (butPtr->defaultState == tkActiveUid)) {
  646.     Rect box = (**controlHandle).contrlRect;
  647.     RGBColor rgbColor;
  648.  
  649.     TkSetMacColor(butPtr->highlightColorPtr->pixel, &rgbColor);
  650.     RGBForeColor(&rgbColor);
  651.     PenSize(butPtr->highlightWidth - 1, butPtr->highlightWidth - 1);
  652.     InsetRect(&box, -butPtr->highlightWidth, -butPtr->highlightWidth);
  653.     FrameRoundRect(&box, 16, 16);
  654.     }
  655.     if (windowColorChanged) {
  656.     RGBColor dummyColor;
  657.     ChangeBackgroundWindowColor(windowRef, saveBackColor, &dummyColor);
  658.     }
  659.     
  660.     /*
  661.      * Clean up: remove the hidden window from the main window list.
  662.      */
  663.  
  664.     LMSetWindowList((WindowRef) ((CWindowPeek) windowRef)->nextWindow);
  665. }
  666.  
  667. /*
  668.  *--------------------------------------------------------------
  669.  *
  670.  * UpdateControlColors --
  671.  *
  672.  *    This function will review the colors used to display
  673.  *    a Macintosh button.  If any non-standard colors are
  674.  *    used we create a custom palette for the button, populate
  675.  *    with the colors for the button and install the palette.
  676.  *
  677.  * Results:
  678.  *    None.
  679.  *
  680.  * Side effects:
  681.  *    The Macintosh control may get a custom palette installed.
  682.  *
  683.  *--------------------------------------------------------------
  684.  */
  685.  
  686. static int
  687. UpdateControlColors(
  688.     TkButton *butPtr,
  689.     ControlRef controlHandle,
  690.     CCTabHandle ccTabHandle,
  691.     RGBColor *saveColorPtr)
  692. {
  693.     XColor *xcolor;
  694.     
  695.     xcolor = Tk_3DBorderColor(butPtr->normalBorder);
  696.  
  697.     (**ccTabHandle).ccSeed = 0;
  698.     (**ccTabHandle).ccRider = 0;
  699.     (**ccTabHandle).ctSize = 3;
  700.     (**ccTabHandle).ctTable[0].value = cBodyColor;
  701.     TkSetMacColor(xcolor->pixel,
  702.     &(**ccTabHandle).ctTable[0].rgb);
  703.     (**ccTabHandle).ctTable[1].value = cTextColor;
  704.     TkSetMacColor(butPtr->normalFg->pixel,
  705.     &(**ccTabHandle).ctTable[1].rgb);
  706.     (**ccTabHandle).ctTable[2].value = cFrameColor;
  707.     TkSetMacColor(butPtr->highlightColorPtr->pixel,
  708.     &(**ccTabHandle).ctTable[2].rgb);
  709.     SetControlColor(controlHandle, ccTabHandle);
  710.     
  711.     if (((xcolor->pixel >> 24) != CONTROL_BODY_PIXEL) && 
  712.         ((butPtr->type == TYPE_CHECK_BUTTON) ||
  713.             (butPtr->type == TYPE_RADIO_BUTTON))) {
  714.     RGBColor newColor;
  715.     
  716.     TkSetMacColor(xcolor->pixel, &newColor);
  717.     ChangeBackgroundWindowColor((**controlHandle).contrlOwner,
  718.         newColor, saveColorPtr);
  719.     return true;
  720.     }
  721.     
  722.     return false;
  723. }
  724.  
  725. /*
  726.  *--------------------------------------------------------------
  727.  *
  728.  * ChangeBackgroundWindowColor --
  729.  *
  730.  *    This procedure will change the background color entry
  731.  *    in the Window's colortable.  The system isn't notified
  732.  *    of the change.  This call should only be used to fool
  733.  *    the drawing routines for checkboxes and radiobuttons.
  734.  *    Any change should be temporary and be reverted after
  735.  *    the widget is drawn.
  736.  *
  737.  * Results:
  738.  *    None.
  739.  *
  740.  * Side effects:
  741.  *    The Window's color table will be adjusted.
  742.  *
  743.  *--------------------------------------------------------------
  744.  */
  745.  
  746. static void
  747. ChangeBackgroundWindowColor(
  748.     WindowRef macintoshWindow,    /* A Mac window whose color to change. */
  749.     RGBColor rgbColor,        /* The new RGB Color for the background. */
  750.     RGBColor *oldColor)        /* The old color of the background. */
  751. {
  752.     AuxWinHandle auxWinHandle;
  753.     WCTabHandle winCTabHandle;
  754.     short ctIndex;
  755.     ColorSpecPtr rgbScan;
  756.     
  757.     GetAuxWin(macintoshWindow, &auxWinHandle);
  758.     winCTabHandle = (WCTabHandle) ((**auxWinHandle).awCTable);
  759.  
  760.     /*
  761.      * Scan through the color table until we find the content
  762.      * (background) color for the window.  Don't tell the system
  763.      * about the change - it will generate damage and we will get
  764.      * into an infinite loop.
  765.      */
  766.  
  767.     ctIndex = (**winCTabHandle).ctSize;
  768.     while (ctIndex > -1) {
  769.     rgbScan = ctIndex + (**winCTabHandle).ctTable;
  770.  
  771.     if (rgbScan->value == wContentColor) {
  772.         *oldColor = rgbScan->rgb;
  773.         rgbScan->rgb = rgbColor;
  774.         break;
  775.     }
  776.     ctIndex--;
  777.     }
  778. }
  779.  
  780. /*
  781.  *----------------------------------------------------------------------
  782.  *
  783.  * ButtonExitProc --
  784.  *
  785.  *    This procedure is invoked just before the application exits.
  786.  *    It frees all of the control handles, our dummy window, etc.
  787.  *
  788.  * Results:
  789.  *    None.
  790.  *
  791.  * Side effects:
  792.  *    Memory is freed.
  793.  *
  794.  *----------------------------------------------------------------------
  795.  */
  796.  
  797. static void
  798. ButtonExitProc(clientData)
  799.     ClientData clientData;        /* Not used. */
  800. {
  801.     Rect pixRect = {0, 0, 10, 10};
  802.     Rect rgnRect = {0, 0, 0, 0};
  803.  
  804.     /*
  805.      * Restore our dummy window to it's origional state by putting it
  806.      * back in the window list and restoring it's bits.  The destroy
  807.      * the controls and window.
  808.      */
  809.  
  810.     ((CWindowPeek) windowRef)->nextWindow = (CWindowPeek) LMGetWindowList();
  811.     LMSetWindowList(windowRef);
  812.     ((CWindowPeek) windowRef)->port.portPixMap = oldPixPtr;
  813.     ((CWindowPeek) windowRef)->port.portRect = pixRect;
  814.     RectRgn(((CWindowPeek) windowRef)->port.visRgn, &rgnRect);
  815.     RectRgn(((CWindowPeek) windowRef)->strucRgn, &rgnRect);
  816.     RectRgn(((CWindowPeek) windowRef)->updateRgn, &rgnRect);
  817.     RectRgn(((CWindowPeek) windowRef)->contRgn, &rgnRect);
  818.     PortChanged(windowRef);
  819.  
  820.     DisposeControl(buttonHandle);
  821.     DisposeControl(checkHandle);
  822.     DisposeControl(radioHandle);
  823.     DisposeWindow(windowRef);
  824.     windowRef = NULL;
  825. }
  826.