Code Search for Developers
 
 
  

Spin.h from FreeOrion at Krugle


Show Spin.h syntax highlighted

// -*- C++ -*-
/* GG is a GUI for SDL and OpenGL.
   Copyright (C) 2003 T. Zachary Laine

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1
   of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
    
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA

   If you do not wish to comply with the terms of the LGPL please
   contact the author as other terms are available for a fee.
    
   Zach Laine
   whatwasthataddress@hotmail.com */

/** \file Spin.h
    Contains the Spin class template, which provides a spin-box control that allows the user to select a value from a 
    range an arbitrary type (int, double, an enum, etc.). */

#ifndef _GG_Spin_h_
#define _GG_Spin_h_

#include <GG/Button.h>
#include <GG/DrawUtil.h>
#include <GG/Edit.h>
#include <GG/GUI.h>
#include <GG/StyleFactory.h>
#include <GG/WndEditor.h>
#include <GG/WndEvent.h>

#include <cmath>
#include <limits>


namespace GG {

// forward declaration of helper functions and classes
namespace spin_details {
    template <class T> T mod(T, T);
    template <class T> T div(T, T);

    template <class T>
    struct SetValueAction : AttributeChangedAction<T>
    {
        SetValueAction(Spin<T>* spin) : m_spin(spin) {}
        void operator()(const T& value) {m_spin->SetValue(m_spin->Value());}
    private:
        Spin<T>* m_spin;
    };

    template <class T>
    struct SetMinValueAction : AttributeChangedAction<T>
    {
        SetMinValueAction(Spin<T>* spin) : m_spin(spin) {}
        void operator()(const T& value) {m_spin->SetValue(value);}
    private:
        Spin<T>* m_spin;
    };

    template <class T>
    struct SetMaxValueAction : AttributeChangedAction<T>
    {
        SetMaxValueAction(Spin<T>* spin) : m_spin(spin) {}
        void operator()(const T& value) {m_spin->SetValue(value);}
    private:
        Spin<T>* m_spin;
    };

    template <class T>
    struct SetButtonWidthAction : AttributeChangedAction<int>
    {
        SetButtonWidthAction(Spin<T>* spin) : m_spin(spin) {}
        void operator()(const int& width) {m_spin->SetButtonWidth(width);}
    private:
        Spin<T>* m_spin;
    };
}


/** a spin box control.  This control class is templated so that arbitrary data types can be used with Spin.  All the
    built-in numeric types are supported by the code here.  If you want to use some other type, such as an enum type,
    you need to define operator+(), operator-(), and template specializations of spin_details::mod() and
    spin_details::div().  Spin controls are optionally directly editable by the user.  When the user inputs a value that
    is not valid for the Spin's parameters (not on a step boundary, or outside the allowed range), the input gets locked
    to the nearest valid value.  The user is responsible for selecting a min, max, and step size that make sense.  For
    instance, min = 0, max = 4, step = 3 may produce odd results if the user increments all the way to the top, then
    back down, to produce the sequence 0, 3, 4, 1, 0.  To avoid this, choose the values so that (max - min) mod step ==
    0.  It is possible to provide custom buttons for a Spin to use; if you choose to add custom buttons, make sure they
    look alright at arbitrary sizes, and note that Spin buttons are always H wide by H/2 tall, where H is the height of
    the Spin, less the thickness of the Spin's border.  Note that if you want Spin controls to be automatically
    serialized in your application, you need to export each instantiation of Spin<> yourself (e.g. Spin<int> or
    Spin<double>).  See the boost serialization documentation for details. */
template <class T>
class Spin : public Control
{
public:
    /** \name Signal Types */ //@{
    typedef typename boost::signal<void (T)> ValueChangedSignalType;  ///< emitted whenever the value of the Spin has changed
    //@}

    /** \name Slot Types */ //@{
    typedef typename ValueChangedSignalType::slot_type ValueChangedSlotType;   ///< type of functor(s) invoked on a ValueChangedSignalType
    //@}

    /** \name Structors */ //@{
    /** ctor that does not required height. Height is determined from the font and point size used.*/
    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, 
         Clr text_color = CLR_BLACK, Clr interior = CLR_ZERO, Flags<WndFlag> flags = CLICKABLE);

    ~Spin(); // dtor
    //@}

    /** \name Accessors */ //@{
    virtual Pt MinUsableSize() const;

    T     Value() const;              ///< returns the current value of the control's text
    T     StepSize() const;           ///< returns the step size of the control
    T     MinValue() const;           ///< returns the minimum value of the control
    T     MaxValue() const;           ///< returns the maximum value of the control
    bool  Editable() const;           ///< returns true if the spinbox can have its value typed in directly

    int   ButtonWidth() const;        ///< returns the width used for the up and down buttons

    Clr   TextColor() const;          ///< returns the text color
    Clr   InteriorColor() const;      ///< returns the the interior color of the control
    Clr   HiliteColor() const;        ///< returns the color used to render hiliting around selected text
    Clr   SelectedTextColor() const;  ///< returns the color used to render selected text

    mutable ValueChangedSignalType ValueChangedSignal; ///< the value changed signal object for this DynamicGraphic
    //@}

    /** \name Mutators */ //@{
    virtual void Render();
    virtual void KeyPress(Key key, Flags<ModKey> mod_keys);
    virtual void MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys);

    virtual void SizeMove(const Pt& ul, const Pt& lr);

    virtual void Disable(bool b = true);
    virtual void SetColor(Clr c);

    void Incr();  ///< increments the value of the control's text by StepSize(), up to at most MaxValue()
    void Decr();  ///< decrements the value of the control's text by StepSize(), down to at least MinValue()

    /** sets the value of the control's text to \a value, locked to the range [MinValue(), MaxValue()]*/
    void SetValue(T value);

    void SetStepSize(T step);   ///< sets the step size of the control to \a step
    void SetMinValue(T value);  ///< sets the minimum value of the control to \a value
    void SetMaxValue(T value);  ///< sets the maximum value of the control to \a value

    /** turns on or off the mode that allows the user to edit the value in the spinbox directly. */
    void AllowEdits(bool b = true);

    void SetButtonWidth(int width);    ///< sets the width used for the up and down buttons

    void SetTextColor(Clr c);          ///< sets the text color
    void SetInteriorColor(Clr c);      ///< sets the interior color of the control
    void SetHiliteColor(Clr c);        ///< sets the color used to render hiliting around selected text
    void SetSelectedTextColor(Clr c);  ///< sets the color used to render selected text   

    virtual void DefineAttributes(WndEditor* editor);
    //@}

protected:
    typedef T ValueType;

    enum {BORDER_THICK = 2, PIXEL_MARGIN = 5};

    /** \name Structors */ //@{
    Spin(); ///< default ctor
    //@}

    /** \name Accessors */ //@{
    Button*     UpButton() const;   ///< returns a pointer to the Button control used as this control's up button
    Button*     DownButton() const; ///< returns a pointer to the Button control used as this control's down button
    Edit*       GetEdit() const;    ///< returns a pointer to the Edit control used to render this control's text and accept keyboard input
    //@}

    /** \name Mutators */ //@{
    virtual bool EventFilter(Wnd* w, const WndEvent& event);
    //@}

private:
    void ConnectSignals();
    void Init(const boost::shared_ptr<Font>& font, Clr color, Clr text_color, Clr interior, Flags<WndFlag> flags);
    void ValueUpdated(const std::string& val_text);

    T          m_value;
    T          m_step_size;
    T          m_min_value;
    T          m_max_value;

    bool       m_editable;

    Edit*      m_edit;
    Button*    m_up_button;
    Button*    m_down_button;

    int        m_button_width;

    friend class boost::serialization::access;
    template <class Archive>
    void serialize(Archive& ar, const unsigned int version);
};


// template implementations
template<class T>
Spin<T>::Spin() : 
    Control(),
    m_value(),
    m_step_size(),
    m_min_value(),
    m_max_value(),
    m_editable(false),
    m_edit(0),
    m_up_button(0),
    m_down_button(0),
    m_button_width(15)
{}

template<class T>
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, 
              Clr text_color/* = CLR_BLACK*/, Clr interior/* = CLR_ZERO*/, Flags<WndFlag> flags/* = CLICKABLE*/) : 
    Control(x, y, w, font->Height() + 2 * PIXEL_MARGIN, flags),
    m_value(value),
    m_step_size(step),
    m_min_value(min),
    m_max_value(max),
    m_editable(edits),
    m_edit(0),
    m_up_button(0),
    m_down_button(0),
    m_button_width(15)
{
    Init(font, color, text_color, interior, flags);
}

template<class T>
Spin<T>::~Spin()
{}

template<class T>
Pt Spin<T>::MinUsableSize() const
{
    Pt edit_min = m_edit->MinUsableSize();
    Pt up_min = m_up_button->MinUsableSize();
    Pt down_min = m_down_button->MinUsableSize();
    return Pt(edit_min.x + std::max(up_min.x, down_min.x) + 2 * BORDER_THICK,
              std::max(up_min.y + down_min.y, edit_min.y) + 2 * BORDER_THICK);
}

template<class T>
T Spin<T>::Value() const
{
    return m_value;
}

template<class T>
T Spin<T>::StepSize() const
{
    return m_step_size;
}

template<class T>
T Spin<T>::MinValue() const
{
    return m_min_value;
}

template<class T>
T Spin<T>::MaxValue() const
{
    return m_max_value;
}

template<class T>
bool Spin<T>::Editable() const 
{
    return m_editable;
}

template<class T>
int Spin<T>::ButtonWidth() const
{
    return m_button_width;
}

template<class T>
Clr Spin<T>::TextColor() const
{
    return m_edit->TextColor();
}

template<class T>
Clr Spin<T>::InteriorColor() const
{
    return m_edit->InteriorColor();
}

template<class T>
Clr Spin<T>::HiliteColor() const
{
    return m_edit->HiliteColor();
}

template<class T>
Clr Spin<T>::SelectedTextColor() const
{
    return m_edit->SelectedTextColor();
}

template<class T>
void Spin<T>::Render()
{
    Clr color_to_use = Disabled() ? DisabledColor(Color()) : Color();
    Clr int_color_to_use = Disabled() ? DisabledColor(InteriorColor()) : InteriorColor();
    Pt ul = UpperLeft(), lr = LowerRight();
    BeveledRectangle(ul.x, ul.y, lr.x, lr.y, int_color_to_use, color_to_use, false, BORDER_THICK);
}

template<class T>
void Spin<T>::KeyPress(Key key, Flags<ModKey> mod_keys)
{
    switch (key) {
    case GGK_HOME:
        SetValue(m_min_value);
        break;
    case GGK_END:
        SetValue(m_max_value);
        break;
    case GGK_PAGEUP:
    case GGK_UP:
    case GGK_PLUS:
    case GGK_KP_PLUS:
        Incr();
        break;
    case GGK_PAGEDOWN:
    case GGK_DOWN:
    case GGK_MINUS:
    case GGK_KP_MINUS:
        Decr();
        break;
    default:
        break;
    }
}

template<class T>
void Spin<T>::MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys)
{
    for (int i = 0; i < move; ++i) {
        Incr();
    }
    for (int i = 0; i < -move; ++i) {
        Decr();
    }
}

template<class T>
void Spin<T>::SizeMove(const Pt& ul, const Pt& lr)
{
    Wnd::SizeMove(ul, lr);
    const int BUTTON_X_POS = Width() - m_button_width - BORDER_THICK;
    const int BUTTONS_HEIGHT = Height() - 2 * BORDER_THICK; // height of *both* buttons
    m_edit->SizeMove(Pt(0, 0), Pt(Width() - m_button_width, Height()));
    m_up_button->SizeMove(Pt(BUTTON_X_POS, BORDER_THICK),
                          Pt(BUTTON_X_POS + m_button_width, BORDER_THICK + BUTTONS_HEIGHT / 2));
    m_down_button->SizeMove(Pt(BUTTON_X_POS, BORDER_THICK + BUTTONS_HEIGHT / 2),
                            Pt(BUTTON_X_POS + m_button_width, BORDER_THICK + BUTTONS_HEIGHT));
}

template<class T>
void Spin<T>::Disable(bool b/* = true*/)
{
    Control::Disable(b);
    m_edit->Disable(b);
    m_up_button->Disable(b);
    m_down_button->Disable(b);
}

template<class T>
void Spin<T>::SetColor(Clr c)
{
    Control::SetColor(c);
    m_up_button->SetColor(c);
    m_down_button->SetColor(c);
}

template<class T>
void Spin<T>::Incr()
{
    SetValue(m_value + m_step_size);
}

template<class T>
void Spin<T>::Decr()
{
    SetValue(m_value - m_step_size);
}

template<class T>
void Spin<T>::SetValue(T value)
{
    T old_value = m_value;
    if (value < m_min_value) {
        m_value = m_min_value;
    } else if (m_max_value < value) {
        m_value = m_max_value;
    } else {
        // if the value supplied does not equal a valid value
        if (std::abs(spin_details::mod((value - m_min_value), m_step_size)) > std::numeric_limits<T>::epsilon()) {
            // find nearest valid value to the one supplied
            T closest_below = spin_details::div((value - m_min_value), m_step_size) * m_step_size + m_min_value;
            T closest_above = closest_below + m_step_size;
            m_value = ((value - closest_below) < (closest_above - value) ? closest_below : closest_above);
        } else {
            m_value = value;
        }
    }
    *m_edit << m_value;
    if (m_value != old_value)
        ValueChangedSignal(m_value);
}

template<class T>
void Spin<T>::SetStepSize(T step)
{
    m_step_size = step;
    SetValue(m_value);
}

template<class T>
void Spin<T>::SetMinValue(T value)
{
    m_min_value = value;
    if (m_value < m_min_value)
        SetValue(m_min_value);
}

template<class T>
void Spin<T>::SetMaxValue(T value)
{
    m_max_value = value;
    if (m_max_value < m_value)
        SetValue(m_max_value);
}

template<class T>
void Spin<T>::SetTextColor(Clr c)
{
    m_edit->SetTextColor(c);
}

template<class T>
void Spin<T>::SetButtonWidth(int width)
{
    if (1 <= width) {
        if (Width() - 2 * BORDER_THICK - 1 < width)
            width = Width() - 2 * BORDER_THICK - 1;
        m_button_width = width;
        SizeMove(RelativeUpperLeft(), RelativeLowerRight());
    }
}

template<class T>
void Spin<T>::SetInteriorColor(Clr c)
{
    m_edit->SetInteriorColor(c);
}

template<class T>
void Spin<T>::SetHiliteColor(Clr c)
{
    m_edit->SetHiliteColor(c);
}

template<class T>
void Spin<T>::SetSelectedTextColor(Clr c)
{
    m_edit->SetSelectedTextColor(c);
}

template<class T>
void Spin<T>::DefineAttributes(WndEditor* editor)
{
    if (!editor)
        return;
    Control::DefineAttributes(editor);
    if (boost::is_same<T, int>::value)
        editor->Label("Spin<int>");
    else if (boost::is_same<T, double>::value)
        editor->Label("Spin<double>");
    else
        editor->Label("Spin<T>");
    boost::shared_ptr<spin_details::SetValueAction<T> > set_value_action(new spin_details::SetValueAction<T>(this));
    editor->Attribute<T>("Value", m_value, set_value_action);
    editor->Attribute<T>("Step Size", m_step_size, set_value_action);
    boost::shared_ptr<spin_details::SetMinValueAction<T> > set_min_value_action(new spin_details::SetMinValueAction<T>(this));
    editor->Attribute<T>("Min Value", m_min_value, set_min_value_action);
    boost::shared_ptr<spin_details::SetMaxValueAction<T> > set_max_value_action(new spin_details::SetMaxValueAction<T>(this));
    editor->Attribute<T>("Max Value", m_max_value, set_max_value_action);
    editor->Attribute("Editable", m_editable);
    boost::shared_ptr<spin_details::SetButtonWidthAction<T> > set_button_width_action(new spin_details::SetButtonWidthAction<T>(this));
    editor->Attribute<int>("Button Width", m_button_width, set_button_width_action);
}

template<class T>
Button* Spin<T>::UpButton() const
{
    return m_up_button;
}

template<class T>
Button* Spin<T>::DownButton() const
{
    return m_down_button;
}

template<class T>
Edit* Spin<T>::GetEdit() const
{
    return m_edit;
}

template<class T>
bool Spin<T>::EventFilter(Wnd* w, const WndEvent& event)
{
    if (w == m_edit) {
        if (!m_editable && event.Type() == WndEvent::GainingFocus) {
            GUI::GetGUI()->SetFocusWnd(this);
            return true;
        } else {
            return !m_editable;
        }
    }
    return false;
}

template<class T>
void Spin<T>::ConnectSignals()
{
    Connect(m_edit->FocusUpdateSignal, &Spin::ValueUpdated, this);
    Connect(m_up_button->ClickedSignal, &Spin::Incr, this);
    Connect(m_down_button->ClickedSignal, &Spin::Decr, this);
}

template<class T>
void Spin<T>::Init(const boost::shared_ptr<Font>& font, Clr color, Clr text_color, Clr interior, Flags<WndFlag> flags)
{
    boost::shared_ptr<StyleFactory> style = GetStyleFactory();
    Control::SetColor(color);
    m_edit = style->NewSpinEdit(0, 0, 1, boost::lexical_cast<std::string>(m_value), font, CLR_ZERO, text_color, interior);
    boost::shared_ptr<Font> small_font = GUI::GetGUI()->GetFont(font->FontName(), static_cast<int>(font->PointSize() * 0.75));
    m_up_button = style->NewSpinIncrButton(0, 0, 1, 1, "+", small_font, color);
    m_down_button = style->NewSpinDecrButton(0, 0, 1, 1, "-", small_font, color);
    m_edit->InstallEventFilter(this);
    m_up_button->InstallEventFilter(this);
    m_down_button->InstallEventFilter(this);
    AttachChild(m_edit);
    AttachChild(m_up_button);
    AttachChild(m_down_button);
    ConnectSignals();
    SizeMove(UpperLeft(), LowerRight());
}

template<class T>
void Spin<T>::ValueUpdated(const std::string& val_text)
{
    T value;
    try {
        value = boost::lexical_cast<T>(val_text);
    } catch (boost::bad_lexical_cast) {
        SetValue(m_min_value);
        return;
    }
    SetValue(value);
}

template <class T>
template <class Archive>
void Spin<T>::serialize(Archive& ar, const unsigned int version)
{
    ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Control)
        & BOOST_SERIALIZATION_NVP(m_value)
        & BOOST_SERIALIZATION_NVP(m_step_size)
        & BOOST_SERIALIZATION_NVP(m_min_value)
        & BOOST_SERIALIZATION_NVP(m_max_value)
        & BOOST_SERIALIZATION_NVP(m_editable)
        & BOOST_SERIALIZATION_NVP(m_edit)
        & BOOST_SERIALIZATION_NVP(m_up_button)
        & BOOST_SERIALIZATION_NVP(m_down_button);

    if (Archive::is_loading::value)
        ConnectSignals();
}

namespace spin_details {
    // provides a typesafe mod function
    template <class T> inline 
    T mod (T dividend, T divisor) {return dividend % divisor;}

    // template specializations
    template <> inline 
    float mod<float> (float dividend, float divisor) {return std::fmod(dividend, divisor);}
    template <> inline 
    double mod<double> (double dividend, double divisor) {return std::fmod(dividend, divisor);}
    template <> inline 
    long double mod<long double> (long double dividend, long double divisor) {return std::fmod(dividend, divisor);}

    // provides a typesafe div function
    template <class T> inline 
    T div (T dividend, T divisor) {return dividend / divisor;}

    // template specializations
    template <> inline 
    float div<float> (float dividend, float divisor) {return std::floor(dividend / divisor);}
    template <> inline 
    double div<double> (double dividend, double divisor) {return std::floor(dividend / divisor);}
    template <> inline 
    long double div<long double> (long double dividend, long double divisor) {return std::floor(dividend / divisor);}
} // namespace spin_details

} // namespace GG

#endif // _GG_Spin_h_





See more files for this project here

FreeOrion

FreeOrion brings nation building to a galactic scale with its full-featured grand campaign and in-game racial histories, in addition to the classic 4X model of galactic conquest and tactical combat.

Project homepage: http://sourceforge.net/projects/freeorion
Programming language(s): C,C++
License: other

  Ogre/
    OgreGUI.h
  SDL/
    SDLGUI.h
  dialogs/
    ColorDlg.h
    FileDlg.h
    ThreeButtonDlg.h
  AlignmentFlags.h
  Base.h
  BrowseInfoWnd.h
  Button.h
  Clr.h
  Control.h
  Cursor.h
  DrawUtil.h
  DropDownList.h
  DynamicGraphic.h
  Edit.h
  Enum.h
  EventPump.h
  Exception.h
  Flags.h
  Font.h
  GUI.h
  Layout.h
  ListBox.h
  Menu.h
  MultiEdit.h
  PluginInterface.h
  PtRect.h
  Scroll.h
  Signal0.h
  Signal1.h
  Signal2.h
  Signal3.h
  Signal4.h
  Signal5.h
  Signal6.h
  Signal7.h
  Signal8.h
  SignalTemplate.h
  SignalsAndSlots.h
  Slider.h
  Spin.h
  StaticGraphic.h
  StyleFactory.h
  TabWnd.h
  TextControl.h
  Texture.h
  Timer.h
  Wnd.h
  WndEditor.h
  WndEvent.h
  ZList.h
  gen_signals.py
  glext.h