Scrollbar.c revision 5b16253f
1/*********************************************************** 2 3Copyright 1987, 1988, 1994, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 25 26Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. 27 28 All Rights Reserved 29 30Permission to use, copy, modify, and distribute this software and its 31documentation for any purpose and without fee is hereby granted, 32provided that the above copyright notice appear in all copies and that 33both that copyright notice and this permission notice appear in 34supporting documentation, and that the name of Digital not be 35used in advertising or publicity pertaining to distribution of the 36software without specific, written prior permission. 37 38DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 39ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 40DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 41ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 42WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 43ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 44SOFTWARE. 45 46******************************************************************/ 47 48#ifdef HAVE_CONFIG_H 49#include <config.h> 50#endif 51#include <X11/IntrinsicP.h> 52#include <X11/StringDefs.h> 53#include <X11/Xmu/Drawing.h> 54#include <X11/Xaw/ScrollbarP.h> 55#include <X11/Xaw/XawInit.h> 56#include "Private.h" 57 58#define NoButton -1 59#define PICKLENGTH(widget, x, y) \ 60(((widget)->scrollbar.orientation == XtorientHorizontal) ? (x) : (y)) 61 62/* 63 * Class Methods 64 */ 65static void XawScrollbarClassInitialize(void); 66static void XawScrollbarDestroy(Widget); 67static void XawScrollbarInitialize(Widget, Widget, ArgList, Cardinal*_args); 68static void XawScrollbarRealize(Widget, Mask*, XSetWindowAttributes*); 69static void XawScrollbarRedisplay(Widget, XEvent*, Region); 70static void XawScrollbarResize(Widget); 71static Boolean XawScrollbarSetValues(Widget, Widget, Widget, 72 ArgList, Cardinal*); 73 74/* 75 * Prototypes 76 */ 77static Boolean CompareEvents(XEvent*, XEvent*); 78static void CreateGC(Widget); 79static float FloatInRange(float, float, float); 80static float FractionLoc(ScrollbarWidget, int, int); 81static void ExtractPosition(XEvent*, Position*, Position*); 82static int InRange(int, int, int); 83static void FillArea(ScrollbarWidget, int, int, int); 84static Bool LookAhead(Widget, XEvent*); 85static void PaintThumb(ScrollbarWidget); 86static Bool PeekNotifyEvent(Display*, XEvent*, char*); 87static void SetDimensions(ScrollbarWidget); 88 89/* 90 * Actions 91 */ 92static void EndScroll(Widget, XEvent*, String*, Cardinal*); 93static void MoveThumb(Widget, XEvent*, String*, Cardinal*); 94static void NotifyScroll(Widget, XEvent*, String*, Cardinal*); 95static void NotifyThumb(Widget, XEvent*, String*, Cardinal*); 96static void StartScroll(Widget, XEvent*, String*, Cardinal*); 97 98/* 99 * Initialization 100 */ 101static char defaultTranslations[] = 102"<Btn1Down>:" "StartScroll(Forward)\n" 103"<Btn2Down>:" "StartScroll(Continuous) MoveThumb() NotifyThumb()\n" 104"<Btn3Down>:" "StartScroll(Backward)\n" 105"<Btn4Down>:" "StartScroll(Backward)\n" 106"<Btn5Down>:" "StartScroll(Forward)\n" 107"<Btn2Motion>:" "MoveThumb() NotifyThumb()\n" 108"<BtnUp>:" "NotifyScroll(Proportional) EndScroll()\n"; 109 110static float floatZero = 0.0; 111 112#define Offset(field) XtOffsetOf(ScrollbarRec, field) 113 114static XtResource resources[] = { 115 { 116 XtNlength, 117 XtCLength, 118 XtRDimension, 119 sizeof(Dimension), 120 Offset(scrollbar.length), 121 XtRImmediate, 122 (XtPointer)1 123 }, 124 { 125 XtNthickness, 126 XtCThickness, 127 XtRDimension, 128 sizeof(Dimension), 129 Offset(scrollbar.thickness), 130 XtRImmediate, 131 (XtPointer)14 132 }, 133 { 134 XtNorientation, 135 XtCOrientation, 136 XtROrientation, 137 sizeof(XtOrientation), 138 Offset(scrollbar.orientation), 139 XtRImmediate, 140 (XtPointer)XtorientVertical 141 }, 142 { 143 XtNscrollProc, 144 XtCCallback, 145 XtRCallback, 146 sizeof(XtPointer), 147 Offset(scrollbar.scrollProc), 148 XtRCallback, 149 NULL 150 }, 151 { 152 XtNthumbProc, 153 XtCCallback, 154 XtRCallback, 155 sizeof(XtPointer), 156 Offset(scrollbar.thumbProc), 157 XtRCallback, 158 NULL 159 }, 160 { 161 XtNjumpProc, 162 XtCCallback, 163 XtRCallback, 164 sizeof(XtPointer), 165 Offset(scrollbar.jumpProc), 166 XtRCallback, 167 NULL 168 }, 169 { 170 XtNthumb, 171 XtCThumb, 172 XtRBitmap, 173 sizeof(Pixmap), 174 Offset(scrollbar.thumb), 175 XtRImmediate, 176 (XtPointer)XtUnspecifiedPixmap 177 }, 178 { 179 XtNforeground, 180 XtCForeground, 181 XtRPixel, 182 sizeof(Pixel), 183 Offset(scrollbar.foreground), 184 XtRString, 185 (XtPointer)XtDefaultForeground 186 }, 187 { 188 XtNshown, 189 XtCShown, 190 XtRFloat, 191 sizeof(float), 192 Offset(scrollbar.shown), 193 XtRFloat, 194 (XtPointer)&floatZero 195 }, 196 { 197 XtNtopOfThumb, 198 XtCTopOfThumb, 199 XtRFloat, 200 sizeof(float), 201 Offset(scrollbar.top), 202 XtRFloat, 203 (XtPointer)&floatZero 204 }, 205 { 206 XtNscrollVCursor, 207 XtCCursor, 208 XtRCursor, 209 sizeof(Cursor), 210 Offset(scrollbar.verCursor), 211 XtRString, 212 (XtPointer)"sb_v_double_arrow" 213 }, 214 { 215 XtNscrollHCursor, 216 XtCCursor, 217 XtRCursor, 218 sizeof(Cursor), 219 Offset(scrollbar.horCursor), 220 XtRString, 221 (XtPointer)"sb_h_double_arrow" 222 }, 223 { 224 XtNscrollUCursor, 225 XtCCursor, 226 XtRCursor, 227 sizeof(Cursor), 228 Offset(scrollbar.upCursor), 229 XtRString, 230 (XtPointer)"sb_up_arrow" 231 }, 232 { 233 XtNscrollDCursor, 234 XtCCursor, 235 XtRCursor, 236 sizeof(Cursor), 237 Offset(scrollbar.downCursor), 238 XtRString, 239 (XtPointer)"sb_down_arrow" 240 }, 241 { 242 XtNscrollLCursor, 243 XtCCursor, 244 XtRCursor, 245 sizeof(Cursor), 246 Offset(scrollbar.leftCursor), 247 XtRString, 248 (XtPointer)"sb_left_arrow" 249 }, 250 { 251 XtNscrollRCursor, 252 XtCCursor, 253 XtRCursor, 254 sizeof(Cursor), 255 Offset(scrollbar.rightCursor), 256 XtRString, 257 (XtPointer)"sb_right_arrow" 258 }, 259 { 260 XtNminimumThumb, 261 XtCMinimumThumb, 262 XtRDimension, 263 sizeof(Dimension), 264 Offset(scrollbar.min_thumb), 265 XtRImmediate, 266 (XtPointer)7 267 }, 268}; 269#undef Offset 270 271static XtActionsRec actions[] = { 272 {"StartScroll", StartScroll}, 273 {"MoveThumb", MoveThumb}, 274 {"NotifyThumb", NotifyThumb}, 275 {"NotifyScroll", NotifyScroll}, 276 {"EndScroll", EndScroll}, 277}; 278 279#define Superclass (&simpleClassRec) 280ScrollbarClassRec scrollbarClassRec = { 281 /* core */ 282 { 283 (WidgetClass)&simpleClassRec, /* superclass */ 284 "Scrollbar", /* class_name */ 285 sizeof(ScrollbarRec), /* widget_size */ 286 XawScrollbarClassInitialize, /* class_initialize */ 287 NULL, /* class_part_init */ 288 False, /* class_inited */ 289 XawScrollbarInitialize, /* initialize */ 290 NULL, /* initialize_hook */ 291 XawScrollbarRealize, /* realize */ 292 actions, /* actions */ 293 XtNumber(actions), /* num_actions */ 294 resources, /* resources */ 295 XtNumber(resources), /* num_resources */ 296 NULLQUARK, /* xrm_class */ 297 True, /* compress_motion */ 298 True, /* compress_exposure */ 299 True, /* compress_enterleave */ 300 False, /* visible_interest */ 301 XawScrollbarDestroy, /* destroy */ 302 XawScrollbarResize, /* resize */ 303 XawScrollbarRedisplay, /* expose */ 304 XawScrollbarSetValues, /* set_values */ 305 NULL, /* set_values_hook */ 306 XtInheritSetValuesAlmost, /* set_values_almost */ 307 NULL, /* get_values_hook */ 308 NULL, /* accept_focus */ 309 XtVersion, /* version */ 310 NULL, /* callback_private */ 311 defaultTranslations, /* tm_table */ 312 XtInheritQueryGeometry, /* query_geometry */ 313 XtInheritDisplayAccelerator, /* display_accelerator */ 314 NULL, /* extension */ 315 }, 316 /* simple */ 317 { 318 XtInheritChangeSensitive, /* change_sensitive */ 319 }, 320 /* scrollbar */ 321 { 322 NULL, /* extension */ 323 }, 324}; 325 326WidgetClass scrollbarWidgetClass = (WidgetClass)&scrollbarClassRec; 327 328/* 329 * Implementation 330 */ 331static void 332XawScrollbarClassInitialize(void) 333{ 334 XawInitializeWidgetSet(); 335 XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation, 336 NULL, 0); 337 XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString, 338 NULL, 0, XtCacheNone, NULL); 339} 340 341/* 342 * Make sure the first number is within the range specified by the other 343 * two numbers. 344 */ 345static int 346InRange(int num, int small, int big) 347{ 348 return ((num < small) ? small : ((num > big) ? big : num)); 349} 350 351/* 352 * Same as above, but for floating numbers 353 */ 354static float 355FloatInRange(float num, float small, float big) 356{ 357 return ((num < small) ? small : ((num > big) ? big : num)); 358} 359 360/* Fill the area specified by top and bottom with the given pattern */ 361static float 362FractionLoc(ScrollbarWidget w, int x, int y) 363{ 364 float result; 365 366 result = PICKLENGTH(w, (float)x / (float)XtWidth(w), (float)y / (float)XtHeight(w)); 367 368 return (FloatInRange(result, 0.0, 1.0)); 369} 370 371static void 372FillArea(ScrollbarWidget w, int top, int bottom, int thumb) 373{ 374 Dimension length; 375 376 top = XawMax(1, top); 377 if (w->scrollbar.orientation == XtorientHorizontal) 378 bottom = XawMin(bottom, XtWidth(w) - 1); 379 else 380 bottom = XawMin(bottom, XtHeight(w) - 1); 381 382 if (bottom <= top) 383 return; 384 385 length = (Dimension)(bottom - top); 386 387 switch(thumb) { 388 /* Fill the new Thumb location */ 389 case 1: 390 if (w->scrollbar.orientation == XtorientHorizontal) 391 XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc, 392 top, 1, length, (unsigned)(XtHeight(w) - 2)); 393 else 394 XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc, 395 1, top, (unsigned)(XtWidth(w) - 2), length); 396 break; 397 /* Clear the old Thumb location */ 398 case 0: 399 if (w->scrollbar.orientation == XtorientHorizontal) 400 XClearArea(XtDisplay(w), XtWindow(w), 401 top, 1, length, (unsigned)(XtHeight(w) - 2), False); 402 else 403 XClearArea(XtDisplay(w), XtWindow(w), 404 1, top, (unsigned)(XtWidth(w) - 2), length, False); 405 break; 406 } 407} 408 409 410/* Paint the thumb in the area specified by w->top and 411 w->shown. The old area is erased. The painting and 412 erasing is done cleverly so that no flickering will occur. */ 413static void 414PaintThumb(ScrollbarWidget w) 415{ 416 Position oldtop, oldbot, newtop, newbot; 417 418 oldtop = w->scrollbar.topLoc; 419 oldbot = (Position)(oldtop + w->scrollbar.shownLength); 420 newtop = (Position)(w->scrollbar.length * w->scrollbar.top); 421 newbot = (Position)(newtop + (int)(w->scrollbar.length * w->scrollbar.shown)); 422 if (newbot < newtop + (int)w->scrollbar.min_thumb) 423 newbot = (Position)(newtop + w->scrollbar.min_thumb); 424 w->scrollbar.topLoc = newtop; 425 w->scrollbar.shownLength = (Dimension)(newbot - newtop); 426 427 if (XtIsRealized((Widget)w)) { 428 if (newtop < oldtop) 429 FillArea(w, newtop, XawMin(newbot, oldtop), 1); 430 if (newtop > oldtop) 431 FillArea(w, oldtop, XawMin(newtop, oldbot), 0); 432 if (newbot < oldbot) 433 FillArea(w, XawMax(newbot, oldtop), oldbot, 0); 434 if (newbot > oldbot) 435 FillArea(w, XawMax(newtop, oldbot), newbot, 1); 436 } 437} 438 439static void 440SetDimensions(ScrollbarWidget w) 441{ 442 if (w->scrollbar.orientation == XtorientVertical) { 443 w->scrollbar.length = XtHeight(w); 444 w->scrollbar.thickness = XtWidth(w); 445 } 446 else { 447 w->scrollbar.length = XtWidth(w); 448 w->scrollbar.thickness = XtHeight(w); 449 } 450} 451 452static void 453XawScrollbarDestroy(Widget w) 454{ 455 ScrollbarWidget sbw = (ScrollbarWidget)w; 456 457 XtReleaseGC(w, sbw->scrollbar.gc); 458} 459 460static void 461CreateGC(Widget w) 462{ 463 ScrollbarWidget sbw = (ScrollbarWidget)w; 464 XGCValues gcValues; 465 XtGCMask mask; 466 unsigned int depth = 1; 467 468 if (sbw->scrollbar.thumb == XtUnspecifiedPixmap) 469 sbw->scrollbar.thumb = XmuCreateStippledPixmap(XtScreen(w), 470 (Pixel)1, (Pixel)0, 471 depth); 472 else if (sbw->scrollbar.thumb != None) { 473 Window root; 474 int x, y; 475 unsigned int width, height, bw; 476 477 XGetGeometry(XtDisplay(w), sbw->scrollbar.thumb, &root, &x, &y, 478 &width, &height, &bw, &depth); 479 } 480 481 gcValues.foreground = sbw->scrollbar.foreground; 482 gcValues.background = sbw->core.background_pixel; 483 mask = GCForeground | GCBackground; 484 485 if (sbw->scrollbar.thumb != None) { 486 if (depth == 1) { 487 gcValues.fill_style = FillOpaqueStippled; 488 gcValues.stipple = sbw->scrollbar.thumb; 489 mask |= GCFillStyle | GCStipple; 490 } 491 else { 492 gcValues.fill_style = FillTiled; 493 gcValues.tile = sbw->scrollbar.thumb; 494 mask |= GCFillStyle | GCTile; 495 } 496 } 497 sbw->scrollbar.gc = XtGetGC(w, mask, &gcValues); 498} 499 500/* ARGSUSED */ 501static void 502XawScrollbarInitialize(Widget request _X_UNUSED, Widget cnew, 503 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 504{ 505 ScrollbarWidget w = (ScrollbarWidget)cnew; 506 507 CreateGC(cnew); 508 509 if (XtWidth(w) == 0) 510 XtWidth(w) = w->scrollbar.orientation == XtorientVertical ? 511 w->scrollbar.thickness : w->scrollbar.length; 512 513 if (XtHeight(w) == 0) 514 XtHeight(w) = w->scrollbar.orientation == XtorientHorizontal ? 515 w->scrollbar.thickness : w->scrollbar.length; 516 517 SetDimensions(w); 518 w->scrollbar.direction = 0; 519 w->scrollbar.topLoc = 0; 520 w->scrollbar.shownLength = w->scrollbar.min_thumb; 521} 522 523static void 524XawScrollbarRealize(Widget gw, Mask *valueMask, 525 XSetWindowAttributes *attributes) 526{ 527 ScrollbarWidget w = (ScrollbarWidget)gw; 528 529 w->scrollbar.inactiveCursor = w->scrollbar.orientation == XtorientVertical ? 530 w->scrollbar.verCursor : w->scrollbar.horCursor; 531 532 XtVaSetValues(gw, XtNcursor, w->scrollbar.inactiveCursor, NULL); 533 534 /* 535 * The Simple widget actually stuffs the value in the valuemask 536 */ 537 (*scrollbarWidgetClass->core_class.superclass->core_class.realize) 538 (gw, valueMask, attributes); 539} 540 541/*ARGSUSED*/ 542static Boolean 543XawScrollbarSetValues(Widget current, Widget request _X_UNUSED, Widget desired, 544 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 545{ 546 ScrollbarWidget w = (ScrollbarWidget)current; 547 ScrollbarWidget dw = (ScrollbarWidget)desired; 548 Boolean redraw = False; 549 550 /* 551 * If these values are outside the acceptable range ignore them... 552 */ 553 if (dw->scrollbar.top < 0.0 || dw->scrollbar.top > 1.0) 554 dw->scrollbar.top = w->scrollbar.top; 555 556 if (dw->scrollbar.shown < 0.0 || dw->scrollbar.shown > 1.0) 557 dw->scrollbar.shown = w->scrollbar.shown; 558 559 if (XtIsRealized (desired)) { 560 if (w->scrollbar.foreground != dw->scrollbar.foreground || 561 w->core.background_pixel != dw->core.background_pixel || 562 w->scrollbar.thumb != dw->scrollbar.thumb) { 563 XtReleaseGC((Widget)dw, w->scrollbar.gc); 564 CreateGC((Widget)dw); 565 redraw = True; 566 } 567 if (w->scrollbar.top != dw->scrollbar.top || 568 w->scrollbar.shown != dw->scrollbar.shown) 569 redraw = True; 570 } 571 572 return (redraw); 573} 574 575static void 576XawScrollbarResize(Widget gw) 577{ 578 /* ForgetGravity has taken care of background, but thumb may 579 * have to move as a result of the new size. */ 580 SetDimensions((ScrollbarWidget)gw); 581 XawScrollbarRedisplay(gw, NULL, NULL); 582} 583 584/*ARGSUSED*/ 585static void 586XawScrollbarRedisplay(Widget gw, XEvent *event, Region region) 587{ 588 ScrollbarWidget w = (ScrollbarWidget)gw; 589 int x, y; 590 unsigned int width, height; 591 592 if (Superclass->core_class.expose) 593 (*Superclass->core_class.expose)(gw, event, region); 594 595 if (w->scrollbar.orientation == XtorientHorizontal) { 596 x = w->scrollbar.topLoc; 597 y = 1; 598 width = (unsigned)(w->scrollbar.shownLength); 599 height = (unsigned)(XtHeight(w) - 2); 600 } 601 else { 602 x = 1; 603 y = w->scrollbar.topLoc; 604 width = (unsigned)(XtWidth(w) - 2); 605 height = (unsigned)(w->scrollbar.shownLength); 606 } 607 608 if (region == NULL || 609 XRectInRegion(region, x, y, width, height) != RectangleOut) { 610 /* Forces entire thumb to be painted */ 611 w->scrollbar.topLoc = (Position)(-(w->scrollbar.length + 1)); 612 PaintThumb(w); 613 } 614} 615 616/*ARGSUSED*/ 617static void 618StartScroll(Widget gw, XEvent *event _X_UNUSED, String *params, Cardinal *num_params) 619{ 620 ScrollbarWidget w = (ScrollbarWidget)gw; 621 Cursor cursor; 622 char direction; 623 624 if (w->scrollbar.direction != 0) /* if we're already scrolling */ 625 return; 626 if (*num_params > 0) 627 direction = *params[0]; 628 else 629 direction = 'C'; 630 631 w->scrollbar.direction = direction; 632 633 switch(direction) { 634 case 'B': 635 case 'b': 636 cursor = w->scrollbar.orientation == XtorientVertical ? 637 w->scrollbar.downCursor : w->scrollbar.rightCursor; 638 break; 639 case 'F': 640 case 'f': 641 cursor = w->scrollbar.orientation == XtorientVertical ? 642 w->scrollbar.upCursor : w->scrollbar.leftCursor; 643 break; 644 case 'C': 645 case 'c': 646 cursor = w->scrollbar.orientation == XtorientVertical ? 647 w->scrollbar.rightCursor : w->scrollbar.upCursor; 648 break; 649 default: 650 return; /* invalid invocation */ 651 } 652 653 XtVaSetValues(gw, XtNcursor, cursor, NULL); 654 655 XFlush(XtDisplay(w)); 656} 657 658static Boolean 659CompareEvents(XEvent *oldEvent, XEvent *newEvent) 660{ 661#define Check(field) if (newEvent->field != oldEvent->field) return (False) 662 663 Check(xany.display); 664 Check(xany.type); 665 Check(xany.window); 666 667 switch(newEvent->type) { 668 case MotionNotify: 669 Check(xmotion.state); 670 break; 671 case ButtonPress: 672 case ButtonRelease: 673 Check(xbutton.state); 674 Check(xbutton.button); 675 break; 676 case KeyPress: 677 case KeyRelease: 678 Check(xkey.state); 679 Check(xkey.keycode); 680 break; 681 case EnterNotify: 682 case LeaveNotify: 683 Check(xcrossing.mode); 684 Check(xcrossing.detail); 685 Check(xcrossing.state); 686 break; 687 } 688#undef Check 689 690 return (True); 691} 692 693struct EventData { 694 XEvent *oldEvent; 695 int count; 696}; 697 698static Bool 699PeekNotifyEvent(Display *dpy, XEvent *event, char *args) 700{ 701 struct EventData *eventData = (struct EventData*)args; 702 703 return (++eventData->count == QLength(dpy) /* since PeekIf blocks */ 704 || CompareEvents(event, eventData->oldEvent)); 705} 706 707static Bool 708LookAhead(Widget w, XEvent *event) 709{ 710 XEvent newEvent; 711 struct EventData eventData; 712 713 if (QLength(XtDisplay(w)) == 0) 714 return (False); 715 716 eventData.count = 0; 717 eventData.oldEvent = event; 718 719 XPeekIfEvent(XtDisplay(w), &newEvent, PeekNotifyEvent, (char*)&eventData); 720 721 if (CompareEvents(event, &newEvent)) 722 return (True); 723 724 return (False); 725} 726 727static void 728ExtractPosition(XEvent *event, Position *x, Position *y) 729{ 730 switch(event->type) { 731 case MotionNotify: 732 *x = (Position)event->xmotion.x; 733 *y = (Position)event->xmotion.y; 734 break; 735 case ButtonPress: 736 case ButtonRelease: 737 *x = (Position)event->xbutton.x; 738 *y = (Position)event->xbutton.y; 739 break; 740 case KeyPress: 741 case KeyRelease: 742 *x = (Position)event->xkey.x; 743 *y = (Position)event->xkey.y; 744 break; 745 case EnterNotify: 746 case LeaveNotify: 747 *x = (Position)event->xcrossing.x; 748 *y = (Position)event->xcrossing.y; 749 break; 750 default: 751 *x = 0; 752 *y = 0; 753 break; 754 } 755} 756 757static void 758NotifyScroll(Widget gw, XEvent *event, String *params, Cardinal *num_params) 759{ 760 ScrollbarWidget w = (ScrollbarWidget)gw; 761 long call_data = 0; 762 char style; 763 Position x, y; 764 765 if (w->scrollbar.direction == 0) /* if no StartScroll */ 766 return; 767 768 if (LookAhead(gw, event)) 769 return; 770 771 if (*num_params > 0) 772 style = *params[0]; 773 else 774 style = 'P'; 775 776 switch(style) { 777 case 'P': /* Proportional */ 778 case 'p': 779 ExtractPosition(event, &x, &y); 780 call_data = InRange(PICKLENGTH(w, x, y), 0, (int)w->scrollbar.length); 781 break; 782 case 'F': /* FullLength */ 783 case 'f': 784 call_data = w->scrollbar.length; 785 break; 786 } 787 788 switch(w->scrollbar.direction) { 789 case 'B': 790 case 'b': 791 call_data = -call_data; 792 /*FALLTHROUGH*/ 793 case 'F': 794 case 'f': 795 XtCallCallbacks(gw, XtNscrollProc, (XtPointer)call_data); 796 break; 797 case 'C': 798 case 'c': /* NotifyThumb has already called the thumbProc(s) */ 799 break; 800 } 801} 802 803/*ARGSUSED*/ 804static void 805EndScroll(Widget gw, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 806{ 807 ScrollbarWidget w = (ScrollbarWidget)gw; 808 809 XtVaSetValues(gw, XtNcursor, w->scrollbar.inactiveCursor, NULL); 810 XFlush(XtDisplay(w)); /* make sure it get propagated */ 811 812 w->scrollbar.direction = 0; 813} 814 815/*ARGSUSED*/ 816static void 817MoveThumb(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 818{ 819 ScrollbarWidget w = (ScrollbarWidget)gw; 820 Position x, y; 821 822 if (w->scrollbar.direction == 0) /* if no StartScroll */ 823 return; 824 825 if (LookAhead(gw, event)) 826 return; 827 828 if (!event->xmotion.same_screen) 829 return; 830 831 ExtractPosition(event, &x, &y); 832 w->scrollbar.top = FractionLoc(w, x, y); 833} 834 835/*ARGSUSED*/ 836static void 837NotifyThumb(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 838{ 839 ScrollbarWidget w = (ScrollbarWidget)gw; 840 union { 841 XtPointer xtp; 842 float xtf; 843 } xtpf; 844 845 if (w->scrollbar.direction == 0) /* if no StartScroll */ 846 return; 847 848 if (LookAhead(gw, event)) 849 return; 850 851 /* thumbProc is not pretty, but is necessary for backwards 852 compatibility on those architectures for which it work{s,ed}; 853 the intent is to pass a (truncated) float by value. */ 854 xtpf.xtf = w->scrollbar.top; 855 XtCallCallbacks(gw, XtNthumbProc, xtpf.xtp); 856 XtCallCallbacks(gw, XtNjumpProc, (XtPointer)&w->scrollbar.top); 857 858 PaintThumb(w); 859} 860 861/* 862 * Public routines 863 */ 864/* Set the scroll bar to the given location. */ 865void 866XawScrollbarSetThumb(Widget gw, 867#if NeedWidePrototypes 868 double top, double shown 869#else 870 float top, float shown 871#endif 872 ) 873{ 874 ScrollbarWidget w = (ScrollbarWidget)gw; 875 876 if (w->scrollbar.direction == 'c') /* if still thumbing */ 877 return; 878 879 w->scrollbar.top = (float)((top > 1.0) 880 ? 1.0 881 : ((top >= 0.0) 882 ? top 883 : w->scrollbar.top)); 884 885 w->scrollbar.shown = (float)((shown > 1.0) 886 ? 1.0 887 : (shown >= 0.0 888 ? shown 889 : w->scrollbar.shown)); 890 PaintThumb(w); 891} 892