GG
|
00001 // -*- C++ -*- 00002 /* GG is a GUI for SDL and OpenGL. 00003 Copyright (C) 2003-2008 T. Zachary Laine 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public License 00007 as published by the Free Software Foundation; either version 2.1 00008 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the Free 00017 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 00018 02111-1307 USA 00019 00020 If you do not wish to comply with the terms of the LGPL please 00021 contact the author as other terms are available for a fee. 00022 00023 Zach Laine 00024 whatwasthataddress@gmail.com */ 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 // forward declaration of helper functions and classes 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(); // dtor 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 // template implementations 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_BLACK*/, Clr interior/* = CLR_ZERO*/, Flags<WndFlag> flags/* = INTERACTIVE*/) : 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; // height of *both* buttons 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/* = true*/) 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 // if the value supplied does not equal a valid value 00566 if (std::abs(spin_details::mod(static_cast<T>(value - m_min_value), m_step_size)) > 00567 std::numeric_limits<T>::epsilon()) { 00568 // find nearest valid value to the one supplied 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 // provides a typesafe mod function 00612 template <class T> inline 00613 T mod (T dividend, T divisor) {return static_cast<T>(dividend % divisor);} 00614 00615 // template specializations 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 // provides a typesafe div function 00624 template <class T> inline 00625 T div (T dividend, T divisor) {return static_cast<T>(dividend / divisor);} 00626 00627 // template specializations 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 } // namespace spin_details 00635 00636 } // namespace GG 00637 00638 #endif // _GG_Spin_h_ 00639