1 /********************************************************************************
2 * *
3 * R e a l S l i d e r W i d g e t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1997,2021 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published by *
10 * the Free Software Foundation; either version 3 of the License, or *
11 * (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 *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/> *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "fxkeys.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXMutex.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXSize.h"
32 #include "FXPoint.h"
33 #include "FXRectangle.h"
34 #include "FXStringDictionary.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXEvent.h"
38 #include "FXWindow.h"
39 #include "FXDCWindow.h"
40 #include "FXApp.h"
41 #include "FXRealSlider.h"
42
43
44
45
46 /*
47 Notes:
48 - Maybe add bindings for arrow keys for value changes.
49 - Yes, this *does* have a lot in common with FXSlider and its
50 probably a good idea to give FXRealSlider and FXSlider a common
51 base class at some point.
52 */
53
54 #define TICKSIZE 4 // Length of ticks
55 #define OVERHANG 4 // Default amount of overhang
56 #define MINOVERHANG 3 // Minimal amount of overhang
57 #define HEADINSIDEBAR 20 // Default for inside bar head size
58 #define HEADOVERHANGING 9 // Default for overhanging head size
59
60 #define REALSLIDER_MASK (REALSLIDER_VERTICAL|REALSLIDER_ARROW_UP|REALSLIDER_ARROW_DOWN|REALSLIDER_INSIDE_BAR|REALSLIDER_TICKS_TOP|REALSLIDER_TICKS_BOTTOM)
61
62 using namespace FX;
63
64 /*******************************************************************************/
65
66 namespace FX {
67
68 // Map
69 FXDEFMAP(FXRealSlider) FXRealSliderMap[]={
70 FXMAPFUNC(SEL_PAINT,0,FXRealSlider::onPaint),
71 FXMAPFUNC(SEL_MOTION,0,FXRealSlider::onMotion),
72 FXMAPFUNC(SEL_MOUSEWHEEL,0,FXRealSlider::onMouseWheel),
73 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXRealSlider::onLeftBtnPress),
74 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXRealSlider::onLeftBtnRelease),
75 FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXRealSlider::onMiddleBtnPress),
76 FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXRealSlider::onMiddleBtnRelease),
77 FXMAPFUNC(SEL_KEYPRESS,0,FXRealSlider::onKeyPress),
78 FXMAPFUNC(SEL_KEYRELEASE,0,FXRealSlider::onKeyRelease),
79 FXMAPFUNC(SEL_UNGRABBED,0,FXRealSlider::onUngrabbed),
80 FXMAPFUNC(SEL_QUERY_TIP,0,FXRealSlider::onQueryTip),
81 FXMAPFUNC(SEL_QUERY_HELP,0,FXRealSlider::onQueryHelp),
82 FXMAPFUNC(SEL_TIMEOUT,FXRealSlider::ID_AUTOSLIDE,FXRealSlider::onAutoSlide),
83 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_SETVALUE,FXRealSlider::onCmdSetValue),
84 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_SETINTVALUE,FXRealSlider::onCmdSetIntValue),
85 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_GETINTVALUE,FXRealSlider::onCmdGetIntValue),
86 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_SETLONGVALUE,FXRealSlider::onCmdSetLongValue),
87 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_GETLONGVALUE,FXRealSlider::onCmdGetLongValue),
88 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_SETREALVALUE,FXRealSlider::onCmdSetRealValue),
89 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_GETREALVALUE,FXRealSlider::onCmdGetRealValue),
90 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_SETINTRANGE,FXRealSlider::onCmdSetIntRange),
91 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_GETINTRANGE,FXRealSlider::onCmdGetIntRange),
92 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_SETREALRANGE,FXRealSlider::onCmdSetRealRange),
93 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_GETREALRANGE,FXRealSlider::onCmdGetRealRange),
94 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_SETHELPSTRING,FXRealSlider::onCmdSetHelp),
95 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_GETHELPSTRING,FXRealSlider::onCmdGetHelp),
96 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_SETTIPSTRING,FXRealSlider::onCmdSetTip),
97 FXMAPFUNC(SEL_COMMAND,FXRealSlider::ID_GETTIPSTRING,FXRealSlider::onCmdGetTip),
98 };
99
100
101 // Object implementation
FXIMPLEMENT(FXRealSlider,FXFrame,FXRealSliderMap,ARRAYNUMBER (FXRealSliderMap))102 FXIMPLEMENT(FXRealSlider,FXFrame,FXRealSliderMap,ARRAYNUMBER(FXRealSliderMap))
103
104
105 // Make a slider
106 FXRealSlider::FXRealSlider(){
107 flags|=FLAG_ENABLED;
108 headPos=0;
109 headSize=0;
110 slotSize=0;
111 slotColor=0;
112 dragPoint=0;
113 range[0]=0.0;
114 range[1]=0.0;
115 delta=0.0;
116 incr=0.01;
117 gran=0.0;
118 pos=0.0;
119 }
120
121
122 // Make a slider
FXRealSlider(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb)123 FXRealSlider::FXRealSlider(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb):FXFrame(p,opts,x,y,w,h,pl,pr,pt,pb){
124 flags|=FLAG_ENABLED;
125 baseColor=getApp()->getBaseColor();
126 hiliteColor=getApp()->getHiliteColor();
127 shadowColor=getApp()->getShadowColor();
128 borderColor=getApp()->getBorderColor();
129 target=tgt;
130 message=sel;
131 headPos=0;
132 headSize=(options&REALSLIDER_INSIDE_BAR)?HEADINSIDEBAR:HEADOVERHANGING;
133 slotSize=5;
134 slotColor=getApp()->getBackColor();
135 dragPoint=0;
136 range[0]=0.0;
137 range[1]=1.0;
138 delta=0.0;
139 incr=0.01;
140 gran=0.0;
141 pos=0.5;
142 }
143
144
145 // Enable the window
enable()146 void FXRealSlider::enable(){
147 if(!(flags&FLAG_ENABLED)){
148 FXFrame::enable();
149 update();
150 }
151 }
152
153
154 // Disable the window
disable()155 void FXRealSlider::disable(){
156 if(flags&FLAG_ENABLED){
157 FXFrame::disable();
158 update();
159 }
160 }
161
162
163 // Get default width
getDefaultWidth()164 FXint FXRealSlider::getDefaultWidth(){
165 FXint w;
166 if(options&REALSLIDER_VERTICAL){
167 if(options&REALSLIDER_INSIDE_BAR) w=4+headSize/2;
168 else if(options&(REALSLIDER_ARROW_LEFT|REALSLIDER_ARROW_RIGHT)) w=slotSize+MINOVERHANG*2+headSize/2;
169 else w=slotSize+MINOVERHANG*2;
170 if(options&REALSLIDER_TICKS_LEFT) w+=TICKSIZE;
171 if(options&REALSLIDER_TICKS_RIGHT) w+=TICKSIZE;
172 }
173 else{
174 w=headSize+4;
175 }
176 return w+padleft+padright+(border<<1);
177 }
178
179
180 // Get default height
getDefaultHeight()181 FXint FXRealSlider::getDefaultHeight(){
182 FXint h;
183 if(options&REALSLIDER_VERTICAL){
184 h=headSize+4;
185 }
186 else{
187 if(options&REALSLIDER_INSIDE_BAR) h=4+headSize/2;
188 else if(options&(REALSLIDER_ARROW_UP|REALSLIDER_ARROW_DOWN)) h=slotSize+2*MINOVERHANG+headSize/2;
189 else h=slotSize+MINOVERHANG*2;
190 if(options&REALSLIDER_TICKS_TOP) h+=TICKSIZE;
191 if(options&REALSLIDER_TICKS_BOTTOM) h+=TICKSIZE;
192 }
193 return h+padtop+padbottom+(border<<1);
194 }
195
196
197 // Returns true because a slider can receive focus
canFocus() const198 FXbool FXRealSlider::canFocus() const { return true; }
199
200
201 // Layout changed; even though the position is still
202 // the same, the head may have to be moved.
layout()203 void FXRealSlider::layout(){
204 setValue(pos);
205 flags&=~FLAG_DIRTY;
206 }
207
208
209 // Set help using a message
onCmdSetHelp(FXObject *,FXSelector,void * ptr)210 long FXRealSlider::onCmdSetHelp(FXObject*,FXSelector,void* ptr){
211 setHelpText(*((FXString*)ptr));
212 return 1;
213 }
214
215
216 // Get help using a message
onCmdGetHelp(FXObject *,FXSelector,void * ptr)217 long FXRealSlider::onCmdGetHelp(FXObject*,FXSelector,void* ptr){
218 *((FXString*)ptr)=getHelpText();
219 return 1;
220 }
221
222
223 // Set tip using a message
onCmdSetTip(FXObject *,FXSelector,void * ptr)224 long FXRealSlider::onCmdSetTip(FXObject*,FXSelector,void* ptr){
225 setTipText(*((FXString*)ptr));
226 return 1;
227 }
228
229
230 // Get tip using a message
onCmdGetTip(FXObject *,FXSelector,void * ptr)231 long FXRealSlider::onCmdGetTip(FXObject*,FXSelector,void* ptr){
232 *((FXString*)ptr)=getTipText();
233 return 1;
234 }
235
236
237 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)238 long FXRealSlider::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
239 if(FXFrame::onQueryTip(sender,sel,ptr)) return 1;
240 if((flags&FLAG_TIP) && !tip.empty()){
241 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip);
242 return 1;
243 }
244 return 0;
245 }
246
247
248 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)249 long FXRealSlider::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
250 if(FXFrame::onQueryHelp(sender,sel,ptr)) return 1;
251 if((flags&FLAG_HELP) && !help.empty()){
252 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
253 return 1;
254 }
255 return 0;
256 }
257
258
259 // Update value from a message
onCmdSetValue(FXObject *,FXSelector,void * ptr)260 long FXRealSlider::onCmdSetValue(FXObject*,FXSelector,void* ptr){
261 setValue((FXdouble)(FXival)ptr);
262 return 1;
263 }
264
265
266 // Update value from a message
onCmdSetIntValue(FXObject *,FXSelector,void * ptr)267 long FXRealSlider::onCmdSetIntValue(FXObject*,FXSelector,void* ptr){
268 setValue((FXdouble)*((FXint*)ptr));
269 return 1;
270 }
271
272
273 // Obtain value from text field
onCmdGetIntValue(FXObject *,FXSelector,void * ptr)274 long FXRealSlider::onCmdGetIntValue(FXObject*,FXSelector,void* ptr){
275 *((FXint*)ptr)=(FXint)getValue();
276 return 1;
277 }
278
279
280 // Update value from a message
onCmdSetLongValue(FXObject *,FXSelector,void * ptr)281 long FXRealSlider::onCmdSetLongValue(FXObject*,FXSelector,void* ptr){
282 setValue((FXdouble)*((FXlong*)ptr));
283 return 1;
284 }
285
286
287 // Obtain value with a message
onCmdGetLongValue(FXObject *,FXSelector,void * ptr)288 long FXRealSlider::onCmdGetLongValue(FXObject*,FXSelector,void* ptr){
289 *((FXlong*)ptr)=(FXlong)getValue();
290 return 1;
291 }
292
293
294 // Update value from a message
onCmdSetRealValue(FXObject *,FXSelector,void * ptr)295 long FXRealSlider::onCmdSetRealValue(FXObject*,FXSelector,void* ptr){
296 setValue(*((FXdouble*)ptr));
297 return 1;
298 }
299
300
301 // Obtain value with a message
onCmdGetRealValue(FXObject *,FXSelector,void * ptr)302 long FXRealSlider::onCmdGetRealValue(FXObject*,FXSelector,void* ptr){
303 *((FXdouble*)ptr)=getValue();
304 return 1;
305 }
306
307
308 // Update range from a message
onCmdSetIntRange(FXObject *,FXSelector,void * ptr)309 long FXRealSlider::onCmdSetIntRange(FXObject*,FXSelector,void* ptr){
310 setRange((FXdouble)((FXint*)ptr)[0],(FXdouble)((FXint*)ptr)[1]);
311 return 1;
312 }
313
314
315 // Get range with a message
onCmdGetIntRange(FXObject *,FXSelector,void * ptr)316 long FXRealSlider::onCmdGetIntRange(FXObject*,FXSelector,void* ptr){
317 ((FXint*)ptr)[0]=(FXint)range[0];
318 ((FXint*)ptr)[1]=(FXint)range[1];
319 return 1;
320 }
321
322
323 // Update range from a message
onCmdSetRealRange(FXObject *,FXSelector,void * ptr)324 long FXRealSlider::onCmdSetRealRange(FXObject*,FXSelector,void* ptr){
325 setRange(((FXdouble*)ptr)[0],((FXdouble*)ptr)[1]);
326 return 1;
327 }
328
329
330 // Get range with a message
onCmdGetRealRange(FXObject *,FXSelector,void * ptr)331 long FXRealSlider::onCmdGetRealRange(FXObject*,FXSelector,void* ptr){
332 ((FXdouble*)ptr)[0]=range[0];
333 ((FXdouble*)ptr)[1]=range[1];
334 return 1;
335 }
336
337
338 // Pressed LEFT button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)339 long FXRealSlider::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
340 FXEvent *event=(FXEvent*)ptr;
341 FXdouble p=pos;
342 flags&=~FLAG_TIP;
343 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
344 if(isEnabled()){
345 grab();
346 getApp()->removeTimeout(this,ID_AUTOSLIDE);
347 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
348 flags&=~FLAG_UPDATE;
349 if(options&REALSLIDER_VERTICAL){
350 if(event->win_y<headPos){
351 getApp()->addTimeout(this,ID_AUTOSLIDE,getApp()->getScrollDelay(),(void*)(FXival)1);
352 p+=incr;
353 }
354 else if(event->win_y>(headPos+headSize)){
355 getApp()->addTimeout(this,ID_AUTOSLIDE,getApp()->getScrollDelay(),(void*)(FXival)-1);
356 p-=incr;
357 }
358 else{
359 dragPoint=event->win_y-headPos;
360 flags|=FLAG_PRESSED;
361 }
362 }
363 else{
364 if(event->win_x<headPos){
365 getApp()->addTimeout(this,ID_AUTOSLIDE,getApp()->getScrollDelay(),(void*)(FXival)-1);
366 p-=incr;
367 }
368 else if(event->win_x>(headPos+headSize)){
369 getApp()->addTimeout(this,ID_AUTOSLIDE,getApp()->getScrollDelay(),(void*)(FXival)1);
370 p+=incr;
371 }
372 else{
373 dragPoint=event->win_x-headPos;
374 flags|=FLAG_PRESSED;
375 }
376 }
377 if(0.0<gran) p=gran*Math::round(p/gran);
378 if(p<range[0]) p=range[0];
379 if(p>range[1]) p=range[1];
380 if(p!=pos){
381 setValue(p);
382 flags|=FLAG_CHANGED;
383 if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)&pos);
384 }
385 return 1;
386 }
387 return 0;
388 }
389
390
391 // Released Left button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)392 long FXRealSlider::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
393 FXuint flgs=flags;
394 if(isEnabled()){
395 ungrab();
396 getApp()->removeTimeout(this,ID_AUTOSLIDE);
397 setValue(pos); // Hop to exact position
398 flags&=~FLAG_PRESSED;
399 flags&=~FLAG_CHANGED;
400 flags|=FLAG_UPDATE;
401 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
402 if(flgs&FLAG_CHANGED){
403 if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)&pos);
404 }
405 return 1;
406 }
407 return 0;
408 }
409
410
411 // Moving
onMotion(FXObject *,FXSelector,void * ptr)412 long FXRealSlider::onMotion(FXObject*,FXSelector,void* ptr){
413 FXEvent *event=(FXEvent*)ptr;
414 FXint xx,yy,ww,hh,lo,hi,h,travel;
415 FXdouble p;
416 if(!isEnabled()) return 0;
417 if(flags&FLAG_PRESSED){
418 yy=border+padtop+2;
419 xx=border+padleft+2;
420 hh=height-(border<<1)-padtop-padbottom-4;
421 ww=width-(border<<1)-padleft-padright-4;
422 if(options&REALSLIDER_VERTICAL){
423 h=event->win_y-dragPoint;
424 travel=hh-headSize;
425 if(h<yy) h=yy;
426 if(h>yy+travel) h=yy+travel;
427 if(h!=headPos){
428 FXMINMAX(lo,hi,headPos,h);
429 headPos=h;
430 update(border,lo-1,width-(border<<1),hi+headSize+2-lo);
431 }
432 if(travel>0)
433 p=range[0]+((range[1]-range[0])*(yy+travel-h))/travel;
434 else
435 p=range[0];
436 }
437 else{
438 h=event->win_x-dragPoint;
439 travel=ww-headSize;
440 if(h<xx) h=xx;
441 if(h>xx+travel) h=xx+travel;
442 if(h!=headPos){
443 FXMINMAX(lo,hi,headPos,h);
444 headPos=h;
445 update(lo-1,border,hi+headSize+2-lo,height-(border<<1));
446 }
447 if(travel>0)
448 p=range[0]+((range[1]-range[0])*(h-xx))/travel;
449 else
450 p=range[0];
451 }
452 if(0.0<gran) p=gran*Math::round(p/gran);
453 if(p<range[0]) p=range[0];
454 if(p>range[1]) p=range[1];
455 if(pos!=p){
456 pos=p;
457 flags|=FLAG_CHANGED;
458 if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)&pos);
459 }
460 return 1;
461 }
462 return 0;
463 }
464
465
466 // Pressed middle or right
onMiddleBtnPress(FXObject *,FXSelector,void * ptr)467 long FXRealSlider::onMiddleBtnPress(FXObject*,FXSelector,void* ptr){
468 FXEvent *event=(FXEvent*)ptr;
469 FXint xx,yy,ww,hh,lo,hi,h,travel;
470 FXdouble p;
471 flags&=~FLAG_TIP;
472 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
473 if(isEnabled()){
474 grab();
475 if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONPRESS,message),ptr)) return 1;
476 dragPoint=headSize/2;
477 yy=border+padtop+2;
478 xx=border+padleft+2;
479 hh=height-(border<<1)-padtop-padbottom-4;
480 ww=width-(border<<1)-padleft-padright-4;
481 flags&=~FLAG_UPDATE;
482 flags|=FLAG_PRESSED;
483 if(options&REALSLIDER_VERTICAL){
484 h=event->win_y-dragPoint;
485 travel=hh-headSize;
486 if(h<yy) h=yy;
487 if(h>yy+travel) h=yy+travel;
488 if(h!=headPos){
489 FXMINMAX(lo,hi,headPos,h);
490 headPos=h;
491 update(border,lo-1,width-(border<<1),hi+headSize+2-lo);
492 }
493 if(travel>0)
494 p=range[0]+((range[1]-range[0])*(yy+travel-h))/travel;
495 else
496 p=range[0];
497 }
498 else{
499 h=event->win_x-dragPoint;
500 travel=ww-headSize;
501 if(h<xx) h=xx;
502 if(h>xx+travel) h=xx+travel;
503 if(h!=headPos){
504 FXMINMAX(lo,hi,headPos,h);
505 headPos=h;
506 update(lo-1,border,hi+headSize+2-lo,height-(border<<1));
507 }
508 if(travel>0)
509 p=range[0]+((range[1]-range[0])*(h-xx))/travel;
510 else
511 p=range[0];
512 }
513 if(0.0<gran) p=gran*Math::round(p/gran);
514 if(p<range[0]) p=range[0];
515 if(p>range[1]) p=range[1];
516 if(p!=pos){
517 pos=p;
518 flags|=FLAG_CHANGED;
519 if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)&pos);
520 }
521 return 1;
522 }
523 return 0;
524 }
525
526
527 // Released middle button
onMiddleBtnRelease(FXObject *,FXSelector,void * ptr)528 long FXRealSlider::onMiddleBtnRelease(FXObject*,FXSelector,void* ptr){
529 FXuint flgs=flags;
530 if(isEnabled()){
531 ungrab();
532 getApp()->removeTimeout(this,ID_AUTOSLIDE);
533 flags&=~FLAG_PRESSED;
534 flags&=~FLAG_CHANGED;
535 flags|=FLAG_UPDATE;
536 setValue(pos); // Hop to exact position
537 if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONRELEASE,message),ptr)) return 1;
538 if(flgs&FLAG_CHANGED){
539 if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)&pos);
540 }
541 return 1;
542 }
543 return 0;
544 }
545
546
547 // Mouse wheel
onMouseWheel(FXObject *,FXSelector,void * ptr)548 long FXRealSlider::onMouseWheel(FXObject*,FXSelector,void* ptr){
549 FXEvent *event=(FXEvent*)ptr;
550 FXdouble p=pos+incr*(event->code/120);
551 if(p<range[0]) p=range[0];
552 if(p>range[1]) p=range[1];
553 if(pos!=p){
554 setValue(p);
555 if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)&pos);
556 }
557 return 1;
558 }
559
560
561 // Keyboard press
onKeyPress(FXObject *,FXSelector,void * ptr)562 long FXRealSlider::onKeyPress(FXObject*,FXSelector,void* ptr){
563 FXEvent* event=(FXEvent*)ptr;
564 if(isEnabled()){
565 if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
566 switch(event->code){
567 case KEY_Left:
568 case KEY_KP_Left:
569 if(!(options&REALSLIDER_VERTICAL)) goto dec;
570 break;
571 case KEY_Right:
572 case KEY_KP_Right:
573 if(!(options&REALSLIDER_VERTICAL)) goto inc;
574 break;
575 case KEY_Up:
576 case KEY_KP_Up:
577 if(options&REALSLIDER_VERTICAL) goto inc;
578 break;
579 case KEY_Down:
580 case KEY_KP_Down:
581 if(options&REALSLIDER_VERTICAL) goto dec;
582 break;
583 case KEY_plus:
584 case KEY_KP_Add:
585 inc: setValue(pos+incr,true);
586 return 1;
587 case KEY_minus:
588 case KEY_KP_Subtract:
589 dec: setValue(pos-incr,true);
590 return 1;
591 }
592 }
593 return 0;
594 }
595
596
597 // Keyboard release
onKeyRelease(FXObject *,FXSelector,void * ptr)598 long FXRealSlider::onKeyRelease(FXObject*,FXSelector,void* ptr){
599 FXEvent* event=(FXEvent*)ptr;
600 if(isEnabled()){
601 if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
602 switch(event->code){
603 case KEY_Left:
604 case KEY_KP_Left:
605 case KEY_Right:
606 case KEY_KP_Right:
607 if(!(options&REALSLIDER_VERTICAL)) return 1;
608 break;
609 case KEY_Up:
610 case KEY_KP_Up:
611 case KEY_Down:
612 case KEY_KP_Down:
613 if(options&REALSLIDER_VERTICAL) return 1;
614 break;
615 case KEY_plus:
616 case KEY_KP_Add:
617 case KEY_KP_Subtract:
618 case KEY_minus:
619 return 1;
620 }
621 }
622 return 0;
623 }
624
625
626 // The widget lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)627 long FXRealSlider::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
628 FXFrame::onUngrabbed(sender,sel,ptr);
629 getApp()->removeTimeout(this,ID_AUTOSLIDE);
630 flags&=~FLAG_PRESSED;
631 flags&=~FLAG_CHANGED;
632 flags|=FLAG_UPDATE;
633 return 1;
634 }
635
636
637 // Automatically move slider while holding down mouse
onAutoSlide(FXObject *,FXSelector,void * ptr)638 long FXRealSlider::onAutoSlide(FXObject*,FXSelector,void* ptr){
639 FXint dir=(FXint)(FXival)ptr;
640 FXdouble p=pos+incr*dir;
641 if(p<=range[0]){
642 p=range[0];
643 }
644 else if(p>=range[1]){
645 p=range[1];
646 }
647 else{
648 getApp()->addTimeout(this,ID_AUTOSLIDE,getApp()->getScrollSpeed(),(void*)(FXival)dir);
649 }
650 if(p!=pos){
651 setValue(p);
652 flags|=FLAG_CHANGED;
653 if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)&pos);
654 return 1;
655 }
656 return 0;
657 }
658
659
660 // Draw horizontal ticks
drawHorzTicks(FXDCWindow & dc,FXint,FXint y,FXint,FXint)661 void FXRealSlider::drawHorzTicks(FXDCWindow& dc,FXint,FXint y,FXint,FXint){
662 FXdouble interval=range[1]-range[0];
663 FXint travel,offset,p;
664 FXdouble v,d;
665 if(0.0<interval){
666 d=delta;
667 if(d<=0.0) d=incr;
668 dc.setForeground(FXRGB(0,0,0));
669 travel=width-(border<<1)-padleft-padright-headSize-4;
670 offset=border+padleft+2+headSize/2;
671 for(v=range[0]; v<=range[1]; v+=d){
672 p=offset+(FXint)(0.5+((travel*(v-range[0]))/interval));
673 dc.fillRectangle(p,y,1,TICKSIZE);
674 }
675 }
676 }
677
678
679 // Draw vertical ticks
drawVertTicks(FXDCWindow & dc,FXint x,FXint,FXint,FXint)680 void FXRealSlider::drawVertTicks(FXDCWindow& dc,FXint x,FXint,FXint,FXint){
681 FXdouble interval=range[1]-range[0];
682 FXint travel,offset,p;
683 FXdouble v,d;
684 if(0.0<interval){
685 d=delta;
686 if(d<=0.0) d=incr;
687 dc.setForeground(FXRGB(0,0,0));
688 travel=height-(border<<1)-padtop-padbottom-headSize-4;
689 offset=height-border-padbottom-2-headSize/2;
690 for(v=range[0]; v<=range[1]; v+=d){
691 p=offset-(FXint)(0.5+((travel*(v-range[0]))/interval));
692 dc.fillRectangle(x,p,TICKSIZE,1);
693 }
694 }
695 }
696
697
698 // Draw slider head
drawSliderHead(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)699 void FXRealSlider::drawSliderHead(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
700 FXint m;
701 dc.setForeground(baseColor);
702 dc.fillRectangle(x,y,w,h);
703 if(options&REALSLIDER_VERTICAL){
704 m=(h>>1);
705 if(options&REALSLIDER_ARROW_LEFT){
706 dc.setForeground(hiliteColor);
707 dc.drawLine(x+m,y,x+w-1,y);
708 dc.drawLine(x,y+m,x+m,y);
709 dc.setForeground(shadowColor);
710 dc.drawLine(x+1,y+h-m-1,x+m+1,y+h-1);
711 dc.drawLine(x+m,y+h-2,x+w-1,y+h-2);
712 dc.drawLine(x+w-2,y+1,x+w-2,y+h-1);
713 dc.setForeground(borderColor);
714 dc.drawLine(x,y+h-m-1,x+m,y+h-1);
715 dc.drawLine(x+w-1,y+h-1,x+w-1,y);
716 dc.fillRectangle(x+m,y+h-1,w-m,1);
717 }
718 else if(options&REALSLIDER_ARROW_RIGHT){
719 dc.setForeground(hiliteColor);
720 dc.drawLine(x,y,x+w-m-1,y);
721 dc.drawLine(x,y+1,x,y+h-1);
722 dc.drawLine(x+w-1,y+m,x+w-m-1,y);
723 #ifdef WIN32
724 dc.setForeground(shadowColor);
725 dc.drawLine(x+w-1,y+h-m-2,x+w-m-2,y+h-1);
726 dc.drawLine(x+1,y+h-2,x+w-m-1,y+h-2);
727 dc.setForeground(borderColor);
728 dc.drawLine(x+w,y+h-m-2,x+w-m-1,y+h-1);
729 dc.drawLine(x,y+h-1,x+w-m-1,y+h-1);
730 #else
731 dc.setForeground(shadowColor);
732 dc.drawLine(x+w-2,y+h-m-1,x+w-m-2,y+h-1);
733 dc.drawLine(x+1,y+h-2,x+w-m-1,y+h-2);
734 dc.setForeground(borderColor);
735 dc.drawLine(x+w-1,y+h-m-1,x+w-m-1,y+h-1);
736 dc.drawLine(x,y+h-1,x+w-m-1,y+h-1);
737 #endif
738 }
739 else if(options&REALSLIDER_INSIDE_BAR){
740 drawDoubleRaisedRectangle(dc,x,y,w,h);
741 dc.setForeground(shadowColor);
742 dc.drawLine(x+1,y+m-1,x+w-2,y+m-1);
743 dc.setForeground(hiliteColor);
744 dc.drawLine(x+1,y+m,x+w-2,y+m);
745 }
746 else{
747 drawDoubleRaisedRectangle(dc,x,y,w,h);
748 }
749 }
750 else{
751 m=(w>>1);
752 if(options&REALSLIDER_ARROW_UP){
753 dc.setForeground(hiliteColor);
754 dc.drawLine(x,y+m,x+m,y);
755 dc.drawLine(x,y+m,x,y+h-1);
756 dc.setForeground(shadowColor);
757 dc.drawLine(x+w-1,y+m+1,x+w-m-1,y+1);
758 dc.drawLine(x+w-2,y+m+1,x+w-2,y+h-1);
759 dc.drawLine(x+1,y+h-2,x+w-2,y+h-2);
760 dc.setForeground(borderColor);
761 dc.drawLine(x+w-1,y+m,x+w-m-1,y);
762 dc.drawLine(x+w-1,y+m,x+w-1,y+h-1);
763 dc.fillRectangle(x,y+h-1,w,1);
764 }
765 else if(options&REALSLIDER_ARROW_DOWN){
766 dc.setForeground(hiliteColor);
767 dc.drawLine(x,y,x+w-1,y);
768 dc.drawLine(x,y+1,x,y+h-m-1);
769 dc.drawLine(x,y+h-m-1,x+m,y+h-1);
770 dc.setForeground(shadowColor);
771 dc.drawLine(x+w-2,y+1,x+w-2,y+h-m-1);
772 dc.drawLine(x+w-1,y+h-m-2,x+w-m-1,y+h-2);
773 dc.setForeground(borderColor);
774 dc.drawLine(x+w-1,y+h-m-1,x+w-m-1,y+h-1);
775 dc.fillRectangle(x+w-1,y,1,h-m);
776 }
777 else if(options&REALSLIDER_INSIDE_BAR){
778 drawDoubleRaisedRectangle(dc,x,y,w,h);
779 dc.setForeground(shadowColor);
780 dc.drawLine(x+m-1,y+1,x+m-1,y+h-2);
781 dc.setForeground(hiliteColor);
782 dc.drawLine(x+m,y+1,x+m,y+h-1);
783 }
784 else{
785 drawDoubleRaisedRectangle(dc,x,y,w,h);
786 }
787 }
788 }
789
790
791 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)792 long FXRealSlider::onPaint(FXObject*,FXSelector,void* ptr){
793 FXDCWindow dc(this,(FXEvent*)ptr);
794 FXint tx,ty,hhs=headSize/2;
795 FXint xx,yy,ww,hh;
796
797 // Repaint background
798 dc.setForeground(backColor);
799 dc.fillRectangle(0,0,width,height);
800
801 // Repaint border
802 drawFrame(dc,0,0,width,height);
803
804 // Slot placement
805 xx=border+padleft;
806 yy=border+padtop;
807 ww=width-(border<<1)-padleft-padright;
808 hh=height-(border<<1)-padtop-padbottom;
809
810 // Draw the slot
811 if(options&REALSLIDER_VERTICAL){
812
813 // Adjust slot placement for tickmarks
814 if(options&REALSLIDER_TICKS_LEFT){ xx+=TICKSIZE; ww-=TICKSIZE; }
815 if(options&REALSLIDER_TICKS_RIGHT){ ww-=TICKSIZE; }
816
817 // Draw slider
818 if(options&REALSLIDER_INSIDE_BAR){
819 drawDoubleSunkenRectangle(dc,xx,yy,ww,hh);
820 dc.setStipple(STIPPLE_GRAY);
821 dc.setForeground(slotColor);
822 dc.setBackground(baseColor);
823 dc.setFillStyle(FILL_OPAQUESTIPPLED);
824 dc.fillRectangle(xx+2,yy+2,ww-4,hh-4);
825 dc.setFillStyle(FILL_SOLID);
826 if(options&REALSLIDER_TICKS_LEFT) drawVertTicks(dc,border+padleft,yy,ww,hh);
827 if(options&REALSLIDER_TICKS_RIGHT) drawVertTicks(dc,width-padright-border-TICKSIZE,yy,ww,hh);
828 if(isEnabled()) drawSliderHead(dc,xx+2,headPos,ww-4,headSize);
829 }
830 else{
831 if(options&REALSLIDER_ARROW_LEFT) tx=xx+hhs+(ww-slotSize-hhs)/2;
832 else if(options&REALSLIDER_ARROW_RIGHT) tx=xx+(ww-slotSize-hhs)/2;
833 else tx=xx+(ww-slotSize)/2;
834 drawDoubleSunkenRectangle(dc,tx,yy,slotSize,hh);
835 dc.setForeground(slotColor);
836 dc.fillRectangle(tx+2,yy+2,slotSize-4,hh-4);
837 if(options&REALSLIDER_TICKS_LEFT) drawVertTicks(dc,border+padleft,yy,ww,hh);
838 if(options&REALSLIDER_TICKS_RIGHT) drawVertTicks(dc,width-padright-border-TICKSIZE,yy,ww,hh);
839 if(isEnabled()) drawSliderHead(dc,xx,headPos,ww,headSize);
840 }
841 }
842 else{
843
844 // Adjust slot placement for tickmarks
845 if(options&REALSLIDER_TICKS_TOP){ yy+=TICKSIZE; hh-=TICKSIZE; }
846 if(options&REALSLIDER_TICKS_BOTTOM){ hh-=TICKSIZE; }
847
848 // Draw slider
849 if(options&REALSLIDER_INSIDE_BAR){
850 drawDoubleSunkenRectangle(dc,xx,yy,ww,hh);
851 dc.setForeground(slotColor);
852 dc.setStipple(STIPPLE_GRAY);
853 dc.setForeground(slotColor);
854 dc.setBackground(baseColor);
855 dc.setFillStyle(FILL_OPAQUESTIPPLED);
856 dc.fillRectangle(xx+2,yy+2,ww-4,hh-4);
857 dc.setFillStyle(FILL_SOLID);
858 if(options&REALSLIDER_TICKS_TOP) drawHorzTicks(dc,xx,border+padtop,ww,hh);
859 if(options&REALSLIDER_TICKS_BOTTOM) drawHorzTicks(dc,xx,height-border-padbottom-TICKSIZE,ww,hh);
860 if(isEnabled()) drawSliderHead(dc,headPos,yy+2,headSize,hh-4);
861 }
862 else{
863 if(options&REALSLIDER_ARROW_UP) ty=yy+hhs+(hh-slotSize-hhs)/2;
864 else if(options&REALSLIDER_ARROW_DOWN) ty=yy+(hh-slotSize-hhs)/2;
865 else ty=yy+(hh-slotSize)/2;
866 drawDoubleSunkenRectangle(dc,xx,ty,ww,slotSize);
867 dc.setForeground(slotColor);
868 dc.fillRectangle(xx+2,ty+2,ww-4,slotSize-4);
869 if(options&REALSLIDER_TICKS_TOP) drawHorzTicks(dc,xx,border+padtop,ww,hh);
870 if(options&REALSLIDER_TICKS_BOTTOM) drawHorzTicks(dc,xx,height-border-padbottom-TICKSIZE,ww,hh);
871 if(isEnabled()) drawSliderHead(dc,headPos,yy,headSize,hh);
872 }
873 }
874 return 1;
875 }
876
877
878 // Set slider range; this also revalidates the position,
879 // and possibly moves the head [even if the position was still OK,
880 // the head might still have to be moved to the exact position].
setRange(FXdouble lo,FXdouble hi,FXbool notify)881 void FXRealSlider::setRange(FXdouble lo,FXdouble hi,FXbool notify){
882 if(lo>hi){ fxerror("%s::setRange: trying to set negative range.\n",getClassName()); }
883 if(range[0]!=lo || range[1]!=hi){
884 if(options&(REALSLIDER_TICKS_TOP|REALSLIDER_TICKS_BOTTOM)) update();
885 range[0]=lo;
886 range[1]=hi;
887 setValue(pos,notify);
888 }
889 }
890
891
892 // Set position; this should always cause the head to reflect
893 // the exact [discrete] value representing pos, even if several
894 // head positions may represent the same position!
895 // Also, the minimal amount is repainted, as one sometimes as very
896 // large/wide sliders.
setValue(FXdouble p,FXbool notify)897 void FXRealSlider::setValue(FXdouble p,FXbool notify){
898 FXdouble interval=range[1]-range[0];
899 FXint travel,lo,hi,h;
900 if(p<range[0]) p=range[0];
901 if(p>range[1]) p=range[1];
902 if(options&REALSLIDER_VERTICAL){
903 travel=height-(border<<1)-padtop-padbottom-headSize-4;
904 h=height-border-padbottom-2-headSize;
905 if(0<interval) h-=(FXint)(0.5+((travel*(p-range[0]))/interval));
906 if(h!=headPos){
907 FXMINMAX(lo,hi,headPos,h);
908 headPos=h;
909 update(border,lo-1,width-(border<<1),hi+headSize+2-lo);
910 }
911 }
912 else{
913 travel=width-(border<<1)-padleft-padright-headSize-4;
914 h=border+padleft+2;
915 if(0<interval) h+=(FXint)(0.5+((travel*(p-range[0]))/interval));
916 if(h!=headPos){
917 FXMINMAX(lo,hi,headPos,h);
918 headPos=h;
919 update(lo-1,border,hi+headSize+2-lo,height-(border<<1));
920 }
921 }
922 if(pos!=p){
923 pos=p;
924 if(notify && target){target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)&pos);}
925 }
926 }
927
928
929 // Get slider options
getSliderStyle() const930 FXuint FXRealSlider::getSliderStyle() const {
931 return (options&REALSLIDER_MASK);
932 }
933
934
935 // Set slider options
setSliderStyle(FXuint style)936 void FXRealSlider::setSliderStyle(FXuint style){
937 FXuint opts=(options&~REALSLIDER_MASK) | (style&REALSLIDER_MASK);
938 if(options!=opts){
939 headSize=(opts&REALSLIDER_INSIDE_BAR)?HEADINSIDEBAR:HEADOVERHANGING;
940 options=opts;
941 recalc();
942 update();
943 }
944 }
945
946
947 // Set head size
setHeadSize(FXint hs)948 void FXRealSlider::setHeadSize(FXint hs){
949 if(headSize!=hs){
950 headSize=hs;
951 recalc();
952 update();
953 }
954 }
955
956
957 // Set slot size
setSlotSize(FXint bs)958 void FXRealSlider::setSlotSize(FXint bs){
959 if(slotSize!=bs){
960 slotSize=bs;
961 recalc();
962 update();
963 }
964 }
965
966
967 // Set slider increment
setIncrement(FXdouble inc)968 void FXRealSlider::setIncrement(FXdouble inc){
969 if(inc<=0.0){ fxerror("%s::setIncrement: negative or zero increment specified.\n",getClassName()); }
970 incr=inc;
971 }
972
973
974 // Change slider granularity
setGranularity(FXdouble gr)975 void FXRealSlider::setGranularity(FXdouble gr){
976 if(gr<0.0){ fxerror("%s::setGranularity: negative granularity specified.\n",getClassName()); }
977 gran=gr;
978 }
979
980
981 // Set slot color
setSlotColor(FXColor clr)982 void FXRealSlider::setSlotColor(FXColor clr){
983 if(slotColor!=clr){
984 slotColor=clr;
985 update();
986 }
987 }
988
989
990 // Change the delta between ticks
setTickDelta(FXdouble dist)991 void FXRealSlider::setTickDelta(FXdouble dist){
992 if(dist<0.0) dist=0.0;
993 if(delta!=dist){
994 if(options&(REALSLIDER_TICKS_TOP|REALSLIDER_TICKS_BOTTOM)) update();
995 delta=dist;
996 }
997 }
998
999
1000 // Save object to stream
save(FXStream & store) const1001 void FXRealSlider::save(FXStream& store) const {
1002 FXFrame::save(store);
1003 store << headSize;
1004 store << slotSize;
1005 store << slotColor;
1006 store << range[0];
1007 store << range[1];
1008 store << delta;
1009 store << incr;
1010 store << gran;
1011 store << pos;
1012 store << help;
1013 store << tip;
1014 }
1015
1016
1017 // Load object from stream
load(FXStream & store)1018 void FXRealSlider::load(FXStream& store){
1019 FXFrame::load(store);
1020 store >> headSize;
1021 store >> slotSize;
1022 store >> slotColor;
1023 store >> range[0];
1024 store >> range[1];
1025 store >> delta;
1026 store >> incr;
1027 store >> gran;
1028 store >> pos;
1029 store >> help;
1030 store >> tip;
1031 }
1032
1033
1034 // Delete
~FXRealSlider()1035 FXRealSlider::~FXRealSlider(){
1036 getApp()->removeTimeout(this,ID_AUTOSLIDE);
1037 }
1038
1039 }
1040