1 /* vi:set ts=8 sts=4 sw=4 noet: */ 2 /* 3 * MODIFIED ATHENA SCROLLBAR (USING ARROWHEADS AT ENDS OF TRAVEL) 4 * Modifications Copyright 1992 by Mitch Trachtenberg 5 * Rights, permissions, and disclaimer of warranty are as in the DEC and MIT 6 * notice below. 7 * $XConsortium: Scrollbar.c,v 1.72 94/04/17 20:12:40 kaleb Exp $ 8 */ 9 10 /* 11 * Modified for Vim by Bill Foster and Bram Moolenaar 12 */ 13 14 /* 15 16 Copyright (c) 1987, 1988, 1994 X Consortium 17 18 Permission is hereby granted, free of charge, to any person obtaining a copy 19 of this software and associated documentation files (the "Software"), to deal 20 in the Software without restriction, including without limitation the rights 21 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 copies of the Software, and to permit persons to whom the Software is 23 furnished to do so, subject to the following conditions: 24 25 The above copyright notice and this permission notice shall be included in all 26 copies or substantial portions of the Software. 27 28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X 31 CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 32 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 33 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 35 Except as contained in this notice, the name of the X Consortium shall not be 36 used in advertising or otherwise to promote the sale, use or other dealings in 37 this Software without prior written authorization from the X Consortium. 38 39 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. 40 41 All Rights Reserved 42 43 Permission to use, copy, modify, and distribute this software and its 44 documentation for any purpose and without fee is hereby granted, provided that 45 the above copyright notice appear in all copies and that both that copyright 46 notice and this permission notice appear in supporting documentation, and that 47 the name of Digital not be used in advertising or publicity pertaining to 48 distribution of the software without specific, written prior permission. 49 50 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 51 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL 52 BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 53 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 54 OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 55 CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 56 57 */ 58 59 /* ScrollBar.c */ 60 /* created by weissman, Mon Jul 7 13:20:03 1986 */ 61 /* converted by swick, Thu Aug 27 1987 */ 62 63 #include "vim.h" 64 65 #include <X11/IntrinsicP.h> 66 #include <X11/StringDefs.h> 67 68 #include <X11/Xaw/XawInit.h> 69 #include "gui_at_sb.h" 70 71 #include <X11/Xmu/Drawing.h> 72 73 /* Private definitions. */ 74 75 static char defaultTranslations[] = 76 "<Btn1Down>: NotifyScroll()\n\ 77 <Btn2Down>: MoveThumb() NotifyThumb()\n\ 78 <Btn3Down>: NotifyScroll()\n\ 79 <Btn4Down>: ScrollOneLineUp()\n\ 80 Shift<Btn4Down>: ScrollPageUp()\n\ 81 <Btn5Down>: ScrollOneLineDown()\n\ 82 Shift<Btn5Down>: ScrollPageDown()\n\ 83 <Btn1Motion>: HandleThumb()\n\ 84 <Btn3Motion>: HandleThumb()\n\ 85 <Btn2Motion>: MoveThumb() NotifyThumb()\n\ 86 <BtnUp>: EndScroll()"; 87 88 static float floatZero = 0.0; 89 90 #define Offset(field) XtOffsetOf(ScrollbarRec, field) 91 92 static XtResource resources[] = 93 { 94 {XtNlength, XtCLength, XtRDimension, sizeof(Dimension), 95 Offset(scrollbar.length), XtRImmediate, (XtPointer) 1}, 96 {XtNthickness, XtCThickness, XtRDimension, sizeof(Dimension), 97 Offset(scrollbar.thickness), XtRImmediate, (XtPointer) 14}, 98 {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation), 99 Offset(scrollbar.orientation), XtRImmediate, (XtPointer) XtorientVertical}, 100 {XtNscrollProc, XtCCallback, XtRCallback, sizeof(XtPointer), 101 Offset(scrollbar.scrollProc), XtRCallback, NULL}, 102 {XtNthumbProc, XtCCallback, XtRCallback, sizeof(XtPointer), 103 Offset(scrollbar.thumbProc), XtRCallback, NULL}, 104 {XtNjumpProc, XtCCallback, XtRCallback, sizeof(XtPointer), 105 Offset(scrollbar.jumpProc), XtRCallback, NULL}, 106 {XtNthumb, XtCThumb, XtRBitmap, sizeof(Pixmap), 107 Offset(scrollbar.thumb), XtRImmediate, (XtPointer) XtUnspecifiedPixmap}, 108 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), 109 Offset(scrollbar.foreground), XtRString, XtDefaultForeground}, 110 {XtNshown, XtCShown, XtRFloat, sizeof(float), 111 Offset(scrollbar.shown), XtRFloat, (XtPointer)&floatZero}, 112 {XtNtopOfThumb, XtCTopOfThumb, XtRFloat, sizeof(float), 113 Offset(scrollbar.top), XtRFloat, (XtPointer)&floatZero}, 114 {XtNmaxOfThumb, XtCMaxOfThumb, XtRFloat, sizeof(float), 115 Offset(scrollbar.max), XtRFloat, (XtPointer)&floatZero}, 116 {XtNminimumThumb, XtCMinimumThumb, XtRDimension, sizeof(Dimension), 117 Offset(scrollbar.min_thumb), XtRImmediate, (XtPointer) 7}, 118 {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), 119 Offset(scrollbar.shadow_width), XtRImmediate, (XtPointer) 1}, 120 {XtNtopShadowPixel, XtCTopShadowPixel, XtRPixel, sizeof(Pixel), 121 Offset(scrollbar.top_shadow_pixel), XtRString, XtDefaultBackground}, 122 {XtNbottomShadowPixel, XtCBottomShadowPixel, XtRPixel, sizeof(Pixel), 123 Offset(scrollbar.bot_shadow_pixel), XtRString, XtDefaultForeground}, 124 {XtNlimitThumb, XtCLimitThumb, XtRBool, sizeof(Bool), 125 Offset(scrollbar.limit_thumb), XtRImmediate, (XtPointer)0} 126 }; 127 #undef Offset 128 129 static void ClassInitialize(void); 130 static void Initialize(Widget, Widget, ArgList, Cardinal *); 131 static void Destroy(Widget); 132 static void Realize(Widget, Mask *, XSetWindowAttributes *); 133 static void Resize(Widget); 134 static void Redisplay(Widget, XEvent *, Region); 135 static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal *); 136 137 static void HandleThumb(Widget, XEvent *, String *, Cardinal *); 138 static void MoveThumb(Widget, XEvent *, String *, Cardinal *); 139 static void NotifyThumb(Widget, XEvent *, String *, Cardinal *); 140 static void NotifyScroll(Widget, XEvent *, String *, Cardinal *); 141 static void EndScroll(Widget, XEvent *, String *, Cardinal *); 142 static void ScrollOneLineUp(Widget, XEvent *, String *, Cardinal *); 143 static void ScrollOneLineDown(Widget, XEvent *, String *, Cardinal *); 144 static void ScrollPageUp(Widget, XEvent *, String *, Cardinal *); 145 static void ScrollPageDown(Widget, XEvent *, String *, Cardinal *); 146 static void ScrollSome(Widget w, XEvent *event, int call_data); 147 static void _Xaw3dDrawShadows(Widget, XEvent *, Region, int); 148 static void AllocTopShadowGC(Widget); 149 static void AllocBotShadowGC(Widget); 150 151 static XtActionsRec actions[] = 152 { 153 {"HandleThumb", HandleThumb}, 154 {"MoveThumb", MoveThumb}, 155 {"NotifyThumb", NotifyThumb}, 156 {"NotifyScroll", NotifyScroll}, 157 {"EndScroll", EndScroll}, 158 {"ScrollOneLineUp", ScrollOneLineUp}, 159 {"ScrollOneLineDown", ScrollOneLineDown}, 160 {"ScrollPageUp", ScrollPageUp}, 161 {"ScrollPageDown", ScrollPageDown} 162 }; 163 164 165 ScrollbarClassRec vim_scrollbarClassRec = 166 { 167 { /* core fields */ 168 /* superclass */ (WidgetClass) &simpleClassRec, 169 /* class_name */ "Scrollbar", 170 /* size */ sizeof(ScrollbarRec), 171 /* class_initialize */ ClassInitialize, 172 /* class_part_init */ NULL, 173 /* class_inited */ FALSE, 174 /* initialize */ Initialize, 175 /* initialize_hook */ NULL, 176 /* realize */ Realize, 177 /* actions */ actions, 178 /* num_actions */ XtNumber(actions), 179 /* resources */ resources, 180 /* num_resources */ XtNumber(resources), 181 /* xrm_class */ NULLQUARK, 182 /* compress_motion */ TRUE, 183 /* compress_exposure*/ TRUE, 184 /* compress_enterleave*/ TRUE, 185 /* visible_interest */ FALSE, 186 /* destroy */ Destroy, 187 /* resize */ Resize, 188 /* expose */ Redisplay, 189 /* set_values */ SetValues, 190 /* set_values_hook */ NULL, 191 /* set_values_almost */ XtInheritSetValuesAlmost, 192 /* get_values_hook */ NULL, 193 /* accept_focus */ NULL, 194 /* version */ XtVersion, 195 /* callback_private */ NULL, 196 /* tm_table */ defaultTranslations, 197 /* query_geometry */ XtInheritQueryGeometry, 198 /* display_accelerator*/ XtInheritDisplayAccelerator, 199 /* extension */ NULL 200 }, 201 { /* simple fields */ 202 /* change_sensitive */ XtInheritChangeSensitive, 203 #ifndef OLDXAW 204 /* extension */ NULL 205 #endif 206 }, 207 { /* scrollbar fields */ 208 /* empty */ 0 209 } 210 }; 211 212 WidgetClass vim_scrollbarWidgetClass = (WidgetClass)&vim_scrollbarClassRec; 213 214 #define NoButton -1 215 #define PICKLENGTH(widget, x, y) \ 216 ((widget->scrollbar.orientation == XtorientHorizontal) ? (x) : (y)) 217 #define AT_MIN(x,y) ((x) < (y) ? (x) : (y)) 218 #define AT_MAX(x,y) ((x) > (y) ? (x) : (y)) 219 220 #define LINE_DELAY 300 221 #define PAGE_DELAY 300 222 #define LINE_REPEAT 50 223 #define PAGE_REPEAT 250 224 225 static void 226 ClassInitialize(void) 227 { 228 XawInitializeWidgetSet(); 229 XtAddConverter( XtRString, XtROrientation, XmuCvtStringToOrientation, 230 (XtConvertArgList)NULL, (Cardinal)0 ); 231 } 232 233 #define MARGIN(sbw) (sbw)->scrollbar.thickness + (sbw)->scrollbar.shadow_width 234 235 static void 236 FillArea( 237 ScrollbarWidget sbw, 238 Position top, 239 Position bottom, 240 int fill, 241 int draw_shadow) 242 { 243 int tlen = bottom - top; /* length of thumb in pixels */ 244 int sw, margin, floor; 245 int lx, ly, lw, lh; 246 247 if (bottom <= 0 || bottom <= top) 248 return; 249 sw = sbw->scrollbar.shadow_width; 250 if (sw < 0) 251 sw = 0; 252 margin = MARGIN (sbw); 253 floor = sbw->scrollbar.length - margin + 2; 254 255 if (sbw->scrollbar.orientation == XtorientHorizontal) 256 { 257 lx = ((top < margin) ? margin : top); 258 ly = sw; 259 lw = (((top + tlen) > floor) ? floor - top : tlen); 260 lh = sbw->core.height - 2 * sw; 261 } 262 else 263 { 264 lx = sw; 265 ly = ((top < margin) ? margin : top); 266 lw = sbw->core.width - 2 * sw; 267 lh = (((top + tlen) > floor) ? floor - top : tlen); 268 } 269 if (lh <= 0 || lw <= 0) 270 return; 271 272 if (draw_shadow) 273 { 274 if (!(sbw->scrollbar.orientation == XtorientHorizontal)) 275 { 276 /* Top border */ 277 XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 278 sbw->scrollbar.top_shadow_GC, 279 lx, ly, lx + lw - 1, ly); 280 281 /* Bottom border */ 282 XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 283 sbw->scrollbar.bot_shadow_GC, 284 lx, ly + lh - 1, lx + lw - 1, ly + lh - 1); 285 } 286 else 287 { 288 /* Left border */ 289 XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 290 sbw->scrollbar.top_shadow_GC, 291 lx, ly, lx, ly + lh - 1); 292 293 /* Right border */ 294 XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 295 sbw->scrollbar.bot_shadow_GC, 296 lx + lw - 1, ly, lx + lw - 1, ly + lh - 1); 297 } 298 return; 299 } 300 301 if (fill) 302 { 303 XFillRectangle(XtDisplay((Widget) sbw), XtWindow((Widget) sbw), 304 sbw->scrollbar.gc, 305 lx, ly, (unsigned int) lw, (unsigned int) lh); 306 307 if (!(sbw->scrollbar.orientation == XtorientHorizontal)) 308 { 309 /* Left border */ 310 XDrawLine(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 311 sbw->scrollbar.top_shadow_GC, 312 lx, ly, lx, ly + lh - 1); 313 314 /* Right border */ 315 XDrawLine(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 316 sbw->scrollbar.bot_shadow_GC, 317 lx + lw - 1, ly, lx + lw - 1, ly + lh - 1); 318 } 319 else 320 { 321 /* Top border */ 322 XDrawLine(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 323 sbw->scrollbar.top_shadow_GC, 324 lx, ly, lx + lw - 1, ly); 325 326 /* Bottom border */ 327 XDrawLine(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 328 sbw->scrollbar.bot_shadow_GC, 329 lx, ly + lh - 1, lx + lw - 1, ly + lh - 1); 330 } 331 } 332 else 333 { 334 XClearArea(XtDisplay((Widget) sbw), XtWindow((Widget) sbw), 335 lx, ly, (unsigned int) lw, (unsigned int) lh, FALSE); 336 } 337 } 338 339 /* Paint the thumb in the area specified by sbw->top and 340 sbw->shown. The old area is erased. The painting and 341 erasing is done cleverly so that no flickering will occur. 342 */ 343 344 static void 345 PaintThumb(ScrollbarWidget sbw) 346 { 347 Position oldtop, oldbot, newtop, newbot; 348 Dimension margin, tzl; 349 350 margin = MARGIN (sbw); 351 tzl = sbw->scrollbar.length - 2 * margin; 352 newtop = margin + (int)(tzl * sbw->scrollbar.top); 353 newbot = newtop + (int)(tzl * sbw->scrollbar.shown) + 1; 354 if (newbot < newtop + (int)sbw->scrollbar.min_thumb) 355 newbot = newtop + sbw->scrollbar.min_thumb; 356 357 oldtop = sbw->scrollbar.topLoc; 358 oldbot = oldtop + sbw->scrollbar.shownLength; 359 sbw->scrollbar.topLoc = newtop; 360 sbw->scrollbar.shownLength = newbot - newtop; 361 if (XtIsRealized ((Widget) sbw)) 362 { 363 if (newtop < oldtop) 364 FillArea(sbw, newtop, AT_MIN(newbot, oldtop+1),1,0); 365 if (newtop > oldtop) 366 FillArea(sbw, oldtop, AT_MIN(newtop, oldbot ),0,0); 367 if (newbot < oldbot) 368 FillArea(sbw, AT_MAX(newbot, oldtop), oldbot, 0,0); 369 if (newbot > oldbot) 370 FillArea(sbw, AT_MAX(newtop, oldbot-1), newbot, 1,0); 371 372 /* Only draw the missing shadows */ 373 FillArea(sbw, newtop, newbot, 0, 1); 374 } 375 } 376 377 static void 378 PaintArrows(ScrollbarWidget sbw) 379 { 380 XPoint point[6]; 381 Dimension thickness = sbw->scrollbar.thickness - 1; 382 Dimension size; 383 Dimension off; 384 385 if (XtIsRealized((Widget) sbw)) 386 { 387 if ((int)thickness * 2 > (int)sbw->scrollbar.length) 388 { 389 size = sbw->scrollbar.length / 2; 390 off = (int)(thickness - size) / 2; 391 } 392 else 393 { 394 size = thickness; 395 off = 0; 396 } 397 point[0].x = off + sbw->scrollbar.shadow_width; 398 point[0].y = size; 399 point[1].x = thickness - off - sbw->scrollbar.shadow_width; 400 point[1].y = size; 401 point[2].x = thickness / 2; 402 point[2].y = sbw->scrollbar.shadow_width; 403 404 point[3].x = off + sbw->scrollbar.shadow_width; 405 point[3].y = sbw->scrollbar.length - size; 406 point[4].x = thickness - off - sbw->scrollbar.shadow_width; 407 point[4].y = sbw->scrollbar.length - size; 408 point[5].x = thickness / 2; 409 point[5].y = sbw->scrollbar.length - sbw->scrollbar.shadow_width - 1; 410 411 /* horizontal arrows require that x and y coordinates be swapped */ 412 if (sbw->scrollbar.orientation == XtorientHorizontal) 413 { 414 int n; 415 int swap; 416 for (n = 0; n < 6; n++) 417 { 418 swap = point[n].x; 419 point[n].x = point[n].y; 420 point[n].y = swap; 421 } 422 } 423 /* draw the up/left arrow */ 424 XFillPolygon (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 425 sbw->scrollbar.gc, 426 point, 3, 427 Convex, CoordModeOrigin); 428 XDrawLines (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 429 sbw->scrollbar.bot_shadow_GC, 430 point, 3, 431 CoordModeOrigin); 432 XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 433 sbw->scrollbar.top_shadow_GC, 434 point[0].x, point[0].y, 435 point[2].x, point[2].y); 436 /* draw the down/right arrow */ 437 XFillPolygon (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 438 sbw->scrollbar.gc, 439 point+3, 3, 440 Convex, CoordModeOrigin); 441 XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 442 sbw->scrollbar.top_shadow_GC, 443 point[3].x, point[3].y, 444 point[4].x, point[4].y); 445 XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 446 sbw->scrollbar.top_shadow_GC, 447 point[3].x, point[3].y, 448 point[5].x, point[5].y); 449 XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw), 450 sbw->scrollbar.bot_shadow_GC, 451 point[4].x, point[4].y, 452 point[5].x, point[5].y); 453 } 454 } 455 456 static void 457 Destroy(Widget w) 458 { 459 ScrollbarWidget sbw = (ScrollbarWidget) w; 460 if (sbw->scrollbar.timer_id != (XtIntervalId) 0) 461 XtRemoveTimeOut (sbw->scrollbar.timer_id); 462 XtReleaseGC(w, sbw->scrollbar.gc); 463 XtReleaseGC(w, sbw->scrollbar.top_shadow_GC); 464 XtReleaseGC(w, sbw->scrollbar.bot_shadow_GC); 465 } 466 467 static void 468 CreateGC(Widget w) 469 { 470 ScrollbarWidget sbw = (ScrollbarWidget) w; 471 XGCValues gcValues; 472 XtGCMask mask; 473 unsigned int depth = 1; 474 475 if (sbw->scrollbar.thumb == XtUnspecifiedPixmap) 476 { 477 sbw->scrollbar.thumb = XmuCreateStippledPixmap (XtScreen(w), 478 (Pixel) 1, (Pixel) 0, depth); 479 } 480 else if (sbw->scrollbar.thumb != None) 481 { 482 Window root; 483 int x, y; 484 unsigned int width, height, bw; 485 486 if (XGetGeometry (XtDisplay(w), sbw->scrollbar.thumb, &root, &x, &y, 487 &width, &height, &bw, &depth) == 0) 488 emsg(_("Scrollbar Widget: Could not get geometry of thumb pixmap.")); 489 } 490 491 gcValues.foreground = sbw->scrollbar.foreground; 492 gcValues.background = sbw->core.background_pixel; 493 mask = GCForeground | GCBackground; 494 495 if (sbw->scrollbar.thumb != None) 496 { 497 gcValues.fill_style = FillSolid; 498 mask |= GCFillStyle; 499 } 500 /* the creation should be non-caching, because */ 501 /* we now set and clear clip masks on the gc returned */ 502 sbw->scrollbar.gc = XtGetGC (w, mask, &gcValues); 503 } 504 505 static void 506 SetDimensions(ScrollbarWidget sbw) 507 { 508 if (sbw->scrollbar.orientation == XtorientVertical) 509 { 510 sbw->scrollbar.length = sbw->core.height; 511 sbw->scrollbar.thickness = sbw->core.width; 512 } 513 else 514 { 515 sbw->scrollbar.length = sbw->core.width; 516 sbw->scrollbar.thickness = sbw->core.height; 517 } 518 } 519 520 static void 521 Initialize( 522 Widget request UNUSED, /* what the client asked for */ 523 Widget new, /* what we're going to give him */ 524 ArgList args UNUSED, 525 Cardinal *num_args UNUSED) 526 { 527 ScrollbarWidget sbw = (ScrollbarWidget) new; 528 529 CreateGC(new); 530 AllocTopShadowGC(new); 531 AllocBotShadowGC(new); 532 533 if (sbw->core.width == 0) 534 sbw->core.width = (sbw->scrollbar.orientation == XtorientVertical) 535 ? sbw->scrollbar.thickness : sbw->scrollbar.length; 536 537 if (sbw->core.height == 0) 538 sbw->core.height = (sbw->scrollbar.orientation == XtorientHorizontal) 539 ? sbw->scrollbar.thickness : sbw->scrollbar.length; 540 541 SetDimensions(sbw); 542 sbw->scrollbar.scroll_mode = SMODE_NONE; 543 sbw->scrollbar.timer_id = (XtIntervalId)0; 544 sbw->scrollbar.topLoc = 0; 545 sbw->scrollbar.shownLength = sbw->scrollbar.min_thumb; 546 } 547 548 static void 549 Realize( 550 Widget w, 551 Mask *valueMask, 552 XSetWindowAttributes *attributes) 553 { 554 /* The Simple widget actually stuffs the value in the valuemask. */ 555 (*vim_scrollbarWidgetClass->core_class.superclass->core_class.realize) 556 (w, valueMask, attributes); 557 } 558 559 static Boolean 560 SetValues( 561 Widget current, /* what I am */ 562 Widget request UNUSED, /* what he wants me to be */ 563 Widget desired, /* what I will become */ 564 ArgList args UNUSED, 565 Cardinal *num_args UNUSED) 566 { 567 ScrollbarWidget sbw = (ScrollbarWidget) current; 568 ScrollbarWidget dsbw = (ScrollbarWidget) desired; 569 Boolean redraw = FALSE; 570 571 /* 572 * If these values are outside the acceptable range ignore them... 573 */ 574 if (dsbw->scrollbar.top < 0.0 || dsbw->scrollbar.top > 1.0) 575 dsbw->scrollbar.top = sbw->scrollbar.top; 576 577 if (dsbw->scrollbar.shown < 0.0 || dsbw->scrollbar.shown > 1.0) 578 dsbw->scrollbar.shown = sbw->scrollbar.shown; 579 580 /* 581 * Change colors and stuff... 582 */ 583 if (XtIsRealized(desired)) 584 { 585 if (sbw->scrollbar.foreground != dsbw->scrollbar.foreground || 586 sbw->core.background_pixel != dsbw->core.background_pixel || 587 sbw->scrollbar.thumb != dsbw->scrollbar.thumb) 588 { 589 XtReleaseGC(desired, sbw->scrollbar.gc); 590 CreateGC (desired); 591 redraw = TRUE; 592 } 593 if (sbw->scrollbar.top != dsbw->scrollbar.top || 594 sbw->scrollbar.shown != dsbw->scrollbar.shown) 595 redraw = TRUE; 596 } 597 return redraw; 598 } 599 600 static void 601 Resize(Widget w) 602 { 603 /* ForgetGravity has taken care of background, but thumb may 604 * have to move as a result of the new size. */ 605 SetDimensions ((ScrollbarWidget) w); 606 Redisplay(w, (XEvent*) NULL, (Region)NULL); 607 } 608 609 610 static void 611 Redisplay(Widget w, XEvent *event, Region region) 612 { 613 ScrollbarWidget sbw = (ScrollbarWidget) w; 614 int x, y; 615 unsigned int width, height; 616 617 _Xaw3dDrawShadows(w, event, region, FALSE); 618 619 if (sbw->scrollbar.orientation == XtorientHorizontal) 620 { 621 x = sbw->scrollbar.topLoc; 622 y = 1; 623 width = sbw->scrollbar.shownLength; 624 height = sbw->core.height - 2; 625 } 626 else 627 { 628 x = 1; 629 y = sbw->scrollbar.topLoc; 630 width = sbw->core.width - 2; 631 height = sbw->scrollbar.shownLength; 632 } 633 if (region == NULL || 634 XRectInRegion (region, x, y, width, height) != RectangleOut) 635 { 636 /* Forces entire thumb to be painted. */ 637 sbw->scrollbar.topLoc = -(sbw->scrollbar.length + 1); 638 PaintThumb (sbw); 639 } 640 /* we'd like to be region aware here!!!! */ 641 PaintArrows(sbw); 642 } 643 644 645 static Boolean 646 CompareEvents(XEvent *oldEvent, XEvent *newEvent) 647 { 648 #define Check(field) \ 649 do { \ 650 if (newEvent->field != oldEvent->field) \ 651 return False; \ 652 } while (0) 653 654 Check(xany.display); 655 Check(xany.type); 656 Check(xany.window); 657 658 switch (newEvent->type) 659 { 660 case MotionNotify: 661 Check(xmotion.state); 662 break; 663 case ButtonPress: 664 case ButtonRelease: 665 Check(xbutton.state); 666 Check(xbutton.button); 667 break; 668 case KeyPress: 669 case KeyRelease: 670 Check(xkey.state); 671 Check(xkey.keycode); 672 break; 673 case EnterNotify: 674 case LeaveNotify: 675 Check(xcrossing.mode); 676 Check(xcrossing.detail); 677 Check(xcrossing.state); 678 break; 679 } 680 #undef Check 681 682 return True; 683 } 684 685 struct EventData 686 { 687 XEvent *oldEvent; 688 int count; 689 }; 690 691 static Bool 692 PeekNotifyEvent(Display *dpy, XEvent *event, char *args) 693 { 694 struct EventData *eventData = (struct EventData*)args; 695 696 return ((++eventData->count == QLength(dpy)) /* since PeekIf blocks */ 697 || CompareEvents(event, eventData->oldEvent)); 698 } 699 700 701 static Boolean 702 LookAhead(Widget w, XEvent *event) 703 { 704 XEvent newEvent; 705 struct EventData eventData; 706 707 if (QLength (XtDisplay (w)) == 0) 708 return False; 709 710 eventData.count = 0; 711 eventData.oldEvent = event; 712 713 XPeekIfEvent (XtDisplay (w), &newEvent, PeekNotifyEvent, (char*)&eventData); 714 715 return CompareEvents (event, &newEvent); 716 } 717 718 719 static void 720 ExtractPosition( 721 XEvent *event, 722 Position *x, /* RETURN */ 723 Position *y, /* RETURN */ 724 unsigned int *state) /* RETURN */ 725 { 726 switch (event->type) 727 { 728 case MotionNotify: 729 *x = event->xmotion.x; 730 *y = event->xmotion.y; 731 if (state != NULL) 732 *state = event->xmotion.state; 733 break; 734 case ButtonPress: 735 case ButtonRelease: 736 *x = event->xbutton.x; 737 *y = event->xbutton.y; 738 if (state != NULL) 739 *state = event->xbutton.state; 740 break; 741 case KeyPress: 742 case KeyRelease: 743 *x = event->xkey.x; 744 *y = event->xkey.y; 745 if (state != NULL) 746 *state = event->xkey.state; 747 break; 748 case EnterNotify: 749 case LeaveNotify: 750 *x = event->xcrossing.x; 751 *y = event->xcrossing.y; 752 if (state != NULL) 753 *state = event->xcrossing.state; 754 break; 755 default: 756 *x = 0; *y = 0; 757 if (state != NULL) 758 *state = 0; 759 } 760 } 761 762 static void 763 HandleThumb( 764 Widget w, 765 XEvent *event, 766 String *params, 767 Cardinal *num_params) 768 { 769 Position x, y, loc; 770 ScrollbarWidget sbw = (ScrollbarWidget) w; 771 772 ExtractPosition(event, &x, &y, (unsigned int *)NULL); 773 loc = PICKLENGTH(sbw, x, y); 774 /* if the motion event puts the pointer in thumb, call Move and Notify */ 775 /* also call Move and Notify if we're already in continuous scroll mode */ 776 if (sbw->scrollbar.scroll_mode == SMODE_CONT || 777 (loc >= sbw->scrollbar.topLoc && 778 loc <= sbw->scrollbar.topLoc + (int)sbw->scrollbar.shownLength)) 779 { 780 XtCallActionProc(w, "MoveThumb", event, params, *num_params); 781 XtCallActionProc(w, "NotifyThumb", event, params, *num_params); 782 } 783 } 784 785 static void 786 RepeatNotify(XtPointer client_data, XtIntervalId *idp UNUSED) 787 { 788 ScrollbarWidget sbw = (ScrollbarWidget) client_data; 789 int call_data; 790 char mode = sbw->scrollbar.scroll_mode; 791 unsigned long rep; 792 793 if (mode == SMODE_NONE || mode == SMODE_CONT) 794 { 795 sbw->scrollbar.timer_id = (XtIntervalId)0; 796 return; 797 } 798 799 if (mode == SMODE_LINE_DOWN || mode == SMODE_LINE_UP) 800 { 801 call_data = ONE_LINE_DATA; 802 rep = LINE_REPEAT; 803 } 804 else 805 { 806 call_data = ONE_PAGE_DATA; 807 rep = PAGE_REPEAT; 808 } 809 810 if (mode == SMODE_PAGE_UP || mode == SMODE_LINE_UP) 811 call_data = -call_data; 812 813 XtCallCallbacks((Widget)sbw, XtNscrollProc, (XtPointer)(long_u)call_data); 814 815 sbw->scrollbar.timer_id = 816 XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)sbw), 817 rep, 818 RepeatNotify, 819 client_data); 820 } 821 822 /* 823 * Same as above, but for floating numbers. 824 */ 825 static float 826 FloatInRange(float num, float small, float big) 827 { 828 return (num < small) ? small : ((num > big) ? big : num); 829 } 830 831 static void 832 ScrollOneLineUp( 833 Widget w, 834 XEvent *event, 835 String *params UNUSED, 836 Cardinal *num_params UNUSED) 837 { 838 ScrollSome(w, event, -ONE_LINE_DATA); 839 } 840 841 static void 842 ScrollOneLineDown( 843 Widget w, 844 XEvent *event, 845 String *params UNUSED, 846 Cardinal *num_params UNUSED) 847 { 848 ScrollSome(w, event, ONE_LINE_DATA); 849 } 850 851 static void 852 ScrollPageDown( 853 Widget w, 854 XEvent *event, 855 String *params UNUSED, 856 Cardinal *num_params UNUSED) 857 { 858 ScrollSome(w, event, ONE_PAGE_DATA); 859 } 860 861 static void 862 ScrollPageUp( 863 Widget w, 864 XEvent *event, 865 String *params UNUSED, 866 Cardinal *num_params UNUSED) 867 { 868 ScrollSome(w, event, -ONE_PAGE_DATA); 869 } 870 871 static void 872 ScrollSome( 873 Widget w, 874 XEvent *event, 875 int call_data) 876 { 877 ScrollbarWidget sbw = (ScrollbarWidget) w; 878 879 if (sbw->scrollbar.scroll_mode == SMODE_CONT) /* if scroll continuous */ 880 return; 881 882 if (LookAhead(w, event)) 883 return; 884 885 sbw->scrollbar.scroll_mode = SMODE_LINE_UP; 886 XtCallCallbacks(w, XtNscrollProc, (XtPointer)(long_u)call_data); 887 } 888 889 static void 890 NotifyScroll( 891 Widget w, 892 XEvent *event, 893 String *params UNUSED, 894 Cardinal *num_params UNUSED) 895 { 896 ScrollbarWidget sbw = (ScrollbarWidget) w; 897 Position x, y, loc; 898 Dimension arrow_size; 899 unsigned long delay = 0; 900 int call_data = 0; 901 unsigned int state; 902 903 if (sbw->scrollbar.scroll_mode == SMODE_CONT) /* if scroll continuous */ 904 return; 905 906 if (LookAhead (w, event)) 907 return; 908 909 ExtractPosition(event, &x, &y, &state); 910 loc = PICKLENGTH(sbw, x, y); 911 912 if ((int)sbw->scrollbar.thickness * 2 > (int)sbw->scrollbar.length) 913 arrow_size = sbw->scrollbar.length / 2; 914 else 915 arrow_size = sbw->scrollbar.thickness; 916 917 /* 918 * handle CTRL modifier 919 */ 920 if (state & ControlMask) 921 { 922 if (loc > sbw->scrollbar.topLoc + (Position)sbw->scrollbar.shownLength) 923 call_data = END_PAGE_DATA; 924 else 925 call_data = -END_PAGE_DATA; 926 sbw->scrollbar.scroll_mode = SMODE_NONE; 927 } 928 /* 929 * handle first arrow zone 930 */ 931 else if (loc < (Position)arrow_size) 932 { 933 call_data = -ONE_LINE_DATA; 934 sbw->scrollbar.scroll_mode = SMODE_LINE_UP; 935 delay = LINE_DELAY; 936 } 937 938 /* 939 * handle last arrow zone 940 */ 941 else if (loc > (Position)(sbw->scrollbar.length - arrow_size)) 942 { 943 call_data = ONE_LINE_DATA; 944 sbw->scrollbar.scroll_mode = SMODE_LINE_DOWN; 945 delay = LINE_DELAY; 946 } 947 948 /* 949 * handle zone "above" the thumb 950 */ 951 else if (loc < sbw->scrollbar.topLoc) 952 { 953 call_data = -ONE_PAGE_DATA; 954 sbw->scrollbar.scroll_mode = SMODE_PAGE_UP; 955 delay = PAGE_DELAY; 956 } 957 958 /* 959 * handle zone "below" the thumb 960 */ 961 else if (loc > sbw->scrollbar.topLoc + (Position)sbw->scrollbar.shownLength) 962 { 963 call_data = ONE_PAGE_DATA; 964 sbw->scrollbar.scroll_mode = SMODE_PAGE_DOWN; 965 delay = PAGE_DELAY; 966 } 967 968 if (call_data) 969 XtCallCallbacks(w, XtNscrollProc, (XtPointer)(long_u)call_data); 970 971 /* establish autoscroll */ 972 if (delay) 973 sbw->scrollbar.timer_id = 974 XtAppAddTimeOut(XtWidgetToApplicationContext(w), 975 delay, RepeatNotify, (XtPointer)w); 976 } 977 978 static void 979 EndScroll( 980 Widget w, 981 XEvent *event UNUSED, 982 String *params UNUSED, 983 Cardinal *num_params UNUSED) 984 { 985 ScrollbarWidget sbw = (ScrollbarWidget) w; 986 987 sbw->scrollbar.scroll_mode = SMODE_NONE; 988 /* no need to remove any autoscroll timeout; it will no-op */ 989 /* because the scroll_mode is SMODE_NONE */ 990 /* but be sure to remove timeout in destroy proc */ 991 } 992 993 static float 994 FractionLoc(ScrollbarWidget sbw, int x, int y) 995 { 996 int margin; 997 float height, width; 998 999 margin = MARGIN(sbw); 1000 x -= margin; 1001 y -= margin; 1002 height = (float)sbw->core.height - 2 * margin; 1003 width = (float)sbw->core.width - 2 * margin; 1004 return PICKLENGTH(sbw, x / width, y / height); 1005 } 1006 1007 static void 1008 MoveThumb( 1009 Widget w, 1010 XEvent *event, 1011 String *params UNUSED, 1012 Cardinal *num_params UNUSED) 1013 { 1014 ScrollbarWidget sbw = (ScrollbarWidget)w; 1015 Position x, y; 1016 float top; 1017 char old_mode = sbw->scrollbar.scroll_mode; 1018 1019 sbw->scrollbar.scroll_mode = SMODE_CONT; /* indicate continuous scroll */ 1020 1021 if (LookAhead(w, event)) 1022 return; 1023 1024 if (!event->xmotion.same_screen) 1025 return; 1026 1027 ExtractPosition(event, &x, &y, (unsigned int *)NULL); 1028 1029 top = FractionLoc(sbw, x, y); 1030 1031 if (old_mode != SMODE_CONT) /* start dragging: set offset */ 1032 { 1033 if (event->xbutton.button == Button2) 1034 sbw->scrollbar.scroll_off = sbw->scrollbar.shown / 2.; 1035 else 1036 sbw->scrollbar.scroll_off = top - sbw->scrollbar.top; 1037 } 1038 1039 top -= sbw->scrollbar.scroll_off; 1040 if (sbw->scrollbar.limit_thumb) 1041 top = FloatInRange(top, 0.0, 1042 sbw->scrollbar.max - sbw->scrollbar.shown + 0.000001); 1043 else 1044 top = FloatInRange(top, 0.0, sbw->scrollbar.max); 1045 1046 sbw->scrollbar.top = top; 1047 PaintThumb(sbw); 1048 XFlush(XtDisplay(w)); /* re-draw it before Notifying */ 1049 } 1050 1051 1052 static void 1053 NotifyThumb( 1054 Widget w, 1055 XEvent *event, 1056 String *params UNUSED, 1057 Cardinal *num_params UNUSED) 1058 { 1059 ScrollbarWidget sbw = (ScrollbarWidget)w; 1060 /* Use a union to avoid a warning for the weird conversion from float to 1061 * XtPointer. Comes from Xaw/Scrollbar.c. */ 1062 union { 1063 XtPointer xtp; 1064 float xtf; 1065 } xtpf; 1066 1067 if (LookAhead(w, event)) 1068 return; 1069 1070 /* thumbProc is not pretty, but is necessary for backwards 1071 compatibility on those architectures for which it work{s,ed}; 1072 the intent is to pass a (truncated) float by value. */ 1073 xtpf.xtf = sbw->scrollbar.top; 1074 XtCallCallbacks(w, XtNthumbProc, xtpf.xtp); 1075 XtCallCallbacks(w, XtNjumpProc, (XtPointer)&sbw->scrollbar.top); 1076 } 1077 1078 static void 1079 AllocTopShadowGC(Widget w) 1080 { 1081 ScrollbarWidget sbw = (ScrollbarWidget) w; 1082 XtGCMask valuemask; 1083 XGCValues myXGCV; 1084 1085 valuemask = GCForeground; 1086 myXGCV.foreground = sbw->scrollbar.top_shadow_pixel; 1087 sbw->scrollbar.top_shadow_GC = XtGetGC(w, valuemask, &myXGCV); 1088 } 1089 1090 static void 1091 AllocBotShadowGC(Widget w) 1092 { 1093 ScrollbarWidget sbw = (ScrollbarWidget) w; 1094 XtGCMask valuemask; 1095 XGCValues myXGCV; 1096 1097 valuemask = GCForeground; 1098 myXGCV.foreground = sbw->scrollbar.bot_shadow_pixel; 1099 sbw->scrollbar.bot_shadow_GC = XtGetGC(w, valuemask, &myXGCV); 1100 } 1101 1102 static void 1103 _Xaw3dDrawShadows( 1104 Widget gw, 1105 XEvent *event UNUSED, 1106 Region region, 1107 int out) 1108 { 1109 XPoint pt[6]; 1110 ScrollbarWidget sbw = (ScrollbarWidget) gw; 1111 Dimension s = sbw->scrollbar.shadow_width; 1112 /* 1113 * draw the shadows using the core part width and height, 1114 * and the scrollbar part shadow_width. 1115 * 1116 * no point to do anything if the shadow_width is 0 or the 1117 * widget has not been realized. 1118 */ 1119 if (s > 0 && XtIsRealized(gw)) 1120 { 1121 Dimension h = sbw->core.height; 1122 Dimension w = sbw->core.width; 1123 Dimension wms = w - s; 1124 Dimension hms = h - s; 1125 Display *dpy = XtDisplay (gw); 1126 Window win = XtWindow (gw); 1127 GC top, bot; 1128 1129 if (out) 1130 { 1131 top = sbw->scrollbar.top_shadow_GC; 1132 bot = sbw->scrollbar.bot_shadow_GC; 1133 } 1134 else 1135 { 1136 top = sbw->scrollbar.bot_shadow_GC; 1137 bot = sbw->scrollbar.top_shadow_GC; 1138 } 1139 1140 /* top-left shadow */ 1141 if ((region == NULL) || 1142 (XRectInRegion (region, 0, 0, w, s) != RectangleOut) || 1143 (XRectInRegion (region, 0, 0, s, h) != RectangleOut)) 1144 { 1145 pt[0].x = 0; pt[0].y = h; 1146 pt[1].x = pt[1].y = 0; 1147 pt[2].x = w; pt[2].y = 0; 1148 pt[3].x = wms; pt[3].y = s; 1149 pt[4].x = pt[4].y = s; 1150 pt[5].x = s; pt[5].y = hms; 1151 XFillPolygon (dpy, win, top, pt, 6, Complex, CoordModeOrigin); 1152 } 1153 1154 /* bottom-right shadow */ 1155 if ((region == NULL) || 1156 (XRectInRegion (region, 0, hms, w, s) != RectangleOut) || 1157 (XRectInRegion (region, wms, 0, s, h) != RectangleOut)) 1158 { 1159 pt[0].x = 0; pt[0].y = h; 1160 pt[1].x = w; pt[1].y = h; 1161 pt[2].x = w; pt[2].y = 0; 1162 pt[3].x = wms; pt[3].y = s; 1163 pt[4].x = wms; pt[4].y = hms; 1164 pt[5].x = s; pt[5].y = hms; 1165 XFillPolygon (dpy, win, bot, pt, 6, Complex, CoordModeOrigin); 1166 } 1167 } 1168 } 1169 1170 1171 /* 1172 * Set the scroll bar to the given location. 1173 */ 1174 void 1175 vim_XawScrollbarSetThumb(Widget w, double top, double shown, double max) 1176 { 1177 ScrollbarWidget sbw = (ScrollbarWidget) w; 1178 1179 if (sbw->scrollbar.scroll_mode == SMODE_CONT) /* if still thumbing */ 1180 return; 1181 1182 sbw->scrollbar.max = (max > 1.0) ? 1.0 : 1183 (max >= 0.0) ? max : sbw->scrollbar.max; 1184 1185 sbw->scrollbar.top = (top > sbw->scrollbar.max) ? sbw->scrollbar.max : 1186 (top >= 0.0) ? top : sbw->scrollbar.top; 1187 1188 sbw->scrollbar.shown = (shown > 1.0) ? 1.0 : 1189 (shown >= 0.0) ? shown : sbw->scrollbar.shown; 1190 1191 PaintThumb(sbw); 1192 } 1193