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