4 * This file implements the Unix specific portion of the button widgets.
6 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
8 * See the file "license.terms" for information on usage and redistribution of
9 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
17 * Shared with menu widget.
20 MODULE_SCOPE void TkpDrawCheckIndicator(Tk_Window tkwin,
21 Display *display, Drawable d, int x, int y,
22 Tk_3DBorder bgBorder, XColor *indicatorColor,
23 XColor *selectColor, XColor *disColor, int on,
24 int disabled, int mode);
27 * Declaration of Unix specific button structure.
30 typedef struct UnixButton {
31 TkButton info; /* Generic button info. */
35 * The class function table for the button widgets.
38 const Tk_ClassProcs tkpButtonProcs = {
39 sizeof(Tk_ClassProcs), /* size */
40 TkButtonWorldChanged, /* worldChangedProc */
41 NULL, /* createProc */
47 * The header info here is ignored, it's the image that's important. The
48 * colors will be applied as follows:
56 * H = disabled Indicator Color
60 static const char *const button_images[] = {
61 /* width height ncolors chars_per_pixel */
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",
102 * Sizes and offsets into above XPM file.
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
122 * Indicator Draw Modes
125 #define CHECK_BUTTON 0
127 #define RADIO_BUTTON 2
131 *----------------------------------------------------------------------
133 * TkpDrawCheckIndicator -
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
142 * An image is drawn in the drawable at the location given.
144 *----------------------------------------------------------------------
148 TkpDrawCheckIndicator(
149 Tk_Window tkwin, /* handle for resource alloc */
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 */
163 int imgsel, imgstart;
164 TkBorder *bg_brdr = (TkBorder*)bgBorder;
167 unsigned long imgColors[8];
176 if (tkwin == NULL || display == NULL || d == None || bgBorder == NULL
177 || indicatorColor == NULL) {
181 if (disableColor == NULL) {
182 disableColor = bg_brdr->bgColorPtr;
185 if (selectColor == NULL) {
186 selectColor = bg_brdr->bgColorPtr;
189 depth = Tk_Depth(tkwin);
192 * Compute starting point and dimensions of image inside button_images to
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;
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;
212 dim = CHECK_MENU_DIM;
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;
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;
229 dim = RADIO_MENU_DIM;
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.
238 pixmap = Tk_GetPixmap(display, d, dim, dim, depth);
239 if (pixmap == None) {
246 img = XGetImage(display, pixmap, 0, 0,
247 (unsigned int)dim, (unsigned int)dim, AllPlanes, ZPixmap);
253 * Set up the color mapping table.
256 TkpGetShadows(bg_brdr, tkwin);
259 Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel;
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);
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);
271 Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel;
273 Tk_GetColorByValue(tkwin, indicatorColor)->pixel;
275 Tk_GetColorByValue(tkwin, disableColor)->pixel;
278 * Create the image, painting it into an XImage one pixel at a time.
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']);
289 * Copy onto our target drawable surface.
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);
297 XPutImage(display, pixmap, copyGC, img, 0, 0, 0, 0,
298 (unsigned)dim, (unsigned)dim);
299 XCopyArea(display, pixmap, d, copyGC, 0, 0,
300 (unsigned)dim, (unsigned)dim, x, y);
306 Tk_FreeGC(display, copyGC);
308 Tk_FreePixmap(display, pixmap);
312 *----------------------------------------------------------------------
316 * Allocate a new TkButton structure.
319 * Returns a newly allocated TkButton structure.
322 * Registers an event handler for the widget.
324 *----------------------------------------------------------------------
329 TCL_UNUSED(Tk_Window))
331 return (TkButton *)ckalloc(sizeof(UnixButton));
335 *----------------------------------------------------------------------
337 * TkpDisplayButton --
339 * This function is invoked to display a button widget. It is normally
340 * invoked as an idle handler.
346 * Commands are output to X to display the button in its current mode.
347 * The REDRAW_PENDING flag is cleared.
349 *----------------------------------------------------------------------
356 int *x, /* shift this x coordinate */
357 int *y, /* shift this y coordinate */
358 int width, /* width of image/text */
359 int height) /* height of image/text */
361 if (relief != TK_RELIEF_RAISED
362 && butPtr->type == TYPE_BUTTON
363 && !Tk_StrictMotif(butPtr->tkwin)) {
368 * This is an (unraised) button widget, so we offset the text to make
369 * the button appear to move up and down as the relief changes.
372 shiftX = shiftY = (relief == TK_RELIEF_SUNKEN) ? 2 : 1;
374 if (relief != TK_RELIEF_RIDGE) {
376 * Take back one pixel if the padding is even, otherwise the
377 * content will be displayed too far right/down.
380 if ((Tk_Width(butPtr->tkwin) - width) % 2 == 0) {
383 if ((Tk_Height(butPtr->tkwin) - height) % 2 == 0) {
395 ClientData clientData) /* Information about widget. */
397 TkButton *butPtr = (TkButton *)clientData;
401 int x = 0; /* Initialization only needed to stop compiler
404 Tk_Window tkwin = butPtr->tkwin;
405 int width = 0, height = 0, fullWidth, fullHeight;
406 int textXOffset, textYOffset;
407 int haveImage = 0, haveText = 0;
408 int imageWidth, imageHeight;
409 int imageXOffset = 0, imageYOffset = 0;
410 /* image information that will be used to
411 * restrict disabled pixmap as well */
413 butPtr->flags &= ~REDRAW_PENDING;
414 if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
418 border = butPtr->normalBorder;
419 if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) {
420 gc = butPtr->disabledGC;
421 } else if ((butPtr->state == STATE_ACTIVE)
422 && !Tk_StrictMotif(butPtr->tkwin)) {
423 gc = butPtr->activeTextGC;
424 border = butPtr->activeBorder;
426 gc = butPtr->normalTextGC;
428 if ((butPtr->flags & SELECTED) && (butPtr->selectBorder != NULL)
429 && !butPtr->indicatorOn) {
430 border = butPtr->selectBorder;
434 * Override the relief specified for the button if this is a checkbutton
435 * or radiobutton and there's no indicator. The new relief is as follows:
436 * If the button is select --> "sunken"
437 * If relief==overrelief --> relief
438 * Otherwise --> overrelief
440 * The effect we are trying to achieve is as follows:
442 * value mouse-over? --> relief
443 * ------- ------------ --------
449 * This is accomplished by configuring the checkbutton or radiobutton like
452 * -indicatoron 0 -overrelief raised -offrelief flat
454 * Bindings (see library/button.tcl) will copy the -overrelief into
455 * -relief on mouseover. Hence, we can tell if we are in mouse-over by
456 * comparing relief against overRelief. This is an aweful kludge, but it
457 * gives use the desired behavior while keeping the code backwards
461 relief = butPtr->relief;
462 if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
463 if (butPtr->flags & SELECTED) {
464 relief = TK_RELIEF_SUNKEN;
465 } else if (butPtr->overRelief != relief) {
466 relief = butPtr->offRelief;
471 * In order to avoid screen flashes, this function redraws the button in a
472 * pixmap, then copies the pixmap to the screen in a single operation.
473 * This means that there's no point in time where the on-screen image has
477 pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
478 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
479 Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
480 Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
483 * Display image or bitmap or text for button.
486 if (butPtr->image != NULL) {
487 Tk_SizeOfImage(butPtr->image, &width, &height);
489 } else if (butPtr->bitmap != None) {
490 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
494 imageHeight = height;
496 haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0);
498 if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
504 switch ((enum compound) butPtr->compound) {
506 case COMPOUND_BOTTOM:
508 * Image is above or below text.
511 if (butPtr->compound == COMPOUND_TOP) {
512 textYOffset = height + butPtr->padY;
514 imageYOffset = butPtr->textHeight + butPtr->padY;
516 fullHeight = height + butPtr->textHeight + butPtr->padY;
517 fullWidth = (width > butPtr->textWidth ? width :
519 textXOffset = (fullWidth - butPtr->textWidth)/2;
520 imageXOffset = (fullWidth - width)/2;
525 * Image is left or right of text.
528 if (butPtr->compound == COMPOUND_LEFT) {
529 textXOffset = width + butPtr->padX;
531 imageXOffset = butPtr->textWidth + butPtr->padX;
533 fullWidth = butPtr->textWidth + butPtr->padX + width;
534 fullHeight = (height > butPtr->textHeight ? height :
536 textYOffset = (fullHeight - butPtr->textHeight)/2;
537 imageYOffset = (fullHeight - height)/2;
539 case COMPOUND_CENTER:
541 * Image and text are superimposed.
544 fullWidth = (width > butPtr->textWidth ? width :
546 fullHeight = (height > butPtr->textHeight ? height :
548 textXOffset = (fullWidth - butPtr->textWidth)/2;
549 imageXOffset = (fullWidth - width)/2;
550 textYOffset = (fullHeight - butPtr->textHeight)/2;
551 imageYOffset = (fullHeight - height)/2;
557 TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
558 butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y);
560 x += butPtr->indicatorSpace;
561 ShiftByOffset(butPtr, relief, &x, &y, width, height);
565 if (butPtr->image != NULL) {
567 * Do boundary clipping, so that Tk_RedrawImage is passed valid
568 * coordinates. [Bug 979239]
571 if (imageXOffset < 0) {
574 if (imageYOffset < 0) {
577 if (width > Tk_Width(tkwin)) {
578 width = Tk_Width(tkwin);
580 if (height > Tk_Height(tkwin)) {
581 height = Tk_Height(tkwin);
583 if ((width + imageXOffset) > Tk_Width(tkwin)) {
584 imageXOffset = Tk_Width(tkwin) - width;
586 if ((height + imageYOffset) > Tk_Height(tkwin)) {
587 imageYOffset = Tk_Height(tkwin) - height;
590 if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
591 Tk_RedrawImage(butPtr->selectImage, 0, 0,
592 width, height, pixmap, imageXOffset, imageYOffset);
593 } else if ((butPtr->tristateImage != NULL) && (butPtr->flags & TRISTATED)) {
594 Tk_RedrawImage(butPtr->tristateImage, 0, 0,
595 width, height, pixmap, imageXOffset, imageYOffset);
597 Tk_RedrawImage(butPtr->image, 0, 0, width,
598 height, pixmap, imageXOffset, imageYOffset);
601 XSetClipOrigin(butPtr->display, gc, imageXOffset, imageYOffset);
602 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc,
603 0, 0, (unsigned int) width, (unsigned int) height,
604 imageXOffset, imageYOffset, 1);
605 XSetClipOrigin(butPtr->display, gc, 0, 0);
608 Tk_DrawTextLayout(butPtr->display, pixmap, gc,
609 butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1);
610 Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
611 butPtr->textLayout, x + textXOffset, y + textYOffset,
616 TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
617 butPtr->indicatorSpace + width, height, &x, &y);
618 x += butPtr->indicatorSpace;
619 ShiftByOffset(butPtr, relief, &x, &y, width, height);
622 if (butPtr->image != NULL) {
624 * Do boundary clipping, so that Tk_RedrawImage is passed
625 * valid coordinates. [Bug 979239]
628 if (imageXOffset < 0) {
631 if (imageYOffset < 0) {
634 if (width > Tk_Width(tkwin)) {
635 width = Tk_Width(tkwin);
637 if (height > Tk_Height(tkwin)) {
638 height = Tk_Height(tkwin);
640 if ((width + imageXOffset) > Tk_Width(tkwin)) {
641 imageXOffset = Tk_Width(tkwin) - width;
643 if ((height + imageYOffset) > Tk_Height(tkwin)) {
644 imageYOffset = Tk_Height(tkwin) - height;
647 if ((butPtr->selectImage != NULL) &&
648 (butPtr->flags & SELECTED)) {
649 Tk_RedrawImage(butPtr->selectImage, 0, 0, width,
650 height, pixmap, imageXOffset, imageYOffset);
651 } else if ((butPtr->tristateImage != NULL) &&
652 (butPtr->flags & TRISTATED)) {
653 Tk_RedrawImage(butPtr->tristateImage, 0, 0, width,
654 height, pixmap, imageXOffset, imageYOffset);
656 Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
657 imageXOffset, imageYOffset);
660 XSetClipOrigin(butPtr->display, gc, x, y);
661 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
662 (unsigned int) width, (unsigned int) height, x, y, 1);
663 XSetClipOrigin(butPtr->display, gc, 0, 0);
667 TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
668 butPtr->indicatorSpace + butPtr->textWidth,
669 butPtr->textHeight, &x, &y);
671 x += butPtr->indicatorSpace;
672 ShiftByOffset(butPtr, relief, &x, &y, width, height);
673 Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
675 Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
676 butPtr->textLayout, x, y, butPtr->underline);
677 y += butPtr->textHeight/2;
682 * Draw the indicator for check buttons and radio buttons. At this point,
683 * x and y refer to the top-left corner of the text or image or bitmap.
686 if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
687 if (butPtr->indicatorDiameter > 2*butPtr->borderWidth) {
688 TkBorder *selBorder = (TkBorder *) butPtr->selectBorder;
689 XColor *selColor = NULL;
691 if (selBorder != NULL) {
692 selColor = selBorder->bgColorPtr;
694 x -= butPtr->indicatorSpace/2;
695 y = Tk_Height(tkwin)/2;
696 TkpDrawCheckIndicator(tkwin, butPtr->display, pixmap, x, y,
697 border, butPtr->normalFg, selColor, butPtr->disabledFg,
698 ((butPtr->flags & SELECTED) ? 1 :
699 (butPtr->flags & TRISTATED) ? 2 : 0),
700 (butPtr->state == STATE_DISABLED), CHECK_BUTTON);
702 } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
703 if (butPtr->indicatorDiameter > 2*butPtr->borderWidth) {
704 TkBorder *selBorder = (TkBorder *) butPtr->selectBorder;
705 XColor *selColor = NULL;
707 if (selBorder != NULL) {
708 selColor = selBorder->bgColorPtr;
710 x -= butPtr->indicatorSpace/2;
711 y = Tk_Height(tkwin)/2;
712 TkpDrawCheckIndicator(tkwin, butPtr->display, pixmap, x, y,
713 border, butPtr->normalFg, selColor, butPtr->disabledFg,
714 ((butPtr->flags & SELECTED) ? 1 :
715 (butPtr->flags & TRISTATED) ? 2 : 0),
716 (butPtr->state == STATE_DISABLED), RADIO_BUTTON);
721 * If the button is disabled with a stipple rather than a special
722 * foreground color, generate the stippled effect. If the widget is
723 * selected and we use a different background color when selected, must
724 * temporarily modify the GC so the stippling is the right color.
727 if ((butPtr->state == STATE_DISABLED)
728 && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
729 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
730 && (butPtr->selectBorder != NULL)) {
731 XSetForeground(butPtr->display, butPtr->stippleGC,
732 Tk_3DBorderColor(butPtr->selectBorder)->pixel);
736 * Stipple the whole button if no disabledFg was specified, otherwise
737 * restrict stippling only to displayed image
740 if (butPtr->disabledFg == NULL) {
741 XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 0, 0,
742 (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin));
744 XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC,
745 imageXOffset, imageYOffset,
746 (unsigned) imageWidth, (unsigned) imageHeight);
748 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
749 && (butPtr->selectBorder != NULL)) {
750 XSetForeground(butPtr->display, butPtr->stippleGC,
751 Tk_3DBorderColor(butPtr->normalBorder)->pixel);
756 * Draw the border and traversal highlight last. This way, if the button's
757 * contents overflow they'll be covered up by the border. This code is
758 * complicated by the possible combinations of focus highlight and default
759 * rings. We draw the focus and highlight rings using the highlight border
760 * and highlight foreground color.
763 if (relief != TK_RELIEF_FLAT) {
764 int inset = butPtr->highlightWidth;
766 if (butPtr->defaultState == DEFAULT_ACTIVE) {
768 * Draw the default ring with 2 pixels of space between the
769 * default ring and the button and the default ring and the focus
770 * ring. Note that we need to explicitly draw the space in the
771 * highlightBorder color to ensure that we overwrite any overflow
772 * text and/or a different button background color.
775 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
776 inset, Tk_Width(tkwin) - 2*inset,
777 Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
779 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
780 inset, Tk_Width(tkwin) - 2*inset,
781 Tk_Height(tkwin) - 2*inset, 1, TK_RELIEF_SUNKEN);
783 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
784 inset, Tk_Width(tkwin) - 2*inset,
785 Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
788 } else if (butPtr->defaultState == DEFAULT_NORMAL) {
790 * Leave room for the default ring and write over any text or
794 Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0,
795 0, Tk_Width(tkwin), Tk_Height(tkwin), 5, TK_RELIEF_FLAT);
800 * Draw the button border.
803 Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
804 Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
805 butPtr->borderWidth, relief);
807 if (butPtr->highlightWidth > 0) {
808 if (butPtr->flags & GOT_FOCUS) {
809 gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
811 gc = Tk_GCForColor(Tk_3DBorderColor(butPtr->highlightBorder),
816 * Make sure the focus ring shrink-wraps the actual button, not the
817 * padding space left for a default ring.
820 if (butPtr->defaultState == DEFAULT_NORMAL) {
821 TkDrawInsetFocusHighlight(tkwin, gc, butPtr->highlightWidth,
824 Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
829 * Copy the information from the off-screen pixmap onto the screen, then
833 XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
834 butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
835 (unsigned) Tk_Height(tkwin), 0, 0);
836 Tk_FreePixmap(butPtr->display, pixmap);
840 *----------------------------------------------------------------------
842 * TkpComputeButtonGeometry --
844 * After changes in a button's text or bitmap, this function recomputes
845 * the button's geometry and passes this information along to the
846 * geometry manager for the window.
852 * The button's window may change size.
854 *----------------------------------------------------------------------
858 TkpComputeButtonGeometry(
859 TkButton *butPtr) /* Button whose geometry may have changed. */
861 int width, height, avgWidth, txtWidth, txtHeight;
862 int haveImage = 0, haveText = 0;
865 butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
868 * Leave room for the default ring if needed.
871 if (butPtr->defaultState != DEFAULT_DISABLED) {
874 butPtr->indicatorSpace = 0;
882 if (butPtr->image != NULL) {
883 Tk_SizeOfImage(butPtr->image, &width, &height);
885 } else if (butPtr->bitmap != None) {
886 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
890 if (haveImage == 0 || butPtr->compound != COMPOUND_NONE) {
891 Tk_FreeTextLayout(butPtr->textLayout);
893 butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
894 Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength,
895 butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight);
897 txtWidth = butPtr->textWidth;
898 txtHeight = butPtr->textHeight;
899 avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
900 Tk_GetFontMetrics(butPtr->tkfont, &fm);
901 haveText = (txtWidth != 0 && txtHeight != 0);
905 * If the button is compound (i.e., it shows both an image and text), the
906 * new geometry is a combination of the image and text geometry. We only
907 * honor the compound bit if the button has both text and an image,
908 * because otherwise it is not really a compound button.
911 if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
912 switch ((enum compound) butPtr->compound) {
914 case COMPOUND_BOTTOM:
916 * Image is above or below text.
919 height += txtHeight + butPtr->padY;
920 width = (width > txtWidth ? width : txtWidth);
925 * Image is left or right of text.
928 width += txtWidth + butPtr->padX;
929 height = (height > txtHeight ? height : txtHeight);
931 case COMPOUND_CENTER:
933 * Image and text are superimposed.
936 width = (width > txtWidth ? width : txtWidth);
937 height = (height > txtHeight ? height : txtHeight);
942 if (butPtr->width > 0) {
943 width = butPtr->width;
945 if (butPtr->height > 0) {
946 height = butPtr->height;
949 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
950 butPtr->indicatorSpace = height;
951 if (butPtr->type == TYPE_CHECK_BUTTON) {
952 butPtr->indicatorDiameter = (65*height)/100;
954 butPtr->indicatorDiameter = (75*height)/100;
958 width += 2*butPtr->padX;
959 height += 2*butPtr->padY;
962 if (butPtr->width > 0) {
963 width = butPtr->width;
965 if (butPtr->height > 0) {
966 height = butPtr->height;
969 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
970 butPtr->indicatorSpace = height;
971 if (butPtr->type == TYPE_CHECK_BUTTON) {
972 butPtr->indicatorDiameter = (65*height)/100;
974 butPtr->indicatorDiameter = (75*height)/100;
981 if (butPtr->width > 0) {
982 width = butPtr->width * avgWidth;
984 if (butPtr->height > 0) {
985 height = butPtr->height * fm.linespace;
987 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
988 butPtr->indicatorDiameter = fm.linespace;
989 if (butPtr->type == TYPE_CHECK_BUTTON) {
990 butPtr->indicatorDiameter =
991 (80*butPtr->indicatorDiameter)/100;
993 butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
999 * When issuing the geometry request, add extra space for the indicator,
1000 * if any, and for the border and padding, plus two extra pixels so the
1001 * display can be offset by 1 pixel in either direction for the raised or
1005 if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
1006 width += 2*butPtr->padX;
1007 height += 2*butPtr->padY;
1009 if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
1013 Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
1014 + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
1015 Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);