+ return true;
+}
+
+// ----------------------------------------------------------------------------
+// Item access
+// ----------------------------------------------------------------------------
+
+bool wxTreeCtrl::IsHiddenRoot(const wxTreeItemId& item) const
+{
+ return HITEM(item) == TVI_ROOT && HasFlag(wxTR_HIDE_ROOT);
+}
+
+wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") );
+
+ wxChar buf[512]; // the size is arbitrary...
+
+ wxTreeViewItem tvItem(item, TVIF_TEXT);
+ tvItem.pszText = buf;
+ tvItem.cchTextMax = WXSIZEOF(buf);
+ if ( !DoGetItem(&tvItem) )
+ {
+ // don't return some garbage which was on stack, but an empty string
+ buf[0] = wxT('\0');
+ }
+
+ return wxString(buf);
+}
+
+void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
+{
+ wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
+
+ if ( IS_VIRTUAL_ROOT(item) )
+ return;
+
+ wxTreeViewItem tvItem(item, TVIF_TEXT);
+ tvItem.pszText = wxMSW_CONV_LPTSTR(text);
+ DoSetItem(&tvItem);
+
+ // when setting the text of the item being edited, the text control should
+ // be updated to reflect the new text as well, otherwise calling
+ // SetItemText() in the OnBeginLabelEdit() handler doesn't have any effect
+ //
+ // don't use GetEditControl() here because m_textCtrl is not set yet
+ HWND hwndEdit = TreeView_GetEditControl(GetHwnd());
+ if ( hwndEdit )
+ {
+ if ( item == m_idEdited )
+ {
+ ::SetWindowText(hwndEdit, text.t_str());
+ }
+ }
+}
+
+int wxTreeCtrl::GetItemImage(const wxTreeItemId& item,
+ wxTreeItemIcon which) const
+{
+ wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
+
+ if ( IsHiddenRoot(item) )
+ {
+ // no images for hidden root item
+ return -1;
+ }
+
+ wxTreeItemParam *param = GetItemParam(item);
+
+ return param && param->HasImage(which) ? param->GetImage(which) : -1;
+}
+
+void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image,
+ wxTreeItemIcon which)
+{
+ wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
+ wxCHECK_RET( which >= 0 &&
+ which < wxTreeItemIcon_Max,
+ wxT("invalid image index"));
+
+
+ if ( IsHiddenRoot(item) )
+ {
+ // no images for hidden root item
+ return;
+ }
+
+ wxTreeItemParam *data = GetItemParam(item);
+ if ( !data )
+ return;
+
+ data->SetImage(image, which);
+
+ RefreshItem(item);
+}
+
+wxTreeItemParam *wxTreeCtrl::GetItemParam(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
+
+ wxTreeViewItem tvItem(item, TVIF_PARAM);
+
+ // hidden root may still have data.
+ if ( IS_VIRTUAL_ROOT(item) )
+ {
+ return GET_VIRTUAL_ROOT()->GetParam();
+ }
+
+ // visible node.
+ if ( !DoGetItem(&tvItem) )
+ {
+ return NULL;
+ }
+
+ return (wxTreeItemParam *)tvItem.lParam;
+}
+
+bool wxTreeCtrl::HandleTreeEvent(wxTreeEvent& event) const
+{
+ if ( event.m_item.IsOk() )
+ {
+ event.SetClientObject(GetItemData(event.m_item));
+ }
+
+ return HandleWindowEvent(event);
+}
+
+wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
+{
+ wxTreeItemParam *data = GetItemParam(item);
+
+ return data ? data->GetData() : NULL;
+}
+
+void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
+{
+ // first, associate this piece of data with this item
+ if ( data )
+ {
+ data->SetId(item);
+ }
+
+ wxTreeItemParam *param = GetItemParam(item);
+
+ wxCHECK_RET( param, wxT("failed to change tree items data") );
+
+ param->SetData(data);
+}
+
+void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
+{
+ wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
+
+ if ( IS_VIRTUAL_ROOT(item) )
+ return;
+
+ wxTreeViewItem tvItem(item, TVIF_CHILDREN);
+ tvItem.cChildren = (int)has;
+ DoSetItem(&tvItem);
+}
+
+void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
+{
+ wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
+
+ if ( IS_VIRTUAL_ROOT(item) )
+ return;
+
+ wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
+ tvItem.state = bold ? TVIS_BOLD : 0;
+ DoSetItem(&tvItem);
+}
+
+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);
+}
+
+void wxTreeCtrl::RefreshItem(const wxTreeItemId& item)
+{
+ if ( IS_VIRTUAL_ROOT(item) )
+ return;
+
+ wxRect rect;
+ if ( GetBoundingRect(item, rect) )
+ {
+ RefreshRect(rect);
+ }
+}
+
+wxColour wxTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
+
+ wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
+ return it == m_attrs.end() ? wxNullColour : it->second->GetTextColour();
+}
+
+wxColour wxTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
+
+ wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
+ return it == m_attrs.end() ? wxNullColour : it->second->GetBackgroundColour();
+}
+
+wxFont wxTreeCtrl::GetItemFont(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") );
+
+ wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
+ return it == m_attrs.end() ? wxNullFont : it->second->GetFont();
+}
+
+void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
+ const wxColour& col)
+{
+ wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
+
+ wxTreeItemAttr *attr;
+ wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
+ if ( it == m_attrs.end() )
+ {
+ m_hasAnyAttr = true;
+
+ m_attrs[item.m_pItem] =
+ attr = new wxTreeItemAttr;
+ }
+ else
+ {
+ attr = it->second;
+ }
+
+ attr->SetTextColour(col);
+
+ RefreshItem(item);
+}
+
+void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
+ const wxColour& col)
+{
+ wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
+
+ wxTreeItemAttr *attr;
+ wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
+ if ( it == m_attrs.end() )
+ {
+ m_hasAnyAttr = true;
+
+ m_attrs[item.m_pItem] =
+ attr = new wxTreeItemAttr;
+ }
+ else // already in the hash
+ {
+ attr = it->second;
+ }
+
+ attr->SetBackgroundColour(col);
+
+ RefreshItem(item);
+}
+
+void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
+{
+ wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
+
+ wxTreeItemAttr *attr;
+ wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
+ if ( it == m_attrs.end() )
+ {
+ m_hasAnyAttr = true;
+
+ m_attrs[item.m_pItem] =
+ attr = new wxTreeItemAttr;
+ }
+ else // already in the hash
+ {
+ attr = it->second;
+ }
+
+ attr->SetFont(font);
+
+ // Reset the item's text to ensure that the bounding rect will be adjusted
+ // for the new font.
+ SetItemText(item, GetItemText(item));
+
+ RefreshItem(item);
+}
+
+// ----------------------------------------------------------------------------
+// Item status
+// ----------------------------------------------------------------------------
+
+bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
+
+ if ( item == wxTreeItemId(TVI_ROOT) )
+ {
+ // virtual (hidden) root is never visible
+ return false;
+ }
+
+ // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
+ TVGetItemRectParam param;
+
+ // true means to get rect for just the text, not the whole line
+ if ( !wxTreeView_GetItemRect(GetHwnd(), HITEM(item), param, TRUE) )
+ {
+ // if TVM_GETITEMRECT returned false, then the item is definitely not
+ // visible (because its parent is not expanded)
+ return false;
+ }
+
+ // however if it returned true, the item might still be outside the
+ // currently visible part of the tree, test for it (notice that partly
+ // visible means visible here)
+ return param.rect.bottom > 0 && param.rect.top < GetClientSize().y;
+}
+
+bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
+
+ if ( IS_VIRTUAL_ROOT(item) )
+ {
+ wxTreeItemIdValue cookie;
+ return GetFirstChild(item, cookie).IsOk();
+ }
+
+ wxTreeViewItem tvItem(item, TVIF_CHILDREN);
+ DoGetItem(&tvItem);
+
+ return tvItem.cChildren != 0;
+}
+
+bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
+
+ wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED);
+ DoGetItem(&tvItem);
+
+ return (tvItem.state & TVIS_EXPANDED) != 0;
+}
+
+bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
+
+ wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED);
+ DoGetItem(&tvItem);
+
+ return (tvItem.state & TVIS_SELECTED) != 0;
+}
+
+bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
+
+ wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
+ DoGetItem(&tvItem);
+
+ return (tvItem.state & TVIS_BOLD) != 0;
+}
+
+// ----------------------------------------------------------------------------
+// navigation
+// ----------------------------------------------------------------------------
+
+wxTreeItemId wxTreeCtrl::GetRootItem() const
+{
+ // Root may be real (visible) or virtual (hidden).
+ if ( GET_VIRTUAL_ROOT() )
+ return TVI_ROOT;
+
+ return wxTreeItemId(TreeView_GetRoot(GetHwnd()));
+}
+
+wxTreeItemId wxTreeCtrl::GetSelection() const
+{
+ wxCHECK_MSG( !HasFlag(wxTR_MULTIPLE), wxTreeItemId(),
+ wxT("this only works with single selection controls") );
+
+ return GetFocusedItem();
+}
+
+wxTreeItemId wxTreeCtrl::GetFocusedItem() const
+{
+ return wxTreeItemId(TreeView_GetSelection(GetHwnd()));
+}
+
+wxTreeItemId wxTreeCtrl::GetItemParent(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree 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(hItem);
+}
+
+wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item,
+ wxTreeItemIdValue& cookie) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
+
+ // remember the last child returned in 'cookie'
+ cookie = TreeView_GetChild(GetHwnd(), HITEM(item));
+
+ return wxTreeItemId(cookie);
+}
+
+wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item),
+ wxTreeItemIdValue& cookie) const
+{
+ wxTreeItemId fromCookie(cookie);
+
+ HTREEITEM hitem = HITEM(fromCookie);
+
+ hitem = TreeView_GetNextSibling(GetHwnd(), hitem);
+
+ wxTreeItemId item(hitem);
+
+ cookie = item.m_pItem;
+
+ return item;
+}
+
+wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
+
+ // can this be done more efficiently?
+ wxTreeItemIdValue cookie;
+
+ wxTreeItemId childLast,
+ child = GetFirstChild(item, cookie);
+ while ( child.IsOk() )
+ {
+ childLast = child;
+ child = GetNextChild(item, cookie);
+ }
+
+ return childLast;
+}
+
+wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
+ return wxTreeItemId(TreeView_GetNextSibling(GetHwnd(), HITEM(item)));
+}
+
+wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
+ return wxTreeItemId(TreeView_GetPrevSibling(GetHwnd(), HITEM(item)));
+}
+
+wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
+{
+ return wxTreeItemId(TreeView_GetFirstVisible(GetHwnd()));
+}
+
+wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
+ wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() for must be visible itself!"));
+
+ wxTreeItemId next(TreeView_GetNextVisible(GetHwnd(), HITEM(item)));
+ if ( next.IsOk() && !IsVisible(next) )
+ {
+ // Win32 considers that any non-collapsed item is visible while we want
+ // to return only really visible items
+ next.Unset();
+ }
+
+ return next;
+}
+
+wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
+ wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() for must be visible itself!"));
+
+ wxTreeItemId prev(TreeView_GetPrevVisible(GetHwnd(), HITEM(item)));
+ if ( prev.IsOk() && !IsVisible(prev) )
+ {
+ // just as above, Win32 function will happily return the previous item
+ // in the tree for the first visible item too
+ prev.Unset();
+ }
+
+ return prev;
+}
+
+// ----------------------------------------------------------------------------
+// multiple selections emulation
+// ----------------------------------------------------------------------------
+
+size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const
+{
+ TraverseSelections selector(this, selections);
+
+ return selector.GetCount();
+}
+
+// ----------------------------------------------------------------------------
+// Usual operations
+// ----------------------------------------------------------------------------
+
+wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent,
+ const wxTreeItemId& hInsertAfter,
+ const wxString& text,
+ int image, int selectedImage,
+ wxTreeItemData *data)
+{
+ wxCHECK_MSG( parent.IsOk() || !TreeView_GetRoot(GetHwnd()),
+ wxTreeItemId(),
+ wxT("can't have more than one root in the tree") );
+
+ TV_INSERTSTRUCT tvIns;
+ tvIns.hParent = HITEM(parent);
+ tvIns.hInsertAfter = HITEM(hInsertAfter);
+
+ // this is how we insert the item as the first child: supply a NULL
+ // hInsertAfter
+ if ( !tvIns.hInsertAfter )
+ {
+ tvIns.hInsertAfter = TVI_FIRST;
+ }
+
+ UINT mask = 0;
+ if ( !text.empty() )
+ {
+ mask |= TVIF_TEXT;
+ tvIns.item.pszText = wxMSW_CONV_LPTSTR(text);
+ }
+ else
+ {
+ tvIns.item.pszText = NULL;
+ tvIns.item.cchTextMax = 0;
+ }
+
+ // create the param which will store the other item parameters
+ wxTreeItemParam *param = new wxTreeItemParam;
+
+ // we return the images on demand as they depend on whether the item is
+ // expanded or collapsed too in our case
+ mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvIns.item.iImage = I_IMAGECALLBACK;
+ tvIns.item.iSelectedImage = I_IMAGECALLBACK;
+
+ param->SetImage(image, wxTreeItemIcon_Normal);
+ param->SetImage(selectedImage, wxTreeItemIcon_Selected);
+
+ mask |= TVIF_PARAM;
+ tvIns.item.lParam = (LPARAM)param;
+ tvIns.item.mask = mask;
+
+ // don't use the hack below for the children of hidden root: this results
+ // in a crash inside comctl32.dll when we call TreeView_GetItemRect()
+ const bool firstChild = !IsHiddenRoot(parent) &&
+ !TreeView_GetChild(GetHwnd(), HITEM(parent));
+
+ HTREEITEM id = TreeView_InsertItem(GetHwnd(), &tvIns);
+ if ( id == 0 )
+ {
+ wxLogLastError(wxT("TreeView_InsertItem"));
+ }
+
+ // apparently some Windows versions (2000 and XP are reported to do this)
+ // sometimes don't refresh the tree after adding the first child and so we
+ // need this to make the "[+]" appear
+ if ( firstChild )
+ {
+ TVGetItemRectParam param;
+
+ wxTreeView_GetItemRect(GetHwnd(), HITEM(parent), param, FALSE);
+ ::InvalidateRect(GetHwnd(), ¶m.rect, FALSE);
+ }
+
+ // associate the application tree item with Win32 tree item handle
+ param->SetItem(id);
+
+ // setup wxTreeItemData
+ if ( data != NULL )
+ {
+ param->SetData(data);
+ data->SetId(id);
+ }
+
+ return wxTreeItemId(id);
+}
+
+wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
+ int image, int selectedImage,
+ wxTreeItemData *data)
+{
+ if ( HasFlag(wxTR_HIDE_ROOT) )
+ {
+ wxASSERT_MSG( !m_pVirtualRoot, wxT("tree can have only a single root") );
+
+ // create a virtual root item, the parent for all the others
+ wxTreeItemParam *param = new wxTreeItemParam;
+ param->SetData(data);
+
+ m_pVirtualRoot = new wxVirtualNode(param);
+
+ return TVI_ROOT;
+ }
+
+ return DoInsertAfter(wxTreeItemId(), wxTreeItemId(),
+ text, image, selectedImage, data);
+}
+
+wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent,
+ size_t index,
+ const wxString& text,
+ int image, int selectedImage,
+ wxTreeItemData *data)
+{
+ wxTreeItemId idPrev;
+ if ( index == (size_t)-1 )
+ {
+ // special value: append to the end
+ idPrev = TVI_LAST;
+ }
+ else // find the item from index
+ {
+ wxTreeItemIdValue cookie;
+ wxTreeItemId idCur = GetFirstChild(parent, cookie);
+ while ( index != 0 && idCur.IsOk() )
+ {
+ index--;
+
+ idPrev = idCur;
+ idCur = GetNextChild(parent, cookie);
+ }
+
+ // assert, not check: if the index is invalid, we will append the item
+ // to the end
+ wxASSERT_MSG( index == 0, wxT("bad index in wxTreeCtrl::InsertItem") );
+ }
+
+ return DoInsertAfter(parent, idPrev, text, image, selectedImage, data);
+}
+
+void wxTreeCtrl::Delete(const wxTreeItemId& item)
+{
+ // unlock tree selections on vista, without this the
+ // tree ctrl will eventually crash after item deletion
+ TreeItemUnlocker unlock_all;
+
+ if ( HasFlag(wxTR_MULTIPLE) )
+ {
+ bool selected = IsSelected(item);
+ wxTreeItemId next;
+
+ if ( selected )
+ {
+ next = TreeView_GetNextVisible(GetHwnd(), HITEM(item));
+
+ if ( !next.IsOk() )
+ {
+ next = TreeView_GetPrevVisible(GetHwnd(), HITEM(item));
+ }
+ }
+
+ {
+ TempSetter set(m_changingSelection);
+ if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
+ {
+ wxLogLastError(wxT("TreeView_DeleteItem"));
+ return;
+ }
+ }
+
+ if ( !selected )
+ {
+ return;
+ }
+
+ if ( item == m_htSelStart )
+ m_htSelStart.Unset();
+
+ if ( item == m_htClickedItem )
+ m_htClickedItem.Unset();
+
+ if ( next.IsOk() )
+ {
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, next);
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, next);
+ (void)HandleTreeEvent(changedEvent);
+ }
+ else
+ {
+ DoUnselectItem(next);
+ ClearFocusedItem();
+ }
+ }
+ }
+ else
+ {
+ if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
+ {
+ wxLogLastError(wxT("TreeView_DeleteItem"));
+ }
+ }
+}
+
+// delete all children (but don't delete the item itself)
+void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
+{
+ // unlock tree selections on vista for the duration of this call
+ TreeItemUnlocker unlock_all;
+
+ wxTreeItemIdValue cookie;
+
+ wxArrayTreeItemIds children;
+ wxTreeItemId child = GetFirstChild(item, cookie);
+ while ( child.IsOk() )
+ {
+ children.Add(child);
+
+ child = GetNextChild(item, cookie);
+ }
+
+ size_t nCount = children.Count();
+ for ( size_t n = 0; n < nCount; n++ )
+ {
+ Delete(children[n]);
+ }
+}
+
+void wxTreeCtrl::DeleteAllItems()
+{
+ // unlock tree selections on vista for the duration of this call
+ TreeItemUnlocker unlock_all;
+
+ // invalidate all the items we store as they're going to become invalid
+ m_htSelStart =
+ m_htClickedItem = wxTreeItemId();
+
+ // delete the "virtual" root item.
+ if ( GET_VIRTUAL_ROOT() )
+ {
+ delete GET_VIRTUAL_ROOT();
+ m_pVirtualRoot = NULL;
+ }
+
+ // and all the real items
+
+ if ( !TreeView_DeleteAllItems(GetHwnd()) )
+ {
+ wxLogLastError(wxT("TreeView_DeleteAllItems"));
+ }
+}
+
+void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag)
+{
+ wxASSERT_MSG( flag == TVE_COLLAPSE ||
+ flag == (TVE_COLLAPSE | TVE_COLLAPSERESET) ||
+ flag == TVE_EXPAND ||
+ flag == TVE_TOGGLE,
+ wxT("Unknown flag in wxTreeCtrl::DoExpand") );
+
+ // A hidden root can be neither expanded nor collapsed.
+ wxCHECK_RET( !IsHiddenRoot(item),
+ 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
+ // compatible behaviour and also in order to not have surprises with the
+ // future versions, don't rely on this and still do everything ourselves.
+ // To avoid that the messages be sent twice when the item is expanded for
+ // the first time we must clear TVIS_EXPANDEDONCE style manually.
+
+ wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDEDONCE);
+ tvItem.state = 0;
+ DoSetItem(&tvItem);
+
+ if ( IsExpanded(item) )
+ {
+ wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_COLLAPSING,
+ this, wxTreeItemId(item));
+
+ if ( !IsTreeEventAllowed(event) )
+ return;
+ }
+
+ if ( TreeView_Expand(GetHwnd(), HITEM(item), flag) )
+ {
+ if ( IsExpanded(item) )
+ return;
+
+ wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_COLLAPSED, this, item);
+ (void)HandleTreeEvent(event);
+ }
+ //else: change didn't took place, so do nothing at all
+}
+
+void wxTreeCtrl::Expand(const wxTreeItemId& item)
+{
+ DoExpand(item, TVE_EXPAND);
+}
+
+void wxTreeCtrl::Collapse(const wxTreeItemId& item)
+{
+ DoExpand(item, TVE_COLLAPSE);
+}
+
+void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
+{
+ DoExpand(item, TVE_COLLAPSE | TVE_COLLAPSERESET);
+}
+
+void wxTreeCtrl::Toggle(const wxTreeItemId& item)
+{
+ DoExpand(item, TVE_TOGGLE);
+}
+
+void wxTreeCtrl::Unselect()
+{
+ wxASSERT_MSG( !HasFlag(wxTR_MULTIPLE),
+ wxT("doesn't make sense, may be you want UnselectAll()?") );
+
+ // the current focus
+ HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
+
+ if ( !htFocus )
+ {
+ return;
+ }
+
+ if ( HasFlag(wxTR_MULTIPLE) )
+ {
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING,
+ this, wxTreeItemId());
+ changingEvent.m_itemOld = htFocus;
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ ClearFocusedItem();
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
+ this, wxTreeItemId());
+ changedEvent.m_itemOld = htFocus;
+ (void)HandleTreeEvent(changedEvent);
+ }
+ }
+ else
+ {
+ ClearFocusedItem();
+ }
+}
+
+void wxTreeCtrl::DoUnselectAll()
+{
+ wxArrayTreeItemIds selections;
+ size_t count = GetSelections(selections);
+
+ for ( size_t n = 0; n < count; n++ )
+ {
+ DoUnselectItem(selections[n]);
+ }
+
+ m_htSelStart.Unset();
+}
+
+void wxTreeCtrl::UnselectAll()
+{
+ if ( HasFlag(wxTR_MULTIPLE) )
+ {
+ HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
+ if ( !htFocus ) return;
+
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this);
+ changingEvent.m_itemOld = htFocus;
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ DoUnselectAll();
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this);
+ changedEvent.m_itemOld = htFocus;
+ (void)HandleTreeEvent(changedEvent);
+ }
+ }
+ else
+ {
+ Unselect();
+ }
+}
+
+void wxTreeCtrl::DoSelectChildren(const wxTreeItemId& parent)
+{
+ DoUnselectAll();
+
+ wxTreeItemIdValue cookie;
+ wxTreeItemId child = GetFirstChild(parent, cookie);
+ while ( child.IsOk() )
+ {
+ DoSelectItem(child, true);
+ child = GetNextChild(child, cookie);
+ }
+}
+
+void wxTreeCtrl::SelectChildren(const wxTreeItemId& parent)
+{
+ wxCHECK_RET( HasFlag(wxTR_MULTIPLE),
+ "this only works with multiple selection controls" );
+
+ HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
+
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this);
+ changingEvent.m_itemOld = htFocus;
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ DoSelectChildren(parent);
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this);
+ changedEvent.m_itemOld = htFocus;
+ (void)HandleTreeEvent(changedEvent);
+ }
+}
+
+void wxTreeCtrl::DoSelectItem(const wxTreeItemId& item, bool select)
+{
+ TempSetter set(m_changingSelection);
+
+ ::SelectItem(GetHwnd(), HITEM(item), select);
+}
+
+void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select)
+{
+ wxCHECK_RET( !IsHiddenRoot(item), wxT("can't select hidden root item") );
+
+ if ( select == IsSelected(item) )
+ {
+ // nothing to do, the item is already in the requested state
+ return;
+ }
+
+ if ( HasFlag(wxTR_MULTIPLE) )
+ {
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
+ DoSelectItem(item, select);
+
+ if ( !htFocus )
+ {
+ SetFocusedItem(item);
+ }
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
+ this, item);
+ (void)HandleTreeEvent(changedEvent);
+ }
+ }
+ else // single selection
+ {
+ wxTreeItemId itemOld, itemNew;
+ if ( select )
+ {
+ itemOld = GetSelection();
+ itemNew = item;
+ }
+ else // deselecting the currently selected item
+ {
+ itemOld = item;
+ // leave itemNew invalid
+ }
+
+ // Recent versions of comctl32.dll send TVN_SELCHANG{ED,ING} events
+ // when we call TreeView_SelectItem() but apparently some old ones did
+ // not so send the events ourselves and ignore those generated by
+ // TreeView_SelectItem() if m_changingSelection is set.
+ wxTreeEvent
+ changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, itemNew);
+ changingEvent.SetOldItem(itemOld);
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ TempSetter set(m_changingSelection);
+
+ if ( !TreeView_SelectItem(GetHwnd(), HITEM(itemNew)) )
+ {
+ wxLogLastError(wxT("TreeView_SelectItem"));
+ }
+ else // ok
+ {
+ ::SetFocus(GetHwnd(), HITEM(item));
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
+ this, itemNew);
+ changedEvent.SetOldItem(itemOld);
+ (void)HandleTreeEvent(changedEvent);
+ }
+ }
+ //else: program vetoed the change
+ }
+}
+
+void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
+{
+ wxCHECK_RET( !IsHiddenRoot(item), wxT("can't show hidden root item") );
+
+ // no error return
+ TreeView_EnsureVisible(GetHwnd(), HITEM(item));
+}
+
+void wxTreeCtrl::ScrollTo(const wxTreeItemId& item)
+{
+ if ( !TreeView_SelectSetFirstVisible(GetHwnd(), HITEM(item)) )
+ {
+ wxLogLastError(wxT("TreeView_SelectSetFirstVisible"));
+ }
+}
+
+wxTextCtrl *wxTreeCtrl::GetEditControl() const
+{
+ return m_textCtrl;
+}
+
+void wxTreeCtrl::DeleteTextCtrl()
+{
+ if ( m_textCtrl )
+ {
+ // the HWND corresponding to this control is deleted by the tree
+ // control itself and we don't know when exactly this happens, so check
+ // if the window still exists before calling UnsubclassWin()
+ if ( !::IsWindow(GetHwndOf(m_textCtrl)) )
+ {
+ m_textCtrl->SetHWND(0);
+ }
+
+ m_textCtrl->UnsubclassWin();
+ m_textCtrl->SetHWND(0);
+ wxDELETE(m_textCtrl);
+
+ m_idEdited.Unset();
+ }
+}
+
+wxTextCtrl *wxTreeCtrl::EditLabel(const wxTreeItemId& item,
+ wxClassInfo *textControlClass)
+{
+ wxASSERT( textControlClass->IsKindOf(wxCLASSINFO(wxTextCtrl)) );
+
+ DeleteTextCtrl();
+
+ m_idEdited = item;
+ 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 )
+ {
+ wxDELETE(m_textCtrl);
+ return NULL;
+ }
+
+ // textctrl is subclassed in MSWOnNotify
+ return m_textCtrl;
+}
+
+// End label editing, optionally cancelling the edit
+void wxTreeCtrl::DoEndEditLabel(bool discardChanges)
+{
+ TreeView_EndEditLabelNow(GetHwnd(), discardChanges);