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#ifndef OLDXAW 320 NULL, 321#endif 322 }, 323 /* scrollbar */ 324 { 325 NULL, /* extension */ 326 }, 327}; 328 329WidgetClass scrollbarWidgetClass = (WidgetClass)&scrollbarClassRec; 330 331/* 332 * Implementation 333 */ 334static void 335XawScrollbarClassInitialize(void) 336{ 337 XawInitializeWidgetSet(); 338 XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation, 339 NULL, 0); 340 XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString, 341 NULL, 0, XtCacheNone, NULL); 342} 343 344/* 345 * Make sure the first number is within the range specified by the other 346 * two numbers. 347 */ 348static int 349InRange(int num, int small, int big) 350{ 351 return ((num < small) ? small : ((num > big) ? big : num)); 352} 353 354/* 355 * Same as above, but for floating numbers 356 */ 357static float 358FloatInRange(float num, float small, float big) 359{ 360 return ((num < small) ? small : ((num > big) ? big : num)); 361} 362 363/* Fill the area specified by top and bottom with the given pattern */ 364static float 365FractionLoc(ScrollbarWidget w, int x, int y) 366{ 367 float result; 368 369 result = PICKLENGTH(w, (float)x / (float)XtWidth(w), (float)y / (float)XtHeight(w)); 370 371 return (FloatInRange(result, 0.0, 1.0)); 372} 373 374static void 375FillArea(ScrollbarWidget w, int top, int bottom, int thumb) 376{ 377 Dimension length; 378 379 top = XawMax(1, top); 380 if (w->scrollbar.orientation == XtorientHorizontal) 381 bottom = XawMin(bottom, XtWidth(w) - 1); 382 else 383 bottom = XawMin(bottom, XtHeight(w) - 1); 384 385 if (bottom <= top) 386 return; 387 388 length = (Dimension)(bottom - top); 389 390 switch(thumb) { 391 /* Fill the new Thumb location */ 392 case 1: 393 if (w->scrollbar.orientation == XtorientHorizontal) 394 XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc, 395 top, 1, length, (unsigned)(XtHeight(w) - 2)); 396 else 397 XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc, 398 1, top, (unsigned)(XtWidth(w) - 2), length); 399 break; 400 /* Clear the old Thumb location */ 401 case 0: 402 if (w->scrollbar.orientation == XtorientHorizontal) 403 XClearArea(XtDisplay(w), XtWindow(w), 404 top, 1, length, (unsigned)(XtHeight(w) - 2), False); 405 else 406 XClearArea(XtDisplay(w), XtWindow(w), 407 1, top, (unsigned)(XtWidth(w) - 2), length, False); 408 break; 409 } 410} 411 412 413/* Paint the thumb in the area specified by w->top and 414 w->shown. The old area is erased. The painting and 415 erasing is done cleverly so that no flickering will occur. */ 416static void 417PaintThumb(ScrollbarWidget w) 418{ 419 Position oldtop, oldbot, newtop, newbot; 420 421 oldtop = w->scrollbar.topLoc; 422 oldbot = (Position)(oldtop + w->scrollbar.shownLength); 423 newtop = (Position)(w->scrollbar.length * w->scrollbar.top); 424 newbot = (Position)(newtop + (int)(w->scrollbar.length * w->scrollbar.shown)); 425 if (newbot < newtop + (int)w->scrollbar.min_thumb) 426 newbot = (Position)(newtop + w->scrollbar.min_thumb); 427 w->scrollbar.topLoc = newtop; 428 w->scrollbar.shownLength = (Dimension)(newbot - newtop); 429 430 if (XtIsRealized((Widget)w)) { 431 if (newtop < oldtop) 432 FillArea(w, newtop, XawMin(newbot, oldtop), 1); 433 if (newtop > oldtop) 434 FillArea(w, oldtop, XawMin(newtop, oldbot), 0); 435 if (newbot < oldbot) 436 FillArea(w, XawMax(newbot, oldtop), oldbot, 0); 437 if (newbot > oldbot) 438 FillArea(w, XawMax(newtop, oldbot), newbot, 1); 439 } 440} 441 442static void 443SetDimensions(ScrollbarWidget w) 444{ 445 if (w->scrollbar.orientation == XtorientVertical) { 446 w->scrollbar.length = XtHeight(w); 447 w->scrollbar.thickness = XtWidth(w); 448 } 449 else { 450 w->scrollbar.length = XtWidth(w); 451 w->scrollbar.thickness = XtHeight(w); 452 } 453} 454 455static void 456XawScrollbarDestroy(Widget w) 457{ 458 ScrollbarWidget sbw = (ScrollbarWidget)w; 459 460 XtReleaseGC(w, sbw->scrollbar.gc); 461} 462 463static void 464CreateGC(Widget w) 465{ 466 ScrollbarWidget sbw = (ScrollbarWidget)w; 467 XGCValues gcValues; 468 XtGCMask mask; 469 unsigned int depth = 1; 470 471 if (sbw->scrollbar.thumb == XtUnspecifiedPixmap) 472 sbw->scrollbar.thumb = XmuCreateStippledPixmap(XtScreen(w), 473 (Pixel)1, (Pixel)0, 474 depth); 475 else if (sbw->scrollbar.thumb != None) { 476 Window root; 477 int x, y; 478 unsigned int width, height, bw; 479 480 XGetGeometry(XtDisplay(w), sbw->scrollbar.thumb, &root, &x, &y, 481 &width, &height, &bw, &depth); 482 } 483 484 gcValues.foreground = sbw->scrollbar.foreground; 485 gcValues.background = sbw->core.background_pixel; 486 mask = GCForeground | GCBackground; 487 488 if (sbw->scrollbar.thumb != None) { 489 if (depth == 1) { 490 gcValues.fill_style = FillOpaqueStippled; 491 gcValues.stipple = sbw->scrollbar.thumb; 492 mask |= GCFillStyle | GCStipple; 493 } 494 else { 495 gcValues.fill_style = FillTiled; 496 gcValues.tile = sbw->scrollbar.thumb; 497 mask |= GCFillStyle | GCTile; 498 } 499 } 500 sbw->scrollbar.gc = XtGetGC(w, mask, &gcValues); 501} 502 503/* ARGSUSED */ 504static void 505XawScrollbarInitialize(Widget request _X_UNUSED, Widget cnew, 506 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 507{ 508 ScrollbarWidget w = (ScrollbarWidget)cnew; 509 510 CreateGC(cnew); 511 512 if (XtWidth(w) == 0) 513 XtWidth(w) = w->scrollbar.orientation == XtorientVertical ? 514 w->scrollbar.thickness : w->scrollbar.length; 515 516 if (XtHeight(w) == 0) 517 XtHeight(w) = w->scrollbar.orientation == XtorientHorizontal ? 518 w->scrollbar.thickness : w->scrollbar.length; 519 520 SetDimensions(w); 521 w->scrollbar.direction = 0; 522 w->scrollbar.topLoc = 0; 523 w->scrollbar.shownLength = w->scrollbar.min_thumb; 524} 525 526static void 527XawScrollbarRealize(Widget gw, Mask *valueMask, 528 XSetWindowAttributes *attributes) 529{ 530 ScrollbarWidget w = (ScrollbarWidget)gw; 531 532 w->scrollbar.inactiveCursor = w->scrollbar.orientation == XtorientVertical ? 533 w->scrollbar.verCursor : w->scrollbar.horCursor; 534 535 XtVaSetValues(gw, XtNcursor, w->scrollbar.inactiveCursor, NULL); 536 537 /* 538 * The Simple widget actually stuffs the value in the valuemask 539 */ 540 (*scrollbarWidgetClass->core_class.superclass->core_class.realize) 541 (gw, valueMask, attributes); 542} 543 544/*ARGSUSED*/ 545static Boolean 546XawScrollbarSetValues(Widget current, Widget request _X_UNUSED, Widget desired, 547 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 548{ 549 ScrollbarWidget w = (ScrollbarWidget)current; 550 ScrollbarWidget dw = (ScrollbarWidget)desired; 551 Boolean redraw = False; 552 553 /* 554 * If these values are outside the acceptable range ignore them... 555 */ 556 if (dw->scrollbar.top < 0.0 || dw->scrollbar.top > 1.0) 557 dw->scrollbar.top = w->scrollbar.top; 558 559 if (dw->scrollbar.shown < 0.0 || dw->scrollbar.shown > 1.0) 560 dw->scrollbar.shown = w->scrollbar.shown; 561 562 if (XtIsRealized (desired)) { 563 if (w->scrollbar.foreground != dw->scrollbar.foreground || 564 w->core.background_pixel != dw->core.background_pixel || 565 w->scrollbar.thumb != dw->scrollbar.thumb) { 566 XtReleaseGC((Widget)dw, w->scrollbar.gc); 567 CreateGC((Widget)dw); 568 redraw = True; 569 } 570 if (w->scrollbar.top != dw->scrollbar.top || 571 w->scrollbar.shown != dw->scrollbar.shown) 572 redraw = True; 573 } 574 575 return (redraw); 576} 577 578static void 579XawScrollbarResize(Widget gw) 580{ 581 /* ForgetGravity has taken care of background, but thumb may 582 * have to move as a result of the new size. */ 583 SetDimensions((ScrollbarWidget)gw); 584 XawScrollbarRedisplay(gw, NULL, NULL); 585} 586 587/*ARGSUSED*/ 588static void 589XawScrollbarRedisplay(Widget gw, XEvent *event, Region region) 590{ 591 ScrollbarWidget w = (ScrollbarWidget)gw; 592 int x, y; 593 unsigned int width, height; 594 595 if (Superclass->core_class.expose) 596 (*Superclass->core_class.expose)(gw, event, region); 597 598 if (w->scrollbar.orientation == XtorientHorizontal) { 599 x = w->scrollbar.topLoc; 600 y = 1; 601 width = (unsigned)(w->scrollbar.shownLength); 602 height = (unsigned)(XtHeight(w) - 2); 603 } 604 else { 605 x = 1; 606 y = w->scrollbar.topLoc; 607 width = (unsigned)(XtWidth(w) - 2); 608 height = (unsigned)(w->scrollbar.shownLength); 609 } 610 611 if (region == NULL || 612 XRectInRegion(region, x, y, width, height) != RectangleOut) { 613 /* Forces entire thumb to be painted */ 614 w->scrollbar.topLoc = (Position)(-(w->scrollbar.length + 1)); 615 PaintThumb(w); 616 } 617} 618 619/*ARGSUSED*/ 620static void 621StartScroll(Widget gw, XEvent *event _X_UNUSED, String *params, Cardinal *num_params) 622{ 623 ScrollbarWidget w = (ScrollbarWidget)gw; 624 Cursor cursor; 625 char direction; 626 627 if (w->scrollbar.direction != 0) /* if we're already scrolling */ 628 return; 629 if (*num_params > 0) 630 direction = *params[0]; 631 else 632 direction = 'C'; 633 634 w->scrollbar.direction = direction; 635 636 switch(direction) { 637 case 'B': 638 case 'b': 639 cursor = w->scrollbar.orientation == XtorientVertical ? 640 w->scrollbar.downCursor : w->scrollbar.rightCursor; 641 break; 642 case 'F': 643 case 'f': 644 cursor = w->scrollbar.orientation == XtorientVertical ? 645 w->scrollbar.upCursor : w->scrollbar.leftCursor; 646 break; 647 case 'C': 648 case 'c': 649 cursor = w->scrollbar.orientation == XtorientVertical ? 650 w->scrollbar.rightCursor : w->scrollbar.upCursor; 651 break; 652 default: 653 return; /* invalid invocation */ 654 } 655 656 XtVaSetValues(gw, XtNcursor, cursor, NULL); 657 658 XFlush(XtDisplay(w)); 659} 660 661static Boolean 662CompareEvents(XEvent *oldEvent, XEvent *newEvent) 663{ 664#define Check(field) if (newEvent->field != oldEvent->field) return (False) 665 666 Check(xany.display); 667 Check(xany.type); 668 Check(xany.window); 669 670 switch(newEvent->type) { 671 case MotionNotify: 672 Check(xmotion.state); 673 break; 674 case ButtonPress: 675 case ButtonRelease: 676 Check(xbutton.state); 677 Check(xbutton.button); 678 break; 679 case KeyPress: 680 case KeyRelease: 681 Check(xkey.state); 682 Check(xkey.keycode); 683 break; 684 case EnterNotify: 685 case LeaveNotify: 686 Check(xcrossing.mode); 687 Check(xcrossing.detail); 688 Check(xcrossing.state); 689 break; 690 } 691#undef Check 692 693 return (True); 694} 695 696struct EventData { 697 XEvent *oldEvent; 698 int count; 699}; 700 701static Bool 702PeekNotifyEvent(Display *dpy, XEvent *event, char *args) 703{ 704 struct EventData *eventData = (struct EventData*)args; 705 706 return (++eventData->count == QLength(dpy) /* since PeekIf blocks */ 707 || CompareEvents(event, eventData->oldEvent)); 708} 709 710static Bool 711LookAhead(Widget w, XEvent *event) 712{ 713 XEvent newEvent; 714 struct EventData eventData; 715 716 if (QLength(XtDisplay(w)) == 0) 717 return (False); 718 719 eventData.count = 0; 720 eventData.oldEvent = event; 721 722 XPeekIfEvent(XtDisplay(w), &newEvent, PeekNotifyEvent, (char*)&eventData); 723 724 if (CompareEvents(event, &newEvent)) 725 return (True); 726 727 return (False); 728} 729 730static void 731ExtractPosition(XEvent *event, Position *x, Position *y) 732{ 733 switch(event->type) { 734 case MotionNotify: 735 *x = (Position)event->xmotion.x; 736 *y = (Position)event->xmotion.y; 737 break; 738 case ButtonPress: 739 case ButtonRelease: 740 *x = (Position)event->xbutton.x; 741 *y = (Position)event->xbutton.y; 742 break; 743 case KeyPress: 744 case KeyRelease: 745 *x = (Position)event->xkey.x; 746 *y = (Position)event->xkey.y; 747 break; 748 case EnterNotify: 749 case LeaveNotify: 750 *x = (Position)event->xcrossing.x; 751 *y = (Position)event->xcrossing.y; 752 break; 753 default: 754 *x = 0; 755 *y = 0; 756 break; 757 } 758} 759 760static void 761NotifyScroll(Widget gw, XEvent *event, String *params, Cardinal *num_params) 762{ 763 ScrollbarWidget w = (ScrollbarWidget)gw; 764 long call_data = 0; 765 char style; 766 Position x, y; 767 768 if (w->scrollbar.direction == 0) /* if no StartScroll */ 769 return; 770 771 if (LookAhead(gw, event)) 772 return; 773 774 if (*num_params > 0) 775 style = *params[0]; 776 else 777 style = 'P'; 778 779 switch(style) { 780 case 'P': /* Proportional */ 781 case 'p': 782 ExtractPosition(event, &x, &y); 783 call_data = InRange(PICKLENGTH(w, x, y), 0, (int)w->scrollbar.length); 784 break; 785 case 'F': /* FullLength */ 786 case 'f': 787 call_data = w->scrollbar.length; 788 break; 789 } 790 791 switch(w->scrollbar.direction) { 792 case 'B': 793 case 'b': 794 call_data = -call_data; 795 /*FALLTHROUGH*/ 796 case 'F': 797 case 'f': 798 XtCallCallbacks(gw, XtNscrollProc, (XtPointer)call_data); 799 break; 800 case 'C': 801 case 'c': /* NotifyThumb has already called the thumbProc(s) */ 802 break; 803 } 804} 805 806/*ARGSUSED*/ 807static void 808EndScroll(Widget gw, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 809{ 810 ScrollbarWidget w = (ScrollbarWidget)gw; 811 812 XtVaSetValues(gw, XtNcursor, w->scrollbar.inactiveCursor, NULL); 813 XFlush(XtDisplay(w)); /* make sure it get propagated */ 814 815 w->scrollbar.direction = 0; 816} 817 818/*ARGSUSED*/ 819static void 820MoveThumb(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 821{ 822 ScrollbarWidget w = (ScrollbarWidget)gw; 823 Position x, y; 824 825 if (w->scrollbar.direction == 0) /* if no StartScroll */ 826 return; 827 828 if (LookAhead(gw, event)) 829 return; 830 831 if (!event->xmotion.same_screen) 832 return; 833 834 ExtractPosition(event, &x, &y); 835 w->scrollbar.top = FractionLoc(w, x, y); 836} 837 838/*ARGSUSED*/ 839static void 840NotifyThumb(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED) 841{ 842 ScrollbarWidget w = (ScrollbarWidget)gw; 843 union { 844 XtPointer xtp; 845 float xtf; 846 } xtpf; 847 848 if (w->scrollbar.direction == 0) /* if no StartScroll */ 849 return; 850 851 if (LookAhead(gw, event)) 852 return; 853 854 /* thumbProc is not pretty, but is necessary for backwards 855 compatibility on those architectures for which it work{s,ed}; 856 the intent is to pass a (truncated) float by value. */ 857 xtpf.xtf = w->scrollbar.top; 858 XtCallCallbacks(gw, XtNthumbProc, xtpf.xtp); 859 XtCallCallbacks(gw, XtNjumpProc, (XtPointer)&w->scrollbar.top); 860 861 PaintThumb(w); 862} 863 864/* 865 * Public routines 866 */ 867/* Set the scroll bar to the given location. */ 868void 869XawScrollbarSetThumb(Widget gw, 870#if NeedWidePrototypes 871 double top, double shown 872#else 873 float top, float shown 874#endif 875 ) 876{ 877 ScrollbarWidget w = (ScrollbarWidget)gw; 878 879 if (w->scrollbar.direction == 'c') /* if still thumbing */ 880 return; 881 882 w->scrollbar.top = (float)((top > 1.0) 883 ? 1.0 884 : ((top >= 0.0) 885 ? top 886 : w->scrollbar.top)); 887 888 w->scrollbar.shown = (float)((shown > 1.0) 889 ? 1.0 890 : (shown >= 0.0 891 ? shown 892 : w->scrollbar.shown)); 893 PaintThumb(w); 894} 895