/* * tkMacOSXMenubutton.c -- * * This file implements the Macintosh specific portion of the * menubutton widget. * * Copyright (c) 1996 by Sun Microsystems, Inc. * Copyright 2001, Apple Computer, Inc. * Copyright (c) 2006-2007 Daniel A. Steffen * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tkMacOSXMenubutton.c,v 1.2.2.10 2007/04/29 02:26:49 das Exp $ */ #include "tkMacOSXInt.h" #include "tkMenu.h" #include "tkMenubutton.h" #include "tkMacOSXFont.h" #include "tkMacOSXDebug.h" #define kShadowOffset (3) /* amount to offset shadow from frame */ #define kTriangleWidth (11) /* width of the triangle */ #define kTriangleHeight (6) /* height of the triangle */ #define kTriangleMargin (5) /* margin around triangle */ #define TK_POPUP_OFFSET 32 /* size of popup marker */ MODULE_SCOPE int TkMacOSXGetNewMenuID(Tcl_Interp *interp, TkMenu *menuInstPtr, int cascade, short *menuIDPtr); MODULE_SCOPE void TkMacOSXFreeMenuID(short menuID); typedef struct { SInt16 initialValue; SInt16 minValue; SInt16 maxValue; SInt16 procID; int isBevel; } MenuButtonControlParams; typedef struct { int len; Str255 title; ControlFontStyleRec style; } ControlTitleParams; /* * Declaration of Mac specific button structure. */ typedef struct MacMenuButton { TkMenuButton info; /* Generic button info. */ WindowRef windowRef; ControlRef userPane; ControlRef control; MenuRef menuRef; unsigned long userPaneBackground; int flags; MenuButtonControlParams params; ControlTitleParams titleParams; ControlButtonContentInfo bevelButtonContent; OpenCPicParams picParams; } MacMenuButton; /* * Forward declarations for procedures defined later in this file: */ static OSStatus SetUserPaneDrawProc(ControlRef control, ControlUserPaneDrawProcPtr upp); static OSStatus SetUserPaneSetUpSpecialBackgroundProc(ControlRef control, ControlUserPaneBackgroundProcPtr upp); static void UserPaneDraw(ControlRef control, ControlPartCode cpc); static void UserPaneBackgroundProc(ControlHandle, ControlBackgroundPtr info); static int MenuButtonInitControl (MacMenuButton *mbPtr, Rect *paneRect, Rect *cntrRect ); static int UpdateControlColors(MacMenuButton *mbPtr); static void ComputeMenuButtonControlParams(TkMenuButton *mbPtr, MenuButtonControlParams * paramsPtr); static void ComputeControlTitleParams(TkMenuButton *mbPtr, ControlTitleParams *paramsPtr); static void CompareControlTitleParams(ControlTitleParams *p1Ptr, ControlTitleParams *p2Ptr, int *titleChanged, int *styleChanged); /* * The structure below defines menubutton class behavior by means of * procedures that can be invoked from generic window code. */ Tk_ClassProcs tkpMenubuttonClass = { sizeof(Tk_ClassProcs), /* size */ TkMenuButtonWorldChanged, /* worldChangedProc */ }; /* *---------------------------------------------------------------------- * * TkpCreateMenuButton -- * * Allocate a new TkMenuButton structure. * * Results: * Returns a newly allocated TkMenuButton structure. * * Side effects: * Registers an event handler for the widget. * *---------------------------------------------------------------------- */ TkMenuButton * TkpCreateMenuButton( Tk_Window tkwin) { MacMenuButton *mbPtr = (MacMenuButton *) ckalloc(sizeof(MacMenuButton)); mbPtr->userPaneBackground = PIXEL_MAGIC << 24; mbPtr->flags = 0; mbPtr->userPane = NULL; mbPtr->control = NULL; mbPtr->picParams.version = -2; mbPtr->picParams.hRes = 0x00480000; mbPtr->picParams.vRes = 0x00480000; mbPtr->picParams.srcRect.top = 0; mbPtr->picParams.srcRect.left = 0; mbPtr->picParams.reserved1 = 0; mbPtr->picParams.reserved2 = 0; mbPtr->bevelButtonContent.contentType = kControlContentPictHandle; mbPtr->menuRef = NULL; bzero(&mbPtr->params, sizeof(mbPtr->params)); bzero(&mbPtr->titleParams,sizeof(mbPtr->titleParams)); return (TkMenuButton *) mbPtr; } /* *---------------------------------------------------------------------- * * TkpDisplayMenuButton -- * * This procedure is invoked to display a menubutton widget. * * Results: * None. * * Side effects: * Commands are output to X to display the menubutton in its * current mode. * *---------------------------------------------------------------------- */ void TkpDisplayMenuButton( ClientData clientData) /* Information about widget. */ { TkMenuButton *butPtr = (TkMenuButton *) clientData; Tk_Window tkwin = butPtr->tkwin; TkWindow *winPtr; Pixmap pixmap; MacMenuButton *mbPtr = (MacMenuButton *) butPtr; CGrafPtr destPort, savePort; Boolean portChanged; int hasImageOrBitmap = 0, width, height; OSStatus err; ControlButtonGraphicAlignment theAlignment; Rect paneRect, cntrRect; butPtr->flags &= ~REDRAW_PENDING; if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } pixmap = (Pixmap) Tk_WindowId(tkwin); destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin)); portChanged = QDSwapPort(destPort, &savePort); TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin)); winPtr = (TkWindow *)butPtr->tkwin; paneRect.left = winPtr->privatePtr->xOff; paneRect.top = winPtr->privatePtr->yOff; paneRect.right = paneRect.left+Tk_Width(butPtr->tkwin)-1; paneRect.bottom = paneRect.top+Tk_Height(butPtr->tkwin)-1; cntrRect = paneRect; cntrRect.left += butPtr->inset; cntrRect.top += butPtr->inset; cntrRect.right -= butPtr->inset; cntrRect.bottom -= butPtr->inset; if (mbPtr->userPane) { MenuButtonControlParams params; bzero(¶ms, sizeof(params)); ComputeMenuButtonControlParams(butPtr, ¶ms ); if (bcmp(¶ms,&mbPtr->params,sizeof(params))) { if (mbPtr->userPane) { DisposeControl(mbPtr->userPane); mbPtr->userPane = NULL; mbPtr->control = NULL; } } } if (!mbPtr->userPane) { if (MenuButtonInitControl(mbPtr, &paneRect, &cntrRect)) { TkMacOSXDbgMsg("Init Control failed"); return; } } SetControlBounds(mbPtr->userPane,&paneRect); SetControlBounds(mbPtr->control,&cntrRect); /* * We need to cache the title and its style */ if (!(mbPtr->flags & 2)) { ControlTitleParams titleParams; int titleChanged; int styleChanged; ComputeControlTitleParams(butPtr,&titleParams); CompareControlTitleParams(&titleParams,&mbPtr->titleParams, &titleChanged,&styleChanged); if (titleChanged) { CFStringRef cf = CFStringCreateWithCString(NULL, (char*) titleParams.title, kCFStringEncodingUTF8); if (hasImageOrBitmap) { SetControlTitleWithCFString(mbPtr->control, cf); } else { SetMenuItemTextWithCFString(mbPtr->menuRef, 1, cf); } CFRelease(cf); bcopy(titleParams.title, mbPtr->titleParams.title, titleParams.len + 1); mbPtr->titleParams.len = titleParams.len; } if ((titleChanged||styleChanged) && titleParams .len) { if (hasImageOrBitmap) { err = ChkErr(SetControlFontStyle, mbPtr->control, &titleParams.style); if (err != noErr) { return; } } bcopy(&titleParams.style, &mbPtr->titleParams.style, sizeof(titleParams.style)); } } if (butPtr->image != None) { Tk_SizeOfImage(butPtr->image, &width, &height); hasImageOrBitmap = 1; } else if (butPtr->bitmap != None) { Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); hasImageOrBitmap = 1; } if (hasImageOrBitmap) { mbPtr->picParams.srcRect.right = width; mbPtr->picParams.srcRect.bottom = height; /* * Set the flag to circumvent clipping and bounds problems with OS * 10.0.4 */ tkPictureIsOpen = 1; mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams); if (!mbPtr->bevelButtonContent.u.picture) { TkMacOSXDbgMsg("OpenCPicture failed"); } /* * TO DO - There is one case where XCopyPlane calls CopyDeepMask, * which does not get recorded in the picture. So the bitmap code * will fail in that case. */ if (butPtr->image != NULL) { Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, 0, 0); } else { XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, NULL, 0, 0, width, height, 0, 0, 1); } ClosePicture(); tkPictureIsOpen = 0; ChkErr(SetControlData, mbPtr->control, kControlButtonPart, kControlBevelButtonContentTag, sizeof(ControlButtonContentInfo), (char *) &mbPtr->bevelButtonContent); switch (butPtr->anchor) { case TK_ANCHOR_N: theAlignment = kControlBevelButtonAlignTop; break; case TK_ANCHOR_NE: theAlignment = kControlBevelButtonAlignTopRight; break; case TK_ANCHOR_E: theAlignment = kControlBevelButtonAlignRight; break; case TK_ANCHOR_SE: theAlignment = kControlBevelButtonAlignBottomRight; break; case TK_ANCHOR_S: theAlignment = kControlBevelButtonAlignBottom; break; case TK_ANCHOR_SW: theAlignment = kControlBevelButtonAlignBottomLeft; break; case TK_ANCHOR_W: theAlignment = kControlBevelButtonAlignLeft; break; case TK_ANCHOR_NW: theAlignment = kControlBevelButtonAlignTopLeft; break; case TK_ANCHOR_CENTER: theAlignment = kControlBevelButtonAlignCenter; break; } ChkErr(SetControlData, mbPtr->control, kControlButtonPart, kControlBevelButtonGraphicAlignTag, sizeof(ControlButtonGraphicAlignment), (char *) &theAlignment); } if (butPtr->flags & GOT_FOCUS) { HiliteControl(mbPtr->control,kControlButtonPart); } else { HiliteControl(mbPtr->control,kControlNoPart); } UpdateControlColors(mbPtr); if (mbPtr->flags&2) { ShowControl(mbPtr->control); ShowControl(mbPtr->userPane); mbPtr->flags ^= 2; } else { Draw1Control(mbPtr->userPane); SetControlVisibility(mbPtr->control, true, true); } if (hasImageOrBitmap) { KillPicture(mbPtr->bevelButtonContent.u.picture); } if (portChanged) { QDSwapPort(savePort, NULL); } } /* *---------------------------------------------------------------------- * * TkpDestroyMenuButton -- * * Free data structures associated with the menubutton control. * * Results: * None. * * Side effects: * Restores the default control state. * *---------------------------------------------------------------------- */ void TkpDestroyMenuButton( TkMenuButton *mbPtr) { MacMenuButton *macMbPtr = (MacMenuButton *) mbPtr; if (macMbPtr->userPane) { DisposeControl(macMbPtr->userPane); macMbPtr->userPane = NULL; } if (macMbPtr->menuRef) { short menuID = GetMenuID(macMbPtr->menuRef); TkMacOSXFreeMenuID(menuID); DisposeMenu(macMbPtr->menuRef); macMbPtr->menuRef = NULL; } } /* *---------------------------------------------------------------------- * * TkpComputeMenuButtonGeometry -- * * After changes in a menu button's text or bitmap, this procedure * recomputes the menu button's geometry and passes this information * along to the geometry manager for the window. * * Results: * None. * * Side effects: * The menu button's window may change size. * *---------------------------------------------------------------------- */ void TkpComputeMenuButtonGeometry(mbPtr) register TkMenuButton *mbPtr; /* Widget record for menu button. */ { int width, height, mm, pixels; int hasImageOrBitmap = 0; mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth; if (mbPtr->image != None) { Tk_SizeOfImage(mbPtr->image, &width, &height); if (mbPtr->width > 0) { width = mbPtr->width; } if (mbPtr->height > 0) { height = mbPtr->height; } hasImageOrBitmap = 1; } else if (mbPtr->bitmap != None) { Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height); if (mbPtr->width > 0) { width = mbPtr->width; } if (mbPtr->height > 0) { height = mbPtr->height; } hasImageOrBitmap = 1; } else { hasImageOrBitmap = 0; Tk_FreeTextLayout(mbPtr->textLayout); mbPtr->textLayout = Tk_ComputeTextLayout(mbPtr->tkfont, mbPtr->text, -1, mbPtr->wrapLength, mbPtr->justify, 0, &mbPtr->textWidth, &mbPtr->textHeight); width = mbPtr->textWidth; height = mbPtr->textHeight; if (mbPtr->width > 0) { width = mbPtr->width * Tk_TextWidth(mbPtr->tkfont, "0", 1); } if (mbPtr->height > 0) { Tk_FontMetrics fm; Tk_GetFontMetrics(mbPtr->tkfont, &fm); height = mbPtr->height * fm.linespace; } width += 2*mbPtr->padX; height += 2*mbPtr->padY; } if (mbPtr->indicatorOn) { mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin)); pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin)); mbPtr->indicatorHeight = kTriangleHeight; mbPtr->indicatorWidth = kTriangleWidth + kTriangleMargin; width += mbPtr->indicatorWidth; } else { mbPtr->indicatorHeight = 0; mbPtr->indicatorWidth = 0; } if (!hasImageOrBitmap) { width += TK_POPUP_OFFSET; } Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset), (int) (height + 2*mbPtr->inset)); Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset); } /* *---------------------------------------------------------------------- * * ComputeMenuButtonControlParams -- * * This procedure computes the various parameters used * when creating a Carbon control (NewControl) * These are determined by the various tk menu button parameters * * Results: * None. * * Side effects: * Sets the control initialisation parameters * *---------------------------------------------------------------------- */ static void ComputeMenuButtonControlParams( TkMenuButton *mbPtr, MenuButtonControlParams *paramsPtr) { int fakeMenuID = 256; /* * Determine ProcID based on button type and dimensions * * We need to set minValue to some non-zero value, * Otherwise, the markers do not show up */ paramsPtr->minValue = kControlBehaviorMultiValueMenu; paramsPtr->maxValue = 0; if (mbPtr->image || mbPtr->bitmap) { paramsPtr->isBevel = 1; if (mbPtr->borderWidth <= 2) { paramsPtr->procID = kControlBevelButtonSmallBevelProc; } else if (mbPtr->borderWidth == 3) { paramsPtr->procID = kControlBevelButtonNormalBevelProc; } else { paramsPtr->procID = kControlBevelButtonLargeBevelProc; } if (mbPtr->indicatorOn) { paramsPtr->initialValue = fakeMenuID; } else { paramsPtr->initialValue = 0; } } else { paramsPtr->isBevel = 0; paramsPtr->procID = kControlPopupButtonProc + kControlPopupVariableWidthVariant; paramsPtr->minValue = -12345; paramsPtr->maxValue = -1; paramsPtr->initialValue = 0; } } /* *---------------------------------------------------------------------- * * returns 0 if same, 1 otherwise * *---------------------------------------------------------------------- */ static void CompareControlTitleParams( ControlTitleParams *p1Ptr, ControlTitleParams *p2Ptr, int *titleChanged, int *styleChanged) { if (p1Ptr->len != p2Ptr->len) { *titleChanged = 1; } else if (bcmp(p1Ptr->title,p2Ptr->title,p1Ptr->len)) { *titleChanged = 1; } else { *titleChanged = 0; } if (p1Ptr->len && p2Ptr->len) { *styleChanged = bcmp(&p1Ptr->style, &p2Ptr->style, sizeof(p2Ptr->style)); } else { *styleChanged = p1Ptr->len||p2Ptr->len; } } static void ComputeControlTitleParams( TkMenuButton *butPtr, ControlTitleParams *paramsPtr) { Tk_Font font; paramsPtr->len = TkFontGetFirstTextLayout(butPtr->textLayout, &font, (char*) paramsPtr->title); paramsPtr->title[paramsPtr->len] = 0; if (paramsPtr->len) { TkMacOSXInitControlFontStyle(font,¶msPtr->style); } } /* *---------------------------------------------------------------------- * * MenuButtonInitControl -- * * This procedure initialises a Carbon control * * Results: * 0 on success, 1 on failure. * * Side effects: * A background pane control and the control itself is created * The contol is embedded in the background control * The background control is embedded in the root control * of the containing window * The creation parameters for the control are also computed * *---------------------------------------------------------------------- */ int MenuButtonInitControl( MacMenuButton *mbPtr, /* Mac button. */ Rect *paneRect, Rect *cntrRect) { OSStatus err; TkMenuButton *butPtr = (TkMenuButton *) mbPtr; SInt16 procID, initialValue, minValue, maxValue; Boolean initiallyVisible; SInt32 controlReference; short menuID; ControlRef rootControl = TkMacOSXGetRootControl(Tk_WindowId(butPtr->tkwin)); mbPtr->windowRef = GetWindowFromPort( TkMacOSXGetDrawablePort(Tk_WindowId(butPtr->tkwin))); /* * Set up the user pane */ initiallyVisible = false; initialValue = kControlSupportsEmbedding | kControlHasSpecialBackground; minValue = 0; maxValue = 1; procID = kControlUserPaneProc; controlReference = (SInt32)mbPtr; mbPtr->userPane = NewControl(mbPtr->windowRef, paneRect, "\p", initiallyVisible, initialValue, minValue, maxValue, procID, controlReference); if (!mbPtr->userPane) { TkMacOSXDbgMsg("Failed to create user pane control"); return 1; } err = ChkErr(EmbedControl, mbPtr->userPane, rootControl); if (err != noErr) { return 1; } SetUserPaneSetUpSpecialBackgroundProc(mbPtr->userPane, UserPaneBackgroundProc); SetUserPaneDrawProc(mbPtr->userPane,UserPaneDraw); initiallyVisible = false; ComputeMenuButtonControlParams(butPtr,&mbPtr->params); /* * Do this only if we are using bevel buttons. */ ComputeControlTitleParams(butPtr,&mbPtr->titleParams); mbPtr->control = NewControl(mbPtr->windowRef, cntrRect, "\p" /* mbPtr->titleParams.title */, initiallyVisible, mbPtr->params.initialValue, mbPtr->params.minValue, mbPtr->params.maxValue, mbPtr->params.procID, controlReference); if (!mbPtr->control) { TkMacOSXDbgMsg("Failed to create control of type %d", mbPtr->params.procID); return 1; } err = ChkErr(EmbedControl, mbPtr->control, mbPtr->userPane); if (err != noErr ) { return 1; } if (mbPtr->params.isBevel) { CFStringRef cf = CFStringCreateWithCString(NULL, (char*) mbPtr->titleParams.title, kCFStringEncodingUTF8); SetControlTitleWithCFString(mbPtr->control, cf); CFRelease(cf); if (mbPtr->titleParams.len) { err = ChkErr(SetControlFontStyle, mbPtr->control, &mbPtr->titleParams.style); if (err != noErr) { return 1; } } } else { CFStringRef cfStr; err = TkMacOSXGetNewMenuID(mbPtr->info.interp, (TkMenu *) mbPtr, 0, &menuID); if (err != TCL_OK) { return 1; } err = ChkErr(CreateNewMenu, menuID, kMenuAttrDoNotUseUserCommandKeys, &(mbPtr->menuRef)); if (err != noErr) { return 1; } cfStr = CFStringCreateWithCString(NULL, Tk_PathName(mbPtr->info.tkwin), kCFStringEncodingUTF8); if (!cfStr) { TkMacOSXDbgMsg("CFStringCreateWithCString failed"); return 1; } err = ChkErr(SetMenuTitleWithCFString, mbPtr->menuRef, cfStr); CFRelease(cfStr); if (err != noErr) { return 1; } cfStr = CFStringCreateWithCString(NULL, (char*) mbPtr->titleParams.title, kCFStringEncodingUTF8); AppendMenuItemText(mbPtr->menuRef, "\px"); if (cfStr) { SetMenuItemTextWithCFString(mbPtr->menuRef, 1, cfStr); CFRelease(cfStr); } ChkErr(SetControlData, mbPtr->control, kControlNoPart, kControlPopupButtonMenuRefTag, sizeof(mbPtr->menuRef), &mbPtr->menuRef); SetControlMinimum(mbPtr->control, 1); SetControlMaximum(mbPtr->control, 1); SetControlValue(mbPtr->control, 1); } mbPtr->flags |= 2; return 0; } /* *-------------------------------------------------------------- * * SetUserPane * * Utility function to add a UserPaneDrawProc * to a userPane control. From MoreControls code * from Apple DTS. * * Results: * MacOS system error. * * Side effects: * The user pane gets a new UserPaneDrawProc. * *-------------------------------------------------------------- */ OSStatus SetUserPaneDrawProc( ControlRef control, ControlUserPaneDrawProcPtr upp) { ControlUserPaneDrawUPP myControlUserPaneDrawUPP = NewControlUserPaneDrawUPP(upp); return SetControlData(control, kControlNoPart,kControlUserPaneDrawProcTag, sizeof(myControlUserPaneDrawUPP), (Ptr)&myControlUserPaneDrawUPP); } /* *-------------------------------------------------------------- * * SetUserPaneSetUpSpecialBackgroundProc -- * * Utility function to add a UserPaneBackgroundProc * to a userPane control * * Results: * MacOS system error. * * Side effects: * The user pane gets a new UserPaneBackgroundProc. * *-------------------------------------------------------------- */ OSStatus SetUserPaneSetUpSpecialBackgroundProc( ControlRef control, ControlUserPaneBackgroundProcPtr upp) { ControlUserPaneBackgroundUPP myControlUserPaneBackgroundUPP = NewControlUserPaneBackgroundUPP(upp); return SetControlData(control, kControlNoPart, kControlUserPaneBackgroundProcTag, sizeof(myControlUserPaneBackgroundUPP), (Ptr) &myControlUserPaneBackgroundUPP); } /* *-------------------------------------------------------------- * * UserPaneDraw -- * * This function draws the background of the user pane that will * lie under checkboxes and radiobuttons. * * Results: * None. * * Side effects: * The user pane gets updated to the current color. * *-------------------------------------------------------------- */ void UserPaneDraw( ControlRef control, ControlPartCode cpc) { Rect contrlRect; MacMenuButton * mbPtr = (MacMenuButton *)(intptr_t)GetControlReference(control); GetControlBounds(control,&contrlRect); TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL); EraseRect (&contrlRect); } /* *-------------------------------------------------------------- * * UserPaneBackgroundProc -- * * This function sets up the background of the user pane that will * lie under checkboxes and radiobuttons. * * Results: * None. * * Side effects: * The user pane background gets set to the current color. * *-------------------------------------------------------------- */ void UserPaneBackgroundProc( ControlHandle control, ControlBackgroundPtr info) { MacMenuButton *mbPtr = (MacMenuButton *)(intptr_t)GetControlReference(control); if (info->colorDevice) { TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL); } } /* *-------------------------------------------------------------- * * UpdateControlColors -- * * This function will review the colors used to display * a Macintosh button. If any non-standard colors are * used we create a custom palette for the button, populate * with the colors for the button and install the palette. * * Under Appearance, we just set the pointer that will be * used by the UserPaneDrawProc. * * Results: * None. * * Side effects: * The Macintosh control may get a custom palette installed. * *-------------------------------------------------------------- */ static int UpdateControlColors( MacMenuButton *mbPtr) { XColor *xcolor; TkMenuButton * butPtr = ( TkMenuButton * )mbPtr; /* * Under Appearance we cannot change the background of the * button itself. However, the color we are setting is the color * of the containing userPane. This will be the color that peeks * around the rounded corners of the button. * We make this the highlightbackground rather than the background, * because if you color the background of a frame containing a * button, you usually also color the highlightbackground as well, * or you will get a thin grey ring around the button. */ xcolor = Tk_3DBorderColor(butPtr->normalBorder); mbPtr->userPaneBackground = xcolor->pixel; return false; }