Panner.c revision 5b16253f
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(String, 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#ifndef OLDXAW 327 NULL, 328#endif 329 }, 330 /* panner */ 331 { 332 NULL, /* extension */ 333 } 334}; 335 336WidgetClass pannerWidgetClass = (WidgetClass) &pannerClassRec; 337 338 339/* 340 * Implementation 341 */ 342static void 343reset_shadow_gc(PannerWidget pw) 344{ 345 XtGCMask valuemask = GCForeground; 346 XGCValues values; 347 unsigned long pixels[3]; 348 349 if (pw->panner.shadow_gc) 350 XtReleaseGC((Widget)pw, pw->panner.shadow_gc); 351 352 pixels[0] = pw->panner.foreground; 353 pixels[1] = pw->core.background_pixel; 354 pixels[2] = pw->panner.shadow_color; 355 356 if (!pw->panner.stipple_name && 357 !XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap, 358 pixels, 3) && 359 XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap, 360 pixels, 2)) { 361 valuemask = GCTile | GCFillStyle; 362 values.fill_style = FillTiled; 363 values.tile = XmuCreateStippledPixmap(XtScreen((Widget)pw), 364 pw->panner.foreground, 365 pw->core.background_pixel, 366 pw->core.depth); 367 } 368 else { 369 if (!pw->panner.line_width && 370 !XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap, 371 pixels, 2)) 372 pw->panner.line_width = 1; 373 valuemask = GCForeground; 374 values.foreground = pw->panner.shadow_color; 375 } 376 if (pw->panner.line_width > 0) { 377 values.line_width = pw->panner.line_width; 378 valuemask |= GCLineWidth; 379 } 380 381 pw->panner.shadow_gc = XtGetGC((Widget)pw, valuemask, &values); 382} 383 384static void 385reset_slider_gc(PannerWidget pw) 386{ 387 XtGCMask valuemask = GCForeground; 388 XGCValues values; 389 390 if (pw->panner.slider_gc) 391 XtReleaseGC((Widget)pw, pw->panner.slider_gc); 392 393 values.foreground = pw->panner.foreground; 394 395 pw->panner.slider_gc = XtGetGC((Widget)pw, valuemask, &values); 396} 397 398static void 399reset_xor_gc(PannerWidget pw) 400{ 401 if (pw->panner.xor_gc) 402 XtReleaseGC((Widget)pw, pw->panner.xor_gc); 403 404 if (pw->panner.rubber_band) { 405 XtGCMask valuemask = (GCForeground | GCFunction); 406 XGCValues values; 407 Pixel tmp; 408 409 tmp = (pw->panner.foreground == pw->core.background_pixel ? 410 pw->panner.shadow_color : pw->panner.foreground); 411 values.foreground = tmp ^ pw->core.background_pixel; 412 values.function = GXxor; 413 if (pw->panner.line_width > 0) { 414 valuemask |= GCLineWidth; 415 values.line_width = pw->panner.line_width; 416 } 417 pw->panner.xor_gc = XtGetGC((Widget)pw, valuemask, &values); 418 } 419 else 420 pw->panner.xor_gc = NULL; 421} 422 423static void 424check_knob(PannerWidget pw, Bool knob) 425{ 426 Position pad = (Position)(pw->panner.internal_border << 1); 427 Position maxx = (Position)(XtWidth(pw) - pad - pw->panner.knob_width); 428 Position maxy = (Position)(XtHeight(pw) - pad - pw->panner.knob_height); 429 Position *x = knob ? &pw->panner.knob_x : &pw->panner.tmp.x; 430 Position *y = knob ? &pw->panner.knob_y : &pw->panner.tmp.y; 431 432 /* 433 * note that positions are already normalized (i.e. internal_border 434 * has been subtracted out) 435 */ 436 if (*x < 0) 437 *x = 0; 438 if (*x > maxx) 439 *x = maxx; 440 441 if (*y < 0) 442 *y = 0; 443 if (*y > maxy) 444 *y = maxy; 445 446 if (knob) { 447 pw->panner.slider_x = (Position)((double)pw->panner.knob_x 448 / pw->panner.haspect + 0.5); 449 pw->panner.slider_y = (Position)((double)pw->panner.knob_y 450 / pw->panner.vaspect + 0.5); 451 pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE; 452 } 453} 454 455static void 456move_shadow(PannerWidget pw) 457{ 458 if (pw->panner.shadow_thickness > 0) { 459 int lw = pw->panner.shadow_thickness + (pw->panner.line_width << 1); 460 int pad = pw->panner.internal_border; 461 462 if (pw->panner.knob_height > lw && pw->panner.knob_width > lw) { 463 XRectangle *r = pw->panner.shadow_rects; 464 465 r->x = (short)(pw->panner.knob_x + pad + pw->panner.knob_width); 466 r->y = (short)(pw->panner.knob_y + pad + lw); 467 r->width = (unsigned short)(pw->panner.shadow_thickness); 468 r->height = (unsigned short)(pw->panner.knob_height - lw); 469 r++; 470 r->x = (short)(pw->panner.knob_x + pad + lw); 471 r->y = (short)(pw->panner.knob_y + pad + pw->panner.knob_height); 472 r->width = (unsigned short)(pw->panner.knob_width - lw + pw->panner.shadow_thickness); 473 r->height = (unsigned short)(pw->panner.shadow_thickness); 474 pw->panner.shadow_valid = True; 475 return; 476 } 477 } 478 pw->panner.shadow_valid = False; 479} 480 481static void 482scale_knob(PannerWidget pw, Bool location, Bool size) 483{ 484 if (location) { 485 pw->panner.knob_x = (Position)PANNER_HSCALE(pw, pw->panner.slider_x); 486 pw->panner.knob_y = (Position)PANNER_VSCALE(pw, pw->panner.slider_y); 487 } 488 if (size) { 489 Dimension width, height; 490 491 if (pw->panner.slider_width < 1) 492 pw->panner.slider_width = pw->panner.canvas_width; 493 if (pw->panner.slider_height < 1) 494 pw->panner.slider_height = pw->panner.canvas_height; 495 width = Min(pw->panner.slider_width, pw->panner.canvas_width); 496 height = Min(pw->panner.slider_height, pw->panner.canvas_height); 497 498 pw->panner.knob_width = (Dimension)PANNER_HSCALE(pw, width); 499 pw->panner.knob_height = (Dimension)PANNER_VSCALE(pw, height); 500 } 501 if (!pw->panner.allow_off) 502 check_knob(pw, True); 503 move_shadow(pw); 504} 505 506static void 507rescale(PannerWidget pw) 508{ 509 int hpad = pw->panner.internal_border << 1; 510 int vpad = hpad; 511 512 if (pw->panner.canvas_width < 1) 513 pw->panner.canvas_width = XtWidth(pw); 514 if (pw->panner.canvas_height < 1) 515 pw->panner.canvas_height = XtHeight(pw); 516 517 if (XtWidth(pw) <= hpad) 518 hpad = 0; 519 if (XtHeight(pw) <= vpad) 520 vpad = 0; 521 522 pw->panner.haspect = ((double)XtWidth(pw) - hpad + .5) 523 / (double)pw->panner.canvas_width; 524 pw->panner.vaspect = ((double)XtHeight(pw) - vpad + .5) 525 / (double)pw->panner.canvas_height; 526 scale_knob(pw, True, True); 527} 528 529static void 530get_default_size(PannerWidget pw, Dimension *wp, Dimension *hp) 531{ 532 Dimension pad = (Dimension)(pw->panner.internal_border << 1); 533 534 *wp = (Dimension)(PANNER_DSCALE(pw, pw->panner.canvas_width) + pad); 535 *hp = (Dimension)(PANNER_DSCALE(pw, pw->panner.canvas_height) + pad); 536} 537 538static Bool 539get_event_xy(PannerWidget pw, XEvent *event, int *x, int *y) 540{ 541 int pad = pw->panner.internal_border; 542 543 switch (event->type) { 544 case ButtonPress: 545 case ButtonRelease: 546 *x = event->xbutton.x - pad; 547 *y = event->xbutton.y - pad; 548 return (True); 549 case KeyPress: 550 case KeyRelease: 551 *x = event->xkey.x - pad; 552 *y = event->xkey.y - pad; 553 return (True); 554 case EnterNotify: 555 case LeaveNotify: 556 *x = event->xcrossing.x - pad; 557 *y = event->xcrossing.y - pad; 558 return (True); 559 case MotionNotify: 560 *x = event->xmotion.x - pad; 561 *y = event->xmotion.y - pad; 562 return (True); 563 } 564 565 return (False); 566} 567 568static int 569parse_page_string(String s, int pagesize, int canvassize, Bool *relative) 570{ 571 String cp; 572 double val = 1.0; 573 Bool rel = False; 574 575 /* 576 * syntax: spaces [+-] number spaces [pc\0] spaces 577 */ 578 for (; isascii((unsigned char)*s) && isspace((unsigned char)*s); s++) /* skip white space */ 579 ; 580 581 if (*s == '+' || *s == '-') { /* deal with signs */ 582 rel = True; 583 if (*s == '-') 584 val = -1.0; 585 s++; 586 } 587 if (!*s) { /* if null then return nothing */ 588 *relative = True; 589 return (0); 590 } 591 592 /* skip over numbers */ 593 for (cp = s; isascii((unsigned char)*s) && (isdigit((unsigned char)*s) || *s == '.'); s++) 594 ; 595 val *= atof(cp); 596 597 /* skip blanks */ 598 for (; isascii((unsigned char)*s) && isspace((unsigned char)*s); s++) 599 ; 600 601 if (*s) { /* if units */ 602 switch (s[0]) { 603 case 'p': 604 case 'P': 605 val *= (double)pagesize; 606 break; 607 case 'c': 608 case 'C': 609 val *= (double)canvassize; 610 break; 611 } 612 } 613 *relative = rel; 614 615 return ((int)val); 616} 617 618#define DRAW_TMP(pw) \ 619{ \ 620 XDrawRectangle(XtDisplay(pw), XtWindow(pw), \ 621 pw->panner.xor_gc, \ 622 (pw->panner.tmp.x + pw->panner.internal_border), \ 623 (pw->panner.tmp.y + pw->panner.internal_border), \ 624 (unsigned)(pw->panner.knob_width - 1), \ 625 (unsigned)(pw->panner.knob_height - 1)); \ 626 pw->panner.tmp.showing = !pw->panner.tmp.showing; \ 627} 628 629#define UNDRAW_TMP(pw) \ 630{ \ 631 if (pw->panner.tmp.showing) \ 632 DRAW_TMP(pw); \ 633} 634 635#define BACKGROUND_STIPPLE(pw) \ 636XmuLocatePixmapFile(pw->core.screen, pw->panner.stipple_name, \ 637 pw->panner.shadow_color, pw->core.background_pixel, \ 638 pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL) 639 640#define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap) 641 642/*ARGSUSED*/ 643static void 644XawPannerInitialize(Widget greq, Widget gnew, ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 645{ 646 PannerWidget req = (PannerWidget)greq, cnew = (PannerWidget)gnew; 647 Dimension defwidth, defheight; 648 649 if (req->panner.canvas_width < 1) 650 cnew->panner.canvas_width = 1; 651 if (req->panner.canvas_height < 1) 652 cnew->panner.canvas_height = 1; 653 if (req->panner.default_scale < 1) 654 cnew->panner.default_scale = PANNER_DEFAULT_SCALE; 655 656 get_default_size(req, &defwidth, &defheight); 657 if (XtWidth(req) < 1) 658 XtWidth(cnew) = defwidth; 659 if (XtHeight(req) < 1) 660 XtHeight(cnew) = defheight; 661 662 cnew->panner.shadow_gc = NULL; 663 reset_shadow_gc(cnew); /* shadowColor */ 664 cnew->panner.slider_gc = NULL; 665 reset_slider_gc(cnew); /* foreground */ 666 cnew->panner.xor_gc = NULL; 667 reset_xor_gc(cnew); /* foreground ^ background */ 668 669 rescale(cnew); /* does a position check */ 670 cnew->panner.shadow_valid = False; 671 cnew->panner.tmp.doing = False; 672 cnew->panner.tmp.showing = False; 673 } 674 675static void 676XawPannerRealize(Widget gw, XtValueMask *valuemaskp, 677 XSetWindowAttributes *attr) 678{ 679 PannerWidget pw = (PannerWidget)gw; 680 Pixmap pm = XtUnspecifiedPixmap; 681 Bool gotpm = False; 682 683 if (pw->core.background_pixmap == XtUnspecifiedPixmap) { 684 if (pw->panner.stipple_name) 685 pm = BACKGROUND_STIPPLE(pw); 686 687 if (PIXMAP_OKAY(pm)) { 688 attr->background_pixmap = pm; 689 *valuemaskp |= CWBackPixmap; 690 *valuemaskp &= (XtValueMask)(~CWBackPixel); 691 gotpm = True; 692 } 693 } 694 (*pannerWidgetClass->core_class.superclass->core_class.realize) 695 (gw, valuemaskp, attr); 696 697 if (gotpm) 698 XFreePixmap(XtDisplay(gw), pm); 699} 700 701static void 702XawPannerDestroy(Widget gw) 703{ 704 PannerWidget pw = (PannerWidget)gw; 705 706 XtReleaseGC(gw, pw->panner.shadow_gc); 707 XtReleaseGC(gw, pw->panner.slider_gc); 708 XtReleaseGC(gw, pw->panner.xor_gc); 709} 710 711static void 712XawPannerResize(Widget gw) 713{ 714 rescale((PannerWidget)gw); 715} 716 717static void 718XawPannerRedisplay(Widget gw, XEvent *event, Region region) 719{ 720 PannerWidget pw = (PannerWidget)gw; 721 Display *dpy = XtDisplay(gw); 722 Window w = XtWindow(gw); 723 int pad = pw->panner.internal_border; 724 Dimension lw = pw->panner.line_width; 725 Dimension extra = (Dimension)(pw->panner.shadow_thickness + (lw << 1)); 726 int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad; 727 728 if (Superclass->core_class.expose) 729 (Superclass->core_class.expose)(gw, event, region); 730 731 pw->panner.tmp.showing = False; 732 XClearArea(XtDisplay(pw), XtWindow(pw), 733 (int)pw->panner.last_x - ((int)lw) + pad, 734 (int)pw->panner.last_y - ((int)lw) + pad, 735 (unsigned)(pw->panner.knob_width + extra), 736 (unsigned)(pw->panner.knob_height + extra), 737 False); 738 pw->panner.last_x = pw->panner.knob_x; 739 pw->panner.last_y = pw->panner.knob_y; 740 741 XFillRectangle(dpy, w, pw->panner.slider_gc, kx, ky, 742 (unsigned)(pw->panner.knob_width - 1), 743 (unsigned)(pw->panner.knob_height - 1)); 744 745 if (lw) 746 XDrawRectangle(dpy, w, pw->panner.shadow_gc, kx, ky, 747 (unsigned)(pw->panner.knob_width - 1), 748 (unsigned)(pw->panner.knob_height - 1)); 749 750 if (pw->panner.shadow_valid) 751 XFillRectangles(dpy, w, pw->panner.shadow_gc, pw->panner.shadow_rects, 2); 752 753 if (pw->panner.tmp.doing && pw->panner.rubber_band) 754 DRAW_TMP(pw); 755} 756 757/*ARGSUSED*/ 758static Boolean 759XawPannerSetValues(Widget gcur, Widget greq _X_UNUSED, Widget gnew, 760 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 761{ 762 PannerWidget cur = (PannerWidget)gcur; 763 PannerWidget cnew = (PannerWidget)gnew; 764 Bool redisplay = False; 765 766 if (cur->panner.foreground != cnew->panner.foreground) { 767 reset_slider_gc(cnew); 768 if (cur->panner.foreground != cur->core.background_pixel) 769 reset_xor_gc(cnew); 770 redisplay = True; 771 } 772 else if (cur->panner.line_width != cnew->panner.line_width || 773 cur->core.background_pixel != cnew->core.background_pixel) { 774 reset_xor_gc(cnew); 775 redisplay = True; 776 } 777 if (cur->panner.shadow_color != cnew->panner.shadow_color) { 778 reset_shadow_gc(cnew); 779 if (cur->panner.foreground == cur->core.background_pixel) 780 reset_xor_gc(cnew); 781 redisplay = True; 782 } 783 if (cur->panner.shadow_thickness != cnew->panner.shadow_thickness) { 784 move_shadow(cnew); 785 redisplay = True; 786 } 787 if (cur->panner.rubber_band != cnew->panner.rubber_band) { 788 reset_xor_gc(cnew); 789 if (cnew->panner.tmp.doing) 790 redisplay = True; 791 } 792 793 if ((cur->panner.stipple_name != cnew->panner.stipple_name 794 || cur->panner.shadow_color != cnew->panner.shadow_color 795 || cur->core.background_pixel != cnew->core.background_pixel) 796 && XtIsRealized(gnew)) { 797 Pixmap pm = cnew->panner.stipple_name ? 798 BACKGROUND_STIPPLE(cnew) : XtUnspecifiedPixmap; 799 800 if (PIXMAP_OKAY(pm)) { 801 XSetWindowBackgroundPixmap(XtDisplay(cnew), XtWindow(cnew), pm); 802 XFreePixmap(XtDisplay(cnew), pm); 803 } 804 else 805 XSetWindowBackground(XtDisplay(cnew), XtWindow(cnew), 806 cnew->core.background_pixel); 807 808 redisplay = True; 809 } 810 811 if (cnew->panner.resize_to_pref && 812 (cur->panner.canvas_width != cnew->panner.canvas_width 813 || cur->panner.canvas_height != cnew->panner.canvas_height 814 || cur->panner.resize_to_pref != cnew->panner.resize_to_pref)) { 815 get_default_size(cnew, &cnew->core.width, &cnew->core.height); 816 redisplay = True; 817 } 818 else if (cur->panner.canvas_width != cnew->panner.canvas_width 819 || cur->panner.canvas_height != cnew->panner.canvas_height 820 || cur->panner.internal_border != cnew->panner.internal_border) { 821 rescale(cnew); /* does a scale_knob as well */ 822 redisplay = True; 823 } 824 else { 825 Bool loc = cur->panner.slider_x != cnew->panner.slider_x || 826 cur->panner.slider_y != cnew->panner.slider_y; 827 Bool siz = cur->panner.slider_width != cnew->panner.slider_width || 828 cur->panner.slider_height != cnew->panner.slider_height; 829 if (loc || siz || (cur->panner.allow_off != cnew->panner.allow_off 830 && cnew->panner.allow_off)) { 831 scale_knob(cnew, loc, siz); 832 redisplay = True; 833 } 834 } 835 836 return (Boolean)(redisplay); 837} 838 839static void 840XawPannerSetValuesAlmost(Widget gold, Widget gnew, XtWidgetGeometry *req, 841 XtWidgetGeometry *reply) 842{ 843 if (reply->request_mode == 0) /* got turned down, so cope */ 844 XawPannerResize(gnew); 845 846 (*pannerWidgetClass->core_class.superclass->core_class.set_values_almost) 847 (gold, gnew, req, reply); 848} 849 850static XtGeometryResult 851XawPannerQueryGeometry(Widget gw, XtWidgetGeometry *intended, 852 XtWidgetGeometry *pref) 853{ 854 PannerWidget pw = (PannerWidget)gw; 855 856 pref->request_mode = (CWWidth | CWHeight); 857 get_default_size(pw, &pref->width, &pref->height); 858 859 if (((intended->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight)) 860 && intended->width == pref->width && intended->height == pref->height) 861 return (XtGeometryYes); 862 else if (pref->width == XtWidth(pw) && pref->height == XtHeight(pw)) 863 return (XtGeometryNo); 864 865 return (XtGeometryAlmost); 866} 867 868 869/*ARGSUSED*/ 870static void 871ActionStart(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 872{ 873 PannerWidget pw = (PannerWidget)gw; 874 int x, y; 875 876 if (!get_event_xy(pw, event, &x, &y)) { 877 XBell(XtDisplay(gw), 0); 878 return; 879 } 880 881 pw->panner.tmp.doing = True; 882 pw->panner.tmp.startx = pw->panner.knob_x; 883 pw->panner.tmp.starty = pw->panner.knob_y; 884 pw->panner.tmp.dx = (Position)(x - pw->panner.knob_x); 885 pw->panner.tmp.dy = (Position)(y - pw->panner.knob_y); 886 pw->panner.tmp.x = pw->panner.knob_x; 887 pw->panner.tmp.y = pw->panner.knob_y; 888 if (pw->panner.rubber_band) 889 DRAW_TMP(pw); 890} 891 892/*ARGSUSED*/ 893static void 894ActionStop(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 895{ 896 PannerWidget pw = (PannerWidget)gw; 897 int x, y; 898 899 if (get_event_xy(pw, event, &x, &y)) { 900 pw->panner.tmp.x = (Position)(x - pw->panner.tmp.dx); 901 pw->panner.tmp.y = (Position)(y - pw->panner.tmp.dy); 902 if (!pw->panner.allow_off) 903 check_knob(pw, False); 904 } 905 if (pw->panner.rubber_band) 906 DRAW_TMP(pw); 907 pw->panner.tmp.doing = False; 908} 909 910static void 911ActionAbort(Widget gw, XEvent *event, String *params, Cardinal *num_params) 912{ 913 PannerWidget pw = (PannerWidget)gw; 914 915 if (!pw->panner.tmp.doing) 916 return; 917 918 if (pw->panner.rubber_band) 919 UNDRAW_TMP(pw); 920 921 if (!pw->panner.rubber_band) { /* restore old position */ 922 pw->panner.tmp.x = pw->panner.tmp.startx; 923 pw->panner.tmp.y = pw->panner.tmp.starty; 924 ActionNotify(gw, event, params, num_params); 925 } 926 pw->panner.tmp.doing = False; 927} 928 929static void 930ActionMove(Widget gw, XEvent *event, String *params, Cardinal *num_params) 931{ 932 PannerWidget pw = (PannerWidget)gw; 933 int x, y; 934 935 if (!pw->panner.tmp.doing) 936 return; 937 938 if (!get_event_xy(pw, event, &x, &y)) { 939 XBell(XtDisplay(gw), 0); /* should do error message */ 940 return; 941 } 942 943 if (pw->panner.rubber_band) 944 UNDRAW_TMP(pw); 945 pw->panner.tmp.x = (Position)(x - pw->panner.tmp.dx); 946 pw->panner.tmp.y = (Position)(y - pw->panner.tmp.dy); 947 948 if (!pw->panner.rubber_band) 949 ActionNotify(gw, event, params, num_params); 950 else { 951 if (!pw->panner.allow_off) 952 check_knob(pw, False); 953 DRAW_TMP(pw); 954 } 955} 956 957 958static void 959ActionPage(Widget gw, XEvent *event, String *params, Cardinal *num_params) 960{ 961 PannerWidget pw = (PannerWidget)gw; 962 Cardinal zero = 0; 963 Bool isin = pw->panner.tmp.doing; 964 int x, y; 965 Bool relx, rely; 966 int pad = pw->panner.internal_border << 1; 967 968 if (*num_params != 2) { 969 XBell(XtDisplay(gw), 0); 970 return; 971 } 972 973 x = parse_page_string(params[0], pw->panner.knob_width, 974 (int)XtWidth(pw) - pad, &relx); 975 y = parse_page_string(params[1], pw->panner.knob_height, 976 (int)XtHeight(pw) - pad, &rely); 977 978 if (relx) 979 x += pw->panner.knob_x; 980 if (rely) 981 y += pw->panner.knob_y; 982 983 if (isin) { /* if in, then use move */ 984 XEvent ev; 985 986 ev.xbutton.type = ButtonPress; 987 ev.xbutton.x = x; 988 ev.xbutton.y = y; 989 ActionMove(gw, &ev, NULL, &zero); 990 } 991 else { 992 pw->panner.tmp.doing = True; 993 pw->panner.tmp.x = (Position)x; 994 pw->panner.tmp.y = (Position)y; 995 ActionNotify(gw, event, NULL, &zero); 996 pw->panner.tmp.doing = False; 997 } 998} 999 1000/*ARGSUSED*/ 1001static void 1002ActionNotify(Widget gw, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 1003{ 1004 PannerWidget pw = (PannerWidget)gw; 1005 1006 if (!pw->panner.tmp.doing) 1007 return; 1008 1009 if (!pw->panner.allow_off) 1010 check_knob(pw, False); 1011 pw->panner.knob_x = pw->panner.tmp.x; 1012 pw->panner.knob_y = pw->panner.tmp.y; 1013 move_shadow(pw); 1014 1015 pw->panner.slider_x = (Position)((double)pw->panner.knob_x 1016 / pw->panner.haspect + 0.5); 1017 pw->panner.slider_y = (Position)((double) pw->panner.knob_y 1018 / pw->panner.vaspect + 0.5); 1019 if (!pw->panner.allow_off) { 1020 Position tmp; 1021 1022 if (pw->panner.slider_x 1023 > (tmp = (Position)(pw->panner.canvas_width - 1024 pw->panner.slider_width))) 1025 pw->panner.slider_x = tmp; 1026 if (pw->panner.slider_x < 0) 1027 pw->panner.slider_x = 0; 1028 if (pw->panner.slider_y 1029 > (tmp = (Position)(pw->panner.canvas_height - 1030 pw->panner.slider_height))) 1031 pw->panner.slider_y = tmp; 1032 if (pw->panner.slider_y < 0) 1033 pw->panner.slider_y = 0; 1034 } 1035 1036 if (pw->panner.last_x != pw->panner.knob_x || 1037 pw->panner.last_y != pw->panner.knob_y) { 1038 XawPannerReport rep; 1039 1040 XawPannerRedisplay(gw, NULL, NULL); 1041 rep.changed = XawPRSliderX | XawPRSliderY; 1042 rep.slider_x = pw->panner.slider_x; 1043 rep.slider_y = pw->panner.slider_y; 1044 rep.slider_width = pw->panner.slider_width; 1045 rep.slider_height = pw->panner.slider_height; 1046 rep.canvas_width = pw->panner.canvas_width; 1047 rep.canvas_height = pw->panner.canvas_height; 1048 XtCallCallbackList(gw, pw->panner.report_callbacks, (XtPointer)&rep); 1049 } 1050} 1051 1052/*ARGSUSED*/ 1053static void 1054ActionSet(Widget gw, XEvent *event _X_UNUSED, String *params, Cardinal *num_params) 1055{ 1056 PannerWidget pw = (PannerWidget)gw; 1057 Bool rb; 1058 1059 if (*num_params < 2 || 1060 XmuCompareISOLatin1(params[0], "rubberband") != 0) { 1061 XBell(XtDisplay(gw), 0); 1062 return; 1063 } 1064 1065 if (XmuCompareISOLatin1(params[1], "on") == 0) 1066 rb = True; 1067 else if (XmuCompareISOLatin1(params[1], "off") == 0) 1068 rb = False; 1069 else if (XmuCompareISOLatin1(params[1], "toggle") == 0) 1070 rb = !pw->panner.rubber_band; 1071 else { 1072 XBell(XtDisplay(gw), 0); 1073 return; 1074 } 1075 1076 if (rb != pw->panner.rubber_band) { 1077 Arg args[1]; 1078 1079 XtSetArg(args[0], XtNrubberBand, rb); 1080 XtSetValues(gw, args, 1); 1081 } 1082} 1083