GG

Spin.h

Go to the documentation of this file.
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