From 9dfbf520eb3f8ed03416c72222bb4ee74b8fcb0e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 30 Jul 1999 22:45:55 +0000 Subject: [PATCH 1/1] wxMSW::wxTreeCtrl has multiple selection too (somewhat documented) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@3220 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/latex/wx/treectrl.tex | 23 +++ include/wx/generic/treectrl.h | 16 +- include/wx/msw/treectrl.h | 19 +- samples/treectrl/treetest.cpp | 81 ++++++-- samples/treectrl/treetest.h | 19 +- src/generic/treectrl.cpp | 58 +++--- src/msw/treectrl.cpp | 366 ++++++++++++++++++++++++++-------- 7 files changed, 432 insertions(+), 150 deletions(-) diff --git a/docs/latex/wx/treectrl.tex b/docs/latex/wx/treectrl.tex index f150147bfd..6e5b52fb27 100644 --- a/docs/latex/wx/treectrl.tex +++ b/docs/latex/wx/treectrl.tex @@ -24,6 +24,9 @@ To intercept events from a tree control, use the event table macros described in left of parent items.} \twocolitem{\windowstyle{wxTR\_EDIT\_LABELS}}{Use this style if you wish the user to be able to edit labels in the tree control.} +\twocolitem{\windowstyle{wxTR\_MULTIPLE}}{Use this style to allow the user to +select more than one item in the control - by default, only one item may be +selected.} \end{twocollist} See also \helpref{window styles overview}{windowstyles}. @@ -385,6 +388,18 @@ Gets the selected item image. \constfunc{wxTreeItemId}{GetSelection}{\void} Returns the selection, or an invalid item if there is no selection. +This function only works with the controls without wxTR\_MULTIPLE style, use +\helpref{GetSelections}{wxtreectrlgetselections} for the controls which do have +this style. + +\membersection{wxTreeCtrl::GetSelections}\label{wxtreectrlgetselections} + +\constfunc{size\_t}{GetSelections}{\param{wxArrayTreeItemIds\& }{selection}} + +Fills the array of tree items passed in with the currently selected items. This +function can be called only if the control has the wxTR\_MULTIPLE style. + +Returns the number of selected items. \membersection{wxTreeCtrl::HitTest}\label{wxtreectrlhittest} @@ -572,6 +587,14 @@ Toggles the given item between collapsed and expanded states. Removes the selection from the currently selected item (if any). +\membersection{wxTreeCtrl::UnselectAll}\label{wxtreectrlunselectall} + +\func{void}{UnselectAll}{\void} + +This function either behaves the same as \helpref{Unselect}{wxtreectrlunselect} +if the control doesn't have wxTR\_MULTIPLE style, or removes the selection from +all items if it does have this style. + \section{\class{wxTreeItemData}}\label{wxtreeitemdata} wxTreeItemData is some (arbitrary) user class associated with some item. The diff --git a/include/wx/generic/treectrl.h b/include/wx/generic/treectrl.h index 7720cddc91..43718e0469 100644 --- a/include/wx/generic/treectrl.h +++ b/include/wx/generic/treectrl.h @@ -32,7 +32,7 @@ WXDLLEXPORT_DATA(extern const char*) wxTreeCtrlNameStr; #include "wx/dynarray.h" #include "wx/timer.h" -//those defines should only be done in generic/treectrl.h, +//those defines should only be done in generic/treectrl.h, //because wxMSW doesn't allow mutiple selection #ifndef wxTR_SINGLE @@ -177,7 +177,7 @@ class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl public: wxTreeTextCtrl(void) {}; - wxTreeTextCtrl( wxWindow *parent, const wxWindowID id, + wxTreeTextCtrl( wxWindow *parent, const wxWindowID id, bool *accept, wxString *res, wxTreeCtrl *owner, const wxString &value = "", const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, @@ -185,7 +185,7 @@ class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl const wxString &name = "wxTreeTextCtrlText" ); void OnChar( wxKeyEvent &event ); void OnKillFocus( wxFocusEvent &event ); - + DECLARE_EVENT_TABLE() }; @@ -231,10 +231,10 @@ public: unsigned int GetIndent() const { return m_indent; } void SetIndent(unsigned int indent); - // spacing is the number of pixels between the start and the Text + // spacing is the number of pixels between the start and the Text unsigned int GetSpacing() const { return m_spacing; } void SetSpacing(unsigned int spacing); - + // image list: these functions allow to associate an image list with // the control and retrieve it. Note that the control does _not_ delete // the associated image list when it's deleted in order to allow image @@ -420,7 +420,7 @@ public: // been before. void EditLabel( const wxTreeItemId& item ) { Edit( item ); } void Edit( const wxTreeItemId& item ); - + // sorting // this function is called to compare 2 items and should return -1, 0 // or +1 if the first item is less than, equal to or greater than the @@ -447,7 +447,7 @@ public: // Draw Special Information void DrawBorder(wxTreeItemId& item); void DrawLine(wxTreeItemId& item, bool below); - + protected: friend class wxGenericTreeItem; friend class wxTreeRenameTimer; @@ -491,7 +491,7 @@ protected: void RefreshSubtree( wxGenericTreeItem *item ); void RefreshLine( wxGenericTreeItem *item ); - + void OnRenameTimer(); void OnRenameAccept(); diff --git a/include/wx/msw/treectrl.h b/include/wx/msw/treectrl.h index 213b4934ff..66871a4891 100644 --- a/include/wx/msw/treectrl.h +++ b/include/wx/msw/treectrl.h @@ -121,6 +121,8 @@ protected: long m_itemId; }; +WX_DEFINE_ARRAY(wxTreeItemId, wxArrayTreeItemIds); + // ---------------------------------------------------------------------------- // wxTreeItemData is some (arbitrary) user class associated with some item. The // main advantage of having this class (compared to old untyped interface) is @@ -275,7 +277,8 @@ public: // if 'recursively' is FALSE, only immediate children count, otherwise // the returned number is the number of all items in this branch - size_t GetChildrenCount(const wxTreeItemId& item, bool recursively = TRUE); + size_t GetChildrenCount(const wxTreeItemId& item, + bool recursively = TRUE) const; // navigation // ---------- @@ -288,6 +291,12 @@ public: // get the item currently selected (may return NULL if no selection) wxTreeItemId GetSelection() const; + // get the items currently selected, return the number of such item + // + // NB: this operation is expensive and can take a long time for a + // control with a lot of items (~ O(number of items)). + size_t GetSelections(wxArrayTreeItemIds& selections) const; + // get the parent of this item (may return NULL if root) wxTreeItemId GetParent(const wxTreeItemId& item) const; @@ -365,6 +374,8 @@ public: // remove the selection from currently selected item (if any) void Unselect(); + // unselect all items (only makes sense for multiple selection control) + void UnselectAll(); // select this item void SelectItem(const wxTreeItemId& item); // make sure this item is visible (expanding the parent item and/or @@ -445,6 +456,10 @@ public: virtual bool MSWCommand(WXUINT param, WXWORD id); virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result); + // 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); + protected: // SetImageList helper void SetAnyImageList(wxImageList *imageList, int which); @@ -469,6 +484,8 @@ private: int image, int selectedImage, wxTreeItemData *data); + void DoSetItemImages(const wxTreeItemId& item, int image, int imageSel); + void DeleteTextCtrl(); DECLARE_DYNAMIC_CLASS(wxTreeCtrl) diff --git a/samples/treectrl/treetest.cpp b/samples/treectrl/treetest.cpp index c626ea433f..6b585f079c 100644 --- a/samples/treectrl/treetest.cpp +++ b/samples/treectrl/treetest.cpp @@ -33,12 +33,13 @@ #include "math.h" -#include "treetest.h" - #ifdef __WXMSW__ - #define NO_ADVANCED_FEATURES + //#define NO_MULTIPLE_SELECTION + #define NO_VARIABLE_HEIGHT #endif +#include "treetest.h" + // under Windows the icons are in the .rc file #ifndef __WXMSW__ #include "icon1.xpm" @@ -59,7 +60,11 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(TreeTest_Quit, MyFrame::OnQuit) EVT_MENU(TreeTest_About, MyFrame::OnAbout) EVT_MENU(TreeTest_Dump, MyFrame::OnDump) - EVT_MENU(TreeTest_Dump_Selected, MyFrame::OnDumpSelected) +#ifndef NO_MULTIPLE_SELECTION + EVT_MENU(TreeTest_DumpSelected, MyFrame::OnDumpSelected) + EVT_MENU(TreeTest_Select, MyFrame::OnSelect) + EVT_MENU(TreeTest_Unselect, MyFrame::OnUnselect) +#endif // NO_MULTIPLE_SELECTION EVT_MENU(TreeTest_Rename, MyFrame::OnRename) EVT_MENU(TreeTest_Sort, MyFrame::OnSort) EVT_MENU(TreeTest_SortRev, MyFrame::OnSortRev) @@ -76,6 +81,7 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(TreeTest_DecIndent, MyFrame::OnDecIndent) EVT_MENU(TreeTest_IncSpacing, MyFrame::OnIncSpacing) EVT_MENU(TreeTest_DecSpacing, MyFrame::OnDecSpacing) + EVT_MENU(TreeTest_ToggleIcon, MyFrame::OnToggleIcon) END_EVENT_TABLE() BEGIN_EVENT_TABLE(MyTreeCtrl, wxTreeCtrl) @@ -153,14 +159,20 @@ MyFrame::MyFrame(const wxString& title, int x, int y, int w, int h) tree_menu->Append(TreeTest_DecSpacing, "Reduce spacing by 5 points\tCtrl-R"); item_menu->Append(TreeTest_Dump, "&Dump item children"); -#ifndef NO_ADVANCED_FEATURES - item_menu->Append(TreeTest_Dump_Selected, "Dump selected items\tAlt-S"); -#endif item_menu->Append(TreeTest_Rename, "&Rename item..."); item_menu->AppendSeparator(); item_menu->Append(TreeTest_Bold, "Make item &bold"); item_menu->Append(TreeTest_UnBold, "Make item ¬ bold"); + item_menu->AppendSeparator(); + item_menu->Append(TreeTest_ToggleIcon, "Toggle the items &icon"); + +#ifndef NO_MULTIPLE_SELECTION + item_menu->AppendSeparator(); + item_menu->Append(TreeTest_DumpSelected, "Dump selected items\tAlt-D"); + item_menu->Append(TreeTest_Select, "Select current item\tAlt-S"); + item_menu->Append(TreeTest_Unselect, "Unselect everything\tAlt-U"); +#endif wxMenuBar *menu_bar = new wxMenuBar; menu_bar->Append(file_menu, "&File"); @@ -172,10 +184,8 @@ MyFrame::MyFrame(const wxString& title, int x, int y, int w, int h) wxDefaultPosition, wxDefaultSize, wxTR_HAS_BUTTONS | wxTR_EDIT_LABELS | -#ifndef NO_ADVANCED_FEATURES wxTR_MULTIPLE | wxTR_HAS_VARIABLE_ROW_HEIGHT | -#endif wxSUNKEN_BORDER); wxTextCtrl *textCtrl = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxDefaultSize, @@ -268,20 +278,33 @@ void MyFrame::OnDump(wxCommandEvent& WXUNUSED(event)) m_treeCtrl->GetItemsRecursively(root, -1); } +#ifndef NO_MULTIPLE_SELECTION + void MyFrame::OnDumpSelected(wxCommandEvent& WXUNUSED(event)) { -#ifndef NO_ADVANCED_FEATURES - wxArrayTreeItemIds array; + wxArrayTreeItemIds array; - m_treeCtrl->GetSelections(array); - size_t nos=array.Count(); - wxLogMessage(wxString("items selected : ")<< (int)nos); + size_t count = m_treeCtrl->GetSelections(array); + wxLogMessage(_T("%u items selected"), count); - for (size_t n=0; nGetItemText(array.Item(n))); -#endif + for ( size_t n = 0; n < count; n++ ) + { + wxLogMessage("\t%s", m_treeCtrl->GetItemText(array.Item(n)).c_str()); + } +} + +void MyFrame::OnSelect(wxCommandEvent& event) +{ + m_treeCtrl->SelectItem(m_treeCtrl->GetSelection()); +} + +void MyFrame::OnUnselect(wxCommandEvent& event) +{ + m_treeCtrl->UnselectAll(); } +#endif // NO_MULTIPLE_SELECTION + void MyFrame::DoSetBold(bool bold) { wxTreeItemId item = m_treeCtrl->GetSelection(); @@ -370,6 +393,15 @@ void MyFrame::OnDecSpacing(wxCommandEvent& WXUNUSED(event)) m_treeCtrl->SetSpacing( indent-5 ); } +void MyFrame::OnToggleIcon(wxCommandEvent& WXUNUSED(event)) +{ + wxTreeItemId item = m_treeCtrl->GetSelection(); + + CHECK_ITEM( item ); + + m_treeCtrl->DoToggleIcon(item); +} + // MyTreeCtrl implementation IMPLEMENT_DYNAMIC_CLASS(MyTreeCtrl, wxTreeCtrl) @@ -379,7 +411,7 @@ MyTreeCtrl::MyTreeCtrl(wxWindow *parent, const wxWindowID id, : wxTreeCtrl(parent, id, pos, size, style) { #if (USE_TR_HAS_VARIABLE_ROW_HIGHT && wxUSE_LIBJPEG) - wxImage::AddHandler(new wxJPEGHandler); + wxImage::AddHandler(new wxJPEGHandler); wxImage image; image.LoadFile(wxString("horse.jpg"), wxBITMAP_TYPE_JPEG ); @@ -450,8 +482,7 @@ void MyTreeCtrl::AddItemsRecursively(const wxTreeItemId& idParent, else str.Printf("%s child %d", "Folder", n + 1); -// int image = depth == 1 ? TreeCtrlIcon_File : TreeCtrlIcon_Folder; - int image = depth == 1 ? -1 : TreeCtrlIcon_Folder; + int image = depth == 1 ? TreeCtrlIcon_File : TreeCtrlIcon_Folder; wxTreeItemId id = AppendItem(idParent, str, image, image, new MyTreeItemData(str)); @@ -489,7 +520,7 @@ void MyTreeCtrl::GetItemsRecursively(const wxTreeItemId& idParent, long cookie) if(id <= 0) return; - wxString text=GetItemText(id); + wxString text = GetItemText(id); wxLogMessage(text); if (ItemHasChildren(id)) @@ -498,6 +529,14 @@ void MyTreeCtrl::GetItemsRecursively(const wxTreeItemId& idParent, long cookie) GetItemsRecursively(idParent, cookie); } +void MyTreeCtrl::DoToggleIcon(const wxTreeItemId& item) +{ + int image = GetItemImage(item) == TreeCtrlIcon_Folder ? TreeCtrlIcon_File + : TreeCtrlIcon_Folder; + + SetItemImage(item, image); +} + // avoid repetition #define TREE_EVENT_HANDLER(name) \ diff --git a/samples/treectrl/treetest.h b/samples/treectrl/treetest.h index 12c6b2245b..dec33dc30a 100644 --- a/samples/treectrl/treetest.h +++ b/samples/treectrl/treetest.h @@ -65,9 +65,11 @@ public: void AddTestItemsToTree(size_t numChildren, size_t depth); void DoSortChildren(const wxTreeItemId& item, bool reverse = FALSE) - { m_reverseSort = reverse; wxTreeCtrl::SortChildren(item); } + { m_reverseSort = reverse; wxTreeCtrl::SortChildren(item); } void DoEnsureVisible() { EnsureVisible(m_lastItem); } + void DoToggleIcon(const wxTreeItemId& item); + protected: virtual int OnCompareItems(const wxTreeItemId& i1, const wxTreeItemId& i2); @@ -109,7 +111,11 @@ public: void OnAbout(wxCommandEvent& event); void OnDump(wxCommandEvent& event); +#ifndef NO_MULTIPLE_SELECTION void OnDumpSelected(wxCommandEvent& event); + void OnSelect(wxCommandEvent& event); + void OnUnselect(wxCommandEvent& event); +#endif // NO_MULTIPLE_SELECTION void OnDelete(wxCommandEvent& event); void OnDeleteChildren(wxCommandEvent& event); void OnDeleteAll(wxCommandEvent& event); @@ -126,13 +132,15 @@ public: void OnSortRev(wxCommandEvent& event) { DoSort(TRUE); } void OnAddItem(wxCommandEvent& event); - + void OnIncIndent(wxCommandEvent& event); void OnDecIndent(wxCommandEvent& event); void OnIncSpacing(wxCommandEvent& event); void OnDecSpacing(wxCommandEvent& event); + void OnToggleIcon(wxCommandEvent& event); + private: void DoSort(bool reverse = FALSE); @@ -149,7 +157,7 @@ enum TreeTest_Quit, TreeTest_About, TreeTest_Dump, - TreeTest_Dump_Selected, + TreeTest_DumpSelected, TreeTest_Sort, TreeTest_SortRev, TreeTest_Bold, @@ -166,5 +174,8 @@ enum TreeTest_DecIndent, TreeTest_IncSpacing, TreeTest_DecSpacing, - TreeTest_Ctrl = 100 + TreeTest_ToggleIcon, + TreeTest_Select, + TreeTest_Unselect, + TreeTest_Ctrl = 1000 }; diff --git a/src/generic/treectrl.cpp b/src/generic/treectrl.cpp index cffc64230c..1e6e008231 100644 --- a/src/generic/treectrl.cpp +++ b/src/generic/treectrl.cpp @@ -203,14 +203,14 @@ void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) { (*m_accept) = TRUE; (*m_res) = GetValue(); - m_owner->SetFocus(); + m_owner->SetFocus(); return; } if (event.m_keyCode == WXK_ESCAPE) { (*m_accept) = FALSE; (*m_res) = ""; - m_owner->SetFocus(); + m_owner->SetFocus(); return; } event.Skip(); @@ -221,7 +221,7 @@ void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) if (wxPendingDelete.Member(this)) return; wxPendingDelete.Append(this); - + if ((*m_accept) && ((*m_res) != m_startValue)) m_owner->OnRenameAccept(); } @@ -232,7 +232,7 @@ void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) // ----------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxNotifyEvent) - + wxTreeEvent::wxTreeEvent( wxEventType commandType, int id ) : wxNotifyEvent( commandType, id ) { @@ -463,7 +463,7 @@ void wxTreeCtrl::Init() m_imageListState = (wxImageList *) NULL; m_dragCount = 0; - + m_renameTimer = new wxTreeRenameTimer( this ); } @@ -493,7 +493,7 @@ wxTreeCtrl::~wxTreeCtrl() wxDELETE( m_hilightBrush ); DeleteAllItems(); - + delete m_renameTimer; } @@ -1143,16 +1143,18 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId, GetEventHandler()->ProcessEvent( event ); } -void wxTreeCtrl::FillArray(wxGenericTreeItem *item, wxArrayTreeItemIds &array) const +void wxTreeCtrl::FillArray(wxGenericTreeItem *item, + wxArrayTreeItemIds &array) const { - if (item->HasHilight()) array.Add(wxTreeItemId(item)); + if ( item->HasHilight() ) + array.Add(wxTreeItemId(item)); - if (item->HasChildren()) + if ( item->HasChildren() ) { - wxArrayGenericTreeItems& children = item->GetChildren(); - size_t count = children.Count(); - for ( size_t n = 0; n < count; ++n ) - FillArray(children[n],array); + wxArrayGenericTreeItems& children = item->GetChildren(); + size_t count = children.GetCount(); + for ( size_t n = 0; n < count; ++n ) + FillArray(children[n],array); } } @@ -1445,12 +1447,12 @@ void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int & dc.SetPen( *wxGREY_PEN ); dc.SetBrush( *wxWHITE_BRUSH ); dc.DrawRectangle( horizX+(m_indent-5), y-4, 11, 9 ); - + dc.SetPen( *wxBLACK_PEN ); dc.DrawLine( horizX+(m_indent-2), y, horizX+(m_indent+3), y ); if (!item->IsExpanded()) dc.DrawLine( horizX+m_indent, y-2, horizX+m_indent, y+3 ); - + dc.SetPen( m_dottedPen ); } @@ -1793,20 +1795,20 @@ void wxTreeCtrl::Edit( const wxTreeItemId& item ) if (!item.IsOk()) return; m_currentEdit = item.m_pItem; - + wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() ); te.m_item = m_currentEdit; te.SetEventObject( this ); GetEventHandler()->ProcessEvent( te ); if (!te.IsAllowed()) return; - + wxString s = m_currentEdit->GetText(); int x = m_currentEdit->GetX(); int y = m_currentEdit->GetY(); int w = m_currentEdit->GetWidth(); int h = m_currentEdit->GetHeight(); - + int image_h = 0; int image_w = 0; if ((m_currentEdit->IsExpanded()) && (m_currentEdit->GetSelectedImage() != -1)) @@ -1844,12 +1846,12 @@ void wxTreeCtrl::OnRenameAccept() le.SetEventObject( this ); le.m_label = m_renameRes; GetEventHandler()->ProcessEvent( le ); - + if (!le.IsAllowed()) return; - + SetItemText( m_currentEdit, m_renameRes ); } - + void wxTreeCtrl::OnMouse( wxMouseEvent &event ) { if (!event.LeftIsDown()) m_dragCount = 0; @@ -1887,14 +1889,14 @@ void wxTreeCtrl::OnMouse( wxMouseEvent &event ) return; } - if (event.LeftUp() && (item == m_current) && - (flags & wxTREE_HITTEST_ONITEMLABEL) && - HasFlag(wxTR_EDIT_LABELS) ) + if (event.LeftUp() && (item == m_current) && + (flags & wxTREE_HITTEST_ONITEMLABEL) && + HasFlag(wxTR_EDIT_LABELS) ) { m_renameTimer->Start( 100, TRUE ); return; } - + bool is_multiple=(GetWindowStyleFlag() & wxTR_MULTIPLE); bool extended_select=(event.ShiftDown() && is_multiple); bool unselect_others=!(extended_select || (event.ControlDown() && is_multiple)); @@ -1937,7 +1939,7 @@ void wxTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) { long text_w = 0; long text_h = 0; - + wxFont fontOld; wxFont fontNew; if (item->IsBold()) @@ -1958,14 +1960,14 @@ void wxTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) wxFAIL_MSG(_T("wxDC::GetFont() failed!")); } } - + dc.GetTextExtent( item->GetText(), &text_w, &text_h ); text_h+=2; // restore normal font for bold items if (fontOld.Ok()) dc.SetFont( fontOld); - + int image_h = 0; int image_w = 0; if ((item->IsExpanded()) && (item->GetSelectedImage() != -1)) diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index b7a0473428..c3eb0e1d4f 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -30,10 +30,6 @@ #include "wx/window.h" #include "wx/msw/private.h" -#ifndef WX_PRECOMP - #include "wx/settings.h" -#endif - // Mingw32 is a bit mental even though this is done in winundef #ifdef GetFirstChild #undef GetFirstChild @@ -70,15 +66,45 @@ // a convenient wrapper around TV_ITEM struct which adds a ctor struct wxTreeViewItem : public TV_ITEM { - wxTreeViewItem(const wxTreeItemId& item, - UINT mask_, UINT stateMask_ = 0) + wxTreeViewItem(const wxTreeItemId& item, // the item handle + UINT mask_, // fields which are valid + UINT stateMask_ = 0) // for TVIF_STATE only { - mask = mask_; + // hItem member is always valid + mask = mask_ | TVIF_HANDLE; stateMask = stateMask_; hItem = (HTREEITEM) (WXHTREEITEM) item; } }; +// a class which encapsulates the tree traversal logic: it vists all (unless +// OnVisit() returns FALSE) items under the given one +class wxTreeTraversal +{ +public: + wxTreeTraversal(const wxTreeCtrl *tree) + { + m_tree = tree; + } + + // do traverse the tree: visit all items (recursively by default) under the + // given one; return TRUE if all items were traversed or FALSE if the + // traversal was aborted because OnVisit returned FALSE + bool DoTraverse(const wxTreeItemId& root, bool recursively = TRUE); + + // override this function to do whatever is needed for each item, return + // FALSE to stop traversing + virtual bool OnVisit(const wxTreeItemId& item) = 0; + +protected: + const wxTreeCtrl *GetTree() const { return m_tree; } + +private: + bool Traverse(const wxTreeItemId& root, bool recursively); + + const wxTreeCtrl *m_tree; +}; + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- @@ -102,6 +128,37 @@ static const wxEventType g_events[2][2] = // implementation // ============================================================================ +// ---------------------------------------------------------------------------- +// tree traversal +// ---------------------------------------------------------------------------- + +bool wxTreeTraversal::DoTraverse(const wxTreeItemId& root, bool recursively) +{ + if ( !OnVisit(root) ) + return FALSE; + + return Traverse(root, recursively); +} + +bool wxTreeTraversal::Traverse(const wxTreeItemId& root, bool recursively) +{ + long cookie; + wxTreeItemId child = m_tree->GetFirstChild(root, cookie); + while ( child.IsOk() ) + { + // depth first traversal + if ( recursively && !Traverse(child, TRUE) ) + return FALSE; + + if ( !OnVisit(child) ) + return FALSE; + + child = m_tree->GetNextChild(root, cookie); + } + + return TRUE; +} + // ---------------------------------------------------------------------------- // construction and destruction // ---------------------------------------------------------------------------- @@ -113,37 +170,22 @@ void wxTreeCtrl::Init() m_textCtrl = NULL; } -bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id, - const wxPoint& pos, const wxSize& size, - long style, const wxValidator& validator, +bool wxTreeCtrl::Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, const wxString& name) { Init(); - wxSystemSettings settings; - - SetName(name); - SetValidator(validator); - - m_windowStyle = style; - - SetParent(parent); - - m_windowId = (id == -1) ? NewControlId() : id; + if ( !CreateControl(parent, id, pos, size, style, validator, name) ) + return FALSE; DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP | TVS_HASLINES | TVS_SHOWSELALWAYS; - bool want3D; - WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D) ; - - // Even with extended styles, need to combine with WS_BORDER - // for them to look right. - if ( want3D || wxStyleHasBorder(m_windowStyle) ) - { - wstyle |= WS_BORDER; - } - if ( m_windowStyle & wxTR_HAS_BUTTONS ) wstyle |= TVS_HASBUTTONS; @@ -153,26 +195,67 @@ bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id, if ( m_windowStyle & wxTR_LINES_AT_ROOT ) wstyle |= TVS_LINESATROOT; + // we emulate the multiple selection tree controls by using checkboxes: set + // up the image list we need for this if we do have multiple selections + if ( m_windowStyle & wxTR_MULTIPLE ) + wstyle |= TVS_CHECKBOXES; + // Create the tree control. - m_hWnd = (WXHWND)::CreateWindowEx - ( - exStyle, - WC_TREEVIEW, - _T(""), - wstyle, - pos.x, pos.y, size.x, size.y, - (HWND)parent->GetHWND(), - (HMENU)m_windowId, - wxGetInstance(), - NULL - ); - - wxCHECK_MSG( m_hWnd, FALSE, _T("Failed to create tree ctrl") ); - - if ( parent ) - parent->AddChild(this); - - SubclassWin(m_hWnd); + if ( !MSWCreateControl(WC_TREEVIEW, wstyle) ) + return FALSE; + + // 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(_T("DrawFrameControl(check)")); + } + + bmp.SetHBITMAP((WXHBITMAP)hbmpCheck); + imagelistCheckboxes.Add(bmp); + + if ( !::DrawFrameControl(hdcMem, &rect, + DFC_BUTTON, + DFCS_BUTTONCHECK) ) + { + wxLogLastError(_T("DrawFrameControl(uncheck)")); + } + + bmp.SetHBITMAP((WXHBITMAP)hbmpCheck); + imagelistCheckboxes.Add(bmp); + + // clean up + ::DeleteDC(hdcMem); + + // set the imagelist + SetStateImageList(&imagelistCheckboxes); + } +#endif // 0 + + SetSize(pos.x, pos.y, size.x, size.y); return TRUE; } @@ -254,29 +337,36 @@ void wxTreeCtrl::SetStateImageList(wxImageList *imageList) SetAnyImageList(m_imageListState = imageList, TVSIL_STATE); } -size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively) +size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, + bool recursively) const { - long cookie; + class TraverseCounter : public wxTreeTraversal + { + public: + TraverseCounter(const wxTreeCtrl *tree, + const wxTreeItemId& root, + bool recursively) + : wxTreeTraversal(tree) + { + m_count = 0; - size_t result = 0; + DoTraverse(root, recursively); + } - wxArrayLong children; - wxTreeItemId child = GetFirstChild(item, cookie); - while ( child.IsOk() ) - { - if ( recursively ) + virtual bool OnVisit(const wxTreeItemId& item) { - // recursive call - result += GetChildrenCount(child, TRUE); + m_count++; + + return TRUE; } - // add the child to the result in any case - result++; + size_t GetCount() const { return m_count; } - child = GetNextChild(item, cookie); - } + private: + size_t m_count; + } counter(this, item, recursively); - return result; + return counter.GetCount(); } // ---------------------------------------------------------------------------- @@ -306,6 +396,16 @@ void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) DoSetItem(&tvItem); } +void wxTreeCtrl::DoSetItemImages(const wxTreeItemId& item, + int image, + int imageSel) +{ + wxTreeViewItem tvItem(item, TVIF_IMAGE | TVIF_SELECTEDIMAGE); + tvItem.iSelectedImage = imageSel; + tvItem.iImage = image; + DoSetItem(&tvItem); +} + int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const { wxTreeViewItem tvItem(item, TVIF_IMAGE); @@ -316,9 +416,10 @@ int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image) { - wxTreeViewItem tvItem(item, TVIF_IMAGE); - tvItem.iImage = image; - DoSetItem(&tvItem); + // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always + // change both normal and selected image - otherwise the change simply + // doesn't take place! + DoSetItemImages(item, image, GetItemSelectedImage(item)); } int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const @@ -331,9 +432,10 @@ int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image) { - wxTreeViewItem tvItem(item, TVIF_SELECTEDIMAGE); - tvItem.iSelectedImage = image; - DoSetItem(&tvItem); + // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always + // change both normal and selected image - otherwise the change simply + // doesn't take place! + DoSetItemImages(item, GetItemImage(item), image); } wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const @@ -433,6 +535,9 @@ wxTreeItemId wxTreeCtrl::GetRootItem() const wxTreeItemId wxTreeCtrl::GetSelection() const { + wxCHECK_MSG( !(m_windowStyle & wxTR_MULTIPLE), (WXHTREEITEM)0, + _T("this only works with single selection controls") ); + return wxTreeItemId((WXHTREEITEM) TreeView_GetSelection(GetHwnd())); } @@ -507,6 +612,62 @@ wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item)); } +// ---------------------------------------------------------------------------- +// multiple selections emulation +// ---------------------------------------------------------------------------- + +bool wxTreeCtrl::IsItemChecked(const wxTreeItemId& item) const +{ + // 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) +{ + // receive the desired information. + wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK); + + // state images are one-based + tvItem.state = (check ? 2 : 1) << 12; + + DoSetItem(&tvItem); +} + +size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const +{ + class TraverseSelections : public wxTreeTraversal + { + public: + TraverseSelections(const wxTreeCtrl *tree, + wxArrayTreeItemIds& selections) + : wxTreeTraversal(tree), m_selections(selections) + { + m_selections.Empty(); + + DoTraverse(tree->GetRootItem()); + } + + virtual bool OnVisit(const wxTreeItemId& item) + { + if ( GetTree()->IsItemChecked(item) ) + { + m_selections.Add(item); + } + + return TRUE; + } + + private: + wxArrayTreeItemIds& m_selections; + } selector(this, selections); + + return selections.GetCount(); +} + // ---------------------------------------------------------------------------- // Usual operations // ---------------------------------------------------------------------------- @@ -721,38 +882,67 @@ void wxTreeCtrl::Toggle(const wxTreeItemId& item) void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action) { - DoExpand(item, action); + DoExpand(item, action); } void wxTreeCtrl::Unselect() { + wxASSERT_MSG( !(m_windowStyle & wxTR_MULTIPLE), _T("doesn't make sense") ); + + // just remove the selection SelectItem(wxTreeItemId((WXHTREEITEM) 0)); } -void wxTreeCtrl::SelectItem(const wxTreeItemId& item) +void wxTreeCtrl::UnselectAll() { - // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive - // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so - // send them ourselves - - wxTreeEvent event(wxEVT_NULL, m_windowId); - event.m_item = item; - event.SetEventObject(this); - - event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING); - if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() ) + if ( m_windowStyle & wxTR_MULTIPLE ) { - if ( !TreeView_SelectItem(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item) ) + wxArrayTreeItemIds selections; + size_t count = GetSelections(selections); + for ( size_t n = 0; n < count; n++ ) { - wxLogLastError("TreeView_SelectItem"); + SetItemCheck(selections[n], FALSE); } - else + } + else + { + // just remove the selection + Unselect(); + } +} + +void wxTreeCtrl::SelectItem(const wxTreeItemId& item) +{ + if ( m_windowStyle & wxTR_MULTIPLE ) + { + // selecting the item means checking it + SetItemCheck(item); + } + else + { + // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive + // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so + // send them ourselves + + wxTreeEvent event(wxEVT_NULL, m_windowId); + event.m_item = item; + event.SetEventObject(this); + + event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING); + if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() ) { - event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); - (void)GetEventHandler()->ProcessEvent(event); + if ( !TreeView_SelectItem(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item) ) + { + wxLogLastError("TreeView_SelectItem"); + } + else + { + event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); + (void)GetEventHandler()->ProcessEvent(event); + } } + //else: program vetoed the change } - //else: program vetoed the change } void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item) -- 2.45.2