Panner.c revision 421c997b
1/* 2 * 3Copyright 1989, 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 * Author: Jim Fulton, MIT X Consortium 26 */ 27 28#ifdef HAVE_CONFIG_H 29#include <config.h> 30#endif 31#include <ctype.h> 32#include <math.h> 33#include <X11/IntrinsicP.h> 34#include <X11/StringDefs.h> 35#include <X11/Xos.h> 36#include <X11/Xmu/CharSet.h> 37#include <X11/Xmu/Drawing.h> 38#include <X11/Xmu/Misc.h> 39#include <X11/Xaw/PannerP.h> 40#include <X11/Xaw/XawInit.h> 41#include "Private.h" 42 43#if defined(ISC) && __STDC__ && !defined(ISC30) 44extern double atof(char *); 45#else 46#include <stdlib.h> /* for atof() */ 47#endif 48 49/* 50 * Class Methods 51 */ 52static void XawPannerDestroy(Widget); 53static void XawPannerInitialize(Widget, Widget, ArgList, Cardinal*); 54static XtGeometryResult XawPannerQueryGeometry(Widget, XtWidgetGeometry*, 55 XtWidgetGeometry*); 56static void XawPannerRealize(Widget, XtValueMask*, XSetWindowAttributes*); 57static void XawPannerRedisplay(Widget, XEvent*, Region); 58static void XawPannerResize(Widget); 59static Boolean XawPannerSetValues(Widget, Widget, Widget, ArgList, Cardinal*); 60static void XawPannerSetValuesAlmost(Widget, Widget, XtWidgetGeometry*, 61 XtWidgetGeometry*); 62 63/* 64 * Prototypes 65 */ 66static void check_knob(PannerWidget, Bool); 67static void get_default_size(PannerWidget, Dimension*, Dimension*); 68static Bool get_event_xy(PannerWidget, XEvent*, int*, int*); 69static void move_shadow(PannerWidget); 70static int parse_page_string(char*, int, int, Bool*); 71static void rescale(PannerWidget); 72static void reset_shadow_gc(PannerWidget); 73static void reset_slider_gc(PannerWidget); 74static void reset_xor_gc(PannerWidget); 75static void scale_knob(PannerWidget, Bool, Bool); 76 77/* 78 * Actions 79 */ 80static void ActionAbort(Widget, XEvent*, String*, Cardinal*); 81static void ActionMove(Widget, XEvent*, String*, Cardinal*); 82static void ActionNotify(Widget, XEvent*, String*, Cardinal*); 83static void ActionPage(Widget, XEvent*, String*, Cardinal*); 84static void ActionSet(Widget, XEvent*, String*, Cardinal*); 85static void ActionStart(Widget, XEvent*, String*, Cardinal*); 86static void ActionStop(Widget, XEvent*, String*, Cardinal*); 87 88/* 89 * From Xmu/Distinct.c 90 */ 91Bool XmuDistinguishablePixels(Display*, Colormap, unsigned long*, int); 92 93/* 94 * Initialization 95 */ 96static char defaultTranslations[] = 97"<Btn1Down>:" "start()\n" 98"<Btn1Motion>:" "move()\n" 99"<Btn1Up>:" "notify() stop()\n" 100"<Btn2Down>:" "abort()\n" 101":<Key>KP_Enter:" "set(rubberband,toggle)\n" 102"<Key>space:" "page(+1p,+1p)\n" 103"<Key>Delete:" "page(-1p,-1p)\n" 104":<Key>KP_Delete:" "page(-1p,-1p)\n" 105"<Key>BackSpace:" "page(-1p,-1p)\n" 106"<Key>Left:" "page(-.5p,+0)\n" 107":<Key>KP_Left:" "page(-.5p,+0)\n" 108"<Key>Right:" "page(+.5p,+0)\n" 109":<Key>KP_Right:" "page(+.5p,+0)\n" 110"<Key>Up:" "page(+0,-.5p)\n" 111":<Key>KP_Up:" "page(+0,-.5p)\n" 112"<Key>Down:" "page(+0,+.5p)\n" 113":<Key>KP_Down:" "page(+0,+.5p)\n" 114"<Key>Home:" "page(0,0)\n" 115":<Key>KP_Home:" "page(0,0)\n" 116; 117 118static XtActionsRec actions[] = { 119 {"start", ActionStart}, /* start tmp graphics */ 120 {"stop", ActionStop}, /* stop tmp graphics */ 121 {"abort", ActionAbort}, /* punt */ 122 {"move", ActionMove}, /* move tmp graphics on Motion event */ 123 {"page", ActionPage}, /* page around usually from keyboard */ 124 {"notify", ActionNotify}, /* callback new position */ 125 {"set", ActionSet}, /* set various parameters */ 126}; 127 128#define offset(field) XtOffsetOf(PannerRec, panner.field) 129static XtResource resources[] = { 130 { 131 XtNallowOff, 132 XtCAllowOff, 133 XtRBoolean, 134 sizeof(Boolean), 135 offset(allow_off), 136 XtRImmediate, 137 (XtPointer)False 138 }, 139 { 140 XtNresize, 141 XtCResize, 142 XtRBoolean, 143 sizeof(Boolean), 144 offset(resize_to_pref), 145 XtRImmediate, 146 (XtPointer)True 147 }, 148 { 149 XtNreportCallback, 150 XtCReportCallback, 151 XtRCallback, 152 sizeof(XtPointer), 153 offset(report_callbacks), 154 XtRCallback, 155 NULL 156 }, 157 { 158 XtNdefaultScale, 159 XtCDefaultScale, 160 XtRDimension, 161 sizeof(Dimension), 162 offset(default_scale), 163 XtRImmediate, 164 (XtPointer)PANNER_DEFAULT_SCALE 165 }, 166 { 167 XtNrubberBand, 168 XtCRubberBand, 169 XtRBoolean, 170 sizeof(Boolean), 171 offset(rubber_band), 172 XtRImmediate, 173 (XtPointer)False 174 }, 175 { 176 XtNforeground, 177 XtCForeground, 178 XtRPixel, 179 sizeof(Pixel), 180 offset(foreground), 181 XtRString, 182 (XtPointer)XtDefaultBackground 183 }, 184 { 185 XtNinternalSpace, 186 XtCInternalSpace, 187 XtRDimension, 188 sizeof(Dimension), 189 offset(internal_border), 190 XtRImmediate, 191 (XtPointer)4 192 }, 193 { 194 XtNlineWidth, 195 XtCLineWidth, 196 XtRDimension, 197 sizeof(Dimension), 198 offset(line_width), 199 XtRImmediate, 200 (XtPointer)0 201 }, 202 { 203 XtNcanvasWidth, 204 XtCCanvasWidth, 205 XtRDimension, 206 sizeof(Dimension), 207 offset(canvas_width), 208 XtRImmediate, 209 (XtPointer)0 210 }, 211 { 212 XtNcanvasHeight, 213 XtCCanvasHeight, 214 XtRDimension, 215 sizeof(Dimension), 216 offset(canvas_height), 217 XtRImmediate, 218 (XtPointer)0 219 }, 220 { 221 XtNsliderX, 222 XtCSliderX, 223 XtRPosition, 224 sizeof(Position), 225 offset(slider_x), 226 XtRImmediate, 227 (XtPointer)0 228 }, 229 { 230 XtNsliderY, 231 XtCSliderY, 232 XtRPosition, 233 sizeof(Position), 234 offset(slider_y), 235 XtRImmediate, 236 (XtPointer)0 237 }, 238 { 239 XtNsliderWidth, 240 XtCSliderWidth, 241 XtRDimension, 242 sizeof(Dimension), 243 offset(slider_width), 244 XtRImmediate, 245 (XtPointer)0 246 }, 247 { 248 XtNsliderHeight, 249 XtCSliderHeight, 250 XtRDimension, 251 sizeof(Dimension), 252 offset(slider_height), 253 XtRImmediate, 254 (XtPointer)0 255 }, 256 { 257 XtNshadowColor, 258 XtCShadowColor, 259 XtRPixel, 260 sizeof(Pixel), 261 offset(shadow_color), 262 XtRString, 263 (XtPointer)XtDefaultForeground 264 }, 265 { 266 XtNshadowThickness, 267 XtCShadowThickness, 268 XtRDimension, 269 sizeof(Dimension), 270 offset(shadow_thickness), 271 XtRImmediate, 272 (XtPointer)2 273 }, 274 { 275 XtNbackgroundStipple, 276 XtCBackgroundStipple, 277 XtRString, 278 sizeof(String), 279 offset(stipple_name), 280 XtRImmediate, 281 NULL 282 }, 283}; 284#undef offset 285 286#define Superclass (&simpleClassRec) 287PannerClassRec pannerClassRec = { 288 /* core */ 289 { 290 (WidgetClass)Superclass, /* superclass */ 291 "Panner", /* class_name */ 292 sizeof(PannerRec), /* widget_size */ 293 XawInitializeWidgetSet, /* class_initialize */ 294 NULL, /* class_part_initialize */ 295 False, /* class_inited */ 296 XawPannerInitialize, /* initialize */ 297 NULL, /* initialize_hook */ 298 XawPannerRealize, /* realize */ 299 actions, /* actions */ 300 XtNumber(actions), /* num_actions */ 301 resources, /* resources */ 302 XtNumber(resources), /* num_resources */ 303 NULLQUARK, /* xrm_class */ 304 True, /* compress_motion */ 305 True, /* compress_exposure */ 306 True, /* compress_enterleave */ 307 False, /* visible_interest */ 308 XawPannerDestroy, /* destroy */ 309 XawPannerResize, /* resize */ 310 XawPannerRedisplay, /* expose */ 311 XawPannerSetValues, /* set_values */ 312 NULL, /* set_values_hook */ 313 XawPannerSetValuesAlmost, /* set_values_almost */ 314 NULL, /* get_values_hook */ 315 NULL, /* accept_focus */ 316 XtVersion, /* version */ 317 NULL, /* callback_private */ 318 defaultTranslations, /* tm_table */ 319 XawPannerQueryGeometry, /* query_geometry */ 320 XtInheritDisplayAccelerator, /* display_accelerator */ 321 NULL, /* extension */ 322 }, 323 /* simple */ 324 { 325 XtInheritChangeSensitive, /* change_sensitive */ 326 }, 327 /* panner */ 328 { 329 NULL, /* extension */ 330 } 331}; 332 333WidgetClass pannerWidgetClass = (WidgetClass) &pannerClassRec; 334 335 336/* 337 * Implementation 338 */ 339static void 340reset_shadow_gc(PannerWidget pw) 341{ 342 XtGCMask valuemask = GCForeground; 343 XGCValues values; 344 unsigned long pixels[3]; 345 346 if (pw->panner.shadow_gc) 347 XtReleaseGC((Widget)pw, pw->panner.shadow_gc); 348 349 pixels[0] = pw->panner.foreground; 350 pixels[1] = pw->core.background_pixel; 351 pixels[2] = pw->panner.shadow_color; 352 353 if (!pw->panner.stipple_name && 354 !XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap, 355 pixels, 3) && 356 XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap, 357 pixels, 2)) { 358 valuemask = GCTile | GCFillStyle; 359 values.fill_style = FillTiled; 360 values.tile = XmuCreateStippledPixmap(XtScreen((Widget)pw), 361 pw->panner.foreground, 362 pw->core.background_pixel, 363 pw->core.depth); 364 } 365 else { 366 if (!pw->panner.line_width && 367 !XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap, 368 pixels, 2)) 369 pw->panner.line_width = 1; 370 valuemask = GCForeground; 371 values.foreground = pw->panner.shadow_color; 372 } 373 if (pw->panner.line_width > 0) { 374 values.line_width = pw->panner.line_width; 375 valuemask |= GCLineWidth; 376 } 377 378 pw->panner.shadow_gc = XtGetGC((Widget)pw, valuemask, &values); 379} 380 381static void 382reset_slider_gc(PannerWidget pw) 383{ 384 XtGCMask valuemask = GCForeground; 385 XGCValues values; 386 387 if (pw->panner.slider_gc) 388 XtReleaseGC((Widget)pw, pw->panner.slider_gc); 389 390 values.foreground = pw->panner.foreground; 391 392 pw->panner.slider_gc = XtGetGC((Widget)pw, valuemask, &values); 393} 394 395static void 396reset_xor_gc(PannerWidget pw) 397{ 398 if (pw->panner.xor_gc) 399 XtReleaseGC((Widget)pw, pw->panner.xor_gc); 400 401 if (pw->panner.rubber_band) { 402 XtGCMask valuemask = (GCForeground | GCFunction); 403 XGCValues values; 404 Pixel tmp; 405 406 tmp = (pw->panner.foreground == pw->core.background_pixel ? 407 pw->panner.shadow_color : pw->panner.foreground); 408 values.foreground = tmp ^ pw->core.background_pixel; 409 values.function = GXxor; 410 if (pw->panner.line_width > 0) { 411 valuemask |= GCLineWidth; 412 values.line_width = pw->panner.line_width; 413 } 414 pw->panner.xor_gc = XtGetGC((Widget)pw, valuemask, &values); 415 } 416 else 417 pw->panner.xor_gc = NULL; 418} 419 420static void 421check_knob(PannerWidget pw, Bool knob) 422{ 423 Position pad = pw->panner.internal_border << 1; 424 Position maxx = (Position)XtWidth(pw) - pad - 425 (Position)pw->panner.knob_width; 426 Position maxy = (Position)XtHeight(pw) - pad - 427 (Position)pw->panner.knob_height; 428 Position *x = knob ? &pw->panner.knob_x : &pw->panner.tmp.x; 429 Position *y = knob ? &pw->panner.knob_y : &pw->panner.tmp.y; 430 431 /* 432 * note that positions are already normalized (i.e. internal_border 433 * has been subtracted out) 434 */ 435 if (*x < 0) 436 *x = 0; 437 if (*x > maxx) 438 *x = maxx; 439 440 if (*y < 0) 441 *y = 0; 442 if (*y > maxy) 443 *y = maxy; 444 445 if (knob) { 446 pw->panner.slider_x = (Position)((double)pw->panner.knob_x 447 / pw->panner.haspect + 0.5); 448 pw->panner.slider_y = (Position)((double)pw->panner.knob_y 449 / pw->panner.vaspect + 0.5); 450 pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE; 451 } 452} 453 454static void 455move_shadow(PannerWidget pw) 456{ 457 if (pw->panner.shadow_thickness > 0) { 458 int lw = pw->panner.shadow_thickness + (pw->panner.line_width << 1); 459 int pad = pw->panner.internal_border; 460 461 if (pw->panner.knob_height > lw && pw->panner.knob_width > lw) { 462 XRectangle *r = pw->panner.shadow_rects; 463 464 r->x = pw->panner.knob_x + pad + pw->panner.knob_width; 465 r->y = pw->panner.knob_y + pad + lw; 466 r->width = pw->panner.shadow_thickness; 467 r->height = pw->panner.knob_height - lw; 468 r++; 469 r->x = pw->panner.knob_x + pad + lw; 470 r->y = pw->panner.knob_y + pad + pw->panner.knob_height; 471 r->width = pw->panner.knob_width - lw + pw->panner.shadow_thickness; 472 r->height = pw->panner.shadow_thickness; 473 pw->panner.shadow_valid = True; 474 return; 475 } 476 } 477 pw->panner.shadow_valid = False; 478} 479 480static void 481scale_knob(PannerWidget pw, Bool location, Bool size) 482{ 483 if (location) { 484 pw->panner.knob_x = (Position)PANNER_HSCALE(pw, pw->panner.slider_x); 485 pw->panner.knob_y = (Position)PANNER_VSCALE(pw, pw->panner.slider_y); 486 } 487 if (size) { 488 Dimension width, height; 489 490 if (pw->panner.slider_width < 1) 491 pw->panner.slider_width = pw->panner.canvas_width; 492 if (pw->panner.slider_height < 1) 493 pw->panner.slider_height = pw->panner.canvas_height; 494 width = Min(pw->panner.slider_width, pw->panner.canvas_width); 495 height = Min(pw->panner.slider_height, pw->panner.canvas_height); 496 497 pw->panner.knob_width = (Dimension)PANNER_HSCALE(pw, width); 498 pw->panner.knob_height = (Dimension)PANNER_VSCALE(pw, height); 499 } 500 if (!pw->panner.allow_off) 501 check_knob(pw, True); 502 move_shadow(pw); 503} 504 505static void 506rescale(PannerWidget pw) 507{ 508 int hpad = pw->panner.internal_border << 1; 509 int vpad = hpad; 510 511 if (pw->panner.canvas_width < 1) 512 pw->panner.canvas_width = XtWidth(pw); 513 if (pw->panner.canvas_height < 1) 514 pw->panner.canvas_height = XtHeight(pw); 515 516 if (XtWidth(pw) <= hpad) 517 hpad = 0; 518 if (XtHeight(pw) <= vpad) 519 vpad = 0; 520 521 pw->panner.haspect = ((double)XtWidth(pw) - hpad + .5) 522 / (double)pw->panner.canvas_width; 523 pw->panner.vaspect = ((double)XtHeight(pw) - vpad + .5) 524 / (double)pw->panner.canvas_height; 525 scale_knob(pw, True, True); 526} 527 528static void 529get_default_size(PannerWidget pw, Dimension *wp, Dimension *hp) 530{ 531 Dimension pad = pw->panner.internal_border << 1; 532 533 *wp = PANNER_DSCALE(pw, pw->panner.canvas_width) + pad; 534 *hp = PANNER_DSCALE(pw, pw->panner.canvas_height) + pad; 535} 536 537static Bool 538get_event_xy(PannerWidget pw, XEvent *event, int *x, int *y) 539{ 540 int pad = pw->panner.internal_border; 541 542 switch (event->type) { 543 case ButtonPress: 544 case ButtonRelease: 545 *x = event->xbutton.x - pad; 546 *y = event->xbutton.y - pad; 547 return (True); 548 case KeyPress: 549 case KeyRelease: 550 *x = event->xkey.x - pad; 551 *y = event->xkey.y - pad; 552 return (True); 553 case EnterNotify: 554 case LeaveNotify: 555 *x = event->xcrossing.x - pad; 556 *y = event->xcrossing.y - pad; 557 return (True); 558 case MotionNotify: 559 *x = event->xmotion.x - pad; 560 *y = event->xmotion.y - pad; 561 return (True); 562 } 563 564 return (False); 565} 566 567static int 568parse_page_string(char *s, int pagesize, int canvassize, Bool *relative) 569{ 570 char *cp; 571 double val = 1.0; 572 Bool rel = False; 573 574 /* 575 * syntax: spaces [+-] number spaces [pc\0] spaces 576 */ 577 for (; isascii(*s) && isspace(*s); s++) /* skip white space */ 578 ; 579 580 if (*s == '+' || *s == '-') { /* deal with signs */ 581 rel = True; 582 if (*s == '-') 583 val = -1.0; 584 s++; 585 } 586 if (!*s) { /* if null then return nothing */ 587 *relative = True; 588 return (0); 589 } 590 591 /* skip over numbers */ 592 for (cp = s; isascii(*s) && (isdigit(*s) || *s == '.'); s++) 593 ; 594 val *= atof(cp); 595 596 /* skip blanks */ 597 for (; isascii(*s) && isspace(*s); s++) 598 ; 599 600 if (*s) { /* if units */ 601 switch (s[0]) { 602 case 'p': 603 case 'P': 604 val *= (double)pagesize; 605 break; 606 case 'c': 607 case 'C': 608 val *= (double)canvassize; 609 break; 610 } 611 } 612 *relative = rel; 613 614 return ((int)val); 615} 616 617#define DRAW_TMP(pw) \ 618{ \ 619 XDrawRectangle(XtDisplay(pw), XtWindow(pw), \ 620 pw->panner.xor_gc, \ 621 pw->panner.tmp.x + pw->panner.internal_border, \ 622 pw->panner.tmp.y + pw->panner.internal_border, \ 623 pw->panner.knob_width - 1, \ 624 pw->panner.knob_height - 1); \ 625 pw->panner.tmp.showing = !pw->panner.tmp.showing; \ 626} 627 628#define UNDRAW_TMP(pw) \ 629{ \ 630 if (pw->panner.tmp.showing) \ 631 DRAW_TMP(pw); \ 632} 633 634#define BACKGROUND_STIPPLE(pw) \ 635XmuLocatePixmapFile(pw->core.screen, pw->panner.stipple_name, \ 636 pw->panner.shadow_color, pw->core.background_pixel, \ 637 pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL) 638 639#define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap) 640 641/*ARGSUSED*/ 642static void 643XawPannerInitialize(Widget greq, Widget gnew, ArgList args, Cardinal *num_args) 644{ 645 PannerWidget req = (PannerWidget)greq, cnew = (PannerWidget)gnew; 646 Dimension defwidth, defheight; 647 648 if (req->panner.canvas_width < 1) 649 cnew->panner.canvas_width = 1; 650 if (req->panner.canvas_height < 1) 651 cnew->panner.canvas_height = 1; 652 if (req->panner.default_scale < 1) 653 cnew->panner.default_scale = PANNER_DEFAULT_SCALE; 654 655 get_default_size(req, &defwidth, &defheight); 656 if (XtWidth(req) < 1) 657 XtWidth(cnew) = defwidth; 658 if (XtHeight(req) < 1) 659 XtHeight(cnew) = defheight; 660 661 cnew->panner.shadow_gc = NULL; 662 reset_shadow_gc(cnew); /* shadowColor */ 663 cnew->panner.slider_gc = NULL; 664 reset_slider_gc(cnew); /* foreground */ 665 cnew->panner.xor_gc = NULL; 666 reset_xor_gc(cnew); /* foreground ^ background */ 667 668 rescale(cnew); /* does a position check */ 669 cnew->panner.shadow_valid = False; 670 cnew->panner.tmp.doing = False; 671 cnew->panner.tmp.showing = False; 672 } 673 674static void 675XawPannerRealize(Widget gw, XtValueMask *valuemaskp, 676 XSetWindowAttributes *attr) 677{ 678 PannerWidget pw = (PannerWidget)gw; 679 Pixmap pm = XtUnspecifiedPixmap; 680 Bool gotpm = False; 681 682 if (pw->core.background_pixmap == XtUnspecifiedPixmap) { 683 if (pw->panner.stipple_name) 684 pm = BACKGROUND_STIPPLE(pw); 685 686 if (PIXMAP_OKAY(pm)) { 687 attr->background_pixmap = pm; 688 *valuemaskp |= CWBackPixmap; 689 *valuemaskp &= ~CWBackPixel; 690 gotpm = True; 691 } 692 } 693 (*pannerWidgetClass->core_class.superclass->core_class.realize) 694 (gw, valuemaskp, attr); 695 696 if (gotpm) 697 XFreePixmap(XtDisplay(gw), pm); 698} 699 700static void 701XawPannerDestroy(Widget gw) 702{ 703 PannerWidget pw = (PannerWidget)gw; 704 705 XtReleaseGC(gw, pw->panner.shadow_gc); 706 XtReleaseGC(gw, pw->panner.slider_gc); 707 XtReleaseGC(gw, pw->panner.xor_gc); 708} 709 710static void 711XawPannerResize(Widget gw) 712{ 713 rescale((PannerWidget)gw); 714} 715 716static void 717XawPannerRedisplay(Widget gw, XEvent *event, Region region) 718{ 719 PannerWidget pw = (PannerWidget)gw; 720 Display *dpy = XtDisplay(gw); 721 Window w = XtWindow(gw); 722 int pad = pw->panner.internal_border; 723 Dimension lw = pw->panner.line_width; 724 Dimension extra = pw->panner.shadow_thickness + (lw << 1); 725 int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad; 726 727 if (Superclass->core_class.expose) 728 (Superclass->core_class.expose)(gw, event, region); 729 730 pw->panner.tmp.showing = False; 731 XClearArea(XtDisplay(pw), XtWindow(pw), 732 (int)pw->panner.last_x - ((int)lw) + pad, 733 (int)pw->panner.last_y - ((int)lw) + pad, 734 pw->panner.knob_width + extra, 735 pw->panner.knob_height + extra, 736 False); 737 pw->panner.last_x = pw->panner.knob_x; 738 pw->panner.last_y = pw->panner.knob_y; 739 740 XFillRectangle(dpy, w, pw->panner.slider_gc, kx, ky, 741 pw->panner.knob_width - 1, pw->panner.knob_height - 1); 742 743 if (lw) 744 XDrawRectangle(dpy, w, pw->panner.shadow_gc, kx, ky, 745 pw->panner.knob_width - 1, pw->panner.knob_height - 1); 746 747 if (pw->panner.shadow_valid) 748 XFillRectangles(dpy, w, pw->panner.shadow_gc, pw->panner.shadow_rects, 2); 749 750 if (pw->panner.tmp.doing && pw->panner.rubber_band) 751 DRAW_TMP(pw); 752} 753 754/*ARGSUSED*/ 755static Boolean 756XawPannerSetValues(Widget gcur, Widget greq, Widget gnew, 757 ArgList args, Cardinal *num_args) 758{ 759 PannerWidget cur = (PannerWidget)gcur; 760 PannerWidget cnew = (PannerWidget)gnew; 761 Bool redisplay = False; 762 763 if (cur->panner.foreground != cnew->panner.foreground) { 764 reset_slider_gc(cnew); 765 if (cur->panner.foreground != cur->core.background_pixel) 766 reset_xor_gc(cnew); 767 redisplay = True; 768 } 769 else if (cur->panner.line_width != cnew->panner.line_width || 770 cur->core.background_pixel != cnew->core.background_pixel) { 771 reset_xor_gc(cnew); 772 redisplay = True; 773 } 774 if (cur->panner.shadow_color != cnew->panner.shadow_color) { 775 reset_shadow_gc(cnew); 776 if (cur->panner.foreground == cur->core.background_pixel) 777 reset_xor_gc(cnew); 778 redisplay = True; 779 } 780 if (cur->panner.shadow_thickness != cnew->panner.shadow_thickness) { 781 move_shadow(cnew); 782 redisplay = True; 783 } 784 if (cur->panner.rubber_band != cnew->panner.rubber_band) { 785 reset_xor_gc(cnew); 786 if (cnew->panner.tmp.doing) 787 redisplay = True; 788 } 789 790 if ((cur->panner.stipple_name != cnew->panner.stipple_name 791 || cur->panner.shadow_color != cnew->panner.shadow_color 792 || cur->core.background_pixel != cnew->core.background_pixel) 793 && XtIsRealized(gnew)) { 794 Pixmap pm = cnew->panner.stipple_name ? 795 BACKGROUND_STIPPLE(cnew) : XtUnspecifiedPixmap; 796 797 if (PIXMAP_OKAY(pm)) { 798 XSetWindowBackgroundPixmap(XtDisplay(cnew), XtWindow(cnew), pm); 799 XFreePixmap(XtDisplay(cnew), pm); 800 } 801 else 802 XSetWindowBackground(XtDisplay(cnew), XtWindow(cnew), 803 cnew->core.background_pixel); 804 805 redisplay = True; 806 } 807 808 if (cnew->panner.resize_to_pref && 809 (cur->panner.canvas_width != cnew->panner.canvas_width 810 || cur->panner.canvas_height != cnew->panner.canvas_height 811 || cur->panner.resize_to_pref != cnew->panner.resize_to_pref)) { 812 get_default_size(cnew, &cnew->core.width, &cnew->core.height); 813 redisplay = True; 814 } 815 else if (cur->panner.canvas_width != cnew->panner.canvas_width 816 || cur->panner.canvas_height != cnew->panner.canvas_height 817 || cur->panner.internal_border != cnew->panner.internal_border) { 818 rescale(cnew); /* does a scale_knob as well */ 819 redisplay = True; 820 } 821 else { 822 Bool loc = cur->panner.slider_x != cnew->panner.slider_x || 823 cur->panner.slider_y != cnew->panner.slider_y; 824 Bool siz = cur->panner.slider_width != cnew->panner.slider_width || 825 cur->panner.slider_height != cnew->panner.slider_height; 826 if (loc || siz || (cur->panner.allow_off != cnew->panner.allow_off 827 && cnew->panner.allow_off)) { 828 scale_knob(cnew, loc, siz); 829 redisplay = True; 830 } 831 } 832 833 return (redisplay); 834} 835 836static void 837XawPannerSetValuesAlmost(Widget gold, Widget gnew, XtWidgetGeometry *req, 838 XtWidgetGeometry *reply) 839{ 840 if (reply->request_mode == 0) /* got turned down, so cope */ 841 XawPannerResize(gnew); 842 843 (*pannerWidgetClass->core_class.superclass->core_class.set_values_almost) 844 (gold, gnew, req, reply); 845} 846 847static XtGeometryResult 848XawPannerQueryGeometry(Widget gw, XtWidgetGeometry *intended, 849 XtWidgetGeometry *pref) 850{ 851 PannerWidget pw = (PannerWidget)gw; 852 853 pref->request_mode = (CWWidth | CWHeight); 854 get_default_size(pw, &pref->width, &pref->height); 855 856 if (((intended->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight)) 857 && intended->width == pref->width && intended->height == pref->height) 858 return (XtGeometryYes); 859 else if (pref->width == XtWidth(pw) && pref->height == XtHeight(pw)) 860 return (XtGeometryNo); 861 862 return (XtGeometryAlmost); 863} 864 865 866/*ARGSUSED*/ 867static void 868ActionStart(Widget gw, XEvent *event, String *params, Cardinal *num_params) 869{ 870 PannerWidget pw = (PannerWidget)gw; 871 int x, y; 872 873 if (!get_event_xy(pw, event, &x, &y)) { 874 XBell(XtDisplay(gw), 0); 875 return; 876 } 877 878 pw->panner.tmp.doing = True; 879 pw->panner.tmp.startx = pw->panner.knob_x; 880 pw->panner.tmp.starty = pw->panner.knob_y; 881 pw->panner.tmp.dx = x - pw->panner.knob_x; 882 pw->panner.tmp.dy = y - pw->panner.knob_y; 883 pw->panner.tmp.x = pw->panner.knob_x; 884 pw->panner.tmp.y = pw->panner.knob_y; 885 if (pw->panner.rubber_band) 886 DRAW_TMP(pw); 887} 888 889/*ARGSUSED*/ 890static void 891ActionStop(Widget gw, XEvent *event, String *params, Cardinal *num_params) 892{ 893 PannerWidget pw = (PannerWidget)gw; 894 int x, y; 895 896 if (get_event_xy(pw, event, &x, &y)) { 897 pw->panner.tmp.x = x - pw->panner.tmp.dx; 898 pw->panner.tmp.y = y - pw->panner.tmp.dy; 899 if (!pw->panner.allow_off) 900 check_knob(pw, False); 901 } 902 if (pw->panner.rubber_band) 903 DRAW_TMP(pw); 904 pw->panner.tmp.doing = False; 905} 906 907static void 908ActionAbort(Widget gw, XEvent *event, String *params, Cardinal *num_params) 909{ 910 PannerWidget pw = (PannerWidget)gw; 911 912 if (!pw->panner.tmp.doing) 913 return; 914 915 if (pw->panner.rubber_band) 916 UNDRAW_TMP(pw); 917 918 if (!pw->panner.rubber_band) { /* restore old position */ 919 pw->panner.tmp.x = pw->panner.tmp.startx; 920 pw->panner.tmp.y = pw->panner.tmp.starty; 921 ActionNotify(gw, event, params, num_params); 922 } 923 pw->panner.tmp.doing = False; 924} 925 926static void 927ActionMove(Widget gw, XEvent *event, String *params, Cardinal *num_params) 928{ 929 PannerWidget pw = (PannerWidget)gw; 930 int x, y; 931 932 if (!pw->panner.tmp.doing) 933 return; 934 935 if (!get_event_xy(pw, event, &x, &y)) { 936 XBell(XtDisplay(gw), 0); /* should do error message */ 937 return; 938 } 939 940 if (pw->panner.rubber_band) 941 UNDRAW_TMP(pw); 942 pw->panner.tmp.x = x - pw->panner.tmp.dx; 943 pw->panner.tmp.y = y - pw->panner.tmp.dy; 944 945 if (!pw->panner.rubber_band) 946 ActionNotify(gw, event, params, num_params); 947 else { 948 if (!pw->panner.allow_off) 949 check_knob(pw, False); 950 DRAW_TMP(pw); 951 } 952} 953 954 955static void 956ActionPage(Widget gw, XEvent *event, String *params, Cardinal *num_params) 957{ 958 PannerWidget pw = (PannerWidget)gw; 959 Cardinal zero = 0; 960 Bool isin = pw->panner.tmp.doing; 961 int x, y; 962 Bool relx, rely; 963 int pad = pw->panner.internal_border << 1; 964 965 if (*num_params != 2) { 966 XBell(XtDisplay(gw), 0); 967 return; 968 } 969 970 x = parse_page_string(params[0], pw->panner.knob_width, 971 (int)XtWidth(pw) - pad, &relx); 972 y = parse_page_string(params[1], pw->panner.knob_height, 973 (int)XtHeight(pw) - pad, &rely); 974 975 if (relx) 976 x += pw->panner.knob_x; 977 if (rely) 978 y += pw->panner.knob_y; 979 980 if (isin) { /* if in, then use move */ 981 XEvent ev; 982 983 ev.xbutton.type = ButtonPress; 984 ev.xbutton.x = x; 985 ev.xbutton.y = y; 986 ActionMove(gw, &ev, NULL, &zero); 987 } 988 else { 989 pw->panner.tmp.doing = True; 990 pw->panner.tmp.x = x; 991 pw->panner.tmp.y = y; 992 ActionNotify(gw, event, NULL, &zero); 993 pw->panner.tmp.doing = False; 994 } 995} 996 997/*ARGSUSED*/ 998static void 999ActionNotify(Widget gw, XEvent *event, String *params, Cardinal *num_params) 1000{ 1001 PannerWidget pw = (PannerWidget)gw; 1002 1003 if (!pw->panner.tmp.doing) 1004 return; 1005 1006 if (!pw->panner.allow_off) 1007 check_knob(pw, False); 1008 pw->panner.knob_x = pw->panner.tmp.x; 1009 pw->panner.knob_y = pw->panner.tmp.y; 1010 move_shadow(pw); 1011 1012 pw->panner.slider_x = (Position)((double)pw->panner.knob_x 1013 / pw->panner.haspect + 0.5); 1014 pw->panner.slider_y = (Position)((double) pw->panner.knob_y 1015 / pw->panner.vaspect + 0.5); 1016 if (!pw->panner.allow_off) { 1017 Position tmp; 1018 1019 if (pw->panner.slider_x 1020 > (tmp = (Position)pw->panner.canvas_width - 1021 (Position)pw->panner.slider_width)) 1022 pw->panner.slider_x = tmp; 1023 if (pw->panner.slider_x < 0) 1024 pw->panner.slider_x = 0; 1025 if (pw->panner.slider_y 1026 > (tmp = (Position)pw->panner.canvas_height - 1027 (Position)pw->panner.slider_height)) 1028 pw->panner.slider_y = tmp; 1029 if (pw->panner.slider_y < 0) 1030 pw->panner.slider_y = 0; 1031 } 1032 1033 if (pw->panner.last_x != pw->panner.knob_x || 1034 pw->panner.last_y != pw->panner.knob_y) { 1035 XawPannerReport rep; 1036 1037 XawPannerRedisplay(gw, NULL, NULL); 1038 rep.changed = XawPRSliderX | XawPRSliderY; 1039 rep.slider_x = pw->panner.slider_x; 1040 rep.slider_y = pw->panner.slider_y; 1041 rep.slider_width = pw->panner.slider_width; 1042 rep.slider_height = pw->panner.slider_height; 1043 rep.canvas_width = pw->panner.canvas_width; 1044 rep.canvas_height = pw->panner.canvas_height; 1045 XtCallCallbackList(gw, pw->panner.report_callbacks, (XtPointer)&rep); 1046 } 1047} 1048 1049/*ARGSUSED*/ 1050static void 1051ActionSet(Widget gw, XEvent *event, String *params, Cardinal *num_params) 1052{ 1053 PannerWidget pw = (PannerWidget)gw; 1054 Bool rb; 1055 1056 if (*num_params < 2 || 1057 XmuCompareISOLatin1(params[0], "rubberband") != 0) { 1058 XBell(XtDisplay(gw), 0); 1059 return; 1060 } 1061 1062 if (XmuCompareISOLatin1(params[1], "on") == 0) 1063 rb = True; 1064 else if (XmuCompareISOLatin1(params[1], "off") == 0) 1065 rb = False; 1066 else if (XmuCompareISOLatin1(params[1], "toggle") == 0) 1067 rb = !pw->panner.rubber_band; 1068 else { 1069 XBell(XtDisplay(gw), 0); 1070 return; 1071 } 1072 1073 if (rb != pw->panner.rubber_band) { 1074 Arg args[1]; 1075 1076 XtSetArg(args[0], XtNrubberBand, rb); 1077 XtSetValues(gw, args, 1); 1078 } 1079} 1080