From: Robert Roebling Date: Mon, 16 Jun 2008 17:50:40 +0000 (+0000) Subject: #9591: Item state (icons) for wxTreeCtrl on any platform X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/03966fcb3f5aeea7d8e6652d40e6804811a785d8 #9591: Item state (icons) for wxTreeCtrl on any platform git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@54267 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/generic/treectlg.h b/include/wx/generic/treectlg.h index b039951de3..356e48de48 100644 --- a/include/wx/generic/treectlg.h +++ b/include/wx/generic/treectlg.h @@ -284,6 +284,9 @@ protected: bool unselect_others = true, bool extended_select = false); + virtual int DoGetItemState(const wxTreeItemId& item) const; + virtual void DoSetItemState(const wxTreeItemId& item, int state); + virtual wxTreeItemId DoInsertItem(const wxTreeItemId& parent, size_t previous, const wxString& text, diff --git a/include/wx/msw/treectrl.h b/include/wx/msw/treectrl.h index 508bb18602..ad8d31210d 100644 --- a/include/wx/msw/treectrl.h +++ b/include/wx/msw/treectrl.h @@ -202,14 +202,6 @@ public: virtual bool SetBackgroundColour(const wxColour &colour); virtual bool SetForegroundColour(const wxColour &colour); - // get/set the check state for the item (only for wxTR_MULTIPLE) - bool IsItemChecked(const wxTreeItemId& item) const; - void SetItemCheck(const wxTreeItemId& item, bool check = true); - - // set/get the item state.image (state == -1 means cycle to the next one) - void SetState(const wxTreeItemId& node, int state); - int GetState(const wxTreeItemId& node); - // returns true if the platform should explicitly apply a theme border virtual bool CanApplyThemeBorder() const { return false; } @@ -223,6 +215,9 @@ protected: // end edit label void DoEndEditLabel(bool discardChanges = false); + virtual int DoGetItemState(const wxTreeItemId& item) const; + virtual void DoSetItemState(const wxTreeItemId& item, int state); + virtual wxTreeItemId DoInsertItem(const wxTreeItemId& parent, size_t pos, const wxString& text, diff --git a/include/wx/treebase.h b/include/wx/treebase.h index 937b658422..a3a75922a5 100644 --- a/include/wx/treebase.h +++ b/include/wx/treebase.h @@ -150,6 +150,11 @@ enum wxTreeItemIcon wxTreeItemIcon_Max }; +// special values for the 'state' parameter of wxTreeCtrl::SetItemState() +static const int wxTREE_ITEMSTATE_NONE = -1; // not state (no display state image) +static const int wxTREE_ITEMSTATE_NEXT = -2; // cycle to the next state +static const int wxTREE_ITEMSTATE_PREV = -3; // cycle to the previous state + // ---------------------------------------------------------------------------- // wxTreeCtrl flags // ---------------------------------------------------------------------------- diff --git a/include/wx/treectrl.h b/include/wx/treectrl.h index bdcc2b3201..77a10793c7 100644 --- a/include/wx/treectrl.h +++ b/include/wx/treectrl.h @@ -118,6 +118,12 @@ public: // get the item's font virtual wxFont GetItemFont(const wxTreeItemId& item) const = 0; + // get the items state + int GetItemState(const wxTreeItemId& item) const + { + return DoGetItemState(item); + } + // modifiers // --------- @@ -156,6 +162,9 @@ public: virtual void SetItemFont(const wxTreeItemId& item, const wxFont& font) = 0; + // set the items state (special state values: wxTREE_ITEMSTATE_NONE/NEXT/PREV) + void SetItemState(const wxTreeItemId& item, int state); + // item status inquiries // --------------------- @@ -387,6 +396,10 @@ public: protected: virtual wxSize DoGetBestSize() const; + // comon part of Get/SetItemState() + virtual int DoGetItemState(const wxTreeItemId& item) const = 0; + virtual void DoSetItemState(const wxTreeItemId& item, int state) = 0; + // common part of Append/Prepend/InsertItem() // // pos is the position at which to insert the item or (size_t)-1 to append diff --git a/interface/treectrl.h b/interface/treectrl.h index a1e582fcc4..f9e5ef8957 100644 --- a/interface/treectrl.h +++ b/interface/treectrl.h @@ -15,7 +15,7 @@ expanded to show further items. Items in a tree control are referenced by wxTreeItemId handles, which may be tested for validity by calling wxTreeItemId::IsOk(). - + A similar control with a fully native implemtation for GTK+ and OS X as well is wxDataViewTreeCtrl. @@ -62,7 +62,7 @@ @endStyleTable See also @ref overview_windowstyles. - + @b Win32 @b notes: wxTreeCtrl class uses the standard common treeview control under Win32 @@ -116,7 +116,7 @@ public: long style = wxTR_HAS_BUTTONS, const wxValidator& validator = wxDefaultValidator, const wxString& name = "treeCtrl"); - + /** Destructor, destroying the tree control. @@ -138,7 +138,7 @@ public: /** Appends an item to the end of the branch identified by @a parent, return a new item id. - + The @a image and @a selImage parameters are an index within the normal image list specifying the image to use for unselected and selected items, respectively. If @a image -1 and @a selImage is -1, the same @@ -424,6 +424,11 @@ public: */ int GetItemSelectedImage(const wxTreeItemId& item) const; + /** + Gets the specified item state. + */ + int GetItemState(const wxTreeItemId& item) const; + /** Returns the item label. */ @@ -524,7 +529,7 @@ public: wxTR_MULTIPLE style. Returns the number of selected items. - + @beginWxPythonOnly The wxPython version of this method accepts no parameters and returns a Python list of @ref wxTreeItemId "wxTreeItemId"s. @@ -557,7 +562,7 @@ public: item that is in a user-defined state. - @c wxTREE_HITTEST_TOLEFT: To the right of the client area. - @c wxTREE_HITTEST_TORIGHT: To the left of the client area. - + @beginWxPythonOnly In wxPython both the wxTreeItemId and the flags are returned as a tuple. @endWxPythonOnly @@ -775,6 +780,15 @@ public: */ void SetItemSelectedImage(const wxTreeItemId& item, int selImage); + /** + Sets the specified item state. The value of @a state may be: + - wxTREE_ITEMSTATE_NONE: to disable the item state (the state image will + be not displayed). + - wxTREE_ITEMSTATE_NEXT: to set the next item state. + - wxTREE_ITEMSTATE_PREV: to set the previous item statem. + */ + void SetItemState(const wxTreeItemId& item, int state); + /** Sets the item label. */ @@ -973,4 +987,3 @@ public: */ void SetToolTip(const wxString& tooltip); }; - diff --git a/src/common/treebase.cpp b/src/common/treebase.cpp index 4bd01ace53..548b01fb76 100644 --- a/src/common/treebase.cpp +++ b/src/common/treebase.cpp @@ -106,6 +106,25 @@ wxTreeCtrlBase::~wxTreeCtrlBase() delete m_imageListState; } +void wxTreeCtrlBase::SetItemState(const wxTreeItemId& item, int state) +{ + if ( state == wxTREE_ITEMSTATE_NEXT ) + { + state = GetItemState(item) + 1; + if ( m_imageListState && state >= m_imageListState->GetImageCount() ) + state = 0; + } + else if ( state == wxTREE_ITEMSTATE_PREV ) + { + state = GetItemState(item) - 1; + if ( state == -1 ) + state = m_imageListState ? m_imageListState->GetImageCount() - 1 : 0; + } + // else: wxTREE_ITEMSTATE_NONE depending on platform + + DoSetItemState(item, state); +} + static void wxGetBestTreeSize(const wxTreeCtrlBase* treeCtrl, wxTreeItemId id, wxSize& size) { @@ -240,4 +259,3 @@ bool wxTreeCtrlBase::IsEmpty() const } #endif // wxUSE_TREECTRL - diff --git a/src/generic/treectlg.cpp b/src/generic/treectlg.cpp index 2b9fd41854..16735f2591 100644 --- a/src/generic/treectlg.cpp +++ b/src/generic/treectlg.cpp @@ -61,6 +61,9 @@ static const int NO_IMAGE = -1; static const int PIXELS_PER_UNIT = 10; +// the margin between the item state image and the item normal image +static const int MARGIN_BETWEEN_STATE_AND_IMAGE = 2; + // the margin between the item image and the item text static const int MARGIN_BETWEEN_IMAGE_AND_TEXT = 4; @@ -153,14 +156,16 @@ public: int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const { return m_images[which]; } wxTreeItemData *GetData() const { return m_data; } + int GetState() const { return m_state; } // returns the current image for the item (depending on its // selected/expanded/whatever state) int GetCurrentImage() const; - void SetText( const wxString &text ); + void SetText( const wxString &text ) { m_text = text; } void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; } void SetData(wxTreeItemData *data) { m_data = data; } + void SetState(int state) { m_state = state; } void SetHasPlus(bool has = true) { m_hasPlus = has; } @@ -248,6 +253,8 @@ private: wxTreeItemData *m_data; // user-provided data + int m_state; // item state + wxArrayGenericTreeItems m_children; // list of children wxGenericTreeItem *m_parent; // parent of this item @@ -509,6 +516,7 @@ wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent, m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE; m_data = data; + m_state = wxTREE_ITEMSTATE_NONE; m_x = m_y = 0; m_isCollapsed = true; @@ -553,11 +561,6 @@ void wxGenericTreeItem::DeleteChildren(wxGenericTreeCtrl *tree) m_children.Empty(); } -void wxGenericTreeItem::SetText( const wxString &text ) -{ - m_text = text; -} - size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const { size_t count = m_children.GetCount(); @@ -634,10 +637,20 @@ wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point, // assuming every image (normal and selected) has the same size! if ( (GetImage() != NO_IMAGE) && theCtrl->m_imageListNormal ) - theCtrl->m_imageListNormal->GetSize(GetImage(), - image_w, image_h); + theCtrl->m_imageListNormal->GetSize(GetImage(), image_w, image_h); + + int state_w = -1; + int state_h; - if ((image_w != -1) && (point.x <= m_x + image_w + 1)) + if ( (GetState() != wxTREE_ITEMSTATE_NONE) && theCtrl->m_imageListState ) + theCtrl->m_imageListState->GetSize(GetState(), state_w, state_h); + + if ((state_w != -1) && (point.x <= m_x + state_w + 1)) + flags |= wxTREE_HITTEST_ONITEMSTATEICON; + else if ((image_w != -1) && + (point.x <= m_x + + (state_w != -1 ? state_w + MARGIN_BETWEEN_STATE_AND_IMAGE : 0) + + image_w + 1)) flags |= wxTREE_HITTEST_ONITEMICON; else flags |= wxTREE_HITTEST_ONITEMLABEL; @@ -939,6 +952,14 @@ wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const return ((wxGenericTreeItem*) item.m_pItem)->GetData(); } +int wxGenericTreeCtrl::DoGetItemState(const wxTreeItemId& item) const +{ + wxCHECK_MSG( item.IsOk(), wxTREE_ITEMSTATE_NONE, wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + return pItem->GetState(); +} + wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); @@ -998,6 +1019,15 @@ void wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *da ((wxGenericTreeItem*) item.m_pItem)->SetData(data); } +void wxGenericTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state) +{ + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + pItem->SetState(state); + RefreshLine(pItem); +} + void wxGenericTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) { wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); @@ -2162,8 +2192,6 @@ int wxGenericTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) { - // TODO implement "state" icon on items - wxTreeItemAttr *attr = item->GetAttributes(); if ( attr && attr->HasFont() ) dc.SetFont(attr->GetFont()); @@ -2188,6 +2216,24 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) } } + int state_h = 0, state_w = 0; + int state = item->GetState(); + if ( state != wxTREE_ITEMSTATE_NONE ) + { + if ( m_imageListState ) + { + m_imageListState->GetSize( state, state_w, state_h ); + if ( image != NO_IMAGE ) + state_w += MARGIN_BETWEEN_STATE_AND_IMAGE; + else + state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT; + } + else + { + state = wxTREE_ITEMSTATE_NONE; + } + } + int total_h = GetLineHeight(item); bool drawItemBackground = false; @@ -2243,13 +2289,14 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) } else { - if ( item->IsSelected() && image != NO_IMAGE ) + if ( item->IsSelected() && + (state != wxTREE_ITEMSTATE_NONE || image != NO_IMAGE) ) { - // If it's selected, and there's an image, then we should - // take care to leave the area under the image painted in the - // background colour. - wxRect rect( item->GetX() + image_w - 2, item->GetY()+offset, - item->GetWidth() - image_w + 2, total_h-offset ); + // If it's selected, and there's an state image or normal image, + // then we should take care to leave the area under the image + // painted in the background colour. + wxRect rect( item->GetX() + state_w + image_w - 2, item->GetY() + offset, + item->GetWidth() - state_w - image_w + 2, total_h - offset ); #if !defined(__WXGTK20__) && !defined(__WXMAC__) dc.DrawRectangle( rect ); #else @@ -2294,12 +2341,22 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) } } + if ( state != wxTREE_ITEMSTATE_NONE ) + { + dc.SetClippingRegion( item->GetX(), item->GetY(), state_w, total_h ); + m_imageListState->Draw( state, dc, + item->GetX(), + item->GetY() + ((total_h > state_h)?((total_h-state_h)/2):0), + wxIMAGELIST_DRAW_TRANSPARENT ); + dc.DestroyClippingRegion(); + } + if ( image != NO_IMAGE ) { - dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h ); + dc.SetClippingRegion( item->GetX() + state_w, item->GetY(), image_w, total_h ); m_imageListNormal->Draw( image, dc, item->GetX(), - item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0), + item->GetY() + ((total_h > image_h)?((total_h-image_h)/2):0), wxIMAGELIST_DRAW_TRANSPARENT ); dc.DestroyClippingRegion(); } @@ -2307,7 +2364,7 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT); int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0; dc.DrawText( item->GetText(), - (wxCoord)(image_w + item->GetX()), + (wxCoord)(state_w + image_w + item->GetX()), (wxCoord)(item->GetY() + extraH)); // restore normal font @@ -3269,6 +3326,12 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) } else if ( event.LeftUp() ) { + if (flags & wxTREE_HITTEST_ONITEMSTATEICON) + { + wxTreeEvent nevent(wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, this, item); + GetEventHandler()->ProcessEvent(nevent); + } + // this facilitates multiple-item drag-and-drop if ( /* item && */ HasFlag(wxTR_MULTIPLE)) @@ -3426,6 +3489,24 @@ void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) } } + int state_h = 0, state_w = 0; + int state = item->GetState(); + if ( state != wxTREE_ITEMSTATE_NONE ) + { + if ( m_imageListState ) + { + m_imageListState->GetSize( state, state_w, state_h ); + if ( image != NO_IMAGE ) + state_w += MARGIN_BETWEEN_STATE_AND_IMAGE; + else + state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT; + } + else + { + state = wxTREE_ITEMSTATE_NONE; + } + } + int total_h = (image_h > text_h) ? image_h : text_h; if (total_h < 30) @@ -3437,7 +3518,7 @@ void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) if (total_h>m_lineHeight) m_lineHeight=total_h; - item->SetWidth(image_w+text_w+2); + item->SetWidth(state_w + image_w + text_w + 2); } // ----------------------------------------------------------------------------- diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 3093583d52..14246836db 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -751,58 +751,6 @@ bool wxTreeCtrl::Create(wxWindow *parent, SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); #endif - - // VZ: this is some experimental code which may be used to get the - // TVS_CHECKBOXES style functionality for comctl32.dll < 4.71. - // AFAIK, the standard DLL does about the same thing anyhow. -#if 0 - if ( m_windowStyle & wxTR_MULTIPLE ) - { - wxBitmap bmp; - - // create the DC compatible with the current screen - HDC hdcMem = CreateCompatibleDC(NULL); - - // create a mono bitmap of the standard size - int x = ::GetSystemMetrics(SM_CXMENUCHECK); - int y = ::GetSystemMetrics(SM_CYMENUCHECK); - wxImageList imagelistCheckboxes(x, y, false, 2); - HBITMAP hbmpCheck = CreateBitmap(x, y, // bitmap size - 1, // # of color planes - 1, // # bits needed for one pixel - 0); // array containing colour data - SelectObject(hdcMem, hbmpCheck); - - // then draw a check mark into it - RECT rect = { 0, 0, x, y }; - if ( !::DrawFrameControl(hdcMem, &rect, - DFC_BUTTON, - DFCS_BUTTONCHECK | DFCS_CHECKED) ) - { - wxLogLastError(wxT("DrawFrameControl(check)")); - } - - bmp.SetHBITMAP((WXHBITMAP)hbmpCheck); - imagelistCheckboxes.Add(bmp); - - if ( !::DrawFrameControl(hdcMem, &rect, - DFC_BUTTON, - DFCS_BUTTONCHECK) ) - { - wxLogLastError(wxT("DrawFrameControl(uncheck)")); - } - - bmp.SetHBITMAP((WXHBITMAP)hbmpCheck); - imagelistCheckboxes.Add(bmp); - - // clean up - ::DeleteDC(hdcMem); - - // set the imagelist - SetStateImageList(&imagelistCheckboxes); - } -#endif // 0 - wxSetCCUnicodeFormat(GetHwnd()); return true; @@ -1444,33 +1392,6 @@ wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const // multiple selections emulation // ---------------------------------------------------------------------------- -bool wxTreeCtrl::IsItemChecked(const wxTreeItemId& item) const -{ - wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); - - // receive the desired information. - wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK); - DoGetItem(&tvItem); - - // state image indices are 1 based - return ((tvItem.state >> 12) - 1) == 1; -} - -void wxTreeCtrl::SetItemCheck(const wxTreeItemId& item, bool check) -{ - wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); - - // receive the desired information. - wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK); - - DoGetItem(&tvItem); - - // state images are one-based - tvItem.state = (check ? 2 : 1) << 12; - - DoSetItem(&tvItem); -} - size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const { TraverseSelections selector(this, selections); @@ -3105,40 +3026,29 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // why do they define INDEXTOSTATEIMAGEMASK but not the inverse? #define STATEIMAGEMASKTOINDEX(state) (((state) & TVIS_STATEIMAGEMASK) >> 12) -void wxTreeCtrl::SetState(const wxTreeItemId& node, int state) +int wxTreeCtrl::DoGetItemState(const wxTreeItemId& item) const { - TV_ITEM tvi; - tvi.hItem = (HTREEITEM)node.m_pItem; - tvi.mask = TVIF_STATE; - tvi.stateMask = TVIS_STATEIMAGEMASK; + wxCHECK_MSG( item.IsOk(), wxTREE_ITEMSTATE_NONE, wxT("invalid tree item") ); - // Select the specified state, or -1 == cycle to the next one. - if ( state == -1 ) - { - TreeView_GetItem(GetHwnd(), &tvi); - - state = STATEIMAGEMASKTOINDEX(tvi.state) + 1; - if ( state == m_imageListState->GetImageCount() ) - state = 1; - } - - wxCHECK_RET( state < m_imageListState->GetImageCount(), - _T("wxTreeCtrl::SetState(): item index out of bounds") ); - - tvi.state = INDEXTOSTATEIMAGEMASK(state); + // receive the desired information + wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK); + DoGetItem(&tvItem); - TreeView_SetItem(GetHwnd(), &tvi); + // state images are one-based + return STATEIMAGEMASKTOINDEX(tvItem.state) - 1; } -int wxTreeCtrl::GetState(const wxTreeItemId& node) +void wxTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state) { - TV_ITEM tvi; - tvi.hItem = (HTREEITEM)node.m_pItem; - tvi.mask = TVIF_STATE; - tvi.stateMask = TVIS_STATEIMAGEMASK; - TreeView_GetItem(GetHwnd(), &tvi); + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + + wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK); - return STATEIMAGEMASKTOINDEX(tvi.state); + // state images are one-based + // 0 if no state image display (wxTREE_ITEMSTATE_NONE = -1) + tvItem.state = INDEXTOSTATEIMAGEMASK(state + 1); + + DoSetItem(&tvItem); } #endif // wxUSE_TREECTRL