1/* 2 * tkMacOSXMenubutton.c -- 3 * 4 * This file implements the Macintosh specific portion of the 5 * menubutton widget. 6 * 7 * Copyright (c) 1996 by Sun Microsystems, Inc. 8 * Copyright 2001, Apple Computer, Inc. 9 * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net> 10 * 11 * See the file "license.terms" for information on usage and redistribution 12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id: tkMacOSXMenubutton.c,v 1.2.2.12 2007/11/09 06:26:56 das Exp $ 15 */ 16 17#include "tkMacOSXPrivate.h" 18#include "tkMenu.h" 19#include "tkMenubutton.h" 20#include "tkMacOSXFont.h" 21#include "tkMacOSXDebug.h" 22 23#define kShadowOffset (3) /* amount to offset shadow from frame */ 24#define kTriangleWidth (11) /* width of the triangle */ 25#define kTriangleHeight (6) /* height of the triangle */ 26#define kTriangleMargin (5) /* margin around triangle */ 27 28#define TK_POPUP_OFFSET 32 /* size of popup marker */ 29 30#define FIRST_DRAW 2 31#define ACTIVE 4 32 33MODULE_SCOPE int TkMacOSXGetNewMenuID(Tcl_Interp *interp, TkMenu *menuInstPtr, 34 int cascade, short *menuIDPtr); 35MODULE_SCOPE void TkMacOSXFreeMenuID(short menuID); 36 37typedef struct { 38 SInt16 initialValue; 39 SInt16 minValue; 40 SInt16 maxValue; 41 SInt16 procID; 42 int isBevel; 43} MenuButtonControlParams; 44 45typedef struct { 46 int len; 47 Str255 title; 48 ControlFontStyleRec style; 49} ControlTitleParams; 50 51/* 52 * Declaration of Mac specific button structure. 53 */ 54 55typedef struct MacMenuButton { 56 TkMenuButton info; /* Generic button info. */ 57 WindowRef windowRef; 58 ControlRef userPane; 59 ControlRef control; 60 MenuRef menuRef; 61 unsigned long userPaneBackground; 62 int flags; 63 MenuButtonControlParams params; 64 ControlTitleParams titleParams; 65 ControlButtonContentInfo bevelButtonContent; 66 OpenCPicParams picParams; 67} MacMenuButton; 68 69/* 70 * Forward declarations for procedures defined later in this file: 71 */ 72 73static OSStatus SetUserPaneDrawProc(ControlRef control, 74 ControlUserPaneDrawProcPtr upp); 75static OSStatus SetUserPaneSetUpSpecialBackgroundProc(ControlRef control, 76 ControlUserPaneBackgroundProcPtr upp); 77static void UserPaneDraw(ControlRef control, ControlPartCode cpc); 78static void UserPaneBackgroundProc(ControlHandle, 79 ControlBackgroundPtr info); 80static int MenuButtonInitControl (MacMenuButton *mbPtr, Rect *paneRect, 81 Rect *cntrRect ); 82static void MenuButtonEventProc(ClientData clientData, XEvent *eventPtr); 83static int UpdateControlColors(MacMenuButton *mbPtr); 84static void ComputeMenuButtonControlParams(TkMenuButton *mbPtr, 85 MenuButtonControlParams * paramsPtr); 86static void ComputeControlTitleParams(TkMenuButton *mbPtr, 87 ControlTitleParams *paramsPtr); 88static void CompareControlTitleParams(ControlTitleParams *p1Ptr, 89 ControlTitleParams *p2Ptr, int *titleChanged, int *styleChanged); 90 91/* 92 * The structure below defines menubutton class behavior by means of 93 * procedures that can be invoked from generic window code. 94 */ 95 96Tk_ClassProcs tkpMenubuttonClass = { 97 sizeof(Tk_ClassProcs), /* size */ 98 TkMenuButtonWorldChanged, /* worldChangedProc */ 99}; 100 101 102/* 103 *---------------------------------------------------------------------- 104 * 105 * TkpCreateMenuButton -- 106 * 107 * Allocate a new TkMenuButton structure. 108 * 109 * Results: 110 * Returns a newly allocated TkMenuButton structure. 111 * 112 * Side effects: 113 * Registers an event handler for the widget. 114 * 115 *---------------------------------------------------------------------- 116 */ 117 118TkMenuButton * 119TkpCreateMenuButton( 120 Tk_Window tkwin) 121{ 122 MacMenuButton *mbPtr = (MacMenuButton *) ckalloc(sizeof(MacMenuButton)); 123 124 Tk_CreateEventHandler(tkwin, ActivateMask, 125 MenuButtonEventProc, (ClientData) mbPtr); 126 mbPtr->flags = 0; 127 mbPtr->userPaneBackground = PIXEL_MAGIC << 24; 128 mbPtr->userPane = NULL; 129 mbPtr->control = NULL; 130 mbPtr->menuRef = NULL; 131 bzero(&mbPtr->params, sizeof(mbPtr->params)); 132 bzero(&mbPtr->titleParams, sizeof(mbPtr->titleParams)); 133 134 return (TkMenuButton *) mbPtr; 135} 136 137/* 138 *---------------------------------------------------------------------- 139 * 140 * TkpDisplayMenuButton -- 141 * 142 * This procedure is invoked to display a menubutton widget. 143 * 144 * Results: 145 * None. 146 * 147 * Side effects: 148 * Commands are output to X to display the menubutton in its 149 * current mode. 150 * 151 *---------------------------------------------------------------------- 152 */ 153 154void 155TkpDisplayMenuButton( 156 ClientData clientData) /* Information about widget. */ 157{ 158 TkMenuButton *butPtr = (TkMenuButton *) clientData; 159 Tk_Window tkwin = butPtr->tkwin; 160 TkWindow *winPtr; 161 Pixmap pixmap; 162 MacMenuButton *mbPtr = (MacMenuButton *) butPtr; 163 CGrafPtr destPort, savePort; 164 Boolean portChanged = false; 165 int hasImageOrBitmap = 0, width, height; 166 OSStatus err; 167 ControlButtonGraphicAlignment theAlignment; 168 Rect paneRect, cntrRect; 169 int active, enabled; 170 171 butPtr->flags &= ~REDRAW_PENDING; 172 if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { 173 return; 174 } 175 pixmap = (Pixmap) Tk_WindowId(tkwin); 176 TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin)); 177 178 winPtr = (TkWindow *)butPtr->tkwin; 179 paneRect.left = winPtr->privatePtr->xOff; 180 paneRect.top = winPtr->privatePtr->yOff; 181 paneRect.right = paneRect.left+Tk_Width(butPtr->tkwin); 182 paneRect.bottom = paneRect.top+Tk_Height(butPtr->tkwin); 183 184 cntrRect = paneRect; 185 186 cntrRect.left += butPtr->inset; 187 cntrRect.top += butPtr->inset; 188 cntrRect.right -= butPtr->inset; 189 cntrRect.bottom -= butPtr->inset; 190 191 if (mbPtr->userPane) { 192 MenuButtonControlParams params; 193 bzero(¶ms, sizeof(params)); 194 ComputeMenuButtonControlParams(butPtr, ¶ms); 195 if ( 196#if 0 197 (winPtr->flags & TK_REBUILD_TOPLEVEL) || 198#endif 199 bcmp(¶ms,&mbPtr->params,sizeof(params))) { 200 if (mbPtr->userPane) { 201 DisposeControl(mbPtr->userPane); 202 mbPtr->userPane = NULL; 203 mbPtr->control = NULL; 204 } 205 } 206 } 207 if (!mbPtr->userPane) { 208 if (MenuButtonInitControl(mbPtr, &paneRect, &cntrRect)) { 209 TkMacOSXDbgMsg("Init Control failed"); 210 return; 211 } 212 } 213 SetControlBounds(mbPtr->userPane, &paneRect); 214 SetControlBounds(mbPtr->control, &cntrRect); 215 216 if (butPtr->image != None) { 217 Tk_SizeOfImage(butPtr->image, &width, &height); 218 hasImageOrBitmap = 1; 219 } else if (butPtr->bitmap != None) { 220 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); 221 hasImageOrBitmap = 1; 222 } 223 224 /* 225 * We need to cache the title and its style 226 */ 227 228 if (!(mbPtr->flags & FIRST_DRAW)) { 229 ControlTitleParams titleParams; 230 int titleChanged; 231 int styleChanged; 232 233 ComputeControlTitleParams(butPtr, &titleParams); 234 CompareControlTitleParams(&titleParams, &mbPtr->titleParams, 235 &titleChanged, &styleChanged); 236 if (titleChanged) { 237 CFStringRef cf = CFStringCreateWithCString(NULL, 238 (char*) titleParams.title, kCFStringEncodingUTF8); 239 240 if (hasImageOrBitmap) { 241 SetControlTitleWithCFString(mbPtr->control, cf); 242 } else { 243 SetMenuItemTextWithCFString(mbPtr->menuRef, 1, cf); 244 } 245 CFRelease(cf); 246 bcopy(titleParams.title, mbPtr->titleParams.title, 247 titleParams.len + 1); 248 mbPtr->titleParams.len = titleParams.len; 249 } 250 if ((titleChanged||styleChanged) && titleParams .len) { 251 if (hasImageOrBitmap) { 252 err = ChkErr(SetControlFontStyle, mbPtr->control, 253 &titleParams.style); 254 if (err != noErr) { 255 return; 256 } 257 } 258 bcopy(&titleParams.style, &mbPtr->titleParams.style, 259 sizeof(titleParams.style)); 260 } 261 } 262 if (hasImageOrBitmap) { 263 { 264 destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin)); 265 portChanged = QDSwapPort(destPort, &savePort); 266 mbPtr->picParams.version = -2; 267 mbPtr->picParams.hRes = 0x00480000; 268 mbPtr->picParams.vRes = 0x00480000; 269 mbPtr->picParams.srcRect.top = 0; 270 mbPtr->picParams.srcRect.left = 0; 271 mbPtr->picParams.srcRect.bottom = height; 272 mbPtr->picParams.srcRect.right = width; 273 mbPtr->picParams.reserved1 = 0; 274 mbPtr->picParams.reserved2 = 0; 275 mbPtr->bevelButtonContent.contentType = kControlContentPictHandle; 276 mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams); 277 if (!mbPtr->bevelButtonContent.u.picture) { 278 TkMacOSXDbgMsg("OpenCPicture failed"); 279 } 280 tkPictureIsOpen = 1; 281 282 /* 283 * TO DO - There is one case where XCopyPlane calls CopyDeepMask, 284 * which does not get recorded in the picture. So the bitmap code 285 * will fail in that case. 286 */ 287 } 288 if (butPtr->image != NULL) { 289 Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, 0, 0); 290 } else { 291 GC gc; 292 293 if (butPtr->state == STATE_DISABLED) { 294 gc = butPtr->disabledGC; 295 } else if (butPtr->state == STATE_ACTIVE) { 296 gc = butPtr->activeTextGC; 297 } else { 298 gc = butPtr->normalTextGC; 299 } 300 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, 301 width, height, 0, 0, 1); 302 } 303 { 304 ClosePicture(); 305 tkPictureIsOpen = 0; 306 if (portChanged) { 307 QDSwapPort(savePort, NULL); 308 } 309 } 310 ChkErr(SetControlData, mbPtr->control, kControlButtonPart, 311 kControlBevelButtonContentTag, 312 sizeof(ControlButtonContentInfo), 313 (char *) &mbPtr->bevelButtonContent); 314 switch (butPtr->anchor) { 315 case TK_ANCHOR_N: 316 theAlignment = kControlBevelButtonAlignTop; 317 break; 318 case TK_ANCHOR_NE: 319 theAlignment = kControlBevelButtonAlignTopRight; 320 break; 321 case TK_ANCHOR_E: 322 theAlignment = kControlBevelButtonAlignRight; 323 break; 324 case TK_ANCHOR_SE: 325 theAlignment = kControlBevelButtonAlignBottomRight; 326 break; 327 case TK_ANCHOR_S: 328 theAlignment = kControlBevelButtonAlignBottom; 329 break; 330 case TK_ANCHOR_SW: 331 theAlignment = kControlBevelButtonAlignBottomLeft; 332 break; 333 case TK_ANCHOR_W: 334 theAlignment = kControlBevelButtonAlignLeft; 335 break; 336 case TK_ANCHOR_NW: 337 theAlignment = kControlBevelButtonAlignTopLeft; 338 break; 339 case TK_ANCHOR_CENTER: 340 theAlignment = kControlBevelButtonAlignCenter; 341 break; 342 } 343 344 ChkErr(SetControlData, mbPtr->control, kControlButtonPart, 345 kControlBevelButtonGraphicAlignTag, 346 sizeof(ControlButtonGraphicAlignment), (char *) &theAlignment); 347 } 348 active = ((mbPtr->flags & ACTIVE) != 0); 349 if (active != IsControlActive(mbPtr->control)) { 350 if (active) { 351 ChkErr(ActivateControl, mbPtr->control); 352 } else { 353 ChkErr(DeactivateControl, mbPtr->control); 354 } 355 } 356 enabled = !(butPtr->state == STATE_DISABLED); 357 if (enabled != IsControlEnabled(mbPtr->control)) { 358 if (enabled) { 359 ChkErr(EnableControl, mbPtr->control); 360 } else { 361 ChkErr(DisableControl, mbPtr->control); 362 } 363 } 364 if (active && enabled) { 365 if (butPtr->state == STATE_ACTIVE) { 366 if (hasImageOrBitmap) { 367 HiliteControl(mbPtr->control, kControlButtonPart); 368 } else { 369 HiliteControl(mbPtr->control, kControlLabelPart); 370 } 371 } else { 372 HiliteControl(mbPtr->control, kControlNoPart); 373 } 374 } 375 UpdateControlColors(mbPtr); 376 if (mbPtr->flags & FIRST_DRAW) { 377 ShowControl(mbPtr->control); 378 ShowControl(mbPtr->userPane); 379 mbPtr->flags ^= FIRST_DRAW; 380 } else { 381 SetControlVisibility(mbPtr->control, true, true); 382 Draw1Control(mbPtr->userPane); 383 } 384 if (hasImageOrBitmap) { 385 if (mbPtr->bevelButtonContent.contentType == 386 kControlContentPictHandle) { 387 KillPicture(mbPtr->bevelButtonContent.u.picture); 388 } 389 } 390} 391 392/* 393 *---------------------------------------------------------------------- 394 * 395 * TkpDestroyMenuButton -- 396 * 397 * Free data structures associated with the menubutton control. 398 * 399 * Results: 400 * None. 401 * 402 * Side effects: 403 * Restores the default control state. 404 * 405 *---------------------------------------------------------------------- 406 */ 407 408void 409TkpDestroyMenuButton( 410 TkMenuButton *mbPtr) 411{ 412 MacMenuButton *macMbPtr = (MacMenuButton *) mbPtr; 413 414 if (macMbPtr->userPane) { 415 DisposeControl(macMbPtr->userPane); 416 macMbPtr->userPane = NULL; 417 } 418 if (macMbPtr->menuRef) { 419 short menuID = GetMenuID(macMbPtr->menuRef); 420 421 TkMacOSXFreeMenuID(menuID); 422 DisposeMenu(macMbPtr->menuRef); 423 macMbPtr->menuRef = NULL; 424 } 425} 426 427/* 428 *---------------------------------------------------------------------- 429 * 430 * TkpComputeMenuButtonGeometry -- 431 * 432 * After changes in a menu button's text or bitmap, this procedure 433 * recomputes the menu button's geometry and passes this information 434 * along to the geometry manager for the window. 435 * 436 * Results: 437 * None. 438 * 439 * Side effects: 440 * The menu button's window may change size. 441 * 442 *---------------------------------------------------------------------- 443 */ 444 445void 446TkpComputeMenuButtonGeometry(mbPtr) 447 register TkMenuButton *mbPtr; /* Widget record for menu button. */ 448{ 449 int width, height, mm, pixels; 450 int hasImageOrBitmap = 0; 451 452 mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth; 453 if (mbPtr->image != None) { 454 Tk_SizeOfImage(mbPtr->image, &width, &height); 455 hasImageOrBitmap = 1; 456 } else if (mbPtr->bitmap != None) { 457 Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height); 458 hasImageOrBitmap = 1; 459 } else { 460 hasImageOrBitmap = 0; 461 Tk_FreeTextLayout(mbPtr->textLayout); 462 mbPtr->textLayout = Tk_ComputeTextLayout(mbPtr->tkfont, mbPtr->text, 463 -1, mbPtr->wrapLength, mbPtr->justify, 0, &mbPtr->textWidth, 464 &mbPtr->textHeight); 465 width = mbPtr->textWidth; 466 height = mbPtr->textHeight; 467 if (mbPtr->width > 0) { 468 width = mbPtr->width * Tk_TextWidth(mbPtr->tkfont, "0", 1); 469 } 470 if (mbPtr->height > 0) { 471 Tk_FontMetrics fm; 472 473 Tk_GetFontMetrics(mbPtr->tkfont, &fm); 474 height = mbPtr->height * fm.linespace; 475 } 476 width += 2*mbPtr->padX; 477 height += 2*mbPtr->padY; 478 } 479 if (hasImageOrBitmap) { 480 if (mbPtr->width > 0) { 481 width = mbPtr->width; 482 } 483 if (mbPtr->height > 0) { 484 height = mbPtr->height; 485 } 486 mbPtr->inset = mbPtr->highlightWidth + 2; 487 width += (2 * mbPtr->borderWidth + 4); 488 height += (2 * mbPtr->borderWidth + 4); 489 } else { 490 width += TK_POPUP_OFFSET; 491 } 492 if (mbPtr->indicatorOn) { 493 mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin)); 494 pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin)); 495 mbPtr->indicatorHeight = kTriangleHeight; 496 mbPtr->indicatorWidth = kTriangleWidth + kTriangleMargin; 497 width += mbPtr->indicatorWidth; 498 } else { 499 mbPtr->indicatorHeight = 0; 500 mbPtr->indicatorWidth = 0; 501 } 502 503 Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset), 504 (int) (height + 2*mbPtr->inset)); 505 Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset); 506} 507 508/* 509 *---------------------------------------------------------------------- 510 * 511 * ComputeMenuButtonControlParams -- 512 * 513 * This procedure computes the various parameters used 514 * when creating a Carbon control (NewControl) 515 * These are determined by the various tk menu button parameters 516 * 517 * Results: 518 * None. 519 * 520 * Side effects: 521 * Sets the control initialisation parameters 522 * 523 *---------------------------------------------------------------------- 524 */ 525 526static void 527ComputeMenuButtonControlParams( 528 TkMenuButton *mbPtr, 529 MenuButtonControlParams *paramsPtr) 530{ 531 int fakeMenuID = 256; 532 533 /* 534 * Determine ProcID based on button type and dimensions 535 * 536 * We need to set minValue to some non-zero value, 537 * Otherwise, the markers do not show up 538 */ 539 540 paramsPtr->minValue = kControlBehaviorMultiValueMenu; 541 paramsPtr->maxValue = 0; 542 if (mbPtr->image || mbPtr->bitmap) { 543 paramsPtr->isBevel = 1; 544 if (mbPtr->borderWidth <= 2) { 545 paramsPtr->procID = kControlBevelButtonSmallBevelProc; 546 } else if (mbPtr->borderWidth == 3) { 547 paramsPtr->procID = kControlBevelButtonNormalBevelProc; 548 } else { 549 paramsPtr->procID = kControlBevelButtonLargeBevelProc; 550 } 551 if (mbPtr->indicatorOn) { 552 paramsPtr->initialValue = fakeMenuID; 553 } else { 554 paramsPtr->initialValue = 0; 555 } 556 } else { 557 paramsPtr->isBevel = 0; 558 paramsPtr->procID = kControlPopupButtonProc 559 + kControlPopupVariableWidthVariant; 560 paramsPtr->minValue = -12345; 561 paramsPtr->maxValue = -1; 562 paramsPtr->initialValue = 0; 563 } 564} 565 566/* 567 *---------------------------------------------------------------------- 568 * 569 * returns 0 if same, 1 otherwise 570 * 571 *---------------------------------------------------------------------- 572 */ 573 574static void 575CompareControlTitleParams( 576 ControlTitleParams *p1Ptr, 577 ControlTitleParams *p2Ptr, 578 int *titleChanged, 579 int *styleChanged) 580{ 581 if (p1Ptr->len != p2Ptr->len) { 582 *titleChanged = 1; 583 } else if (bcmp(p1Ptr->title,p2Ptr->title,p1Ptr->len)) { 584 *titleChanged = 1; 585 } else { 586 *titleChanged = 0; 587 } 588 589 if (p1Ptr->len && p2Ptr->len) { 590 *styleChanged = bcmp(&p1Ptr->style, &p2Ptr->style, 591 sizeof(p2Ptr->style)); 592 } else { 593 *styleChanged = p1Ptr->len||p2Ptr->len; 594 } 595} 596 597static void 598ComputeControlTitleParams( 599 TkMenuButton *butPtr, 600 ControlTitleParams *paramsPtr) 601{ 602 Tk_Font font; 603 604 paramsPtr->len = TkFontGetFirstTextLayout(butPtr->textLayout, &font, 605 (char*) paramsPtr->title); 606 paramsPtr->title[paramsPtr->len] = 0; 607 if (paramsPtr->len) { 608 TkMacOSXInitControlFontStyle(font,¶msPtr->style); 609 } 610} 611 612/* 613 *---------------------------------------------------------------------- 614 * 615 * MenuButtonInitControl -- 616 * 617 * This procedure initialises a Carbon control 618 * 619 * Results: 620 * 0 on success, 1 on failure. 621 * 622 * Side effects: 623 * A background pane control and the control itself is created 624 * The contol is embedded in the background control 625 * The background control is embedded in the root control 626 * of the containing window 627 * The creation parameters for the control are also computed 628 * 629 *---------------------------------------------------------------------- 630 */ 631int 632MenuButtonInitControl( 633 MacMenuButton *mbPtr, /* Mac button. */ 634 Rect *paneRect, 635 Rect *cntrRect) 636{ 637 OSStatus err; 638 TkMenuButton *butPtr = (TkMenuButton *) mbPtr; 639 SInt16 procID, initialValue, minValue, maxValue; 640 Boolean initiallyVisible; 641 SInt32 controlReference; 642 short menuID; 643 ControlRef rootControl = 644 TkMacOSXGetRootControl(Tk_WindowId(butPtr->tkwin)); 645 646 mbPtr->windowRef = TkMacOSXDrawableWindow(Tk_WindowId(butPtr->tkwin)); 647 648 /* 649 * Set up the user pane 650 */ 651 652 initiallyVisible = false; 653 initialValue = kControlSupportsEmbedding | kControlHasSpecialBackground; 654 minValue = 0; 655 maxValue = 1; 656 procID = kControlUserPaneProc; 657 controlReference = (SInt32)mbPtr; 658 mbPtr->userPane = NewControl(mbPtr->windowRef, paneRect, "\p", 659 initiallyVisible, initialValue, minValue, maxValue, procID, 660 controlReference); 661 if (!mbPtr->userPane) { 662 TkMacOSXDbgMsg("Failed to create user pane control"); 663 return 1; 664 } 665 err = ChkErr(EmbedControl, mbPtr->userPane, rootControl); 666 if (err != noErr) { 667 return 1; 668 } 669 SetUserPaneSetUpSpecialBackgroundProc(mbPtr->userPane, 670 UserPaneBackgroundProc); 671 SetUserPaneDrawProc(mbPtr->userPane,UserPaneDraw); 672 initiallyVisible = false; 673 ComputeMenuButtonControlParams(butPtr,&mbPtr->params); 674 675 /* 676 * Do this only if we are using bevel buttons. 677 */ 678 679 ComputeControlTitleParams(butPtr, &mbPtr->titleParams); 680 mbPtr->control = NewControl(mbPtr->windowRef, 681 cntrRect, "\p" /* mbPtr->titleParams.title */, 682 initiallyVisible, mbPtr->params.initialValue, 683 mbPtr->params.minValue, mbPtr->params.maxValue, 684 mbPtr->params.procID, controlReference); 685 if (!mbPtr->control) { 686 TkMacOSXDbgMsg("Failed to create control of type %d", 687 mbPtr->params.procID); 688 return 1; 689 } 690 err = ChkErr(EmbedControl, mbPtr->control, mbPtr->userPane); 691 if (err != noErr ) { 692 return 1; 693 } 694 if (mbPtr->params.isBevel) { 695 CFStringRef cf = CFStringCreateWithCString(NULL, 696 (char*) mbPtr->titleParams.title, kCFStringEncodingUTF8); 697 698 SetControlTitleWithCFString(mbPtr->control, cf); 699 CFRelease(cf); 700 if (mbPtr->titleParams.len) { 701 err = ChkErr(SetControlFontStyle, mbPtr->control, 702 &mbPtr->titleParams.style); 703 if (err != noErr) { 704 return 1; 705 } 706 } 707 } else { 708 CFStringRef cfStr; 709 710 err = TkMacOSXGetNewMenuID(mbPtr->info.interp, (TkMenu *) mbPtr, 0, 711 &menuID); 712 if (err != TCL_OK) { 713 return 1; 714 } 715 err = ChkErr(CreateNewMenu, menuID, kMenuAttrDoNotUseUserCommandKeys, 716 &(mbPtr->menuRef)); 717 if (err != noErr) { 718 return 1; 719 } 720 cfStr = CFStringCreateWithCString(NULL, Tk_PathName(mbPtr->info.tkwin), 721 kCFStringEncodingUTF8); 722 if (!cfStr) { 723 TkMacOSXDbgMsg("CFStringCreateWithCString failed"); 724 return 1; 725 } 726 err = ChkErr(SetMenuTitleWithCFString, mbPtr->menuRef, cfStr); 727 CFRelease(cfStr); 728 if (err != noErr) { 729 return 1; 730 } 731 cfStr = CFStringCreateWithCString(NULL, 732 (char*) mbPtr->titleParams.title, kCFStringEncodingUTF8); 733 AppendMenuItemText(mbPtr->menuRef, "\px"); 734 if (cfStr) { 735 SetMenuItemTextWithCFString(mbPtr->menuRef, 1, cfStr); 736 CFRelease(cfStr); 737 } 738 ChkErr(SetControlData, mbPtr->control, kControlNoPart, 739 kControlPopupButtonMenuRefTag, sizeof(mbPtr->menuRef), 740 &mbPtr->menuRef); 741 SetControlMinimum(mbPtr->control, 1); 742 SetControlMaximum(mbPtr->control, 1); 743 SetControlValue(mbPtr->control, 1); 744 } 745 mbPtr->flags |= FIRST_DRAW; 746 if (IsWindowActive(mbPtr->windowRef)) { 747 mbPtr->flags |= ACTIVE; 748 } 749 return 0; 750} 751 752/* 753 *-------------------------------------------------------------- 754 * 755 * SetUserPane 756 * 757 * Utility function to add a UserPaneDrawProc 758 * to a userPane control. From MoreControls code 759 * from Apple DTS. 760 * 761 * Results: 762 * MacOS system error. 763 * 764 * Side effects: 765 * The user pane gets a new UserPaneDrawProc. 766 * 767 *-------------------------------------------------------------- 768 */ 769OSStatus 770SetUserPaneDrawProc( 771 ControlRef control, 772 ControlUserPaneDrawProcPtr upp) 773{ 774 ControlUserPaneDrawUPP myControlUserPaneDrawUPP = 775 NewControlUserPaneDrawUPP(upp); 776 777 return SetControlData(control, kControlNoPart,kControlUserPaneDrawProcTag, 778 sizeof(myControlUserPaneDrawUPP), (Ptr)&myControlUserPaneDrawUPP); 779} 780 781/* 782 *-------------------------------------------------------------- 783 * 784 * SetUserPaneSetUpSpecialBackgroundProc -- 785 * 786 * Utility function to add a UserPaneBackgroundProc 787 * to a userPane control 788 * 789 * Results: 790 * MacOS system error. 791 * 792 * Side effects: 793 * The user pane gets a new UserPaneBackgroundProc. 794 * 795 *-------------------------------------------------------------- 796 */ 797 798OSStatus 799SetUserPaneSetUpSpecialBackgroundProc( 800 ControlRef control, 801 ControlUserPaneBackgroundProcPtr upp) 802{ 803 ControlUserPaneBackgroundUPP myControlUserPaneBackgroundUPP = 804 NewControlUserPaneBackgroundUPP(upp); 805 806 return SetControlData(control, kControlNoPart, 807 kControlUserPaneBackgroundProcTag, 808 sizeof(myControlUserPaneBackgroundUPP), 809 (Ptr) &myControlUserPaneBackgroundUPP); 810} 811 812/* 813 *-------------------------------------------------------------- 814 * 815 * UserPaneDraw -- 816 * 817 * This function draws the background of the user pane that will 818 * lie under checkboxes and radiobuttons. 819 * 820 * Results: 821 * None. 822 * 823 * Side effects: 824 * The user pane gets updated to the current color. 825 * 826 *-------------------------------------------------------------- 827 */ 828 829void 830UserPaneDraw( 831 ControlRef control, 832 ControlPartCode cpc) 833{ 834 Rect contrlRect; 835 MacMenuButton * mbPtr = 836 (MacMenuButton *)(intptr_t)GetControlReference(control); 837 CGrafPtr port; 838 839 GetPort(&port); 840 GetControlBounds(control,&contrlRect); 841 TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL, port); 842 EraseRect (&contrlRect); 843} 844 845/* 846 *-------------------------------------------------------------- 847 * 848 * UserPaneBackgroundProc -- 849 * 850 * This function sets up the background of the user pane that will 851 * lie under checkboxes and radiobuttons. 852 * 853 * Results: 854 * None. 855 * 856 * Side effects: 857 * The user pane background gets set to the current color. 858 * 859 *-------------------------------------------------------------- 860 */ 861 862void 863UserPaneBackgroundProc( 864 ControlHandle control, 865 ControlBackgroundPtr info) 866{ 867 MacMenuButton *mbPtr = 868 (MacMenuButton *)(intptr_t)GetControlReference(control); 869 870 if (info->colorDevice) { 871 CGrafPtr port; 872 873 GetPort(&port); 874 TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL, port); 875 } 876} 877 878/* 879 *-------------------------------------------------------------- 880 * 881 * UpdateControlColors -- 882 * 883 * This function will review the colors used to display 884 * a Macintosh button. If any non-standard colors are 885 * used we create a custom palette for the button, populate 886 * with the colors for the button and install the palette. 887 * 888 * Under Appearance, we just set the pointer that will be 889 * used by the UserPaneDrawProc. 890 * 891 * Results: 892 * None. 893 * 894 * Side effects: 895 * The Macintosh control may get a custom palette installed. 896 * 897 *-------------------------------------------------------------- 898 */ 899 900static int 901UpdateControlColors( 902 MacMenuButton *mbPtr) 903{ 904 XColor *xcolor; 905 TkMenuButton * butPtr = (TkMenuButton *) mbPtr; 906 907 /* 908 * Under Appearance we cannot change the background of the 909 * button itself. However, the color we are setting is the color 910 * of the containing userPane. This will be the color that peeks 911 * around the rounded corners of the button. 912 * We make this the highlightbackground rather than the background, 913 * because if you color the background of a frame containing a 914 * button, you usually also color the highlightbackground as well, 915 * or you will get a thin grey ring around the button. 916 */ 917 918 xcolor = Tk_3DBorderColor(butPtr->normalBorder); 919 mbPtr->userPaneBackground = xcolor->pixel; 920 921 return false; 922} 923 924/* 925 *-------------------------------------------------------------- 926 * 927 * MenuButtonEventProc -- 928 * 929 * This procedure is invoked by the Tk dispatcher for various 930 * events on buttons. 931 * 932 * Results: 933 * None. 934 * 935 * Side effects: 936 * When it gets exposed, it is redisplayed. 937 * 938 *-------------------------------------------------------------- 939 */ 940 941static void 942MenuButtonEventProc( 943 ClientData clientData, /* Information about window. */ 944 XEvent *eventPtr) /* Information about event. */ 945{ 946 TkMenuButton *buttonPtr = (TkMenuButton *) clientData; 947 MacMenuButton *mbPtr = (MacMenuButton *) clientData; 948 949 if (eventPtr->type == ActivateNotify 950 || eventPtr->type == DeactivateNotify) { 951 if ((buttonPtr->tkwin == NULL) || (!Tk_IsMapped(buttonPtr->tkwin))) { 952 return; 953 } 954 if (eventPtr->type == ActivateNotify) { 955 mbPtr->flags |= ACTIVE; 956 } else { 957 mbPtr->flags &= ~ACTIVE; 958 } 959 if ((buttonPtr->flags & REDRAW_PENDING) == 0) { 960 Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) buttonPtr); 961 buttonPtr->flags |= REDRAW_PENDING; 962 } 963 } 964} 965