Command.c revision 7a84e134
1/* $Xorg: Command.c,v 1.5 2001/02/09 02:03:43 xorgcvs Exp $ */ 2 3/*********************************************************** 4 5Copyright 1987, 1988, 1994, 1998 The Open Group 6 7Permission to use, copy, modify, distribute, and sell this software and its 8documentation for any purpose is hereby granted without fee, provided that 9the above copyright notice appear in all copies and that both that 10copyright notice and this permission notice appear in supporting 11documentation. 12 13The above copyright notice and this permission notice shall be included in 14all copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 23Except as contained in this notice, the name of The Open Group shall not be 24used in advertising or otherwise to promote the sale, use or other dealings 25in this Software without prior written authorization from The Open Group. 26 27 28Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. 29 30 All Rights Reserved 31 32Permission to use, copy, modify, and distribute this software and its 33documentation for any purpose and without fee is hereby granted, 34provided that the above copyright notice appear in all copies and that 35both that copyright notice and this permission notice appear in 36supporting documentation, and that the name of Digital not be 37used in advertising or publicity pertaining to distribution of the 38software without specific, written prior permission. 39 40DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 41ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 42DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 43ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 44WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 45ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 46SOFTWARE. 47 48******************************************************************/ 49/* $XFree86: xc/lib/Xaw/Command.c,v 1.15tsi Exp $ */ 50 51/* 52 * Command.c - Command button widget 53 */ 54 55#ifdef HAVE_CONFIG_H 56#include <config.h> 57#endif 58#include <stdio.h> 59#include <X11/IntrinsicP.h> 60#include <X11/StringDefs.h> 61#include <X11/extensions/shape.h> 62#include <X11/Xmu/Converters.h> 63#include <X11/Xmu/Drawing.h> 64#include <X11/Xmu/Misc.h> 65#include <X11/Xaw/CommandP.h> 66#include <X11/Xaw/XawInit.h> 67#include "Private.h" 68 69#define DEFAULT_HIGHLIGHT_THICKNESS 2 70#define DEFAULT_SHAPE_HIGHLIGHT 32767 71#define STR_EQUAL(str1, str2) (str1 == str2 || strcmp(str1, str2) == 0) 72 73/* 74 * Class Methods 75 */ 76static void XawCommandClassInitialize(void); 77static void XawCommandDestroy(Widget); 78static void XawCommandInitialize(Widget, Widget, ArgList, Cardinal*); 79static void XawCommandRealize(Widget, Mask*, XSetWindowAttributes*); 80static void XawCommandResize(Widget); 81static void XawCommandRedisplay(Widget, XEvent*, Region); 82static Boolean XawCommandSetValues(Widget, Widget, Widget, ArgList, Cardinal*); 83static void XawCommandGetValuesHook(Widget, ArgList, Cardinal*); 84static Bool ChangeSensitive(Widget); 85 86/* 87 * Prototypes 88 */ 89static GC Get_GC(CommandWidget, Pixel, Pixel); 90static void PaintCommandWidget(Widget, XEvent*, Region, Bool); 91static Region HighlightRegion(CommandWidget); 92static Bool ShapeButton(CommandWidget, Bool); 93static void XawCommandToggle(Widget); 94 95/* 96 * Actions 97 */ 98static void Highlight(Widget, XEvent*, String*, Cardinal*); 99static void Notify(Widget, XEvent*, String*, Cardinal*); 100static void Reset(Widget, XEvent*, String*, Cardinal*); 101static void Set(Widget, XEvent*, String*, Cardinal*); 102static void Unhighlight(Widget, XEvent*, String*, Cardinal*); 103static void Unset(Widget, XEvent*, String*, Cardinal*); 104 105/* 106 * Initialization 107 */ 108static char defaultTranslations[] = 109"<Enter>:" "highlight()\n" 110"<Leave>:" "reset()\n" 111"<Btn1Down>:" "set()\n" 112"<Btn1Up>:" "notify() unset()\n" 113; 114 115#define offset(field) XtOffsetOf(CommandRec, field) 116static XtResource resources[] = { 117 { 118 XtNcallback, 119 XtCCallback, 120 XtRCallback, 121 sizeof(XtPointer), 122 offset(command.callbacks), 123 XtRCallback, 124 NULL 125 }, 126 { 127 XtNhighlightThickness, 128 XtCThickness, 129 XtRDimension, 130 sizeof(Dimension), 131 offset(command.highlight_thickness), 132 XtRImmediate, 133 (XtPointer)DEFAULT_SHAPE_HIGHLIGHT 134 }, 135 { 136 XtNshapeStyle, 137 XtCShapeStyle, 138 XtRShapeStyle, 139 sizeof(int), 140 offset(command.shape_style), 141 XtRImmediate, 142 (XtPointer)XawShapeRectangle 143 }, 144 { 145 XtNcornerRoundPercent, 146 XtCCornerRoundPercent, 147 XtRDimension, 148 sizeof(Dimension), 149 offset(command.corner_round), 150 XtRImmediate, 151 (XtPointer)25 152 }, 153}; 154#undef offset 155 156static XtActionsRec actionsList[] = { 157 {"set", Set}, 158 {"notify", Notify}, 159 {"highlight", Highlight}, 160 {"reset", Reset}, 161 {"unset", Unset}, 162 {"unhighlight", Unhighlight} 163}; 164 165#define SuperClass ((LabelWidgetClass)&labelClassRec) 166 167CommandClassRec commandClassRec = { 168 /* core */ 169 { 170 (WidgetClass)SuperClass, /* superclass */ 171 "Command", /* class_name */ 172 sizeof(CommandRec), /* size */ 173 XawCommandClassInitialize, /* class_initialize */ 174 NULL, /* class_part_initialize */ 175 False, /* class_inited */ 176 XawCommandInitialize, /* initialize */ 177 NULL, /* initialize_hook */ 178 XawCommandRealize, /* realize */ 179 actionsList, /* actions */ 180 XtNumber(actionsList), /* num_actions */ 181 resources, /* resources */ 182 XtNumber(resources), /* num_resources */ 183 NULLQUARK, /* xrm_class */ 184 False, /* compress_motion */ 185 True, /* compress_exposure */ 186 True, /* compress_enterleave */ 187 False, /* visible_interest */ 188 XawCommandDestroy, /* destroy */ 189 XawCommandResize, /* resize */ 190 XawCommandRedisplay, /* expose */ 191 XawCommandSetValues, /* set_values */ 192 NULL, /* set_values_hook */ 193 XtInheritSetValuesAlmost, /* set_values_almost */ 194 XawCommandGetValuesHook, /* get_values_hook */ 195 NULL, /* accept_focus */ 196 XtVersion, /* version */ 197 NULL, /* callback_private */ 198 defaultTranslations, /* tm_table */ 199 XtInheritQueryGeometry, /* query_geometry */ 200 XtInheritDisplayAccelerator, /* display_accelerator */ 201 NULL, /* extension */ 202 }, 203 /* simple */ 204 { 205 ChangeSensitive, /* change_sensitive */ 206 }, 207 /* label */ 208 { 209 0, /* not used */ 210 }, 211 /* command */ 212 { 213 0, /* not used */ 214 }, 215}; 216 217WidgetClass commandWidgetClass = (WidgetClass)&commandClassRec; 218 219/* 220 * Implementation 221 */ 222static GC 223Get_GC(CommandWidget cbw, Pixel fg, Pixel bg) 224{ 225 XGCValues values; 226 227 values.foreground = fg; 228 values.background = bg; 229 values.font = cbw->label.font->fid; 230 values.cap_style = CapProjecting; 231 232 if (cbw->command.highlight_thickness > 1) 233 values.line_width = cbw->command.highlight_thickness; 234 else 235 values.line_width = 0; 236 237 if (cbw->simple.international == True) 238 return (XtAllocateGC((Widget)cbw, 0, 239 GCForeground | GCBackground | GCLineWidth | 240 GCCapStyle, &values, GCFont, 0)); 241 else 242 return (XtGetGC((Widget)cbw, 243 GCForeground | GCBackground | GCFont | GCLineWidth | 244 GCCapStyle, &values)); 245} 246 247/*ARGSUSED*/ 248static void 249XawCommandInitialize(Widget request, Widget cnew, 250 ArgList args, Cardinal *num_args) 251{ 252 CommandWidget cbw = (CommandWidget)cnew; 253 int shape_event_base, shape_error_base; 254 255 if (!cbw->label.font) XtError("Aborting: no font found\n"); 256 257 if (cbw->command.shape_style != XawShapeRectangle && 258 !XShapeQueryExtension(XtDisplay(cnew), &shape_event_base, 259 &shape_error_base)) 260 cbw->command.shape_style = XawShapeRectangle; 261 262 if (cbw->command.highlight_thickness == DEFAULT_SHAPE_HIGHLIGHT) { 263 if (cbw->command.shape_style != XawShapeRectangle) 264 cbw->command.highlight_thickness = 0; 265 else 266 cbw->command.highlight_thickness = DEFAULT_HIGHLIGHT_THICKNESS; 267 } 268 269 cbw->command.normal_GC = Get_GC(cbw, cbw->label.foreground, 270 cbw->core.background_pixel); 271 cbw->command.inverse_GC = Get_GC(cbw, cbw->core.background_pixel, 272 cbw->label.foreground); 273 XtReleaseGC(cnew, cbw->label.normal_GC); 274 cbw->label.normal_GC = cbw->command.normal_GC; 275 276 cbw->command.set = False; 277 cbw->command.highlighted = HighlightNone; 278} 279 280static Region 281HighlightRegion(CommandWidget cbw) 282{ 283 static Region outerRegion = NULL, innerRegion, emptyRegion; 284 XRectangle rect; 285 286 if (cbw->command.highlight_thickness == 0 || 287 cbw->command.highlight_thickness > Min(XtWidth(cbw), XtHeight(cbw)) / 2) 288 return (NULL); 289 290 if (outerRegion == NULL) { 291 /* save time by allocating scratch regions only once. */ 292 outerRegion = XCreateRegion(); 293 innerRegion = XCreateRegion(); 294 emptyRegion = XCreateRegion(); 295 } 296 297 rect.x = rect.y = 0; 298 rect.width = XtWidth(cbw); 299 rect.height = XtHeight(cbw); 300 XUnionRectWithRegion(&rect, emptyRegion, outerRegion); 301 rect.x = rect.y = cbw->command.highlight_thickness; 302 rect.width -= cbw->command.highlight_thickness * 2; 303 rect.height -= cbw->command.highlight_thickness * 2; 304 XUnionRectWithRegion(&rect, emptyRegion, innerRegion); 305 XSubtractRegion(outerRegion, innerRegion, outerRegion); 306 307 return (outerRegion); 308} 309 310/*************************** 311* Action Procedures 312***************************/ 313static void 314XawCommandToggle(Widget w) 315{ 316 CommandWidget xaw = (CommandWidget)w; 317 Arg args[2]; 318 Cardinal num_args; 319 320 num_args = 0; 321 XtSetArg(args[num_args], XtNbackground, 322 xaw->label.foreground); ++num_args; 323 XtSetArg(args[num_args], XtNforeground, 324 xaw->core.background_pixel); ++num_args; 325 XtSetValues(w, args, num_args); 326} 327 328/*ARGSUSED*/ 329static void 330Set(Widget w, XEvent *event, String *params, Cardinal *num_params) 331{ 332 CommandWidget cbw = (CommandWidget)w; 333 334 if (cbw->command.set) 335 return; 336 337 XawCommandToggle(w); 338 cbw->command.set= True; 339} 340 341/*ARGSUSED*/ 342static void 343Unset(Widget w, XEvent *event, String *params, Cardinal *num_params) 344{ 345 CommandWidget cbw = (CommandWidget)w; 346 347 if (!cbw->command.set) 348 return; 349 350 cbw->command.set = False; 351 XawCommandToggle(w); 352} 353 354/*ARGSUSED*/ 355static void 356Reset(Widget w, XEvent *event, String *params, Cardinal *num_params) 357{ 358 CommandWidget cbw = (CommandWidget)w; 359 360 if (cbw->command.set) { 361 cbw->command.highlighted = HighlightNone; 362 Unset(w, event, params, num_params); 363 } 364 else 365 Unhighlight(w, event, params, num_params); 366} 367 368/*ARGSUSED*/ 369static void 370Highlight(Widget w, XEvent *event, String *params, Cardinal *num_params) 371{ 372 CommandWidget cbw = (CommandWidget)w; 373 374 if (*num_params == (Cardinal)0) 375 cbw->command.highlighted = HighlightWhenUnset; 376 else { 377 if (*num_params != (Cardinal)1) 378 XtWarning("Too many parameters passed to highlight action table."); 379 switch (params[0][0]) { 380 case 'A': 381 case 'a': 382 cbw->command.highlighted = HighlightAlways; 383 break; 384 default: 385 cbw->command.highlighted = HighlightWhenUnset; 386 break; 387 } 388 } 389 390 if (XtIsRealized(w)) 391 PaintCommandWidget(w, event, HighlightRegion(cbw), True); 392} 393 394/*ARGSUSED*/ 395static void 396Unhighlight(Widget w, XEvent *event, String *params, Cardinal *num_params) 397{ 398 CommandWidget cbw = (CommandWidget)w; 399 400 cbw->command.highlighted = HighlightNone; 401 if (XtIsRealized(w)) 402 PaintCommandWidget(w, event, HighlightRegion(cbw), True); 403} 404 405/*ARGSUSED*/ 406static void 407Notify(Widget w, XEvent *event, String *params, Cardinal *num_params) 408{ 409 CommandWidget cbw = (CommandWidget)w; 410 411 /* check to be sure state is still Set so that user can cancel 412 the action (e.g. by moving outside the window, in the default 413 bindings. 414 */ 415 if (cbw->command.set) 416 XtCallCallbackList(w, cbw->command.callbacks, (XtPointer) NULL); 417} 418 419static void 420XawCommandRedisplay(Widget w, XEvent *event, Region region) 421{ 422 PaintCommandWidget(w, event, region, False); 423} 424 425/* 426 * Function: 427 * PaintCommandWidget 428 * Parameters: 429 * w - command widget 430 * region - region to paint (passed to the superclass) 431 * change - did it change either set or highlight state? 432 */ 433static void 434PaintCommandWidget(Widget w, XEvent *event, Region region, Bool change) 435{ 436 CommandWidget cbw = (CommandWidget)w; 437 Bool very_thick; 438 GC rev_gc; 439 440 very_thick = cbw->command.highlight_thickness 441 > Min(XtWidth(cbw), XtHeight(cbw)) / 2; 442 443 if (cbw->command.highlight_thickness == 0) { 444 (*SuperClass->core_class.expose) (w, event, region); 445 return; 446 } 447 448 /* 449 * If we are set then use the same colors as if we are not highlighted 450 */ 451 452 if (cbw->command.highlighted != HighlightNone) { 453 rev_gc = cbw->command.normal_GC; 454 } 455 else { 456 rev_gc = cbw->command.inverse_GC; 457 } 458 459 if (!((!change && cbw->command.highlighted == HighlightNone) 460 || (cbw->command.highlighted == HighlightWhenUnset 461 && cbw->command.set))) { 462 if (very_thick) 463 XFillRectangle(XtDisplay(w),XtWindow(w), rev_gc, 464 0, 0, XtWidth(cbw), XtHeight(cbw)); 465 else { 466 /* wide lines are centered on the path, so indent it */ 467 if (cbw->core.background_pixmap != XtUnspecifiedPixmap && 468 rev_gc == cbw->command.inverse_GC) { 469 XClearArea(XtDisplay(w), XtWindow(w), 470 0, 0, XtWidth(cbw), cbw->command.highlight_thickness, 471 False); 472 XClearArea(XtDisplay(w), XtWindow(w), 473 0, cbw->command.highlight_thickness, 474 cbw->command.highlight_thickness, 475 XtHeight(cbw) - (cbw->command.highlight_thickness<<1), 476 False); 477 XClearArea(XtDisplay(w), XtWindow(w), 478 XtWidth(cbw) - cbw->command.highlight_thickness, 479 cbw->command.highlight_thickness, 480 cbw->command.highlight_thickness, 481 XtHeight(cbw) - (cbw->command.highlight_thickness<<1), 482 False); 483 XClearArea(XtDisplay(w), XtWindow(w), 484 0, XtHeight(cbw) - cbw->command.highlight_thickness, 485 XtWidth(cbw), cbw->command.highlight_thickness, 486 False); 487 } 488 else { 489 int offset = cbw->command.highlight_thickness / 2; 490 491 XDrawRectangle(XtDisplay(w),XtWindow(w), rev_gc, offset, offset, 492 XtWidth(cbw) - cbw->command.highlight_thickness, 493 XtHeight(cbw) - cbw->command.highlight_thickness); 494 } 495 } 496 } 497 498 (*SuperClass->core_class.expose)(w, event, region); 499} 500 501static void 502XawCommandDestroy(Widget w) 503{ 504 CommandWidget cbw = (CommandWidget)w; 505 506 /* Label will release cbw->command.normal_GC */ 507 XtReleaseGC(w, cbw->command.inverse_GC); 508} 509 510/*ARGSUSED*/ 511static Boolean 512XawCommandSetValues(Widget current, Widget request, Widget cnew, 513 ArgList args, Cardinal *num_args) 514{ 515 CommandWidget oldcbw = (CommandWidget)current; 516 CommandWidget cbw = (CommandWidget)cnew; 517 Boolean redisplay = False; 518 519 if (oldcbw->core.sensitive != cbw->core.sensitive && !cbw->core.sensitive) { 520 cbw->command.highlighted = HighlightNone; 521 redisplay = True; 522 } 523 524 if (cbw->command.set) { 525 unsigned int i; 526 Pixel foreground, background; 527 528 foreground = oldcbw->label.foreground; 529 background = oldcbw->core.background_pixel; 530 for (i = 0; i < *num_args; i++) { 531 if (STR_EQUAL(args[i].name, XtNforeground)) 532 background = cbw->label.foreground; 533 else if (STR_EQUAL(args[i].name, XtNbackground)) 534 foreground = cbw->core.background_pixel; 535 } 536 cbw->label.foreground = foreground; 537 cbw->core.background_pixel = background; 538 } 539 540 if (oldcbw->label.foreground != cbw->label.foreground 541 || oldcbw->core.background_pixel != cbw->core.background_pixel 542 || oldcbw->command.highlight_thickness 543 != cbw->command.highlight_thickness 544 || oldcbw->label.font != cbw->label.font) { 545 XtReleaseGC(cnew, cbw->command.inverse_GC); 546 547 cbw->command.normal_GC = Get_GC(cbw, cbw->label.foreground, 548 cbw->core.background_pixel); 549 cbw->command.inverse_GC = Get_GC(cbw, cbw->core.background_pixel, 550 cbw->label.foreground); 551 XtReleaseGC(cnew, cbw->label.normal_GC); 552 cbw->label.normal_GC = cbw->command.normal_GC; 553 554 redisplay = True; 555 } 556 557 if (XtIsRealized(cnew) 558 && oldcbw->command.shape_style != cbw->command.shape_style 559 && !ShapeButton(cbw, True)) 560 cbw->command.shape_style = oldcbw->command.shape_style; 561 562 return (redisplay); 563} 564 565static void 566XawCommandGetValuesHook(Widget w, ArgList args, Cardinal *num_args) 567{ 568 CommandWidget cbw = (CommandWidget)w; 569 unsigned int i; 570 571 for (i = 0; i < *num_args; i++) { 572 if (STR_EQUAL(args[i].name, XtNforeground)) 573 *((String*)args[i].value) = cbw->command.set ? 574 (String)cbw->core.background_pixel : (String)cbw->label.foreground; 575 else if (STR_EQUAL(args[i].name, XtNbackground)) 576 *((String*)args[i].value) = cbw->command.set ? 577 (String)cbw->label.foreground : (String)cbw->core.background_pixel; 578 } 579} 580 581static void 582XawCommandClassInitialize(void) 583{ 584 XawInitializeWidgetSet(); 585 XtSetTypeConverter(XtRString, XtRShapeStyle, XmuCvtStringToShapeStyle, 586 NULL, 0, XtCacheNone, NULL); 587 XtSetTypeConverter(XtRShapeStyle, XtRString, XmuCvtShapeStyleToString, 588 NULL, 0, XtCacheNone, NULL); 589} 590 591static Bool 592ShapeButton(CommandWidget cbw, Bool checkRectangular) 593{ 594 Dimension corner_size = 0; 595 596 if (cbw->command.shape_style == XawShapeRoundedRectangle) { 597 corner_size = XtWidth(cbw) < XtHeight(cbw) ? 598 XtWidth(cbw) : XtHeight(cbw); 599 corner_size = (corner_size * cbw->command.corner_round) / 100; 600 } 601 602 if (checkRectangular || cbw->command.shape_style != XawShapeRectangle) { 603 if (!XmuReshapeWidget((Widget)cbw, cbw->command.shape_style, 604 corner_size, corner_size)) { 605 cbw->command.shape_style = XawShapeRectangle; 606 return (False); 607 } 608 } 609 610 return (True); 611} 612 613static void 614XawCommandRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes) 615{ 616 (*commandWidgetClass->core_class.superclass->core_class.realize) 617 (w, valueMask, attributes); 618 619 ShapeButton((CommandWidget)w, False); 620} 621 622static void 623XawCommandResize(Widget w) 624{ 625 if (XtIsRealized(w)) 626 ShapeButton((CommandWidget)w, False); 627 628 (*commandWidgetClass->core_class.superclass->core_class.resize)(w); 629} 630 631static Bool 632ChangeSensitive(Widget w) 633{ 634 CommandWidget cbw = (CommandWidget)w; 635 636 if (XtIsRealized(w)) { 637 if (XtIsSensitive(w)) { 638 if (w->core.border_pixmap != XtUnspecifiedPixmap) 639 XSetWindowBorderPixmap(XtDisplay(w), XtWindow(w), 640 w->core.border_pixmap); 641 else 642 XSetWindowBorder(XtDisplay(w), XtWindow(w), 643 w->core.border_pixel); 644 } 645 else { 646 if (cbw->simple.insensitive_border == None) 647 cbw->simple.insensitive_border = 648 XmuCreateStippledPixmap(XtScreen(w), 649 w->core.border_pixel, 650 cbw->command.set ? 651 cbw->label.foreground : 652 w->core.background_pixel, 653 w->core.depth); 654 XSetWindowBorderPixmap(XtDisplay(w), XtWindow(w), 655 cbw->simple.insensitive_border); 656 } 657 } 658 659 return (False); 660} 661