1 /*
2  * IceWM
3  *
4  * Copyright (C) 1997-2002 Marko Macek
5  *
6  * ScrollBar
7  */
8 #include "config.h"
9 #include "yscrollbar.h"
10 #include "yxapp.h"
11 #include "yprefs.h"
12 #include "prefs.h"
13 
14 static YColorName scrollBarBg(&clrScrollBar);
15 static YColorName scrollBarSlider(&clrScrollBarSlider);
16 static YColorName scrollBarButton(&clrScrollBarButton);
17 static YColorName scrollBarActiveArrow(&clrScrollBarArrow);
18 static YColorName scrollBarInactiveArrow(&clrScrollBarInactive);
19 
20 lazy<YTimer> YScrollBar::fScrollTimer;
21 
YScrollBar(Orientation anOrientation,YWindow * aParent)22 YScrollBar::YScrollBar(Orientation anOrientation, YWindow *aParent):
23     YWindow(aParent),
24     fOrientation(anOrientation),
25     fMaximum(0),
26     fMinimum(0),
27     fValue(0),
28     fVisibleAmount(0),
29     fUnitIncrement(1),
30     fBlockIncrement(1),
31     fScrollTo(goNone),
32     fGrabDelta(0),
33     fListener(nullptr),
34     fDNDScroll(false),
35     fConfigured(false),
36     fExposed(false)
37 {
38     setTitle("ScrollBar");
39 }
40 
~YScrollBar()41 YScrollBar::~YScrollBar() {
42     if (fScrollTimer)
43         fScrollTimer->disableTimerListener(this);
44 }
45 
enable()46 void YScrollBar::enable() {
47     if (visible() == false) {
48         show();
49         raise();
50     }
51 }
52 
reverseVideo()53 void YScrollBar::reverseVideo() {
54     scrollBarBg.reverse();
55     scrollBarSlider.reverse();
56     scrollBarButton.reverse();
57     scrollBarActiveArrow.reverse();
58     scrollBarInactiveArrow.reverse();
59 }
60 
background()61 YColor YScrollBar::background() {
62     return scrollBarBg;
63 }
64 
setOrientation(Orientation anOrientation)65 void YScrollBar::setOrientation(Orientation anOrientation) {
66     if (anOrientation != fOrientation) {
67         fOrientation = anOrientation;
68         repaint();
69     }
70 }
71 
setMaximum(int aMaximum)72 void YScrollBar::setMaximum(int aMaximum) {
73     if (aMaximum < fMinimum)
74         aMaximum = fMinimum;
75 
76     if (aMaximum != fMaximum) {
77         fMaximum = aMaximum;
78         repaint();
79     }
80 }
81 
setMinimum(int aMinimum)82 void YScrollBar::setMinimum(int aMinimum) {
83     if (aMinimum > fMaximum)
84         aMinimum = fMaximum;
85 
86     if (aMinimum != fMinimum) {
87         fMinimum = aMinimum;
88         repaint();
89     }
90 }
91 
setVisibleAmount(int aVisibleAmount)92 void YScrollBar::setVisibleAmount(int aVisibleAmount) {
93     if (fVisibleAmount > fMaximum - fMinimum)
94         fVisibleAmount = fMaximum - fMinimum;
95     if (fVisibleAmount < 0)
96         fVisibleAmount = 0;
97 
98     if (aVisibleAmount != fVisibleAmount) {
99         fVisibleAmount = aVisibleAmount;
100         repaint();
101     }
102 }
103 
setUnitIncrement(int anUnitIncrement)104 void YScrollBar::setUnitIncrement(int anUnitIncrement) {
105     fUnitIncrement = anUnitIncrement;
106 }
107 
setBlockIncrement(int aBlockIncrement)108 void YScrollBar::setBlockIncrement(int aBlockIncrement) {
109     fBlockIncrement = aBlockIncrement;
110 }
111 
setValue(int aValue)112 void YScrollBar::setValue(int aValue) {
113     if (aValue > fMaximum - fVisibleAmount)
114         aValue = fMaximum - fVisibleAmount;
115     if (aValue < fMinimum)
116         aValue = fMinimum;
117 
118     if (aValue != fValue) {
119         fValue = aValue;
120         repaint();
121     }
122 }
123 
setValues(int aValue,int aVisibleAmount,int aMin,int aMax)124 void YScrollBar::setValues(int aValue, int aVisibleAmount, int aMin, int aMax) {
125     int v = aValue;
126 
127     if (aMax < aMin)
128         aMax = aMin;
129     if (aVisibleAmount > aMax - aMin)
130         aVisibleAmount = aMax - aMin;
131     if (aVisibleAmount < 0)
132         aVisibleAmount = 0;
133     if (aValue > aMax - aVisibleAmount)
134         aValue = aMax - aVisibleAmount;
135     if (aValue < aMin)
136         aValue = aMin;
137 
138     if (aMax != fMaximum ||
139         aMin != fMinimum ||
140         aValue != fValue ||
141         aVisibleAmount != fVisibleAmount)
142     {
143         fMinimum = aMin;
144         fMaximum = aMax;
145         fValue = aValue;
146         fVisibleAmount = aVisibleAmount;
147         repaint();
148         if (v != fValue)
149             fListener->move(this, fValue);
150     }
151 }
152 
getCoord(int & beg,int & end,int & min,int & max,int & nn)153 void YScrollBar::getCoord(int &beg, int &end, int &min, int &max, int &nn) {
154     int dd = (fMaximum - fMinimum);
155 
156     if (fOrientation == Vertical) {
157         beg = scrollBarHeight;
158         end = height() - scrollBarHeight - 1;
159     } else {
160         beg = scrollBarHeight;
161         end = width() - scrollBarHeight - 1;
162     }
163 
164     nn = end - beg;
165     if (dd <= 0) {
166         min = 0;
167         max = nn;
168         return ;
169     }
170     int aa = nn;
171     int vv = aa * fVisibleAmount / dd;
172     if (vv < SCROLLBAR_MIN) {
173         vv = SCROLLBAR_MIN;
174         aa = nn - vv;
175         if (aa < 0) aa = 0;
176         dd -= fVisibleAmount;
177         if (dd <= 0) {
178             min = 0;
179             max = nn;
180             return ;
181         }
182     }
183     if (vv > nn)
184         vv = nn;
185     min = aa * fValue / dd;
186     max = min + vv;
187 }
188 
189 // !!!! TODO: Warp3, Warp4, Motif borders
190 
paint(Graphics & g,const YRect &)191 void YScrollBar::paint(Graphics &g, const YRect &/*r*/) {
192     int beg, end, min, max, nn;
193 
194     getCoord(beg, end, min, max, nn);
195 
196     /// !!! optimize this
197     if (fOrientation == Vertical) { // ============================ vertical ===
198         const int y(beg + min), h(max - min);
199 
200         g.setColor(scrollBarBg); // -------------------- background, buttons ---
201 
202         switch(wmLook) {
203         case lookWin95:
204             g.fillRect(0, beg, width(), min);
205             g.fillRect(0, y + h + 2, width(), ::max(0, end - h - y - 1));
206 
207             g.setColor(scrollBarButton);
208             g.drawBorderW(0, 0, width() - 1, beg - 1, fScrollTo != goUp);
209             g.fillRect(1, 1, width() - 3, beg - 3);
210 
211             g.drawBorderW(0, end + 1, width() - 1, beg - 1, fScrollTo != goDown);
212             g.fillRect(1, end + 2, width() - 3, beg - 3);
213             break;
214 
215         case lookWarp3:
216             g.fillRect(0, beg, width(), min);
217             g.fillRect(0, y + h + 2, width(), ::max(0, end - h - y - 1));
218 
219             g.setColor(scrollBarButton);
220             g.draw3DRect(0, 0, width() - 1, beg - 1, fScrollTo != goUp);
221             g.fillRect(1, 1, width() - 2, beg - 2);
222 
223             g.draw3DRect(0, end + 1, width() - 1, beg - 1, fScrollTo != goDown);
224             g.fillRect(1, end + 2, width() - 2, beg - 2);
225             break;
226 
227         case lookNice:
228         case lookPixmap:
229         case lookWarp4:
230             g.draw3DRect(0, 0, width() - 1, height() - 1, false);
231             g.fillRect(1, beg, width() - 2, min);
232             g.fillRect(1, y + h + 1, width() - 2, ::max(0, end - h - y));
233 
234             g.setColor(scrollBarButton);
235             g.drawBorderW(1, 1, width() - 3, beg - 2, fScrollTo != goUp);
236             g.fillRect(2, 2, width() - 5, beg - 4);
237 
238             g.drawBorderW(1, end + 1, width() - 3, beg - 2, fScrollTo != goDown);
239             g.fillRect(2, end + 2, width() - 5, beg - 4);
240             break;
241 
242         case lookMotif:
243             g.drawBorderW(0, 0, width() - 1, height() - 1, false);
244             g.fillRect(2, 2, width() - 3, y - 3);
245             g.drawLine(width() - 2, y - 1, width() - 2, y + h + 1);
246             g.fillRect(2, y + h + 2, width() - 3, height() - h - y - 3);
247             break;
248 
249         case lookGtk:
250             g.drawBorderG(0, 0, width() - 1, height() - 1, false);
251             g.fillRect(2, 2, width() - 3, y - 3);
252             g.drawLine(width() - 2, y - 1, width() - 2, y + h + 1);
253             g.fillRect(2, y + h + 2, width() - 3, height() - h - y - 3);
254             break;
255 
256         case lookFlat:
257         case lookMetal:
258             g.fillRect(0, beg, width(), min);
259             g.fillRect(0, y + h + 2, width(), end - h - y - 1);
260 
261             g.setColor(scrollBarButton);
262             g.drawBorderM(0, 0, width() - 1, beg - 1, fScrollTo != goUp);
263             g.fillRect(2, 2, width() - 4, beg - 4);
264             g.drawBorderM(0, end + 1, width() - 1, beg - 1, fScrollTo != goDown);
265             g.fillRect(2, end + 3, width() - 4, beg - 4);
266             break;
267         }
268         // --------------------- upper arrow ---
269         g.setColor(fValue > fMinimum ? scrollBarActiveArrow
270                    : scrollBarInactiveArrow);
271         switch(wmLook) {
272         case lookWin95:
273         case lookWarp3:
274         case lookWarp4:
275             g.drawArrow(Up, 3, (beg - width() + 10) / 2,
276                         width() - 8, fScrollTo == goUp);
277             break;
278 
279         case lookNice:
280         case lookPixmap:
281             g.drawArrow(Up, 4, (beg - width() + 10) / 2,
282                         width() - 10, fScrollTo == goUp);
283             break;
284 
285         case lookMotif:
286         case lookGtk:
287             g.drawArrow(Up, 2, 2, width() - 5, fScrollTo == goUp);
288             break;
289 
290         case lookFlat:
291         case lookMetal:
292             g.drawArrow(Up, 4, (beg - width() + 12) / 2,
293                         width() - 8, fScrollTo == goUp);
294             break;
295         }
296         // --------------------- lower arrow ---
297         g.setColor(fValue < fMaximum - fVisibleAmount ? scrollBarActiveArrow
298                    : scrollBarInactiveArrow);
299         switch(wmLook) {
300         case lookWin95:
301         case lookWarp3:
302         case lookWarp4:
303             g.drawArrow(Down, 3, end + (beg - width() + 12) / 2,
304                         width() - 8, fScrollTo == goDown);
305             break;
306 
307         case lookNice:
308         case lookPixmap:
309             g.drawArrow(Down, 4, end + (beg - width() + 12) / 2,
310                         width() - 10, fScrollTo == goDown);
311             break;
312 
313         case lookMotif:
314         case lookGtk:
315             g.drawArrow(Down, 2, end + 2, width() - 5, fScrollTo == goDown);
316             break;
317 
318         case lookFlat:
319         case lookMetal:
320             g.drawArrow(Down, 4, end + (beg - width() + 14) / 2,
321                         width() - 8, fScrollTo == goDown);
322             break;
323         }
324 
325         g.setColor(scrollBarSlider); // ----------------------------- slider ---
326 
327         switch(wmLook) {
328         case lookWin95:
329             g.drawBorderW(0, y, width() - 1, h + 1, true);
330             g.fillRect(1, y + 1, width() - 3, h - 1);
331             break;
332 
333         case lookWarp3:
334             g.draw3DRect(0, y, width() - 1, h + 1, true);
335             g.fillRect(1, y + 1, width() - 2, h);
336             break;
337 
338         case lookNice:
339         case lookPixmap:
340         case lookWarp4:
341             g.drawBorderW(1, y, width() - 3, h, true);
342             g.fillRect(2, y + 1, width() - 5, h - 2);
343 
344             g.setColor(scrollBarSlider->darker());
345             for (int hy = y + h / 2 - 6; hy < (y + h / 2 + 5); hy+= 2)
346                 g.drawLine(4, hy, width() - 6, hy);
347             g.setColor(scrollBarSlider->brighter());
348             for (int hy = y + h / 2 - 5; hy < (y + h / 2 + 6); hy+= 2)
349                 g.drawLine(4, hy, width() - 6, hy);
350             break;
351 
352         case lookMotif:
353             g.drawBorderW(2, y - 1, width() - 5, h + 2, true);
354             g.fillRect(3, y, width() - 7, h);
355             break;
356 
357         case lookGtk:
358             g.drawBorderG(2, y - 1, width() - 5, h + 2, true);
359             g.fillRect(3, y, width() - 7, h);
360             break;
361 
362         case lookFlat:
363         case lookMetal:
364             g.drawBorderM(0, y, width() - 1, h + 1, true);
365             g.fillRect(2, y + 2, width() - 4, h - 2);
366             break;
367         }
368     } else { // ================================================= horizontal ===
369         const int x(beg + min), w(max - min);
370 
371         g.setColor(scrollBarBg); // -------------------- background, buttons ---
372 
373         switch(wmLook) {
374         case lookWin95:
375             g.fillRect(beg, 0, min, height());
376             g.fillRect(x + w + 2, 0, ::max(0, end - w - x - 1), height());
377 
378             g.setColor(scrollBarButton);
379             g.drawBorderW(0, 0, beg - 1, height() - 1, fScrollTo != goUp);
380             g.fillRect(1, 1, beg - 3, height() - 3);
381 
382             g.drawBorderW(end + 1, 0, beg - 1, height() - 1, fScrollTo != goDown);
383             g.fillRect(end + 2, 1, beg - 3, height() - 3);
384             break;
385 
386         case lookWarp3:
387             g.fillRect(beg, 0, min, height());
388             g.fillRect(x + w + 2, 0, ::max(0, end - w - x - 1), height());
389 
390             g.setColor(scrollBarButton);
391             g.draw3DRect(0, 0, beg - 1, height() - 1, fScrollTo != goUp);
392             g.fillRect(1, 1, beg - 2, height() - 2);
393 
394             g.draw3DRect(end + 1, 0, beg - 1, height() - 1, fScrollTo != goDown);
395             g.fillRect(end + 2, 1, beg - 2, height() - 2);
396             break;
397 
398         case lookNice:
399         case lookPixmap:
400         case lookWarp4:
401             g.draw3DRect(0, 0, width() - 1, height() - 1, false);
402             g.fillRect(beg, 1, min, height() - 2);
403             g.fillRect(x + w + 2, 1, ::max(0, end - w - x), height() - 2);
404 
405             g.setColor(scrollBarButton);
406             g.drawBorderW(1, 1, beg - 2, height() - 3, fScrollTo != goUp);
407             g.fillRect(2, 2, beg - 4, height() - 5);
408 
409             g.drawBorderW(end + 1, 1, beg - 2, height() - 3, fScrollTo != goDown);
410             g.fillRect(end + 2, 2, beg - 4, height() - 5);
411             break;
412 
413         case lookMotif:
414             g.drawBorderW(0, 0, width() - 1, height() - 1, false);
415             g.fillRect(2, 2, x - 3, height() - 3);
416             g.drawLine(x - 1, height() - 2, x + w + 1, height() - 2);
417             g.fillRect(x + w + 2, 2, width() - w - x - 3, height() - 3);
418             break;
419 
420         case lookGtk:
421             g.drawBorderG(0, 0, width() - 1, height() - 1, false);
422             g.fillRect(2, 2, x - 3, height() - 3);
423             g.drawLine(x - 1, height() - 2, x + w + 1, height() - 2);
424             g.fillRect(x + w + 2, 2, width() - w - x - 3, height() - 3);
425             break;
426 
427         case lookFlat:
428         case lookMetal:
429             g.fillRect(beg, 0, min, height());
430             g.fillRect(x + w + 2, 0, end - w - x - 1, height());
431 
432             g.setColor(scrollBarButton);
433             g.drawBorderM(0, 0, beg - 1, height() - 1, fScrollTo != goUp);
434             g.fillRect(2, 2, beg - 4, height() - 4);
435             g.drawBorderM(end + 1, 0, beg - 1, height() - 1, fScrollTo != goDown);
436             g.fillRect(end + 3, 2, beg - 4, height() - 4);
437             break;
438         }
439         // ---------------------- left arrow ---
440         g.setColor(fValue > fMinimum ? scrollBarActiveArrow
441                    : scrollBarInactiveArrow);
442         switch(wmLook) {
443         case lookWin95:
444         case lookWarp3:
445         case lookWarp4:
446             g.drawArrow(Left, (beg - height() + 10) / 2, 3,
447                         height() - 8, fScrollTo == goUp);
448             break;
449 
450         case lookNice:
451         case lookPixmap:
452             g.drawArrow(Left, (beg - height() + 10) / 2, 4,
453                         height() - 10, fScrollTo == goUp);
454             break;
455 
456         case lookMotif:
457         case lookGtk:
458             g.drawArrow(Left, 2, 2, height() - 5, fScrollTo == goUp);
459             break;
460 
461         case lookFlat:
462         case lookMetal:
463             g.drawArrow(Left, (beg - height() + 12) / 2, 4,
464                         height() - 8, fScrollTo == goUp);
465             break;
466         }
467         // --------------------- right arrow ---
468         g.setColor(fValue < fMaximum - fVisibleAmount ? scrollBarActiveArrow
469                    : scrollBarInactiveArrow);
470         switch(wmLook) {
471         case lookWin95:
472         case lookWarp3:
473         case lookWarp4:
474             g.drawArrow(Right, end + (beg - height() + 12) / 2, 3,
475                         height() - 8, fScrollTo == goDown);
476             break;
477 
478         case lookNice:
479         case lookPixmap:
480             g.drawArrow(Right, end + (beg - height() + 12) / 2, 4,
481                         height() - 10, fScrollTo == goDown);
482             break;
483 
484         case lookMotif:
485         case lookGtk:
486             g.drawArrow(Right, end + 2, 2, height() - 5,
487                         fScrollTo == goDown);
488             break;
489 
490         case lookFlat:
491         case lookMetal:
492             g.drawArrow(Right, end + (beg - height() + 14) / 2, 4,
493                         height() - 8, fScrollTo == goDown);
494             break;
495         }
496 
497         g.setColor(scrollBarSlider); // ----------------------------- slider ---
498 
499         switch(wmLook) {
500         case lookWin95:
501             g.drawBorderW(x, 0, w + 1, height() - 1, true);
502             g.fillRect(x + 1, 1, w - 1, height() - 3);
503             break;
504 
505         case lookWarp3:
506             g.draw3DRect(x, 0, w + 1, height() - 1, true);
507             g.fillRect(x + 1, 1, w, height() - 2);
508             break;
509 
510         case lookNice:
511         case lookPixmap:
512         case lookWarp4:
513             g.drawBorderW(x, 1, w + 1, height() - 3, true);
514             g.fillRect(x + 1, 2, w - 1, height() - 5);
515 
516             g.setColor(scrollBarSlider->darker());
517             for (int hx = x + w / 2 - 6; hx < (x + w / 2 + 5); hx+= 2)
518                 g.drawLine(hx, 4, hx, height() - 6);
519             g.setColor(scrollBarSlider->brighter());
520             for (int hx = x + w / 2 - 5; hx < (x + w / 2 + 6); hx+= 2)
521                 g.drawLine(hx, 4, hx, height() - 6);
522 
523             break;
524 
525         case lookMotif:
526             g.drawBorderW(x - 1, 2, w + 3, height() - 5, true);
527             g.fillRect(x, 3, w + 1, height() - 7);
528             break;
529 
530         case lookGtk:
531             g.drawBorderG(x - 1, 2, w + 3, height() - 5, true);
532             g.fillRect(x, 3, w + 1, height() - 7);
533             break;
534 
535         case lookFlat:
536         case lookMetal:
537             g.drawBorderM(x, 0, w + 1, height() - 1, true);
538             g.fillRect(x + 2, 2, w - 2, height() - 4);
539             break;
540         }
541     }
542 }
543 
scroll(int delta)544 void YScrollBar::scroll(int delta) {
545     int fNewPos = fValue;
546 
547     if (delta == 0)
548         return ;
549 
550     fNewPos += delta;
551 
552     if (fNewPos >= fMaximum - fVisibleAmount)
553         fNewPos = fMaximum - fVisibleAmount;
554     if (fNewPos < fMinimum)
555         fNewPos = fMinimum;
556     if (fNewPos != fValue) {
557         delta = fNewPos - fValue;
558         fValue = fNewPos;
559         repaint();
560         fListener->scroll(this, delta);
561     }
562 }
563 
move(int pos)564 void YScrollBar::move(int pos) {
565     int fNewPos = pos;
566 
567     if (fNewPos >= fMaximum - fVisibleAmount)
568         fNewPos = fMaximum - fVisibleAmount;
569     if (fNewPos < fMinimum)
570         fNewPos = fMinimum;
571     if (fValue != fNewPos) {
572         fValue = fNewPos;
573         repaint();
574         fListener->move(this, fValue);
575     }
576 }
577 
handleButton(const XButtonEvent & button)578 void YScrollBar::handleButton(const XButtonEvent &button) {
579     if (handleScrollMouse(button) == true)
580         return ;
581 
582     if (button.button != Button1)
583         return ;
584 
585     if (fMinimum >= fMaximum)
586         return ;
587 
588     if (button.type == ButtonPress) {
589         fScrollTo = getOp(button.x, button.y);
590         doScroll();
591         fScrollTimer->setTimer(scrollBarStartDelay, this, true);
592         repaint();
593     } else if (button.type == ButtonRelease) {
594         fScrollTo = goNone;
595         if (fScrollTimer)
596             fScrollTimer->disableTimerListener(this);
597         repaint();
598     }
599 }
600 
handleMotion(const XMotionEvent & motion)601 void YScrollBar::handleMotion(const XMotionEvent &motion) {
602     if (xapp->buttonMask(motion.state) != Button1Mask)
603         return;
604 
605     if (fOrientation == Vertical) {
606         int h = height() - 2 * width();
607 
608         if (h <= 0 || fMinimum >= fMaximum)
609             return ;
610 
611         if (fScrollTo == goPosition) {
612             int y = motion.y - fGrabDelta - width();
613             if (y < 0) y = 0;
614             int pos = y * (fMaximum - fMinimum) / h;
615             move(pos);
616         }
617     } else {
618         int w = width() - 2 * height();
619 
620         if (w <= 0 || fMinimum >= fMaximum)
621             return ;
622 
623         if (fScrollTo == goPosition) {
624             int x = motion.x - fGrabDelta - height();
625             if (x < 0) x = 0;
626             int pos = x * (fMaximum - fMinimum) / w;
627             move(pos);
628         }
629     }
630 }
631 
handleScrollKeys(const XKeyEvent & key)632 bool YScrollBar::handleScrollKeys(const XKeyEvent &key) {
633     if (key.type == KeyPress) {
634         KeySym k = keyCodeToKeySym(key.keycode);
635         int m = KEY_MODMASK(key.state);
636 
637         if (fOrientation == Vertical) {
638             switch (k) {
639             case XK_Up:
640             case XK_KP_Up:
641                 if (m == 0) {
642                     scroll(-fUnitIncrement);
643                     return true;
644                 }
645                 if (m == ShiftMask) {
646                     scroll(-fUnitIncrement * 4);
647                     return true;
648                 }
649                 if (m == ControlMask) {
650                     scroll(-fUnitIncrement * 16);
651                     return true;
652                 }
653                 break;
654             case XK_Home:
655             case XK_KP_Home:
656                 if (m == 0) {
657                     move(0);
658                     return true;
659                 }
660                 break;
661             case XK_Prior:
662             case XK_KP_Prior:
663                 if (m == ControlMask) {
664                     move(0);
665                     return true;
666                 }
667                 if (m == ShiftMask) {
668                     scroll(-fBlockIncrement * 4);
669                     return true;
670                 }
671                 if (m == 0) {
672                     scroll(-fBlockIncrement);
673                     return true;
674                 }
675                 break;
676             case XK_Down:
677             case XK_KP_Down:
678                 if (m == 0) {
679                     scroll(+fUnitIncrement);
680                     return true;
681                 }
682                 if (m == ShiftMask) {
683                     scroll(+fUnitIncrement * 4);
684                     return true;
685                 }
686                 if (m == ControlMask) {
687                     scroll(+fUnitIncrement * 16);
688                     return true;
689                 }
690                 break;
691             case XK_End:
692             case XK_KP_End:
693                 if (m == 0) {
694                     move(fMaximum - fVisibleAmount);
695                     return true;
696                 }
697                 break;
698             case XK_Next:
699             case XK_KP_Next:
700                 if (m == ControlMask) {
701                     move(fMaximum - fVisibleAmount);
702                     return true;
703                 }
704                 if (m == ShiftMask) {
705                     scroll(+fBlockIncrement * 4);
706                     return true;
707                 }
708                 if (m == 0) {
709                     scroll(+fBlockIncrement);
710                     return true;
711                 }
712                 break;
713             default:
714                 break;
715             }
716         }
717         else if (m == 0 && fOrientation == Horizontal) {
718             switch (k) {
719             case XK_Left:
720             case XK_KP_Left:
721                 scroll(-fUnitIncrement);
722                 return true;
723             case XK_Right:
724             case XK_KP_Right:
725                 scroll(+fUnitIncrement);
726                 return true;
727             case XK_Home:
728             case XK_KP_Home:
729                 move(0);
730                 return true;
731             case XK_End:
732             case XK_KP_End:
733                 move(fMaximum - fVisibleAmount);
734                 return true;
735             default:
736                 break;
737             }
738         }
739     }
740     return false;
741 }
742 
handleScrollMouse(const XButtonEvent & button)743 bool YScrollBar::handleScrollMouse(const XButtonEvent &button) {
744     if (button.type == ButtonPress) {
745         if (button.button == Button4) {
746             scroll(-fBlockIncrement);
747             return true;
748         } else if (button.button == Button5) {
749             scroll(+fBlockIncrement);
750             return true;
751         }
752     }
753     return false;
754 }
755 
doScroll()756 void YScrollBar::doScroll() {
757     switch (fScrollTo) {
758     case goNone:
759         break;
760     case goUp:
761         scroll(-fUnitIncrement);
762         break;
763     case goDown:
764         scroll(+fUnitIncrement);
765         break;
766     case goPageUp:
767         scroll(-fBlockIncrement);
768         break;
769     case goPageDown:
770         scroll(+fBlockIncrement);
771         break;
772     case goPosition:
773         break;
774     }
775 }
776 
handleTimer(YTimer * timer)777 bool YScrollBar::handleTimer(YTimer *timer) {
778     if (timer != fScrollTimer)
779         return false;
780     doScroll();
781     if (!fDNDScroll || (fScrollTo != goPageUp && fScrollTo != goPageDown))
782         timer->setInterval(scrollBarDelay);
783     return true;
784 }
785 
getOp(int x,int y)786 YScrollBar::ScrollOp YScrollBar::getOp(int x, int y) {
787     int beg, end, min, max, nn;
788     ScrollOp fScrollTo;
789 
790     getCoord(beg, end, min, max, nn);
791 
792     fScrollTo = goNone;
793     if (fOrientation == Vertical) {
794 
795         if (y > end) {
796             if (fValue < fMaximum - fVisibleAmount)
797                 fScrollTo = goDown;
798         } else if (y < beg) {
799             if (fValue > 0)
800                 fScrollTo = goUp;
801         } else {
802             if (y < beg + min) {
803                 fScrollTo = goPageUp;
804             } else if (y > beg + max) {
805                 fScrollTo = goPageDown;
806             } else {
807                 fScrollTo = goPosition;
808                 fGrabDelta = y - min - beg;
809             }
810         }
811     } else {
812         if (x > end) {
813             if (fValue < fMaximum - fVisibleAmount)
814                 fScrollTo = goDown;
815         } else if (x < beg) {
816             if (fValue > 0)
817                 fScrollTo = goUp;
818         } else {
819             if (x < beg + min) {
820                 fScrollTo = goPageUp;
821             } else if (x > beg + max) {
822                 fScrollTo = goPageDown;
823             } else {
824                 fScrollTo = goPosition;
825                 fGrabDelta = x - min - beg;
826             }
827         }
828     }
829     return fScrollTo;
830 }
831 
handleDNDEnter()832 void YScrollBar::handleDNDEnter() {
833     fScrollTo = goNone;
834     fDNDScroll = true;
835     if (fScrollTimer)
836         fScrollTimer->disableTimerListener(this);
837 }
838 
handleDNDLeave()839 void YScrollBar::handleDNDLeave() {
840     fScrollTo = goNone;
841     fDNDScroll = false;
842     repaint();
843     if (fScrollTimer)
844         fScrollTimer->disableTimerListener(this);
845 }
846 
handleDNDPosition(int x,int y)847 void YScrollBar::handleDNDPosition(int x, int y) {
848     fScrollTo = getOp(x, y);
849     fScrollTimer->setTimer(scrollBarStartDelay, this, true);
850     repaint();
851 }
852 
configure(const YRect2 & r)853 void YScrollBar::configure(const YRect2& r) {
854     if (r.width() > 1 && r.height() > 1) {
855         fConfigured = true;
856         repaint();
857     }
858 }
859 
handleExpose(const XExposeEvent & expose)860 void YScrollBar::handleExpose(const XExposeEvent &expose) {
861     if (fExposed == false && expose.count == 0) {
862         fExposed = true;
863         repaint();
864     }
865 }
866 
repaint()867 void YScrollBar::repaint() {
868     if (fConfigured && fExposed) {
869         GraphicsBuffer(this).paint();
870     }
871 }
872 
873 // vim: set sw=4 ts=4 et:
874