X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/2899e2232750915f4ff37c0ac178fce1b5858fe0..30760ce7e58df02af3308cd7de8bd7694e791b8d:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index cd68918437..44c408406b 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -45,12 +45,15 @@ #include "wx/dynarray.h" #include "wx/imaglist.h" #include "wx/treectrl.h" +#include "wx/settings.h" #ifdef __GNUWIN32__ +#ifndef wxUSE_NORLANDER_HEADERS #include "wx/msw/gnuwin32/extra.h" #endif +#endif -#if (defined(__WIN95__) && !defined(__GNUWIN32__)) || defined(__TWIN32__) +#if (defined(__WIN95__) && !defined(__GNUWIN32__)) || defined(__TWIN32__) || defined(wxUSE_NORLANDER_HEADERS) #include #endif @@ -64,6 +67,10 @@ // ---------------------------------------------------------------------------- // a convenient wrapper around TV_ITEM struct which adds a ctor +#ifdef __VISUALC__ +#pragma warning( disable : 4097 ) +#endif + struct wxTreeViewItem : public TV_ITEM { wxTreeViewItem(const wxTreeItemId& item, // the item handle @@ -77,6 +84,10 @@ struct wxTreeViewItem : public TV_ITEM } }; +#ifdef __VISUALC__ +#pragma warning( default : 4097 ) +#endif + // a class which encapsulates the tree traversal logic: it vists all (unless // OnVisit() returns FALSE) items under the given one class wxTreeTraversal @@ -105,6 +116,115 @@ private: const wxTreeCtrl *m_tree; }; +// internal class for getting the selected items +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; +}; + +// internal class for counting tree items +class TraverseCounter : public wxTreeTraversal +{ +public: + TraverseCounter(const wxTreeCtrl *tree, + const wxTreeItemId& root, + bool recursively) + : wxTreeTraversal(tree) + { + m_count = 0; + + DoTraverse(root, recursively); + } + + virtual bool OnVisit(const wxTreeItemId& item) + { + m_count++; + + return TRUE; + } + + size_t GetCount() const { return m_count; } + +private: + size_t m_count; +}; + +// ---------------------------------------------------------------------------- +// This class is needed for support of different images: the Win32 common +// control natively supports only 2 images (the normal one and another for the +// selected state). We wish to provide support for 2 more of them for folder +// items (i.e. those which have children): for expanded state and for expanded +// selected state. For this we use this structure to store the additional items +// images. +// +// There is only one problem with this: when we retrieve the item's data, we +// don't know whether we get a pointer to wxTreeItemData or +// wxTreeItemIndirectData. So we have to maintain a list of all items which +// have indirect data inside the listctrl itself. +// ---------------------------------------------------------------------------- +class wxTreeItemIndirectData +{ +public: + // ctor associates this data with the item and the real item data becomes + // available through our GetData() method + wxTreeItemIndirectData(wxTreeCtrl *tree, const wxTreeItemId& item) + { + for ( size_t n = 0; n < WXSIZEOF(m_images); n++ ) + { + m_images[n] = -1; + } + + // save the old data + m_data = tree->GetItemData(item); + + // and set ourselves as the new one + tree->SetIndirectItemData(item, this); + } + + // dtor deletes the associated data as well + ~wxTreeItemIndirectData() { delete m_data; } + + // accessors + // get the real data associated with the item + wxTreeItemData *GetData() const { return m_data; } + // change it + void SetData(wxTreeItemData *data) { m_data = data; } + + // do we have such image? + bool HasImage(wxTreeItemIcon which) const { return m_images[which] != -1; } + // get image + int GetImage(wxTreeItemIcon which) const { return m_images[which]; } + // change it + void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; } + +private: + // all the images associated with the item + int m_images[wxTreeItemIcon_Max]; + + wxTreeItemData *m_data; +}; + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- @@ -195,17 +315,22 @@ bool wxTreeCtrl::Create(wxWindow *parent, if ( m_windowStyle & wxTR_LINES_AT_ROOT ) wstyle |= TVS_LINESATROOT; -#ifndef __GNUWIN32__ +#if !defined( __GNUWIN32__ ) && !defined( __BORLANDC__ ) && !defined(wxUSE_NORLANDER_HEADERS) // 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 !defined(__VISUALC__) || (__VISUALC__ != 1010) if ( m_windowStyle & wxTR_MULTIPLE ) - wstyle |= TVS_CHECKBOXES; + wstyle |= TVS_CHECKBOXES; +#endif #endif // Create the tree control. if ( !MSWCreateControl(WC_TREEVIEW, wstyle) ) return FALSE; + SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW)); + SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); + // 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. @@ -233,7 +358,7 @@ bool wxTreeCtrl::Create(wxWindow *parent, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED) ) { - wxLogLastError(_T("DrawFrameControl(check)")); + wxLogLastError(wxT("DrawFrameControl(check)")); } bmp.SetHBITMAP((WXHBITMAP)hbmpCheck); @@ -243,7 +368,7 @@ bool wxTreeCtrl::Create(wxWindow *parent, DFC_BUTTON, DFCS_BUTTONCHECK) ) { - wxLogLastError(_T("DrawFrameControl(uncheck)")); + wxLogLastError(wxT("DrawFrameControl(uncheck)")); } bmp.SetHBITMAP((WXHBITMAP)hbmpCheck); @@ -342,33 +467,9 @@ void wxTreeCtrl::SetStateImageList(wxImageList *imageList) size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively) const { - class TraverseCounter : public wxTreeTraversal - { - public: - TraverseCounter(const wxTreeCtrl *tree, - const wxTreeItemId& root, - bool recursively) - : wxTreeTraversal(tree) - { - m_count = 0; - - DoTraverse(root, recursively); - } + TraverseCounter counter(this, item, recursively); - virtual bool OnVisit(const wxTreeItemId& item) - { - m_count++; - - return TRUE; - } - - size_t GetCount() const { return m_count; } - - private: - size_t m_count; - } counter(this, item, recursively); - - return counter.GetCount(); + return counter.GetCount() - 1; } // ---------------------------------------------------------------------------- @@ -385,7 +486,7 @@ wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const if ( !DoGetItem(&tvItem) ) { // don't return some garbage which was on stack, but an empty string - buf[0] = _T('\0'); + buf[0] = wxT('\0'); } return wxString(buf); @@ -398,6 +499,46 @@ void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) DoSetItem(&tvItem); } +int wxTreeCtrl::DoGetItemImageFromData(const wxTreeItemId& item, + wxTreeItemIcon which) const +{ + wxTreeViewItem tvItem(item, TVIF_PARAM); + if ( !DoGetItem(&tvItem) ) + { + return -1; + } + + return ((wxTreeItemIndirectData *)tvItem.lParam)->GetImage(which); +} + +void wxTreeCtrl::DoSetItemImageFromData(const wxTreeItemId& item, + int image, + wxTreeItemIcon which) const +{ + wxTreeViewItem tvItem(item, TVIF_PARAM); + if ( !DoGetItem(&tvItem) ) + { + return; + } + + wxTreeItemIndirectData *data = ((wxTreeItemIndirectData *)tvItem.lParam); + + data->SetImage(image, which); + + // make sure that we have selected images as well + if ( which == wxTreeItemIcon_Normal && + !data->HasImage(wxTreeItemIcon_Selected) ) + { + data->SetImage(image, wxTreeItemIcon_Selected); + } + + if ( which == wxTreeItemIcon_Expanded && + !data->HasImage(wxTreeItemIcon_SelectedExpanded) ) + { + data->SetImage(image, wxTreeItemIcon_SelectedExpanded); + } +} + void wxTreeCtrl::DoSetItemImages(const wxTreeItemId& item, int image, int imageSel) @@ -408,36 +549,90 @@ void wxTreeCtrl::DoSetItemImages(const wxTreeItemId& item, DoSetItem(&tvItem); } -int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const +int wxTreeCtrl::GetItemImage(const wxTreeItemId& item, + wxTreeItemIcon which) const { - wxTreeViewItem tvItem(item, TVIF_IMAGE); - DoGetItem(&tvItem); + if ( HasIndirectData(item) ) + { + return DoGetItemImageFromData(item, which); + } - return tvItem.iImage; -} + UINT mask; + switch ( which ) + { + default: + wxFAIL_MSG( wxT("unknown tree item image type") ); -void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image) -{ - // 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)); -} + case wxTreeItemIcon_Normal: + mask = TVIF_IMAGE; + break; -int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const -{ - wxTreeViewItem tvItem(item, TVIF_SELECTEDIMAGE); + case wxTreeItemIcon_Selected: + mask = TVIF_SELECTEDIMAGE; + break; + + case wxTreeItemIcon_Expanded: + case wxTreeItemIcon_SelectedExpanded: + return -1; + } + + wxTreeViewItem tvItem(item, mask); DoGetItem(&tvItem); - return tvItem.iSelectedImage; + return mask == TVIF_IMAGE ? tvItem.iImage : tvItem.iSelectedImage; } -void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image) +void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image, + wxTreeItemIcon which) { + int imageNormal, imageSel; + switch ( which ) + { + default: + wxFAIL_MSG( wxT("unknown tree item image type") ); + + case wxTreeItemIcon_Normal: + imageNormal = image; + imageSel = GetItemSelectedImage(item); + break; + + case wxTreeItemIcon_Selected: + imageNormal = GetItemImage(item); + imageSel = image; + break; + + case wxTreeItemIcon_Expanded: + case wxTreeItemIcon_SelectedExpanded: + if ( !HasIndirectData(item) ) + { + // we need to get the old images first, because after we create + // the wxTreeItemIndirectData GetItemXXXImage() will use it to + // get the images + imageNormal = GetItemImage(item); + imageSel = GetItemSelectedImage(item); + + // if it doesn't have it yet, add it + wxTreeItemIndirectData *data = new + wxTreeItemIndirectData(this, item); + + // copy the data to the new location + data->SetImage(imageNormal, wxTreeItemIcon_Normal); + data->SetImage(imageSel, wxTreeItemIcon_Selected); + } + + DoSetItemImageFromData(item, image, which); + + // reset the normal/selected images because we won't use them any + // more - now they're stored inside the indirect data + imageNormal = + imageSel = I_IMAGECALLBACK; + break; + } + // 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); + DoSetItemImages(item, imageNormal, imageSel); } wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const @@ -448,14 +643,55 @@ wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const return NULL; } - return (wxTreeItemData *)tvItem.lParam; + if ( HasIndirectData(item) ) + { + return ((wxTreeItemIndirectData *)tvItem.lParam)->GetData(); + } + else + { + return (wxTreeItemData *)tvItem.lParam; + } } void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data) { wxTreeViewItem tvItem(item, TVIF_PARAM); - tvItem.lParam = (LPARAM)data; - DoSetItem(&tvItem); + + if ( HasIndirectData(item) ) + { + if ( DoGetItem(&tvItem) ) + { + ((wxTreeItemIndirectData *)tvItem.lParam)->SetData(data); + } + else + { + wxFAIL_MSG( wxT("failed to change tree items data") ); + } + } + else + { + tvItem.lParam = (LPARAM)data; + DoSetItem(&tvItem); + } +} + +void wxTreeCtrl::SetIndirectItemData(const wxTreeItemId& item, + wxTreeItemIndirectData *data) +{ + // this should never happen because it's unnecessary and will probably lead + // to crash too because the code elsewhere supposes that the pointer the + // wxTreeItemIndirectData has is a real wxItemData and not + // wxTreeItemIndirectData as well + wxASSERT_MSG( !HasIndirectData(item), wxT("setting indirect data twice?") ); + + SetItemData(item, (wxTreeItemData *)data); + + m_itemsWithIndirectData.Add(item); +} + +bool wxTreeCtrl::HasIndirectData(const wxTreeItemId& item) const +{ + return m_itemsWithIndirectData.Index(item) != wxNOT_FOUND; } void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) @@ -538,7 +774,7 @@ wxTreeItemId wxTreeCtrl::GetRootItem() const wxTreeItemId wxTreeCtrl::GetSelection() const { wxCHECK_MSG( !(m_windowStyle & wxTR_MULTIPLE), (WXHTREEITEM)0, - _T("this only works with single selection controls") ); + wxT("this only works with single selection controls") ); return wxTreeItemId((WXHTREEITEM) TreeView_GetSelection(GetHwnd())); } @@ -600,7 +836,7 @@ wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const { - wxASSERT_MSG( IsVisible(item), _T("The item you call GetNextVisible() " + wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() " "for must be visible itself!")); return wxTreeItemId((WXHTREEITEM) TreeView_GetNextVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item)); @@ -608,7 +844,7 @@ wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const { - wxASSERT_MSG( IsVisible(item), _T("The item you call GetPrevVisible() " + wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() " "for must be visible itself!")); return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item)); @@ -641,31 +877,7 @@ void wxTreeCtrl::SetItemCheck(const wxTreeItemId& item, bool check) 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); + TraverseSelections selector(this, selections); return selections.GetCount(); } @@ -684,8 +896,9 @@ wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent, tvIns.hParent = (HTREEITEM) (WXHTREEITEM)parent; tvIns.hInsertAfter = (HTREEITEM) (WXHTREEITEM) hInsertAfter; - // This is how we insert the item as the first child: supply a NULL hInsertAfter - if (tvIns.hInsertAfter == (HTREEITEM) 0) + // this is how we insert the item as the first child: supply a NULL + // hInsertAfter + if ( !tvIns.hInsertAfter ) { tvIns.hInsertAfter = TVI_FIRST; } @@ -829,7 +1042,7 @@ void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag) flag == (TVE_COLLAPSE | TVE_COLLAPSERESET) || flag == TVE_EXPAND || flag == TVE_TOGGLE, - _T("Unknown flag in wxTreeCtrl::DoExpand") ); + wxT("Unknown flag in wxTreeCtrl::DoExpand") ); // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must // emulate them. This behaviour has changed slightly with comctl32.dll @@ -889,7 +1102,7 @@ void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action) void wxTreeCtrl::Unselect() { - wxASSERT_MSG( !(m_windowStyle & wxTR_MULTIPLE), _T("doesn't make sense") ); + wxASSERT_MSG( !(m_windowStyle & wxTR_MULTIPLE), wxT("doesn't make sense") ); // just remove the selection SelectItem(wxTreeItemId((WXHTREEITEM) 0)); @@ -1067,7 +1280,7 @@ static int CALLBACK TreeView_CompareCallback(wxTreeItemData *pItem1, wxTreeCtrl *tree) { wxCHECK_MSG( pItem1 && pItem2, 0, - _T("sorting tree without data doesn't make sense") ); + wxT("sorting tree without data doesn't make sense") ); return tree->OnCompareItems(pItem1->GetId(), pItem2->GetId()); } @@ -1134,6 +1347,25 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) switch ( hdr->code ) { + case NM_RCLICK: + { + if ( wxControl::MSWOnNotify(idCtrl, lParam, result) ) + return TRUE; + + TV_HITTESTINFO tvhti; + ::GetCursorPos(&(tvhti.pt)); + ::ScreenToClient(GetHwnd(),&(tvhti.pt)); + if ( TreeView_HitTest(GetHwnd(),&tvhti) ) + { + if( tvhti.flags & TVHT_ONITEM ) + { + event.m_item = (WXHTREEITEM) tvhti.hItem; + eventType=wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK; + } + } + break; + } + case TVN_BEGINDRAG: eventType = wxEVT_COMMAND_TREE_BEGIN_DRAG; // fall through @@ -1216,11 +1448,11 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; default: - wxLogDebug(_T("unexpected code %d in TVN_ITEMEXPAND " + wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND " "message"), tv->action); } - bool ing = (hdr->code == TVN_ITEMEXPANDING); + bool ing = ((int)hdr->code == TVN_ITEMEXPANDING); eventType = g_events[expand][ing]; event.m_item = (WXHTREEITEM) tv->itemNew.hItem; @@ -1282,8 +1514,21 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // prefer to do it here ourself (otherwise deleting a tree // with many items is just too slow) NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam; - wxTreeItemData *data = (wxTreeItemData *)tv->itemOld.lParam; - delete data; // may be NULL, ok + + wxTreeItemId item = event.m_item; + if ( HasIndirectData(item) ) + { + wxTreeItemIndirectData *data = (wxTreeItemIndirectData *) + tv->itemOld.lParam; + delete data; // can't be NULL here + + m_itemsWithIndirectData.Remove(item); + } + else + { + wxTreeItemData *data = (wxTreeItemData *)tv->itemOld.lParam; + delete data; // may be NULL, ok + } processed = TRUE; // Make sure we don't get called twice } @@ -1309,6 +1554,36 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) *result = !event.IsAllowed(); break; + case TVN_GETDISPINFO: + // NB: so far the user can't set the image himself anyhow, so do it + // anyway - but this may change later + if ( /* !processed && */ 1 ) + { + wxTreeItemId item = event.m_item; + TV_DISPINFO *info = (TV_DISPINFO *)lParam; + if ( info->item.mask & TVIF_IMAGE ) + { + info->item.iImage = + DoGetItemImageFromData + ( + item, + IsExpanded(item) ? wxTreeItemIcon_Expanded + : wxTreeItemIcon_Normal + ); + } + if ( info->item.mask & TVIF_SELECTEDIMAGE ) + { + info->item.iSelectedImage = + DoGetItemImageFromData + ( + item, + IsExpanded(item) ? wxTreeItemIcon_SelectedExpanded + : wxTreeItemIcon_Selected + ); + } + } + break; + //default: // for the other messages the return value is ignored and there is // nothing special to do