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