Panner.c revision 7a84e134
1/* 2 * $Xorg: Panner.c,v 1.4 2001/02/09 02:03:45 xorgcvs Exp $ 3 * 4Copyright 1989, 1994, 1998 The Open Group 5 6Permission to use, copy, modify, distribute, and sell this software and its 7documentation for any purpose is hereby granted without fee, provided that 8the above copyright notice appear in all copies and that both that 9copyright notice and this permission notice appear in supporting 10documentation. 11 12The above copyright notice and this permission notice shall be included in 13all copies or substantial portions of the Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22Except as contained in this notice, the name of The Open Group shall not be 23used in advertising or otherwise to promote the sale, use or other dealings 24in this Software without prior written authorization from The Open Group. 25 * 26 * Author: Jim Fulton, MIT X Consortium 27 */ 28 29/* $XFree86: xc/lib/Xaw/Panner.c,v 3.8 2001/07/25 15:04:49 dawes Exp $ */ 30 31#ifdef HAVE_CONFIG_H 32#include <config.h> 33#endif 34#include <ctype.h> 35#include <math.h> 36#include <X11/IntrinsicP.h> 37#include <X11/StringDefs.h> 38#include <X11/Xos.h> 39#include <X11/Xmu/CharSet.h> 40#include <X11/Xmu/Drawing.h> 41#include <X11/Xmu/Misc.h> 42#include <X11/Xaw/PannerP.h> 43#include <X11/Xaw/XawInit.h> 44#include "Private.h" 45 46#if defined(ISC) && __STDC__ && !defined(ISC30) 47extern double atof(char *); 48#else 49#include <stdlib.h> /* for atof() */ 50#endif 51 52/* 53 * Class Methods 54 */ 55static void XawPannerDestroy(Widget); 56static void XawPannerInitialize(Widget, Widget, ArgList, Cardinal*); 57static XtGeometryResult XawPannerQueryGeometry(Widget, XtWidgetGeometry*, 58 XtWidgetGeometry*); 59static void XawPannerRealize(Widget, XtValueMask*, XSetWindowAttributes*); 60static void XawPannerRedisplay(Widget, XEvent*, Region); 61static void XawPannerResize(Widget); 62static Boolean XawPannerSetValues(Widget, Widget, Widget, ArgList, Cardinal*); 63static void XawPannerSetValuesAlmost(Widget, Widget, XtWidgetGeometry*, 64 XtWidgetGeometry*); 65 66/* 67 * Prototypes 68 */ 69static void check_knob(PannerWidget, Bool); 70static void get_default_size(PannerWidget, Dimension*, Dimension*); 71static Bool get_event_xy(PannerWidget, XEvent*, int*, int*); 72static void move_shadow(PannerWidget); 73static int parse_page_string(char*, int, int, Bool*); 74static void rescale(PannerWidget); 75static void reset_shadow_gc(PannerWidget); 76static void reset_slider_gc(PannerWidget); 77static void reset_xor_gc(PannerWidget); 78static void scale_knob(PannerWidget, Bool, Bool); 79 80/* 81 * Actions 82 */ 83static void ActionAbort(Widget, XEvent*, String*, Cardinal*); 84static void ActionMove(Widget, XEvent*, String*, Cardinal*); 85static void ActionNotify(Widget, XEvent*, String*, Cardinal*); 86static void ActionPage(Widget, XEvent*, String*, Cardinal*); 87static void ActionSet(Widget, XEvent*, String*, Cardinal*); 88static void ActionStart(Widget, XEvent*, String*, Cardinal*); 89static void ActionStop(Widget, XEvent*, String*, Cardinal*); 90 91/* 92 * From Xmu/Distinct.c 93 */ 94Bool XmuDistinguishablePixels(Display*, Colormap, unsigned long*, int); 95 96/* 97 * Initialization 98 */ 99static char defaultTranslations[] = 100"<Btn1Down>:" "start()\n" 101"<Btn1Motion>:" "move()\n" 102"<Btn1Up>:" "notify() stop()\n" 103"<Btn2Down>:" "abort()\n" 104":<Key>KP_Enter:" "set(rubberband,toggle)\n" 105"<Key>space:" "page(+1p,+1p)\n" 106"<Key>Delete:" "page(-1p,-1p)\n" 107":<Key>KP_Delete:" "page(-1p,-1p)\n" 108"<Key>BackSpace:" "page(-1p,-1p)\n" 109"<Key>Left:" "page(-.5p,+0)\n" 110":<Key>KP_Left:" "page(-.5p,+0)\n" 111"<Key>Right:" "page(+.5p,+0)\n" 112":<Key>KP_Right:" "page(+.5p,+0)\n" 113"<Key>Up:" "page(+0,-.5p)\n" 114":<Key>KP_Up:" "page(+0,-.5p)\n" 115"<Key>Down:" "page(+0,+.5p)\n" 116":<Key>KP_Down:" "page(+0,+.5p)\n" 117"<Key>Home:" "page(0,0)\n" 118":<Key>KP_Home:" "page(0,0)\n" 119; 120 121static XtActionsRec actions[] = { 122 {"start", ActionStart}, /* start tmp graphics */ 123 {"stop", ActionStop}, /* stop tmp graphics */ 124 {"abort", ActionAbort}, /* punt */ 125 {"move", ActionMove}, /* move tmp graphics on Motion event */ 126 {"page", ActionPage}, /* page around usually from keyboard */ 127 {"notify", ActionNotify}, /* callback new position */ 128 {"set", ActionSet}, /* set various parameters */ 129}; 130 131#define offset(field) XtOffsetOf(PannerRec, panner.field) 132static XtResource resources[] = { 133 { 134 XtNallowOff, 135 XtCAllowOff, 136 XtRBoolean, 137 sizeof(Boolean), 138 offset(allow_off), 139 XtRImmediate, 140 (XtPointer)False 141 }, 142 { 143 XtNresize, 144 XtCResize, 145 XtRBoolean, 146 sizeof(Boolean), 147 offset(resize_to_pref), 148 XtRImmediate, 149 (XtPointer)True 150 }, 151 { 152 XtNreportCallback, 153 XtCReportCallback, 154 XtRCallback, 155 sizeof(XtPointer), 156 offset(report_callbacks), 157 XtRCallback, 158 NULL 159 }, 160 { 161 XtNdefaultScale, 162 XtCDefaultScale, 163 XtRDimension, 164 sizeof(Dimension), 165 offset(default_scale), 166 XtRImmediate, 167 (XtPointer)PANNER_DEFAULT_SCALE 168 }, 169 { 170 XtNrubberBand, 171 XtCRubberBand, 172 XtRBoolean, 173 sizeof(Boolean), 174 offset(rubber_band), 175 XtRImmediate, 176 (XtPointer)False 177 }, 178 { 179 XtNforeground, 180 XtCForeground, 181 XtRPixel, 182 sizeof(Pixel), 183 offset(foreground), 184 XtRString, 185 (XtPointer)XtDefaultBackground 186 }, 187 { 188 XtNinternalSpace, 189 XtCInternalSpace, 190 XtRDimension, 191 sizeof(Dimension), 192 offset(internal_border), 193 XtRImmediate, 194 (XtPointer)4 195 }, 196 { 197 XtNlineWidth, 198 XtCLineWidth, 199 XtRDimension, 200 sizeof(Dimension), 201 offset(line_width), 202 XtRImmediate, 203 (XtPointer)0 204 }, 205 { 206 XtNcanvasWidth, 207 XtCCanvasWidth, 208 XtRDimension, 209 sizeof(Dimension), 210 offset(canvas_width), 211 XtRImmediate, 212 (XtPointer)0 213 }, 214 { 215 XtNcanvasHeight, 216 XtCCanvasHeight, 217 XtRDimension, 218 sizeof(Dimension), 219 offset(canvas_height), 220 XtRImmediate, 221 (XtPointer)0 222 }, 223 { 224 XtNsliderX, 225 XtCSliderX, 226 XtRPosition, 227 sizeof(Position), 228 offset(slider_x), 229 XtRImmediate, 230 (XtPointer)0 231 }, 232 { 233 XtNsliderY, 234 XtCSliderY, 235 XtRPosition, 236 sizeof(Position), 237 offset(slider_y), 238 XtRImmediate, 239 (XtPointer)0 240 }, 241 { 242 XtNsliderWidth, 243 XtCSliderWidth, 244 XtRDimension, 245 sizeof(Dimension), 246 offset(slider_width), 247 XtRImmediate, 248 (XtPointer)0 249 }, 250 { 251 XtNsliderHeight, 252 XtCSliderHeight, 253 XtRDimension, 254 sizeof(Dimension), 255 offset(slider_height), 256 XtRImmediate, 257 (XtPointer)0 258 }, 259 { 260 XtNshadowColor, 261 XtCShadowColor, 262 XtRPixel, 263 sizeof(Pixel), 264 offset(shadow_color), 265 XtRString, 266 (XtPointer)XtDefaultForeground 267 }, 268 { 269 XtNshadowThickness, 270 XtCShadowThickness, 271 XtRDimension, 272 sizeof(Dimension), 273 offset(shadow_thickness), 274 XtRImmediate, 275 (XtPointer)2 276 }, 277 { 278 XtNbackgroundStipple, 279 XtCBackgroundStipple, 280 XtRString, 281 sizeof(String), 282 offset(stipple_name), 283 XtRImmediate, 284 NULL 285 }, 286}; 287#undef offset 288 289#define Superclass (&simpleClassRec) 290PannerClassRec pannerClassRec = { 291 /* core */ 292 { 293 (WidgetClass)Superclass, /* superclass */ 294 "Panner", /* class_name */ 295 sizeof(PannerRec), /* widget_size */ 296 XawInitializeWidgetSet, /* class_initialize */ 297 NULL, /* class_part_initialize */ 298 False, /* class_inited */ 299 XawPannerInitialize, /* initialize */ 300 NULL, /* initialize_hook */ 301 XawPannerRealize, /* realize */ 302 actions, /* actions */ 303 XtNumber(actions), /* num_actions */ 304 resources, /* resources */ 305 XtNumber(resources), /* num_resources */ 306 NULLQUARK, /* xrm_class */ 307 True, /* compress_motion */ 308 True, /* compress_exposure */ 309 True, /* compress_enterleave */ 310 False, /* visible_interest */ 311 XawPannerDestroy, /* destroy */ 312 XawPannerResize, /* resize */ 313 XawPannerRedisplay, /* expose */ 314 XawPannerSetValues, /* set_values */ 315 NULL, /* set_values_hook */ 316 XawPannerSetValuesAlmost, /* set_values_almost */ 317 NULL, /* get_values_hook */ 318 NULL, /* accept_focus */ 319 XtVersion, /* version */ 320 NULL, /* callback_private */ 321 defaultTranslations, /* tm_table */ 322 XawPannerQueryGeometry, /* query_geometry */ 323 XtInheritDisplayAccelerator, /* display_accelerator */ 324 NULL, /* extension */ 325 }, 326 /* simple */ 327 { 328 XtInheritChangeSensitive, /* change_sensitive */ 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 = pw->panner.internal_border << 1; 427 Position maxx = (Position)XtWidth(pw) - pad - 428 (Position)pw->panner.knob_width; 429 Position maxy = (Position)XtHeight(pw) - pad - 430 (Position)pw->panner.knob_height; 431 Position *x = knob ? &pw->panner.knob_x : &pw->panner.tmp.x; 432 Position *y = knob ? &pw->panner.knob_y : &pw->panner.tmp.y; 433 434 /* 435 * note that positions are already normalized (i.e. internal_border 436 * has been subtracted out) 437 */ 438 if (*x < 0) 439 *x = 0; 440 if (*x > maxx) 441 *x = maxx; 442 443 if (*y < 0) 444 *y = 0; 445 if (*y > maxy) 446 *y = maxy; 447 448 if (knob) { 449 pw->panner.slider_x = (Position)((double)pw->panner.knob_x 450 / pw->panner.haspect + 0.5); 451 pw->panner.slider_y = (Position)((double)pw->panner.knob_y 452 / pw->panner.vaspect + 0.5); 453 pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE; 454 } 455} 456 457static void 458move_shadow(PannerWidget pw) 459{ 460 if (pw->panner.shadow_thickness > 0) { 461 int lw = pw->panner.shadow_thickness + (pw->panner.line_width << 1); 462 int pad = pw->panner.internal_border; 463 464 if (pw->panner.knob_height > lw && pw->panner.knob_width > lw) { 465 XRectangle *r = pw->panner.shadow_rects; 466 467 r->x = pw->panner.knob_x + pad + pw->panner.knob_width; 468 r->y = pw->panner.knob_y + pad + lw; 469 r->width = pw->panner.shadow_thickness; 470 r->height = pw->panner.knob_height - lw; 471 r++; 472 r->x = pw->panner.knob_x + pad + lw; 473 r->y = pw->panner.knob_y + pad + pw->panner.knob_height; 474 r->width = pw->panner.knob_width - lw + pw->panner.shadow_thickness; 475 r->height = pw->panner.shadow_thickness; 476 pw->panner.shadow_valid = True; 477 return; 478 } 479 } 480 pw->panner.shadow_valid = False; 481} 482 483static void 484scale_knob(PannerWidget pw, Bool location, Bool size) 485{ 486 if (location) { 487 pw->panner.knob_x = (Position)PANNER_HSCALE(pw, pw->panner.slider_x); 488 pw->panner.knob_y = (Position)PANNER_VSCALE(pw, pw->panner.slider_y); 489 } 490 if (size) { 491 Dimension width, height; 492 493 if (pw->panner.slider_width < 1) 494 pw->panner.slider_width = pw->panner.canvas_width; 495 if (pw->panner.slider_height < 1) 496 pw->panner.slider_height = pw->panner.canvas_height; 497 width = Min(pw->panner.slider_width, pw->panner.canvas_width); 498 height = Min(pw->panner.slider_height, pw->panner.canvas_height); 499 500 pw->panner.knob_width = (Dimension)PANNER_HSCALE(pw, width); 501 pw->panner.knob_height = (Dimension)PANNER_VSCALE(pw, height); 502 } 503 if (!pw->panner.allow_off) 504 check_knob(pw, True); 505 move_shadow(pw); 506} 507 508static void 509rescale(PannerWidget pw) 510{ 511 int hpad = pw->panner.internal_border << 1; 512 int vpad = hpad; 513 514 if (pw->panner.canvas_width < 1) 515 pw->panner.canvas_width = XtWidth(pw); 516 if (pw->panner.canvas_height < 1) 517 pw->panner.canvas_height = XtHeight(pw); 518 519 if (XtWidth(pw) <= hpad) 520 hpad = 0; 521 if (XtHeight(pw) <= vpad) 522 vpad = 0; 523 524 pw->panner.haspect = ((double)XtWidth(pw) - hpad + .5) 525 / (double)pw->panner.canvas_width; 526 pw->panner.vaspect = ((double)XtHeight(pw) - vpad + .5) 527 / (double)pw->panner.canvas_height; 528 scale_knob(pw, True, True); 529} 530 531static void 532get_default_size(PannerWidget pw, Dimension *wp, Dimension *hp) 533{ 534 Dimension pad = pw->panner.internal_border << 1; 535 536 *wp = PANNER_DSCALE(pw, pw->panner.canvas_width) + pad; 537 *hp = PANNER_DSCALE(pw, pw->panner.canvas_height) + pad; 538} 539 540static Bool 541get_event_xy(PannerWidget pw, XEvent *event, int *x, int *y) 542{ 543 int pad = pw->panner.internal_border; 544 545 switch (event->type) { 546 case ButtonPress: 547 case ButtonRelease: 548 *x = event->xbutton.x - pad; 549 *y = event->xbutton.y - pad; 550 return (True); 551 case KeyPress: 552 case KeyRelease: 553 *x = event->xkey.x - pad; 554 *y = event->xkey.y - pad; 555 return (True); 556 case EnterNotify: 557 case LeaveNotify: 558 *x = event->xcrossing.x - pad; 559 *y = event->xcrossing.y - pad; 560 return (True); 561 case MotionNotify: 562 *x = event->xmotion.x - pad; 563 *y = event->xmotion.y - pad; 564 return (True); 565 } 566 567 return (False); 568} 569 570static int 571parse_page_string(char *s, int pagesize, int canvassize, Bool *relative) 572{ 573 char *cp; 574 double val = 1.0; 575 Bool rel = False; 576 577 /* 578 * syntax: spaces [+-] number spaces [pc\0] spaces 579 */ 580 for (; isascii(*s) && isspace(*s); s++) /* skip white space */ 581 ; 582 583 if (*s == '+' || *s == '-') { /* deal with signs */ 584 rel = True; 585 if (*s == '-') 586 val = -1.0; 587 s++; 588 } 589 if (!*s) { /* if null then return nothing */ 590 *relative = True; 591 return (0); 592 } 593 594 /* skip over numbers */ 595 for (cp = s; isascii(*s) && (isdigit(*s) || *s == '.'); s++) 596 ; 597 val *= atof(cp); 598 599 /* skip blanks */ 600 for (; isascii(*s) && isspace(*s); s++) 601 ; 602 603 if (*s) { /* if units */ 604 switch (s[0]) { 605 case 'p': 606 case 'P': 607 val *= (double)pagesize; 608 break; 609 case 'c': 610 case 'C': 611 val *= (double)canvassize; 612 break; 613 } 614 } 615 *relative = rel; 616 617 return ((int)val); 618} 619 620#define DRAW_TMP(pw) \ 621{ \ 622 XDrawRectangle(XtDisplay(pw), XtWindow(pw), \ 623 pw->panner.xor_gc, \ 624 pw->panner.tmp.x + pw->panner.internal_border, \ 625 pw->panner.tmp.y + pw->panner.internal_border, \ 626 pw->panner.knob_width - 1, \ 627 pw->panner.knob_height - 1); \ 628 pw->panner.tmp.showing = !pw->panner.tmp.showing; \ 629} 630 631#define UNDRAW_TMP(pw) \ 632{ \ 633 if (pw->panner.tmp.showing) \ 634 DRAW_TMP(pw); \ 635} 636 637#define BACKGROUND_STIPPLE(pw) \ 638XmuLocatePixmapFile(pw->core.screen, pw->panner.stipple_name, \ 639 pw->panner.shadow_color, pw->core.background_pixel, \ 640 pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL) 641 642#define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap) 643 644/*ARGSUSED*/ 645static void 646XawPannerInitialize(Widget greq, Widget gnew, ArgList args, Cardinal *num_args) 647{ 648 PannerWidget req = (PannerWidget)greq, cnew = (PannerWidget)gnew; 649 Dimension defwidth, defheight; 650 651 if (req->panner.canvas_width < 1) 652 cnew->panner.canvas_width = 1; 653 if (req->panner.canvas_height < 1) 654 cnew->panner.canvas_height = 1; 655 if (req->panner.default_scale < 1) 656 cnew->panner.default_scale = PANNER_DEFAULT_SCALE; 657 658 get_default_size(req, &defwidth, &defheight); 659 if (XtWidth(req) < 1) 660 XtWidth(cnew) = defwidth; 661 if (XtHeight(req) < 1) 662 XtHeight(cnew) = defheight; 663 664 cnew->panner.shadow_gc = NULL; 665 reset_shadow_gc(cnew); /* shadowColor */ 666 cnew->panner.slider_gc = NULL; 667 reset_slider_gc(cnew); /* foreground */ 668 cnew->panner.xor_gc = NULL; 669 reset_xor_gc(cnew); /* foreground ^ background */ 670 671 rescale(cnew); /* does a position check */ 672 cnew->panner.shadow_valid = False; 673 cnew->panner.tmp.doing = False; 674 cnew->panner.tmp.showing = False; 675 } 676 677static void 678XawPannerRealize(Widget gw, XtValueMask *valuemaskp, 679 XSetWindowAttributes *attr) 680{ 681 PannerWidget pw = (PannerWidget)gw; 682 Pixmap pm = XtUnspecifiedPixmap; 683 Bool gotpm = False; 684 685 if (pw->core.background_pixmap == XtUnspecifiedPixmap) { 686 if (pw->panner.stipple_name) 687 pm = BACKGROUND_STIPPLE(pw); 688 689 if (PIXMAP_OKAY(pm)) { 690 attr->background_pixmap = pm; 691 *valuemaskp |= CWBackPixmap; 692 *valuemaskp &= ~CWBackPixel; 693 gotpm = True; 694 } 695 } 696 (*pannerWidgetClass->core_class.superclass->core_class.realize) 697 (gw, valuemaskp, attr); 698 699 if (gotpm) 700 XFreePixmap(XtDisplay(gw), pm); 701} 702 703static void 704XawPannerDestroy(Widget gw) 705{ 706 PannerWidget pw = (PannerWidget)gw; 707 708 XtReleaseGC(gw, pw->panner.shadow_gc); 709 XtReleaseGC(gw, pw->panner.slider_gc); 710 XtReleaseGC(gw, pw->panner.xor_gc); 711} 712 713static void 714XawPannerResize(Widget gw) 715{ 716 rescale((PannerWidget)gw); 717} 718 719static void 720XawPannerRedisplay(Widget gw, XEvent *event, Region region) 721{ 722 PannerWidget pw = (PannerWidget)gw; 723 Display *dpy = XtDisplay(gw); 724 Window w = XtWindow(gw); 725 int pad = pw->panner.internal_border; 726 Dimension lw = pw->panner.line_width; 727 Dimension extra = pw->panner.shadow_thickness + (lw << 1); 728 int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad; 729 730 if (Superclass->core_class.expose) 731 (Superclass->core_class.expose)(gw, event, region); 732 733 pw->panner.tmp.showing = False; 734 XClearArea(XtDisplay(pw), XtWindow(pw), 735 (int)pw->panner.last_x - ((int)lw) + pad, 736 (int)pw->panner.last_y - ((int)lw) + pad, 737 pw->panner.knob_width + extra, 738 pw->panner.knob_height + extra, 739 False); 740 pw->panner.last_x = pw->panner.knob_x; 741 pw->panner.last_y = pw->panner.knob_y; 742 743 XFillRectangle(dpy, w, pw->panner.slider_gc, kx, ky, 744 pw->panner.knob_width - 1, pw->panner.knob_height - 1); 745 746 if (lw) 747 XDrawRectangle(dpy, w, pw->panner.shadow_gc, kx, ky, 748 pw->panner.knob_width - 1, 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, Widget gnew, 760 ArgList args, Cardinal *num_args) 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 (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, Cardinal *num_params) 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 = x - pw->panner.knob_x; 885 pw->panner.tmp.dy = 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, Cardinal *num_params) 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 = x - pw->panner.tmp.dx; 901 pw->panner.tmp.y = 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 = x - pw->panner.tmp.dx; 946 pw->panner.tmp.y = 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 = x; 994 pw->panner.tmp.y = 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, String *params, Cardinal *num_params) 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 (Position)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 (Position)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, 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