diff --git a/docs/changes.txt b/docs/changes.txt index 27e0213a4a..2db927d46d 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -136,6 +136,7 @@ All (GUI): - Fix calculating point position in wxDataViewCtrl::HitTest(). - Fix position of the rectangle returned by wxDataViewCtrl::GetItemRect(). - Add wxDataViewRenderer::GetAccessibleDescription(). +- Add wxDataViewCheckIconTextRenderer class. - Improve wxImage::Scale() handling of pixels with alpha channel (Tim Kosse). - Fix parsing of RGBA strings in wxColour (Laurent Poujoulat). - Refactor code in wxQuantize() for MSVC to avoid crash. diff --git a/include/wx/dvrenderers.h b/include/wx/dvrenderers.h index a5d6b4f339..85a3bd5465 100644 --- a/include/wx/dvrenderers.h +++ b/include/wx/dvrenderers.h @@ -77,6 +77,32 @@ private: DECLARE_VARIANT_OBJECT_EXPORTED(wxDataViewIconText, WXDLLIMPEXP_ADV) +// ---------------------------------------------------------------------------- +// wxDataViewCheckIconText: value class used by wxDataViewCheckIconTextRenderer +// ---------------------------------------------------------------------------- + +class wxDataViewCheckIconText : public wxDataViewIconText +{ +public: + wxDataViewCheckIconText(const wxString& text = wxString(), + const wxIcon& icon = wxNullIcon, + wxCheckBoxState checkedState = wxCHK_UNDETERMINED) + : wxDataViewIconText(text, icon), + m_checkedState(checkedState) + { + } + + wxCheckBoxState GetCheckedState() const { return m_checkedState; } + void SetCheckedState(wxCheckBoxState state) { m_checkedState = state; } + +private: + wxCheckBoxState m_checkedState; + + wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText); +}; + +DECLARE_VARIANT_OBJECT_EXPORTED(wxDataViewCheckIconText, WXDLLIMPEXP_ADV) + // ---------------------------------------------------------------------------- // wxDataViewRendererBase // ---------------------------------------------------------------------------- @@ -509,6 +535,61 @@ typedef wxDataViewTextRenderer wxDataViewDateRenderer; #endif // generic or GTK+ versions +// ---------------------------------------------------------------------------- +// wxDataViewCheckIconTextRenderer: 3-state checkbox + text + optional icon +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_ADV wxDataViewCheckIconTextRenderer + : public wxDataViewCustomRenderer +{ +public: + static wxString GetDefaultType() { return wxS("wxDataViewCheckIconText"); } + + explicit wxDataViewCheckIconTextRenderer + ( + wxDataViewCellMode mode = wxDATAVIEW_CELL_ACTIVATABLE, + int align = wxDVR_DEFAULT_ALIGNMENT + ); + + // This renderer can always display the 3rd ("indeterminate") checkbox + // state if the model contains cells with wxCHK_UNDETERMINED value, but it + // doesn't allow the user to set it by default. Call this method to allow + // this to happen. + void Allow3rdStateForUser(bool allow = true); + + virtual bool SetValue(const wxVariant& value) wxOVERRIDE; + virtual bool GetValue(wxVariant& value) const wxOVERRIDE; + +#if wxUSE_ACCESSIBILITY + virtual wxString GetAccessibleDescription() const wxOVERRIDE; +#endif // wxUSE_ACCESSIBILITY + + virtual wxSize GetSize() const wxOVERRIDE; + virtual bool Render(wxRect cell, wxDC* dc, int state) wxOVERRIDE; + virtual bool ActivateCell(const wxRect& cell, + wxDataViewModel *model, + const wxDataViewItem & item, + unsigned int col, + const wxMouseEvent *mouseEvent) wxOVERRIDE; + +private: + wxSize GetCheckSize() const; + + // Just some arbitrary constants defining margins, in pixels. + enum + { + MARGIN_CHECK_ICON = 3, + MARGIN_ICON_TEXT = 4 + }; + + wxDataViewCheckIconText m_value; + + bool m_allow3rdStateForUser; + + wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewCheckIconTextRenderer); +}; + + // this class is obsolete, its functionality was merged in // wxDataViewTextRenderer itself now, don't use it any more #define wxDataViewTextRendererAttr wxDataViewTextRenderer diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index 90805b3853..ba91146a2d 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -1797,6 +1797,7 @@ enum wxDataViewCellRenderState There is a number of ready-to-use renderers provided: - wxDataViewTextRenderer, - wxDataViewIconTextRenderer, + - wxDataViewCheckIconTextRenderer, - wxDataViewToggleRenderer, - wxDataViewProgressRenderer, - wxDataViewBitmapRenderer, @@ -2059,6 +2060,7 @@ public: wxDataViewIconText can be converted to and from a wxVariant using the left shift operator. + @see wxDataViewCheckIconTextRenderer @library{wxadv} @category{dvc} */ @@ -2081,6 +2083,58 @@ public: }; +/** + This renderer class shows a checkbox in addition to the icon and text shown + by the base class and also allows the user to toggle this checkbox. + + By default this class doesn't allow the user to put the checkbox into the + third, i.e. indeterminate, state, even though it can display the state if + the program returns the corresponding value from the associated model. Call + Allow3rdStateForUser() explicitly if the user should be able to select the + 3rd state interactively too. + + This class is used internally by wxTreeListCtrl, and can be seen in action + in the corresponding sample. + + @see wxDataViewIconTextRenderer, wxDataViewToggleRenderer + @library{wxadv} + @category{dvc} + @since 3.1.1 + */ +class wxDataViewCheckIconTextRenderer : public wxDataViewRenderer +{ +public: + static wxString GetDefaultType() { return wxS("wxDataViewCheckIconText"); } + + /** + Create a new renderer. + + By default the renderer is activatable, i.e. allows the user to toggle + the checkbox. + */ + explicit wxDataViewCheckIconTextRenderer + ( + wxDataViewCellMode mode = wxDATAVIEW_CELL_ACTIVATABLE, + int align = wxDVR_DEFAULT_ALIGNMENT + ); + + /** + Allow the user to interactively select the 3rd state for the items + rendered by this object. + + As described in the class overview, this renderer can always display + the 3rd ("indeterminate") checkbox state if the model contains cells + with wxCHK_UNDETERMINED value, but it doesn't allow the user to set it + by default. Call this method to allow this to happen. + + @param allow If @true, interactively clicking a checked cell switches + it to the indeterminate value and clicking it again unchecks it. + If @false, clicking a checked cell switches to the unchecked value, + skipping the indeterminate one. + */ + void Allow3rdStateForUser(bool allow = true); +}; + /** @class wxDataViewProgressRenderer @@ -2140,6 +2194,7 @@ public: This class is used by wxDataViewCtrl to render toggle controls. + @see wxDataViewCheckIconTextRenderer @library{wxadv} @category{dvc} */ diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index 011c02245f..d01037ad0f 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -1929,6 +1929,200 @@ wxSize wxDataViewDateRenderer::GetSize() const #endif // (defined(wxHAS_GENERIC_DATAVIEWCTRL) || defined(__WXGTK__)) && wxUSE_DATEPICKCTRL +// ---------------------------------------------------------------------------- +// wxDataViewCheckIconTextRenderer implementation +// ---------------------------------------------------------------------------- + +IMPLEMENT_VARIANT_OBJECT_EXPORTED(wxDataViewCheckIconText, WXDLLIMPEXP_ADV) + +wxIMPLEMENT_CLASS(wxDataViewCheckIconText, wxDataViewIconText); + +wxIMPLEMENT_CLASS(wxDataViewCheckIconTextRenderer, wxDataViewRenderer); + +wxDataViewCheckIconTextRenderer::wxDataViewCheckIconTextRenderer + ( + wxDataViewCellMode mode, + int align + ) + : wxDataViewCustomRenderer(GetDefaultType(), mode, align) +{ + m_allow3rdStateForUser = false; +} + +void wxDataViewCheckIconTextRenderer::Allow3rdStateForUser(bool allow) +{ + m_allow3rdStateForUser = allow; +} + +bool wxDataViewCheckIconTextRenderer::SetValue(const wxVariant& value) +{ + m_value << value; + return true; +} + +bool wxDataViewCheckIconTextRenderer::GetValue(wxVariant& value) const +{ + value << m_value; + return true; +} + +#if wxUSE_ACCESSIBILITY +wxString wxDataViewCheckIconTextRenderer::GetAccessibleDescription() const +{ + wxString text = m_value.GetText(); + if ( !text.empty() ) + { + text += wxS(" "); + } + + switch ( m_value.GetCheckedState() ) + { + case wxCHK_CHECKED: + /* TRANSLATORS: Checkbox state name */ + text += _("checked"); + break; + case wxCHK_UNCHECKED: + /* TRANSLATORS: Checkbox state name */ + text += _("unchecked"); + break; + case wxCHK_UNDETERMINED: + /* TRANSLATORS: Checkbox state name */ + text += _("undetermined"); + break; + } + + return text; +} +#endif // wxUSE_ACCESSIBILITY + +wxSize wxDataViewCheckIconTextRenderer::GetSize() const +{ + wxSize size = GetCheckSize(); + size.x += MARGIN_CHECK_ICON; + + if ( m_value.GetIcon().IsOk() ) + { + const wxSize sizeIcon = m_value.GetIcon().GetSize(); + if ( sizeIcon.y > size.y ) + size.y = sizeIcon.y; + + size.x += sizeIcon.x + MARGIN_ICON_TEXT; + } + + wxString text = m_value.GetText(); + if ( text.empty() ) + text = "Dummy"; + + const wxSize sizeText = GetTextExtent(text); + if ( sizeText.y > size.y ) + size.y = sizeText.y; + + size.x += sizeText.x; + + return size; +} + +bool wxDataViewCheckIconTextRenderer::Render(wxRect cell, wxDC* dc, int state) +{ + // Draw the checkbox first. + int renderFlags = 0; + switch ( m_value.GetCheckedState() ) + { + case wxCHK_UNCHECKED: + break; + + case wxCHK_CHECKED: + renderFlags |= wxCONTROL_CHECKED; + break; + + case wxCHK_UNDETERMINED: + renderFlags |= wxCONTROL_UNDETERMINED; + break; + } + + if ( state & wxDATAVIEW_CELL_PRELIT ) + renderFlags |= wxCONTROL_CURRENT; + + const wxSize sizeCheck = GetCheckSize(); + + wxRect rectCheck(cell.GetPosition(), sizeCheck); + rectCheck = rectCheck.CentreIn(cell, wxVERTICAL); + + wxRendererNative::Get().DrawCheckBox + ( + GetView(), *dc, rectCheck, renderFlags + ); + + // Then the icon, if any. + int xoffset = sizeCheck.x + MARGIN_CHECK_ICON; + + const wxIcon& icon = m_value.GetIcon(); + if ( icon.IsOk() ) + { + const wxSize sizeIcon = icon.GetSize(); + wxRect rectIcon(cell.GetPosition(), sizeIcon); + rectIcon.x += xoffset; + rectIcon = rectIcon.CentreIn(cell, wxVERTICAL); + + dc->DrawIcon(icon, rectIcon.GetPosition()); + + xoffset += sizeIcon.x + MARGIN_ICON_TEXT; + } + + // Finally the text. + RenderText(m_value.GetText(), xoffset, cell, dc, state); + + return true; +} + +bool +wxDataViewCheckIconTextRenderer::ActivateCell(const wxRect& WXUNUSED(cell), + wxDataViewModel *model, + const wxDataViewItem & item, + unsigned int col, + const wxMouseEvent *mouseEvent) +{ + if ( mouseEvent ) + { + if ( !wxRect(GetCheckSize()).Contains(mouseEvent->GetPosition()) ) + return false; + } + + // If the 3rd state is user-settable then the cycle is + // unchecked->checked->undetermined. + wxCheckBoxState checkedState = m_value.GetCheckedState(); + switch ( checkedState ) + { + case wxCHK_CHECKED: + checkedState = m_allow3rdStateForUser ? wxCHK_UNDETERMINED + : wxCHK_UNCHECKED; + break; + + case wxCHK_UNDETERMINED: + // Whether 3rd state is user-settable or not, the next state is + // unchecked. + checkedState = wxCHK_UNCHECKED; + break; + + case wxCHK_UNCHECKED: + checkedState = wxCHK_CHECKED; + break; + } + + m_value.SetCheckedState(checkedState); + + wxVariant value; + value << m_value; + + model->ChangeValue(value, item, col); + return true; +} + +wxSize wxDataViewCheckIconTextRenderer::GetCheckSize() const +{ + return wxRendererNative::Get().GetCheckBoxSize(GetView()); +} + //----------------------------------------------------------------------------- // wxDataViewListStore //----------------------------------------------------------------------------- diff --git a/src/generic/treelist.cpp b/src/generic/treelist.cpp index 665a979190..d367ebecce 100644 --- a/src/generic/treelist.cpp +++ b/src/generic/treelist.cpp @@ -364,7 +364,6 @@ public: void SetItemData(Node* item, wxClientData* data); void CheckItem(Node* item, wxCheckBoxState checkedState); - void ToggleItem(wxDataViewItem item); // Implement the base class pure virtual methods. @@ -403,214 +402,6 @@ private: bool m_isFlat; }; -// ============================================================================ -// wxDataViewCheckIconText[Renderer]: special renderer for our first column. -// ============================================================================ - -// Currently this class is private but it could be extracted and made part of -// public API later as could be used directly with wxDataViewCtrl as well. -namespace -{ - -const char* CHECK_ICON_TEXT_TYPE = "wxDataViewCheckIconText"; - -// The value used by wxDataViewCheckIconTextRenderer -class wxDataViewCheckIconText : public wxDataViewIconText -{ -public: - wxDataViewCheckIconText(const wxString& text = wxString(), - const wxIcon& icon = wxNullIcon, - wxCheckBoxState checkedState = wxCHK_UNDETERMINED) - : wxDataViewIconText(text, icon), - m_checkedState(checkedState) - { - } - - wxDataViewCheckIconText(const wxDataViewCheckIconText& other) - : wxDataViewIconText(other), - m_checkedState(other.m_checkedState) - { - } - - // There is no encapsulation anyhow, so just expose this field directly. - wxCheckBoxState m_checkedState; - - -private: - wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText); -}; - -wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCheckIconText, wxDataViewIconText); - -DECLARE_VARIANT_OBJECT(wxDataViewCheckIconText) -IMPLEMENT_VARIANT_OBJECT(wxDataViewCheckIconText) - - -class wxDataViewCheckIconTextRenderer : public wxDataViewCustomRenderer -{ -public: - wxDataViewCheckIconTextRenderer() - : wxDataViewCustomRenderer(CHECK_ICON_TEXT_TYPE, - wxDATAVIEW_CELL_ACTIVATABLE) - { - } - - virtual bool SetValue(const wxVariant& value) wxOVERRIDE - { - m_value << value; - return true; - } - - virtual bool GetValue(wxVariant& WXUNUSED(value)) const wxOVERRIDE - { - return false; - } - -#if wxUSE_ACCESSIBILITY - virtual wxString GetAccessibleDescription() const wxOVERRIDE - { - wxString text = m_value.GetText(); - if ( !text.empty() ) - { - text += wxS(" "); - } - - switch ( m_value.m_checkedState ) - { - case wxCHK_CHECKED: - /* TRANSLATORS: Checkbox state name */ - text += _("checked"); - break; - case wxCHK_UNCHECKED: - /* TRANSLATORS: Checkbox state name */ - text += _("unchecked"); - break; - case wxCHK_UNDETERMINED: - /* TRANSLATORS: Checkbox state name */ - text += _("undetermined"); - break; - } - - return text; - } -#endif // wxUSE_ACCESSIBILITY - - wxSize GetSize() const wxOVERRIDE - { - wxSize size = GetCheckSize(); - size.x += MARGIN_CHECK_ICON; - - if ( m_value.GetIcon().IsOk() ) - { - const wxSize sizeIcon = m_value.GetIcon().GetSize(); - if ( sizeIcon.y > size.y ) - size.y = sizeIcon.y; - - size.x += sizeIcon.x + MARGIN_ICON_TEXT; - } - - wxString text = m_value.GetText(); - if ( text.empty() ) - text = "Dummy"; - - const wxSize sizeText = GetTextExtent(text); - if ( sizeText.y > size.y ) - size.y = sizeText.y; - - size.x += sizeText.x; - - return size; - } - - virtual bool Render(wxRect cell, wxDC* dc, int state) wxOVERRIDE - { - // Draw the checkbox first. - int renderFlags = 0; - switch ( m_value.m_checkedState ) - { - case wxCHK_UNCHECKED: - break; - - case wxCHK_CHECKED: - renderFlags |= wxCONTROL_CHECKED; - break; - - case wxCHK_UNDETERMINED: - renderFlags |= wxCONTROL_UNDETERMINED; - break; - } - - if ( state & wxDATAVIEW_CELL_PRELIT ) - renderFlags |= wxCONTROL_CURRENT; - - const wxSize sizeCheck = GetCheckSize(); - - wxRect rectCheck(cell.GetPosition(), sizeCheck); - rectCheck = rectCheck.CentreIn(cell, wxVERTICAL); - - wxRendererNative::Get().DrawCheckBox - ( - GetView(), *dc, rectCheck, renderFlags - ); - - // Then the icon, if any. - int xoffset = sizeCheck.x + MARGIN_CHECK_ICON; - - const wxIcon& icon = m_value.GetIcon(); - if ( icon.IsOk() ) - { - const wxSize sizeIcon = icon.GetSize(); - wxRect rectIcon(cell.GetPosition(), sizeIcon); - rectIcon.x += xoffset; - rectIcon = rectIcon.CentreIn(cell, wxVERTICAL); - - dc->DrawIcon(icon, rectIcon.GetPosition()); - - xoffset += sizeIcon.x + MARGIN_ICON_TEXT; - } - - // Finally the text. - RenderText(m_value.GetText(), xoffset, cell, dc, state); - - return true; - } - - // Event handlers toggling the items checkbox if it was clicked. - virtual bool ActivateCell(const wxRect& WXUNUSED(cell), - wxDataViewModel *model, - const wxDataViewItem & item, - unsigned int WXUNUSED(col), - const wxMouseEvent *mouseEvent) wxOVERRIDE - { - if ( mouseEvent ) - { - if ( !wxRect(GetCheckSize()).Contains(mouseEvent->GetPosition()) ) - return false; - } - - static_cast(model)->ToggleItem(item); - return true; - } - -protected: - wxSize GetCheckSize() const - { - return wxRendererNative::Get().GetCheckBoxSize(GetView()); - } - -private: - // Just some arbitrary constants defining margins, in pixels. - enum - { - MARGIN_CHECK_ICON = 3, - MARGIN_ICON_TEXT = 4 - }; - - wxDataViewCheckIconText m_value; -}; - -} // anonymous namespace - // ============================================================================ // wxTreeListModel implementation // ============================================================================ @@ -837,40 +628,6 @@ void wxTreeListModel::CheckItem(Node* item, wxCheckBoxState checkedState) ItemChanged(ToDVI(item)); } -void wxTreeListModel::ToggleItem(wxDataViewItem dvi) -{ - Node* const item = FromDVI(dvi); - - wxCHECK_RET( item, "Invalid item" ); - - const wxCheckBoxState stateOld = item->m_checkedState; - - // If the 3rd state is user-settable then the cycle is - // unchecked->checked->undetermined. - switch ( stateOld ) - { - case wxCHK_CHECKED: - item->m_checkedState = m_treelist->HasFlag(wxTL_USER_3STATE) - ? wxCHK_UNDETERMINED - : wxCHK_UNCHECKED; - break; - - case wxCHK_UNDETERMINED: - // Whether 3rd state is user-settable or not, the next state is - // unchecked. - item->m_checkedState = wxCHK_UNCHECKED; - break; - - case wxCHK_UNCHECKED: - item->m_checkedState = wxCHK_CHECKED; - break; - } - - ItemChanged(ToDVI(item)); - - m_treelist->OnItemToggled(item, stateOld); -} - unsigned wxTreeListModel::GetColumnCount() const { return m_numColumns; @@ -881,8 +638,8 @@ wxString wxTreeListModel::GetColumnType(unsigned col) const if ( col == 0 ) { return m_treelist->HasFlag(wxTL_CHECKBOX) - ? wxS("wxDataViewCheckIconText") - : wxS("wxDataViewIconText"); + ? wxDataViewCheckIconTextRenderer::GetDefaultType() + : wxDataViewIconTextRenderer::GetDefaultType(); } else // All the other columns contain just text. { @@ -928,12 +685,26 @@ wxTreeListModel::GetValue(wxVariant& variant, } bool -wxTreeListModel::SetValue(const wxVariant& WXUNUSED(variant), - const wxDataViewItem& WXUNUSED(item), +wxTreeListModel::SetValue(const wxVariant& variant, + const wxDataViewItem& item, unsigned WXUNUSED(col)) { - // We are not editable currently. - return false; + Node* const node = FromDVI(item); + + wxCHECK_MSG( item, false, "Invalid item" ); + + const wxCheckBoxState stateOld = node->m_checkedState; + + // We don't allow changing anything but the checked state currently, yet we + // still get the full wxVariant containing the text and the icon as well, + // so we need to extract just the part we're interested in from it first. + wxDataViewCheckIconText value; + value << variant; + node->m_checkedState = value.GetCheckedState(); + + m_treelist->OnItemToggled(node, stateOld); + + return true; } wxDataViewItem wxTreeListModel::GetParent(const wxDataViewItem& item) const @@ -1122,7 +893,12 @@ wxTreeListCtrl::DoInsertColumn(const wxString& title, if ( HasFlag(wxTL_CHECKBOX) ) { // Use our custom renderer to show the checkbox. - renderer = new wxDataViewCheckIconTextRenderer; + wxDataViewCheckIconTextRenderer* const + rendererCheckIconText = new wxDataViewCheckIconTextRenderer; + if ( HasFlag(wxTL_USER_3STATE) ) + rendererCheckIconText->Allow3rdStateForUser(); + + renderer = rendererCheckIconText; } else // We still need a special renderer to show the icons. {