1/* 2 * tkUnixMenubu.c -- 3 * 4 * This file implements the Unix specific portion of the menubutton 5 * widget. 6 * 7 * Copyright (c) 1996-1997 by Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id$ 13 */ 14 15#include "tkInt.h" 16#include "tkMenubutton.h" 17 18/* 19 * The structure below defines menubutton class behavior by means of functions 20 * that can be invoked from generic window code. 21 */ 22 23Tk_ClassProcs tkpMenubuttonClass = { 24 sizeof(Tk_ClassProcs), /* size */ 25 TkMenuButtonWorldChanged, /* worldChangedProc */ 26}; 27 28/* 29 *---------------------------------------------------------------------- 30 * 31 * TkpCreateMenuButton -- 32 * 33 * Allocate a new TkMenuButton structure. 34 * 35 * Results: 36 * Returns a newly allocated TkMenuButton structure. 37 * 38 * Side effects: 39 * Registers an event handler for the widget. 40 * 41 *---------------------------------------------------------------------- 42 */ 43 44TkMenuButton * 45TkpCreateMenuButton( 46 Tk_Window tkwin) 47{ 48 return (TkMenuButton *)ckalloc(sizeof(TkMenuButton)); 49} 50 51/* 52 *---------------------------------------------------------------------- 53 * 54 * TkpDisplayMenuButton -- 55 * 56 * This function is invoked to display a menubutton widget. 57 * 58 * Results: 59 * None. 60 * 61 * Side effects: 62 * Commands are output to X to display the menubutton in its current 63 * mode. 64 * 65 *---------------------------------------------------------------------- 66 */ 67 68void 69TkpDisplayMenuButton( 70 ClientData clientData) /* Information about widget. */ 71{ 72 register TkMenuButton *mbPtr = (TkMenuButton *) clientData; 73 GC gc; 74 Tk_3DBorder border; 75 Pixmap pixmap; 76 int x = 0; /* Initialization needed only to stop compiler 77 * warning. */ 78 int y = 0; 79 register Tk_Window tkwin = mbPtr->tkwin; 80 int fullWidth, fullHeight; 81 int textXOffset, textYOffset; 82 int imageWidth, imageHeight; 83 int imageXOffset, imageYOffset; 84 int width = 0, height = 0; 85 /* Image information that will be used to 86 * restrict disabled pixmap as well */ 87 int haveImage = 0, haveText = 0; 88 89 mbPtr->flags &= ~REDRAW_PENDING; 90 if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { 91 return; 92 } 93 94 if ((mbPtr->state == STATE_DISABLED) && (mbPtr->disabledFg != NULL)) { 95 gc = mbPtr->disabledGC; 96 border = mbPtr->normalBorder; 97 } else if ((mbPtr->state == STATE_ACTIVE) 98 && !Tk_StrictMotif(mbPtr->tkwin)) { 99 gc = mbPtr->activeTextGC; 100 border = mbPtr->activeBorder; 101 } else { 102 gc = mbPtr->normalTextGC; 103 border = mbPtr->normalBorder; 104 } 105 106 if (mbPtr->image != None) { 107 Tk_SizeOfImage(mbPtr->image, &width, &height); 108 haveImage = 1; 109 } else if (mbPtr->bitmap != None) { 110 Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height); 111 haveImage = 1; 112 } 113 imageWidth = width; 114 imageHeight = height; 115 116 haveText = (mbPtr->textWidth != 0 && mbPtr->textHeight != 0); 117 118 /* 119 * In order to avoid screen flashes, this function redraws the menu button 120 * in a pixmap, then copies the pixmap to the screen in a single 121 * operation. This means that there's no point in time where the on-sreen 122 * image has been cleared. 123 */ 124 125 pixmap = Tk_GetPixmap(mbPtr->display, Tk_WindowId(tkwin), 126 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); 127 Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin), 128 Tk_Height(tkwin), 0, TK_RELIEF_FLAT); 129 130 imageXOffset = 0; 131 imageYOffset = 0; 132 textXOffset = 0; 133 textYOffset = 0; 134 fullWidth = 0; 135 fullHeight = 0; 136 137 if (mbPtr->compound != COMPOUND_NONE && haveImage && haveText) { 138 switch ((enum compound) mbPtr->compound) { 139 case COMPOUND_TOP: 140 case COMPOUND_BOTTOM: 141 /* 142 * Image is above or below text. 143 */ 144 145 if (mbPtr->compound == COMPOUND_TOP) { 146 textYOffset = height + mbPtr->padY; 147 } else { 148 imageYOffset = mbPtr->textHeight + mbPtr->padY; 149 } 150 fullHeight = height + mbPtr->textHeight + mbPtr->padY; 151 fullWidth = (width > mbPtr->textWidth ? width : mbPtr->textWidth); 152 textXOffset = (fullWidth - mbPtr->textWidth)/2; 153 imageXOffset = (fullWidth - width)/2; 154 break; 155 case COMPOUND_LEFT: 156 case COMPOUND_RIGHT: 157 /* 158 * Image is left or right of text. 159 */ 160 161 if (mbPtr->compound == COMPOUND_LEFT) { 162 textXOffset = width + mbPtr->padX; 163 } else { 164 imageXOffset = mbPtr->textWidth + mbPtr->padX; 165 } 166 fullWidth = mbPtr->textWidth + mbPtr->padX + width; 167 fullHeight = (height > mbPtr->textHeight ? height : 168 mbPtr->textHeight); 169 textYOffset = (fullHeight - mbPtr->textHeight)/2; 170 imageYOffset = (fullHeight - height)/2; 171 break; 172 case COMPOUND_CENTER: 173 /* 174 * Image and text are superimposed. 175 */ 176 177 fullWidth = (width > mbPtr->textWidth ? width : mbPtr->textWidth); 178 fullHeight = (height > mbPtr->textHeight ? height : 179 mbPtr->textHeight); 180 textXOffset = (fullWidth - mbPtr->textWidth)/2; 181 imageXOffset = (fullWidth - width)/2; 182 textYOffset = (fullHeight - mbPtr->textHeight)/2; 183 imageYOffset = (fullHeight - height)/2; 184 break; 185 case COMPOUND_NONE: 186 break; 187 } 188 189 TkComputeAnchor(mbPtr->anchor, tkwin, 0, 0, 190 mbPtr->indicatorWidth + fullWidth, fullHeight, &x, &y); 191 192 imageXOffset += x; 193 imageYOffset += y; 194 if (mbPtr->image != NULL) { 195 Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap, 196 imageXOffset, imageYOffset); 197 } else if (mbPtr->bitmap != None) { 198 XSetClipOrigin(mbPtr->display, gc, imageXOffset, imageYOffset); 199 XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap, 200 gc, 0, 0, (unsigned) width, (unsigned) height, 201 imageXOffset, imageYOffset, 1); 202 XSetClipOrigin(mbPtr->display, gc, 0, 0); 203 } 204 205 Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout, 206 x + textXOffset, y + textYOffset, 0, -1); 207 Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout, 208 x + textXOffset, y + textYOffset, mbPtr->underline); 209 } else if (haveImage) { 210 TkComputeAnchor(mbPtr->anchor, tkwin, 0, 0, 211 width + mbPtr->indicatorWidth, height, &x, &y); 212 imageXOffset += x; 213 imageYOffset += y; 214 if (mbPtr->image != NULL) { 215 Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap, 216 imageXOffset, imageYOffset); 217 } else if (mbPtr->bitmap != None) { 218 XSetClipOrigin(mbPtr->display, gc, x, y); 219 XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap, 220 gc, 0, 0, (unsigned) width, (unsigned) height, 221 x, y, 1); 222 XSetClipOrigin(mbPtr->display, gc, 0, 0); 223 } 224 } else { 225 TkComputeAnchor(mbPtr->anchor, tkwin, mbPtr->padX, mbPtr->padY, 226 mbPtr->textWidth + mbPtr->indicatorWidth, 227 mbPtr->textHeight, &x, &y); 228 Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout, 229 x + textXOffset, y + textYOffset, 0, -1); 230 Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc, 231 mbPtr->textLayout, x + textXOffset, y + textYOffset, 232 mbPtr->underline); 233 } 234 235 /* 236 * If the menu button is disabled with a stipple rather than a special 237 * foreground color, generate the stippled effect. 238 */ 239 240 if ((mbPtr->state == STATE_DISABLED) 241 && ((mbPtr->disabledFg == NULL) || (mbPtr->image != NULL))) { 242 /* 243 * Stipple the whole button if no disabledFg was specified, otherwise 244 * restrict stippling only to displayed image 245 */ 246 247 if (mbPtr->disabledFg == NULL) { 248 XFillRectangle(mbPtr->display, pixmap, mbPtr->stippleGC, 249 mbPtr->inset, mbPtr->inset, 250 (unsigned) (Tk_Width(tkwin) - 2*mbPtr->inset), 251 (unsigned) (Tk_Height(tkwin) - 2*mbPtr->inset)); 252 } else { 253 XFillRectangle(mbPtr->display, pixmap, mbPtr->stippleGC, 254 imageXOffset, imageYOffset, 255 (unsigned) imageWidth, (unsigned) imageHeight); 256 } 257 } 258 259 /* 260 * Draw the cascade indicator for the menu button on the right side of the 261 * window, if desired. 262 */ 263 264 if (mbPtr->indicatorOn) { 265 int borderWidth; 266 267 borderWidth = (mbPtr->indicatorHeight+1)/3; 268 if (borderWidth < 1) { 269 borderWidth = 1; 270 } 271 /*y += mbPtr->textHeight / 2;*/ 272 Tk_Fill3DRectangle(tkwin, pixmap, border, 273 Tk_Width(tkwin) - mbPtr->inset - mbPtr->indicatorWidth 274 + mbPtr->indicatorHeight, 275 ((int) (Tk_Height(tkwin) - mbPtr->indicatorHeight))/2, 276 mbPtr->indicatorWidth - 2*mbPtr->indicatorHeight, 277 mbPtr->indicatorHeight, borderWidth, TK_RELIEF_RAISED); 278 } 279 280 /* 281 * Draw the border and traversal highlight last. This way, if the menu 282 * button's contents overflow onto the border they'll be covered up by the 283 * border. 284 */ 285 286 if (mbPtr->relief != TK_RELIEF_FLAT) { 287 Tk_Draw3DRectangle(tkwin, pixmap, border, 288 mbPtr->highlightWidth, mbPtr->highlightWidth, 289 Tk_Width(tkwin) - 2*mbPtr->highlightWidth, 290 Tk_Height(tkwin) - 2*mbPtr->highlightWidth, 291 mbPtr->borderWidth, mbPtr->relief); 292 } 293 if (mbPtr->highlightWidth != 0) { 294 GC gc; 295 296 if (mbPtr->flags & GOT_FOCUS) { 297 gc = Tk_GCForColor(mbPtr->highlightColorPtr, pixmap); 298 } else { 299 gc = Tk_GCForColor(mbPtr->highlightBgColorPtr, pixmap); 300 } 301 Tk_DrawFocusHighlight(tkwin, gc, mbPtr->highlightWidth, pixmap); 302 } 303 304 /* 305 * Copy the information from the off-screen pixmap onto the screen, then 306 * delete the pixmap. 307 */ 308 309 XCopyArea(mbPtr->display, pixmap, Tk_WindowId(tkwin), 310 mbPtr->normalTextGC, 0, 0, (unsigned) Tk_Width(tkwin), 311 (unsigned) Tk_Height(tkwin), 0, 0); 312 Tk_FreePixmap(mbPtr->display, pixmap); 313} 314 315/* 316 *---------------------------------------------------------------------- 317 * 318 * TkpDestroyMenuButton -- 319 * 320 * Free data structures associated with the menubutton control. 321 * 322 * Results: 323 * None. 324 * 325 * Side effects: 326 * Restores the default control state. 327 * 328 *---------------------------------------------------------------------- 329 */ 330 331void 332TkpDestroyMenuButton( 333 TkMenuButton *mbPtr) 334{ 335} 336 337/* 338 *---------------------------------------------------------------------- 339 * 340 * TkpComputeMenuButtonGeometry -- 341 * 342 * After changes in a menu button's text or bitmap, this function 343 * recomputes the menu button's geometry and passes this information 344 * along to the geometry manager for the window. 345 * 346 * Results: 347 * None. 348 * 349 * Side effects: 350 * The menu button's window may change size. 351 * 352 *---------------------------------------------------------------------- 353 */ 354 355void 356TkpComputeMenuButtonGeometry( 357 TkMenuButton *mbPtr) /* Widget record for menu button. */ 358{ 359 int width, height, mm, pixels; 360 int avgWidth, txtWidth, txtHeight; 361 int haveImage = 0, haveText = 0; 362 Tk_FontMetrics fm; 363 364 mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth; 365 366 width = 0; 367 height = 0; 368 txtWidth = 0; 369 txtHeight = 0; 370 avgWidth = 0; 371 372 if (mbPtr->image != None) { 373 Tk_SizeOfImage(mbPtr->image, &width, &height); 374 haveImage = 1; 375 } else if (mbPtr->bitmap != None) { 376 Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height); 377 haveImage = 1; 378 } 379 380 if (haveImage == 0 || mbPtr->compound != COMPOUND_NONE) { 381 Tk_FreeTextLayout(mbPtr->textLayout); 382 383 mbPtr->textLayout = Tk_ComputeTextLayout(mbPtr->tkfont, mbPtr->text, 384 -1, mbPtr->wrapLength, mbPtr->justify, 0, &mbPtr->textWidth, 385 &mbPtr->textHeight); 386 txtWidth = mbPtr->textWidth; 387 txtHeight = mbPtr->textHeight; 388 avgWidth = Tk_TextWidth(mbPtr->tkfont, "0", 1); 389 Tk_GetFontMetrics(mbPtr->tkfont, &fm); 390 haveText = (txtWidth != 0 && txtHeight != 0); 391 } 392 393 /* 394 * If the menubutton is compound (ie, it shows both an image and text), 395 * the new geometry is a combination of the image and text geometry. We 396 * only honor the compound bit if the menubutton has both text and an 397 * image, because otherwise it is not really a compound menubutton. 398 */ 399 400 if (mbPtr->compound != COMPOUND_NONE && haveImage && haveText) { 401 switch ((enum compound) mbPtr->compound) { 402 case COMPOUND_TOP: 403 case COMPOUND_BOTTOM: 404 /* 405 * Image is above or below text. 406 */ 407 408 height += txtHeight + mbPtr->padY; 409 width = (width > txtWidth ? width : txtWidth); 410 break; 411 case COMPOUND_LEFT: 412 case COMPOUND_RIGHT: 413 /* 414 * Image is left or right of text. 415 */ 416 417 width += txtWidth + mbPtr->padX; 418 height = (height > txtHeight ? height : txtHeight); 419 break; 420 case COMPOUND_CENTER: 421 /* 422 * Image and text are superimposed. 423 */ 424 425 width = (width > txtWidth ? width : txtWidth); 426 height = (height > txtHeight ? height : txtHeight); 427 break; 428 case COMPOUND_NONE: 429 break; 430 } 431 if (mbPtr->width > 0) { 432 width = mbPtr->width; 433 } 434 if (mbPtr->height > 0) { 435 height = mbPtr->height; 436 } 437 width += 2*mbPtr->padX; 438 height += 2*mbPtr->padY; 439 } else { 440 if (haveImage) { 441 if (mbPtr->width > 0) { 442 width = mbPtr->width; 443 } 444 if (mbPtr->height > 0) { 445 height = mbPtr->height; 446 } 447 } else { 448 width = txtWidth; 449 height = txtHeight; 450 if (mbPtr->width > 0) { 451 width = mbPtr->width * avgWidth; 452 } 453 if (mbPtr->height > 0) { 454 height = mbPtr->height * fm.linespace; 455 } 456 } 457 } 458 459 if (! haveImage) { 460 width += 2*mbPtr->padX; 461 height += 2*mbPtr->padY; 462 } 463 464 if (mbPtr->indicatorOn) { 465 mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin)); 466 pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin)); 467 mbPtr->indicatorHeight= (INDICATOR_HEIGHT * pixels)/(10*mm); 468 mbPtr->indicatorWidth = (INDICATOR_WIDTH * pixels)/(10*mm) 469 + 2*mbPtr->indicatorHeight; 470 width += mbPtr->indicatorWidth; 471 } else { 472 mbPtr->indicatorHeight = 0; 473 mbPtr->indicatorWidth = 0; 474 } 475 476 Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset), 477 (int) (height + 2*mbPtr->inset)); 478 Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset); 479} 480 481/* 482 * Local Variables: 483 * mode: c 484 * c-basic-offset: 4 485 * fill-column: 78 486 * End: 487 */ 488