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