X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/deb1de30031e07348b5391974fff35f76f7aec5f..e3dbf5934e997729c6f4f5154b1b7f29d5d22149:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index c5c49719b4..84a8b01c20 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -116,6 +116,7 @@ static HTREEITEM GetItemFromPoint(HWND hwndTV, int x, int y) // wrappers for TreeView_GetItem/TreeView_SetItem static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem) { + TV_ITEM tvi; tvi.mask = TVIF_STATE | TVIF_HANDLE; tvi.stateMask = TVIS_SELECTED; @@ -291,6 +292,8 @@ struct wxTreeViewItem : public TV_ITEM UINT mask_, // fields which are valid UINT stateMask_ = 0) // for TVIF_STATE only { + wxZeroMemory(*this); + // hItem member is always valid mask = mask_ | TVIF_HANDLE; stateMask = stateMask_; @@ -298,10 +301,39 @@ struct wxTreeViewItem : public TV_ITEM } }; +// wxVirutalNode is used in place of a single root when 'hidden' root is +// specified. +class wxVirtualNode : public wxTreeViewItem +{ +public: + wxVirtualNode(wxTreeItemData *data) + : wxTreeViewItem(TVI_ROOT, 0) + { + m_data = data; + } + + ~wxVirtualNode() + { + delete m_data; + } + + wxTreeItemData *GetData() const { return m_data; } + void SetData(wxTreeItemData *data) { delete m_data; m_data = data; } + +private: + wxTreeItemData *m_data; +}; + #ifdef __VISUALC__ #pragma warning( default : 4097 ) #endif +// a macro to get the virtual root, returns NULL if none +#define GET_VIRTUAL_ROOT() ((wxVirtualNode *)m_pVirtualRoot) + +// returns TRUE if the item is the virtual root +#define IS_VIRTUAL_ROOT(item) (HITEM(item) == TVI_ROOT) + // a class which encapsulates the tree traversal logic: it vists all (unless // OnVisit() returns FALSE) items under the given one class wxTreeTraversal @@ -345,6 +377,12 @@ public: virtual bool OnVisit(const wxTreeItemId& item) { + // can't visit a virtual node. + if ( (GetTree()->GetRootItem() == item) && (GetTree()->GetWindowStyle() & wxTR_HIDE_ROOT)) + { + return TRUE; + } + #if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE if ( GetTree()->IsItemChecked(item) ) #else @@ -537,6 +575,7 @@ void wxTreeCtrl::Init() m_hasAnyAttr = FALSE; m_dragImage = NULL; m_htSelStart = 0; + m_pVirtualRoot = NULL; // initialize the global array of events now as it can't be done statically // with the wxEVT_XXX values being allocated during run-time only @@ -691,6 +730,7 @@ wxTreeCtrl::~wxTreeCtrl() DeleteTextCtrl(); // delete user data to prevent memory leaks + // also deletes hidden root node storage. DeleteAllItems(); if (m_ownsImageListNormal) delete m_imageListNormal; @@ -705,6 +745,9 @@ wxTreeCtrl::~wxTreeCtrl() bool wxTreeCtrl::DoGetItem(wxTreeViewItem* tvItem) const { + wxCHECK_MSG( tvItem->hItem != TVI_ROOT, FALSE, + _T("can't retrieve virtual root item") ); + if ( !TreeView_GetItem(GetHwnd(), tvItem) ) { wxLogLastError(wxT("TreeView_GetItem")); @@ -758,7 +801,9 @@ void wxTreeCtrl::SetAnyImageList(wxImageList *imageList, int which) void wxTreeCtrl::SetImageList(wxImageList *imageList) { - if (m_ownsImageListNormal) delete m_imageListNormal; + if (m_ownsImageListNormal) + delete m_imageListNormal; + SetAnyImageList(m_imageListNormal = imageList, TVSIL_NORMAL); m_ownsImageListNormal = FALSE; } @@ -840,6 +885,9 @@ wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) { + if ( IS_VIRTUAL_ROOT(item) ) + return; + wxTreeViewItem tvItem(item, TVIF_TEXT); tvItem.pszText = (wxChar *)text.c_str(); // conversion is ok DoSetItem(&tvItem); @@ -898,6 +946,12 @@ void wxTreeCtrl::DoSetItemImages(const wxTreeItemId& item, int wxTreeCtrl::GetItemImage(const wxTreeItemId& item, wxTreeItemIcon which) const { + if ( (HITEM(item) == TVI_ROOT) && (m_windowStyle & wxTR_HIDE_ROOT) ) + { + // TODO: Maybe a hidden root can still provide images? + return -1; + } + if ( HasIndirectData(item) ) { return DoGetItemImageFromData(item, which); @@ -931,6 +985,12 @@ int wxTreeCtrl::GetItemImage(const wxTreeItemId& item, void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image, wxTreeItemIcon which) { + if ( IS_VIRTUAL_ROOT(item) ) + { + // TODO: Maybe a hidden root can still store images? + return; + } + int imageNormal, imageSel; switch ( which ) { @@ -984,6 +1044,14 @@ void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image, wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const { wxTreeViewItem tvItem(item, TVIF_PARAM); + + // Hidden root may have data. + if ( IS_VIRTUAL_ROOT(item) ) + { + return GET_VIRTUAL_ROOT()->GetData(); + } + + // Visible node. if ( !DoGetItem(&tvItem) ) { return NULL; @@ -1000,6 +1068,11 @@ wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data) { + if ( IS_VIRTUAL_ROOT(item) ) + { + GET_VIRTUAL_ROOT()->SetData(data); + } + // first, associate this piece of data with this item if ( data ) { @@ -1054,6 +1127,9 @@ bool wxTreeCtrl::HasIndirectData(const wxTreeItemId& item) const void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) { + if ( IS_VIRTUAL_ROOT(item) ) + return; + wxTreeViewItem tvItem(item, TVIF_CHILDREN); tvItem.cChildren = (int)has; DoSetItem(&tvItem); @@ -1061,6 +1137,9 @@ void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold) { + if ( IS_VIRTUAL_ROOT(item) ) + return; + wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD); tvItem.state = bold ? TVIS_BOLD : 0; DoSetItem(&tvItem); @@ -1068,6 +1147,9 @@ void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold) void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight) { + if ( IS_VIRTUAL_ROOT(item) ) + return; + wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_DROPHILITED); tvItem.state = highlight ? TVIS_DROPHILITED : 0; DoSetItem(&tvItem); @@ -1075,6 +1157,9 @@ void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight) void wxTreeCtrl::RefreshItem(const wxTreeItemId& item) { + if ( IS_VIRTUAL_ROOT(item) ) + return; + wxRect rect; if ( GetBoundingRect(item, rect) ) { @@ -1193,6 +1278,10 @@ bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const wxTreeItemId wxTreeCtrl::GetRootItem() const { + // Root may be real (visible) or virtual (hidden). + if ( GET_VIRTUAL_ROOT() ) + return TVI_ROOT; + return wxTreeItemId((WXHTREEITEM) TreeView_GetRoot(GetHwnd())); } @@ -1206,7 +1295,24 @@ wxTreeItemId wxTreeCtrl::GetSelection() const wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const { - return wxTreeItemId((WXHTREEITEM) TreeView_GetParent(GetHwnd(), HITEM(item))); + HTREEITEM hItem; + + if ( IS_VIRTUAL_ROOT(item) ) + { + // no parent for the virtual root + hItem = 0; + } + else // normal item + { + hItem = TreeView_GetParent(GetHwnd(), HITEM(item)); + if ( !hItem && HasFlag(wxTR_HIDE_ROOT) ) + { + // the top level items should have the virtual root as their parent + hItem = TVI_ROOT; + } + } + + return wxTreeItemId((WXHTREEITEM)hItem); } wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, @@ -1292,6 +1398,8 @@ void wxTreeCtrl::SetItemCheck(const wxTreeItemId& item, bool check) // receive the desired information. wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK); + DoGetItem(&tvItem); + // state images are one-based tvItem.state = (check ? 2 : 1) << 12; @@ -1397,7 +1505,16 @@ wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text, int image, int selectedImage, wxTreeItemData *data) { - return DoInsertItem(wxTreeItemId((long) (WXHTREEITEM) 0), (long)(WXHTREEITEM) 0, + + if ( m_windowStyle & wxTR_HIDE_ROOT ) + { + // create a virtual root item, the parent for all the others + m_pVirtualRoot = new wxVirtualNode(data); + + return TVI_ROOT; + } + + return DoInsertItem(wxTreeItemId((long)(WXHTREEITEM) 0), (long)(WXHTREEITEM) 0, text, image, selectedImage, data); } @@ -1486,6 +1603,9 @@ void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item) void wxTreeCtrl::DeleteAllItems() { + // delete stored root item. + delete GET_VIRTUAL_ROOT(); + if ( !TreeView_DeleteAllItems(GetHwnd()) ) { wxLogLastError(wxT("TreeView_DeleteAllItems")); @@ -1500,6 +1620,10 @@ void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag) flag == TVE_TOGGLE, wxT("Unknown flag in wxTreeCtrl::DoExpand") ); + // A hidden root can be neither expanded nor collapsed. + wxCHECK_RET( !(m_windowStyle & wxTR_HIDE_ROOT) || (HITEM(item) != TVI_ROOT), + wxT("Can't expand/collapse hidden root node!") ) + // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must // emulate them. This behaviour has changed slightly with comctl32.dll // v 4.70 - now it does send them but only the first time. To maintain @@ -1639,24 +1763,6 @@ void wxTreeCtrl::ScrollTo(const wxTreeItemId& item) wxTextCtrl* wxTreeCtrl::GetEditControl() const { - // normally, we could try to do something like this to return something - // even when the editing was started by the user and not by calling - // EditLabel() - but as nobody has asked for this so far and there might be - // problems in the code below, I leave it disabled for now (VZ) -#if 0 - if ( !m_textCtrl ) - { - HWND hwndText = TreeView_GetEditControl(GetHwnd()); - if ( hwndText ) - { - m_textCtrl = new wxTextCtrl(this, -1); - m_textCtrl->Hide(); - m_textCtrl->SetHWND((WXHWND)hwndText); - } - //else: not editing label right now - } -#endif // 0 - return m_textCtrl; } @@ -1686,20 +1792,19 @@ wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item, DeleteTextCtrl(); + m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject(); HWND hWnd = (HWND) TreeView_EditLabel(GetHwnd(), HITEM(item)); // this is not an error - the TVN_BEGINLABELEDIT handler might have // returned FALSE if ( !hWnd ) { + delete m_textCtrl; + m_textCtrl = NULL; return NULL; } - m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject(); - m_textCtrl->SetParent(this); - m_textCtrl->SetHWND((WXHWND)hWnd); - m_textCtrl->SubclassWin((WXHWND)hWnd); - + // textctrl is subclassed in MSWOnNotify return m_textCtrl; } @@ -2175,13 +2280,12 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) eventType = wxEVT_COMMAND_TREE_KEY_DOWN; TV_KEYDOWN *info = (TV_KEYDOWN *)lParam; - // we pass 0 as last CreateKeyEvent() parameter because we + // we pass 0 as 2 last CreateKeyEvent() parameters because we // don't have access to the real key press flags here - but as // it is only used to determin wxKeyEvent::m_altDown flag it's // not too bad event.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, - wxCharCodeMSWToWX(info->wVKey), - 0); + wxCharCodeMSWToWX(info->wVKey)); // a separate event for Space/Return if ( !wxIsCtrlDown() && !wxIsShiftDown() && @@ -2402,6 +2506,32 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case TVN_BEGINLABELEDIT: // return TRUE to cancel label editing *result = !event.IsAllowed(); + // set ES_WANTRETURN ( like we do in BeginLabelEdit ) + if(event.IsAllowed()) + { + HWND hText = TreeView_GetEditControl(GetHwnd()); + if(hText != NULL) + { + // MBN: if m_textCtrl already has an HWND, it is a stale + // pointer from a previous edit (because the user + // didn't modify the label before dismissing the control, + // and TVN_ENDLABELEDIT was not sent), so delete it + if(m_textCtrl && m_textCtrl->GetHWND() != 0) + DeleteTextCtrl(); + if(!m_textCtrl) + m_textCtrl = new wxTextCtrl(); + m_textCtrl->SetParent(this); + m_textCtrl->SetHWND((WXHWND)hText); + m_textCtrl->SubclassWin((WXHWND)hText); + + // set wxTE_PROCESS_ENTER style for the text control to + // force it to process the Enter presses itself, otherwise + // they could be stolen from it by the dialog + // navigation code + m_textCtrl->SetWindowStyle(m_textCtrl->GetWindowStyle() + | wxTE_PROCESS_ENTER); + } + } break; case TVN_ENDLABELEDIT: