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

通讯编程

开发平台:

Visual C++

  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.  * RCS: @(#) $Id: tkMacButton.c,v 1.17.2.3 2004/10/27 00:37:38 davygrvy Exp $
  13.  */
  14. #include "tkButton.h"
  15. #include "tkMacInt.h"
  16. #include <Controls.h>
  17. #include <ControlDefinitions.h>
  18. #include <LowMem.h>
  19. #include <Appearance.h>
  20. #include <ToolUtils.h>
  21. /*
  22.  * Some defines used to control what type of control is drawn.
  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. #define DRAW_BEVEL 3
  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. static WindowRef windowRef = NULL;
  35. static CWindowRecord windowRecord;
  36. static ControlRef buttonHandle;
  37. static ControlRef checkHandle;
  38. static ControlRef radioHandle;
  39. static ControlRef smallBevelHandle;
  40. static ControlRef smallStickyBevelHandle;
  41. static ControlRef medBevelHandle;
  42. static ControlRef medStickyBevelHandle;
  43. static ControlRef largeBevelHandle;
  44. static ControlRef largeStickyBevelHandle;
  45. /*
  46.  * These are used to store the image content for
  47.  * beveled buttons - i.e. buttons with images.
  48.  */
  49.  
  50. static ControlButtonContentInfo bevelButtonContent;
  51. static OpenCPicParams picParams;
  52. static CCTabHandle buttonTabHandle;
  53. static CCTabHandle checkTabHandle;
  54. static CCTabHandle radioTabHandle;
  55. static PixMapHandle oldPixPtr;
  56. /*
  57.  * These functions are used when Appearance is present.
  58.  * By embedding all our controls in a userPane control,
  59.  * we can color the background of the text in radiobuttons
  60.  * and checkbuttons.  Thanks to Peter Gontier of Apple DTS
  61.  * for help on this one.
  62.  */
  63. static ControlRef userPaneHandle;
  64. static RGBColor gUserPaneBackground = { ~0, ~0, ~0};
  65. static pascal OSErr SetUserPaneDrawProc(ControlRef control,
  66. ControlUserPaneDrawProcPtr upp);
  67. static pascal OSErr SetUserPaneSetUpSpecialBackgroundProc(ControlRef control,
  68. ControlUserPaneBackgroundProcPtr upp);
  69. static pascal void UserPaneDraw(ControlRef control, ControlPartCode cpc);
  70. static pascal void UserPaneBackgroundProc(ControlHandle,
  71. ControlBackgroundPtr info);
  72. /*
  73.  * Forward declarations for procedures defined later in this file:
  74.  */
  75. static void ButtonEventProc _ANSI_ARGS_((
  76.     ClientData clientData, XEvent *eventPtr));
  77. static int UpdateControlColors _ANSI_ARGS_((TkButton *butPtr,
  78. ControlRef controlHandle, CCTabHandle ccTabHandle,
  79. RGBColor *saveColorPtr));
  80. static void DrawBufferedControl _ANSI_ARGS_((TkButton *butPtr,
  81. GWorldPtr destPort, GC gc, Pixmap pixmap));
  82. static void InitSampleControls();
  83. static void SetupBevelButton _ANSI_ARGS_((TkButton *butPtr,
  84. ControlRef controlHandle, 
  85. GWorldPtr destPort, GC gc, Pixmap pixmap));
  86. static void ChangeBackgroundWindowColor _ANSI_ARGS_((
  87.     WindowRef macintoshWindow, RGBColor rgbColor,
  88.     RGBColor *oldColor));
  89. static void ButtonExitProc _ANSI_ARGS_((ClientData clientData));
  90. /*
  91.  * The class procedure table for the button widgets.
  92.  */
  93. Tk_ClassProcs tkpButtonProcs = { 
  94.     sizeof(Tk_ClassProcs), /* size */
  95.     TkButtonWorldChanged, /* worldChangedProc */
  96. };
  97. /*
  98.  *----------------------------------------------------------------------
  99.  *
  100.  * TkpCreateButton --
  101.  *
  102.  * Allocate a new TkButton structure.
  103.  *
  104.  * Results:
  105.  * Returns a newly allocated TkButton structure.
  106.  *
  107.  * Side effects:
  108.  * Registers an event handler for the widget.
  109.  *
  110.  *----------------------------------------------------------------------
  111.  */
  112. TkButton *
  113. TkpCreateButton(
  114.     Tk_Window tkwin)
  115. {
  116.     TkButton *buttonPtr;
  117.     buttonPtr = (TkButton *) ckalloc(sizeof(TkButton));
  118.     Tk_CreateEventHandler(tkwin, ActivateMask,
  119.     ButtonEventProc, (ClientData) buttonPtr);
  120.     return buttonPtr;
  121. }
  122. /*
  123.  *----------------------------------------------------------------------
  124.  *
  125.  * TkpDisplayButton --
  126.  *
  127.  * This procedure is invoked to display a button widget.  It is
  128.  * normally invoked as an idle handler.
  129.  *
  130.  * Results:
  131.  * None.
  132.  *
  133.  * Side effects:
  134.  * Commands are output to X to display the button in its
  135.  * current mode.  The REDRAW_PENDING flag is cleared.
  136.  *
  137.  *----------------------------------------------------------------------
  138.  */
  139. void
  140. TkpDisplayButton(
  141.     ClientData clientData) /* Information about widget. */
  142. {
  143.     TkButton *butPtr = (TkButton *) clientData;
  144.     Pixmap pixmap;
  145.     GC gc;
  146.     Tk_3DBorder border;
  147.     int x = 0; /* Initialization only needed to stop
  148.  * compiler warning. */
  149.     int y, relief;
  150.     register Tk_Window tkwin = butPtr->tkwin;
  151.     int width, height, fullWidth, fullHeight;
  152.     int textXOffset, textYOffset;
  153.     int haveImage = 0, haveText = 0;
  154.     int offset; /* 0 means this is a normal widget.  1 means
  155.  * it is an image button, so we offset the
  156.  * image to make the button appear to move
  157.  * up and down as the relief changes. */
  158.     int hasImageOrBitmap;
  159.     CGrafPtr saveWorld;
  160.     GDHandle saveDevice;
  161.     GWorldPtr destPort;
  162.     int drawType, borderWidth;
  163.     int imageWidth, imageHeight;
  164.     int imageXOffset = 0, imageYOffset = 0; /* image information that will
  165.      * be used to restrict disabled
  166.      * pixmap as well */
  167.     GetGWorld(&saveWorld, &saveDevice);
  168.     butPtr->flags &= ~REDRAW_PENDING;
  169.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  170. return;
  171.     }
  172.     /*
  173.      * In order to avoid screen flashes, this procedure redraws
  174.      * the button in a pixmap, then copies the pixmap to the
  175.      * screen in a single operation.  This means that there's no
  176.      * point in time where the on-sreen image has been cleared.
  177.      */
  178.     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
  179.     Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  180.     hasImageOrBitmap = ((butPtr->image != NULL) || (butPtr->bitmap != None));
  181.     offset = (butPtr->type == TYPE_BUTTON) && hasImageOrBitmap;
  182.     border = butPtr->normalBorder;
  183.     if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) {
  184. gc = butPtr->disabledGC;
  185.     } else if ((butPtr->type == TYPE_BUTTON)
  186.     && (butPtr->state == STATE_ACTIVE)) {
  187. gc = butPtr->activeTextGC;
  188. border = butPtr->activeBorder;
  189.     } else {
  190. gc = butPtr->normalTextGC;
  191.     }
  192.     
  193.     if ((butPtr->flags & SELECTED) && (butPtr->state != STATE_ACTIVE)
  194.     && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
  195. border = butPtr->selectBorder;
  196.     }
  197.     /*
  198.      * Override the relief specified for the button if this is a
  199.      * checkbutton or radiobutton and there's no indicator.
  200.      * However, don't do this in the presence of Appearance, since
  201.      * then the bevel button will take care of the relief.
  202.      */
  203.     relief = butPtr->relief;
  204.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) { 
  205. if (!TkMacHaveAppearance() || !hasImageOrBitmap) {
  206.     if (butPtr->flags & SELECTED) {
  207. relief = TK_RELIEF_SUNKEN;
  208.     } else if (butPtr->overRelief != relief) {
  209. relief = butPtr->offRelief;
  210.     }
  211. }
  212.     }
  213.     /*
  214.      * See the comment in UpdateControlColors as to why we use the 
  215.      * highlightbackground for the border of Macintosh buttons.
  216.      */
  217.      
  218.     if (butPtr->type == TYPE_BUTTON) {
  219. Tk_Fill3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0, 0,
  220. Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  221.     } else {
  222. Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, 0, 0,
  223. Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  224.     }
  225.    
  226.     if (butPtr->type == TYPE_LABEL) {
  227. drawType = DRAW_LABEL;
  228.     } else if (butPtr->type == TYPE_BUTTON) {
  229. if (!hasImageOrBitmap) {
  230.     drawType = DRAW_CONTROL;
  231. } else if (butPtr->image != None) {
  232.     drawType = DRAW_BEVEL;
  233. } else {
  234.     /*
  235.      * TO DO - The current way the we draw bitmaps (XCopyPlane)
  236.      * uses CopyDeepMask in this one case.  The Picture recording 
  237.      * does not record this call, and so we can't use the
  238.      * Appearance bevel button here.  The only case that would
  239.      * exercise this is if you use a bitmap, with
  240.      * -data & -mask specified.  We should probably draw the 
  241.      * appearance button and overprint the image in this case.
  242.      * This just punts and draws the old-style, ugly, button.
  243.      */
  244.      
  245.     if (gc->clip_mask == 0) {
  246. drawType = DRAW_BEVEL;
  247.     } else {
  248. TkpClipMask *clipPtr = (TkpClipMask*) gc->clip_mask;
  249. if ((clipPtr->type == TKP_CLIP_PIXMAP) &&
  250. (clipPtr->value.pixmap != butPtr->bitmap)) {
  251.     drawType = DRAW_CUSTOM;
  252. } else {
  253.     drawType = DRAW_BEVEL;
  254. }
  255.     }
  256. }
  257.     } else {
  258. if (butPtr->indicatorOn) {
  259.     drawType = DRAW_CONTROL;
  260. } else if (hasImageOrBitmap) {
  261.     if (gc->clip_mask == 0) {
  262. drawType = DRAW_BEVEL;
  263.     } else {
  264. TkpClipMask *clipPtr = (TkpClipMask*) gc->clip_mask;
  265. if ((clipPtr->type == TKP_CLIP_PIXMAP) &&
  266. (clipPtr->value.pixmap != butPtr->bitmap)) {
  267.     drawType = DRAW_CUSTOM;
  268. } else {
  269.     drawType = DRAW_BEVEL;
  270. }
  271.     }
  272. } else {
  273.     drawType = DRAW_CUSTOM;
  274. }
  275.     }
  276.     /*
  277.      * Draw the native portion of the buttons. Start by creating the control
  278.      * if it doesn't already exist.  Then configure the Macintosh control from
  279.      * the Tk info.  Finally, we call Draw1Control to draw to the screen.
  280.      */
  281.     if ((drawType == DRAW_CONTROL) || 
  282.     ((drawType == DRAW_BEVEL) && TkMacHaveAppearance())) {
  283. borderWidth = 0;
  284. /*
  285.  * This part uses Macintosh rather than Tk calls to draw
  286.  * to the screen.  Make sure the ports etc. are set correctly.
  287.  */
  288. destPort = TkMacGetDrawablePort(pixmap);
  289. SetGWorld(destPort, NULL);
  290. DrawBufferedControl(butPtr, destPort, gc, pixmap);
  291.     }
  292.     if ((drawType == DRAW_CUSTOM) || (drawType == DRAW_LABEL)) {
  293. borderWidth = butPtr->borderWidth;
  294.     }
  295.     /*
  296.      * Display image or bitmap or text for button.  This has
  297.      * already been done under Appearance with the Bevel
  298.      * button types.
  299.      */
  300.     if ((drawType == DRAW_BEVEL) && TkMacHaveAppearance()) {
  301. /* Empty Body */
  302.     } else {
  303. if (butPtr->image != None) {
  304.     Tk_SizeOfImage(butPtr->image, &width, &height);
  305.     haveImage = 1;
  306. } else if (butPtr->bitmap != None) {
  307.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  308.     haveImage = 1;
  309. }
  310. imageWidth  = width;
  311. imageHeight = height;
  312. haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0);
  313. if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
  314.     textXOffset = 0;
  315.     textYOffset = 0;
  316.     fullWidth = 0;
  317.     fullHeight = 0;
  318.     
  319.     switch ((enum compound) butPtr->compound) {
  320. case COMPOUND_TOP: 
  321. case COMPOUND_BOTTOM: {
  322.     /* Image is above or below text */
  323.     if (butPtr->compound == COMPOUND_TOP) {
  324. textYOffset = height + butPtr->padY;
  325.     } else {
  326. imageYOffset = butPtr->textHeight + butPtr->padY;
  327.     }
  328.     fullHeight = height + butPtr->textHeight + butPtr->padY;
  329.     fullWidth = (width > butPtr->textWidth ? width :
  330.     butPtr->textWidth);
  331.     textXOffset = (fullWidth - butPtr->textWidth)/2;
  332.     imageXOffset = (fullWidth - width)/2;
  333.     break;
  334. }
  335. case COMPOUND_LEFT:
  336. case COMPOUND_RIGHT: {
  337.     /* Image is left or right of text */
  338.     if (butPtr->compound == COMPOUND_LEFT) {
  339. textXOffset = width + butPtr->padX;
  340.     } else {
  341. imageXOffset = butPtr->textWidth + butPtr->padX;
  342.     }
  343.     fullWidth = butPtr->textWidth + butPtr->padX + width;
  344.     fullHeight = (height > butPtr->textHeight ? height :
  345.     butPtr->textHeight);
  346.     textYOffset = (fullHeight - butPtr->textHeight)/2;
  347.     imageYOffset = (fullHeight - height)/2;
  348.     break;
  349. }
  350. case COMPOUND_CENTER: {
  351.     /* Image and text are superimposed */
  352.     fullWidth = (width > butPtr->textWidth ? width :
  353.     butPtr->textWidth);
  354.     fullHeight = (height > butPtr->textHeight ? height :
  355.     butPtr->textHeight);
  356.     textXOffset = (fullWidth - butPtr->textWidth)/2;
  357.     imageXOffset = (fullWidth - width)/2;
  358.     textYOffset = (fullHeight - butPtr->textHeight)/2;
  359.     imageYOffset = (fullHeight - height)/2;
  360.     break;
  361. }
  362. case COMPOUND_NONE: {break;}
  363.     }
  364.     
  365.     TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
  366.     butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y);
  367.     
  368.     x += butPtr->indicatorSpace;
  369.     
  370.     x += offset;
  371.     y += offset;
  372.     if (relief == TK_RELIEF_RAISED) {
  373. x -= offset;
  374. y -= offset;
  375.     } else if (relief == TK_RELIEF_SUNKEN) {
  376. x += offset;
  377. y += offset;
  378.     }
  379.     imageXOffset += x;
  380.     imageYOffset += y;
  381.     if (butPtr->image != NULL) {
  382. if ((butPtr->selectImage != NULL) &&
  383. (butPtr->flags & SELECTED)) {
  384.     Tk_RedrawImage(butPtr->selectImage, 0, 0,
  385.     width, height, pixmap, imageXOffset, imageYOffset);
  386. } else {
  387.     Tk_RedrawImage(butPtr->image, 0, 0, width,
  388.     height, pixmap, imageXOffset, imageYOffset);
  389. }
  390.     } else {
  391. XSetClipOrigin(butPtr->display, gc,
  392. imageXOffset, imageYOffset);
  393. XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc,
  394. 0, 0, (unsigned int) width, (unsigned int) height,
  395. imageXOffset, imageYOffset, 1);
  396. XSetClipOrigin(butPtr->display, gc, 0, 0);
  397.     }
  398.     
  399.     Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
  400.     x + textXOffset, y + textYOffset, 0, -1);
  401.     Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
  402.     butPtr->textLayout, x + textXOffset, y + textYOffset,
  403.     butPtr->underline);
  404.     y += fullHeight/2;
  405. } else {
  406.     if (haveImage) {
  407. TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
  408. butPtr->indicatorSpace + width, height, &x, &y);
  409. x += butPtr->indicatorSpace;
  410. x += offset;
  411. y += offset;
  412. if (relief == TK_RELIEF_RAISED) {
  413.     x -= offset;
  414.     y -= offset;
  415. } else if (relief == TK_RELIEF_SUNKEN) {
  416.     x += offset;
  417.     y += offset;
  418. }
  419. imageXOffset += x;
  420. imageYOffset += y;
  421. if (butPtr->image != NULL) {
  422.     if ((butPtr->selectImage != NULL) &&
  423.     (butPtr->flags & SELECTED)) {
  424. Tk_RedrawImage(butPtr->selectImage, 0, 0, width,
  425. height, pixmap, imageXOffset, imageYOffset);
  426.     } else {
  427. Tk_RedrawImage(butPtr->image, 0, 0, width, height,
  428. pixmap, imageXOffset, imageYOffset);
  429.     }
  430. } else {
  431.     XSetClipOrigin(butPtr->display, gc, x, y);
  432.     XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc,
  433.     0, 0, (unsigned int) width,
  434.     (unsigned int) height, x, y, 1);
  435.     XSetClipOrigin(butPtr->display, gc, 0, 0);
  436. }
  437. y += height/2;
  438.     } else {
  439. TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX,
  440. butPtr->padY,
  441. butPtr->indicatorSpace + butPtr->textWidth,
  442. butPtr->textHeight, &x, &y);
  443. x += butPtr->indicatorSpace;
  444. Tk_DrawTextLayout(butPtr->display, pixmap, gc,
  445. butPtr->textLayout, x, y, 0, -1);
  446. y += butPtr->textHeight/2;
  447.     }
  448. }
  449.     }
  450.     /*
  451.      * If the button is disabled with a stipple rather than a special
  452.      * foreground color, generate the stippled effect. If the widget
  453.      * is selected and we use a different background color when selected,
  454.      * must temporarily modify the GC so the stippling is the right color.
  455.      */
  456.     if ((butPtr->state == STATE_DISABLED)
  457.     && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
  458. if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  459. && (butPtr->selectBorder != NULL)) {
  460.     XSetForeground(butPtr->display, butPtr->stippleGC,
  461.     Tk_3DBorderColor(butPtr->selectBorder)->pixel);
  462. }
  463. /*
  464.  * Stipple the whole button if no disabledFg was specified,
  465.  * otherwise restrict stippling only to displayed image
  466.  */
  467. if (butPtr->disabledFg == NULL) {
  468.     XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 0, 0,
  469.     (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin));
  470. } else {
  471.     XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC,
  472.     imageXOffset, imageYOffset,
  473.     (unsigned) imageWidth, (unsigned) imageHeight);
  474. }
  475. if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  476. && (butPtr->selectBorder != NULL)) {
  477.     XSetForeground(butPtr->display, butPtr->stippleGC,
  478.     Tk_3DBorderColor(butPtr->normalBorder)->pixel);
  479. }
  480.     }
  481.     /*
  482.      * Draw the border and traversal highlight last.  This way, if the
  483.      * button's contents overflow they'll be covered up by the border.
  484.      */
  485.     if (relief != TK_RELIEF_FLAT) {
  486. int inset = butPtr->highlightWidth;
  487. Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
  488. Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
  489. butPtr->borderWidth, relief);
  490.     }
  491.     /*
  492.      * Copy the information from the off-screen pixmap onto the screen,
  493.      * then delete the pixmap.
  494.      */
  495.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  496.     butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  497.     (unsigned) Tk_Height(tkwin), 0, 0);
  498.     Tk_FreePixmap(butPtr->display, pixmap);
  499.     SetGWorld(saveWorld, saveDevice);
  500. }
  501. /*
  502.  *----------------------------------------------------------------------
  503.  *
  504.  * TkpComputeButtonGeometry --
  505.  *
  506.  * After changes in a button's text or bitmap, this procedure
  507.  * recomputes the button's geometry and passes this information
  508.  * along to the geometry manager for the window.
  509.  *
  510.  * Results:
  511.  * None.
  512.  *
  513.  * Side effects:
  514.  * The button's window may change size.
  515.  *
  516.  *----------------------------------------------------------------------
  517.  */
  518. void
  519. TkpComputeButtonGeometry(
  520.     TkButton *butPtr) /* Button whose geometry may have changed. */
  521. {
  522.     int width, height, avgWidth, haveImage = 0, haveText = 0;
  523.     int txtWidth, txtHeight;
  524.     Tk_FontMetrics fm;
  525.     width = 0;
  526.     height = 0;
  527.     txtWidth = 0;
  528.     txtHeight = 0;
  529.     avgWidth = 0;
  530.     /*
  531.      * First figure out the size of the contents of the button.
  532.      */
  533.      
  534.     butPtr->indicatorSpace = 0;
  535.     if (butPtr->image != NULL) {
  536. Tk_SizeOfImage(butPtr->image, &width, &height);
  537. haveImage = 1;
  538.     } else if (butPtr->bitmap != None) {
  539. Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  540. haveImage = 1;
  541.     }
  542.     if (haveImage == 0 || butPtr->compound != COMPOUND_NONE) {
  543. Tk_FreeTextLayout(butPtr->textLayout);
  544. butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
  545. Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength,
  546. butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight);
  547. txtWidth = butPtr->textWidth;
  548. txtHeight = butPtr->textHeight;
  549. avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
  550. Tk_GetFontMetrics(butPtr->tkfont, &fm);
  551. haveText = (txtWidth != 0 && txtHeight != 0);
  552.     }
  553.     /*
  554.      * If the button is compound (ie, it shows both an image and text),
  555.      * the new geometry is a combination of the image and text geometry.
  556.      * We only honor the compound bit if the button has both text and an
  557.      * image, because otherwise it is not really a compound button.
  558.      */
  559.     if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
  560. switch ((enum compound) butPtr->compound) {
  561.     case COMPOUND_TOP:
  562.     case COMPOUND_BOTTOM: {
  563. /* Image is above or below text */
  564. height += txtHeight + butPtr->padY;
  565. width = (width > txtWidth ? width : txtWidth);
  566. break;
  567.     }
  568.     case COMPOUND_LEFT:
  569.     case COMPOUND_RIGHT: {
  570. /* Image is left or right of text */
  571. width += txtWidth + butPtr->padX;
  572. height = (height > txtHeight ? height : txtHeight);
  573. break;
  574.     }
  575.     case COMPOUND_CENTER: {
  576. /* Image and text are superimposed */
  577. width = (width > txtWidth ? width : txtWidth);
  578. height = (height > txtHeight ? height : txtHeight);
  579. break;
  580.     }
  581.     case COMPOUND_NONE: {break;}
  582. }
  583. if (butPtr->width > 0) {
  584.     width = butPtr->width;
  585. }
  586. if (butPtr->height > 0) {
  587.     height = butPtr->height;
  588. }
  589. if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  590.     butPtr->indicatorSpace = height;
  591.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  592. butPtr->indicatorDiameter = (65*height)/100;
  593.     } else {
  594. butPtr->indicatorDiameter = (75*height)/100;
  595.     }
  596. }
  597. width += 2*butPtr->padX;
  598. height += 2*butPtr->padY;
  599.     } else {
  600. if (haveImage) {
  601.     if (butPtr->width > 0) {
  602. width = butPtr->width;
  603.     }
  604.     if (butPtr->height > 0) {
  605. height = butPtr->height;
  606.     }
  607.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  608. butPtr->indicatorSpace = height;
  609. if (butPtr->type == TYPE_CHECK_BUTTON) {
  610.     butPtr->indicatorDiameter = (65*height)/100;
  611. } else {
  612.     butPtr->indicatorDiameter = (75*height)/100;
  613. }
  614.     }
  615. } else {
  616.     width = txtWidth;
  617.     height = txtHeight;
  618.     if (butPtr->width > 0) {
  619. width = butPtr->width * avgWidth;
  620.     }
  621.     if (butPtr->height > 0) {
  622. height = butPtr->height * fm.linespace;
  623.     }
  624.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  625. butPtr->indicatorDiameter = fm.linespace;
  626. if (butPtr->type == TYPE_CHECK_BUTTON) {
  627.     butPtr->indicatorDiameter =
  628. (80*butPtr->indicatorDiameter)/100;
  629. }
  630. butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
  631.     }
  632. }
  633.     }
  634.     /*
  635.      * Now figure out the size of the border decorations for the button.
  636.      */
  637.      
  638.     if (butPtr->highlightWidth < 0) {
  639. butPtr->highlightWidth = 0;
  640.     }
  641.     
  642.     /*
  643.      * The width and height calculation for Appearance buttons with images & 
  644.      * non-Appearance buttons with images is different.  In the latter case, 
  645.      * we add the borderwidth to the inset, since we are going to stamp a
  646.      * 3-D border over the image.  In the former, we add it to the height,
  647.      * directly, since Appearance will draw the border as part of our control.
  648.      *
  649.      * When issuing the geometry request, add extra space for the indicator,
  650.      * if any, and for the border and padding, plus if this is an image two 
  651.      * extra pixels so the display can be offset by 1 pixel in either
  652.      * direction for the raised or lowered effect.
  653.      *
  654.      * The highlight width corresponds to the default ring on the Macintosh.
  655.      * As such, the highlight width is only added if the button is the default
  656.      * button. The actual width of the default ring is one less than the
  657.      * highlight width as there is also one pixel of spacing.
  658.      * Appearance buttons with images do not have a highlight ring, because the 
  659.      * Bevel button type does not support one.
  660.      */
  661.     if ((butPtr->image == None) && (butPtr->bitmap == None)) {
  662. width += 2*butPtr->padX;
  663. height += 2*butPtr->padY;
  664.     }
  665.     
  666.     if ((butPtr->type == TYPE_BUTTON)) {
  667. if ((butPtr->image == None) && (butPtr->bitmap == None)) {
  668.     butPtr->inset = 0;
  669.     if (butPtr->defaultState != STATE_DISABLED) {
  670. butPtr->inset += butPtr->highlightWidth;
  671.     }
  672. } else if (TkMacHaveAppearance()) {
  673.     butPtr->inset = 0;
  674.     width += (2 * butPtr->borderWidth + 4);
  675.     height += (2 * butPtr->borderWidth + 4);
  676. } else {
  677.     butPtr->inset = butPtr->borderWidth;
  678.     width += 2;
  679.     height += 2;
  680.     if (butPtr->defaultState != STATE_DISABLED) {
  681. butPtr->inset += butPtr->highlightWidth;
  682.     }
  683. }
  684.     } else if ((butPtr->type != TYPE_LABEL)) {
  685. if (butPtr->indicatorOn) {
  686.     butPtr->inset = 0;
  687. } else {
  688.     /*
  689.      * Under Appearance, the Checkbutton or radiobutton with an image
  690.      * is represented by a BevelButton with the Sticky defProc...  
  691.      * So we must set its height in the same way as the Button 
  692.      * with an image or bitmap.
  693.      */
  694.     if (((butPtr->image != None) || (butPtr->bitmap != None))
  695.     && TkMacHaveAppearance()) {
  696. int border;
  697. butPtr->inset = 0;
  698. if ( butPtr->borderWidth <= 2 ) {
  699.     border = 6;
  700. }  else {
  701.     border = 2 * butPtr->borderWidth + 2;
  702. }        
  703. width += border;
  704. height += border;
  705.     } else {
  706. butPtr->inset = butPtr->borderWidth;
  707.     }
  708. }
  709.     } else {
  710. butPtr->inset = butPtr->borderWidth;
  711.     }
  712.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
  713.     + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
  714.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
  715. }
  716. /*
  717.  *----------------------------------------------------------------------
  718.  *
  719.  * TkpDestroyButton --
  720.  *
  721.  * Free data structures associated with the button control.
  722.  *
  723.  * Results:
  724.  * None.
  725.  *
  726.  * Side effects:
  727.  * Restores the default control state.
  728.  *
  729.  *----------------------------------------------------------------------
  730.  */
  731. void
  732. TkpDestroyButton(
  733.     TkButton *butPtr)
  734. {
  735.     /* Do nothing. */
  736. }
  737. /*
  738.  *--------------------------------------------------------------
  739.  *
  740.  * DrawBufferedControl --
  741.  *
  742.  * This function uses a dummy Macintosh window to allow
  743.  * drawing Mac controls to any GWorld (including off-screen
  744.  * bitmaps).  In addition, this code may apply custom
  745.  * colors passed in the TkButton.
  746.  *
  747.  * Results:
  748.  * None.
  749.  *
  750.  * Side effects:
  751.  * Control is to the GWorld.  Static state created on
  752.  * first invocation of this routine.
  753.  *
  754.  *--------------------------------------------------------------
  755.  */
  756. static void
  757. DrawBufferedControl(
  758.     TkButton *butPtr, /* Tk button. */
  759.     GWorldPtr destPort, /* Off screen GWorld. */
  760.     GC gc, /* The GC we are drawing into - needed for
  761.  * the bevel button */
  762.     Pixmap pixmap /* The pixmap we are drawing into - needed
  763.    for the bevel button */
  764.     )
  765. {
  766.     ControlRef controlHandle;
  767.     CCTabHandle ccTabHandle;
  768.     int windowColorChanged = false;
  769.     RGBColor saveBackColor;
  770.     int isBevel = 0;
  771.    
  772.     if (windowRef == NULL) {
  773. InitSampleControls();
  774.     }
  775.     
  776.     /*
  777.      * Now swap in the passed in GWorld for the portBits of our fake
  778.      * window. We also adjust various fields in the WindowRecord to make
  779.      * the system think this is a normal window.
  780.      * Note, we can use DrawControlInCurrentPort under Appearance, so we don't
  781.      * need to swap pixmaps.
  782.      */
  783.     
  784.     if (!TkMacHaveAppearance()) {
  785. ((CWindowPeek) windowRef)->port.portPixMap = destPort->portPixMap;
  786.     }
  787.     
  788.     ((CWindowPeek) windowRef)->port.portRect = destPort->portRect;
  789.     RectRgn(((CWindowPeek) windowRef)->port.visRgn, &destPort->portRect);
  790.     RectRgn(((CWindowPeek) windowRef)->strucRgn, &destPort->portRect);
  791.     RectRgn(((CWindowPeek) windowRef)->updateRgn, &destPort->portRect);
  792.     RectRgn(((CWindowPeek) windowRef)->contRgn, &destPort->portRect);
  793.     PortChanged(windowRef);
  794.     
  795.     /*
  796.      * Set up control in hidden window to match what we need
  797.      * to draw in the buffered window.
  798.      */
  799.      
  800.     isBevel = 0;   
  801.     switch (butPtr->type) {
  802. case TYPE_BUTTON:
  803.     if (TkMacHaveAppearance()) {
  804. if ((butPtr->image == None) && (butPtr->bitmap == None)) {
  805.     controlHandle = buttonHandle;
  806.     ccTabHandle = buttonTabHandle;
  807. } else {
  808.     if (butPtr->borderWidth <= 2) {
  809. controlHandle = smallBevelHandle;
  810.     } else if (butPtr->borderWidth == 3) {
  811. controlHandle = medBevelHandle;
  812.     } else {
  813. controlHandle = largeBevelHandle;
  814.     }
  815.     ccTabHandle = buttonTabHandle;
  816.     SetupBevelButton(butPtr, controlHandle, destPort, 
  817.     gc, pixmap);
  818.     isBevel = 1;
  819. }
  820.     } else {
  821. controlHandle = buttonHandle;
  822. ccTabHandle = buttonTabHandle;
  823.     }
  824.     break;
  825. case TYPE_RADIO_BUTTON:
  826.     if (TkMacHaveAppearance()) {
  827. if (((butPtr->image == None) && (butPtr->bitmap == None))
  828. || (butPtr->indicatorOn)) {
  829.     controlHandle = radioHandle;
  830.     ccTabHandle = radioTabHandle;
  831. } else {
  832.     if (butPtr->borderWidth <= 2) {
  833. controlHandle = smallStickyBevelHandle;
  834.     } else if (butPtr->borderWidth == 3) {
  835. controlHandle = medStickyBevelHandle;
  836.     } else {
  837. controlHandle = largeStickyBevelHandle;
  838.     }
  839.     ccTabHandle = radioTabHandle;
  840.     SetupBevelButton(butPtr, controlHandle, destPort, 
  841.     gc, pixmap);
  842.     isBevel = 1;
  843. }
  844.     } else {
  845. controlHandle = radioHandle;
  846. ccTabHandle = radioTabHandle;
  847.     }        
  848.     break;
  849. case TYPE_CHECK_BUTTON:
  850.     if (TkMacHaveAppearance()) {
  851. if (((butPtr->image == None) && (butPtr->bitmap == None))
  852. || (butPtr->indicatorOn)) {
  853.     controlHandle = checkHandle;
  854.     ccTabHandle = checkTabHandle;
  855. } else {
  856.     if (butPtr->borderWidth <= 2) {
  857. controlHandle = smallStickyBevelHandle;
  858.     } else if (butPtr->borderWidth == 3) {
  859. controlHandle = medStickyBevelHandle;
  860.     } else {
  861. controlHandle = largeStickyBevelHandle;
  862.     }
  863.     ccTabHandle = checkTabHandle;
  864.     SetupBevelButton(butPtr, controlHandle, destPort, 
  865.     gc, pixmap);
  866.     isBevel = 1;
  867. }
  868.     } else {
  869. controlHandle = checkHandle;
  870. ccTabHandle = checkTabHandle;
  871.     }        
  872.     break;
  873.     }
  874.     
  875.     (**controlHandle).contrlRect.left = butPtr->inset;
  876.     (**controlHandle).contrlRect.top = butPtr->inset;
  877.     (**controlHandle).contrlRect.right = Tk_Width(butPtr->tkwin) 
  878. - butPtr->inset;
  879.     (**controlHandle).contrlRect.bottom = Tk_Height(butPtr->tkwin) 
  880. - butPtr->inset;
  881.     
  882.     /*
  883.      * Setting the control visibility by hand does not 
  884.      * seem to work under Appearance. 
  885.      */
  886.      
  887.     if (TkMacHaveAppearance()) {
  888. SetControlVisibility(controlHandle, true, false);      
  889. (**userPaneHandle).contrlRect.left = 0;
  890. (**userPaneHandle).contrlRect.top = 0;
  891. (**userPaneHandle).contrlRect.right = Tk_Width(butPtr->tkwin);
  892. (**userPaneHandle).contrlRect.bottom = Tk_Height(butPtr->tkwin);
  893.     } else {   
  894. (**controlHandle).contrlVis = 255;
  895.     }   
  896.     
  897.     
  898.     if (butPtr->flags & SELECTED) {
  899. (**controlHandle).contrlValue = 1;
  900.     } else {
  901. (**controlHandle).contrlValue = 0;
  902.     }
  903.     
  904.     if (!tkMacAppInFront || butPtr->state == STATE_DISABLED) {
  905. (**controlHandle).contrlHilite = kControlInactivePart;
  906.     } else if (butPtr->state == STATE_ACTIVE) {
  907. if (isBevel) {
  908.     (**controlHandle).contrlHilite = kControlButtonPart;
  909. } else {
  910.     switch (butPtr->type) {
  911. case TYPE_BUTTON:
  912.     (**controlHandle).contrlHilite = kControlButtonPart;
  913.     break;
  914. case TYPE_RADIO_BUTTON:
  915.     (**controlHandle).contrlHilite = kControlRadioButtonPart;
  916.     break;
  917. case TYPE_CHECK_BUTTON:
  918.     (**controlHandle).contrlHilite = kControlCheckBoxPart;
  919.     break;
  920.     }
  921. }
  922.     } else {
  923. (**controlHandle).contrlHilite = kControlNoPart;
  924.     }
  925.     /*
  926.      * Before we draw the control we must add the hidden window back to the
  927.      * main window list.  Otherwise, radiobuttons and checkbuttons will draw
  928.      * incorrectly.  I don't really know why - but clearly the control draw
  929.      * proc needs to have the controls window in the window list.  This is not
  930.      * necessary under Appearance, and will have to go when we Carbonize Tk...
  931.      */
  932.     if (!TkMacHaveAppearance()) {
  933.         ((CWindowPeek) windowRef)->nextWindow = (CWindowPeek) LMGetWindowList();
  934.         LMSetWindowList(windowRef);
  935.     }
  936.     /*
  937.      * Now we can set the port to our doctered up window.  We next need
  938.      * to muck with the colors for the port & window to draw the control
  939.      * with the proper Tk colors.  If we need to we also draw a default
  940.      * ring for buttons.
  941.      * Under Appearance, we draw the control directly into destPort, and
  942.      * just set the default control data.
  943.      */
  944.     if (TkMacHaveAppearance()) {
  945. SetPort((GrafPort *) destPort);
  946.     } else {
  947. SetPort(windowRef);
  948.     }
  949.     windowColorChanged = UpdateControlColors(butPtr, controlHandle, 
  950.     ccTabHandle, &saveBackColor);
  951.     if ((butPtr->type == TYPE_BUTTON) && TkMacHaveAppearance()) {
  952. Boolean isDefault;
  953. if (butPtr->defaultState == STATE_ACTIVE) {
  954.     isDefault = true;
  955. } else {
  956.     isDefault = false;
  957. }
  958. SetControlData(controlHandle, kControlNoPart, 
  959. kControlPushButtonDefaultTag,
  960. sizeof(isDefault), (Ptr) &isDefault);
  961.     }
  962.     if (TkMacHaveAppearance()) {
  963. DrawControlInCurrentPort(userPaneHandle);
  964.     } else {
  965. Draw1Control(controlHandle);
  966.     }
  967.     if (!TkMacHaveAppearance() &&
  968.     (butPtr->type == TYPE_BUTTON) && 
  969.     (butPtr->defaultState == STATE_ACTIVE)) {
  970. Rect box = (**controlHandle).contrlRect;
  971. RGBColor rgbColor;
  972. TkSetMacColor(butPtr->highlightColorPtr->pixel, &rgbColor);
  973. RGBForeColor(&rgbColor);
  974. PenSize(butPtr->highlightWidth - 1, butPtr->highlightWidth - 1);
  975. InsetRect(&box, -butPtr->highlightWidth, -butPtr->highlightWidth);
  976. FrameRoundRect(&box, 16, 16);
  977.     }
  978.     
  979.     if (windowColorChanged) {
  980. RGBColor dummyColor;
  981. ChangeBackgroundWindowColor(windowRef, saveBackColor, &dummyColor);
  982.     }
  983.     
  984.     /*
  985.      * Clean up: remove the hidden window from the main window list, and
  986.      * hide the control we drew.  
  987.      */
  988.     if (TkMacHaveAppearance()) {
  989. SetControlVisibility(controlHandle, false, false);
  990. if (isBevel) {
  991.     KillPicture(bevelButtonContent.u.picture);
  992. }     
  993.     } else {   
  994. (**controlHandle).contrlVis = 0;
  995.     }   
  996.     if (!TkMacHaveAppearance()) {    
  997.         LMSetWindowList((WindowRef) ((CWindowPeek) windowRef)->nextWindow);
  998.     }
  999. }
  1000. /*
  1001.  *--------------------------------------------------------------
  1002.  *
  1003.  * InitSampleControls --
  1004.  *
  1005.  * This function initializes a dummy Macintosh window and
  1006.  * sample controls to allow drawing Mac controls to any GWorld 
  1007.  * (including off-screen bitmaps).  
  1008.  *
  1009.  * Results:
  1010.  * None.
  1011.  *
  1012.  * Side effects:
  1013.  * Controls & a window are created.
  1014.  *
  1015.  *--------------------------------------------------------------
  1016.  */
  1017. static void
  1018. InitSampleControls()
  1019. {
  1020.     Rect geometry = {0, 0, 10, 10};
  1021.     CWindowPeek windowList;
  1022.     GWorldPtr frontWin = NULL;
  1023.     TkMacWindowList *winListPtr;
  1024.     /*
  1025.      * Create a dummy window that we can draw to.  We will
  1026.      * actually replace this window's bitmap with the one
  1027.      * we want to draw to at a later time.  This window and
  1028.      * the data structures attached to it are only deallocated
  1029.      * on exit of the application.
  1030.      */
  1031.     /* 
  1032.      * This is a bit of a hack...  The problem is that under appearance,
  1033.      * taking a window out of the window list causes instability, so we can't 
  1034.      * do that.  OTOH, we need to make sure that this window is NEVER the front
  1035.      * window, or we may inadvertently send keystrokes to it...
  1036.      * So we put it BEHIND ".", and then we won't ever be able to destroy
  1037.      * ALL the windows that are above it.
  1038.      */
  1039.       
  1040.     for (winListPtr = tkMacWindowListPtr; winListPtr != NULL; 
  1041.             winListPtr = winListPtr->nextPtr) {
  1042.         frontWin = ((MacDrawable *) tkMacWindowListPtr->winPtr->privatePtr)->portPtr;
  1043.         if (strcmp(tkMacWindowListPtr->winPtr->pathName, ".") == 0) {
  1044.             break;
  1045.         }
  1046.     }
  1047.     
  1048.     windowRef = NewCWindow(NULL, &geometry, "pempty", false, 
  1049.     zoomDocProc, (WindowRef) frontWin, true, 0);
  1050.     if (windowRef == NULL) {
  1051. panic("Can't allocate buffer window.");
  1052.     }
  1053.     /*
  1054.      * Now add the three standard controls to hidden window.  We
  1055.      * only create one of each and reuse them for every widget in
  1056.      * Tk.
  1057.      * Under Appearance, we have to embed the controls in a UserPane
  1058.      * control, so that we can color the background text in 
  1059.      * radiobuttons and checkbuttons.
  1060.      */
  1061.     SetPort(windowRef);
  1062.     if (TkMacHaveAppearance()) {
  1063.     
  1064. OSErr err;
  1065. ControlRef dontCare;
  1066.     
  1067.   /* 
  1068.    * Adding UserPaneBackgroundProcs to the root control does
  1069.  * not seem to work, so we have to add another UserPane to 
  1070.  * the root control.
  1071.  */
  1072.      
  1073. err = CreateRootControl(windowRef, &dontCare);
  1074. if (err != noErr) {
  1075.     panic("Can't create root control in DrawBufferedControl");
  1076. }
  1077.     
  1078. userPaneHandle = NewControl(windowRef, &geometry, "p",
  1079. true, kControlSupportsEmbedding|kControlHasSpecialBackground, 
  1080. 0, 1, kControlUserPaneProc, (SInt32) 0);
  1081. SetUserPaneSetUpSpecialBackgroundProc(userPaneHandle,
  1082. UserPaneBackgroundProc);
  1083. SetUserPaneDrawProc(userPaneHandle, UserPaneDraw);
  1084. buttonHandle = NewControl(windowRef, &geometry, "p",
  1085. false, 1, 0, 1, kControlPushButtonProc, (SInt32) 0);
  1086. EmbedControl(buttonHandle, userPaneHandle);
  1087. checkHandle = NewControl(windowRef, &geometry, "p",
  1088. false, 1, 0, 1, kControlCheckBoxProc, (SInt32) 0);
  1089. EmbedControl(checkHandle, userPaneHandle);
  1090. radioHandle = NewControl(windowRef, &geometry, "p",
  1091. false, 1, 0, 1, kControlRadioButtonProc, (SInt32) 0);
  1092. EmbedControl(radioHandle, userPaneHandle);
  1093. smallBevelHandle = NewControl(windowRef, &geometry, "p",
  1094. false, 0, 0, 
  1095. kControlBehaviorOffsetContents << 16
  1096. | kControlContentPictHandle, 
  1097. kControlBevelButtonSmallBevelProc, (SInt32) 0);
  1098. EmbedControl(smallBevelHandle, userPaneHandle);
  1099. medBevelHandle = NewControl(windowRef, &geometry, "p",
  1100. false, 0, 0, 
  1101. kControlBehaviorOffsetContents << 16
  1102. | kControlContentPictHandle, 
  1103. kControlBevelButtonNormalBevelProc, (SInt32) 0);
  1104. EmbedControl(medBevelHandle, userPaneHandle);
  1105. largeBevelHandle = NewControl(windowRef, &geometry, "p",
  1106. false, 0, 0, 
  1107. kControlBehaviorOffsetContents << 16
  1108. | kControlContentPictHandle, 
  1109. kControlBevelButtonLargeBevelProc, (SInt32) 0);
  1110. EmbedControl(largeBevelHandle, userPaneHandle);
  1111. bevelButtonContent.contentType = kControlContentPictHandle;
  1112. smallStickyBevelHandle = NewControl(windowRef, &geometry, "p",
  1113. false, 0, 0, 
  1114. (kControlBehaviorOffsetContents
  1115. | kControlBehaviorSticky) << 16 
  1116. | kControlContentPictHandle, 
  1117. kControlBevelButtonSmallBevelProc, (SInt32) 0);
  1118. EmbedControl(smallStickyBevelHandle, userPaneHandle);
  1119. medStickyBevelHandle = NewControl(windowRef, &geometry, "p",
  1120. false, 0, 0, 
  1121. (kControlBehaviorOffsetContents
  1122. | kControlBehaviorSticky) << 16 
  1123. | kControlContentPictHandle, 
  1124. kControlBevelButtonNormalBevelProc, (SInt32) 0);
  1125. EmbedControl(medStickyBevelHandle, userPaneHandle);
  1126. largeStickyBevelHandle = NewControl(windowRef, &geometry, "p",
  1127. false, 0, 0, 
  1128. (kControlBehaviorOffsetContents
  1129. | kControlBehaviorSticky) << 16 
  1130. | kControlContentPictHandle, 
  1131. kControlBevelButtonLargeBevelProc, (SInt32) 0);
  1132. EmbedControl(largeStickyBevelHandle, userPaneHandle);
  1133.     
  1134. picParams.version = -2;
  1135. picParams.hRes = 0x00480000;
  1136. picParams.vRes = 0x00480000;
  1137. picParams.srcRect.top = 0;
  1138. picParams.srcRect.left = 0;
  1139.     
  1140. ((CWindowPeek) windowRef)->visible = true;
  1141.     } else {
  1142. buttonHandle = NewControl(windowRef, &geometry, "p",
  1143. false, 1, 0, 1, pushButProc, (SInt32) 0);
  1144. checkHandle = NewControl(windowRef, &geometry, "p",
  1145. false, 1, 0, 1, checkBoxProc, (SInt32) 0);
  1146. radioHandle = NewControl(windowRef, &geometry, "p",
  1147. false, 1, 0, 1, radioButProc, (SInt32) 0);
  1148. ((CWindowPeek) windowRef)->visible = true;
  1149. buttonTabHandle = (CCTabHandle) NewHandle(sizeof(CtlCTab));
  1150. checkTabHandle = (CCTabHandle) NewHandle(sizeof(CtlCTab));
  1151. radioTabHandle = (CCTabHandle) NewHandle(sizeof(CtlCTab));
  1152.     }
  1153.     /*
  1154.      * Remove our window from the window list. This way our
  1155.      * applications and others will not be confused that this
  1156.      * window exists - but no one knows about it.
  1157.      * I actually don't see the point of doing this, and it causes Floating
  1158.      * Window support to crash under Appearance, so I just leave it out.
  1159.      * Note that we have to do without this under Carbon, since you can't
  1160.      * go poking at the window list...
  1161.      */
  1162.     if (!TkMacHaveAppearance()) {
  1163.         windowList = (CWindowPeek) LMGetWindowList();
  1164.         if (windowList == (CWindowPeek) windowRef) {
  1165.     LMSetWindowList((WindowRef) windowList->nextWindow);
  1166.         } else {
  1167.     while ((windowList != NULL) 
  1168.     && (windowList->nextWindow != (CWindowPeek) windowRef)) {
  1169.         windowList = windowList->nextWindow;
  1170.     }
  1171.     if (windowList != NULL) {
  1172.         windowList->nextWindow = windowList->nextWindow->nextWindow;
  1173.     }
  1174.         }
  1175.         ((CWindowPeek) windowRef)->nextWindow = NULL;
  1176.     }
  1177.     /* 
  1178.      * Create an exit handler to clean up this mess if we our
  1179.      * unloaded etc.  We need to remember the windows portPixMap
  1180.      * so it isn't leaked.
  1181.      *
  1182.      * TODO: The ButtonExitProc doesn't currently work and the
  1183.      * code it includes will crash the Mac on exit from Tk.
  1184.  
  1185.      oldPixPtr = ((CWindowPeek) windowRef)->port.portPixMap;
  1186.      TkCreateExitHandler(ButtonExitProc, (ClientData) NULL);
  1187.     */
  1188. }
  1189. /*
  1190.  *--------------------------------------------------------------
  1191.  *
  1192.  * SetupBevelButton --
  1193.  *
  1194.  * Sets up the Bevel Button with image by copying the
  1195.  * source image onto the PicHandle for the button.
  1196.  *
  1197.  * Results:
  1198.  * None
  1199.  *
  1200.  * Side effects:
  1201.  * The image or bitmap for the button is copied over to a picture.
  1202.  *
  1203.  *--------------------------------------------------------------
  1204.  */
  1205. void
  1206. SetupBevelButton(
  1207.     TkButton *butPtr, /* Tk button. */
  1208.     ControlRef controlHandle,  /* The control to set this picture to */
  1209.     GWorldPtr destPort, /* Off screen GWorld. */
  1210.     GC gc, /* The GC we are drawing into - needed for
  1211.  * the bevel button */
  1212.     Pixmap pixmap /* The pixmap we are drawing into - needed
  1213.    for the bevel button */
  1214.     )
  1215. {
  1216.     int height, width;
  1217.     ControlButtonGraphicAlignment theAlignment;
  1218.     
  1219.     SetPort((GrafPtr) destPort);
  1220.     if (butPtr->image != None) {
  1221. Tk_SizeOfImage(butPtr->image, 
  1222. &width, &height);
  1223.     } else {
  1224. Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, 
  1225. &width, &height);
  1226.     }
  1227.     
  1228.     if ((butPtr->width > 0) && (butPtr->width < width)) {
  1229. width = butPtr->width;
  1230.     }
  1231.     if ((butPtr->height > 0) && (butPtr->height < height)) {
  1232. height = butPtr->height;
  1233.     }
  1234.     
  1235.     picParams.srcRect.right = width;
  1236.     picParams.srcRect.bottom = height;
  1237.     
  1238.     bevelButtonContent.u.picture = OpenCPicture(&picParams);
  1239.     
  1240.     /*
  1241.      * TO DO - There is one case where XCopyPlane calls CopyDeepMask,
  1242.      * which does not get recorded in the picture.  So the bitmap code
  1243.      * will fail in that case.
  1244.      */
  1245.      
  1246.     if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
  1247. Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height,
  1248. pixmap, 0, 0);
  1249.     } else if (butPtr->image != NULL) {
  1250. Tk_RedrawImage(butPtr->image, 0, 0, width, 
  1251. height, pixmap, 0, 0);
  1252.     } else {
  1253. XSetClipOrigin(butPtr->display, gc, 0, 0);
  1254. XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
  1255. (unsigned int) width, (unsigned int) height, 0, 0, 1);
  1256.     }
  1257.     
  1258.     ClosePicture();
  1259.     
  1260.     SetControlData(controlHandle, kControlButtonPart,
  1261.     kControlBevelButtonContentTag,
  1262.     sizeof(ControlButtonContentInfo),
  1263.     (char *) &bevelButtonContent);
  1264.     
  1265.     if (butPtr->anchor == TK_ANCHOR_N) {
  1266. theAlignment = kControlBevelButtonAlignTop;
  1267.     } else if (butPtr->anchor == TK_ANCHOR_NE) { 
  1268. theAlignment = kControlBevelButtonAlignTopRight;
  1269.     } else if (butPtr->anchor == TK_ANCHOR_E) { 
  1270. theAlignment = kControlBevelButtonAlignRight;
  1271.     } else if (butPtr->anchor == TK_ANCHOR_SE) {
  1272. theAlignment = kControlBevelButtonAlignBottomRight;
  1273.     } else if (butPtr->anchor == TK_ANCHOR_S) {
  1274. theAlignment = kControlBevelButtonAlignBottom;
  1275.     } else if (butPtr->anchor == TK_ANCHOR_SW) {
  1276. theAlignment = kControlBevelButtonAlignBottomLeft;
  1277.     } else if (butPtr->anchor == TK_ANCHOR_W) {
  1278. theAlignment = kControlBevelButtonAlignLeft;
  1279.     } else if (butPtr->anchor == TK_ANCHOR_NW) {
  1280. theAlignment = kControlBevelButtonAlignTopLeft;
  1281.     } else if (butPtr->anchor == TK_ANCHOR_CENTER) {
  1282. theAlignment = kControlBevelButtonAlignCenter;
  1283.     }
  1284.     SetControlData(controlHandle, kControlButtonPart,
  1285.     kControlBevelButtonGraphicAlignTag,
  1286.     sizeof(ControlButtonGraphicAlignment),
  1287.     (char *) &theAlignment);
  1288. }
  1289. /*
  1290.  *--------------------------------------------------------------
  1291.  *
  1292.  * SetUserPaneDrawProc --
  1293.  *
  1294.  * Utility function to add a UserPaneDrawProc
  1295.  * to a userPane control. From MoreControls code
  1296.  * from Apple DTS.
  1297.  *
  1298.  * Results:
  1299.  * MacOS system error.
  1300.  *
  1301.  * Side effects:
  1302.  * The user pane gets a new UserPaneDrawProc.
  1303.  *
  1304.  *--------------------------------------------------------------
  1305.  */
  1306. pascal OSErr SetUserPaneDrawProc (
  1307.     ControlRef control,
  1308.     ControlUserPaneDrawProcPtr upp)
  1309. {
  1310.     ControlUserPaneDrawUPP myControlUserPaneDrawUPP;
  1311.     myControlUserPaneDrawUPP = NewControlUserPaneDrawProc(upp);
  1312.     return SetControlData (control, 
  1313.     kControlNoPart, kControlUserPaneDrawProcTag, 
  1314.     sizeof(myControlUserPaneDrawUPP), 
  1315.     (Ptr) &myControlUserPaneDrawUPP);
  1316. }
  1317. /*
  1318.  *--------------------------------------------------------------
  1319.  *
  1320.  * SetUserPaneSetUpSpecialBackgroundProc --
  1321.  *
  1322.  * Utility function to add a UserPaneBackgroundProc
  1323.  * to a userPane control
  1324.  *
  1325.  * Results:
  1326.  * MacOS system error.
  1327.  *
  1328.  * Side effects:
  1329.  * The user pane gets a new UserPaneBackgroundProc.
  1330.  *
  1331.  *--------------------------------------------------------------
  1332.  */
  1333. pascal OSErr
  1334. SetUserPaneSetUpSpecialBackgroundProc(
  1335.     ControlRef control, 
  1336.     ControlUserPaneBackgroundProcPtr upp)
  1337. {
  1338.     ControlUserPaneBackgroundUPP myControlUserPaneBackgroundUPP;
  1339.     myControlUserPaneBackgroundUPP = NewControlUserPaneBackgroundProc(upp);
  1340.     return SetControlData (control, kControlNoPart, 
  1341.     kControlUserPaneBackgroundProcTag, 
  1342.     sizeof(myControlUserPaneBackgroundUPP), 
  1343.     (Ptr) &myControlUserPaneBackgroundUPP);
  1344. }
  1345. /*
  1346.  *--------------------------------------------------------------
  1347.  *
  1348.  * UserPaneDraw --
  1349.  *
  1350.  * This function draws the background of the user pane that will 
  1351.  * lie under checkboxes and radiobuttons.
  1352.  *
  1353.  * Results:
  1354.  * None.
  1355.  *
  1356.  * Side effects:
  1357.  * The user pane gets updated to the current color.
  1358.  *
  1359.  *--------------------------------------------------------------
  1360.  */
  1361. pascal void
  1362. UserPaneDraw(
  1363.     ControlRef control,
  1364.     ControlPartCode cpc)
  1365. {
  1366.     Rect contrlRect = (**control).contrlRect;
  1367.     RGBBackColor (&gUserPaneBackground);
  1368.     EraseRect (&contrlRect);
  1369. }
  1370. /*
  1371.  *--------------------------------------------------------------
  1372.  *
  1373.  * UserPaneBackgroundProc --
  1374.  *
  1375.  * This function sets up the background of the user pane that will 
  1376.  * lie under checkboxes and radiobuttons.
  1377.  *
  1378.  * Results:
  1379.  * None.
  1380.  *
  1381.  * Side effects:
  1382.  * The user pane background gets set to the current color.
  1383.  *
  1384.  *--------------------------------------------------------------
  1385.  */
  1386. pascal void
  1387. UserPaneBackgroundProc(
  1388.     ControlHandle,
  1389.     ControlBackgroundPtr info)
  1390. {
  1391.     if (info->colorDevice) {
  1392. RGBBackColor (&gUserPaneBackground);
  1393.     }
  1394. }
  1395. /*
  1396.  *--------------------------------------------------------------
  1397.  *
  1398.  * UpdateControlColors --
  1399.  *
  1400.  * This function will review the colors used to display
  1401.  * a Macintosh button.  If any non-standard colors are
  1402.  * used we create a custom palette for the button, populate
  1403.  * with the colors for the button and install the palette.
  1404.  *
  1405.  * Under Appearance, we just set the pointer that will be
  1406.  * used by the UserPaneDrawProc.
  1407.  *
  1408.  * Results:
  1409.  * None.
  1410.  *
  1411.  * Side effects:
  1412.  * The Macintosh control may get a custom palette installed.
  1413.  *
  1414.  *--------------------------------------------------------------
  1415.  */
  1416. static int
  1417. UpdateControlColors(
  1418.     TkButton *butPtr,
  1419.     ControlRef controlHandle,
  1420.     CCTabHandle ccTabHandle,
  1421.     RGBColor *saveColorPtr)
  1422. {
  1423.     XColor *xcolor;
  1424.     
  1425.     /*
  1426.      * Under Appearance we cannot change the background of the
  1427.      * button itself.  However, the color we are setting is the color
  1428.      * of the containing userPane.  This will be the color that peeks 
  1429.      * around the rounded corners of the button.  
  1430.      * We make this the highlightbackground rather than the background,
  1431.      * because if you color the background of a frame containing a
  1432.      * button, you usually also color the highlightbackground as well,
  1433.      * or you will get a thin grey ring around the button.
  1434.      */
  1435.       
  1436.     if (TkMacHaveAppearance() && (butPtr->type == TYPE_BUTTON)) {
  1437. xcolor = Tk_3DBorderColor(butPtr->highlightBorder);
  1438.     } else {
  1439. xcolor = Tk_3DBorderColor(butPtr->normalBorder);
  1440.     }
  1441.     if (TkMacHaveAppearance()) {
  1442. TkSetMacColor(xcolor->pixel, &gUserPaneBackground);
  1443.     } else {
  1444. (**ccTabHandle).ccSeed = 0;
  1445. (**ccTabHandle).ccRider = 0;
  1446. (**ccTabHandle).ctSize = 3;
  1447. (**ccTabHandle).ctTable[0].value = cBodyColor;
  1448. TkSetMacColor(xcolor->pixel,
  1449. &(**ccTabHandle).ctTable[0].rgb);
  1450. (**ccTabHandle).ctTable[1].value = cTextColor;
  1451. TkSetMacColor(butPtr->normalFg->pixel,
  1452. &(**ccTabHandle).ctTable[1].rgb);
  1453. (**ccTabHandle).ctTable[2].value = cFrameColor;
  1454. TkSetMacColor(butPtr->highlightColorPtr->pixel,
  1455. &(**ccTabHandle).ctTable[2].rgb);
  1456. SetControlColor(controlHandle, ccTabHandle);
  1457. if (((xcolor->pixel >> 24) != CONTROL_BODY_PIXEL) && 
  1458. ((butPtr->type == TYPE_CHECK_BUTTON) ||
  1459. (butPtr->type == TYPE_RADIO_BUTTON))) {
  1460.     RGBColor newColor;
  1461.     if (TkSetMacColor(xcolor->pixel, &newColor)) {
  1462.     ChangeBackgroundWindowColor((**controlHandle).contrlOwner,
  1463.     newColor, saveColorPtr);
  1464.             }
  1465.     return true;
  1466. }
  1467.     }
  1468.     
  1469.     return false;
  1470. }
  1471. /*
  1472.  *--------------------------------------------------------------
  1473.  *
  1474.  * ChangeBackgroundWindowColor --
  1475.  *
  1476.  * This procedure will change the background color entry
  1477.  * in the Window's colortable.  The system isn't notified
  1478.  * of the change. This call should only be used to fool
  1479.  * the drawing routines for checkboxes and radiobuttons.
  1480.  * Any change should be temporary and be reverted after
  1481.  * the widget is drawn.
  1482.  *
  1483.  * Results:
  1484.  * None.
  1485.  *
  1486.  * Side effects:
  1487.  * The Window's color table will be adjusted.
  1488.  *
  1489.  *--------------------------------------------------------------
  1490.  */
  1491. static void
  1492. ChangeBackgroundWindowColor(
  1493.     WindowRef macintoshWindow, /* A Mac window whose color to change. */
  1494.     RGBColor rgbColor, /* The new RGB Color for the background. */
  1495.     RGBColor *oldColor) /* The old color of the background. */
  1496. {
  1497.     AuxWinHandle auxWinHandle;
  1498.     WCTabHandle winCTabHandle;
  1499.     short ctIndex;
  1500.     ColorSpecPtr rgbScan;
  1501.     GetAuxWin(macintoshWindow, &auxWinHandle);
  1502.     winCTabHandle = (WCTabHandle) ((**auxWinHandle).awCTable);
  1503.     /*
  1504.      * Scan through the color table until we find the content
  1505.      * (background) color for the window.  Don't tell the system
  1506.      * about the change - it will generate damage and we will get
  1507.      * into an infinite loop.
  1508.      */
  1509.     ctIndex = (**winCTabHandle).ctSize;
  1510.     while (ctIndex > -1) {
  1511. rgbScan = ctIndex + (**winCTabHandle).ctTable;
  1512. if (rgbScan->value == wContentColor) {
  1513.     *oldColor = rgbScan->rgb;
  1514.     rgbScan->rgb = rgbColor;
  1515.     break;
  1516. }
  1517. ctIndex--;
  1518.     }
  1519. }
  1520. /*
  1521.  *--------------------------------------------------------------
  1522.  *
  1523.  * ButtonEventProc --
  1524.  *
  1525.  * This procedure is invoked by the Tk dispatcher for various
  1526.  * events on buttons.
  1527.  *
  1528.  * Results:
  1529.  * None.
  1530.  *
  1531.  * Side effects:
  1532.  *      When it gets exposed, it is redisplayed.
  1533.  *
  1534.  *--------------------------------------------------------------
  1535.  */
  1536. static void
  1537. ButtonEventProc(
  1538.     ClientData clientData, /* Information about window. */
  1539.     XEvent *eventPtr) /* Information about event. */
  1540. {
  1541.     TkButton *buttonPtr = (TkButton *) clientData;
  1542.     if (eventPtr->type == ActivateNotify
  1543.             || eventPtr->type == DeactivateNotify) {
  1544.         if ((buttonPtr->tkwin == NULL) || (!Tk_IsMapped(buttonPtr->tkwin))) {
  1545.     return;
  1546.         }
  1547.         if ((buttonPtr->flags & REDRAW_PENDING) == 0) {
  1548.     Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) buttonPtr);
  1549.     buttonPtr->flags |= REDRAW_PENDING;
  1550.         }
  1551.     }
  1552. }
  1553. /*
  1554.  *----------------------------------------------------------------------
  1555.  *
  1556.  * ButtonExitProc --
  1557.  *
  1558.  * This procedure is invoked just before the application exits.
  1559.  * It frees all of the control handles, our dummy window, etc.
  1560.  *
  1561.  * Results:
  1562.  * None.
  1563.  *
  1564.  * Side effects:
  1565.  * Memory is freed.
  1566.  *
  1567.  *----------------------------------------------------------------------
  1568.  */
  1569. static void
  1570. ButtonExitProc(clientData)
  1571.     ClientData clientData; /* Not used. */
  1572. {
  1573.     Rect pixRect = {0, 0, 10, 10};
  1574.     Rect rgnRect = {0, 0, 0, 0};
  1575.     /*
  1576.      * Restore our dummy window to it's origional state by putting it
  1577.      * back in the window list and restoring it's bits.  The destroy
  1578.      * the controls and window.
  1579.      */
  1580.  
  1581.     ((CWindowPeek) windowRef)->nextWindow = (CWindowPeek) LMGetWindowList();
  1582.     LMSetWindowList(windowRef);
  1583.     ((CWindowPeek) windowRef)->port.portPixMap = oldPixPtr;
  1584.     ((CWindowPeek) windowRef)->port.portRect = pixRect;
  1585.     RectRgn(((CWindowPeek) windowRef)->port.visRgn, &rgnRect);
  1586.     RectRgn(((CWindowPeek) windowRef)->strucRgn, &rgnRect);
  1587.     RectRgn(((CWindowPeek) windowRef)->updateRgn, &rgnRect);
  1588.     RectRgn(((CWindowPeek) windowRef)->contRgn, &rgnRect);
  1589.     PortChanged(windowRef);
  1590.     DisposeControl(buttonHandle);
  1591.     DisposeControl(checkHandle);
  1592.     DisposeControl(radioHandle);
  1593.     DisposeWindow(windowRef);
  1594.     windowRef = NULL;
  1595. }