1/* 2 * tkUnixButton.c -- 3 * 4 * This file implements the Unix specific portion of the button widgets. 5 * 6 * Copyright (c) 1996-1997 by Sun Microsystems, Inc. 7 * 8 * See the file "license.terms" for information on usage and redistribution of 9 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 10 * 11 * RCS: @(#) $Id$ 12 */ 13 14#include "tkInt.h" 15#include "tkButton.h" 16#include "tk3d.h" 17 18/* 19 * Shared with menu widget. 20 */ 21 22MODULE_SCOPE void TkpDrawCheckIndicator(Tk_Window tkwin, 23 Display *display, Drawable d, int x, int y, 24 Tk_3DBorder bgBorder, XColor *indicatorColor, 25 XColor *selectColor, XColor *disColor, int on, 26 int disabled, int mode); 27 28/* 29 * Declaration of Unix specific button structure. 30 */ 31 32typedef struct UnixButton { 33 TkButton info; /* Generic button info. */ 34} UnixButton; 35 36/* 37 * The class function table for the button widgets. 38 */ 39 40Tk_ClassProcs tkpButtonProcs = { 41 sizeof(Tk_ClassProcs), /* size */ 42 TkButtonWorldChanged, /* worldChangedProc */ 43}; 44 45/* 46 * The button image. 47 * The header info here is ignored, it's the image that's important. The 48 * colors will be applied as follows: 49 * A = Background 50 * B = Background 51 * C = 3D light 52 * D = selectColor 53 * E = 3D dark 54 * F = Background 55 * G = Indicator Color 56 * H = disabled Indicator Color 57 */ 58 59/* XPM */ 60static char *button_images[] = { 61 /* width height ncolors chars_per_pixel */ 62 "52 26 7 1", 63 /* colors */ 64 "A c #808000000000", 65 "B c #000080800000", 66 "C c #808080800000", 67 "D c #000000008080", 68 "E c #808000008080", 69 "F c #000080808080", 70 "G c #000000000000", 71 "H c #000080800000", 72 /* pixels */ 73 "AAAAAAAAAAAABAAAAAAAAAAAABAAAAAAAAAAAABAAAAAAAAAAAAB", 74 "AEEEEEEEEEECBAEEEEEEEEEECBAEEEEEEEEEECBAEEEEEEEEEECB", 75 "AEDDDDDDDDDCBAEDDDDDDDDDCBAEFFFFFFFFFCBAEFFFFFFFFFCB", 76 "AEDDDDDDDDDCBAEDDDDDDDGDCBAEFFFFFFFFFCBAEFFFFFFFHFCB", 77 "AEDDDDDDDDDCBAEDDDDDDGGDCBAEFFFFFFFFFCBAEFFFFFFHHFCB", 78 "AEDDDDDDDDDCBAEDGDDDGGGDCBAEFFFFFFFFFCBAEFHFFFHHHFCB", 79 "AEDDDDDDDDDCBAEDGGDGGGDDCBAEFFFFFFFFFCBAEFHHFHHHFFCB", 80 "AEDDDDDDDDDCBAEDGGGGGDDDCBAEFFFFFFFFFCBAEFHHHHHFFFCB", 81 "AEDDDDDDDDDCBAEDDGGGDDDDCBAEFFFFFFFFFCBAEFFHHHFFFFCB", 82 "AEDDDDDDDDDCBAEDDDGDDDDDCBAEFFFFFFFFFCBAEFFFHFFFFFCB", 83 "AEDDDDDDDDDCBAEDDDDDDDDDCBAEFFFFFFFFFCBAEFFFFFFFFFCB", 84 "ACCCCCCCCCCCBACCCCCCCCCCCBACCCCCCCCCCCBACCCCCCCCCCCB", 85 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", 86 "FFFFAAAAFFFFFFFFFAAAAFFFFFFFFFAAAAFFFFFFFFFAAAAFFFFF", 87 "FFAAEEEEAAFFFFFAAEEEEAAFFFFFAAEEEEAAFFFFFAAEEEEAAFFF", 88 "FAEEDDDDEEBFFFAEEDDDDEEBFFFAEEFFFFEEBFFFAEEFFFFEEBFF", 89 "FAEDDDDDDCBFFFAEDDDDDDCBFFFAEFFFFFFCBFFFAEFFFFFFCBFF", 90 "AEDDDDDDDDCBFAEDDDGGDDDCBFAEFFFFFFFFCBFAEFFFHHFFFCBF", 91 "AEDDDDDDDDCBFAEDDGGGGDDCBFAEFFFFFFFFCBFAEFFHHHHFFCBF", 92 "AEDDDDDDDDCBFAEDDGGGGDDCBFAEFFFFFFFFCBFAEFFHHHHFFCBF", 93 "AEDDDDDDDDCBFAEDDDGGDDDCBFAEFFFFFFFFCBFAEFFFHHFFFCBF", 94 "FAEDDDDDDCBFFFAEDDDDDDCBFFFAEFFFFFFCBFFFAEFFFFFFCBFF", 95 "FACCDDDDCCBFFFACCDDDDCCBFFFACCFFFFCCBFFFACCFFFFCCBFF", 96 "FFBBCCCCBBFFFFFBBCCCCBBFFFFFBBCCCCBBFFFFFBBCCCCBBFFF", 97 "FFFFBBBBFFFFFFFFFBBBBFFFFFFFFFBBBBFFFFFFFFFBBBBFFFFF", 98 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 99}; 100 101/* 102 * Sizes and offsets into above XPM file. 103 */ 104 105#define CHECK_BUTTON_DIM 13 106#define CHECK_MENU_DIM 9 107#define CHECK_START 9 108#define CHECK_ON_OFFSET 13 109#define CHECK_OFF_OFFSET 0 110#define CHECK_DISON_OFFSET 39 111#define CHECK_DISOFF_OFFSET 26 112#define RADIO_BUTTON_DIM 12 113#define RADIO_MENU_DIM 6 114#define RADIO_WIDTH 13 115#define RADIO_START 22 116#define RADIO_ON_OFFSET 13 117#define RADIO_OFF_OFFSET 0 118#define RADIO_DISON_OFFSET 39 119#define RADIO_DISOFF_OFFSET 26 120 121/* 122 * Indicator Draw Modes 123 */ 124 125#define CHECK_BUTTON 0 126#define CHECK_MENU 1 127#define RADIO_BUTTON 2 128#define RADIO_MENU 3 129 130/* 131 *---------------------------------------------------------------------- 132 * 133 * TkpDrawCheckIndicator - 134 * 135 * Draws the checkbox image in the drawable at the (x,y) location, value, 136 * and state given. This routine is use by the button and menu widgets 137 * 138 * Results: 139 * None. 140 * 141 * Side effects: 142 * An image is drawn in the drawable at the location given. 143 * 144 *---------------------------------------------------------------------- 145 */ 146 147void 148TkpDrawCheckIndicator( 149 Tk_Window tkwin, /* handle for resource alloc */ 150 Display *display, 151 Drawable d, /* what to draw on */ 152 int x, int y, /* where to draw */ 153 Tk_3DBorder bgBorder, /* colors of the border */ 154 XColor *indicatorColor, /* color of the indicator */ 155 XColor *selectColor, /* color when selected */ 156 XColor *disableColor, /* color when disabled */ 157 int on, /* are we on? */ 158 int disabled, /* are we disabled? */ 159 int mode) /* kind of indicator to draw */ 160{ 161 int ix, iy; 162 int dim; 163 int imgsel, imgstart; 164 TkBorder *bg_brdr = (TkBorder*)bgBorder; 165 XGCValues gcValues; 166 GC copyGC; 167 unsigned long imgColors[8]; 168 XImage *img; 169 Pixmap pixmap; 170 int depth; 171 172 /* 173 * Sanity check. 174 */ 175 176 if (tkwin == NULL || display == None || d == None || bgBorder == NULL 177 || indicatorColor == NULL) { 178 return; 179 } 180 181 if (disableColor == NULL) { 182 disableColor = bg_brdr->bgColorPtr; 183 } 184 185 if (selectColor == NULL) { 186 selectColor = bg_brdr->bgColorPtr; 187 } 188 189 depth = Tk_Depth(tkwin); 190 191 /* 192 * Compute starting point and dimensions of image inside button_images to 193 * be used. 194 */ 195 196 switch (mode) { 197 default: 198 case CHECK_BUTTON: 199 imgsel = on == 2 ? CHECK_DISON_OFFSET : 200 on == 1 ? CHECK_ON_OFFSET : CHECK_OFF_OFFSET; 201 imgsel += disabled && on != 2 ? CHECK_DISOFF_OFFSET : 0; 202 imgstart = CHECK_START; 203 dim = CHECK_BUTTON_DIM; 204 break; 205 206 case CHECK_MENU: 207 imgsel = on == 2 ? CHECK_DISOFF_OFFSET : 208 on == 1 ? CHECK_ON_OFFSET : CHECK_OFF_OFFSET; 209 imgsel += disabled && on != 2 ? CHECK_DISOFF_OFFSET : 0; 210 imgstart = CHECK_START + 2; 211 imgsel += 2; 212 dim = CHECK_MENU_DIM; 213 break; 214 215 case RADIO_BUTTON: 216 imgsel = on == 2 ? RADIO_DISON_OFFSET : 217 on==1 ? RADIO_ON_OFFSET : RADIO_OFF_OFFSET; 218 imgsel += disabled && on != 2 ? RADIO_DISOFF_OFFSET : 0; 219 imgstart = RADIO_START; 220 dim = RADIO_BUTTON_DIM; 221 break; 222 223 case RADIO_MENU: 224 imgsel = on == 2 ? RADIO_DISOFF_OFFSET : 225 on==1 ? RADIO_ON_OFFSET : RADIO_OFF_OFFSET; 226 imgsel += disabled && on != 2 ? RADIO_DISOFF_OFFSET : 0; 227 imgstart = RADIO_START + 3; 228 imgsel += 3; 229 dim = RADIO_MENU_DIM; 230 break; 231 } 232 233 /* 234 * Allocate the drawing areas to use. Note that we use double-buffering 235 * here because not all code paths leading to this function do so. 236 */ 237 238 pixmap = Tk_GetPixmap(display, d, dim, dim, depth); 239 if (pixmap == None) { 240 return; 241 } 242 243 x -= dim/2; 244 y -= dim/2; 245 246 img = XGetImage(display, pixmap, 0, 0, 247 (unsigned int)dim, (unsigned int)dim, AllPlanes, ZPixmap); 248 if (img == NULL) { 249 return; 250 } 251 252 /* 253 * Set up the color mapping table. 254 */ 255 256 TkpGetShadows(bg_brdr, tkwin); 257 258 imgColors[0 /*A*/] = 259 Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel; 260 imgColors[1 /*B*/] = 261 Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel; 262 imgColors[2 /*C*/] = (bg_brdr->lightColorPtr != NULL) ? 263 Tk_GetColorByValue(tkwin, bg_brdr->lightColorPtr)->pixel : 264 WhitePixelOfScreen(bg_brdr->screen); 265 imgColors[3 /*D*/] = 266 Tk_GetColorByValue(tkwin, selectColor)->pixel; 267 imgColors[4 /*E*/] = (bg_brdr->darkColorPtr != NULL) ? 268 Tk_GetColorByValue(tkwin, bg_brdr->darkColorPtr)->pixel : 269 BlackPixelOfScreen(bg_brdr->screen); 270 imgColors[5 /*F*/] = 271 Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel; 272 imgColors[6 /*G*/] = 273 Tk_GetColorByValue(tkwin, indicatorColor)->pixel; 274 imgColors[7 /*H*/] = 275 Tk_GetColorByValue(tkwin, disableColor)->pixel; 276 277 /* 278 * Create the image, painting it into an XImage one pixel at a time. 279 */ 280 281 for (iy=0 ; iy<dim ; iy++) { 282 for (ix=0 ; ix<dim ; ix++) { 283 XPutPixel(img, ix, iy, 284 imgColors[button_images[imgstart+iy][imgsel+ix] - 'A'] ); 285 } 286 } 287 288 /* 289 * Copy onto our target drawable surface. 290 */ 291 292 memset(&gcValues, 0, sizeof(gcValues)); 293 gcValues.background = bg_brdr->bgColorPtr->pixel; 294 gcValues.graphics_exposures = False; 295 copyGC = Tk_GetGC(tkwin, 0, &gcValues); 296 297 XPutImage(display, pixmap, copyGC, img, 0, 0, 0, 0, 298 (unsigned int)dim, (unsigned int)dim); 299 XCopyArea(display, pixmap, d, copyGC, 0, 0, 300 (unsigned int)dim, (unsigned int)dim, x, y); 301 302 /* 303 * Tidy up. 304 */ 305 306 Tk_FreeGC(display, copyGC); 307 XDestroyImage(img); 308 Tk_FreePixmap(display, pixmap); 309} 310 311/* 312 *---------------------------------------------------------------------- 313 * 314 * TkpCreateButton -- 315 * 316 * Allocate a new TkButton structure. 317 * 318 * Results: 319 * Returns a newly allocated TkButton structure. 320 * 321 * Side effects: 322 * Registers an event handler for the widget. 323 * 324 *---------------------------------------------------------------------- 325 */ 326 327TkButton * 328TkpCreateButton( 329 Tk_Window tkwin) 330{ 331 UnixButton *butPtr = (UnixButton *) ckalloc(sizeof(UnixButton)); 332 return (TkButton *) butPtr; 333} 334 335/* 336 *---------------------------------------------------------------------- 337 * 338 * TkpDisplayButton -- 339 * 340 * This function is invoked to display a button widget. It is normally 341 * invoked as an idle handler. 342 * 343 * Results: 344 * None. 345 * 346 * Side effects: 347 * Commands are output to X to display the button in its current mode. 348 * The REDRAW_PENDING flag is cleared. 349 * 350 *---------------------------------------------------------------------- 351 */ 352 353void 354TkpDisplayButton( 355 ClientData clientData) /* Information about widget. */ 356{ 357 register TkButton *butPtr = (TkButton *) clientData; 358 GC gc; 359 Tk_3DBorder border; 360 Pixmap pixmap; 361 int x = 0; /* Initialization only needed to stop compiler 362 * warning. */ 363 int y, relief; 364 Tk_Window tkwin = butPtr->tkwin; 365 int width, height, fullWidth, fullHeight; 366 int textXOffset, textYOffset; 367 int haveImage = 0, haveText = 0; 368 int offset; /* 1 means this is a button widget, so we 369 * offset the text to make the button appear 370 * to move up and down as the relief 371 * changes. */ 372 int imageWidth, imageHeight; 373 int imageXOffset = 0, imageYOffset = 0; 374 /* image information that will be used to 375 * restrict disabled pixmap as well */ 376 377 butPtr->flags &= ~REDRAW_PENDING; 378 if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { 379 return; 380 } 381 382 border = butPtr->normalBorder; 383 if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) { 384 gc = butPtr->disabledGC; 385 } else if ((butPtr->state == STATE_ACTIVE) 386 && !Tk_StrictMotif(butPtr->tkwin)) { 387 gc = butPtr->activeTextGC; 388 border = butPtr->activeBorder; 389 } else { 390 gc = butPtr->normalTextGC; 391 } 392 if ((butPtr->flags & SELECTED) && (butPtr->selectBorder != NULL) 393 && !butPtr->indicatorOn) { 394 border = butPtr->selectBorder; 395 } 396 397 /* 398 * Override the relief specified for the button if this is a checkbutton 399 * or radiobutton and there's no indicator. The new relief is as follows: 400 * If the button is select --> "sunken" 401 * If relief==overrelief --> relief 402 * Otherwise --> overrelief 403 * 404 * The effect we are trying to achieve is as follows: 405 * 406 * value mouse-over? --> relief 407 * ------- ------------ -------- 408 * off no flat 409 * off yes raised 410 * on no sunken 411 * on yes sunken 412 * 413 * This is accomplished by configuring the checkbutton or radiobutton like 414 * this: 415 * 416 * -indicatoron 0 -overrelief raised -offrelief flat 417 * 418 * Bindings (see library/button.tcl) will copy the -overrelief into 419 * -relief on mouseover. Hence, we can tell if we are in mouse-over by 420 * comparing relief against overRelief. This is an aweful kludge, but it 421 * gives use the desired behavior while keeping the code backwards 422 * compatible. 423 */ 424 425 relief = butPtr->relief; 426 if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) { 427 if (butPtr->flags & SELECTED) { 428 relief = TK_RELIEF_SUNKEN; 429 } else if (butPtr->overRelief != relief) { 430 relief = butPtr->offRelief; 431 } 432 } 433 434 offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin); 435 436 /* 437 * In order to avoid screen flashes, this function redraws the button in a 438 * pixmap, then copies the pixmap to the screen in a single operation. 439 * This means that there's no point in time where the on-screen image has 440 * been cleared. 441 */ 442 443 pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin), 444 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); 445 Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin), 446 Tk_Height(tkwin), 0, TK_RELIEF_FLAT); 447 448 /* 449 * Display image or bitmap or text for button. 450 */ 451 452 if (butPtr->image != NULL) { 453 Tk_SizeOfImage(butPtr->image, &width, &height); 454 haveImage = 1; 455 } else if (butPtr->bitmap != None) { 456 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); 457 haveImage = 1; 458 } 459 imageWidth = width; 460 imageHeight = height; 461 462 haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0); 463 464 if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { 465 textXOffset = 0; 466 textYOffset = 0; 467 fullWidth = 0; 468 fullHeight = 0; 469 470 switch ((enum compound) butPtr->compound) { 471 case COMPOUND_TOP: 472 case COMPOUND_BOTTOM: 473 /* 474 * Image is above or below text. 475 */ 476 477 if (butPtr->compound == COMPOUND_TOP) { 478 textYOffset = height + butPtr->padY; 479 } else { 480 imageYOffset = butPtr->textHeight + butPtr->padY; 481 } 482 fullHeight = height + butPtr->textHeight + butPtr->padY; 483 fullWidth = (width > butPtr->textWidth ? width : 484 butPtr->textWidth); 485 textXOffset = (fullWidth - butPtr->textWidth)/2; 486 imageXOffset = (fullWidth - width)/2; 487 break; 488 case COMPOUND_LEFT: 489 case COMPOUND_RIGHT: 490 /* 491 * Image is left or right of text. 492 */ 493 494 if (butPtr->compound == COMPOUND_LEFT) { 495 textXOffset = width + butPtr->padX; 496 } else { 497 imageXOffset = butPtr->textWidth + butPtr->padX; 498 } 499 fullWidth = butPtr->textWidth + butPtr->padX + width; 500 fullHeight = (height > butPtr->textHeight ? height : 501 butPtr->textHeight); 502 textYOffset = (fullHeight - butPtr->textHeight)/2; 503 imageYOffset = (fullHeight - height)/2; 504 break; 505 case COMPOUND_CENTER: 506 /* 507 * Image and text are superimposed. 508 */ 509 510 fullWidth = (width > butPtr->textWidth ? width : 511 butPtr->textWidth); 512 fullHeight = (height > butPtr->textHeight ? height : 513 butPtr->textHeight); 514 textXOffset = (fullWidth - butPtr->textWidth)/2; 515 imageXOffset = (fullWidth - width)/2; 516 textYOffset = (fullHeight - butPtr->textHeight)/2; 517 imageYOffset = (fullHeight - height)/2; 518 break; 519 case COMPOUND_NONE: 520 break; 521 } 522 523 TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, 524 butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y); 525 526 x += butPtr->indicatorSpace; 527 528 x += offset; 529 y += offset; 530 if (relief == TK_RELIEF_RAISED) { 531 x -= offset; 532 y -= offset; 533 } else if (relief == TK_RELIEF_SUNKEN) { 534 x += offset; 535 y += offset; 536 } 537 538 imageXOffset += x; 539 imageYOffset += y; 540 541 if (butPtr->image != NULL) { 542 /* 543 * Do boundary clipping, so that Tk_RedrawImage is passed valid 544 * coordinates. [Bug 979239] 545 */ 546 547 if (imageXOffset < 0) { 548 imageXOffset = 0; 549 } 550 if (imageYOffset < 0) { 551 imageYOffset = 0; 552 } 553 if (width > Tk_Width(tkwin)) { 554 width = Tk_Width(tkwin); 555 } 556 if (height > Tk_Height(tkwin)) { 557 height = Tk_Height(tkwin); 558 } 559 if ((width + imageXOffset) > Tk_Width(tkwin)) { 560 imageXOffset = Tk_Width(tkwin) - width; 561 } 562 if ((height + imageYOffset) > Tk_Height(tkwin)) { 563 imageYOffset = Tk_Height(tkwin) - height; 564 } 565 566 if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) { 567 Tk_RedrawImage(butPtr->selectImage, 0, 0, 568 width, height, pixmap, imageXOffset, imageYOffset); 569 } else if ((butPtr->tristateImage != NULL) && (butPtr->flags & TRISTATED)) { 570 Tk_RedrawImage(butPtr->tristateImage, 0, 0, 571 width, height, pixmap, imageXOffset, imageYOffset); 572 } else { 573 Tk_RedrawImage(butPtr->image, 0, 0, width, 574 height, pixmap, imageXOffset, imageYOffset); 575 } 576 } else { 577 XSetClipOrigin(butPtr->display, gc, imageXOffset, imageYOffset); 578 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 579 0, 0, (unsigned int) width, (unsigned int) height, 580 imageXOffset, imageYOffset, 1); 581 XSetClipOrigin(butPtr->display, gc, 0, 0); 582 } 583 584 Tk_DrawTextLayout(butPtr->display, pixmap, gc, 585 butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1); 586 Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, 587 butPtr->textLayout, x + textXOffset, y + textYOffset, 588 butPtr->underline); 589 y += fullHeight/2; 590 } else { 591 if (haveImage) { 592 TkComputeAnchor(butPtr->anchor, tkwin, 0, 0, 593 butPtr->indicatorSpace + width, height, &x, &y); 594 x += butPtr->indicatorSpace; 595 596 x += offset; 597 y += offset; 598 if (relief == TK_RELIEF_RAISED) { 599 x -= offset; 600 y -= offset; 601 } else if (relief == TK_RELIEF_SUNKEN) { 602 x += offset; 603 y += offset; 604 } 605 imageXOffset += x; 606 imageYOffset += y; 607 if (butPtr->image != NULL) { 608 /* 609 * Do boundary clipping, so that Tk_RedrawImage is passed 610 * valid coordinates. [Bug 979239] 611 */ 612 613 if (imageXOffset < 0) { 614 imageXOffset = 0; 615 } 616 if (imageYOffset < 0) { 617 imageYOffset = 0; 618 } 619 if (width > Tk_Width(tkwin)) { 620 width = Tk_Width(tkwin); 621 } 622 if (height > Tk_Height(tkwin)) { 623 height = Tk_Height(tkwin); 624 } 625 if ((width + imageXOffset) > Tk_Width(tkwin)) { 626 imageXOffset = Tk_Width(tkwin) - width; 627 } 628 if ((height + imageYOffset) > Tk_Height(tkwin)) { 629 imageYOffset = Tk_Height(tkwin) - height; 630 } 631 632 if ((butPtr->selectImage != NULL) && 633 (butPtr->flags & SELECTED)) { 634 Tk_RedrawImage(butPtr->selectImage, 0, 0, width, 635 height, pixmap, imageXOffset, imageYOffset); 636 } else if ((butPtr->tristateImage != NULL) && 637 (butPtr->flags & TRISTATED)) { 638 Tk_RedrawImage(butPtr->tristateImage, 0, 0, width, 639 height, pixmap, imageXOffset, imageYOffset); 640 } else { 641 Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, 642 imageXOffset, imageYOffset); 643 } 644 } else { 645 XSetClipOrigin(butPtr->display, gc, x, y); 646 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, 647 (unsigned int) width, (unsigned int) height, x, y, 1); 648 XSetClipOrigin(butPtr->display, gc, 0, 0); 649 } 650 y += height/2; 651 } else { 652 TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, 653 butPtr->indicatorSpace + butPtr->textWidth, 654 butPtr->textHeight, &x, &y); 655 656 x += butPtr->indicatorSpace; 657 658 x += offset; 659 y += offset; 660 if (relief == TK_RELIEF_RAISED) { 661 x -= offset; 662 y -= offset; 663 } else if (relief == TK_RELIEF_SUNKEN) { 664 x += offset; 665 y += offset; 666 } 667 Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout, 668 x, y, 0, -1); 669 Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, 670 butPtr->textLayout, x, y, butPtr->underline); 671 y += butPtr->textHeight/2; 672 } 673 } 674 675 /* 676 * Draw the indicator for check buttons and radio buttons. At this point, 677 * x and y refer to the top-left corner of the text or image or bitmap. 678 */ 679 680 if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 681 if (butPtr->indicatorDiameter > 2*butPtr->borderWidth) { 682 TkBorder *selBorder = (TkBorder *) butPtr->selectBorder; 683 XColor *selColor = NULL; 684 685 if (selBorder != NULL) { 686 selColor = selBorder->bgColorPtr; 687 } 688 x -= butPtr->indicatorSpace/2; 689 y = Tk_Height(tkwin)/2; 690 TkpDrawCheckIndicator(tkwin, butPtr->display, pixmap, x, y, 691 border, butPtr->normalFg, selColor, butPtr->disabledFg, 692 ((butPtr->flags & SELECTED) ? 1 : 693 (butPtr->flags & TRISTATED) ? 2 : 0), 694 (butPtr->state == STATE_DISABLED), CHECK_BUTTON); 695 } 696 } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) { 697 if (butPtr->indicatorDiameter > 2*butPtr->borderWidth) { 698 TkBorder *selBorder = (TkBorder *) butPtr->selectBorder; 699 XColor *selColor = NULL; 700 701 if (selBorder != NULL) { 702 selColor = selBorder->bgColorPtr; 703 } 704 x -= butPtr->indicatorSpace/2; 705 y = Tk_Height(tkwin)/2; 706 TkpDrawCheckIndicator(tkwin, butPtr->display, pixmap, x, y, 707 border, butPtr->normalFg, selColor, butPtr->disabledFg, 708 ((butPtr->flags & SELECTED) ? 1 : 709 (butPtr->flags & TRISTATED) ? 2 : 0), 710 (butPtr->state == STATE_DISABLED), RADIO_BUTTON); 711 } 712 } 713 714 /* 715 * If the button is disabled with a stipple rather than a special 716 * foreground color, generate the stippled effect. If the widget is 717 * selected and we use a different background color when selected, must 718 * temporarily modify the GC so the stippling is the right color. 719 */ 720 721 if ((butPtr->state == STATE_DISABLED) 722 && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) { 723 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn 724 && (butPtr->selectBorder != NULL)) { 725 XSetForeground(butPtr->display, butPtr->stippleGC, 726 Tk_3DBorderColor(butPtr->selectBorder)->pixel); 727 } 728 729 /* 730 * Stipple the whole button if no disabledFg was specified, otherwise 731 * restrict stippling only to displayed image 732 */ 733 734 if (butPtr->disabledFg == NULL) { 735 XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 0, 0, 736 (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin)); 737 } else { 738 XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 739 imageXOffset, imageYOffset, 740 (unsigned) imageWidth, (unsigned) imageHeight); 741 } 742 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn 743 && (butPtr->selectBorder != NULL)) { 744 XSetForeground(butPtr->display, butPtr->stippleGC, 745 Tk_3DBorderColor(butPtr->normalBorder)->pixel); 746 } 747 } 748 749 /* 750 * Draw the border and traversal highlight last. This way, if the button's 751 * contents overflow they'll be covered up by the border. This code is 752 * complicated by the possible combinations of focus highlight and default 753 * rings. We draw the focus and highlight rings using the highlight border 754 * and highlight foreground color. 755 */ 756 757 if (relief != TK_RELIEF_FLAT) { 758 int inset = butPtr->highlightWidth; 759 760 if (butPtr->defaultState == DEFAULT_ACTIVE) { 761 /* 762 * Draw the default ring with 2 pixels of space between the 763 * default ring and the button and the default ring and the focus 764 * ring. Note that we need to explicitly draw the space in the 765 * highlightBorder color to ensure that we overwrite any overflow 766 * text and/or a different button background color. 767 */ 768 769 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, 770 inset, Tk_Width(tkwin) - 2*inset, 771 Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT); 772 inset += 2; 773 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, 774 inset, Tk_Width(tkwin) - 2*inset, 775 Tk_Height(tkwin) - 2*inset, 1, TK_RELIEF_SUNKEN); 776 inset++; 777 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset, 778 inset, Tk_Width(tkwin) - 2*inset, 779 Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT); 780 781 inset += 2; 782 } else if (butPtr->defaultState == DEFAULT_NORMAL) { 783 /* 784 * Leave room for the default ring and write over any text or 785 * background color. 786 */ 787 788 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0, 789 0, Tk_Width(tkwin), Tk_Height(tkwin), 5, TK_RELIEF_FLAT); 790 inset += 5; 791 } 792 793 /* 794 * Draw the button border. 795 */ 796 797 Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset, 798 Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset, 799 butPtr->borderWidth, relief); 800 } 801 if (butPtr->highlightWidth > 0) { 802 GC gc; 803 804 if (butPtr->flags & GOT_FOCUS) { 805 gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap); 806 } else { 807 gc = Tk_GCForColor(Tk_3DBorderColor(butPtr->highlightBorder), 808 pixmap); 809 } 810 811 /* 812 * Make sure the focus ring shrink-wraps the actual button, not the 813 * padding space left for a default ring. 814 */ 815 816 if (butPtr->defaultState == DEFAULT_NORMAL) { 817 TkDrawInsetFocusHighlight(tkwin, gc, butPtr->highlightWidth, 818 pixmap, 5); 819 } else { 820 Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap); 821 } 822 } 823 824 /* 825 * Copy the information from the off-screen pixmap onto the screen, then 826 * delete the pixmap. 827 */ 828 829 XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin), 830 butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin), 831 (unsigned) Tk_Height(tkwin), 0, 0); 832 Tk_FreePixmap(butPtr->display, pixmap); 833} 834 835/* 836 *---------------------------------------------------------------------- 837 * 838 * TkpComputeButtonGeometry -- 839 * 840 * After changes in a button's text or bitmap, this function recomputes 841 * the button's geometry and passes this information along to the 842 * geometry manager for the window. 843 * 844 * Results: 845 * None. 846 * 847 * Side effects: 848 * The button's window may change size. 849 * 850 *---------------------------------------------------------------------- 851 */ 852 853void 854TkpComputeButtonGeometry( 855 register TkButton *butPtr) /* Button whose geometry may have changed. */ 856{ 857 int width, height, avgWidth, txtWidth, txtHeight; 858 int haveImage = 0, haveText = 0; 859 Tk_FontMetrics fm; 860 861 butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth; 862 863 /* 864 * Leave room for the default ring if needed. 865 */ 866 867 if (butPtr->defaultState != DEFAULT_DISABLED) { 868 butPtr->inset += 5; 869 } 870 butPtr->indicatorSpace = 0; 871 872 width = 0; 873 height = 0; 874 txtWidth = 0; 875 txtHeight = 0; 876 avgWidth = 0; 877 878 if (butPtr->image != NULL) { 879 Tk_SizeOfImage(butPtr->image, &width, &height); 880 haveImage = 1; 881 } else if (butPtr->bitmap != None) { 882 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); 883 haveImage = 1; 884 } 885 886 if (haveImage == 0 || butPtr->compound != COMPOUND_NONE) { 887 Tk_FreeTextLayout(butPtr->textLayout); 888 889 butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont, 890 Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength, 891 butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight); 892 893 txtWidth = butPtr->textWidth; 894 txtHeight = butPtr->textHeight; 895 avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1); 896 Tk_GetFontMetrics(butPtr->tkfont, &fm); 897 haveText = (txtWidth != 0 && txtHeight != 0); 898 } 899 900 /* 901 * If the button is compound (i.e., it shows both an image and text), the 902 * new geometry is a combination of the image and text geometry. We only 903 * honor the compound bit if the button has both text and an image, 904 * because otherwise it is not really a compound button. 905 */ 906 907 if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { 908 switch ((enum compound) butPtr->compound) { 909 case COMPOUND_TOP: 910 case COMPOUND_BOTTOM: 911 /* 912 * Image is above or below text. 913 */ 914 915 height += txtHeight + butPtr->padY; 916 width = (width > txtWidth ? width : txtWidth); 917 break; 918 case COMPOUND_LEFT: 919 case COMPOUND_RIGHT: 920 /* 921 * Image is left or right of text. 922 */ 923 924 width += txtWidth + butPtr->padX; 925 height = (height > txtHeight ? height : txtHeight); 926 break; 927 case COMPOUND_CENTER: 928 /* 929 * Image and text are superimposed. 930 */ 931 932 width = (width > txtWidth ? width : txtWidth); 933 height = (height > txtHeight ? height : txtHeight); 934 break; 935 case COMPOUND_NONE: 936 break; 937 } 938 if (butPtr->width > 0) { 939 width = butPtr->width; 940 } 941 if (butPtr->height > 0) { 942 height = butPtr->height; 943 } 944 945 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 946 butPtr->indicatorSpace = height; 947 if (butPtr->type == TYPE_CHECK_BUTTON) { 948 butPtr->indicatorDiameter = (65*height)/100; 949 } else { 950 butPtr->indicatorDiameter = (75*height)/100; 951 } 952 } 953 954 width += 2*butPtr->padX; 955 height += 2*butPtr->padY; 956 } else { 957 if (haveImage) { 958 if (butPtr->width > 0) { 959 width = butPtr->width; 960 } 961 if (butPtr->height > 0) { 962 height = butPtr->height; 963 } 964 965 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 966 butPtr->indicatorSpace = height; 967 if (butPtr->type == TYPE_CHECK_BUTTON) { 968 butPtr->indicatorDiameter = (65*height)/100; 969 } else { 970 butPtr->indicatorDiameter = (75*height)/100; 971 } 972 } 973 } else { 974 width = txtWidth; 975 height = txtHeight; 976 977 if (butPtr->width > 0) { 978 width = butPtr->width * avgWidth; 979 } 980 if (butPtr->height > 0) { 981 height = butPtr->height * fm.linespace; 982 } 983 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) { 984 butPtr->indicatorDiameter = fm.linespace; 985 if (butPtr->type == TYPE_CHECK_BUTTON) { 986 butPtr->indicatorDiameter = 987 (80*butPtr->indicatorDiameter)/100; 988 } 989 butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth; 990 } 991 } 992 } 993 994 /* 995 * When issuing the geometry request, add extra space for the indicator, 996 * if any, and for the border and padding, plus two extra pixels so the 997 * display can be offset by 1 pixel in either direction for the raised or 998 * lowered effect. 999 */ 1000 1001 if ((butPtr->image == NULL) && (butPtr->bitmap == None)) { 1002 width += 2*butPtr->padX; 1003 height += 2*butPtr->padY; 1004 } 1005 if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) { 1006 width += 2; 1007 height += 2; 1008 } 1009 Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace 1010 + 2*butPtr->inset), (int) (height + 2*butPtr->inset)); 1011 Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset); 1012} 1013 1014/* 1015 * Local Variables: 1016 * mode: c 1017 * c-basic-offset: 4 1018 * fill-column: 78 1019 * End: 1020 */ 1021