X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/77cff606637e123ff279011919eb8c817e2149e1..47f12f58dbd5e62c5d1c635142a26c3e2208d012:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 8171cae0a3..aebe08ac39 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -67,7 +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 @@ -80,7 +83,10 @@ struct wxTreeViewItem : public TV_ITEM hItem = (HTREEITEM) (WXHTREEITEM) 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 @@ -110,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 // ---------------------------------------------------------------------------- @@ -200,10 +315,10 @@ bool wxTreeCtrl::Create(wxWindow *parent, if ( m_windowStyle & wxTR_LINES_AT_ROOT ) wstyle |= TVS_LINESATROOT; -#if !defined( __GNUWIN32__ ) && !defined( __BORLANDC__ ) && !defined(wxUSE_NORLANDER_HEADERS) +#if !defined( __GNUWIN32__ ) && !defined( __BORLANDC__ ) && !defined( __WATCOMC__ ) && !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 !defined(__VISUALC__) || (__VISUALC__ > 1010) if ( m_windowStyle & wxTR_MULTIPLE ) wstyle |= TVS_CHECKBOXES; #endif @@ -216,7 +331,6 @@ bool wxTreeCtrl::Create(wxWindow *parent, 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. @@ -244,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); @@ -254,7 +368,7 @@ bool wxTreeCtrl::Create(wxWindow *parent, DFC_BUTTON, DFCS_BUTTONCHECK) ) { - wxLogLastError(_T("DrawFrameControl(uncheck)")); + wxLogLastError(wxT("DrawFrameControl(uncheck)")); } bmp.SetHBITMAP((WXHBITMAP)hbmpCheck); @@ -350,41 +464,12 @@ void wxTreeCtrl::SetStateImageList(wxImageList *imageList) SetAnyImageList(m_imageListState = imageList, TVSIL_STATE); } -// 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; -}; - - size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively) const { TraverseCounter counter(this, item, recursively); - return counter.GetCount(); + return counter.GetCount() - 1; } // ---------------------------------------------------------------------------- @@ -401,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); @@ -414,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) @@ -424,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 @@ -464,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) @@ -554,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())); } @@ -616,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)); @@ -624,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)); @@ -655,34 +875,6 @@ void wxTreeCtrl::SetItemCheck(const wxTreeItemId& item, bool check) DoSetItem(&tvItem); } -// internal class for getting the selected - -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; -}; - size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const { TraverseSelections selector(this, selections); @@ -704,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; } @@ -849,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 @@ -909,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)); @@ -1087,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()); } @@ -1154,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 @@ -1197,6 +1409,8 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) event.m_item = (WXHTREEITEM)info->item.hItem; event.m_label = info->item.pszText; + if (info->item.pszText == NULL) + return FALSE; break; } @@ -1236,11 +1450,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; @@ -1302,8 +1516,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 } @@ -1329,6 +1556,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