1/* 2 * tkMacOSXButton.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 * 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: tkMacOSXButton.c,v 1.2.2.20 2007/11/09 07:36:45 das Exp $ 15 */ 16 17#include "tkMacOSXPrivate.h" 18#include "tkButton.h" 19#include "tkMacOSXFont.h" 20#include "tkMacOSXDebug.h" 21 22#define DEFAULT_USE_TK_TEXT 0 23 24#define CONTROL_INITIALIZED 1 25#define FIRST_DRAW 2 26#define ACTIVE 4 27 28#define MAX_VALUE 1 29 30/* 31 * Default insets for controls 32 */ 33 34#define DEF_INSET_LEFT 2 35#define DEF_INSET_RIGHT 2 36#define DEF_INSET_TOP 2 37#define DEF_INSET_BOTTOM 4 38 39/* 40 * Some defines used to control what type of control is drawn. 41 */ 42 43#define DRAW_LABEL 0 /* Labels are treated genericly. */ 44#define DRAW_CONTROL 1 /* Draw using the Native control. */ 45#define DRAW_CUSTOM 2 /* Make our own button drawing. */ 46#define DRAW_BEVEL 3 47 48/* 49 * Declaration of Mac specific button structure. 50 */ 51 52typedef struct { 53 SInt16 initialValue; 54 SInt16 minValue; 55 SInt16 maxValue; 56 SInt16 procID; 57 int isBevel; 58} MacControlParams; 59 60typedef struct { 61 int drawType; 62 Tk_3DBorder border; 63 int relief; 64 int offset; /* 0 means this is a normal widget. 1 means 65 * it is an image button, so we offset the 66 * image to make the button appear to move 67 * up and down as the relief changes. */ 68 GC gc; 69 int hasImageOrBitmap; 70} DrawParams; 71 72typedef struct { 73 TkButton info; /* Generic button info */ 74 int id; 75 int usingControl; 76 int useTkText; 77 int flags; /* Initialisation status */ 78 MacControlParams params; 79 WindowRef windowRef; 80 unsigned long userPaneBackground; 81 ControlRef userPane; /* Carbon control */ 82 ControlRef control; /* Carbon control */ 83 Str255 controlTitle; 84 ControlFontStyleRec fontStyle; 85 /* 86 * The following are used to store the image content for 87 * beveled buttons, i.e. buttons with images. 88 */ 89 ControlButtonContentInfo bevelButtonContent; 90 OpenCPicParams picParams; 91} MacButton; 92 93/* 94 * Forward declarations for procedures defined later in this file: 95 */ 96 97static OSStatus SetUserPaneDrawProc(ControlRef control, 98 ControlUserPaneDrawProcPtr upp); 99static OSStatus SetUserPaneSetUpSpecialBackgroundProc(ControlRef control, 100 ControlUserPaneBackgroundProcPtr upp); 101static void UserPaneDraw(ControlRef control, ControlPartCode cpc); 102static void UserPaneBackgroundProc(ControlHandle, 103 ControlBackgroundPtr info); 104 105static void ButtonEventProc(ClientData clientData, XEvent *eventPtr); 106static int UpdateControlColors(MacButton *mbPtr); 107static void TkMacOSXComputeControlParams(TkButton *butPtr, 108 MacControlParams *paramsPtr); 109static int TkMacOSXComputeDrawParams(TkButton *butPtr, DrawParams *dpPtr); 110static void TkMacOSXDrawControl(MacButton *butPtr, GWorldPtr destPort, GC gc, 111 Pixmap pixmap); 112static void SetupBevelButton(MacButton *butPtr, ControlRef controlHandle, 113 GWorldPtr destPort, GC gc, Pixmap pixmap); 114 115/* 116 * The class procedure table for the button widgets. 117 */ 118 119Tk_ClassProcs tkpButtonProcs = { 120 sizeof(Tk_ClassProcs), /* size */ 121 TkButtonWorldChanged, /* worldChangedProc */ 122}; 123 124static int bCount; 125 126 127/* 128 *---------------------------------------------------------------------- 129 * 130 * TkpCreateButton -- 131 * 132 * Allocate a new TkButton structure. 133 * 134 * Results: 135 * Returns a newly allocated TkButton structure. 136 * 137 * Side effects: 138 * Registers an event handler for the widget. 139 * 140 *---------------------------------------------------------------------- 141 */ 142 143TkButton * 144TkpCreateButton( 145 Tk_Window tkwin) 146{ 147 MacButton *macButtonPtr = (MacButton *) ckalloc(sizeof(MacButton)); 148 149 Tk_CreateEventHandler(tkwin, ActivateMask, 150 ButtonEventProc, (ClientData) macButtonPtr); 151 macButtonPtr->id = bCount++; 152 macButtonPtr->usingControl = 0; 153 macButtonPtr->flags = 0; 154 macButtonPtr->userPaneBackground = PIXEL_MAGIC << 24; 155 macButtonPtr->userPane = NULL; 156 macButtonPtr->control = NULL; 157 macButtonPtr->controlTitle[0] = 0; 158 macButtonPtr->controlTitle[1] = 0; 159 bzero(&macButtonPtr->params, sizeof(macButtonPtr->params)); 160 bzero(&macButtonPtr->fontStyle, sizeof(macButtonPtr->fontStyle)); 161 162 return (TkButton *)macButtonPtr; 163} 164 165/* 166 *---------------------------------------------------------------------- 167 * 168 * TkpDisplayButton -- 169 * 170 * This procedure is invoked to display a button widget. It is 171 * normally invoked as an idle handler. 172 * 173 * Results: 174 * None. 175 * 176 * Side effects: 177 * Commands are output to X to display the button in its 178 * current mode. The REDRAW_PENDING flag is cleared. 179 * 180 *---------------------------------------------------------------------- 181 */ 182 183void 184TkpDisplayButton( 185 ClientData clientData) /* Information about widget. */ 186{ 187 MacButton *macButtonPtr = (MacButton *) clientData; 188 TkButton *butPtr = (TkButton *) clientData; 189 Tk_Window tkwin = butPtr->tkwin; 190 CGrafPtr destPort, savePort; 191 Boolean portChanged; 192 Pixmap pixmap; 193 int width, height, fullWidth, fullHeight, textXOffset, textYOffset; 194 int borderWidth, wasUsingControl; 195 int haveImage = 0, haveText = 0, imageWidth = 0, imageHeight = 0; 196 int imageXOffset = 0, imageYOffset = 0; /* image information that will 197 * be used to restrict disabled 198 * pixmap as well */ 199 DrawParams drawParams, *dpPtr = &drawParams; 200 201 butPtr->flags &= ~REDRAW_PENDING; 202 if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { 203 return; 204 } 205 pixmap = (Pixmap) Tk_WindowId(tkwin); 206 wasUsingControl = macButtonPtr->usingControl; 207 208 if (TkMacOSXComputeDrawParams(butPtr, &drawParams) ) { 209 macButtonPtr->usingControl = 1; 210 if (butPtr->type == TYPE_BUTTON) { 211 macButtonPtr->useTkText = 0; 212 } else { 213 macButtonPtr->useTkText = 1; 214 } 215 } else { 216 macButtonPtr->usingControl = 0; 217 macButtonPtr->useTkText = 1; 218 } 219 220 /* 221 * See the comment in UpdateControlColors as to why we use the 222 * highlightbackground for the border of Macintosh buttons. 223 */ 224 225 if (macButtonPtr->useTkText) { 226 if (butPtr->type == TYPE_BUTTON) { 227 Tk_Fill3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0, 0, 228 Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); 229 } else { 230 Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, 0, 0, 231 Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); 232 } 233 } 234 235 /* 236 * Set up clipping region. Make sure the we are using the port 237 * for this button, or we will set the wrong window's clip. 238 */ 239 240 destPort = TkMacOSXGetDrawablePort(pixmap); 241 portChanged = QDSwapPort(destPort, &savePort); 242 TkMacOSXSetUpClippingRgn(pixmap); 243 244 /* 245 * Draw the native portion of the buttons. Start by creating the control 246 * if it doesn't already exist. Then configure the Macintosh control from 247 * the Tk info. Finally, we call Draw1Control to draw to the screen. 248 */ 249 250 if (macButtonPtr->usingControl) { 251 borderWidth = 0; 252 TkMacOSXDrawControl(macButtonPtr, destPort, dpPtr->gc, pixmap); 253 } else if (wasUsingControl && macButtonPtr->userPane) { 254 DisposeControl(macButtonPtr->userPane); 255 macButtonPtr->userPane = NULL; 256 macButtonPtr->control = NULL; 257 macButtonPtr->flags = 0; 258 } 259 260 if ((dpPtr->drawType == DRAW_CUSTOM) || (dpPtr->drawType == DRAW_LABEL)) { 261 borderWidth = butPtr->borderWidth; 262 } 263 264 /* 265 * Display image or bitmap or text for button. This has 266 * already been done under Appearance with the Bevel 267 * button types. 268 */ 269 270 if (dpPtr->drawType == DRAW_BEVEL) { 271 goto applyStipple; 272 } 273 274 if (butPtr->image != None) { 275 Tk_SizeOfImage(butPtr->image, &width, &height); 276 haveImage = 1; 277 } else if (butPtr->bitmap != None) { 278 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); 279 haveImage = 1; 280 } 281 imageWidth = width; 282 imageHeight = height; 283 284 haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0); 285 if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { 286 int x, y; 287 288 textXOffset = 0; 289 textYOffset = 0; 290 fullWidth = 0; 291 fullHeight = 0; 292 293 switch ((enum compound) butPtr->compound) { 294 case COMPOUND_TOP: 295 case COMPOUND_BOTTOM: 296 /* 297 * Image is above or below text. 298 */ 299 if (butPtr->compound == COMPOUND_TOP) { 300 textYOffset = height + butPtr->padY; 301 } else { 302 imageYOffset = butPtr->textHeight + butPtr->padY; 303 } 304 fullHeight = height + butPtr->textHeight + butPtr->padY; 305 fullWidth = (width > butPtr->textWidth ? width : 306 butPtr->textWidth); 307 textXOffset = (fullWidth - butPtr->textWidth)/2; 308 imageXOffset = (fullWidth - width)/2; 309 break; 310 311 case COMPOUND_LEFT: 312 case COMPOUND_RIGHT: 313 /* 314 * Image is left or right of text. 315 */ 316 317 if (butPtr->compound == COMPOUND_LEFT) { 318 textXOffset = width + butPtr->padX; 319 } else { 320 imageXOffset = butPtr->textWidth + butPtr->padX; 321 } 322 fullWidth = butPtr->textWidth + butPtr->padX + width; 323 fullHeight = (height > butPtr->textHeight ? height : 324 butPtr->textHeight); 325 textYOffset = (fullHeight - butPtr->textHeight)/2; 326 imageYOffset = (fullHeight - height)/2; 327 break; 328 329 case COMPOUND_CENTER: 330 /* 331 * Image and text are superimposed. 332 */ 333 334 fullWidth = (width > butPtr->textWidth ? width : 335 butPtr->textWidth); 336 fullHeight = (height > butPtr->textHeight ? height : 337 butPtr->textHeight); 338 textXOffset = (fullWidth - butPtr->textWidth)/2; 339 imageXOffset = (fullWidth - width)/2; 340 textYOffset = (fullHeight - butPtr->textHeight)/2; 341 imageYOffset = (fullHeight - height)/2; 342 break; 343 344 case COMPOUND_NONE: 345 break; 346 } 347 348 TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, 349 butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y); 350 351 x += butPtr->indicatorSpace; 352 353 x += dpPtr->offset; 354 y += dpPtr->offset; 355 if (dpPtr->relief == TK_RELIEF_RAISED) { 356 x -= dpPtr->offset; 357 y -= dpPtr->offset; 358 } else if (dpPtr->relief == TK_RELIEF_SUNKEN) { 359 x += dpPtr->offset; 360 y += dpPtr->offset; 361 } 362 imageXOffset += x; 363 imageYOffset += y; 364 if (butPtr->image != NULL) { 365 if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) { 366 Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, 367 pixmap, imageXOffset, imageYOffset); 368#if 0 369 } else if ((butPtr->tristateImage != NULL) && 370 (butPtr->flags & TRISTATED)) { 371 Tk_RedrawImage(butPtr->tristateImage, 0, 0, width, height, 372 pixmap, imageXOffset, imageYOffset); 373#endif 374 } else { 375 Tk_RedrawImage(butPtr->image, 0, 0, width, height, 376 pixmap, imageXOffset, imageYOffset); 377 } 378 } else { 379 XSetClipOrigin(butPtr->display, dpPtr->gc, imageXOffset, 380 imageYOffset); 381 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, dpPtr->gc, 382 0, 0, width, height, imageXOffset, imageYOffset, 1); 383 XSetClipOrigin(butPtr->display, dpPtr->gc, 0, 0); 384 } 385 386 if (macButtonPtr->useTkText) { 387 Tk_DrawTextLayout(butPtr->display, pixmap, dpPtr->gc, 388 butPtr->textLayout, x + textXOffset, y + textYOffset, 0, 389 -1); 390 Tk_UnderlineTextLayout(butPtr->display, pixmap, dpPtr->gc, 391 butPtr->textLayout, x + textXOffset, y + textYOffset, 392 butPtr->underline); 393 } 394 y += fullHeight/2; 395 } else if (haveImage) { 396 int x = 0, y; 397 398 TkComputeAnchor(butPtr->anchor, tkwin, 0, 0, 399 butPtr->indicatorSpace + width, height, &x, &y); 400 x += butPtr->indicatorSpace; 401 402 x += dpPtr->offset; 403 y += dpPtr->offset; 404 if (dpPtr->relief == TK_RELIEF_RAISED) { 405 x -= dpPtr->offset; 406 y -= dpPtr->offset; 407 } else if (dpPtr->relief == TK_RELIEF_SUNKEN) { 408 x += dpPtr->offset; 409 y += dpPtr->offset; 410 } 411 imageXOffset += x; 412 imageYOffset += y; 413 if (butPtr->image != NULL) { 414 if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) { 415 Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, 416 pixmap, imageXOffset, imageYOffset); 417#if 0 418 } else if ((butPtr->tristateImage != NULL) && 419 (butPtr->flags & TRISTATED)) { 420 Tk_RedrawImage(butPtr->tristateImage, 0, 0, width, height, 421 pixmap, imageXOffset, imageYOffset); 422#endif 423 } else { 424 Tk_RedrawImage(butPtr->image, 0, 0, width, height, 425 pixmap, imageXOffset, imageYOffset); 426 } 427 } else { 428 XSetClipOrigin(butPtr->display, dpPtr->gc, x, y); 429 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, dpPtr->gc, 430 0, 0, width, height, x, y, 1); 431 XSetClipOrigin(butPtr->display, dpPtr->gc, 0, 0); 432 } 433 y += height/2; 434 } else if (macButtonPtr->useTkText) { 435 int x = 0, y; 436 437 TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, 438 butPtr->indicatorSpace + butPtr->textWidth, 439 butPtr->textHeight, &x, &y); 440 x += butPtr->indicatorSpace; 441 Tk_DrawTextLayout(butPtr->display, pixmap, dpPtr->gc, 442 butPtr->textLayout, x, y, 0, -1); 443 } 444 445 /* 446 * If the button is disabled with a stipple rather than a special 447 * foreground color, generate the stippled effect. If the widget 448 * is selected and we use a different background color when selected, 449 * must temporarily modify the GC so the stippling is the right color. 450 */ 451 452 applyStipple: 453 if (macButtonPtr->useTkText) { 454 if ((butPtr->state == STATE_DISABLED) 455 && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) { 456 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn 457 && (butPtr->selectBorder != NULL)) { 458 XSetForeground(butPtr->display, butPtr->stippleGC, 459 Tk_3DBorderColor(butPtr->selectBorder)->pixel); 460 } 461 462 /* 463 * Stipple the whole button if no disabledFg was specified, 464 * otherwise restrict stippling only to displayed image 465 */ 466 467 if (butPtr->disabledFg == NULL) { 468 XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 469 0, 0, (unsigned) Tk_Width(tkwin), 470 (unsigned) Tk_Height(tkwin)); 471 } else { 472 XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 473 imageXOffset, imageYOffset, 474 (unsigned) imageWidth, (unsigned) imageHeight); 475 } 476 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn 477 && (butPtr->selectBorder != NULL)) { 478 XSetForeground(butPtr->display, butPtr->stippleGC, 479 Tk_3DBorderColor(butPtr->normalBorder)->pixel); 480 } 481 } 482 483 /* 484 * Draw the border and traversal highlight last. This way, if the 485 * button's contents overflow they'll be covered up by the border. 486 */ 487 488 if (dpPtr->relief != TK_RELIEF_FLAT) { 489 int inset = butPtr->highlightWidth; 490 491 Tk_Draw3DRectangle(tkwin, pixmap, dpPtr->border, inset, inset, 492 Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset, 493 borderWidth, dpPtr->relief); 494 } 495 } 496 if (portChanged) { 497 QDSwapPort(savePort, NULL); 498 } 499} 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 519void 520TkpComputeButtonGeometry( 521 TkButton *butPtr) /* Button whose geometry may have changed. */ 522{ 523 int width, height, avgWidth, haveImage = 0, haveText = 0; 524 int xInset, yInset, txtWidth, txtHeight; 525 Tk_FontMetrics fm; 526 DrawParams drawParams; 527 528 /* 529 * First figure out the size of the contents of the button. 530 */ 531 532 width = 0; 533 height = 0; 534 txtWidth = 0; 535 txtHeight = 0; 536 avgWidth = 0; 537 538 butPtr->indicatorSpace = 0; 539 if (butPtr->image != NULL) { 540 Tk_SizeOfImage(butPtr->image, &width, &height); 541 haveImage = 1; 542 } else if (butPtr->bitmap != None) { 543 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); 544 haveImage = 1; 545 } 546 547 if (haveImage == 0 || butPtr->compound != COMPOUND_NONE) { 548 Tk_FreeTextLayout(butPtr->textLayout); 549 butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont, 550 Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength, 551 butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight); 552 553 txtWidth = butPtr->textWidth; 554 txtHeight = butPtr->textHeight; 555 avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1); 556 Tk_GetFontMetrics(butPtr->tkfont, &fm); 557 haveText = (txtWidth != 0 && txtHeight != 0); 558 } 559 560 /* 561 * If the button is compound (ie, it shows both an image and text), 562 * the new geometry is a combination of the image and text geometry. 563 * We only honor the compound bit if the button has both text and an 564 * image, because otherwise it is not really a compound button. 565 */ 566 567 if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { 568 switch ((enum compound) butPtr->compound) { 569 case COMPOUND_TOP: 570 case COMPOUND_BOTTOM: 571 /* 572 * Image is above or below text. 573 */ 574 575 height += txtHeight + butPtr->padY; 576 width = (width > txtWidth ? width : txtWidth); 577 break; 578 case COMPOUND_LEFT: 579 case COMPOUND_RIGHT: 580 /* 581 * Image is left or right of text. 582 */ 583 584 width += txtWidth + butPtr->padX; 585 height = (height > txtHeight ? height : txtHeight); 586 break; 587 case COMPOUND_CENTER: 588 /* 589 * Image and text are superimposed. 590 */ 591 592 width = (width > txtWidth ? width : txtWidth); 593 height = (height > txtHeight ? height : txtHeight); 594 break; 595 case COMPOUND_NONE: 596 break; 597 } 598 if (butPtr->width > 0) { 599 width = butPtr->width; 600 } 601 if (butPtr->height > 0) { 602 height = butPtr->height; 603 } 604 605 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 606 butPtr->indicatorSpace = height; 607 if (butPtr->type == TYPE_CHECK_BUTTON) { 608 butPtr->indicatorDiameter = (65 * height)/100; 609 } else { 610 butPtr->indicatorDiameter = (75 * height)/100; 611 } 612 } 613 614 width += 2 * butPtr->padX; 615 height += 2 * butPtr->padY; 616 } else if (haveImage) { 617 if (butPtr->width > 0) { 618 width = butPtr->width; 619 } 620 if (butPtr->height > 0) { 621 height = butPtr->height; 622 } 623 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 624 butPtr->indicatorSpace = height; 625 if (butPtr->type == TYPE_CHECK_BUTTON) { 626 butPtr->indicatorDiameter = (65 * height)/100; 627 } else { 628 butPtr->indicatorDiameter = (75 * height)/100; 629 } 630 } 631 } else { 632 width = txtWidth; 633 height = txtHeight; 634 if (butPtr->width > 0) { 635 width = butPtr->width * avgWidth; 636 } 637 if (butPtr->height > 0) { 638 height = butPtr->height * fm.linespace; 639 } 640 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 641 butPtr->indicatorDiameter = fm.linespace; 642 if (butPtr->type == TYPE_CHECK_BUTTON) { 643 butPtr->indicatorDiameter = 644 (80 * butPtr->indicatorDiameter)/100; 645 } 646 butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth; 647 } 648 } 649 650 /* 651 * Now figure out the size of the border decorations for the button. 652 */ 653 654 if (butPtr->highlightWidth < 0) { 655 butPtr->highlightWidth = 0; 656 } 657 658 /* 659 * The width and height calculation for Appearance buttons with images & 660 * non-Appearance buttons with images is different. In the latter case, 661 * we add the borderwidth to the inset, since we are going to stamp a 662 * 3-D border over the image. In the former, we add it to the height, 663 * directly, since Appearance will draw the border as part of our control. 664 * 665 * When issuing the geometry request, add extra space for the indicator, 666 * if any, and for the border and padding, plus if this is an image two 667 * extra pixels so the display can be offset by 1 pixel in either 668 * direction for the raised or lowered effect. 669 * 670 * The highlight width corresponds to the default ring on the Macintosh. 671 * As such, the highlight width is only added if the button is the default 672 * button. The actual width of the default ring is one less than the 673 * highlight width as there is also one pixel of spacing. 674 * Appearance buttons with images do not have a highlight ring, because the 675 * Bevel button type does not support one. 676 */ 677 678 if ((butPtr->image == None) && (butPtr->bitmap == None)) { 679 width += 2*butPtr->padX; 680 height += 2*butPtr->padY; 681 } 682 683 if ((butPtr->type == TYPE_BUTTON)) { 684 if ((butPtr->image == None) && (butPtr->bitmap == None)) { 685 butPtr->inset = 0; 686 if (butPtr->defaultState != STATE_DISABLED) { 687 butPtr->inset += butPtr->highlightWidth; 688 } 689 } else { 690 butPtr->inset = 0; 691 width += (2 * butPtr->borderWidth + 4); 692 height += (2 * butPtr->borderWidth + 4); 693 } 694 } else if (butPtr->type == TYPE_LABEL) { 695 butPtr->inset = butPtr->borderWidth; 696 } else if (butPtr->indicatorOn) { 697 butPtr->inset = 0; 698 } else { 699 /* 700 * Under Appearance, the Checkbutton or radiobutton with an image 701 * is represented by a BevelButton with the Sticky defProc... 702 * So we must set its height in the same way as the Button 703 * with an image or bitmap. 704 */ 705 706 if (butPtr->image != None || butPtr->bitmap != None) { 707 int border; 708 709 butPtr->inset = 0; 710 if (butPtr->borderWidth <= 2) { 711 border = 6; 712 } else { 713 border = 2 * butPtr->borderWidth + 2; 714 } 715 width += border; 716 height += border; 717 } else { 718 butPtr->inset = butPtr->borderWidth; 719 } 720 } 721 722 if (TkMacOSXComputeDrawParams(butPtr, &drawParams)) { 723 xInset = butPtr->indicatorSpace + DEF_INSET_LEFT + DEF_INSET_RIGHT; 724 yInset = DEF_INSET_TOP + DEF_INSET_BOTTOM; 725 } else { 726 xInset = butPtr->indicatorSpace+butPtr->inset*2; 727 yInset = butPtr->inset*2; 728 } 729 Tk_GeometryRequest(butPtr->tkwin, width + xInset, height + yInset); 730 Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset); 731} 732 733/* 734 *---------------------------------------------------------------------- 735 * 736 * TkpDestroyButton -- 737 * 738 * Free data structures associated with the button control. 739 * 740 * Results: 741 * None. 742 * 743 * Side effects: 744 * Restores the default control state. 745 * 746 *---------------------------------------------------------------------- 747 */ 748 749void 750TkpDestroyButton( 751 TkButton *butPtr) 752{ 753 MacButton *mbPtr = (MacButton *) butPtr; /* Mac button. */ 754 755 if (mbPtr->userPane) { 756 DisposeControl(mbPtr->userPane); 757 mbPtr->userPane = NULL; 758 } 759} 760 761/* 762 *---------------------------------------------------------------------- 763 * 764 * TkMacOSXInitControl -- 765 * 766 * This procedure initialises a Carbon control. 767 * 768 * Results: 769 * 0 on success, 1 on failure. 770 * 771 * Side effects: 772 * A background pane control and the control itself is created 773 * The contol is embedded in the background control 774 * The background control is embedded in the root control 775 * of the containing window 776 * The creation parameters for the control are also computed 777 * 778 *---------------------------------------------------------------------- 779 */ 780 781static int 782TkMacOSXInitControl( 783 MacButton *mbPtr, /* Mac button. */ 784 GWorldPtr destPort, 785 GC gc, 786 Pixmap pixmap, 787 Rect *paneRect, 788 Rect *cntrRect) 789{ 790 TkButton *butPtr = (TkButton *) mbPtr; 791 ControlRef rootControl; 792 SInt16 procID, initialValue, minValue, maxValue; 793 Boolean initiallyVisible; 794 SInt32 controlReference; 795 796 rootControl = TkMacOSXGetRootControl(Tk_WindowId(butPtr->tkwin)); 797 mbPtr->windowRef = TkMacOSXDrawableWindow(Tk_WindowId(butPtr->tkwin)); 798 799 /* 800 * Set up the user pane. 801 */ 802 803 initiallyVisible = false; 804 initialValue = kControlSupportsEmbedding|kControlHasSpecialBackground; 805 minValue = 0; 806 maxValue = 1; 807 procID = kControlUserPaneProc; 808 controlReference = (SInt32)mbPtr; 809 mbPtr->userPane = NewControl(mbPtr->windowRef, paneRect, "\p", 810 initiallyVisible, initialValue, minValue, maxValue, procID, 811 controlReference); 812 813 if (!mbPtr->userPane) { 814 TkMacOSXDbgMsg("Failed to create user pane control"); 815 return 1; 816 } 817 if (ChkErr(EmbedControl, mbPtr->userPane,rootControl) != noErr) { 818 return 1; 819 } 820 821 SetUserPaneSetUpSpecialBackgroundProc(mbPtr->userPane, 822 UserPaneBackgroundProc); 823 SetUserPaneDrawProc(mbPtr->userPane,UserPaneDraw); 824 initiallyVisible = false; 825 TkMacOSXComputeControlParams(butPtr,&mbPtr->params); 826 mbPtr->control = NewControl(mbPtr->windowRef, cntrRect, "\p", 827 initiallyVisible, mbPtr->params.initialValue, 828 mbPtr->params.minValue, mbPtr->params.maxValue, 829 mbPtr->params.procID, controlReference); 830 831 if (!mbPtr->control) { 832 TkMacOSXDbgMsg("Failed to create control of type %d\n", procID); 833 return 1; 834 } 835 if (ChkErr(EmbedControl, mbPtr->control,mbPtr->userPane) != noErr ) { 836 return 1; 837 } 838 839 mbPtr->flags |= (CONTROL_INITIALIZED | FIRST_DRAW); 840 if (IsWindowActive(mbPtr->windowRef)) { 841 mbPtr->flags |= ACTIVE; 842 } 843 return 0; 844} 845 846/* 847 *-------------------------------------------------------------- 848 * 849 * TkMacOSXDrawControl -- 850 * 851 * This function draws the tk button using Mac controls 852 * In addition, this code may apply custom colors passed 853 * in the TkButton. 854 * 855 * Results: 856 * None. 857 * 858 * Side effects: 859 * The control is created, or reinitialised as needed. 860 * 861 *-------------------------------------------------------------- 862 */ 863 864static void 865TkMacOSXDrawControl( 866 MacButton *mbPtr, /* Mac button. */ 867 GWorldPtr destPort, /* Off screen GWorld. */ 868 GC gc, /* The GC we are drawing into - needed for the 869 * bevel button */ 870 Pixmap pixmap) /* The pixmap we are drawing into - needed for 871 * the bevel button */ 872{ 873 TkButton *butPtr = (TkButton *) mbPtr; 874 TkWindow *winPtr; 875 Rect paneRect, cntrRect; 876 int active, enabled; 877 int rebuild; 878 879 winPtr = (TkWindow *) butPtr->tkwin; 880 881 paneRect.left = winPtr->privatePtr->xOff; 882 paneRect.top = winPtr->privatePtr->yOff; 883 paneRect.right = paneRect.left + Tk_Width(butPtr->tkwin); 884 paneRect.bottom = paneRect.top + Tk_Height(butPtr->tkwin); 885 886 cntrRect = paneRect; 887 888/* 889 cntrRect.left += butPtr->inset; 890 cntrRect.top += butPtr->inset; 891 cntrRect.right -= butPtr->inset; 892 cntrRect.bottom -= butPtr->inset; 893*/ 894 cntrRect.left += DEF_INSET_LEFT; 895 cntrRect.top += DEF_INSET_TOP; 896 cntrRect.right -= DEF_INSET_RIGHT; 897 cntrRect.bottom -= DEF_INSET_BOTTOM; 898 899 /* 900 * The control has been previously initialised. 901 * It may need to be re-initialised 902 */ 903#if 0 904 rebuild = (winPtr->flags & TK_REBUILD_TOPLEVEL); 905 winPtr->flags &= ~TK_REBUILD_TOPLEVEL; 906#else 907 rebuild = 0; 908#endif 909 if (mbPtr->flags) { 910 MacControlParams params; 911 912 TkMacOSXComputeControlParams(butPtr, ¶ms); 913 if (rebuild || bcmp(¶ms, &mbPtr->params, sizeof(params))) { 914 /* 915 * The type of control has changed. 916 * Clean it up and clear the flag. 917 */ 918 919 if (mbPtr->userPane) { 920 DisposeControl(mbPtr->userPane); 921 mbPtr->userPane = NULL; 922 mbPtr->control = NULL; 923 } 924 mbPtr->flags = 0; 925 } 926 } 927 if (!(mbPtr->flags & CONTROL_INITIALIZED)) { 928 if (TkMacOSXInitControl(mbPtr, destPort, gc, pixmap, &paneRect, 929 &cntrRect)) { 930 return; 931 } 932 } 933 SetControlBounds(mbPtr->userPane, &paneRect); 934 SetControlBounds(mbPtr->control, &cntrRect); 935 936 if (!mbPtr->useTkText) { 937 Str255 controlTitle; 938 ControlFontStyleRec fontStyle; 939 Tk_Font font; 940 int len; 941 942 if (((mbPtr->info.image == NULL) && (mbPtr->info.bitmap == None)) 943 || (mbPtr->info.compound != COMPOUND_NONE)) { 944 len = TkFontGetFirstTextLayout(butPtr->textLayout, 945 &font, (char*) controlTitle); 946 controlTitle[len] = 0; 947 } else { 948 len = 0; 949 controlTitle[0] = 0; 950 } 951 if (rebuild || bcmp(mbPtr->controlTitle, controlTitle, len+1)) { 952 CFStringRef cf = CFStringCreateWithCString(NULL, 953 (char*) controlTitle, kCFStringEncodingUTF8); 954 955 if (cf != NULL) { 956 SetControlTitleWithCFString(mbPtr->control, cf); 957 CFRelease(cf); 958 } 959 bcopy(controlTitle, mbPtr->controlTitle, len+1); 960 } 961 if (len) { 962 TkMacOSXInitControlFontStyle(font, &fontStyle); 963 if (bcmp(&mbPtr->fontStyle, &fontStyle, sizeof(fontStyle)) ) { 964 ChkErr(SetControlFontStyle, mbPtr->control, &fontStyle); 965 bcopy(&fontStyle, &mbPtr->fontStyle, sizeof(fontStyle)); 966 } 967 } 968 } 969 if (mbPtr->params.isBevel) { 970 /* 971 * Initialiase the image/button parameters. 972 */ 973 974 SetupBevelButton(mbPtr, mbPtr->control, destPort, gc, pixmap); 975 } 976 977 if (butPtr->flags & SELECTED) { 978 SetControlValue(mbPtr->control, 1); 979#if 0 980 } else if (butPtr->flags & TRISTATED) { 981 SetControlValue(mbPtr->control, 2); 982#endif 983 } else { 984 SetControlValue(mbPtr->control, 0); 985 } 986 987 active = ((mbPtr->flags & ACTIVE) != 0); 988 if (active != IsControlActive(mbPtr->control)) { 989 if (active) { 990 ChkErr(ActivateControl, mbPtr->control); 991 } else { 992 ChkErr(DeactivateControl, mbPtr->control); 993 } 994 } 995 enabled = !(butPtr->state == STATE_DISABLED); 996 if (enabled != IsControlEnabled(mbPtr->control)) { 997 if (enabled) { 998 ChkErr(EnableControl, mbPtr->control); 999 } else { 1000 ChkErr(DisableControl, mbPtr->control); 1001 } 1002 } 1003 if (active && enabled) { 1004 if (butPtr->state == STATE_ACTIVE) { 1005 if (mbPtr->params.isBevel) { 1006 HiliteControl(mbPtr->control, kControlButtonPart); 1007 } else { 1008 switch (butPtr->type) { 1009 case TYPE_BUTTON: 1010 HiliteControl(mbPtr->control, kControlButtonPart); 1011 break; 1012 case TYPE_RADIO_BUTTON: 1013 HiliteControl(mbPtr->control, kControlRadioButtonPart); 1014 break; 1015 case TYPE_CHECK_BUTTON: 1016 HiliteControl(mbPtr->control, kControlCheckBoxPart); 1017 break; 1018 } 1019 } 1020 } else { 1021 HiliteControl(mbPtr->control, kControlNoPart); 1022 } 1023 } 1024 UpdateControlColors(mbPtr); 1025 1026 if (butPtr->type == TYPE_BUTTON && !mbPtr->params.isBevel) { 1027 Boolean isDefault; 1028 1029 if (butPtr->defaultState == STATE_ACTIVE) { 1030 isDefault = true; 1031 } else { 1032 isDefault = false; 1033 } 1034 ChkErr(SetControlData, mbPtr->control, kControlNoPart, 1035 kControlPushButtonDefaultTag, sizeof(isDefault), &isDefault); 1036 } 1037 1038 if (mbPtr->flags & FIRST_DRAW) { 1039 ShowControl(mbPtr->userPane); 1040 ShowControl(mbPtr->control); 1041 mbPtr->flags ^= FIRST_DRAW; 1042 } else { 1043 SetControlVisibility(mbPtr->control, true, true); 1044 Draw1Control(mbPtr->userPane); 1045 } 1046 1047 if (mbPtr->params.isBevel) { 1048 if (mbPtr->bevelButtonContent.contentType == 1049 kControlContentPictHandle) { 1050 KillPicture(mbPtr->bevelButtonContent.u.picture); 1051 } 1052 } 1053} 1054 1055/* 1056 *-------------------------------------------------------------- 1057 * 1058 * SetupBevelButton -- 1059 * 1060 * Sets up the Bevel Button with image by copying the 1061 * source image onto the PicHandle for the button. 1062 * 1063 * Results: 1064 * None 1065 * 1066 * Side effects: 1067 * The image or bitmap for the button is copied over to a picture. 1068 * 1069 *-------------------------------------------------------------- 1070 */ 1071 1072void 1073SetupBevelButton( 1074 MacButton *mbPtr, /* Mac button. */ 1075 ControlRef controlHandle, /* The control to set this picture to. */ 1076 GWorldPtr destPort, /* Off screen GWorld. */ 1077 GC gc, /* The GC we are drawing into - needed for the 1078 * bevel button. */ 1079 Pixmap pixmap) /* The pixmap we are drawing into - needed for 1080 * the bevel button. */ 1081{ 1082 TkButton *butPtr = (TkButton *) mbPtr; 1083 int height, width; 1084 ControlButtonGraphicAlignment theAlignment; 1085 CGrafPtr savePort; 1086 Boolean portChanged = false; 1087 1088 if (butPtr->image != None) { 1089 Tk_SizeOfImage(butPtr->image, &width, &height); 1090 } else { 1091 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); 1092 } 1093 1094 if ((butPtr->width > 0) && (butPtr->width < width)) { 1095 width = butPtr->width; 1096 } 1097 if ((butPtr->height > 0) && (butPtr->height < height)) { 1098 height = butPtr->height; 1099 } 1100 1101 { 1102 portChanged = QDSwapPort(destPort, &savePort); 1103 mbPtr->picParams.version = -2; 1104 mbPtr->picParams.hRes = 0x00480000; 1105 mbPtr->picParams.vRes = 0x00480000; 1106 mbPtr->picParams.srcRect.top = 0; 1107 mbPtr->picParams.srcRect.left = 0; 1108 mbPtr->picParams.srcRect.bottom = height; 1109 mbPtr->picParams.srcRect.right = width; 1110 mbPtr->picParams.reserved1 = 0; 1111 mbPtr->picParams.reserved2 = 0; 1112 mbPtr->bevelButtonContent.contentType = kControlContentPictHandle; 1113 mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams); 1114 if (!mbPtr->bevelButtonContent.u.picture) { 1115 TkMacOSXDbgMsg("OpenCPicture failed"); 1116 } 1117 tkPictureIsOpen = 1; 1118 1119 /* 1120 * TO DO - There is one case where XCopyPlane calls CopyDeepMask, 1121 * which does not get recorded in the picture. So the bitmap code 1122 * will fail in that case. 1123 */ 1124 } 1125 1126 if (butPtr->selectImage != NULL && (butPtr->flags & SELECTED)) { 1127 Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap, 0, 0); 1128#if 0 1129 } else if (butPtr->tristateImage != NULL && (butPtr->flags & TRISTATED)) { 1130 Tk_RedrawImage(butPtr->tristateImage, 0, 0, width, height, pixmap, 0, 1131 0); 1132#endif 1133 } else if (butPtr->image != NULL) { 1134 Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, 0, 0); 1135 } else { 1136 XSetClipOrigin(butPtr->display, gc, 0, 0); 1137 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, width, 1138 height, 0, 0, 1); 1139 } 1140 1141 { 1142 ClosePicture(); 1143 tkPictureIsOpen = 0; 1144 if (portChanged) { 1145 QDSwapPort(savePort, NULL); 1146 } 1147 } 1148 ChkErr(SetControlData, controlHandle, kControlButtonPart, 1149 kControlBevelButtonContentTag, 1150 sizeof(ControlButtonContentInfo), 1151 (char *) &mbPtr->bevelButtonContent); 1152 1153 if (butPtr->anchor == TK_ANCHOR_N) { 1154 theAlignment = kControlBevelButtonAlignTop; 1155 } else if (butPtr->anchor == TK_ANCHOR_NE) { 1156 theAlignment = kControlBevelButtonAlignTopRight; 1157 } else if (butPtr->anchor == TK_ANCHOR_E) { 1158 theAlignment = kControlBevelButtonAlignRight; 1159 } else if (butPtr->anchor == TK_ANCHOR_SE) { 1160 theAlignment = kControlBevelButtonAlignBottomRight; 1161 } else if (butPtr->anchor == TK_ANCHOR_S) { 1162 theAlignment = kControlBevelButtonAlignBottom; 1163 } else if (butPtr->anchor == TK_ANCHOR_SW) { 1164 theAlignment = kControlBevelButtonAlignBottomLeft; 1165 } else if (butPtr->anchor == TK_ANCHOR_W) { 1166 theAlignment = kControlBevelButtonAlignLeft; 1167 } else if (butPtr->anchor == TK_ANCHOR_NW) { 1168 theAlignment = kControlBevelButtonAlignTopLeft; 1169 } else if (butPtr->anchor == TK_ANCHOR_CENTER) { 1170 theAlignment = kControlBevelButtonAlignCenter; 1171 } 1172 ChkErr(SetControlData, controlHandle, kControlButtonPart, 1173 kControlBevelButtonGraphicAlignTag, 1174 sizeof(ControlButtonGraphicAlignment), (char *) &theAlignment); 1175 1176 if (butPtr->compound != COMPOUND_NONE) { 1177 ControlButtonTextPlacement thePlacement = 1178 kControlBevelButtonPlaceNormally; 1179 1180 if (butPtr->compound == COMPOUND_TOP) { 1181 thePlacement = kControlBevelButtonPlaceBelowGraphic; 1182 } else if (butPtr->compound == COMPOUND_BOTTOM) { 1183 thePlacement = kControlBevelButtonPlaceAboveGraphic; 1184 } else if (butPtr->compound == COMPOUND_LEFT) { 1185 thePlacement = kControlBevelButtonPlaceToRightOfGraphic; 1186 } else if (butPtr->compound == COMPOUND_RIGHT) { 1187 thePlacement = kControlBevelButtonPlaceToLeftOfGraphic; 1188 } 1189 ChkErr(SetControlData, controlHandle, kControlButtonPart, 1190 kControlBevelButtonTextPlaceTag, 1191 sizeof(ControlButtonTextPlacement), (char *) &thePlacement); 1192 } 1193} 1194 1195/* 1196 *-------------------------------------------------------------- 1197 * 1198 * SetUserPaneDrawProc -- 1199 * 1200 * Utility function to add a UserPaneDrawProc 1201 * to a userPane control. From MoreControls code 1202 * from Apple DTS. 1203 * 1204 * Results: 1205 * MacOS system error. 1206 * 1207 * Side effects: 1208 * The user pane gets a new UserPaneDrawProc. 1209 * 1210 *-------------------------------------------------------------- 1211 */ 1212 1213OSStatus 1214SetUserPaneDrawProc( 1215 ControlRef control, 1216 ControlUserPaneDrawProcPtr upp) 1217{ 1218 ControlUserPaneDrawUPP myControlUserPaneDrawUPP; 1219 1220 myControlUserPaneDrawUPP = NewControlUserPaneDrawUPP(upp); 1221 return SetControlData(control, kControlNoPart, 1222 kControlUserPaneDrawProcTag, sizeof(myControlUserPaneDrawUPP), 1223 (Ptr) &myControlUserPaneDrawUPP); 1224} 1225 1226/* 1227 *-------------------------------------------------------------- 1228 * 1229 * SetUserPaneSetUpSpecialBackgroundProc -- 1230 * 1231 * Utility function to add a UserPaneBackgroundProc 1232 * to a userPane control 1233 * 1234 * Results: 1235 * MacOS system error. 1236 * 1237 * Side effects: 1238 * The user pane gets a new UserPaneBackgroundProc. 1239 * 1240 *-------------------------------------------------------------- 1241 */ 1242 1243OSStatus 1244SetUserPaneSetUpSpecialBackgroundProc( 1245 ControlRef control, 1246 ControlUserPaneBackgroundProcPtr upp) 1247{ 1248 ControlUserPaneBackgroundUPP myControlUserPaneBackgroundUPP; 1249 1250 myControlUserPaneBackgroundUPP = NewControlUserPaneBackgroundUPP(upp); 1251 return SetControlData(control, kControlNoPart, 1252 kControlUserPaneBackgroundProcTag, 1253 sizeof(myControlUserPaneBackgroundUPP), 1254 (Ptr) &myControlUserPaneBackgroundUPP); 1255} 1256 1257/* 1258 *-------------------------------------------------------------- 1259 * 1260 * UserPaneDraw -- 1261 * 1262 * This function draws the background of the user pane that will 1263 * lie under checkboxes and radiobuttons. 1264 * 1265 * Results: 1266 * None. 1267 * 1268 * Side effects: 1269 * The user pane gets updated to the current color. 1270 * 1271 *-------------------------------------------------------------- 1272 */ 1273 1274void 1275UserPaneDraw( 1276 ControlRef control, 1277 ControlPartCode cpc) 1278{ 1279 MacButton *mbPtr = (MacButton *)(intptr_t)GetControlReference(control); 1280 Rect contrlRect; 1281 CGrafPtr port; 1282 1283 GetPort(&port); 1284 GetControlBounds(control,&contrlRect); 1285 TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL, port); 1286 EraseRect(&contrlRect); 1287} 1288 1289/* 1290 *-------------------------------------------------------------- 1291 * 1292 * UserPaneBackgroundProc -- 1293 * 1294 * This function sets up the background of the user pane that will 1295 * lie under checkboxes and radiobuttons. 1296 * 1297 * Results: 1298 * None. 1299 * 1300 * Side effects: 1301 * The user pane background gets set to the current color. 1302 * 1303 *-------------------------------------------------------------- 1304 */ 1305 1306void 1307UserPaneBackgroundProc( 1308 ControlHandle control, 1309 ControlBackgroundPtr info) 1310{ 1311 MacButton * mbPtr = (MacButton *)(intptr_t)GetControlReference(control); 1312 1313 if (info->colorDevice) { 1314 CGrafPtr port; 1315 1316 GetPort(&port); 1317 TkMacOSXSetColorInPort(mbPtr->userPaneBackground, 0, NULL, port); 1318 } 1319} 1320 1321/* 1322 *-------------------------------------------------------------- 1323 * 1324 * UpdateControlColors -- 1325 * 1326 * This function will review the colors used to display 1327 * a Macintosh button. If any non-standard colors are 1328 * used we create a custom palette for the button, populate 1329 * with the colors for the button and install the palette. 1330 * 1331 * Under Appearance, we just set the pointer that will be 1332 * used by the UserPaneDrawProc. 1333 * 1334 * Results: 1335 * None. 1336 * 1337 * Side effects: 1338 * The Macintosh control may get a custom palette installed. 1339 * 1340 *-------------------------------------------------------------- 1341 */ 1342 1343static int 1344UpdateControlColors( 1345 MacButton *mbPtr) 1346{ 1347 XColor *xcolor; 1348 TkButton *butPtr = (TkButton *) mbPtr; 1349 1350 /* 1351 * Under Appearance we cannot change the background of the 1352 * button itself. However, the color we are setting is the color 1353 * of the containing userPane. This will be the color that peeks 1354 * around the rounded corners of the button. 1355 * We make this the highlightbackground rather than the background, 1356 * because if you color the background of a frame containing a 1357 * button, you usually also color the highlightbackground as well, 1358 * or you will get a thin grey ring around the button. 1359 */ 1360 1361 if (butPtr->type == TYPE_BUTTON) { 1362 xcolor = Tk_3DBorderColor(butPtr->highlightBorder); 1363 } else { 1364 xcolor = Tk_3DBorderColor(butPtr->normalBorder); 1365 } 1366 mbPtr->userPaneBackground = xcolor->pixel; 1367 1368 return false; 1369} 1370 1371/* 1372 *-------------------------------------------------------------- 1373 * 1374 * ButtonEventProc -- 1375 * 1376 * This procedure is invoked by the Tk dispatcher for various 1377 * events on buttons. 1378 * 1379 * Results: 1380 * None. 1381 * 1382 * Side effects: 1383 * When it gets exposed, it is redisplayed. 1384 * 1385 *-------------------------------------------------------------- 1386 */ 1387 1388static void 1389ButtonEventProc( 1390 ClientData clientData, /* Information about window. */ 1391 XEvent *eventPtr) /* Information about event. */ 1392{ 1393 TkButton *buttonPtr = (TkButton *) clientData; 1394 MacButton *mbPtr = (MacButton *) clientData; 1395 1396 if (eventPtr->type == ActivateNotify 1397 || eventPtr->type == DeactivateNotify) { 1398 if ((buttonPtr->tkwin == NULL) || (!Tk_IsMapped(buttonPtr->tkwin))) { 1399 return; 1400 } 1401 if (eventPtr->type == ActivateNotify) { 1402 mbPtr->flags |= ACTIVE; 1403 } else { 1404 mbPtr->flags &= ~ACTIVE; 1405 } 1406 if ((buttonPtr->flags & REDRAW_PENDING) == 0) { 1407 Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) buttonPtr); 1408 buttonPtr->flags |= REDRAW_PENDING; 1409 } 1410 } 1411} 1412 1413/* 1414 *---------------------------------------------------------------------- 1415 * 1416 * TkMacOSXComputeControlParams -- 1417 * 1418 * This procedure computes the various parameters used 1419 * when creating a Carbon control (NewControl). 1420 * These are determined by the various tk button parameters 1421 * 1422 * Results: 1423 * None. 1424 * 1425 * Side effects: 1426 * Sets the control initialisation parameters 1427 * 1428 *---------------------------------------------------------------------- 1429 */ 1430 1431static void 1432TkMacOSXComputeControlParams( 1433 TkButton *butPtr, 1434 MacControlParams *paramsPtr) 1435{ 1436 paramsPtr->isBevel = 0; 1437 1438 /* 1439 * Determine ProcID based on button type and dimensions. 1440 */ 1441 1442 switch (butPtr->type) { 1443 case TYPE_BUTTON: 1444 if ((butPtr->image == None) && (butPtr->bitmap == None)) { 1445 paramsPtr->initialValue = 1; 1446 paramsPtr->minValue = 0; 1447 paramsPtr->maxValue = 1; 1448 paramsPtr->procID = kControlPushButtonProc; 1449 } else { 1450 paramsPtr->initialValue = 0; 1451 paramsPtr->minValue = kControlBehaviorOffsetContents | 1452 kControlContentPictHandle; 1453 paramsPtr->maxValue = 1; 1454 if (butPtr->borderWidth <= 2) { 1455 paramsPtr->procID = kControlBevelButtonSmallBevelProc; 1456 } else if (butPtr->borderWidth == 3) { 1457 paramsPtr->procID = kControlBevelButtonNormalBevelProc; 1458 } else { 1459 paramsPtr->procID = kControlBevelButtonLargeBevelProc; 1460 } 1461 paramsPtr->isBevel = 1; 1462 } 1463 break; 1464 case TYPE_RADIO_BUTTON: 1465 if (((butPtr->image == None) && (butPtr->bitmap == None)) 1466 || (butPtr->indicatorOn)) { 1467 paramsPtr->initialValue = 1; 1468 paramsPtr->minValue = 0; 1469 paramsPtr->maxValue = MAX_VALUE; 1470 paramsPtr->procID = kControlRadioButtonProc; 1471 } else { 1472 paramsPtr->initialValue = 0; 1473 paramsPtr->minValue = kControlBehaviorOffsetContents | 1474 kControlBehaviorSticky | kControlContentPictHandle; 1475 paramsPtr->maxValue = MAX_VALUE; 1476 if (butPtr->borderWidth <= 2) { 1477 paramsPtr->procID = kControlBevelButtonSmallBevelProc; 1478 } else if (butPtr->borderWidth == 3) { 1479 paramsPtr->procID = kControlBevelButtonNormalBevelProc; 1480 } else { 1481 paramsPtr->procID = kControlBevelButtonLargeBevelProc; 1482 } 1483 paramsPtr->isBevel = 1; 1484 } 1485 break; 1486 case TYPE_CHECK_BUTTON: 1487 if (((butPtr->image == None) && (butPtr->bitmap == None)) 1488 || (butPtr->indicatorOn)) { 1489 paramsPtr->initialValue = 1; 1490 paramsPtr->minValue = 0; 1491 paramsPtr->maxValue = MAX_VALUE; 1492 paramsPtr->procID = kControlCheckBoxProc; 1493 } else { 1494 paramsPtr->initialValue = 0; 1495 paramsPtr->minValue = kControlBehaviorOffsetContents | 1496 kControlBehaviorSticky | kControlContentPictHandle; 1497 paramsPtr->maxValue = MAX_VALUE; 1498 if (butPtr->borderWidth <= 2) { 1499 paramsPtr->procID = kControlBevelButtonSmallBevelProc; 1500 } else if (butPtr->borderWidth == 3) { 1501 paramsPtr->procID = kControlBevelButtonNormalBevelProc; 1502 } else { 1503 paramsPtr->procID = kControlBevelButtonLargeBevelProc; 1504 } 1505 paramsPtr->isBevel = 1; 1506 } 1507 break; 1508 } 1509} 1510 1511/* 1512 *---------------------------------------------------------------------- 1513 * 1514 * TkMacOSXComputeDrawParams -- 1515 * 1516 * This procedure computes the various parameters used 1517 * when drawing a button 1518 * These are determined by the various tk button parameters 1519 * 1520 * Results: 1521 * 1 if control will be used, 0 otherwise. 1522 * 1523 * Side effects: 1524 * Sets the button draw parameters 1525 * 1526 *---------------------------------------------------------------------- 1527 */ 1528 1529static int 1530TkMacOSXComputeDrawParams( 1531 TkButton *butPtr, 1532 DrawParams *dpPtr) 1533{ 1534 dpPtr->hasImageOrBitmap = ((butPtr->image != NULL) 1535 || (butPtr->bitmap != None)); 1536 dpPtr->offset = (butPtr->type == TYPE_BUTTON) 1537 && dpPtr->hasImageOrBitmap; 1538 dpPtr->border = butPtr->normalBorder; 1539 if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) { 1540 dpPtr->gc = butPtr->disabledGC; 1541 } else if (butPtr->type == TYPE_BUTTON && butPtr->state == STATE_ACTIVE) { 1542 dpPtr->gc = butPtr->activeTextGC; 1543 dpPtr->border = butPtr->activeBorder; 1544 } else { 1545 dpPtr->gc = butPtr->normalTextGC; 1546 } 1547 1548 if ((butPtr->flags & SELECTED) && (butPtr->state != STATE_ACTIVE) 1549 && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) { 1550 dpPtr->border = butPtr->selectBorder; 1551 } 1552 1553 /* 1554 * Override the relief specified for the button if this is a 1555 * checkbutton or radiobutton and there's no indicator. 1556 * However, don't do this in the presence of Appearance, since 1557 * then the bevel button will take care of the relief. 1558 */ 1559 1560 dpPtr->relief = butPtr->relief; 1561 1562 if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) { 1563 if (!dpPtr->hasImageOrBitmap) { 1564 dpPtr->relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN 1565 : TK_RELIEF_RAISED; 1566 } 1567 } 1568 1569 /* 1570 * Determine the draw type 1571 */ 1572 1573 if (butPtr->type == TYPE_LABEL) { 1574 dpPtr->drawType = DRAW_LABEL; 1575 } else if (butPtr->type == TYPE_BUTTON) { 1576 if (!dpPtr->hasImageOrBitmap) { 1577 dpPtr->drawType = DRAW_CONTROL; 1578 } else if (butPtr->image != None) { 1579 dpPtr->drawType = DRAW_BEVEL; 1580 } else { 1581 /* 1582 * TO DO - The current way the we draw bitmaps (XCopyPlane) 1583 * uses CopyDeepMask in this one case. The Picture recording 1584 * does not record this call, and so we can't use the 1585 * Appearance bevel button here. The only case that would 1586 * exercise this is if you use a bitmap, with 1587 * -data & -mask specified. We should probably draw the 1588 * appearance button and overprint the image in this case. 1589 * This just punts and draws the old-style, ugly, button. 1590 */ 1591 1592 if (dpPtr->gc->clip_mask == 0) { 1593 dpPtr->drawType = DRAW_BEVEL; 1594 } else { 1595 TkpClipMask *clipPtr = (TkpClipMask *) dpPtr->gc->clip_mask; 1596 1597 if ((clipPtr->type == TKP_CLIP_PIXMAP) && 1598 (clipPtr->value.pixmap != butPtr->bitmap)) { 1599 dpPtr->drawType = DRAW_CUSTOM; 1600 } else { 1601 dpPtr->drawType = DRAW_BEVEL; 1602 } 1603 } 1604 } 1605 } else if (butPtr->indicatorOn) { 1606 dpPtr->drawType = DRAW_CONTROL; 1607 } else if (dpPtr->hasImageOrBitmap) { 1608 if (dpPtr->gc->clip_mask == 0) { 1609 dpPtr->drawType = DRAW_BEVEL; 1610 } else { 1611 TkpClipMask *clipPtr = (TkpClipMask*) dpPtr->gc->clip_mask; 1612 1613 if ((clipPtr->type == TKP_CLIP_PIXMAP) && 1614 (clipPtr->value.pixmap != butPtr->bitmap)) { 1615 dpPtr->drawType = DRAW_CUSTOM; 1616 } else { 1617 dpPtr->drawType = DRAW_BEVEL; 1618 } 1619 } 1620 } else { 1621 dpPtr->drawType = DRAW_CUSTOM; 1622 } 1623 1624 if ((dpPtr->drawType == DRAW_CONTROL) || (dpPtr->drawType == DRAW_BEVEL)) { 1625 return 1; 1626 } else { 1627 return 0; 1628 } 1629} 1630