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