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