1/* 2 * tkUnixButton.c -- 3 * 4 * This file implements the Unix specific portion of the button 5 * widgets. 6 * 7 * Copyright (c) 1996-1997 by Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: tkUnixButton.c,v 1.11.2.5 2004/12/02 02:07:43 hobbs Exp $ 13 */ 14 15#include "tkButton.h" 16 17/* 18 * Declaration of Unix specific button structure. 19 */ 20 21typedef struct UnixButton { 22 TkButton info; /* Generic button info. */ 23} UnixButton; 24 25/* 26 * The class procedure table for the button widgets. 27 */ 28 29Tk_ClassProcs tkpButtonProcs = { 30 sizeof(Tk_ClassProcs), /* size */ 31 TkButtonWorldChanged, /* worldChangedProc */ 32}; 33 34/* 35 *---------------------------------------------------------------------- 36 * 37 * TkpCreateButton -- 38 * 39 * Allocate a new TkButton structure. 40 * 41 * Results: 42 * Returns a newly allocated TkButton structure. 43 * 44 * Side effects: 45 * Registers an event handler for the widget. 46 * 47 *---------------------------------------------------------------------- 48 */ 49 50TkButton * 51TkpCreateButton(tkwin) 52 Tk_Window tkwin; 53{ 54 UnixButton *butPtr = (UnixButton *)ckalloc(sizeof(UnixButton)); 55 return (TkButton *) butPtr; 56} 57 58/* 59 *---------------------------------------------------------------------- 60 * 61 * TkpDisplayButton -- 62 * 63 * This procedure is invoked to display a button widget. It is 64 * normally invoked as an idle handler. 65 * 66 * Results: 67 * None. 68 * 69 * Side effects: 70 * Commands are output to X to display the button in its 71 * current mode. The REDRAW_PENDING flag is cleared. 72 * 73 *---------------------------------------------------------------------- 74 */ 75 76void 77TkpDisplayButton(clientData) 78 ClientData clientData; /* Information about widget. */ 79{ 80 register TkButton *butPtr = (TkButton *) clientData; 81 GC gc; 82 Tk_3DBorder border; 83 Pixmap pixmap; 84 int x = 0; /* Initialization only needed to stop 85 * compiler warning. */ 86 int y, relief; 87 Tk_Window tkwin = butPtr->tkwin; 88 int width, height, fullWidth, fullHeight; 89 int textXOffset, textYOffset; 90 int haveImage = 0, haveText = 0; 91 int offset; /* 1 means this is a button widget, so we 92 * offset the text to make the button appear 93 * to move up and down as the relief changes. 94 */ 95 int imageWidth, imageHeight; 96 int imageXOffset = 0, imageYOffset = 0; /* image information that will 97 * be used to restrict disabled 98 * pixmap as well */ 99 100 butPtr->flags &= ~REDRAW_PENDING; 101 if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { 102 return; 103 } 104 105 border = butPtr->normalBorder; 106 if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) { 107 gc = butPtr->disabledGC; 108 } else if ((butPtr->state == STATE_ACTIVE) 109 && !Tk_StrictMotif(butPtr->tkwin)) { 110 gc = butPtr->activeTextGC; 111 border = butPtr->activeBorder; 112 } else { 113 gc = butPtr->normalTextGC; 114 } 115 if ((butPtr->flags & SELECTED) && (butPtr->state != STATE_ACTIVE) 116 && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) { 117 border = butPtr->selectBorder; 118 } 119 120 /* 121 * Override the relief specified for the button if this is a 122 * checkbutton or radiobutton and there's no indicator. The new 123 * relief is as follows: 124 * If the button is select --> "sunken" 125 * If relief==overrelief --> relief 126 * Otherwise --> overrelief 127 * 128 * The effect we are trying to achieve is as follows: 129 * 130 * value mouse-over? --> relief 131 * ------- ------------ -------- 132 * off no flat 133 * off yes raised 134 * on no sunken 135 * on yes sunken 136 * 137 * This is accomplished by configuring the checkbutton or radiobutton 138 * like this: 139 * 140 * -indicatoron 0 -overrelief raised -offrelief flat 141 * 142 * Bindings (see library/button.tcl) will copy the -overrelief into 143 * -relief on mouseover. Hence, we can tell if we are in mouse-over by 144 * comparing relief against overRelief. This is an aweful kludge, but 145 * it gives use the desired behavior while keeping the code backwards 146 * compatible. 147 */ 148 149 relief = butPtr->relief; 150 if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) { 151 if (butPtr->flags & SELECTED) { 152 relief = TK_RELIEF_SUNKEN; 153 } else if (butPtr->overRelief != relief) { 154 relief = butPtr->offRelief; 155 } 156 } 157 158 offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin); 159 160 /* 161 * In order to avoid screen flashes, this procedure redraws 162 * the button in a pixmap, then copies the pixmap to the 163 * screen in a single operation. This means that there's no 164 * point in time where the on-sreen image has been cleared. 165 */ 166 167 pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin), 168 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); 169 Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin), 170 Tk_Height(tkwin), 0, TK_RELIEF_FLAT); 171 172 /* 173 * Display image or bitmap or text for button. 174 */ 175 176 if (butPtr->image != NULL) { 177 Tk_SizeOfImage(butPtr->image, &width, &height); 178 haveImage = 1; 179 } else if (butPtr->bitmap != None) { 180 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); 181 haveImage = 1; 182 } 183 imageWidth = width; 184 imageHeight = height; 185 186 haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0); 187 188 if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { 189 textXOffset = 0; 190 textYOffset = 0; 191 fullWidth = 0; 192 fullHeight = 0; 193 194 switch ((enum compound) butPtr->compound) { 195 case COMPOUND_TOP: 196 case COMPOUND_BOTTOM: { 197 /* Image is above or below text */ 198 if (butPtr->compound == COMPOUND_TOP) { 199 textYOffset = height + butPtr->padY; 200 } else { 201 imageYOffset = butPtr->textHeight + butPtr->padY; 202 } 203 fullHeight = height + butPtr->textHeight + butPtr->padY; 204 fullWidth = (width > butPtr->textWidth ? width : 205 butPtr->textWidth); 206 textXOffset = (fullWidth - butPtr->textWidth)/2; 207 imageXOffset = (fullWidth - width)/2; 208 break; 209 } 210 case COMPOUND_LEFT: 211 case COMPOUND_RIGHT: { 212 /* Image is left or right of text */ 213 if (butPtr->compound == COMPOUND_LEFT) { 214 textXOffset = width + butPtr->padX; 215 } else { 216 imageXOffset = butPtr->textWidth + butPtr->padX; 217 } 218 fullWidth = butPtr->textWidth + butPtr->padX + width; 219 fullHeight = (height > butPtr->textHeight ? height : 220 butPtr->textHeight); 221 textYOffset = (fullHeight - butPtr->textHeight)/2; 222 imageYOffset = (fullHeight - height)/2; 223 break; 224 } 225 case COMPOUND_CENTER: { 226 /* Image and text are superimposed */ 227 fullWidth = (width > butPtr->textWidth ? width : 228 butPtr->textWidth); 229 fullHeight = (height > butPtr->textHeight ? height : 230 butPtr->textHeight); 231 textXOffset = (fullWidth - butPtr->textWidth)/2; 232 imageXOffset = (fullWidth - width)/2; 233 textYOffset = (fullHeight - butPtr->textHeight)/2; 234 imageYOffset = (fullHeight - height)/2; 235 break; 236 } 237 case COMPOUND_NONE: {break;} 238 } 239 240 TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, 241 butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y); 242 243 x += butPtr->indicatorSpace; 244 245 x += offset; 246 y += offset; 247 if (relief == TK_RELIEF_RAISED) { 248 x -= offset; 249 y -= offset; 250 } else if (relief == TK_RELIEF_SUNKEN) { 251 x += offset; 252 y += offset; 253 } 254 255 imageXOffset += x; 256 imageYOffset += y; 257 258 if (butPtr->image != NULL) { 259 /* 260 * Do boundary clipping, so that Tk_RedrawImage is passed 261 * valid coordinates. [Bug 979239] 262 */ 263 264 if (imageXOffset < 0) { 265 imageXOffset = 0; 266 } 267 if (imageYOffset < 0) { 268 imageYOffset = 0; 269 } 270 if (width > Tk_Width(tkwin)) { 271 width = Tk_Width(tkwin); 272 } 273 if (height > Tk_Height(tkwin)) { 274 height = Tk_Height(tkwin); 275 } 276 if ((width + imageXOffset) > Tk_Width(tkwin)) { 277 imageXOffset = Tk_Width(tkwin) - width; 278 } 279 if ((height + imageYOffset) > Tk_Height(tkwin)) { 280 imageYOffset = Tk_Height(tkwin) - height; 281 } 282 283 if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) { 284 Tk_RedrawImage(butPtr->selectImage, 0, 0, 285 width, height, pixmap, imageXOffset, imageYOffset); 286 } else { 287 Tk_RedrawImage(butPtr->image, 0, 0, width, 288 height, pixmap, imageXOffset, imageYOffset); 289 } 290 } else { 291 XSetClipOrigin(butPtr->display, gc, imageXOffset, imageYOffset); 292 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 293 0, 0, (unsigned int) width, (unsigned int) height, 294 imageXOffset, imageYOffset, 1); 295 XSetClipOrigin(butPtr->display, gc, 0, 0); 296 } 297 298 Tk_DrawTextLayout(butPtr->display, pixmap, gc, 299 butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1); 300 Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, 301 butPtr->textLayout, x + textXOffset, y + textYOffset, 302 butPtr->underline); 303 y += fullHeight/2; 304 } else { 305 if (haveImage) { 306 TkComputeAnchor(butPtr->anchor, tkwin, 0, 0, 307 butPtr->indicatorSpace + width, height, &x, &y); 308 x += butPtr->indicatorSpace; 309 310 x += offset; 311 y += offset; 312 if (relief == TK_RELIEF_RAISED) { 313 x -= offset; 314 y -= offset; 315 } else if (relief == TK_RELIEF_SUNKEN) { 316 x += offset; 317 y += offset; 318 } 319 imageXOffset += x; 320 imageYOffset += y; 321 if (butPtr->image != NULL) { 322 /* 323 * Do boundary clipping, so that Tk_RedrawImage is passed 324 * valid coordinates. [Bug 979239] 325 */ 326 327 if (imageXOffset < 0) { 328 imageXOffset = 0; 329 } 330 if (imageYOffset < 0) { 331 imageYOffset = 0; 332 } 333 if (width > Tk_Width(tkwin)) { 334 width = Tk_Width(tkwin); 335 } 336 if (height > Tk_Height(tkwin)) { 337 height = Tk_Height(tkwin); 338 } 339 if ((width + imageXOffset) > Tk_Width(tkwin)) { 340 imageXOffset = Tk_Width(tkwin) - width; 341 } 342 if ((height + imageYOffset) > Tk_Height(tkwin)) { 343 imageYOffset = Tk_Height(tkwin) - height; 344 } 345 346 if ((butPtr->selectImage != NULL) && 347 (butPtr->flags & SELECTED)) { 348 Tk_RedrawImage(butPtr->selectImage, 0, 0, width, 349 height, pixmap, imageXOffset, imageYOffset); 350 } else { 351 Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, 352 imageXOffset, imageYOffset); 353 } 354 } else { 355 XSetClipOrigin(butPtr->display, gc, x, y); 356 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, 357 (unsigned int) width, (unsigned int) height, x, y, 1); 358 XSetClipOrigin(butPtr->display, gc, 0, 0); 359 } 360 y += height/2; 361 } else { 362 TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, 363 butPtr->indicatorSpace + butPtr->textWidth, 364 butPtr->textHeight, &x, &y); 365 366 x += butPtr->indicatorSpace; 367 368 x += offset; 369 y += offset; 370 if (relief == TK_RELIEF_RAISED) { 371 x -= offset; 372 y -= offset; 373 } else if (relief == TK_RELIEF_SUNKEN) { 374 x += offset; 375 y += offset; 376 } 377 Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout, 378 x, y, 0, -1); 379 Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, 380 butPtr->textLayout, x, y, butPtr->underline); 381 y += butPtr->textHeight/2; 382 } 383 } 384 385 /* 386 * Draw the indicator for check buttons and radio buttons. At this 387 * point x and y refer to the top-left corner of the text or image 388 * or bitmap. 389 */ 390 391 if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 392 int dim; 393 394 dim = butPtr->indicatorDiameter; 395 x -= butPtr->indicatorSpace; 396 y -= dim/2; 397 if (dim > 2*butPtr->borderWidth) { 398 Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim, 399 butPtr->borderWidth, 400 (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN : 401 TK_RELIEF_RAISED); 402 x += butPtr->borderWidth; 403 y += butPtr->borderWidth; 404 dim -= 2*butPtr->borderWidth; 405 if (butPtr->flags & SELECTED) { 406 GC gc; 407 if (butPtr->state != STATE_DISABLED) { 408 if (butPtr->selectBorder != NULL) { 409 gc = Tk_3DBorderGC(tkwin, butPtr->selectBorder, 410 TK_3D_FLAT_GC); 411 } else { 412 gc = Tk_3DBorderGC(tkwin, butPtr->normalBorder, 413 TK_3D_FLAT_GC); 414 } 415 } else { 416 if (butPtr->disabledFg != NULL) { 417 gc = butPtr->disabledGC; 418 } else { 419 gc = butPtr->normalTextGC; 420 XSetForeground(butPtr->display, butPtr->disabledGC, 421 Tk_3DBorderColor(butPtr->normalBorder)->pixel); 422 } 423 } 424 425 XFillRectangle(butPtr->display, pixmap, gc, x, y, 426 (unsigned int) dim, (unsigned int) dim); 427 } else { 428 Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y, 429 dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT); 430 } 431 } 432 } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) { 433 XPoint points[4]; 434 int radius; 435 436 radius = butPtr->indicatorDiameter/2; 437 points[0].x = x - butPtr->indicatorSpace; 438 points[0].y = y; 439 points[1].x = points[0].x + radius; 440 points[1].y = points[0].y + radius; 441 points[2].x = points[1].x + radius; 442 points[2].y = points[0].y; 443 points[3].x = points[1].x; 444 points[3].y = points[0].y - radius; 445 if (butPtr->flags & SELECTED) { 446 GC gc; 447 448 if (butPtr->state != STATE_DISABLED) { 449 if (butPtr->selectBorder != NULL) { 450 gc = Tk_3DBorderGC(tkwin, butPtr->selectBorder, 451 TK_3D_FLAT_GC); 452 } else { 453 gc = Tk_3DBorderGC(tkwin, butPtr->normalBorder, 454 TK_3D_FLAT_GC); 455 } 456 } else { 457 if (butPtr->disabledFg != NULL) { 458 gc = butPtr->disabledGC; 459 } else { 460 gc = butPtr->normalTextGC; 461 XSetForeground(butPtr->display, butPtr->disabledGC, 462 Tk_3DBorderColor(butPtr->normalBorder)->pixel); 463 } 464 } 465 466 XFillPolygon(butPtr->display, pixmap, gc, points, 4, Convex, 467 CoordModeOrigin); 468 } else { 469 Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points, 470 4, butPtr->borderWidth, TK_RELIEF_FLAT); 471 } 472 Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, butPtr->borderWidth, 473 (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN : 474 TK_RELIEF_RAISED); 475 } 476 477 /* 478 * If the button is disabled with a stipple rather than a special 479 * foreground color, generate the stippled effect. If the widget 480 * is selected and we use a different background color when selected, 481 * must temporarily modify the GC so the stippling is the right color. 482 */ 483 484 if ((butPtr->state == STATE_DISABLED) 485 && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) { 486 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn 487 && (butPtr->selectBorder != NULL)) { 488 XSetForeground(butPtr->display, butPtr->stippleGC, 489 Tk_3DBorderColor(butPtr->selectBorder)->pixel); 490 } 491 /* 492 * Stipple the whole button if no disabledFg was specified, 493 * otherwise restrict stippling only to displayed image 494 */ 495 if (butPtr->disabledFg == NULL) { 496 XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 0, 0, 497 (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin)); 498 } else { 499 XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 500 imageXOffset, imageYOffset, 501 (unsigned) imageWidth, (unsigned) imageHeight); 502 } 503 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn 504 && (butPtr->selectBorder != NULL)) { 505 XSetForeground(butPtr->display, butPtr->stippleGC, 506 Tk_3DBorderColor(butPtr->normalBorder)->pixel); 507 } 508 } 509 510 /* 511 * Draw the border and traversal highlight last. This way, if the 512 * button's contents overflow they'll be covered up by the border. 513 * This code is complicated by the possible combinations of focus 514 * highlight and default rings. We draw the focus and highlight rings 515 * using the highlight border and highlight foreground color. 516 */ 517 518 if (relief != TK_RELIEF_FLAT) { 519 int inset = butPtr->highlightWidth; 520 521 if (butPtr->defaultState == DEFAULT_ACTIVE) { 522 /* 523 * Draw the default ring with 2 pixels of space between the 524 * default ring and the button and the default ring and the 525 * focus ring. Note that we need to explicitly draw the space 526 * in the highlightBorder color to ensure that we overwrite any 527 * overflow text and/or a different button background color. 528 */ 529 530 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, 531 inset, Tk_Width(tkwin) - 2*inset, 532 Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT); 533 inset += 2; 534 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, 535 inset, Tk_Width(tkwin) - 2*inset, 536 Tk_Height(tkwin) - 2*inset, 1, TK_RELIEF_SUNKEN); 537 inset++; 538 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, 539 inset, Tk_Width(tkwin) - 2*inset, 540 Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT); 541 542 inset += 2; 543 } else if (butPtr->defaultState == DEFAULT_NORMAL) { 544 /* 545 * Leave room for the default ring and write over any text or 546 * background color. 547 */ 548 549 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0, 550 0, Tk_Width(tkwin), Tk_Height(tkwin), 5, TK_RELIEF_FLAT); 551 inset += 5; 552 } 553 554 /* 555 * Draw the button border. 556 */ 557 558 Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset, 559 Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset, 560 butPtr->borderWidth, relief); 561 } 562 if (butPtr->highlightWidth > 0) { 563 GC gc; 564 565 if (butPtr->flags & GOT_FOCUS) { 566 gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap); 567 } else { 568 gc = Tk_GCForColor(Tk_3DBorderColor(butPtr->highlightBorder), 569 pixmap); 570 } 571 572 /* 573 * Make sure the focus ring shrink-wraps the actual button, not the 574 * padding space left for a default ring. 575 */ 576 577 if (butPtr->defaultState == DEFAULT_NORMAL) { 578 TkDrawInsetFocusHighlight(tkwin, gc, butPtr->highlightWidth, 579 pixmap, 5); 580 } else { 581 Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap); 582 } 583 } 584 585 /* 586 * Copy the information from the off-screen pixmap onto the screen, 587 * then delete the pixmap. 588 */ 589 590 XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin), 591 butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin), 592 (unsigned) Tk_Height(tkwin), 0, 0); 593 Tk_FreePixmap(butPtr->display, pixmap); 594} 595 596/* 597 *---------------------------------------------------------------------- 598 * 599 * TkpComputeButtonGeometry -- 600 * 601 * After changes in a button's text or bitmap, this procedure 602 * recomputes the button's geometry and passes this information 603 * along to the geometry manager for the window. 604 * 605 * Results: 606 * None. 607 * 608 * Side effects: 609 * The button's window may change size. 610 * 611 *---------------------------------------------------------------------- 612 */ 613 614void 615TkpComputeButtonGeometry(butPtr) 616 register TkButton *butPtr; /* Button whose geometry may have changed. */ 617{ 618 int width, height, avgWidth, txtWidth, txtHeight; 619 int haveImage = 0, haveText = 0; 620 Tk_FontMetrics fm; 621 622 butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth; 623 624 /* 625 * Leave room for the default ring if needed. 626 */ 627 628 if (butPtr->defaultState != DEFAULT_DISABLED) { 629 butPtr->inset += 5; 630 } 631 butPtr->indicatorSpace = 0; 632 633 width = 0; 634 height = 0; 635 txtWidth = 0; 636 txtHeight = 0; 637 avgWidth = 0; 638 639 if (butPtr->image != NULL) { 640 Tk_SizeOfImage(butPtr->image, &width, &height); 641 haveImage = 1; 642 } else if (butPtr->bitmap != None) { 643 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); 644 haveImage = 1; 645 } 646 647 if (haveImage == 0 || butPtr->compound != COMPOUND_NONE) { 648 Tk_FreeTextLayout(butPtr->textLayout); 649 650 butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont, 651 Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength, 652 butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight); 653 654 txtWidth = butPtr->textWidth; 655 txtHeight = butPtr->textHeight; 656 avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1); 657 Tk_GetFontMetrics(butPtr->tkfont, &fm); 658 haveText = (txtWidth != 0 && txtHeight != 0); 659 } 660 661 /* 662 * If the button is compound (ie, it shows both an image and text), 663 * the new geometry is a combination of the image and text geometry. 664 * We only honor the compound bit if the button has both text and an 665 * image, because otherwise it is not really a compound button. 666 */ 667 668 if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { 669 switch ((enum compound) butPtr->compound) { 670 case COMPOUND_TOP: 671 case COMPOUND_BOTTOM: { 672 /* Image is above or below text */ 673 height += txtHeight + butPtr->padY; 674 width = (width > txtWidth ? width : txtWidth); 675 break; 676 } 677 case COMPOUND_LEFT: 678 case COMPOUND_RIGHT: { 679 /* Image is left or right of text */ 680 width += txtWidth + butPtr->padX; 681 height = (height > txtHeight ? height : txtHeight); 682 break; 683 } 684 case COMPOUND_CENTER: { 685 /* Image and text are superimposed */ 686 width = (width > txtWidth ? width : txtWidth); 687 height = (height > txtHeight ? height : txtHeight); 688 break; 689 } 690 case COMPOUND_NONE: {break;} 691 } 692 if (butPtr->width > 0) { 693 width = butPtr->width; 694 } 695 if (butPtr->height > 0) { 696 height = butPtr->height; 697 } 698 699 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 700 butPtr->indicatorSpace = height; 701 if (butPtr->type == TYPE_CHECK_BUTTON) { 702 butPtr->indicatorDiameter = (65*height)/100; 703 } else { 704 butPtr->indicatorDiameter = (75*height)/100; 705 } 706 } 707 708 width += 2*butPtr->padX; 709 height += 2*butPtr->padY; 710 711 } else { 712 if (haveImage) { 713 if (butPtr->width > 0) { 714 width = butPtr->width; 715 } 716 if (butPtr->height > 0) { 717 height = butPtr->height; 718 } 719 720 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 721 butPtr->indicatorSpace = height; 722 if (butPtr->type == TYPE_CHECK_BUTTON) { 723 butPtr->indicatorDiameter = (65*height)/100; 724 } else { 725 butPtr->indicatorDiameter = (75*height)/100; 726 } 727 } 728 } else { 729 width = txtWidth; 730 height = txtHeight; 731 732 if (butPtr->width > 0) { 733 width = butPtr->width * avgWidth; 734 } 735 if (butPtr->height > 0) { 736 height = butPtr->height * fm.linespace; 737 } 738 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 739 butPtr->indicatorDiameter = fm.linespace; 740 if (butPtr->type == TYPE_CHECK_BUTTON) { 741 butPtr->indicatorDiameter = 742 (80*butPtr->indicatorDiameter)/100; 743 } 744 butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth; 745 } 746 } 747 } 748 749 /* 750 * When issuing the geometry request, add extra space for the indicator, 751 * if any, and for the border and padding, plus two extra pixels so the 752 * display can be offset by 1 pixel in either direction for the raised 753 * or lowered effect. 754 */ 755 756 if ((butPtr->image == NULL) && (butPtr->bitmap == None)) { 757 width += 2*butPtr->padX; 758 height += 2*butPtr->padY; 759 } 760 if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) { 761 width += 2; 762 height += 2; 763 } 764 Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace 765 + 2*butPtr->inset), (int) (height + 2*butPtr->inset)); 766 Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset); 767} 768