1 /********************************************************************************
2 *                                                                               *
3 *                         S c r o l l b a r   O b j e c t s                     *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1997,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (at your option) any later version.            *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXScrollBar.cpp,v 1.27 2006/01/22 17:58:41 fox Exp $                     *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxkeys.h"
28 #include "FXHash.h"
29 #include "FXThread.h"
30 #include "FXStream.h"
31 #include "FXString.h"
32 #include "FXSize.h"
33 #include "FXPoint.h"
34 #include "FXRectangle.h"
35 #include "FXRegistry.h"
36 #include "FXApp.h"
37 #include "FXDCWindow.h"
38 #include "FXScrollBar.h"
39 
40 /*
41   Notes:
42   - Should increase/decrease, and slider get messages instead?
43   - Scrollbar items should derive from FXWindow (as they are very simple).
44   - If non-scrollable, but drawn anyway, don't draw thumb!
45   - In case of a coarse range, we have rounding also.
46   - The API's setPosition(), setRange() and setPage() should probably have
47     an optional notify callback.
48 */
49 
50 
51 #define SCROLLBAR_MASK  (SCROLLBAR_HORIZONTAL|SCROLLBAR_WHEELJUMP)
52 
53 using namespace FX;
54 
55 /*******************************************************************************/
56 
57 namespace FX {
58 
59 // Map
60 FXDEFMAP(FXScrollBar) FXScrollBarMap[]={
61   FXMAPFUNC(SEL_PAINT,0,FXScrollBar::onPaint),
62   FXMAPFUNC(SEL_MOTION,0,FXScrollBar::onMotion),
63   FXMAPFUNC(SEL_MOUSEWHEEL,0,FXScrollBar::onMouseWheel),
64   FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXScrollBar::onLeftBtnPress),
65   FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXScrollBar::onLeftBtnRelease),
66   FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXScrollBar::onMiddleBtnPress),
67   FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXScrollBar::onMiddleBtnRelease),
68   FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXScrollBar::onRightBtnPress),
69   FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXScrollBar::onRightBtnRelease),
70   FXMAPFUNC(SEL_UNGRABBED,0,FXScrollBar::onUngrabbed),
71   FXMAPFUNC(SEL_TIMEOUT,FXScrollBar::ID_TIMEWHEEL,FXScrollBar::onTimeWheel),
72   FXMAPFUNC(SEL_TIMEOUT,FXScrollBar::ID_AUTOSCROLL,FXScrollBar::onAutoScroll),
73   FXMAPFUNC(SEL_COMMAND,FXScrollBar::ID_SETVALUE,FXScrollBar::onCmdSetValue),
74   FXMAPFUNC(SEL_COMMAND,FXScrollBar::ID_SETINTVALUE,FXScrollBar::onCmdSetIntValue),
75   FXMAPFUNC(SEL_COMMAND,FXScrollBar::ID_GETINTVALUE,FXScrollBar::onCmdGetIntValue),
76   FXMAPFUNC(SEL_COMMAND,FXScrollBar::ID_SETINTRANGE,FXScrollBar::onCmdSetIntRange),
77   FXMAPFUNC(SEL_COMMAND,FXScrollBar::ID_GETINTRANGE,FXScrollBar::onCmdGetIntRange),
78   };
79 
80 
81 // Object implementation
FXIMPLEMENT(FXScrollBar,FXWindow,FXScrollBarMap,ARRAYNUMBER (FXScrollBarMap))82 FXIMPLEMENT(FXScrollBar,FXWindow,FXScrollBarMap,ARRAYNUMBER(FXScrollBarMap))
83 
84 
85 // For deserialization
86 FXScrollBar::FXScrollBar(){
87   flags|=FLAG_ENABLED|FLAG_SHOWN;
88   barsize=15;
89   thumbsize=8;
90   thumbpos=15;
91   dragpoint=0;
92   mode=MODE_NONE;
93   }
94 
95 
96 // Make a scrollbar
FXScrollBar(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h)97 FXScrollBar::FXScrollBar(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):
98   FXWindow(p,opts,x,y,w,h){
99   flags|=FLAG_ENABLED|FLAG_SHOWN;
100   backColor=getApp()->getBaseColor();
101   hiliteColor=getApp()->getHiliteColor();
102   shadowColor=getApp()->getShadowColor();
103   borderColor=getApp()->getBorderColor();
104   arrowColor=getApp()->getForeColor();
105   barsize=getApp()->getScrollBarSize();
106   thumbpos=barsize;
107   thumbsize=barsize>>1;
108   target=tgt;
109   message=sel;
110   dragpoint=0;
111   range=100;
112   page=1;
113   line=1;
114   pos=0;
115   mode=MODE_NONE;
116   }
117 
118 
119 // Get default size
getDefaultWidth()120 FXint FXScrollBar::getDefaultWidth(){
121   return (options&SCROLLBAR_HORIZONTAL) ? barsize+barsize+(barsize>>1) : barsize;
122   }
123 
124 
getDefaultHeight()125 FXint FXScrollBar::getDefaultHeight(){
126   return (options&SCROLLBAR_HORIZONTAL) ? barsize : barsize+barsize+(barsize>>1);
127   }
128 
129 
130 // Layout changed
layout()131 void FXScrollBar::layout(){
132   setPosition(pos);
133   flags&=~FLAG_DIRTY;
134   }
135 
136 
137 // Update value from a message
onCmdSetValue(FXObject *,FXSelector,void * ptr)138 long FXScrollBar::onCmdSetValue(FXObject*,FXSelector,void* ptr){
139   setPosition((FXint)(FXival)ptr);
140   return 1;
141   }
142 
143 
144 // Update value from a message
onCmdSetIntValue(FXObject *,FXSelector,void * ptr)145 long FXScrollBar::onCmdSetIntValue(FXObject*,FXSelector,void* ptr){
146   setPosition(*((FXint*)ptr));
147   return 1;
148   }
149 
150 
151 
152 // Obtain value with a message
onCmdGetIntValue(FXObject *,FXSelector,void * ptr)153 long FXScrollBar::onCmdGetIntValue(FXObject*,FXSelector,void* ptr){
154   *((FXint*)ptr)=getPosition();
155   return 1;
156   }
157 
158 
159 // Update range from a message
onCmdSetIntRange(FXObject *,FXSelector,void * ptr)160 long FXScrollBar::onCmdSetIntRange(FXObject*,FXSelector,void* ptr){
161   setRange(((FXint*)ptr)[1]);
162   return 1;
163   }
164 
165 
166 // Get range with a message
onCmdGetIntRange(FXObject *,FXSelector,void * ptr)167 long FXScrollBar::onCmdGetIntRange(FXObject*,FXSelector,void* ptr){
168   ((FXint*)ptr)[0]=0;
169   ((FXint*)ptr)[1]=getRange();
170   return 1;
171   }
172 
173 
174 // Pressed LEFT button in slider
175 // Note we don't move the focus to the scrollbar widget!
onLeftBtnPress(FXObject *,FXSelector,void * ptr)176 long FXScrollBar::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
177   register FXEvent *event=(FXEvent*)ptr;
178   register FXint p=pos;
179   if(isEnabled()){
180     grab();
181     getApp()->removeTimeout(this,ID_TIMEWHEEL);
182     getApp()->removeTimeout(this,ID_AUTOSCROLL);
183     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
184     flags&=~FLAG_UPDATE;
185     if(options&SCROLLBAR_HORIZONTAL){     // Horizontal scrollbar
186       if(event->win_x<height){                   // Left arrow
187         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)-line);
188         p=pos-line;
189         update();
190         mode=MODE_DEC;
191         }
192       else if(width-height<=event->win_x){       // Right arrow
193         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)line);
194         p=pos+line;
195         update();
196         mode=MODE_INC;
197         }
198       else if(event->win_x<thumbpos){             // Page left
199         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)-page);
200         p=pos-page;
201         update();
202         mode=MODE_PAGE_DEC;
203         }
204       else if(thumbpos+thumbsize<=event->win_x){  // Page right
205         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)page);
206         p=pos+page;
207         update();
208         mode=MODE_PAGE_INC;
209         }
210       else{                                       // Grabbed the puck
211         if(event->state&(CONTROLMASK|SHIFTMASK|ALTMASK)) mode=MODE_FINE_DRAG;
212         dragpoint=event->win_x-thumbpos;
213         mode=MODE_DRAG;
214         }
215       }
216     else{                                 // Vertical scrollbar
217       if(event->win_y<width){                   // Up arrow
218         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)-line);
219         p=pos-line;
220         update();
221         mode=MODE_DEC;
222         }
223       else if(height-width<=event->win_y){      // Down arrow
224         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)line);
225         p=pos+line;
226         update();
227         mode=MODE_INC;
228         }
229       else if(event->win_y<thumbpos){             // Page up
230         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)-page);
231         p=pos-page;
232         update();
233         mode=MODE_PAGE_DEC;
234         }
235       else if(thumbpos+thumbsize<=event->win_y){  // Page down
236         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)page);
237         p=pos+page;
238         update();
239         mode=MODE_PAGE_INC;
240         }
241       else{                                       // Grabbed the puck
242         if(event->state&(CONTROLMASK|SHIFTMASK|ALTMASK)) mode=MODE_FINE_DRAG;
243         dragpoint=event->win_y-thumbpos;
244         mode=MODE_DRAG;
245         }
246       }
247     if(p<0) p=0;
248     if(p>(range-page)) p=range-page;
249     if(p!=pos){
250       setPosition(p);
251       flags|=FLAG_CHANGED;
252       if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)pos);
253       }
254     return 1;
255     }
256   return 0;
257   }
258 
259 
260 // Released LEFT button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)261 long FXScrollBar::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
262   FXuint flgs=flags;
263   if(isEnabled()){
264     ungrab();
265     flags&=~FLAG_CHANGED;
266     flags|=FLAG_UPDATE;
267     dragpoint=0;
268     mode=MODE_NONE;
269     setPosition(pos);
270     update();
271     getApp()->removeTimeout(this,ID_TIMEWHEEL);
272     getApp()->removeTimeout(this,ID_AUTOSCROLL);
273     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
274     if(flgs&FLAG_CHANGED){
275       if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXival)pos);
276       }
277     return 1;
278     }
279   return 0;
280   }
281 
282 
283 // Pressed MIDDLE button in slider
onMiddleBtnPress(FXObject *,FXSelector,void * ptr)284 long FXScrollBar::onMiddleBtnPress(FXObject*,FXSelector,void* ptr){
285   FXEvent *event=(FXEvent*)ptr;
286   register FXint p=pos;
287   register int travel,lo,hi,t;
288   if(isEnabled()){
289     grab();
290     getApp()->removeTimeout(this,ID_TIMEWHEEL);
291     getApp()->removeTimeout(this,ID_AUTOSCROLL);
292     if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONPRESS,message),ptr)) return 1;
293     mode=MODE_DRAG;
294     flags&=~FLAG_UPDATE;
295     dragpoint=thumbsize/2;
296     if(options&SCROLLBAR_HORIZONTAL){
297       travel=width-height-height-thumbsize;
298       t=event->win_x-dragpoint;
299       if(t<height) t=height;
300       if(t>(width-height-thumbsize)) t=width-height-thumbsize;
301       if(t!=thumbpos){
302         FXMINMAX(lo,hi,t,thumbpos);
303         update(lo,0,hi+thumbsize-lo,height);
304         thumbpos=t;
305         }
306       if(travel>0){ p=(FXint)((((FXdouble)(thumbpos-height))*(range-page))/travel); } else { p=0; }
307       }
308     else{
309       travel=height-width-width-thumbsize;
310       t=event->win_y-dragpoint;
311       if(t<width) t=width;
312       if(t>(height-width-thumbsize)) t=height-width-thumbsize;
313       if(t!=thumbpos){
314         FXMINMAX(lo,hi,t,thumbpos);
315         update(0,lo,width,hi+thumbsize-lo);
316         thumbpos=t;
317         }
318       if(travel>0){ p=(FXint)((((FXdouble)(thumbpos-width))*(range-page))/travel); } else { p=0; }
319       }
320     if(p<0) p=0;
321     if(p>(range-page)) p=range-page;
322     if(pos!=p){
323       pos=p;
324       flags|=FLAG_CHANGED;
325       if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)pos);
326       }
327     return 1;
328     }
329   return 0;
330   }
331 
332 
333 // Released MIDDLE button
onMiddleBtnRelease(FXObject *,FXSelector,void * ptr)334 long FXScrollBar::onMiddleBtnRelease(FXObject*,FXSelector,void* ptr){
335   FXuint flgs=flags;
336   if(isEnabled()){
337     ungrab();
338     flags&=~FLAG_CHANGED;
339     flags|=FLAG_UPDATE;
340     dragpoint=0;
341     mode=MODE_NONE;
342     setPosition(pos);
343     update();
344     getApp()->removeTimeout(this,ID_TIMEWHEEL);
345     getApp()->removeTimeout(this,ID_AUTOSCROLL);
346     if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONRELEASE,message),ptr)) return 1;
347     if(flgs&FLAG_CHANGED){
348       if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXival)pos);
349       }
350     return 1;
351     }
352   return 0;
353   }
354 
355 
356 // Pressed RIGHT button in slider
onRightBtnPress(FXObject *,FXSelector,void * ptr)357 long FXScrollBar::onRightBtnPress(FXObject*,FXSelector,void* ptr){
358   register FXEvent *event=(FXEvent*)ptr;
359   register FXint p=pos;
360   if(isEnabled()){
361     grab();
362     getApp()->removeTimeout(this,ID_TIMEWHEEL);
363     getApp()->removeTimeout(this,ID_AUTOSCROLL);
364     if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONPRESS,message),ptr)) return 1;
365     flags&=~FLAG_UPDATE;
366     if(options&SCROLLBAR_HORIZONTAL){     // Horizontal scrollbar
367       if(event->win_x<height){                   // Left arrow
368         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)-1);
369         p=pos-1;
370         update();
371         mode=MODE_DEC;
372         }
373       else if(width-height<=event->win_x){       // Right arrow
374         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)1);
375         p=pos+1;
376         update();
377         mode=MODE_INC;
378         }
379       else if(event->win_x<thumbpos){             // Page left
380         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)-line);
381         p=pos-line;
382         update();
383         mode=MODE_PAGE_DEC;
384         }
385       else if(thumbpos+thumbsize<=event->win_x){  // Page right
386         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)line);
387         p=pos+line;
388         update();
389         mode=MODE_PAGE_INC;
390         }
391       else{                                       // Grabbed the puck
392         dragpoint=event->win_x-thumbpos;
393         mode=MODE_FINE_DRAG;
394         }
395       }
396     else{                                 // Vertical scrollbar
397       if(event->win_y<width){                   // Up arrow
398         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)-1);
399         p=pos-1;
400         update();
401         mode=MODE_DEC;
402         }
403       else if(height-width<=event->win_y){      // Down arrow
404         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)1);
405         p=pos+1;
406         update();
407         mode=MODE_INC;
408         }
409       else if(event->win_y<thumbpos){             // Page up
410         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)-line);
411         p=pos-line;
412         update();
413         mode=MODE_PAGE_DEC;
414         }
415       else if(thumbpos+thumbsize<=event->win_y){  // Page down
416         getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollDelay(),(void*)(FXival)line);
417         p=pos+line;
418         update();
419         mode=MODE_PAGE_INC;
420         }
421       else{                                       // Grabbed the puck
422         dragpoint=event->win_y-thumbpos;
423         mode=MODE_FINE_DRAG;
424         }
425       }
426     if(p<0) p=0;
427     if(p>(range-page)) p=range-page;
428     if(p!=pos){
429       setPosition(p);
430       flags|=FLAG_CHANGED;
431       if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)pos);
432       }
433     return 1;
434     }
435   return 0;
436   }
437 
438 
439 // Released RIGHT button
onRightBtnRelease(FXObject *,FXSelector,void * ptr)440 long FXScrollBar::onRightBtnRelease(FXObject*,FXSelector,void* ptr){
441   FXuint flgs=flags;
442   if(isEnabled()){
443     ungrab();
444     flags&=~FLAG_CHANGED;
445     flags|=FLAG_UPDATE;
446     dragpoint=0;
447     mode=MODE_NONE;
448     setPosition(pos);
449     update();
450     getApp()->removeTimeout(this,ID_TIMEWHEEL);
451     getApp()->removeTimeout(this,ID_AUTOSCROLL);
452     if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message),ptr)) return 1;
453     if(flgs&FLAG_CHANGED){
454       if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXival)pos);
455       }
456     return 1;
457     }
458   return 0;
459   }
460 
461 
462 // The widget lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)463 long FXScrollBar::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
464   FXWindow::onUngrabbed(sender,sel,ptr);
465   getApp()->removeTimeout(this,ID_TIMEWHEEL);
466   getApp()->removeTimeout(this,ID_AUTOSCROLL);
467   flags&=~FLAG_CHANGED;
468   flags|=FLAG_UPDATE;
469   dragpoint=0;
470   mode=MODE_NONE;
471   return 1;
472   }
473 
474 
475 // Moving
onMotion(FXObject *,FXSelector,void * ptr)476 long FXScrollBar::onMotion(FXObject*,FXSelector,void* ptr){
477   FXEvent *event=(FXEvent*)ptr;
478   FXint travel,hi,lo,t,p;
479   if(!isEnabled()) return 0;
480   if(mode>=MODE_DRAG){
481     p=0;
482 
483     // If modifiers down, fine scrolling method goes in effect, if
484     // not, switch back to coarse mode (thanks, Tony <verant@mail.ru>)!
485     if(event->state&(CONTROLMASK|SHIFTMASK|ALTMASK|RIGHTBUTTONMASK))
486       mode=MODE_FINE_DRAG;
487     else
488       mode=MODE_DRAG;
489 
490     // Coarse movements
491     if(mode==MODE_DRAG){
492       if(options&SCROLLBAR_HORIZONTAL){
493         travel=width-height-height-thumbsize;
494         t=event->win_x-dragpoint;
495         if(t<height) t=height;
496         if(t>(width-height-thumbsize)) t=width-height-thumbsize;
497         if(t!=thumbpos){
498           FXMINMAX(lo,hi,t,thumbpos);
499           update(lo,0,hi+thumbsize-lo,height);
500           thumbpos=t;
501           }
502         if(travel>0){ p=(FXint)((((FXdouble)(thumbpos-height))*(range-page)+travel/2)/travel); }
503         }
504       else{
505         travel=height-width-width-thumbsize;
506         t=event->win_y-dragpoint;
507         if(t<width) t=width;
508         if(t>(height-width-thumbsize)) t=height-width-thumbsize;
509         if(t!=thumbpos){
510           FXMINMAX(lo,hi,t,thumbpos);
511           update(0,lo,width,hi+thumbsize-lo);
512           thumbpos=t;
513           }
514         if(travel>0){ p=(FXint)((((FXdouble)(thumbpos-width))*(range-page)+travel/2)/travel); }
515         }
516       }
517 
518     // Fine movements
519     else if(mode==MODE_FINE_DRAG){
520       if(options&SCROLLBAR_HORIZONTAL){
521         travel=width-height-height-thumbsize;
522         p=pos+event->win_x-event->last_x;
523         if(p<0) p=0;
524         if(p>(range-page)) p=range-page;
525         if(range>page){ t=height+(FXint)((((FXdouble)pos)*travel)/(range-page)); } else { t=height; }
526         if(t!=thumbpos){
527           FXMINMAX(lo,hi,t,thumbpos);
528           update(lo,0,hi+thumbsize-lo,height);
529           thumbpos=t;
530           }
531         }
532       else{
533         travel=height-width-width-thumbsize;
534         p=pos+event->win_y-event->last_y;
535         if(p<0) p=0;
536         if(p>(range-page)) p=range-page;
537         if(range>page){ t=width+(FXint)((((FXdouble)pos)*travel)/(range-page)); } else { t=width; }
538         if(t!=thumbpos){
539           FXMINMAX(lo,hi,t,thumbpos);
540           update(0,lo,width,hi+thumbsize-lo);
541           thumbpos=t;
542           }
543         }
544       }
545 
546     // Clamp range and issue callbacks
547     if(p<0) p=0;
548     if(p>(range-page)) p=range-page;
549     if(pos!=p){
550       pos=p;
551       flags|=FLAG_CHANGED;
552       if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)pos);
553       return 1;
554       }
555     }
556   return 0;
557   }
558 
559 
560 // Mouse wheel
onMouseWheel(FXObject *,FXSelector,void * ptr)561 long FXScrollBar::onMouseWheel(FXObject*,FXSelector,void* ptr){
562   FXEvent* ev=(FXEvent*)ptr;
563   FXint jump,dragjump;
564   if(isEnabled()){
565     getApp()->removeTimeout(this,ID_TIMEWHEEL);
566     getApp()->removeTimeout(this,ID_AUTOSCROLL);
567     if(!(ev->state&(LEFTBUTTONMASK|MIDDLEBUTTONMASK|RIGHTBUTTONMASK))){
568       if(ev->state&ALTMASK) jump=line;                      // Fine scrolling
569       else if(ev->state&CONTROLMASK) jump=page;             // Coarse scrolling
570       else jump=FXMIN(page,getApp()->getWheelLines()*line); // Normal scrolling
571       if(dragpoint==0) dragpoint=pos;                       // Were not scrolling already?
572       dragpoint-=ev->code*jump/120;                         // Move scroll position
573       if(dragpoint<0) dragpoint=0;
574       if(dragpoint>(range-page)) dragpoint=range-page;
575       if(dragpoint!=pos){
576         if(options&SCROLLBAR_WHEELJUMP){
577           setPosition(dragpoint);
578           dragpoint=0;
579           if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXival)pos);
580           }
581         else{
582           dragjump=(dragpoint-pos);
583           if(FXABS(dragjump)>16) dragjump/=16;
584           getApp()->addTimeout(this,ID_TIMEWHEEL,5,(void*)(FXival)dragjump);
585           }
586         }
587       else{
588         dragpoint=0;
589         }
590       return 1;
591       }
592     }
593   return 0;
594   }
595 
596 
597 // Smoothly scroll to desired value as determined by wheel
onTimeWheel(FXObject *,FXSelector,void * ptr)598 long FXScrollBar::onTimeWheel(FXObject*,FXSelector,void* ptr){
599   register FXint p=pos+(FXint)(FXival)ptr;
600   if(dragpoint<pos){
601     if(p<=dragpoint){
602       setPosition(dragpoint);
603       dragpoint=0;
604       if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXival)pos);
605       }
606     else{
607       setPosition(p);
608       getApp()->addTimeout(this,ID_TIMEWHEEL,5,ptr);
609       if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)pos);
610       }
611     }
612   else if(dragpoint>pos){
613     if(p>=dragpoint){
614       setPosition(dragpoint);
615       dragpoint=0;
616       if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXival)pos);
617       }
618     else{
619       setPosition(p);
620       getApp()->addTimeout(this,ID_TIMEWHEEL,5,ptr);
621       if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)pos);
622       }
623     }
624   else{
625     dragpoint=0;
626     }
627   return 1;
628   }
629 
630 
631 // Automatic scroll based on timer
onAutoScroll(FXObject *,FXSelector,void * ptr)632 long FXScrollBar::onAutoScroll(FXObject*,FXSelector,void* ptr){
633   register FXint p=pos+(FXint)(FXival)ptr;
634   if(p<=0){
635     p=0;
636     }
637   else if(p>=(range-page)){
638     p=range-page;
639     }
640   else{
641     getApp()->addTimeout(this,ID_AUTOSCROLL,getApp()->getScrollSpeed(),ptr);
642     }
643   if(p!=pos){
644     setPosition(p);
645     flags|=FLAG_CHANGED;
646     if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)pos);
647     return 1;
648     }
649   return 0;
650   }
651 
652 
653 // Draw button in scrollbar; this is slightly different from a raised rectangle
drawButton(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h,FXbool down)654 void FXScrollBar::drawButton(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h,FXbool down){
655   dc.setForeground(backColor);
656   dc.fillRectangle(x+2,y+2,w-4,h-4);
657   if(!down){
658     dc.setForeground(backColor);
659     dc.fillRectangle(x,y,w-1,1);
660     dc.fillRectangle(x,y,1,h-1);
661     dc.setForeground(hiliteColor);
662     dc.fillRectangle(x+1,y+1,w-2,1);
663     dc.fillRectangle(x+1,y+1,1,h-2);
664     dc.setForeground(shadowColor);
665     dc.fillRectangle(x+1,y+h-2,w-2,1);
666     dc.fillRectangle(x+w-2,y+1,1,h-2);
667     dc.setForeground(borderColor);
668     dc.fillRectangle(x,y+h-1,w,1);
669     dc.fillRectangle(x+w-1,y,1,h);
670     }
671   else{
672     dc.setForeground(borderColor);
673     dc.fillRectangle(x,y,w-2,1);
674     dc.fillRectangle(x,y,1,h-2);
675     dc.setForeground(shadowColor);
676     dc.fillRectangle(x+1,y+1,w-3,1);
677     dc.fillRectangle(x+1,y+1,1,h-3);
678     dc.setForeground(hiliteColor);
679     dc.fillRectangle(x,y+h-1,w-1,1);
680     dc.fillRectangle(x+w-1,y+1,1,h-1);
681     dc.setForeground(backColor);
682     dc.fillRectangle(x+1,y+h-2,w-1,1);
683     dc.fillRectangle(x+w-2,y+2,1,h-2);
684     }
685   }
686 
687 
688 // Draw left arrow
drawLeftArrow(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h,FXbool down)689 void FXScrollBar::drawLeftArrow(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h,FXbool down){
690   FXPoint points[3];
691   FXint ah,ab;
692   ab=(h-7)|1;
693   ah=ab>>1;
694   x=x+((w-ah)>>1);
695   y=y+((h-ab)>>1);
696   if(down){ ++x; ++y; }
697   points[0].x=x+ah;
698   points[0].y=y;
699   points[1].x=x+ah;
700   points[1].y=y+ab-1;
701   points[2].x=x;
702   points[2].y=y+(ab>>1);
703   dc.setForeground(arrowColor);
704   dc.fillPolygon(points,3);
705   }
706 
707 
708 // Draw right arrow
drawRightArrow(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h,FXbool down)709 void FXScrollBar::drawRightArrow(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h,FXbool down){
710   FXPoint points[3];
711   FXint ah,ab;
712   ab=(h-7)|1;
713   ah=ab>>1;
714   x=x+((w-ah)>>1);
715   y=y+((h-ab)>>1);
716   if(down){ ++x; ++y; }
717   points[0].x=x;
718   points[0].y=y;
719   points[1].x=x;
720   points[1].y=y+ab-1;
721   points[2].x=x+ah;
722   points[2].y=y+(ab>>1);
723   dc.setForeground(arrowColor);
724   dc.fillPolygon(points,3);
725   }
726 
727 
728 // Draw up arrow
drawUpArrow(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h,FXbool down)729 void FXScrollBar::drawUpArrow(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h,FXbool down){
730   FXPoint points[3];
731   FXint ah,ab;
732   ab=(w-7)|1;
733   ah=ab>>1;
734   x=x+((w-ab)>>1);
735   y=y+((h-ah)>>1);
736   if(down){ ++x; ++y; }
737   points[0].x=x+(ab>>1);
738   points[0].y=y-1;
739   points[1].x=x;
740   points[1].y=y+ah;
741   points[2].x=x+ab;
742   points[2].y=y+ah;
743   dc.setForeground(arrowColor);
744   dc.fillPolygon(points,3);
745   }
746 
747 
748 // Draw down arrow
drawDownArrow(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h,FXbool down)749 void FXScrollBar::drawDownArrow(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h,FXbool down){
750   FXPoint points[3];
751   FXint ah,ab;
752   ab=(w-7)|1;
753   ah=ab>>1;
754   x=x+((w-ab)>>1);
755   y=y+((h-ah)>>1);
756   if(down){ ++x; ++y; }
757   points[0].x=x+1;
758   points[0].y=y;
759   points[1].x=x+ab-1;
760   points[1].y=y;
761   points[2].x=x+(ab>>1);
762   points[2].y=y+ah;
763   dc.setForeground(arrowColor);
764   dc.fillPolygon(points,3);
765   }
766 
767 
768 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)769 long FXScrollBar::onPaint(FXObject*,FXSelector,void* ptr){
770   register FXEvent *ev=(FXEvent*)ptr;
771   register int total;
772   FXDCWindow dc(this,ev);
773   if(options&SCROLLBAR_HORIZONTAL){
774     total=width-height-height;
775     if(thumbsize<total){                                    // Scrollable
776       drawButton(dc,thumbpos,0,thumbsize,height,0);
777       dc.setStipple(STIPPLE_GRAY);
778       dc.setFillStyle(FILL_OPAQUESTIPPLED);
779       if(mode==MODE_PAGE_DEC){
780         dc.setForeground(backColor);
781         dc.setBackground(shadowColor);
782         }
783       else{
784         dc.setForeground(hiliteColor);
785         dc.setBackground(backColor);
786         }
787       dc.fillRectangle(height,0,thumbpos-height,height);
788       if(mode==MODE_PAGE_INC){
789         dc.setForeground(backColor);
790         dc.setBackground(shadowColor);
791         }
792       else{
793         dc.setForeground(hiliteColor);
794         dc.setBackground(backColor);
795         }
796       dc.fillRectangle(thumbpos+thumbsize,0,width-height-thumbpos-thumbsize,height);
797       }
798     else{                                                   // Non-scrollable
799       dc.setStipple(STIPPLE_GRAY);
800       dc.setFillStyle(FILL_OPAQUESTIPPLED);
801       dc.setForeground(hiliteColor);
802       dc.setBackground(backColor);
803       dc.fillRectangle(height,0,total,height);
804       }
805     dc.setFillStyle(FILL_SOLID);
806     drawButton(dc,width-height,0,height,height,(mode==MODE_INC));
807     drawRightArrow(dc,width-height,0,height,height,(mode==MODE_INC));
808     drawButton(dc,0,0,height,height,(mode==MODE_DEC));
809     drawLeftArrow(dc,0,0,height,height,(mode==MODE_DEC));
810     }
811   else{
812     total=height-width-width;
813     if(thumbsize<total){                                    // Scrollable
814       drawButton(dc,0,thumbpos,width,thumbsize,0);
815       dc.setStipple(STIPPLE_GRAY);
816       dc.setFillStyle(FILL_OPAQUESTIPPLED);
817       if(mode==MODE_PAGE_DEC){
818         dc.setForeground(backColor);
819         dc.setBackground(shadowColor);
820         }
821       else{
822         dc.setForeground(hiliteColor);
823         dc.setBackground(backColor);
824         }
825       dc.fillRectangle(0,width,width,thumbpos-width);
826       if(mode==MODE_PAGE_INC){
827         dc.setForeground(backColor);
828         dc.setBackground(shadowColor);
829         }
830       else{
831         dc.setForeground(hiliteColor);
832         dc.setBackground(backColor);
833         }
834       dc.fillRectangle(0,thumbpos+thumbsize,width,height-width-thumbpos-thumbsize);
835       }
836     else{                                                   // Non-scrollable
837       dc.setStipple(STIPPLE_GRAY);
838       dc.setFillStyle(FILL_OPAQUESTIPPLED);
839       dc.setForeground(hiliteColor);
840       dc.setBackground(backColor);
841       dc.fillRectangle(0,width,width,total);
842       }
843     dc.setFillStyle(FILL_SOLID);
844     drawButton(dc,0,height-width,width,width,(mode==MODE_INC));
845     drawDownArrow(dc,0,height-width,width,width,(mode==MODE_INC));
846     drawButton(dc,0,0,width,width,(mode==MODE_DEC));
847     drawUpArrow(dc,0,0,width,width,(mode==MODE_DEC));
848     }
849   return 1;
850   }
851 
852 
853 // Set range
setRange(FXint r)854 void FXScrollBar::setRange(FXint r){
855   if(r<1) r=1;
856   if(range!=r){
857     range=r;
858     setPage(page);
859     }
860   }
861 
862 
863 // Set page size
setPage(FXint p)864 void FXScrollBar::setPage(FXint p){
865   if(p<1) p=1;
866   if(p>range) p=range;
867   if(page!=p){
868     page=p;
869     setPosition(pos);
870     }
871   }
872 
873 
874 // Set line size
setLine(FXint l)875 void FXScrollBar::setLine(FXint l){
876   if(l<1) l=1;
877   line=l;
878   }
879 
880 
881 // Set position; tricky because the thumb size may have changed
882 // as well; we do the minimal possible update to repaint properly.
setPosition(FXint p)883 void FXScrollBar::setPosition(FXint p){
884   FXint total,travel,lo,hi,l,h;
885   pos=p;
886   if(pos<0) pos=0;
887   if(pos>(range-page)) pos=range-page;
888   lo=thumbpos;
889   hi=thumbpos+thumbsize;
890   if(options&SCROLLBAR_HORIZONTAL){
891     total=width-height-height;
892     thumbsize=(total*page)/range;
893     if(thumbsize<(barsize>>1)) thumbsize=(barsize>>1);
894     travel=total-thumbsize;
895     if(range>page){ thumbpos=height+(FXint)((((FXdouble)pos)*travel)/(range-page)); } else { thumbpos=height; }
896     l=thumbpos;
897     h=thumbpos+thumbsize;
898     if(l!=lo || h!=hi){
899       update(FXMIN(l,lo),0,FXMAX(h,hi)-FXMIN(l,lo),height);
900       }
901     }
902   else{
903     total=height-width-width;
904     thumbsize=(total*page)/range;
905     if(thumbsize<(barsize>>1)) thumbsize=(barsize>>1);
906     travel=total-thumbsize;
907     if(range>page){ thumbpos=width+(FXint)((((FXdouble)pos)*travel)/(range-page)); } else { thumbpos=width; }
908     l=thumbpos;
909     h=thumbpos+thumbsize;
910     if(l!=lo || h!=hi){
911       update(0,FXMIN(l,lo),width,FXMAX(h,hi)-FXMIN(l,lo));
912       }
913     }
914   }
915 
916 
917 // Set highlight color
setHiliteColor(FXColor clr)918 void FXScrollBar::setHiliteColor(FXColor clr){
919   if(hiliteColor!=clr){
920     hiliteColor=clr;
921     update();
922     }
923   }
924 
925 
926 // Set shadow color
setShadowColor(FXColor clr)927 void FXScrollBar::setShadowColor(FXColor clr){
928   if(shadowColor!=clr){
929     shadowColor=clr;
930     update();
931     }
932   }
933 
934 
935 // Set border color
setBorderColor(FXColor clr)936 void FXScrollBar::setBorderColor(FXColor clr){
937   if(borderColor!=clr){
938     borderColor=clr;
939     update();
940     }
941   }
942 
943 
944 // Set arrow color
setArrowColor(FXColor clr)945 void FXScrollBar::setArrowColor(FXColor clr){
946   if(arrowColor!=clr){
947     arrowColor=clr;
948     update();
949     }
950   }
951 
952 
953 // Change the scrollbar style
getScrollBarStyle() const954 FXuint FXScrollBar::getScrollBarStyle() const {
955   return (options&SCROLLBAR_MASK);
956   }
957 
958 
959 // Get the current scrollbar style
setScrollBarStyle(FXuint style)960 void FXScrollBar::setScrollBarStyle(FXuint style){
961   FXuint opts=(options&~SCROLLBAR_MASK) | (style&SCROLLBAR_MASK);
962   if(options!=opts){
963     options=opts;
964     recalc();
965     update();
966     }
967   }
968 
969 
970 // Change the bar size
setBarSize(FXint size)971 void FXScrollBar::setBarSize(FXint size){
972   if(barsize!=size){
973     barsize=size;
974     recalc();
975     }
976   }
977 
978 
979 // Save object to stream
save(FXStream & store) const980 void FXScrollBar::save(FXStream& store) const {
981   FXWindow::save(store);
982   store << barsize;
983   store << hiliteColor;
984   store << shadowColor;
985   store << borderColor;
986   store << arrowColor;
987   store << range;
988   store << page;
989   store << line;
990   store << pos;
991   }
992 
993 
994 // Load object from stream
load(FXStream & store)995 void FXScrollBar::load(FXStream& store){
996   FXWindow::load(store);
997   store >> barsize;
998   store >> hiliteColor;
999   store >> shadowColor;
1000   store >> borderColor;
1001   store >> arrowColor;
1002   store >> range;
1003   store >> page;
1004   store >> line;
1005   store >> pos;
1006   }
1007 
1008 
1009 // Delete
~FXScrollBar()1010 FXScrollBar::~FXScrollBar(){
1011   getApp()->removeTimeout(this,ID_TIMEWHEEL);
1012   getApp()->removeTimeout(this,ID_AUTOSCROLL);
1013   }
1014 
1015 
1016 /*******************************************************************************/
1017 
1018 // Map
1019 FXDEFMAP(FXScrollCorner) FXScrollCornerMap[]={
1020   FXMAPFUNC(SEL_PAINT,0,FXScrollCorner::onPaint),
1021   };
1022 
1023 
1024 // Object implementation
FXIMPLEMENT(FXScrollCorner,FXWindow,FXScrollCornerMap,ARRAYNUMBER (FXScrollCornerMap))1025 FXIMPLEMENT(FXScrollCorner,FXWindow,FXScrollCornerMap,ARRAYNUMBER(FXScrollCornerMap))
1026 
1027 
1028 // Deserialization
1029 FXScrollCorner::FXScrollCorner(){
1030   flags|=FLAG_ENABLED|FLAG_SHOWN;
1031   }
1032 
1033 
1034 // Construct and init
FXScrollCorner(FXComposite * p)1035 FXScrollCorner::FXScrollCorner(FXComposite* p):FXWindow(p){
1036   backColor=getApp()->getBaseColor();
1037   flags|=FLAG_ENABLED|FLAG_SHOWN;
1038   }
1039 
1040 
1041 // Slightly different from Frame border
onPaint(FXObject *,FXSelector,void * ptr)1042 long FXScrollCorner::onPaint(FXObject*,FXSelector,void* ptr){
1043   FXEvent *ev=(FXEvent*)ptr;
1044   FXDCWindow dc(this,ev);
1045   dc.setForeground(backColor);
1046   dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h);
1047   return 1;
1048   }
1049 
1050 
enable()1051 void FXScrollCorner::enable(){ }
1052 
1053 
disable()1054 void FXScrollCorner::disable(){ }
1055 
1056 }
1057