4 * This file implements the Unix specific portion of the menubutton
7 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 #include "tkMenubutton.h"
18 *----------------------------------------------------------------------
20 * TkpCreateMenuButton --
22 * Allocate a new TkMenuButton structure.
25 * Returns a newly allocated TkMenuButton structure.
30 *----------------------------------------------------------------------
35 TCL_UNUSED(Tk_Window))
37 return (TkMenuButton *)ckalloc(sizeof(TkMenuButton));
41 *----------------------------------------------------------------------
43 * TkpDisplayMenuButton --
45 * This function is invoked to display a menubutton widget.
51 * Commands are output to X to display the menubutton in its current
54 *----------------------------------------------------------------------
59 ClientData clientData) /* Information about widget. */
61 TkMenuButton *mbPtr = (TkMenuButton *)clientData;
65 int x = 0; /* Initialization needed only to stop compiler
68 Tk_Window tkwin = mbPtr->tkwin;
69 int fullWidth, fullHeight;
70 int textXOffset, textYOffset;
71 int imageWidth, imageHeight;
72 int imageXOffset, imageYOffset;
73 int width = 0, height = 0;
74 /* Image information that will be used to
75 * restrict disabled pixmap as well */
76 int haveImage = 0, haveText = 0;
78 mbPtr->flags &= ~REDRAW_PENDING;
79 if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
83 if ((mbPtr->state == STATE_DISABLED) && (mbPtr->disabledFg != NULL)) {
84 gc = mbPtr->disabledGC;
85 border = mbPtr->normalBorder;
86 } else if ((mbPtr->state == STATE_ACTIVE)
87 && !Tk_StrictMotif(mbPtr->tkwin)) {
88 gc = mbPtr->activeTextGC;
89 border = mbPtr->activeBorder;
91 gc = mbPtr->normalTextGC;
92 border = mbPtr->normalBorder;
95 if (mbPtr->image != NULL) {
96 Tk_SizeOfImage(mbPtr->image, &width, &height);
98 } else if (mbPtr->bitmap != None) {
99 Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
103 imageHeight = height;
105 haveText = (mbPtr->textWidth != 0 && mbPtr->textHeight != 0);
108 * In order to avoid screen flashes, this function redraws the menu button
109 * in a pixmap, then copies the pixmap to the screen in a single
110 * operation. This means that there's no point in time where the on-sreen
111 * image has been cleared.
114 pixmap = Tk_GetPixmap(mbPtr->display, Tk_WindowId(tkwin),
115 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
116 Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
117 Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
126 if (mbPtr->compound != COMPOUND_NONE && haveImage && haveText) {
127 switch ((enum compound) mbPtr->compound) {
129 case COMPOUND_BOTTOM:
131 * Image is above or below text.
134 if (mbPtr->compound == COMPOUND_TOP) {
135 textYOffset = height + mbPtr->padY;
137 imageYOffset = mbPtr->textHeight + mbPtr->padY;
139 fullHeight = height + mbPtr->textHeight + mbPtr->padY;
140 fullWidth = (width > mbPtr->textWidth ? width : mbPtr->textWidth);
141 textXOffset = (fullWidth - mbPtr->textWidth)/2;
142 imageXOffset = (fullWidth - width)/2;
147 * Image is left or right of text.
150 if (mbPtr->compound == COMPOUND_LEFT) {
151 textXOffset = width + mbPtr->padX;
153 imageXOffset = mbPtr->textWidth + mbPtr->padX;
155 fullWidth = mbPtr->textWidth + mbPtr->padX + width;
156 fullHeight = (height > mbPtr->textHeight ? height :
158 textYOffset = (fullHeight - mbPtr->textHeight)/2;
159 imageYOffset = (fullHeight - height)/2;
161 case COMPOUND_CENTER:
163 * Image and text are superimposed.
166 fullWidth = (width > mbPtr->textWidth ? width : mbPtr->textWidth);
167 fullHeight = (height > mbPtr->textHeight ? height :
169 textXOffset = (fullWidth - mbPtr->textWidth)/2;
170 imageXOffset = (fullWidth - width)/2;
171 textYOffset = (fullHeight - mbPtr->textHeight)/2;
172 imageYOffset = (fullHeight - height)/2;
178 TkComputeAnchor(mbPtr->anchor, tkwin, 0, 0,
179 mbPtr->indicatorWidth + fullWidth, fullHeight, &x, &y);
183 if (mbPtr->image != NULL) {
184 Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap,
185 imageXOffset, imageYOffset);
186 } else if (mbPtr->bitmap != None) {
187 XSetClipOrigin(mbPtr->display, gc, imageXOffset, imageYOffset);
188 XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap,
189 gc, 0, 0, (unsigned) width, (unsigned) height,
190 imageXOffset, imageYOffset, 1);
191 XSetClipOrigin(mbPtr->display, gc, 0, 0);
194 Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout,
195 x + textXOffset, y + textYOffset, 0, -1);
196 Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout,
197 x + textXOffset, y + textYOffset, mbPtr->underline);
198 } else if (haveImage) {
199 TkComputeAnchor(mbPtr->anchor, tkwin, 0, 0,
200 width + mbPtr->indicatorWidth, height, &x, &y);
203 if (mbPtr->image != NULL) {
204 Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap,
205 imageXOffset, imageYOffset);
206 } else if (mbPtr->bitmap != None) {
207 XSetClipOrigin(mbPtr->display, gc, x, y);
208 XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap,
209 gc, 0, 0, (unsigned) width, (unsigned) height,
211 XSetClipOrigin(mbPtr->display, gc, 0, 0);
214 TkComputeAnchor(mbPtr->anchor, tkwin, mbPtr->padX, mbPtr->padY,
215 mbPtr->textWidth + mbPtr->indicatorWidth,
216 mbPtr->textHeight, &x, &y);
217 Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout,
218 x + textXOffset, y + textYOffset, 0, -1);
219 Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc,
220 mbPtr->textLayout, x + textXOffset, y + textYOffset,
225 * If the menu button is disabled with a stipple rather than a special
226 * foreground color, generate the stippled effect.
229 if ((mbPtr->state == STATE_DISABLED)
230 && ((mbPtr->disabledFg == NULL) || (mbPtr->image != NULL))) {
232 * Stipple the whole button if no disabledFg was specified, otherwise
233 * restrict stippling only to displayed image
236 if (mbPtr->disabledFg == NULL) {
237 XFillRectangle(mbPtr->display, pixmap, mbPtr->stippleGC,
238 mbPtr->inset, mbPtr->inset,
239 (unsigned) (Tk_Width(tkwin) - 2*mbPtr->inset),
240 (unsigned) (Tk_Height(tkwin) - 2*mbPtr->inset));
242 XFillRectangle(mbPtr->display, pixmap, mbPtr->stippleGC,
243 imageXOffset, imageYOffset,
244 (unsigned) imageWidth, (unsigned) imageHeight);
249 * Draw the cascade indicator for the menu button on the right side of the
250 * window, if desired.
253 if (mbPtr->indicatorOn) {
256 borderWidth = (mbPtr->indicatorHeight+1)/3;
257 if (borderWidth < 1) {
260 /*y += mbPtr->textHeight / 2;*/
261 Tk_Fill3DRectangle(tkwin, pixmap, border,
262 Tk_Width(tkwin) - mbPtr->inset - mbPtr->indicatorWidth
263 + mbPtr->indicatorHeight,
264 ((int) (Tk_Height(tkwin) - mbPtr->indicatorHeight))/2,
265 mbPtr->indicatorWidth - 2*mbPtr->indicatorHeight,
266 mbPtr->indicatorHeight, borderWidth, TK_RELIEF_RAISED);
270 * Draw the border and traversal highlight last. This way, if the menu
271 * button's contents overflow onto the border they'll be covered up by the
275 if (mbPtr->relief != TK_RELIEF_FLAT) {
276 Tk_Draw3DRectangle(tkwin, pixmap, border,
277 mbPtr->highlightWidth, mbPtr->highlightWidth,
278 Tk_Width(tkwin) - 2*mbPtr->highlightWidth,
279 Tk_Height(tkwin) - 2*mbPtr->highlightWidth,
280 mbPtr->borderWidth, mbPtr->relief);
282 if (mbPtr->highlightWidth != 0) {
283 if (mbPtr->flags & GOT_FOCUS) {
284 gc = Tk_GCForColor(mbPtr->highlightColorPtr, pixmap);
286 gc = Tk_GCForColor(mbPtr->highlightBgColorPtr, pixmap);
288 Tk_DrawFocusHighlight(tkwin, gc, mbPtr->highlightWidth, pixmap);
292 * Copy the information from the off-screen pixmap onto the screen, then
296 XCopyArea(mbPtr->display, pixmap, Tk_WindowId(tkwin),
297 mbPtr->normalTextGC, 0, 0, (unsigned) Tk_Width(tkwin),
298 (unsigned) Tk_Height(tkwin), 0, 0);
299 Tk_FreePixmap(mbPtr->display, pixmap);
303 *----------------------------------------------------------------------
305 * TkpDestroyMenuButton --
307 * Free data structures associated with the menubutton control.
313 * Restores the default control state.
315 *----------------------------------------------------------------------
319 TkpDestroyMenuButton(
325 *----------------------------------------------------------------------
327 * TkpComputeMenuButtonGeometry --
329 * After changes in a menu button's text or bitmap, this function
330 * recomputes the menu button's geometry and passes this information
331 * along to the geometry manager for the window.
337 * The menu button's window may change size.
339 *----------------------------------------------------------------------
343 TkpComputeMenuButtonGeometry(
344 TkMenuButton *mbPtr) /* Widget record for menu button. */
346 int width, height, mm, pixels;
347 int avgWidth, txtWidth, txtHeight;
348 int haveImage = 0, haveText = 0;
351 mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth;
359 if (mbPtr->image != NULL) {
360 Tk_SizeOfImage(mbPtr->image, &width, &height);
362 } else if (mbPtr->bitmap != None) {
363 Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
367 if (haveImage == 0 || mbPtr->compound != COMPOUND_NONE) {
368 Tk_FreeTextLayout(mbPtr->textLayout);
370 mbPtr->textLayout = Tk_ComputeTextLayout(mbPtr->tkfont, mbPtr->text,
371 -1, mbPtr->wrapLength, mbPtr->justify, 0, &mbPtr->textWidth,
373 txtWidth = mbPtr->textWidth;
374 txtHeight = mbPtr->textHeight;
375 avgWidth = Tk_TextWidth(mbPtr->tkfont, "0", 1);
376 Tk_GetFontMetrics(mbPtr->tkfont, &fm);
377 haveText = (txtWidth != 0 && txtHeight != 0);
381 * If the menubutton is compound (ie, it shows both an image and text),
382 * the new geometry is a combination of the image and text geometry. We
383 * only honor the compound bit if the menubutton has both text and an
384 * image, because otherwise it is not really a compound menubutton.
387 if (mbPtr->compound != COMPOUND_NONE && haveImage && haveText) {
388 switch ((enum compound) mbPtr->compound) {
390 case COMPOUND_BOTTOM:
392 * Image is above or below text.
395 height += txtHeight + mbPtr->padY;
396 width = (width > txtWidth ? width : txtWidth);
401 * Image is left or right of text.
404 width += txtWidth + mbPtr->padX;
405 height = (height > txtHeight ? height : txtHeight);
407 case COMPOUND_CENTER:
409 * Image and text are superimposed.
412 width = (width > txtWidth ? width : txtWidth);
413 height = (height > txtHeight ? height : txtHeight);
418 if (mbPtr->width > 0) {
419 width = mbPtr->width;
421 if (mbPtr->height > 0) {
422 height = mbPtr->height;
424 width += 2*mbPtr->padX;
425 height += 2*mbPtr->padY;
428 if (mbPtr->width > 0) {
429 width = mbPtr->width;
431 if (mbPtr->height > 0) {
432 height = mbPtr->height;
437 if (mbPtr->width > 0) {
438 width = mbPtr->width * avgWidth;
440 if (mbPtr->height > 0) {
441 height = mbPtr->height * fm.linespace;
447 width += 2*mbPtr->padX;
448 height += 2*mbPtr->padY;
451 if (mbPtr->indicatorOn) {
452 mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin));
453 pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin));
454 mbPtr->indicatorHeight= (INDICATOR_HEIGHT * pixels)/(10*mm);
455 mbPtr->indicatorWidth = (INDICATOR_WIDTH * pixels)/(10*mm)
456 + 2*mbPtr->indicatorHeight;
457 width += mbPtr->indicatorWidth;
459 mbPtr->indicatorHeight = 0;
460 mbPtr->indicatorWidth = 0;
463 Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset),
464 (int) (height + 2*mbPtr->inset));
465 Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset);