diff --git a/TODO b/TODO index c3b4bb261d..854025c903 100644 --- a/TODO +++ b/TODO @@ -5,3 +5,5 @@ All MSW GTK + +* check/radio box remains pressed diff --git a/include/wx/chkconf.h b/include/wx/chkconf.h index 9f779319ae..8d055d7758 100644 --- a/include/wx/chkconf.h +++ b/include/wx/chkconf.h @@ -68,6 +68,19 @@ # endif #endif /* wxUSE_RADIOBTN */ +#if wxUSE_RADIOBOX +# if !wxUSE_RADIOBTN || !wxUSE_STATBOX +# ifdef wxABORT_ON_CONFIG_ERROR +# error "wxUSE_RADIOBOX requires wxUSE_RADIOBTN and wxUSE_STATBOX" +# else +# undef wxUSE_RADIOBTN +# undef wxUSE_STATBOX +# define wxUSE_RADIOBTN 1 +# define wxUSE_STATBOX 1 +# endif +# endif +#endif /* wxUSE_RADIOBOX */ + #if wxUSE_STOPWATCH # if !wxUSE_LONGLONG # ifdef wxABORT_ON_CONFIG_ERROR diff --git a/include/wx/radiobox.h b/include/wx/radiobox.h index 7b293a44ca..608d4023d9 100644 --- a/include/wx/radiobox.h +++ b/include/wx/radiobox.h @@ -18,7 +18,72 @@ WXDLLEXPORT_DATA(extern const wxChar*) wxRadioBoxNameStr; -#if defined(__WXMSW__) +// ---------------------------------------------------------------------------- +// wxRadioBoxBase is not a base class at all, but rather a mix-in because the +// real wxRadioBox derives from different classes on different platforms: for +// example, it is a +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxRadioBoxBase +{ +public: + // selection + virtual void SetSelection(int n) = 0; + virtual int GetSelection() const = 0; + + virtual wxString GetStringSelection() const + { + wxString s; + int sel = GetSelection(); + if ( sel != wxNOT_FOUND ) + s = GetString(sel); + + return s; + } + + virtual bool SetStringSelection(const wxString& s) + { + int sel = FindString(s); + if ( sel != wxNOT_FOUND ) + { + SetSelection(sel); + + return TRUE; + } + + return FALSE; + } + + // string access + virtual int GetCount() const = 0; + virtual int FindString(const wxString& s) const + { + int count = GetCount(); + for ( int n = 0; n < count; n++ ) + { + if ( GetString(n) == s ) + return n; + } + + return wxNOT_FOUND; + } + + virtual wxString GetString(int n) const = 0; + virtual void SetString(int n, const wxString& label) = 0; + + // change the individual radio button state + virtual void Enable(int n, bool enable = TRUE) = 0; + virtual void Show(int n, bool show = TRUE) = 0; + + // for compatibility only, don't use + int Number() const { return GetCount(); } + wxString GetLabel(int n) const { return GetString(n); } + void SetLabel(int n, const wxString& label) { SetString(n, label); } +}; + +#if defined(__WXUNIVERSAL__) + #include "wx/univ/radiobox.h" +#elif defined(__WXMSW__) #include "wx/msw/radiobox.h" #elif defined(__WXMOTIF__) #include "wx/motif/radiobox.h" diff --git a/include/wx/univ/radiobox.h b/include/wx/univ/radiobox.h new file mode 100644 index 0000000000..0cab16a50e --- /dev/null +++ b/include/wx/univ/radiobox.h @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/univ/radiobox.h +// Purpose: wxRadioBox declaration +// Author: Vadim Zeitlin +// Modified by: +// Created: 11.09.00 +// RCS-ID: $Id$ +// Copyright: (c) 2000 Vadim Zeitlin +// Licence: wxWindows license +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_UNIV_RADIOBOX_H_ +#define _WX_UNIV_RADIOBOX_H_ + +#ifdef __GNUG__ + #pragma interface "univradiobox.h" +#endif + +class WXDLLEXPORT wxRadioButton; + +#include "wx/statbox.h" +#include "wx/dynarray.h" + +WX_DEFINE_ARRAY(wxRadioButton *, wxArrayRadioButtons); + +// ---------------------------------------------------------------------------- +// wxRadioBox: a box full of radio buttons +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxRadioBox : public wxStaticBox, + public wxRadioBoxBase +{ +public: + // wxRadioBox construction + wxRadioBox() { Init(); } + + wxRadioBox(wxWindow *parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + int n = 0, const wxString *choices = NULL, + int majorDim = 0, + long style = wxRA_SPECIFY_COLS, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxRadioBoxNameStr) + { + Init(); + + (void)Create(parent, id, title, pos, size, n, choices, + majorDim, style, val, name); + } + + bool Create(wxWindow *parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + int n = 0, const wxString *choices = NULL, + int majorDim = 0, + long style = wxRA_SPECIFY_COLS, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxRadioBoxNameStr); + + // implement wxRadioBox interface + virtual void SetSelection(int n); + virtual int GetSelection() const; + + virtual int GetCount() const { return m_buttons.GetCount(); } + virtual wxString GetString(int n) const; + virtual void SetString(int n, const wxString& label); + + virtual void Enable(int n, bool enable = TRUE); + virtual void Show(int n, bool show = TRUE); + + // we also override the wxControl methods to avoid virtual function hiding + virtual bool Enable(bool enable = TRUE); + virtual bool Show(bool show = TRUE); + virtual wxString GetLabel() const; + virtual void SetLabel(const wxString& label); + + // wxUniversal-only methods + void Append(int n, const wxString *choices); + +protected: + // override the base class methods dealing with window positioning/sizing + // as we must move/size the buttons as well + virtual void DoMoveWindow(int x, int y, int width, int height); + virtual wxSize DoGetBestClientSize() const; + + // common part of all ctors + void Init(); + + // check that the index is valid + bool IsValid(int n) const { return n >= 0 && n < GetCount(); } + + // sets m_majorDim and calculate m_numCols and m_numRows + void SetMajorDim(int majorDim); + + // calculate the max size of all buttons + wxSize GetMaxButtonSize() const; + + // the currently selected radio button or -1 + int m_selection; + + // the parameters defining the button layout: majorDim meaning depends on + // the style and is the (max) number of columns if it includes + // wxRA_SPECIFY_COLS and is the (max) number of rows if it includes + // wxRA_SPECIFY_ROWS - the number of rows and columns is calculated from + // it + int m_majorDim, + m_numCols, + m_numRows; + + // all radio buttons + wxArrayRadioButtons m_buttons; + +private: + DECLARE_DYNAMIC_CLASS(wxRadioBox) +}; + +#endif // _WX_UNIV_RADIOBOX_H_ diff --git a/include/wx/univ/radiobut.h b/include/wx/univ/radiobut.h index fd0c642108..0871048abe 100644 --- a/include/wx/univ/radiobut.h +++ b/include/wx/univ/radiobut.h @@ -35,7 +35,7 @@ public: const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxCheckBoxNameStr) + const wxString& name = wxRadioButtonNameStr) { Init(); @@ -49,7 +49,7 @@ public: const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxCheckBoxNameStr); + const wxString& name = wxRadioButtonNameStr); // override some base class methods virtual void ChangeValue(bool value); diff --git a/include/wx/univ/statbox.h b/include/wx/univ/statbox.h index 7a8f530f61..f76638a825 100644 --- a/include/wx/univ/statbox.h +++ b/include/wx/univ/statbox.h @@ -1,4 +1,4 @@ -/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // Name: wx/univ/statbox.h // Purpose: wxStaticBox declaration // Author: Vadim Zeitlin @@ -47,9 +47,21 @@ public: long style = 0, const wxString& name = wxStaticBoxNameStr); + // the origin of the static box is inside the border and under the label: + // take account of this + virtual wxPoint GetClientAreaOrigin() const; + protected: + // take into account the border/label here as well + virtual void DoSetClientSize(int width, int height); + virtual void DoGetClientSize(int *width, int *height) const; + + // draw the control virtual void DoDraw(wxControlRenderer *renderer); + // get the size of the border + wxRect GetBorderGeometry() const; + private: DECLARE_DYNAMIC_CLASS(wxStaticBox) }; diff --git a/samples/univ/univ.cpp b/samples/univ/univ.cpp index d2c020ba38..65b76fc150 100644 --- a/samples/univ/univ.cpp +++ b/samples/univ/univ.cpp @@ -38,6 +38,7 @@ #include "wx/button.h" #include "wx/checkbox.h" #include "wx/listbox.h" + #include "wx/radiobox.h" #include "wx/radiobut.h" #include "wx/scrolbar.h" #include "wx/scrolwin.h" @@ -345,6 +346,17 @@ MyUnivFrame::MyUnivFrame(const wxString& title) wxALIGN_RIGHT); new wxRadioButton(this, -1, _T("Toggle me"), wxPoint(10, 600)); new wxRadioButton(this, -1, _T("And then me"), wxPoint(150, 600)); + + new wxRadioBox(this, -1, _T("&Horizontal"), + wxPoint(100, 470), wxDefaultSize, + WXSIZEOF(choices), choices, + WXSIZEOF(choices), + wxRA_SPECIFY_COLS); + new wxRadioBox(this, -1, _T("&Vertical"), + wxPoint(300, 550), wxDefaultSize, + WXSIZEOF(choices), choices, + WXSIZEOF(choices), + wxRA_SPECIFY_ROWS); } void MyUnivFrame::OnButton(wxCommandEvent& event) diff --git a/src/univ/files.lst b/src/univ/files.lst index 0adcd161c0..13a021e31a 100644 --- a/src/univ/files.lst +++ b/src/univ/files.lst @@ -6,6 +6,7 @@ UNIVOBJS = \ control.o \ inphand.o \ listbox.o \ + radiobox.o \ radiobut.o \ renderer.o \ scrolbar.o \ @@ -26,6 +27,7 @@ UNIVDEPS = \ control.d \ inphand.d \ listbox.d \ + radiobox.d \ radiobut.d \ renderer.d \ scrolbar.d \ diff --git a/src/univ/radiobox.cpp b/src/univ/radiobox.cpp new file mode 100644 index 0000000000..46fafc6a0c --- /dev/null +++ b/src/univ/radiobox.cpp @@ -0,0 +1,307 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: univ/radiobox.cpp +// Purpose: wxRadioBox implementation +// Author: Vadim Zeitlin +// Modified by: +// Created: 11.09.00 +// RCS-ID: $Id$ +// Copyright: (c) 2000 Vadim Zeitlin +// Licence: wxWindows license +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#ifdef __GNUG__ + #pragma implementation "univradiobox.h" +#endif + +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_RADIOBOX + +#ifndef WX_PRECOMP + #include "wx/dcclient.h" + #include "wx/radiobox.h" + #include "wx/radiobut.h" + #include "wx/validate.h" +#endif + +#include "wx/univ/theme.h" +#include "wx/univ/renderer.h" +#include "wx/univ/inphand.h" +#include "wx/univ/colschem.h" + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +static const int BUTTON_BORDER_X = 3; +static const int BUTTON_BORDER_Y = 3; + +static const int BOX_BORDER_X = 0; +static const int BOX_BORDER_Y = 0; + +// ============================================================================ +// implementation +// ============================================================================ + +IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl) + +// ---------------------------------------------------------------------------- +// wxRadioBox creation +// ---------------------------------------------------------------------------- + +void wxRadioBox::Init() +{ + m_selection = -1; + m_majorDim = 0; +} + +bool wxRadioBox::Create(wxWindow *parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos, + const wxSize& size, + int n, + const wxString *choices, + int majorDim, + long style, + const wxValidator& val, + const wxString& name) +{ + if ( !wxStaticBox::Create(parent, id, title, pos, size, style, name) ) + return FALSE; + +#if wxUSE_VALIDATORS + SetValidator(val); +#endif // wxUSE_VALIDATORS + + Append(n, choices); + + SetMajorDim(majorDim); + + if ( size == wxDefaultSize ) + { + SetClientSize(DoGetBestClientSize()); + } + + return TRUE; +} + +void wxRadioBox::SetMajorDim(int majorDim) +{ + m_majorDim = majorDim; + + int minorDim = (GetCount() + m_majorDim - 1) / m_majorDim; + + if ( GetWindowStyle() & wxRA_SPECIFY_COLS ) + { + m_numCols = majorDim; + m_numRows = minorDim; + } + else // wxRA_SPECIFY_ROWS + { + m_numCols = minorDim; + m_numRows = majorDim; + } +} + +void wxRadioBox::Append(int count, const wxString *choices) +{ + if ( !count ) + return; + + wxWindow *parent = GetParent(); + m_buttons.Alloc(count); + for ( int n = 0; n < count; n++ ) + { + // make the first button in the box the start of new group + m_buttons.Add(new wxRadioButton(parent, -1, choices[n], + wxDefaultPosition, wxDefaultSize, + n == 0 ? wxRB_GROUP : 0)); + } +} + +// ---------------------------------------------------------------------------- +// selection +// ---------------------------------------------------------------------------- + +void wxRadioBox::SetSelection(int n) +{ + wxCHECK_RET( IsValid(n), _T("invalid index in wxRadioBox::SetSelection") ); + + // this will unselect the previously selected button in our group + m_buttons[n]->SetValue(TRUE); +} + +int wxRadioBox::GetSelection() const +{ + return m_selection; +} + +// ---------------------------------------------------------------------------- +// methods forwarded to the buttons +// ---------------------------------------------------------------------------- + +wxString wxRadioBox::GetString(int n) const +{ + wxCHECK_MSG( IsValid(n), _T(""), + _T("invalid index in wxRadioBox::GetString") ); + + return m_buttons[n]->GetLabel(); +} + +void wxRadioBox::SetString(int n, const wxString& label) +{ + wxCHECK_RET( IsValid(n), _T("invalid index in wxRadioBox::SetString") ); + + m_buttons[n]->SetLabel(label); +} + +void wxRadioBox::Enable(int n, bool enable) +{ + wxCHECK_RET( IsValid(n), _T("invalid index in wxRadioBox::Enable") ); + + m_buttons[n]->Enable(enable); +} + +void wxRadioBox::Show(int n, bool show) +{ + wxCHECK_RET( IsValid(n), _T("invalid index in wxRadioBox::Show") ); + + m_buttons[n]->Show(show); +} + +// ---------------------------------------------------------------------------- +// methods forwarded to the static box +// ---------------------------------------------------------------------------- + +bool wxRadioBox::Enable(bool enable) +{ + return wxStaticBox::Enable(enable); +} + +bool wxRadioBox::Show(bool show) +{ + return wxStaticBox::Show(show); +} + +wxString wxRadioBox::GetLabel() const +{ + return wxStaticBox::GetLabel(); +} + +void wxRadioBox::SetLabel(const wxString& label) +{ + wxStaticBox::SetLabel(label); +} + +// ---------------------------------------------------------------------------- +// buttons positioning +// ---------------------------------------------------------------------------- + +wxSize wxRadioBox::GetMaxButtonSize() const +{ + int widthMax, heightMax, width, height; + widthMax = heightMax = 0; + + int count = GetCount(); + for ( int n = 0; n < count; n++ ) + { + m_buttons[n]->GetSize(&width, &height); + + if ( width > widthMax ) + widthMax = width; + if ( height > heightMax ) + heightMax = height; + } + + // add a intra button border + widthMax += BUTTON_BORDER_X; + heightMax += BUTTON_BORDER_Y; + + return wxSize(widthMax, heightMax); +} + +/* + Remember that wxRA_SPECIFY_COLS means that the buttons go from top to + bottom and from left to right while wxRA_SPECIFY_ROWS means that they are + laid out from left to right and then from top to bottom +*/ + +wxSize wxRadioBox::DoGetBestClientSize() const +{ + wxSize sizeBtn = GetMaxButtonSize(); + sizeBtn.x *= m_numCols; + sizeBtn.y *= m_numRows; + + // add a border around all buttons + sizeBtn.x += 2*BOX_BORDER_X; + sizeBtn.y += 2*BOX_BORDER_Y; + + return sizeBtn; +} + +void wxRadioBox::DoMoveWindow(int x0, int y0, int width, int height) +{ + wxStaticBox::DoMoveWindow(x0, y0, width, height); + + wxSize sizeBtn = GetMaxButtonSize(); + wxPoint ptOrigin = GetClientAreaOrigin(); + + x0 += ptOrigin.x + BOX_BORDER_X; + y0 += ptOrigin.y + BOX_BORDER_Y; + + int x = x0, + y = y0; + + int count = GetCount(); + for ( int n = 0; n < count; n++ ) + { + m_buttons[n]->SetSize(x, y, sizeBtn.x, sizeBtn.y); + + if ( GetWindowStyle() & wxRA_SPECIFY_COLS ) + { + // from to to bottom + if ( n % m_numRows ) + { + // continue in this column + y += sizeBtn.y; + } + else + { + // start a new column + x += sizeBtn.x; + y = y0; + } + } + else // wxRA_SPECIFY_ROWS: mirror the code above + { + // from left to right + if ( n % m_numCols ) + { + // continue in this row + x += sizeBtn.x; + } + else + { + // start a new row + y += sizeBtn.y; + x = x0; + } + } + } +} + +#endif // wxUSE_RADIOBOX + diff --git a/src/univ/statbox.cpp b/src/univ/statbox.cpp index 4e0ba723e8..f4bc0fbfda 100644 --- a/src/univ/statbox.cpp +++ b/src/univ/statbox.cpp @@ -65,10 +65,55 @@ bool wxStaticBox::Create(wxWindow *parent, void wxStaticBox::DoDraw(wxControlRenderer *renderer) { - // we never have a border, so don't call the base class version whcih draws + // we never have a border, so don't call the base class version which draws // it renderer->DrawFrame(); } +// ---------------------------------------------------------------------------- +// geometry +// ---------------------------------------------------------------------------- + +wxRect wxStaticBox::GetBorderGeometry() const +{ + // FIXME should use the renderer here + wxRect rect; + rect.width = + rect.x = GetCharWidth() / 2 + 1; + rect.y = GetCharHeight() + 1; + rect.height = rect.y / 2; + + return rect; +} + +wxPoint wxStaticBox::GetClientAreaOrigin() const +{ + wxPoint pt = wxControl::GetClientAreaOrigin(); + wxRect rect = GetBorderGeometry(); + pt.x += rect.x; + pt.y += rect.y; + + return pt; +} + +void wxStaticBox::DoSetClientSize(int width, int height) +{ + wxRect rect = GetBorderGeometry(); + + wxControl::DoSetClientSize(width + rect.x + rect.width, + height + rect.y + rect.height); +} + +void wxStaticBox::DoGetClientSize(int *width, int *height) const +{ + wxControl::DoGetClientSize(width, height); + + wxRect rect = GetBorderGeometry(); + if ( width ) + *width -= rect.x + rect.width; + if ( height ) + *height -= rect.y + rect.height; +} + #endif // wxUSE_STATBOX