SUMO - Simulation of Urban MObility
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FXRealSpinDial.cpp
Go to the documentation of this file.
1 /****************************************************************************/
10 //
11 /****************************************************************************/
12 // SUMO, Simulation of Urban MObility; see http://sumo-sim.org/
13 // Copyright (C) 2004-2014 DLR (http://www.dlr.de/) and contributors
14 /****************************************************************************/
15 //
16 // This file is part of SUMO.
17 // SUMO is free software: you can redistribute it and/or modify
18 // it under the terms of the GNU General Public License as published by
19 // the Free Software Foundation, either version 3 of the License, or
20 // (at your option) any later version.
21 //
22 /****************************************************************************/
23 
24 
25 /* =========================================================================
26  * included modules
27  * ======================================================================= */
28 #ifdef _MSC_VER
29 #include <windows_config.h>
30 #else
31 #include <config.h>
32 #endif
33 
34 #include <fx.h>
35 #include "xincs.h"
36 #include "fxver.h"
37 #include "fxdefs.h"
38 #include "fxkeys.h"
39 #include "FXStream.h"
40 #include "FXString.h"
41 #include "FXSize.h"
42 #include "FXPoint.h"
43 #include "FXRectangle.h"
44 #include "FXRegistry.h"
45 #include "FXAccelTable.h"
46 #include "FXApp.h"
47 #include "FXLabel.h"
48 #include "FXTextField.h"
49 #include "FXDial.h"
50 #include "FXRealSpinDial.h"
51 
52 #include <float.h>
53 
54 #ifdef CHECK_MEMORY_LEAKS
55 #include <foreign/nvwa/debug_new.h>
56 #endif // CHECK_MEMORY_LEAKS
57 /*
58  Notes:
59  - Based originally on Lyle's FXSpinner.
60  - Can use with spin buttons, dial, or both, and with or without text
61  - Three increment levels, fine, normal, and coarse. Access different modes
62  with CONTROL key (fine control) and SHIFT key (coarse mode). Modifiers
63  affect all of up/down keys, mousewheel, dial and spinbuttons.
64  - Can specify display format for text either as a precision,showExponent pair
65  or an sprintf format string. (String format can include extra text like '$'!)
66  - Wheel mouse increment/decrement in even multiples of fine/norm/coarse scales.
67  (Key modifers sometimes require mouse motion to kick in because FOX doesn't
68  have a [public] way to query the key state asynchronously. Hacked extern to
69  FOX's internal WIN32 function for querying this, so it works on Win32)
70  - Dial warps the pointer at the edge of the screen so you don't run out of
71  screen real estate.
72 */
73 #define DIALINCR 160
74 #define DIALMULT 40
75 #define DIALWIDTH 12
76 #define BUTTONWIDTH 12
77 #define GAPWIDTH 1
78 
79 #define INTMAX 2147483647
80 #define INTMIN (-INTMAX-1)
81 
82 #define SPINDIAL_MASK (SPINDIAL_CYCLIC|SPINDIAL_NOTEXT|SPINDIAL_NOBUTTONS|SPINDIAL_NODIAL|SPINDIAL_NOMAX|SPINDIAL_NOMIN|SPINDIAL_LOG)
83 
84 using namespace FX;
85 
86 /*******************************************************************************/
87 /* Custom FXDial subclass */
88 /*******************************************************************************/
89 namespace FX {
90 class FXRealSpinDialDial : public FXDial {
91  FXDECLARE(FXRealSpinDialDial)
92 protected:
94 private:
96  FXRealSpinDialDial& operator=(const FXRealSpinDialDial&);
97 public:
98  //long onDefault(FXObject*,FXSelector,void* );
99  long onKey(FXObject*, FXSelector, void*);
100  long onButtonPress(FXObject*, FXSelector, void*);
101  long onButtonRelease(FXObject*, FXSelector, void*);
102  long onRightButtonPress(FXObject*, FXSelector, void*);
103  long onRightButtonRelease(FXObject*, FXSelector, void*);
104  long onMotion(FXObject*, FXSelector, void*);
105  long onAuto(FXObject*, FXSelector, void*);
106  enum {
107  ID_AUTOSPIN = FXDial::ID_LAST,
108  ID_LAST
109  };
110 public:
111 
113  FXRealSpinDialDial(FXComposite* p, FXObject* tgt = NULL, FXSelector sel = 0, FXuint opts = DIAL_NORMAL,
114  FXint x = 0, FXint y = 0, FXint w = 0, FXint h = 0,
115  FXint pl = DEFAULT_PAD, FXint pr = DEFAULT_PAD, FXint pt = DEFAULT_PAD, FXint pb = DEFAULT_PAD):
116  FXDial(p, tgt, sel, opts, x, y, w, h, pl, pr, pt, pb) {}
117 
118 };
119 
120 FXDEFMAP(FXRealSpinDialDial) FXSpinDialMap[] = {
121  FXMAPFUNC(SEL_KEYPRESS, 0, FXRealSpinDialDial::onKey),
122  FXMAPFUNC(SEL_KEYRELEASE, 0, FXRealSpinDialDial::onKey),
123  FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, FXRealSpinDialDial::onButtonPress),
124  FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, 0, FXRealSpinDialDial::onRightButtonRelease),
125  FXMAPFUNC(SEL_RIGHTBUTTONPRESS, 0, FXRealSpinDialDial::onRightButtonPress),
126  FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, FXRealSpinDialDial::onButtonRelease),
127  FXMAPFUNC(SEL_MOTION, 0, FXRealSpinDialDial::onMotion),
128 
130 
131  //FXMAPFUNC(SEL_KEYPRESS,0, FXRealSpinDialDial::onKeyPress),
132  //FXMAPFUNC(SEL_KEYRELEASE,0,FXRealSpinDialDial::onKeyRelease),
133 };
134 FXIMPLEMENT(FXRealSpinDialDial, FXDial, FXSpinDialMap, ARRAYNUMBER(FXSpinDialMap))
135 //FXIMPLEMENT(FXRealSpinDialDial,FXDial,0,0)
136 
137 //long FXRealSpinDialDial::onDefault(FXObject*o,FXSelector s,void*p )
138 //{
139 // printf("DEFAULT!\n");
140 // if (target) return target->handle(o,s,p);
141 // return 0;
142 //}
143 long FXRealSpinDialDial::onKey(FXObject* o, FXSelector s, void* p) {
144  if (target) {
145  return target->handle(o, s, p);
146  }
147  return 0;
148 }
149 long FXRealSpinDialDial::onButtonPress(FXObject* o, FXSelector s, void* p) {
150  grabKeyboard();
151  return FXDial::onLeftBtnPress(o, s, p);
152 }
153 long FXRealSpinDialDial::onButtonRelease(FXObject* o, FXSelector s, void* p) {
154  ungrabKeyboard();
155  return FXDial::onLeftBtnRelease(o, s, p);
156 }
157 long FXRealSpinDialDial::onRightButtonPress(FXObject* /*o*/, FXSelector /*s*/, void* p) {
158  if (isEnabled()) {
159  grab();
160  grabKeyboard();
161  //if(target && target->handle(this,FXSEL(SEL_RIGHTBUTTONPRESS,message),ptr)) return 1;
162  FXEvent* event = (FXEvent*)p;
163  if (options & DIAL_HORIZONTAL) {
164  dragpoint = event->win_x;
165  } else {
166  dragpoint = event->win_y;
167  }
168  getApp()->addTimeout(this, ID_AUTOSPIN, getApp()->getScrollSpeed());
169  }
170  return 1;
171 }
172 long FXRealSpinDialDial::onRightButtonRelease(FXObject* /*o*/, FXSelector /*s*/, void* /*p*/) {
173  ungrab();
174  ungrabKeyboard();
175  getApp()->removeTimeout(this, ID_AUTOSPIN);
176  if (isEnabled()) {
177  //if(target && target->handle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message),p)) return 1;
178  }
179  return 1;
180 
181 }
182 long FXRealSpinDialDial::onAuto(FXObject* /*o*/, FXSelector /*s*/, void* /*p*/) {
183  getApp()->addTimeout(this, ID_AUTOSPIN, getApp()->getScrollSpeed());
184  setValue(getValue() + int((dragpoint - dragpos) / float(5)));
185  int v = getValue();
186  if (target) {
187  target->handle(this, FXSEL(SEL_CHANGED, message), &v);
188  }
189  return 1;
190 }
191 
192 long FXRealSpinDialDial::onMotion(FXObject* o, FXSelector s, void* p) {
193  if (!isEnabled()) {
194  return 0;
195  }
196  if (target && target->handle(this, FXSEL(SEL_MOTION, message), p)) {
197  return 1;
198  }
199 
200  FXbool bJump = FALSE;
201  FXEvent* e = (FXEvent*)p;
202  if (!(flags & FLAG_PRESSED)) { // not doing clickdrag
203  dragpos = e->win_y;
204  }
205  FXWindow* rootWin = getApp()->getRootWindow();
206  FXint x = e->root_x, y = e->root_y;
207  if (e->root_x >= rootWin->getWidth() - 1) {
208  x -= 40;
209  dragpoint -= 40;
210  bJump = TRUE;
211  } else if (e->root_x <= 10) {
212  x += 40;
213  dragpoint += 40;
214  bJump = TRUE;
215  }
216  if (e->root_y >= rootWin->getHeight() - 1) {
217  y -= 40;
218  dragpoint -= 40;
219  bJump = TRUE;
220  } else if (e->root_y <= 10) {
221  y += 40;
222  dragpoint += 40;
223  bJump = TRUE;
224  }
225  if (bJump) {
226  rootWin->setCursorPosition(x, y);
227  return 1;
228  } else {
229  return FXDial::onMotion(o, s, p);
230  }
231 }
232 
233 }
234 
235 /*******************************************************************************/
236 /* Custom FXArrowButton subclass */
237 /*******************************************************************************/
238 namespace FX {
240  FXDECLARE(FXRealSpinDialBtn)
241 protected:
243 private:
245  FXRealSpinDialBtn& operator=(const FXRealSpinDialBtn&);
246 public:
247  //long onDefault(FXObject*,FXSelector,void* );
248  long onKey(FXObject*, FXSelector, void*);
249  long onButtonPress(FXObject*, FXSelector, void*);
250  long onButtonRelease(FXObject*, FXSelector, void*);
251  enum {
252  ID_AUTOSPIN = FXDial::ID_LAST,
253  ID_LAST
254  };
255 public:
256 
258  FXRealSpinDialBtn(FXComposite* p, FXObject* tgt = NULL, FXSelector sel = 0,
259  FXuint opts = ARROW_NORMAL,
260  FXint x = 0, FXint y = 0, FXint w = 0, FXint h = 0,
261  FXint pl = DEFAULT_PAD, FXint pr = DEFAULT_PAD, FXint pt = DEFAULT_PAD, FXint pb = DEFAULT_PAD):
262  FXArrowButton(p, tgt, sel, opts, x, y, w, h, pl, pr, pt, pb) {}
263 
264 };
265 
266 FXDEFMAP(FXRealSpinDialBtn) FXSpinDialBtnMap[] = {
267  FXMAPFUNC(SEL_KEYPRESS, 0, FXRealSpinDialBtn::onKey),
268  FXMAPFUNC(SEL_KEYRELEASE, 0, FXRealSpinDialBtn::onKey),
269  FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, FXRealSpinDialBtn::onButtonPress),
270  FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, FXRealSpinDialBtn::onButtonRelease),
271 
272 
273  //FXMAPFUNC(SEL_KEYPRESS,0, FXRealSpinDialBtn::onKeyPress),
274  //FXMAPFUNC(SEL_KEYRELEASE,0,FXRealSpinDialBtn::onKeyRelease),
275 };
276 FXIMPLEMENT(FXRealSpinDialBtn, FXArrowButton, FXSpinDialBtnMap, ARRAYNUMBER(FXSpinDialBtnMap))
277 //FXIMPLEMENT(FXRealSpinDialBtn,FXDial,0,0)
278 
279 //long FXRealSpinDialBtn::onDefault(FXObject*o,FXSelector s,void*p )
280 //{
281 // printf("DEFAULT!\n");
282 // if (target) return target->handle(o,s,p);
283 // return 0;
284 //}
285 long FXRealSpinDialBtn::onKey(FXObject* o, FXSelector s, void* p) {
286  if (target) {
287  return target->handle(o, s, p);
288  }
289  return 0;
290 }
291 long FXRealSpinDialBtn::onButtonPress(FXObject* o, FXSelector s, void* p) {
292  grabKeyboard();
293  return FXArrowButton::onLeftBtnPress(o, s, p);
294 }
295 long FXRealSpinDialBtn::onButtonRelease(FXObject* o, FXSelector s, void* p) {
296  ungrabKeyboard();
297  return FXArrowButton::onLeftBtnRelease(o, s, p);
298 }
299 
300 
301 }
302 
303 
304 /*******************************************************************************/
305 /* FXTextField subclass */
306 /*******************************************************************************/
307 
308 namespace FX {
310  FXDECLARE(FXRealSpinDialText)
311 protected:
313 private:
315  FXRealSpinDialText& operator=(const FXRealSpinDialText&);
316 public:
317  long onCmdSetRealValue(FXObject*, FXSelector, void*);
318  long onMotion(FXObject*, FXSelector, void*);
319  enum {
320  ID_LAST = FXTextField::ID_LAST
321  };
322  enum {
323  FLAG_FMTSTRING = 0x1
324  };
325 public:
326 
328  FXRealSpinDialText(FXComposite* p, FXint ncols, FXObject* tgt = NULL, FXSelector sel = 0,
329  FXuint opts = TEXTFIELD_NORMAL,
330  FXint x = 0, FXint y = 0, FXint w = 0, FXint h = 0, FXint
331  pl = DEFAULT_PAD, FXint pr = DEFAULT_PAD, FXint pt = DEFAULT_PAD, FXint pb = DEFAULT_PAD
332  ) :
333  FXTextField(p, ncols, tgt, sel, opts, x, y, w, h, pl, pr, pt, pb),
334  precision(3),
335  exponent(FALSE),
336  flags(0) {}
337 
338  void setNumberFormat(FXint prec, FXbool bExp = FALSE) {
339  precision = prec;
340  exponent = bExp;
341  flags &= ~FLAG_FMTSTRING;
342  }
343  FXint getNumberFormatPrecision() const {
344  return precision;
345  }
346  FXbool getNumberFormatExponent() const {
347  return exponent;
348  }
349  void setFormatString(const FXchar* fmt) {
350  fmtString = fmt;
351  flags |= FLAG_FMTSTRING;
352  }
353  FXString getNumberFormatString() const {
354  return fmtString;
355  }
356 
357 protected:
358  FXint precision;
359  FXbool exponent;
360  FXString fmtString;
361  FXuint flags;
362 };
363 
364 FXDEFMAP(FXRealSpinDialText) FXSpinDialTextMap[] = {
365  FXMAPFUNC(SEL_MOTION, 0, FXRealSpinDialText::onMotion),
366  FXMAPFUNC(SEL_COMMAND, FXWindow::ID_SETREALVALUE, FXRealSpinDialText::onCmdSetRealValue),
367 };
368 FXIMPLEMENT(FXRealSpinDialText, FXTextField, FXSpinDialTextMap, ARRAYNUMBER(FXSpinDialTextMap))
369 
370 long FXRealSpinDialText::onMotion(FXObject* o, FXSelector s, void* ptr) {
371  // Forward motion events so we can monitor key state. We don't get the modifier
372  // keys themselves if we aren't focused, so this seems the best we can do.
373  if (!isEnabled()) {
374  return 0;
375  }
376  if (target && target->handle(this, FXSEL(SEL_MOTION, message), ptr)) {
377  return 1;
378  }
379  return FXTextField::onMotion(o, s, ptr);
380 }
381 long FXRealSpinDialText::onCmdSetRealValue(FXObject* /*o*/, FXSelector /*s*/, void* ptr) {
382  // setText(FXStringVal(*((FXdouble*)ptr)));
383  if (flags & FLAG_FMTSTRING) {
384  setText(FXStringFormat(fmtString.text(), *((FXdouble*)ptr)));
385  } else {
386  setText(FXStringVal(*((FXdouble*)ptr), precision, exponent));
387  }
388  return 1;
389 }
390 
391 }
392 
393 /*******************************************************************************/
394 /* FXRealSpinDial */
395 /*******************************************************************************/
396 
397 namespace FX {
398 
399 // Message map
400 FXDEFMAP(FXRealSpinDial) FXRealSpinDialMap[] = {
401  FXMAPFUNC(SEL_KEYPRESS, 0, FXRealSpinDial::onKeyPress),
402  FXMAPFUNC(SEL_KEYRELEASE, 0, FXRealSpinDial::onKeyRelease),
403  FXMAPFUNC(SEL_MOTION, 0, FXRealSpinDial::onMotion),
404  FXMAPFUNC(SEL_MOTION, FXRealSpinDial::ID_ENTRY, FXRealSpinDial::onMotion),
405  FXMAPFUNC(SEL_MOTION, FXRealSpinDial::ID_DIAL, FXRealSpinDial::onMotion),
407  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_ENTRY, FXRealSpinDial::onCmdEntry),
408  FXMAPFUNC(SEL_CHANGED, FXRealSpinDial::ID_ENTRY, FXRealSpinDial::onChgEntry),
409  FXMAPFUNC(SEL_UPDATE, FXRealSpinDial::ID_DIAL, FXRealSpinDial::onUpdDial),
410  FXMAPFUNC(SEL_CHANGED, FXRealSpinDial::ID_DIAL, FXRealSpinDial::onChgDial),
411  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_DIAL, FXRealSpinDial::onCmdDial),
412  FXMAPFUNC(SEL_MOUSEWHEEL, FXRealSpinDial::ID_ENTRY, FXRealSpinDial::onMouseWheel),
413  FXMAPFUNC(SEL_MOUSEWHEEL, FXRealSpinDial::ID_DIAL, FXRealSpinDial::onMouseWheel),
416  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_SETVALUE, FXRealSpinDial::onCmdSetValue),
417  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_SETINTVALUE, FXRealSpinDial::onCmdSetIntValue),
418  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_GETINTVALUE, FXRealSpinDial::onCmdGetIntValue),
419  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_SETINTRANGE, FXRealSpinDial::onCmdSetIntRange),
420  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_GETINTRANGE, FXRealSpinDial::onCmdGetIntRange),
421  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_SETREALVALUE, FXRealSpinDial::onCmdSetRealValue),
422  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_GETREALVALUE, FXRealSpinDial::onCmdGetRealValue),
423  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_SETREALRANGE, FXRealSpinDial::onCmdSetRealRange),
424  FXMAPFUNC(SEL_COMMAND, FXRealSpinDial::ID_GETREALRANGE, FXRealSpinDial::onCmdGetRealRange),
429 };
430 
431 
432 // Object implementation
433 FXIMPLEMENT(FXRealSpinDial, FXPacker, FXRealSpinDialMap, ARRAYNUMBER(FXRealSpinDialMap))
434 
435 
436 // Construct spinner out of two buttons and a text field
438  flags = (flags | FLAG_ENABLED | FLAG_SHOWN)&~FLAG_UPDATE;
439  textField = (FXRealSpinDialText*) - 1L;
440  dial = (FXDial*) - 1L;
441  upButton = (FXRealSpinDialBtn*) - 1L;
442  downButton = (FXRealSpinDialBtn*) - 1L;
443  range[0] = -DBL_MAX;
444  range[1] = DBL_MAX;
445  incr[0] = 0.1;
446  incr[1] = 1.0;
447  incr[2] = 10;
448  pos = 1;
449  dialpos = 0;
450 }
451 
452 
453 // Construct spinner out of dial and a text field
454 FXRealSpinDial::FXRealSpinDial(FXComposite* p, FXint cols, FXObject* tgt, FXSelector sel, FXuint opts, FXint x, FXint y, FXint w, FXint h, FXint pl, FXint pr, FXint pt, FXint pb):
455  FXPacker(p, opts&~(FRAME_RIDGE), x, y, w, h, 0, 0, 0, 0, 0, 0) {
456  flags = (flags | FLAG_ENABLED | FLAG_SHOWN)&~FLAG_UPDATE;
457  target = tgt;
458  message = sel;
459  dial = new FXRealSpinDialDial(this, this, ID_DIAL, DIAL_VERTICAL, 0, 0, 0, 0, 0, 0, 0, 0);
460  dial->setNotchSpacing(450);
461  dial->setRevolutionIncrement(DIALINCR);
462  upButton = new FXRealSpinDialBtn(this, this, ID_INCREMENT, FRAME_RAISED | FRAME_THICK | ARROW_UP | ARROW_REPEAT, 0, 0, 0, 0, 0, 0, 0, 0);
463  downButton = new FXRealSpinDialBtn(this, this, ID_DECREMENT, FRAME_RAISED | FRAME_THICK | ARROW_DOWN | ARROW_REPEAT, 0, 0, 0, 0, 0, 0, 0, 0);
464  // flag SPINDIAL_NOMAX collides with flag TEXTFIELD_PASSWORD
465  // flag SPINDIAL_NOMIN collides with flag TEXTFIELD_INTEGER
466  textField = new FXRealSpinDialText(this, cols, this, ID_ENTRY, (opts & ~(SPINDIAL_NOMAX | SPINDIAL_NOMIN)) | TEXTFIELD_REAL | JUSTIFY_RIGHT, 0, 0, 0, 0, pl, pr, pt, pb);
467  textField->setText("0");
468  range[0] = (options & SPINDIAL_NOMIN) ? -DBL_MAX : 0;
469  range[1] = (options & SPINDIAL_NOMAX) ? DBL_MAX : 100;
470  dial->setRange(INTMIN, INTMAX);
471  dialpos = dial->getValue();
472  incr[0] = 0.1;
473  incr[1] = 1.0;
474  incr[2] = 10;
475  pos = 0;
476  keystate = 0;
477 }
478 
479 
480 // Get default width
482  FXint tw = 0;
483  if (!(options & SPINDIAL_NOTEXT)) {
484  tw = textField->getDefaultWidth();
485  }
486  return tw + DIALWIDTH + GAPWIDTH + (border << 1);
487 }
488 
489 
490 // Get default height
492  return textField->getDefaultHeight() + (border << 1);
493 }
494 
495 
496 // Create window
499 }
500 
501 
502 // Enable the widget
504  if (!(flags & FLAG_ENABLED)) {
506  textField->enable();
507  dial->enable();
508  }
509 }
510 
511 
512 // Disable the widget
514  if (flags & FLAG_ENABLED) {
516  textField->disable();
517  dial->disable();
518  }
519 }
520 
521 
522 // Recompute layout
524  FXint dialHeight, buttonHeight, textHeight;
525 
526  textHeight = height - 2 * border;
527  dialHeight = textHeight;
528  buttonHeight = textHeight >> 1;
529 
530  FXuint hideOpts = SPINDIAL_NOTEXT | SPINDIAL_NODIAL | SPINDIAL_NOBUTTONS;
531  if ((options & hideOpts) == hideOpts) {
532  flags &= ~FLAG_DIRTY;
533  return; // nothing to layout
534  }
535 
536  FXint right = width - border;
537 
538  if (options & SPINDIAL_NOTEXT) {
539  // Dial takes up the extra space if shown, otherwise spinbuttons
540  if (!(options & SPINDIAL_NODIAL)) {
541  // HAS DIAL
542  int left = border;
543  if (!(options & SPINDIAL_NOBUTTONS)) {
544  FXint bw = BUTTONWIDTH;
545  upButton->position(border, border, bw, buttonHeight);
546  downButton->position(border, height - buttonHeight - border, bw, buttonHeight);
547  left += bw + GAPWIDTH;
548  }
549  dial->position(left, border, right - left, dialHeight);
550  } else {
551  upButton->position(border, border, right - border, buttonHeight);
552  downButton->position(border, height - buttonHeight - border, right - border, buttonHeight);
553  }
554  } else {
555  // dial/buttons are default width, text stretches to fill the rest
556  if (!(options & SPINDIAL_NODIAL)) {
557  FXint w = DIALWIDTH;
558  dial->position(right - w, border, w, dialHeight);
559  right -= w + GAPWIDTH;
560  }
561  if (!(options & SPINDIAL_NOBUTTONS)) {
562  FXint w = BUTTONWIDTH;
563  upButton->position(right - w, border, w, buttonHeight);
564  downButton->position(right - w, height - buttonHeight - border, w, buttonHeight);
565  right -= w + GAPWIDTH;
566  }
567  textField->position(border, border, right - border, textHeight);
568  }
569  flags &= ~FLAG_DIRTY;
570 }
571 
572 
573 // Respond to dial message
574 long FXRealSpinDial::onUpdDial(FXObject* sender, FXSelector, void*) {
575  if (isEnabled() && ((options & SPINDIAL_CYCLIC) || (pos <= range[1]))) {
576  sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), NULL);
577  } else {
578  sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), NULL);
579  }
580  return 1;
581 }
582 
583 
584 // Respond to dial message
585 long FXRealSpinDial::onChgDial(FXObject* /*p*/, FXSelector /*sel*/, void* /*ptr*/) {
586  if (!isEnabled()) {
587  return 0;
588  }
589  FXdouble newpos;
590  FXdouble inc;
591  if (FXApp::instance()->getKeyState(CONTROLMASK)) {
592  inc = incr[0];
593  } else if (FXApp::instance()->getKeyState(SHIFTMASK)) {
594  inc = incr[2];
595  } else {
596  inc = incr[1];
597  }
598  FXint dialdelta = dial->getValue() - dialpos;
599  if (options & SPINDIAL_LOG) {
600  newpos = pos * pow(inc , DIALMULT * FXdouble(dialdelta) / DIALINCR);
601  } else {
602  newpos = pos + DIALMULT * inc * (dialdelta) / DIALINCR;
603  }
604  // Now clamp newpos.
605  if (dialdelta > 0) {
606  if (options & SPINDIAL_LOG) {
607  if (options & SPINDIAL_CYCLIC && newpos > range[1]) {
608  FXdouble lr0 = log(range[0]), lr1 = log(range[1]), lnp = log(newpos);
609  newpos = exp(lr0 + fmod(lnp - lr0, lr1 - lr0));
610  }
611  } else {
612  if (options & SPINDIAL_CYCLIC) {
613  newpos = range[0] + fmod(newpos - range[0], range[1] - range[0] + 1);
614  }
615  }
616  } else {
617  if (options & SPINDIAL_LOG) {
618  if (options & SPINDIAL_CYCLIC && newpos < range[0]) {
619  FXdouble lr0 = log(range[0]), lr1 = log(range[1]), lpos = log(pos);
620  FXdouble span = lr1 - lr0;
621  newpos = exp(lr0 + fmod(lpos - lr0 + 1 + (span - inc), span));
622  }
623  } else {
624  if (options & SPINDIAL_CYCLIC) {
625  newpos = range[0] + fmod(pos + (range[1] - range[0] + 1 + (newpos - pos) - range[0]) , range[1] - range[0] + 1);
626  }
627  }
628  }
629  setValue(newpos);
630  if (target) {
631  target->handle(this, FXSEL(SEL_COMMAND, message), (void*)&pos);
632  }
633  dialpos = dial->getValue();
634  return 1;
635 }
636 
637 // Respond to dial message
638 long FXRealSpinDial::onCmdDial(FXObject*, FXSelector /*sel*/, void*) {
639  if (!isEnabled()) {
640  return 0;
641  }
642  // if(target) target->handle(this,FXSEL(SEL_COMMAND,message),(void*)&pos);
643  dialpos = dial->getValue() % DIALINCR;
644  dial->setValue(dialpos);
645  return 1;
646 }
647 
648 
649 // Respond to increment message
650 long FXRealSpinDial::onUpdIncrement(FXObject* sender, FXSelector, void*) {
651  if (isEnabled() && ((options & REALSPIN_CYCLIC) || (pos < range[1]))) {
652  sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), NULL);
653  } else {
654  sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), NULL);
655  }
656  return 1;
657 }
658 
659 
660 // Respond to increment message
661 long FXRealSpinDial::onCmdIncrement(FXObject*, FXSelector, void*) {
662  if (!isEnabled()) {
663  return 0;
664  }
665  FXint mode;
666  if (keystate & CONTROLMASK) {
667  mode = SPINDIAL_INC_FINE;
668  } else if (keystate & SHIFTMASK) {
669  mode = SPINDIAL_INC_COARSE;
670  } else {
671  mode = SPINDIAL_INC_NORMAL;
672  }
673  increment(mode);
674  if (target) {
675  target->handle(this, FXSEL(SEL_COMMAND, message), (void*)&pos);
676  }
677  return 1;
678 }
679 
680 
681 // Disable decrement if at low end already
682 long FXRealSpinDial::onUpdDecrement(FXObject* sender, FXSelector, void*) {
683  if (isEnabled() && ((options & REALSPIN_CYCLIC) || (range[0] < pos))) {
684  sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), NULL);
685  } else {
686  sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), NULL);
687  }
688  return 1;
689 }
690 
691 
692 // Respond to decrement message
693 long FXRealSpinDial::onCmdDecrement(FXObject*, FXSelector, void*) {
694  if (!isEnabled()) {
695  return 0;
696  }
697  FXint mode;
698  if (keystate & CONTROLMASK) {
699  mode = SPINDIAL_INC_FINE;
700  } else if (keystate & SHIFTMASK) {
701  mode = SPINDIAL_INC_COARSE;
702  } else {
703  mode = SPINDIAL_INC_NORMAL;
704  }
705  decrement(mode);
706  if (target) {
707  target->handle(this, FXSEL(SEL_COMMAND, message), (void*)&pos);
708  }
709  return 1;
710 }
711 
712 
713 
714 // Update from text field
715 long FXRealSpinDial::onUpdEntry(FXObject*, FXSelector, void*) {
716  return target && target->handle(this, FXSEL(SEL_UPDATE, message), NULL);
717 }
718 
719 long FXRealSpinDial::onMouseWheel(FXObject* /*o*/, FXSelector /*s*/, void* p) {
720  FXint mode;
721  keystate = ((FXEvent*)p)->state;
722  if (keystate & CONTROLMASK) {
723  mode = SPINDIAL_INC_FINE;
724  } else if (keystate & SHIFTMASK) {
725  mode = SPINDIAL_INC_COARSE;
726  } else {
727  mode = SPINDIAL_INC_NORMAL;
728  }
729  if (((FXEvent*)p)->code > 0) {
730  increment(mode);
731  } else {
732  decrement(mode);
733  }
734  if (target) {
735  target->handle(this, FXSEL(SEL_COMMAND, message), (void*)&pos);
736  }
737  return 1;
738 }
739 
740 // Text field changed
741 long FXRealSpinDial::onChgEntry(FXObject*, FXSelector, void*) {
742  register FXdouble value = FXDoubleVal(textField->getText());
743  if (value < range[0]) {
744  value = range[0];
745  }
746  if (value > range[1]) {
747  value = range[1];
748  }
749  if (value != pos) {
750  pos = value;
751  if (target) {
752  target->handle(this, FXSEL(SEL_CHANGED, message), (void*)&pos);
753  }
754  }
755  return 1;
756 }
757 
758 
759 // Text field command
760 long FXRealSpinDial::onCmdEntry(FXObject*, FXSelector, void*) {
761  textField->setText(FXStringVal(pos)); // Put back adjusted value
762  if (target) {
763  target->handle(this, FXSEL(SEL_COMMAND, message), (void*)&pos);
764  }
765  return 1;
766 }
767 
768 
769 // Keyboard press
770 long FXRealSpinDial::onKeyPress(FXObject* sender, FXSelector sel, void* ptr) {
771  FXEvent* event = (FXEvent*)ptr;
772  if (!isEnabled()) {
773  return 0;
774  }
775  keystate = event->state;
776  if (target && target->handle(this, FXSEL(SEL_KEYPRESS, message), ptr)) {
777  return 1;
778  }
779  FXint mode;
780  if (keystate & CONTROLMASK) {
781  mode = SPINDIAL_INC_FINE;
782  } else if (keystate & SHIFTMASK) {
783  mode = SPINDIAL_INC_COARSE;
784  } else {
785  mode = SPINDIAL_INC_NORMAL;
786  }
787  switch (event->code) {
788  case KEY_Up:
789  case KEY_KP_Up:
790  increment(mode);
791  if (target) {
792  target->handle(this, FXSEL(SEL_COMMAND, message), (void*)&pos);
793  }
794  return 1;
795  case KEY_Down:
796  case KEY_KP_Down:
797  decrement(mode);
798  if (target) {
799  target->handle(this, FXSEL(SEL_COMMAND, message), (void*)&pos);
800  }
801  return 1;
802  default:
803  return textField->handle(sender, sel, ptr);
804  }
805 }
806 
807 
808 // Keyboard release
809 long FXRealSpinDial::onKeyRelease(FXObject* sender, FXSelector sel, void* ptr) {
810  FXEvent* event = (FXEvent*)ptr;
811  if (!isEnabled()) {
812  return 0;
813  }
814  keystate = event->state;
815  if (target && target->handle(this, FXSEL(SEL_KEYRELEASE, message), ptr)) {
816  return 1;
817  }
818  switch (event->code) {
819  case KEY_Up:
820  case KEY_KP_Up:
821  case KEY_Down:
822  case KEY_KP_Down:
823  return 1;
824  default:
825  return textField->handle(sender, sel, ptr);
826  }
827 }
828 
829 // Mouse motion
830 long FXRealSpinDial::onMotion(FXObject* /*sender*/, FXSelector /*sel*/, void* ptr) {
831  if (!isEnabled()) {
832  return 0;
833  }
834  keystate = ((FXEvent*)ptr)->state;
835  return 0;
836 }
837 
838 // Update value from a message
839 long FXRealSpinDial::onCmdSetValue(FXObject*, FXSelector, void* ptr) {
840  setValue((FXdouble)(size_t)ptr);
841  return 1;
842 }
843 
844 
845 // Update value from a message
846 long FXRealSpinDial::onCmdSetIntValue(FXObject*, FXSelector, void* ptr) {
847  setValue(FXdouble(*((FXint*)ptr)));
848  return 1;
849 }
850 
851 
852 // Obtain value from spinner
853 long FXRealSpinDial::onCmdGetIntValue(FXObject*, FXSelector, void* ptr) {
854  *((FXint*)ptr) = (FXint)getValue();
855  return 1;
856 }
857 
858 
859 // Update range from a message
860 long FXRealSpinDial::onCmdSetIntRange(FXObject*, FXSelector, void* ptr) {
861  FXdouble lo = (FXdouble)((FXint*)ptr)[0];
862  FXdouble hi = (FXdouble)((FXint*)ptr)[1];
863  setRange(lo, hi);
864  return 1;
865 }
866 
867 
868 // Get range with a message
869 long FXRealSpinDial::onCmdGetIntRange(FXObject*, FXSelector, void* ptr) {
870  ((FXdouble*)ptr)[0] = range[0];
871  ((FXdouble*)ptr)[1] = range[1];
872  return 1;
873 }
874 
875 
876 // Update value from a message
877 long FXRealSpinDial::onCmdSetRealValue(FXObject*, FXSelector, void* ptr) {
878  setValue(*((FXdouble*)ptr));
879  return 1;
880 }
881 
882 
883 // Obtain value from spinner
884 long FXRealSpinDial::onCmdGetRealValue(FXObject*, FXSelector, void* ptr) {
885  *((FXdouble*)ptr) = getValue();
886  return 1;
887 }
888 
889 
890 // Update range from a message
891 long FXRealSpinDial::onCmdSetRealRange(FXObject*, FXSelector, void* ptr) {
892  setRange(((FXdouble*)ptr)[0], ((FXdouble*)ptr)[1]);
893  return 1;
894 }
895 
896 
897 // Get range with a message
898 long FXRealSpinDial::onCmdGetRealRange(FXObject*, FXSelector, void* ptr) {
899  getRange(((FXdouble*)ptr)[0], ((FXdouble*)ptr)[1]);
900  return 1;
901 }
902 
903 
904 
905 // Increment spinner
906 void FXRealSpinDial::increment(FXint incMode) {
907  FXdouble inc = incr[incMode + 1];
908  FXdouble newpos;
909  if (range[0] < range[1]) {
910  if (options & SPINDIAL_LOG) {
911  newpos = pos * inc;
912  if (options & SPINDIAL_CYCLIC && newpos > range[1]) {
913  // can have a huge magnitude disparity here, so better to work in log space
914  FXdouble lr0 = log(range[0]), lr1 = log(range[1]), lnp = log(newpos);
915  newpos = exp(lr0 + fmod(lnp - lr0, lr1 - lr0));
916  }
917  } else {
918  newpos = pos + inc;
919  if (options & SPINDIAL_CYCLIC) {
920  newpos = range[0] + fmod(newpos - range[0], range[1] - range[0] + 1);
921  }
922  }
923  setValue(newpos);
924  }
925 }
926 
927 
928 // Decrement spinner
929 void FXRealSpinDial::decrement(FXint incMode) {
930  FXdouble inc = incr[incMode + 1];
931  FXdouble newpos;
932  if (range[0] < range[1]) {
933  if (options & SPINDIAL_LOG) {
934  newpos = pos / inc;
935  if (options & SPINDIAL_CYCLIC && newpos < range[0]) {
936  // can have a huge magnitude disparity here, so better to work in log space
937  FXdouble lr0 = log(range[0]), lr1 = log(range[1]), lpos = log(pos);
938  FXdouble span = lr1 - lr0;
939  newpos = exp(lr0 + fmod(lpos - lr0 + 1 + (span - inc), span));
940  }
941  } else {
942  newpos = pos - inc;
943  if (options & SPINDIAL_CYCLIC) {
944  newpos = range[0] + fmod(pos + (range[1] - range[0] + 1 + (newpos - pos) - range[0]) , range[1] - range[0] + 1);
945  }
946  }
947  setValue(newpos);
948  }
949 }
950 
951 // True if spinner is cyclic
952 FXbool FXRealSpinDial::isCyclic() const {
953  return (options & SPINDIAL_CYCLIC) != 0;
954 }
955 
956 
957 // Set spinner cyclic mode
958 void FXRealSpinDial::setCyclic(FXbool cyclic) {
959  if (cyclic) {
960  options |= SPINDIAL_CYCLIC;
961  } else {
962  options &= ~SPINDIAL_CYCLIC;
963  }
964 }
965 
966 
967 // Set spinner range; this also revalidates the position,
968 void FXRealSpinDial::setRange(FXdouble lo, FXdouble hi) {
969  if (lo > hi) {
970  fxerror("%s::setRange: trying to set negative range.\n", getClassName());
971  }
972  if (range[0] != lo || range[1] != hi) {
973  range[0] = lo;
974  range[1] = hi;
975  setValue(pos);
976  }
977 }
978 
979 
980 // Set new value
981 void FXRealSpinDial::setValue(FXdouble value) {
982  if (value < range[0]) {
983  value = range[0];
984  }
985  if (value > range[1]) {
986  value = range[1];
987  }
988  if (pos != value) {
989  textField->handle(this, FXSEL(SEL_COMMAND, ID_SETREALVALUE), &value);
990  pos = value;
991  }
992 }
993 
994 
995 // Change value increment
996 void FXRealSpinDial::setIncrement(FXdouble inc) {
997  if (inc < 0) {
998  fxerror("%s::setIncrement: negative or zero increment specified.\n", getClassName());
999  }
1000  incr[1] = inc;
1001 }
1003  if (inc < 0) {
1004  fxerror("%s::setIncrement: negative or zero increment specified.\n", getClassName());
1005  }
1006  incr[0] = inc;
1007 }
1009  if (inc < 0) {
1010  fxerror("%s::setIncrement: negative or zero increment specified.\n", getClassName());
1011  }
1012  incr[2] = inc;
1013 }
1014 void FXRealSpinDial::setIncrements(FXdouble fine, FXdouble inc, FXdouble coarse) {
1015  if (inc < 0) {
1016  fxerror("%s::setIncrement: negative or zero increment specified.\n", getClassName());
1017  }
1018  incr[0] = fine;
1019  incr[1] = inc;
1020  incr[2] = coarse;
1021 }
1022 
1023 
1024 // True if text supposed to be visible
1026  return textField->shown();
1027 }
1028 
1029 
1030 // Change text visibility
1032  FXuint opts = shown ? (options&~SPINDIAL_NOTEXT) : (options | SPINDIAL_NOTEXT);
1033  if (options != opts) {
1034  options = opts;
1035  recalc();
1036  }
1037 }
1038 
1039 
1040 // Set the font used in the text field|
1041 void FXRealSpinDial::setFont(FXFont* fnt) {
1042  textField->setFont(fnt);
1043 }
1044 
1045 
1046 // Return the font used in the text field
1047 FXFont* FXRealSpinDial::getFont() const {
1048  return textField->getFont();
1049 }
1050 
1051 
1052 // Set help text
1053 void FXRealSpinDial::setHelpText(const FXString& text) {
1054  textField->setHelpText(text);
1055  dial->setHelpText(text);
1056  upButton->setHelpText(text);
1057  downButton->setHelpText(text);
1058 }
1059 
1060 
1061 // Get help text
1063  return textField->getHelpText();
1064 }
1065 
1066 
1067 // Set tip text
1068 void FXRealSpinDial::setTipText(const FXString& text) {
1069  textField->setTipText(text);
1070  dial->setTipText(text);
1071  upButton->setTipText(text);
1072  downButton->setTipText(text);
1073 }
1074 
1075 
1076 
1077 // Get tip text
1078 FXString FXRealSpinDial::getTipText() const {
1079  return textField->getTipText();
1080 }
1081 
1082 
1083 // Change spinner style
1085  FXuint opts = (options&~SPINDIAL_MASK) | (style & SPINDIAL_MASK);
1086  if (options != opts) {
1087  if (opts & SPINDIAL_NOMIN) {
1088  range[0] = -DBL_MAX;
1089  }
1090  if (opts & SPINDIAL_NOMAX) {
1091  range[1] = DBL_MAX;
1092  }
1093  options = opts;
1094  recalc();
1095  }
1096 }
1097 
1098 
1099 // Get spinner style
1101  return (options & SPINDIAL_MASK);
1102 }
1103 
1104 
1105 // Allow editing of the text field
1106 void FXRealSpinDial::setEditable(FXbool edit) {
1107  textField->setEditable(edit);
1108 }
1109 
1110 
1111 // Return TRUE if text field is editable
1113  return textField->isEditable();
1114 }
1115 
1116 // Change color of the dial
1117 void FXRealSpinDial::setDialColor(FXColor clr) {
1118  dial->setBackColor(clr);
1119 }
1120 
1121 // Return color of the dial
1123  return dial->getBackColor();
1124 }
1125 
1126 // Change color of the up arrow
1128  upButton->setArrowColor(clr);
1129 }
1130 
1131 // Return color of the up arrow
1133  return upButton->getArrowColor();
1134 }
1135 
1136 // Change color of the down arrow
1138  downButton->setArrowColor(clr);
1139 }
1140 
1141 // Return color of the the down arrow
1143  return downButton->getArrowColor();
1144 }
1145 
1146 
1147 // Change text color
1148 void FXRealSpinDial::setTextColor(FXColor clr) {
1149  textField->setTextColor(clr);
1150 }
1151 
1152 // Return text color
1154  return textField->getTextColor();
1155 }
1156 
1157 // Change selected background color
1159  textField->setSelBackColor(clr);
1160 }
1161 
1162 // Return selected background color
1164  return textField->getSelBackColor();
1165 }
1166 
1167 // Change selected text color
1169  textField->setSelTextColor(clr);
1170 }
1171 
1172 // Return selected text color
1174  return textField->getSelTextColor();
1175 }
1176 
1177 // Changes the cursor color
1179  textField->setCursorColor(clr);
1180 }
1181 
1182 // Return the cursor color
1184  return textField->getCursorColor();
1185 }
1186 
1187 void FXRealSpinDial::setNumberFormat(FXint prec, FXbool bExp) {
1188  textField->setNumberFormat(prec, bExp);
1189 }
1190 
1193 }
1194 
1197 }
1198 
1199 void FXRealSpinDial::setFormatString(const FXchar* fmt) {
1200  textField->setFormatString(fmt);
1201 }
1202 
1204  return textField->getNumberFormatString();
1205 }
1206 
1207 // Save object to stream
1208 void FXRealSpinDial::save(FXStream& store) const {
1209  FXPacker::save(store);
1210  store << textField;
1211  store << dial;
1212  store << upButton;
1213  store << downButton;
1214  store << range[0] << range[1];
1215  store << incr[0] << incr[1] << incr[2];
1216  store << pos;
1217 }
1218 
1219 
1220 // Load object from stream
1221 void FXRealSpinDial::load(FXStream& store) {
1222  FXPacker::load(store);
1223  store >> textField;
1224  store >> dial;
1225  store >> upButton;
1226  store >> downButton;
1227  store >> range[0] >> range[1];
1228  store >> incr[0] >> incr[1] >> incr[2];
1229  store >> pos;
1230 }
1231 
1232 
1233 // Destruct spinner:- trash it!
1235  textField = (FXRealSpinDialText*) - 1L;
1236  dial = (FXDial*) - 1L;
1237  upButton = (FXRealSpinDialBtn*) - 1L;
1238  downButton = (FXRealSpinDialBtn*) - 1L;
1239 }
1240 
1241 }
1242 
1243 
1244 void
1245 FXRealSpinDial::selectAll() {
1246  textField->selectAll();
1247 }
1248 
1249 
1250 
1251 const FXDial&
1253  return *dial;
1254 }
1255 
1256