GG
Spin.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 /* GG is a GUI for SDL and OpenGL.
3  Copyright (C) 2003-2008 T. Zachary Laine
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public License
7  as published by the Free Software Foundation; either version 2.1
8  of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free
17  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18  02111-1307 USA
19 
20  If you do not wish to comply with the terms of the LGPL please
21  contact the author as other terms are available for a fee.
22 
23  Zach Laine
24  whatwasthataddress@gmail.com */
25 
30 #ifndef _GG_Spin_h_
31 #define _GG_Spin_h_
32 
33 #include <GG/Button.h>
34 #include <GG/DrawUtil.h>
35 #include <GG/Edit.h>
36 #include <GG/GUI.h>
37 #include <GG/StyleFactory.h>
38 #include <GG/WndEvent.h>
39 
40 #include <cmath>
41 #include <limits>
42 
43 
44 namespace GG {
45 
46 // forward declaration of helper functions and classes
47 namespace spin_details {
48  template <class T> T mod(T, T);
49  template <class T> T div(T, T);
50 }
51 
52 
72 template <class T>
73 class Spin : public Control
74 {
75 public:
77  typedef typename boost::signal<void (T)> ValueChangedSignalType;
78 
79 
81 
83  Spin(X x, Y y, X w, T value, T step, T min, T max, bool edits, const boost::shared_ptr<Font>& font, Clr color,
84  Clr text_color = CLR_BLACK, Clr interior = CLR_ZERO, Flags<WndFlag> flags = INTERACTIVE);
85 
86  ~Spin(); // dtor
88 
90  virtual Pt MinUsableSize() const;
91 
92  T Value() const;
93  T StepSize() const;
94  T MinValue() const;
95  T MaxValue() const;
96  bool Editable() const;
97 
98  X ButtonWidth() const;
99 
100  Clr TextColor() const;
101  Clr InteriorColor() const;
102  Clr HiliteColor() const;
103  Clr SelectedTextColor() const;
104 
106 
107 
109  virtual void Render();
110 
111  virtual void SizeMove(const Pt& ul, const Pt& lr);
112 
113  virtual void Disable(bool b = true);
114  virtual void SetColor(Clr c);
115 
116  void Incr();
117  void Decr();
118 
120  void SetValue(T value);
121 
122  void SetStepSize(T step);
123  void SetMinValue(T value);
124  void SetMaxValue(T value);
125 
127  void AllowEdits(bool b = true);
128 
129  void SetButtonWidth(X width);
130 
131  void SetTextColor(Clr c);
132  void SetInteriorColor(Clr c);
133  void SetHiliteColor(Clr c);
134  void SetSelectedTextColor(Clr c);
135 
136 
137 protected:
138  typedef T ValueType;
139 
140  enum {BORDER_THICK = 2, PIXEL_MARGIN = 5};
141 
143  Spin();
144 
145 
147  Button* UpButton() const;
148  Button* DownButton() const;
149  Edit* GetEdit() const;
150 
151 
153  virtual void KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys);
154  virtual void MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys);
155 
156  virtual bool EventFilter(Wnd* w, const WndEvent& event);
158 
159 private:
160  void ConnectSignals();
161  void Init(const boost::shared_ptr<Font>& font, Clr color, Clr text_color, Clr interior, Flags<WndFlag> flags);
162  void ValueUpdated(const std::string& val_text);
163  void IncrImpl(bool signal);
164  void DecrImpl(bool signal);
165  void SetValueImpl(T value, bool signal);
166 
167  T m_value;
168  T m_step_size;
169  T m_min_value;
170  T m_max_value;
171 
172  bool m_editable;
173 
174  Edit* m_edit;
175  Button* m_up_button;
176  Button* m_down_button;
177 
178  X m_button_width;
179 
180  static void ValueChangedEcho(const T& value);
181 };
182 
183 
184 // template implementations
185 template<class T>
187  Control(),
188  m_value(),
189  m_step_size(),
190  m_min_value(),
191  m_max_value(),
192  m_editable(false),
193  m_edit(0),
194  m_up_button(0),
195  m_down_button(0),
196  m_button_width(15)
197 {}
198 
199 template<class T>
200 Spin<T>::Spin(X x, Y y, X w, T value, T step, T min, T max, bool edits, const boost::shared_ptr<Font>& font, Clr color,
201  Clr text_color/* = CLR_BLACK*/, Clr interior/* = CLR_ZERO*/, Flags<WndFlag> flags/* = INTERACTIVE*/) :
202  Control(x, y, w, font->Height() + 2 * PIXEL_MARGIN, flags),
203  m_value(value),
204  m_step_size(step),
205  m_min_value(min),
206  m_max_value(max),
207  m_editable(edits),
208  m_edit(0),
209  m_up_button(0),
210  m_down_button(0),
211  m_button_width(15)
212 {
213  Init(font, color, text_color, interior, flags);
214 
215  if (INSTRUMENT_ALL_SIGNALS)
216  Connect(ValueChangedSignal, &ValueChangedEcho);
217 }
218 
219 template<class T>
221 {}
222 
223 template<class T>
225 {
226  Pt edit_min = m_edit->MinUsableSize();
227  Pt up_min = m_up_button->MinUsableSize();
228  Pt down_min = m_down_button->MinUsableSize();
229  return Pt(edit_min.x + std::max(up_min.x, down_min.x) + 2 * BORDER_THICK,
230  std::max(up_min.y + down_min.y, edit_min.y) + 2 * BORDER_THICK);
231 }
232 
233 template<class T>
234 T Spin<T>::Value() const
235 { return m_value; }
236 
237 template<class T>
239 { return m_step_size; }
240 
241 template<class T>
243 { return m_min_value; }
244 
245 template<class T>
247 { return m_max_value; }
248 
249 template<class T>
250 bool Spin<T>::Editable() const
251 { return m_editable; }
252 
253 template<class T>
255 { return m_button_width; }
256 
257 template<class T>
259 { return m_edit->TextColor(); }
260 
261 template<class T>
263 { return m_edit->InteriorColor(); }
264 
265 template<class T>
267 { return m_edit->HiliteColor(); }
268 
269 template<class T>
271 { return m_edit->SelectedTextColor(); }
272 
273 template<class T>
275 {
276  Clr color_to_use = Disabled() ? DisabledColor(Color()) : Color();
277  Clr int_color_to_use = Disabled() ? DisabledColor(InteriorColor()) : InteriorColor();
278  Pt ul = UpperLeft(), lr = LowerRight();
279  BeveledRectangle(ul, lr, int_color_to_use, color_to_use, false, BORDER_THICK);
280 }
281 
282 template<class T>
283 void Spin<T>::SizeMove(const Pt& ul, const Pt& lr)
284 {
285  Wnd::SizeMove(ul, lr);
286  const X BUTTON_X_POS = Width() - m_button_width - BORDER_THICK;
287  const Y BUTTONS_HEIGHT = Height() - 2 * BORDER_THICK; // height of *both* buttons
288  m_edit->SizeMove(Pt(), Pt(Width() - m_button_width, Height()));
289  m_up_button->SizeMove(Pt(BUTTON_X_POS, Y(BORDER_THICK)),
290  Pt(BUTTON_X_POS + m_button_width, BORDER_THICK + BUTTONS_HEIGHT / 2));
291  m_down_button->SizeMove(Pt(BUTTON_X_POS, BORDER_THICK + BUTTONS_HEIGHT / 2),
292  Pt(BUTTON_X_POS + m_button_width, BORDER_THICK + BUTTONS_HEIGHT));
293 }
294 
295 template<class T>
296 void Spin<T>::Disable(bool b/* = true*/)
297 {
298  Control::Disable(b);
299  m_edit->Disable(b);
300  m_up_button->Disable(b);
301  m_down_button->Disable(b);
302 }
303 
304 template<class T>
306 {
308  m_up_button->SetColor(c);
309  m_down_button->SetColor(c);
310 }
311 
312 template<class T>
314 { SetValueImpl(m_value + m_step_size, false); }
315 
316 template<class T>
318 { SetValueImpl(m_value - m_step_size, false); }
319 
320 template<class T>
321 void Spin<T>::SetValue(T value)
322 { SetValueImpl(value, false); }
323 
324 template<class T>
326 {
327  m_step_size = step;
328  SetValue(m_value);
329 }
330 
331 template<class T>
332 void Spin<T>::SetMinValue(T value)
333 {
334  m_min_value = value;
335  if (m_value < m_min_value)
336  SetValue(m_min_value);
337 }
338 
339 template<class T>
340 void Spin<T>::SetMaxValue(T value)
341 {
342  m_max_value = value;
343  if (m_max_value < m_value)
344  SetValue(m_max_value);
345 }
346 
347 template<class T>
349 { m_edit->SetTextColor(c); }
350 
351 template<class T>
353 {
354  if (1 <= width) {
355  if (Width() - 2 * BORDER_THICK - 1 < width)
356  width = Width() - 2 * BORDER_THICK - 1;
357  m_button_width = width;
358  SizeMove(RelativeUpperLeft(), RelativeLowerRight());
359  }
360 }
361 
362 template<class T>
364 { m_edit->SetInteriorColor(c); }
365 
366 template<class T>
368 { m_edit->SetHiliteColor(c); }
369 
370 template<class T>
372 { m_edit->SetSelectedTextColor(c); }
373 
374 template<class T>
376 { return m_up_button; }
377 
378 template<class T>
380 { return m_down_button; }
381 
382 template<class T>
384 { return m_edit; }
385 
386 template<class T>
387 void Spin<T>::KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys)
388 {
389  switch (key) {
390  case GGK_HOME:
391  SetValueImpl(m_min_value, true);
392  break;
393  case GGK_END:
394  SetValueImpl(m_max_value, true);
395  break;
396  case GGK_PAGEUP:
397  case GGK_UP:
398  case GGK_PLUS:
399  case GGK_KP_PLUS:
400  IncrImpl(true);
401  break;
402  case GGK_PAGEDOWN:
403  case GGK_DOWN:
404  case GGK_MINUS:
405  case GGK_KP_MINUS:
406  DecrImpl(true);
407  break;
408  default:
409  break;
410  }
411 }
412 
413 template<class T>
414 void Spin<T>::MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys)
415 {
416  for (int i = 0; i < move; ++i) {
417  IncrImpl(true);
418  }
419  for (int i = 0; i < -move; ++i) {
420  DecrImpl(true);
421  }
422 }
423 
424 template<class T>
425 bool Spin<T>::EventFilter(Wnd* w, const WndEvent& event)
426 {
427  if (w == m_edit) {
428  if (!m_editable && event.Type() == WndEvent::GainingFocus) {
429  GUI::GetGUI()->SetFocusWnd(this);
430  return true;
431  } else {
432  return !m_editable;
433  }
434  }
435  return false;
436 }
437 
438 template<class T>
440 {
441  Connect(m_edit->FocusUpdateSignal, &Spin::ValueUpdated, this);
442  Connect(m_up_button->ClickedSignal, boost::bind(&Spin::IncrImpl, this, true));
443  Connect(m_down_button->ClickedSignal, boost::bind(&Spin::DecrImpl, this, true));
444 }
445 
446 template<class T>
447 void Spin<T>::Init(const boost::shared_ptr<Font>& font, Clr color, Clr text_color, Clr interior, Flags<WndFlag> flags)
448 {
449  boost::shared_ptr<StyleFactory> style = GetStyleFactory();
450  Control::SetColor(color);
451  m_edit = style->NewSpinEdit(X0, Y0, X1, boost::lexical_cast<std::string>(m_value), font, CLR_ZERO, text_color, interior);
452  boost::shared_ptr<Font> small_font = GUI::GetGUI()->GetFont(font, static_cast<int>(font->PointSize() * 0.75));
453  m_up_button = style->NewSpinIncrButton(X0, Y0, X1, Y1, "+", small_font, color);
454  m_down_button = style->NewSpinDecrButton(X0, Y0, X1, Y1, "-", small_font, color);
455  m_edit->InstallEventFilter(this);
456  m_up_button->InstallEventFilter(this);
457  m_down_button->InstallEventFilter(this);
458  AttachChild(m_edit);
459  AttachChild(m_up_button);
460  AttachChild(m_down_button);
461  ConnectSignals();
462  SizeMove(UpperLeft(), LowerRight());
463 }
464 
465 template<class T>
466 void Spin<T>::ValueUpdated(const std::string& val_text)
467 {
468  T value;
469  try {
470  value = boost::lexical_cast<T>(val_text);
471  } catch (boost::bad_lexical_cast) {
472  SetValueImpl(m_min_value, true);
473  return;
474  }
475  SetValueImpl(value, true);
476 }
477 
478 template<class T>
479 void Spin<T>::IncrImpl(bool signal)
480 { SetValueImpl(static_cast<T>(m_value + m_step_size), signal); }
481 
482 template<class T>
483 void Spin<T>::DecrImpl(bool signal)
484 { SetValueImpl(static_cast<T>(m_value - m_step_size), signal); }
485 
486 template<class T>
487 void Spin<T>::SetValueImpl(T value, bool signal)
488 {
489  T old_value = m_value;
490  if (value < m_min_value) {
491  m_value = m_min_value;
492  } else if (m_max_value < value) {
493  m_value = m_max_value;
494  } else {
495  // if the value supplied does not equal a valid value
496  if (std::abs(spin_details::mod(static_cast<T>(value - m_min_value), m_step_size)) >
497  std::numeric_limits<T>::epsilon()) {
498  // find nearest valid value to the one supplied
499  T closest_below =
500  static_cast<T>(
501  spin_details::div(static_cast<T>(value - m_min_value), m_step_size) *
502  static_cast<T>(m_step_size + m_min_value));
503  T closest_above =
504  static_cast<T>(closest_below + m_step_size);
505  m_value =
506  ((value - closest_below) < (closest_above - value) ?
507  closest_below : closest_above);
508  } else {
509  m_value = value;
510  }
511  }
512  *m_edit << m_value;
513  if (signal && m_value != old_value)
514  ValueChangedSignal(m_value);
515 }
516 
517 template <class T>
518 void Spin<T>::ValueChangedEcho(const T& value)
519 { std::cerr << "GG SIGNAL : Spin<>::ValueChangedSignal(value=" << value << ")\n"; }
520 
521 
522 namespace spin_details {
523  // provides a typesafe mod function
524  template <class T> inline
525  T mod (T dividend, T divisor) {return static_cast<T>(dividend % divisor);}
526 
527  // template specializations
528  template <> inline
529  float mod<float> (float dividend, float divisor) {return std::fmod(dividend, divisor);}
530  template <> inline
531  double mod<double> (double dividend, double divisor) {return std::fmod(dividend, divisor);}
532  template <> inline
533  long double mod<long double> (long double dividend, long double divisor) {return std::fmod(dividend, divisor);}
534 
535  // provides a typesafe div function
536  template <class T> inline
537  T div (T dividend, T divisor) {return static_cast<T>(dividend / divisor);}
538 
539  // template specializations
540  template <> inline
541  float div<float> (float dividend, float divisor) {return std::floor(dividend / divisor);}
542  template <> inline
543  double div<double> (double dividend, double divisor) {return std::floor(dividend / divisor);}
544  template <> inline
545  long double div<long double> (long double dividend, long double divisor) {return std::floor(dividend / divisor);}
546 } // namespace spin_details
547 
548 } // namespace GG
549 
550 #endif
551