Spin.h

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 /* GG is a GUI for SDL and OpenGL.
00003    Copyright (C) 2003 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@hotmail.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<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(); // dtor
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 // template implementations
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_BLACK*/, Clr interior/* = CLR_ZERO*/, Flags<WndFlag> flags/* = CLICKABLE*/) : 
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; // height of *both* buttons
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/* = true*/)
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         // if the value supplied does not equal a valid value
00423         if (std::abs(spin_details::mod((value - m_min_value), m_step_size)) > std::numeric_limits<T>::epsilon()) {
00424             // find nearest valid value to the one supplied
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     // provides a typesafe mod function
00611     template <class T> inline 
00612     T mod (T dividend, T divisor) {return dividend % divisor;}
00613 
00614     // template specializations
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     // provides a typesafe div function
00623     template <class T> inline 
00624     T div (T dividend, T divisor) {return dividend / divisor;}
00625 
00626     // template specializations
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 } // namespace spin_details
00634 
00635 } // namespace GG
00636 
00637 #endif // _GG_Spin_h_
00638 

Generated on Wed Mar 26 14:35:42 2008 for GG by  doxygen 1.5.2