00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00030 #ifndef _GG_Spin_h_
00031 #define _GG_Spin_h_
00032
00033 #include <GG/Button.h>
00034 #include <GG/DrawUtil.h>
00035 #include <GG/Edit.h>
00036 #include <GG/GUI.h>
00037 #include <GG/StyleFactory.h>
00038 #include <GG/WndEditor.h>
00039 #include <GG/WndEvent.h>
00040
00041 #include <cmath>
00042 #include <limits>
00043
00044
00045 namespace GG {
00046
00047
00048 namespace spin_details {
00049 template <class T> T mod(T, T);
00050 template <class T> T div(T, T);
00051
00052 template <class T>
00053 struct SetValueAction : AttributeChangedAction<T>
00054 {
00055 SetValueAction(Spin<T>* spin) : m_spin(spin) {}
00056 void operator()(const T& value) {m_spin->SetValue(m_spin->Value());}
00057 private:
00058 Spin<T>* m_spin;
00059 };
00060
00061 template <class T>
00062 struct SetMinValueAction : AttributeChangedAction<T>
00063 {
00064 SetMinValueAction(Spin<T>* spin) : m_spin(spin) {}
00065 void operator()(const T& value) {m_spin->SetValue(value);}
00066 private:
00067 Spin<T>* m_spin;
00068 };
00069
00070 template <class T>
00071 struct SetMaxValueAction : AttributeChangedAction<T>
00072 {
00073 SetMaxValueAction(Spin<T>* spin) : m_spin(spin) {}
00074 void operator()(const T& value) {m_spin->SetValue(value);}
00075 private:
00076 Spin<T>* m_spin;
00077 };
00078
00079 template <class T>
00080 struct SetButtonWidthAction : AttributeChangedAction<X>
00081 {
00082 SetButtonWidthAction(Spin<T>* spin) : m_spin(spin) {}
00083 void operator()(const X& width) {m_spin->SetButtonWidth(width);}
00084 private:
00085 Spin<T>* m_spin;
00086 };
00087 }
00088
00089
00112 template <class T>
00113 class Spin : public Control
00114 {
00115 public:
00117 typedef typename boost::signal<void (T)> ValueChangedSignalType;
00118
00119
00121
00123 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,
00124 Clr text_color = CLR_BLACK, Clr interior = CLR_ZERO, Flags<WndFlag> flags = INTERACTIVE);
00125
00126 ~Spin();
00128
00130 virtual Pt MinUsableSize() const;
00131
00132 T Value() const;
00133 T StepSize() const;
00134 T MinValue() const;
00135 T MaxValue() const;
00136 bool Editable() const;
00137
00138 X ButtonWidth() const;
00139
00140 Clr TextColor() const;
00141 Clr InteriorColor() const;
00142 Clr HiliteColor() const;
00143 Clr SelectedTextColor() const;
00144
00145 mutable ValueChangedSignalType ValueChangedSignal;
00146
00147
00149 virtual void Render();
00150
00151 virtual void SizeMove(const Pt& ul, const Pt& lr);
00152
00153 virtual void Disable(bool b = true);
00154 virtual void SetColor(Clr c);
00155
00156 void Incr();
00157 void Decr();
00158
00160 void SetValue(T value);
00161
00162 void SetStepSize(T step);
00163 void SetMinValue(T value);
00164 void SetMaxValue(T value);
00165
00167 void AllowEdits(bool b = true);
00168
00169 void SetButtonWidth(X width);
00170
00171 void SetTextColor(Clr c);
00172 void SetInteriorColor(Clr c);
00173 void SetHiliteColor(Clr c);
00174 void SetSelectedTextColor(Clr c);
00175
00176 virtual void DefineAttributes(WndEditor* editor);
00178
00179 protected:
00180 typedef T ValueType;
00181
00182 enum {BORDER_THICK = 2, PIXEL_MARGIN = 5};
00183
00185 Spin();
00186
00187
00189 Button* UpButton() const;
00190 Button* DownButton() const;
00191 Edit* GetEdit() const;
00192
00193
00195 virtual void KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys);
00196 virtual void MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys);
00197
00198 virtual bool EventFilter(Wnd* w, const WndEvent& event);
00200
00201 private:
00202 void ConnectSignals();
00203 void Init(const boost::shared_ptr<Font>& font, Clr color, Clr text_color, Clr interior, Flags<WndFlag> flags);
00204 void ValueUpdated(const std::string& val_text);
00205 void IncrImpl(bool signal);
00206 void DecrImpl(bool signal);
00207 void SetValueImpl(T value, bool signal);
00208
00209 T m_value;
00210 T m_step_size;
00211 T m_min_value;
00212 T m_max_value;
00213
00214 bool m_editable;
00215
00216 Edit* m_edit;
00217 Button* m_up_button;
00218 Button* m_down_button;
00219
00220 X m_button_width;
00221
00222 static void ValueChangedEcho(const T& value);
00223
00224 friend class boost::serialization::access;
00225 template <class Archive>
00226 void serialize(Archive& ar, const unsigned int version);
00227 };
00228
00229
00230
00231 template<class T>
00232 Spin<T>::Spin() :
00233 Control(),
00234 m_value(),
00235 m_step_size(),
00236 m_min_value(),
00237 m_max_value(),
00238 m_editable(false),
00239 m_edit(0),
00240 m_up_button(0),
00241 m_down_button(0),
00242 m_button_width(15)
00243 {}
00244
00245 template<class T>
00246 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,
00247 Clr text_color, Clr interior, Flags<WndFlag> flags) :
00248 Control(x, y, w, font->Height() + 2 * PIXEL_MARGIN, flags),
00249 m_value(value),
00250 m_step_size(step),
00251 m_min_value(min),
00252 m_max_value(max),
00253 m_editable(edits),
00254 m_edit(0),
00255 m_up_button(0),
00256 m_down_button(0),
00257 m_button_width(15)
00258 {
00259 Init(font, color, text_color, interior, flags);
00260
00261 if (INSTRUMENT_ALL_SIGNALS)
00262 Connect(ValueChangedSignal, &ValueChangedEcho);
00263 }
00264
00265 template<class T>
00266 Spin<T>::~Spin()
00267 {}
00268
00269 template<class T>
00270 Pt Spin<T>::MinUsableSize() const
00271 {
00272 Pt edit_min = m_edit->MinUsableSize();
00273 Pt up_min = m_up_button->MinUsableSize();
00274 Pt down_min = m_down_button->MinUsableSize();
00275 return Pt(edit_min.x + std::max(up_min.x, down_min.x) + 2 * BORDER_THICK,
00276 std::max(up_min.y + down_min.y, edit_min.y) + 2 * BORDER_THICK);
00277 }
00278
00279 template<class T>
00280 T Spin<T>::Value() const
00281 { return m_value; }
00282
00283 template<class T>
00284 T Spin<T>::StepSize() const
00285 { return m_step_size; }
00286
00287 template<class T>
00288 T Spin<T>::MinValue() const
00289 { return m_min_value; }
00290
00291 template<class T>
00292 T Spin<T>::MaxValue() const
00293 { return m_max_value; }
00294
00295 template<class T>
00296 bool Spin<T>::Editable() const
00297 { return m_editable; }
00298
00299 template<class T>
00300 X Spin<T>::ButtonWidth() const
00301 { return m_button_width; }
00302
00303 template<class T>
00304 Clr Spin<T>::TextColor() const
00305 { return m_edit->TextColor(); }
00306
00307 template<class T>
00308 Clr Spin<T>::InteriorColor() const
00309 { return m_edit->InteriorColor(); }
00310
00311 template<class T>
00312 Clr Spin<T>::HiliteColor() const
00313 { return m_edit->HiliteColor(); }
00314
00315 template<class T>
00316 Clr Spin<T>::SelectedTextColor() const
00317 { return m_edit->SelectedTextColor(); }
00318
00319 template<class T>
00320 void Spin<T>::Render()
00321 {
00322 Clr color_to_use = Disabled() ? DisabledColor(Color()) : Color();
00323 Clr int_color_to_use = Disabled() ? DisabledColor(InteriorColor()) : InteriorColor();
00324 Pt ul = UpperLeft(), lr = LowerRight();
00325 BeveledRectangle(ul, lr, int_color_to_use, color_to_use, false, BORDER_THICK);
00326 }
00327
00328 template<class T>
00329 void Spin<T>::SizeMove(const Pt& ul, const Pt& lr)
00330 {
00331 Wnd::SizeMove(ul, lr);
00332 const X BUTTON_X_POS = Width() - m_button_width - BORDER_THICK;
00333 const Y BUTTONS_HEIGHT = Height() - 2 * BORDER_THICK;
00334 m_edit->SizeMove(Pt(), Pt(Width() - m_button_width, Height()));
00335 m_up_button->SizeMove(Pt(BUTTON_X_POS, Y(BORDER_THICK)),
00336 Pt(BUTTON_X_POS + m_button_width, BORDER_THICK + BUTTONS_HEIGHT / 2));
00337 m_down_button->SizeMove(Pt(BUTTON_X_POS, BORDER_THICK + BUTTONS_HEIGHT / 2),
00338 Pt(BUTTON_X_POS + m_button_width, BORDER_THICK + BUTTONS_HEIGHT));
00339 }
00340
00341 template<class T>
00342 void Spin<T>::Disable(bool b)
00343 {
00344 Control::Disable(b);
00345 m_edit->Disable(b);
00346 m_up_button->Disable(b);
00347 m_down_button->Disable(b);
00348 }
00349
00350 template<class T>
00351 void Spin<T>::SetColor(Clr c)
00352 {
00353 Control::SetColor(c);
00354 m_up_button->SetColor(c);
00355 m_down_button->SetColor(c);
00356 }
00357
00358 template<class T>
00359 void Spin<T>::Incr()
00360 { SetValueImpl(m_value + m_step_size, false); }
00361
00362 template<class T>
00363 void Spin<T>::Decr()
00364 { SetValueImpl(m_value - m_step_size, false); }
00365
00366 template<class T>
00367 void Spin<T>::SetValue(T value)
00368 { SetValueImpl(value, false); }
00369
00370 template<class T>
00371 void Spin<T>::SetStepSize(T step)
00372 {
00373 m_step_size = step;
00374 SetValue(m_value);
00375 }
00376
00377 template<class T>
00378 void Spin<T>::SetMinValue(T value)
00379 {
00380 m_min_value = value;
00381 if (m_value < m_min_value)
00382 SetValue(m_min_value);
00383 }
00384
00385 template<class T>
00386 void Spin<T>::SetMaxValue(T value)
00387 {
00388 m_max_value = value;
00389 if (m_max_value < m_value)
00390 SetValue(m_max_value);
00391 }
00392
00393 template<class T>
00394 void Spin<T>::SetTextColor(Clr c)
00395 { m_edit->SetTextColor(c); }
00396
00397 template<class T>
00398 void Spin<T>::SetButtonWidth(X width)
00399 {
00400 if (1 <= width) {
00401 if (Width() - 2 * BORDER_THICK - 1 < width)
00402 width = Width() - 2 * BORDER_THICK - 1;
00403 m_button_width = width;
00404 SizeMove(RelativeUpperLeft(), RelativeLowerRight());
00405 }
00406 }
00407
00408 template<class T>
00409 void Spin<T>::SetInteriorColor(Clr c)
00410 { m_edit->SetInteriorColor(c); }
00411
00412 template<class T>
00413 void Spin<T>::SetHiliteColor(Clr c)
00414 { m_edit->SetHiliteColor(c); }
00415
00416 template<class T>
00417 void Spin<T>::SetSelectedTextColor(Clr c)
00418 { m_edit->SetSelectedTextColor(c); }
00419
00420 template<class T>
00421 void Spin<T>::DefineAttributes(WndEditor* editor)
00422 {
00423 if (!editor)
00424 return;
00425 Control::DefineAttributes(editor);
00426 if (boost::is_same<T, int>::value)
00427 editor->Label("Spin<int>");
00428 else if (boost::is_same<T, double>::value)
00429 editor->Label("Spin<double>");
00430 else
00431 editor->Label("Spin<T>");
00432 boost::shared_ptr<spin_details::SetValueAction<T> > set_value_action(new spin_details::SetValueAction<T>(this));
00433 editor->Attribute<T>("Value", m_value, set_value_action);
00434 editor->Attribute<T>("Step Size", m_step_size, set_value_action);
00435 boost::shared_ptr<spin_details::SetMinValueAction<T> > set_min_value_action(new spin_details::SetMinValueAction<T>(this));
00436 editor->Attribute<T>("Min Value", m_min_value, set_min_value_action);
00437 boost::shared_ptr<spin_details::SetMaxValueAction<T> > set_max_value_action(new spin_details::SetMaxValueAction<T>(this));
00438 editor->Attribute<T>("Max Value", m_max_value, set_max_value_action);
00439 editor->Attribute("Editable", m_editable);
00440 boost::shared_ptr<spin_details::SetButtonWidthAction<T> > set_button_width_action(new spin_details::SetButtonWidthAction<T>(this));
00441 editor->Attribute<X>("Button Width", m_button_width, set_button_width_action);
00442 }
00443
00444 template<class T>
00445 Button* Spin<T>::UpButton() const
00446 { return m_up_button; }
00447
00448 template<class T>
00449 Button* Spin<T>::DownButton() const
00450 { return m_down_button; }
00451
00452 template<class T>
00453 Edit* Spin<T>::GetEdit() const
00454 { return m_edit; }
00455
00456 template<class T>
00457 void Spin<T>::KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys)
00458 {
00459 switch (key) {
00460 case GGK_HOME:
00461 SetValueImpl(m_min_value, true);
00462 break;
00463 case GGK_END:
00464 SetValueImpl(m_max_value, true);
00465 break;
00466 case GGK_PAGEUP:
00467 case GGK_UP:
00468 case GGK_PLUS:
00469 case GGK_KP_PLUS:
00470 IncrImpl(true);
00471 break;
00472 case GGK_PAGEDOWN:
00473 case GGK_DOWN:
00474 case GGK_MINUS:
00475 case GGK_KP_MINUS:
00476 DecrImpl(true);
00477 break;
00478 default:
00479 break;
00480 }
00481 }
00482
00483 template<class T>
00484 void Spin<T>::MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys)
00485 {
00486 for (int i = 0; i < move; ++i) {
00487 IncrImpl(true);
00488 }
00489 for (int i = 0; i < -move; ++i) {
00490 DecrImpl(true);
00491 }
00492 }
00493
00494 template<class T>
00495 bool Spin<T>::EventFilter(Wnd* w, const WndEvent& event)
00496 {
00497 if (w == m_edit) {
00498 if (!m_editable && event.Type() == WndEvent::GainingFocus) {
00499 GUI::GetGUI()->SetFocusWnd(this);
00500 return true;
00501 } else {
00502 return !m_editable;
00503 }
00504 }
00505 return false;
00506 }
00507
00508 template<class T>
00509 void Spin<T>::ConnectSignals()
00510 {
00511 Connect(m_edit->FocusUpdateSignal, &Spin::ValueUpdated, this);
00512 Connect(m_up_button->ClickedSignal, boost::bind(&Spin::IncrImpl, this, true));
00513 Connect(m_down_button->ClickedSignal, boost::bind(&Spin::DecrImpl, this, true));
00514 }
00515
00516 template<class T>
00517 void Spin<T>::Init(const boost::shared_ptr<Font>& font, Clr color, Clr text_color, Clr interior, Flags<WndFlag> flags)
00518 {
00519 boost::shared_ptr<StyleFactory> style = GetStyleFactory();
00520 Control::SetColor(color);
00521 m_edit = style->NewSpinEdit(X0, Y0, X1, boost::lexical_cast<std::string>(m_value), font, CLR_ZERO, text_color, interior);
00522 boost::shared_ptr<Font> small_font = GUI::GetGUI()->GetFont(font, static_cast<int>(font->PointSize() * 0.75));
00523 m_up_button = style->NewSpinIncrButton(X0, Y0, X1, Y1, "+", small_font, color);
00524 m_down_button = style->NewSpinDecrButton(X0, Y0, X1, Y1, "-", small_font, color);
00525 m_edit->InstallEventFilter(this);
00526 m_up_button->InstallEventFilter(this);
00527 m_down_button->InstallEventFilter(this);
00528 AttachChild(m_edit);
00529 AttachChild(m_up_button);
00530 AttachChild(m_down_button);
00531 ConnectSignals();
00532 SizeMove(UpperLeft(), LowerRight());
00533 }
00534
00535 template<class T>
00536 void Spin<T>::ValueUpdated(const std::string& val_text)
00537 {
00538 T value;
00539 try {
00540 value = boost::lexical_cast<T>(val_text);
00541 } catch (boost::bad_lexical_cast) {
00542 SetValueImpl(m_min_value, true);
00543 return;
00544 }
00545 SetValueImpl(value, true);
00546 }
00547
00548 template<class T>
00549 void Spin<T>::IncrImpl(bool signal)
00550 { SetValueImpl(static_cast<T>(m_value + m_step_size), signal); }
00551
00552 template<class T>
00553 void Spin<T>::DecrImpl(bool signal)
00554 { SetValueImpl(static_cast<T>(m_value - m_step_size), signal); }
00555
00556 template<class T>
00557 void Spin<T>::SetValueImpl(T value, bool signal)
00558 {
00559 T old_value = m_value;
00560 if (value < m_min_value) {
00561 m_value = m_min_value;
00562 } else if (m_max_value < value) {
00563 m_value = m_max_value;
00564 } else {
00565
00566 if (std::abs(spin_details::mod(static_cast<T>(value - m_min_value), m_step_size)) >
00567 std::numeric_limits<T>::epsilon()) {
00568
00569 T closest_below =
00570 static_cast<T>(
00571 spin_details::div(static_cast<T>(value - m_min_value), m_step_size) *
00572 static_cast<T>(m_step_size + m_min_value));
00573 T closest_above =
00574 static_cast<T>(closest_below + m_step_size);
00575 m_value =
00576 ((value - closest_below) < (closest_above - value) ?
00577 closest_below : closest_above);
00578 } else {
00579 m_value = value;
00580 }
00581 }
00582 *m_edit << m_value;
00583 if (signal && m_value != old_value)
00584 ValueChangedSignal(m_value);
00585 }
00586
00587 template <class T>
00588 void Spin<T>::ValueChangedEcho(const T& value)
00589 { std::cerr << "GG SIGNAL : Spin<>::ValueChangedSignal(value=" << value << ")\n"; }
00590
00591
00592 template <class T>
00593 template <class Archive>
00594 void Spin<T>::serialize(Archive& ar, const unsigned int version)
00595 {
00596 ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Control)
00597 & BOOST_SERIALIZATION_NVP(m_value)
00598 & BOOST_SERIALIZATION_NVP(m_step_size)
00599 & BOOST_SERIALIZATION_NVP(m_min_value)
00600 & BOOST_SERIALIZATION_NVP(m_max_value)
00601 & BOOST_SERIALIZATION_NVP(m_editable)
00602 & BOOST_SERIALIZATION_NVP(m_edit)
00603 & BOOST_SERIALIZATION_NVP(m_up_button)
00604 & BOOST_SERIALIZATION_NVP(m_down_button);
00605
00606 if (Archive::is_loading::value)
00607 ConnectSignals();
00608 }
00609
00610 namespace spin_details {
00611
00612 template <class T> inline
00613 T mod (T dividend, T divisor) {return static_cast<T>(dividend % divisor);}
00614
00615
00616 template <> inline
00617 float mod<float> (float dividend, float divisor) {return std::fmod(dividend, divisor);}
00618 template <> inline
00619 double mod<double> (double dividend, double divisor) {return std::fmod(dividend, divisor);}
00620 template <> inline
00621 long double mod<long double> (long double dividend, long double divisor) {return std::fmod(dividend, divisor);}
00622
00623
00624 template <class T> inline
00625 T div (T dividend, T divisor) {return static_cast<T>(dividend / divisor);}
00626
00627
00628 template <> inline
00629 float div<float> (float dividend, float divisor) {return std::floor(dividend / divisor);}
00630 template <> inline
00631 double div<double> (double dividend, double divisor) {return std::floor(dividend / divisor);}
00632 template <> inline
00633 long double div<long double> (long double dividend, long double divisor) {return std::floor(dividend / divisor);}
00634 }
00635
00636 }
00637
00638 #endif // _GG_Spin_h_
00639