X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c7d9c476ea807927781813bca979e9f793599229..645f9bd3772a7a4ca3687553e1af53e5a0bb51c1:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 72fa295d37..c00ead4cf3 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -40,12 +40,9 @@ #include "wx/dynlib.h" #include "wx/msw/private.h" -// Set this to 1 to be _absolutely_ sure that repainting will work for all -// comctl32.dll versions -#define wxUSE_COMCTL32_SAFELY 0 - #include "wx/imaglist.h" #include "wx/msw/dragimag.h" +#include "wx/msw/uxtheme.h" // macros to hide the cast ugliness // -------------------------------- @@ -80,7 +77,7 @@ typedef struct tagNMTVITEMCHANGE // The vista tree control includes some new code that originally broke the // multi-selection tree, causing seemingly spurious item selection state changes // during Shift or Ctrl-click item selection. (To witness the original broken -// behavior, simply make IsLocked() below always return false). This problem was +// behaviour, simply make IsLocked() below always return false). This problem was // solved by using the following class to 'unlock' an item's selection state. class TreeItemUnlocker @@ -106,10 +103,60 @@ private: HTREEITEM TreeItemUnlocker::ms_unlockedItem = NULL; +// another helper class: set the variable to true during its lifetime and reset +// it to false when it is destroyed +// +// it is currently always used with wxTreeCtrl::m_changingSelection +class TempSetter +{ +public: + TempSetter(bool& var) : m_var(var) + { + wxASSERT_MSG( !m_var, "variable shouldn't be already set" ); + m_var = true; + } + + ~TempSetter() + { + m_var = false; + } + +private: + bool& m_var; + + wxDECLARE_NO_COPY_CLASS(TempSetter); +}; + // ---------------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------------- +namespace +{ + +// Work around a problem with TreeView_GetItemRect() when using MinGW/Cygwin: +// it results in warnings about breaking strict aliasing rules because HITEM is +// passed via a RECT pointer, so use a union to avoid them and define our own +// version of the standard macro using it. +union TVGetItemRectParam +{ + RECT rect; + HTREEITEM hItem; +}; + +inline bool +wxTreeView_GetItemRect(HWND hwnd, + HTREEITEM hItem, + TVGetItemRectParam& param, + BOOL fItemRect) +{ + param.hItem = hItem; + return ::SendMessage(hwnd, TVM_GETITEMRECT, fItemRect, + (LPARAM)¶m) == TRUE; +} + +} // anonymous namespace + // wrappers for TreeView_GetItem/TreeView_SetItem static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem) { @@ -407,7 +454,7 @@ public: break; default: - wxFAIL_MSG( _T("unsupported wxTreeItemIcon value") ); + wxFAIL_MSG( wxT("unsupported wxTreeItemIcon value") ); } } @@ -577,72 +624,6 @@ private: // wxWin macros // ---------------------------------------------------------------------------- -#if wxUSE_EXTENDED_RTTI -WX_DEFINE_FLAGS( wxTreeCtrlStyle ) - -wxBEGIN_FLAGS( wxTreeCtrlStyle ) - // new style border flags, we put them first to - // use them for streaming out - wxFLAGS_MEMBER(wxBORDER_SIMPLE) - wxFLAGS_MEMBER(wxBORDER_SUNKEN) - wxFLAGS_MEMBER(wxBORDER_DOUBLE) - wxFLAGS_MEMBER(wxBORDER_RAISED) - wxFLAGS_MEMBER(wxBORDER_STATIC) - wxFLAGS_MEMBER(wxBORDER_NONE) - - // old style border flags - wxFLAGS_MEMBER(wxSIMPLE_BORDER) - wxFLAGS_MEMBER(wxSUNKEN_BORDER) - wxFLAGS_MEMBER(wxDOUBLE_BORDER) - wxFLAGS_MEMBER(wxRAISED_BORDER) - wxFLAGS_MEMBER(wxSTATIC_BORDER) - wxFLAGS_MEMBER(wxBORDER) - - // standard window styles - wxFLAGS_MEMBER(wxTAB_TRAVERSAL) - wxFLAGS_MEMBER(wxCLIP_CHILDREN) - wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) - wxFLAGS_MEMBER(wxWANTS_CHARS) - wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) - wxFLAGS_MEMBER(wxALWAYS_SHOW_SB ) - wxFLAGS_MEMBER(wxVSCROLL) - wxFLAGS_MEMBER(wxHSCROLL) - - wxFLAGS_MEMBER(wxTR_EDIT_LABELS) - wxFLAGS_MEMBER(wxTR_NO_BUTTONS) - wxFLAGS_MEMBER(wxTR_HAS_BUTTONS) - wxFLAGS_MEMBER(wxTR_TWIST_BUTTONS) - wxFLAGS_MEMBER(wxTR_NO_LINES) - wxFLAGS_MEMBER(wxTR_FULL_ROW_HIGHLIGHT) - wxFLAGS_MEMBER(wxTR_LINES_AT_ROOT) - wxFLAGS_MEMBER(wxTR_HIDE_ROOT) - wxFLAGS_MEMBER(wxTR_ROW_LINES) - wxFLAGS_MEMBER(wxTR_HAS_VARIABLE_ROW_HEIGHT) - wxFLAGS_MEMBER(wxTR_SINGLE) - wxFLAGS_MEMBER(wxTR_MULTIPLE) -#if WXWIN_COMPATIBILITY_2_8 - wxFLAGS_MEMBER(wxTR_EXTENDED) -#endif - wxFLAGS_MEMBER(wxTR_DEFAULT_STYLE) - -wxEND_FLAGS( wxTreeCtrlStyle ) - -IMPLEMENT_DYNAMIC_CLASS_XTI(wxTreeCtrl, wxControl,"wx/treectrl.h") - -wxBEGIN_PROPERTIES_TABLE(wxTreeCtrl) - wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent ) - wxEVENT_RANGE_PROPERTY( TreeEvent , wxEVT_COMMAND_TREE_BEGIN_DRAG , wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK , wxTreeEvent ) - wxPROPERTY_FLAGS( WindowStyle , wxTreeCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style -wxEND_PROPERTIES_TABLE() - -wxBEGIN_HANDLERS_TABLE(wxTreeCtrl) -wxEND_HANDLERS_TABLE() - -wxCONSTRUCTOR_5( wxTreeCtrl , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle ) -#else -IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl) -#endif - // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -724,7 +705,9 @@ void wxTreeCtrl::Init() m_pVirtualRoot = NULL; m_dragStarted = false; m_focusLost = true; + m_changingSelection = false; m_triggerStateImageClick = false; + m_mouseUpDeselect = false; // 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 @@ -780,25 +763,25 @@ bool wxTreeCtrl::Create(wxWindow *parent, if ( !MSWCreateControl(WC_TREEVIEW, wstyle, pos, size) ) return false; -#if wxUSE_COMCTL32_SAFELY - wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - wxWindow::SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); -#elif 1 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); -#else - // This works around a bug in the Windows tree control whereby for some versions - // of comctrl32, setting any colour actually draws the background in black. - // This will initialise the background to the system colour. - // THIS FIX NOW REVERTED since it caused problems on _other_ systems. - // Assume the user has an updated comctl32.dll. - ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0,-1); - wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); -#endif wxSetCCUnicodeFormat(GetHwnd()); + if ( m_windowStyle & wxTR_TWIST_BUTTONS ) + { + // Under Vista and later Explorer uses rotating ("twist") buttons + // instead of the default "+/-" ones so apply its theme to the tree + // control to implement this style. + if ( wxGetWinVersion() >= wxWinVersion_Vista ) + { + if ( wxUxThemeEngine *theme = wxUxThemeEngine::GetIfActive() ) + { + theme->SetWindowTheme(GetHwnd(), L"EXPLORER", NULL); + } + } + } + return true; } @@ -841,7 +824,7 @@ wxTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant) bool wxTreeCtrl::DoGetItem(wxTreeViewItem *tvItem) const { wxCHECK_MSG( tvItem->hItem != TVI_ROOT, false, - _T("can't retrieve virtual root item") ); + wxT("can't retrieve virtual root item") ); if ( !TreeView_GetItem(GetHwnd(), tvItem) ) { @@ -917,24 +900,20 @@ size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool wxTreeCtrl::SetBackgroundColour(const wxColour &colour) { -#if !wxUSE_COMCTL32_SAFELY if ( !wxWindowBase::SetBackgroundColour(colour) ) return false; ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel()); -#endif return true; } bool wxTreeCtrl::SetForegroundColour(const wxColour &colour) { -#if !wxUSE_COMCTL32_SAFELY if ( !wxWindowBase::SetForegroundColour(colour) ) return false; ::SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel()); -#endif return true; } @@ -1245,14 +1224,10 @@ bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const } // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect - RECT rect; - - // this ugliness comes directly from MSDN - it *is* the correct way to pass - // the HTREEITEM with TVM_GETITEMRECT - *(HTREEITEM *)&rect = HITEM(item); + TVGetItemRectParam param; // true means to get rect for just the text, not the whole line - if ( !::SendMessage(GetHwnd(), TVM_GETITEMRECT, true, (LPARAM)&rect) ) + 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) @@ -1262,7 +1237,7 @@ bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const // 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 rect.bottom > 0 && rect.top < GetClientSize().y; + return param.rect.bottom > 0 && param.rect.top < GetClientSize().y; } bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const @@ -1329,6 +1304,11 @@ 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())); } @@ -1473,7 +1453,7 @@ wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent, { wxCHECK_MSG( parent.IsOk() || !TreeView_GetRoot(GetHwnd()), wxTreeItemId(), - _T("can't have more than one root in the tree") ); + wxT("can't have more than one root in the tree") ); TV_INSERTSTRUCT tvIns; tvIns.hParent = HITEM(parent); @@ -1530,9 +1510,10 @@ wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent, // need this to make the "[+]" appear if ( firstChild ) { - RECT rect; - TreeView_GetItemRect(GetHwnd(), HITEM(parent), &rect, FALSE); - ::InvalidateRect(GetHwnd(), &rect, FALSE); + TVGetItemRectParam param; + + wxTreeView_GetItemRect(GetHwnd(), HITEM(parent), param, FALSE); + ::InvalidateRect(GetHwnd(), ¶m.rect, FALSE); } // associate the application tree item with Win32 tree item handle @@ -1554,7 +1535,7 @@ wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text, { if ( HasFlag(wxTR_HIDE_ROOT) ) { - wxASSERT_MSG( !m_pVirtualRoot, _T("tree can have only a single 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; @@ -1595,7 +1576,7 @@ wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent, // assert, not check: if the index is invalid, we will append the item // to the end - wxASSERT_MSG( index == 0, _T("bad index in wxTreeCtrl::InsertItem") ); + wxASSERT_MSG( index == 0, wxT("bad index in wxTreeCtrl::InsertItem") ); } return DoInsertAfter(parent, idPrev, text, image, selectedImage, data); @@ -1622,10 +1603,13 @@ void wxTreeCtrl::Delete(const wxTreeItemId& item) } } - if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) ) { - wxLogLastError(wxT("TreeView_DeleteItem")); - return; + TempSetter set(m_changingSelection); + if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) ) + { + wxLogLastError(wxT("TreeView_DeleteItem")); + return; + } } if ( !selected ) @@ -1633,6 +1617,12 @@ void wxTreeCtrl::Delete(const wxTreeItemId& item) 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); @@ -1644,8 +1634,8 @@ void wxTreeCtrl::Delete(const wxTreeItemId& item) } else { - ::SelectItem(GetHwnd(), HITEM(next), false); - TreeView_SelectItem(GetHwnd(), 0); + DoUnselectItem(next); + ClearFocusedItem(); } } } @@ -1687,6 +1677,10 @@ 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() ) { @@ -1787,7 +1781,7 @@ void wxTreeCtrl::Unselect() if ( IsTreeEventAllowed(changingEvent) ) { - TreeView_SelectItem(GetHwnd(), 0); + ClearFocusedItem(); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, wxTreeItemId()); @@ -1797,7 +1791,7 @@ void wxTreeCtrl::Unselect() } else { - TreeView_SelectItem(GetHwnd(), 0); + ClearFocusedItem(); } } @@ -1808,7 +1802,7 @@ void wxTreeCtrl::DoUnselectAll() for ( size_t n = 0; n < count; n++ ) { - ::UnselectItem(GetHwnd(), HITEM(selections[n])); + DoUnselectItem(selections[n]); } m_htSelStart.Unset(); @@ -1827,7 +1821,6 @@ void wxTreeCtrl::UnselectAll() if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - TreeView_SelectItem(GetHwnd(), 0); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this); changedEvent.m_itemOld = htFocus; @@ -1840,12 +1833,53 @@ void wxTreeCtrl::UnselectAll() } } +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), _T("can't select hidden root item") ); + wxCHECK_RET( !IsHiddenRoot(item), wxT("can't select hidden root item") ); - if ( IsSelected(item) == select ) + if ( select == IsSelected(item) ) { + // nothing to do, the item is already in the requested state return; } @@ -1856,11 +1890,11 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) if ( IsTreeEventAllowed(changingEvent) ) { HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd()); - ::SelectItem(GetHwnd(), HITEM(item), select); + DoSelectItem(item, select); if ( !htFocus ) { - ::SetFocus(GetHwnd(), HITEM(item)); + SetFocusedItem(item); } wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, @@ -1868,20 +1902,33 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) (void)HandleTreeEvent(changedEvent); } } - else + else // single selection { - wxASSERT_MSG( select, - _T("SelectItem(false) works only for multiselect") ); - - // in spite 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 + wxTreeItemId itemOld, itemNew; + if ( select ) + { + itemOld = GetSelection(); + itemNew = item; + } + else // deselecting the currently selected item + { + itemOld = item; + // leave itemNew invalid + } - wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item); + // 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) ) { - if ( !TreeView_SelectItem(GetHwnd(), HITEM(item)) ) + TempSetter set(m_changingSelection); + + if ( !TreeView_SelectItem(GetHwnd(), HITEM(itemNew)) ) { wxLogLastError(wxT("TreeView_SelectItem")); } @@ -1890,7 +1937,8 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) ::SetFocus(GetHwnd(), HITEM(item)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, - this, item); + this, itemNew); + changedEvent.SetOldItem(itemOld); (void)HandleTreeEvent(changedEvent); } } @@ -1900,7 +1948,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item) { - wxCHECK_RET( !IsHiddenRoot(item), _T("can't show hidden root item") ); + wxCHECK_RET( !IsHiddenRoot(item), wxT("can't show hidden root item") ); // no error return TreeView_EnsureVisible(GetHwnd(), HITEM(item)); @@ -1933,8 +1981,7 @@ void wxTreeCtrl::DeleteTextCtrl() m_textCtrl->UnsubclassWin(); m_textCtrl->SetHWND(0); - delete m_textCtrl; - m_textCtrl = NULL; + wxDELETE(m_textCtrl); m_idEdited.Unset(); } @@ -1955,8 +2002,7 @@ wxTextCtrl *wxTreeCtrl::EditLabel(const wxTreeItemId& item, // returned false if ( !hWnd ) { - delete m_textCtrl; - m_textCtrl = NULL; + wxDELETE(m_textCtrl); return NULL; } @@ -2007,18 +2053,18 @@ bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId& item, wxRect& rect, bool textOnly) const { - RECT rc; - // Virtual root items have no bounding rectangle if ( IS_VIRTUAL_ROOT(item) ) { return false; } - if ( TreeView_GetItemRect(GetHwnd(), HITEM(item), - &rc, textOnly) ) + TVGetItemRectParam param; + + if ( wxTreeView_GetItemRect(GetHwnd(), HITEM(item), param, textOnly) ) { - rect = wxRect(wxPoint(rc.left, rc.top), wxPoint(rc.right, rc.bottom)); + rect = wxRect(wxPoint(param.rect.left, param.rect.top), + wxPoint(param.rect.right, param.rect.bottom)); return true; } @@ -2029,6 +2075,39 @@ bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId& item, } } +void wxTreeCtrl::ClearFocusedItem() +{ + TempSetter set(m_changingSelection); + + if ( !TreeView_SelectItem(GetHwnd(), 0) ) + { + wxLogLastError(wxT("TreeView_SelectItem")); + } +} + +void wxTreeCtrl::SetFocusedItem(const wxTreeItemId& item) +{ + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + + TempSetter set(m_changingSelection); + + ::SetFocus(GetHwnd(), HITEM(item)); +} + +void wxTreeCtrl::DoUnselectItem(const wxTreeItemId& item) +{ + TempSetter set(m_changingSelection); + + ::UnselectItem(GetHwnd(), HITEM(item)); +} + +void wxTreeCtrl::DoToggleItemSelection(const wxTreeItemId& item) +{ + TempSetter set(m_changingSelection); + + ::ToggleItemSelection(GetHwnd(), HITEM(item)); +} + // ---------------------------------------------------------------------------- // sorting stuff // ---------------------------------------------------------------------------- @@ -2131,7 +2210,16 @@ bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id_) return true; } -bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) +bool wxTreeCtrl::MSWIsOnItem(unsigned flags) const +{ + unsigned mask = TVHT_ONITEM; + if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) ) + mask |= TVHT_ONITEMINDENT | TVHT_ONITEMRIGHT; + + return (flags & mask) != 0; +} + +bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey) { const bool bCtrl = wxIsCtrlDown(); const bool bShift = wxIsShiftDown(); @@ -2152,7 +2240,7 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) if ( IsTreeEventAllowed(changingEvent) ) { - ::ToggleItemSelection(GetHwnd(), htSel); + DoToggleItemSelection(wxTreeItemId(htSel)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, htSel); @@ -2174,7 +2262,7 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), htSel); + DoSelectItem(wxTreeItemId(htSel)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, htSel); @@ -2190,10 +2278,9 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) if ( !bCtrl && !bShift ) { wxArrayTreeItemIds selections; - size_t count = GetSelections(selections); wxTreeItemId next; - if ( htSel && count > 0 ) + if ( htSel ) { next = vkey == VK_UP ? TreeView_GetPrevVisible(GetHwnd(), htSel) @@ -2205,14 +2292,6 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) if ( IsHiddenRoot(next) ) next = TreeView_GetChild(GetHwnd(), HITEM(next)); - - if ( vkey == VK_DOWN ) - { - wxTreeItemId next2 = TreeView_GetNextVisible( - GetHwnd(), HITEM(next)); - if ( next2 ) - next = next2; - } } if ( !next.IsOk() ) @@ -2227,8 +2306,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), HITEM(next)); - ::SetFocus(GetHwnd(), HITEM(next)); + DoSelectItem(next); + SetFocusedItem(next); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, next); @@ -2269,7 +2348,7 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) } } - ::SetFocus(GetHwnd(), HITEM(next)); + SetFocusedItem(next); } break; @@ -2291,8 +2370,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), HITEM(next)); - ::SetFocus(GetHwnd(), HITEM(next)); + DoSelectItem(next); + SetFocusedItem(next); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, next); @@ -2326,8 +2405,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), HITEM(next)); - ::SetFocus(GetHwnd(), HITEM(next)); + DoSelectItem(next); + SetFocusedItem(next); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, next); changedEvent.m_itemOld = htSel; @@ -2386,7 +2465,7 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) SelectRange(GetHwnd(), HITEM(m_htSelStart), HITEM(next), SR_UNSELECT_OTHERS); - ::SetFocus(GetHwnd(), HITEM(next)); + SetFocusedItem(next); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, next); @@ -2404,8 +2483,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), HITEM(next)); - ::SetFocus(GetHwnd(), HITEM(next)); + DoSelectItem(next); + SetFocusedItem(next); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, next); @@ -2507,8 +2586,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) { DoUnselectAll(); m_htSelStart.Unset(); - ::SelectItem(GetHwnd(), HITEM(next)); - ::SetFocus(GetHwnd(), HITEM(next)); + DoSelectItem(next); + SetFocusedItem(next); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, next); @@ -2525,6 +2604,29 @@ bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey) return true; } +bool wxTreeCtrl::MSWHandleTreeKeyDownEvent(WXWPARAM wParam, WXLPARAM lParam) +{ + wxTreeEvent keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN, this); + keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam); + + bool processed = HandleTreeEvent(keyEvent); + + // generate a separate event for Space/Return + if ( !wxIsCtrlDown() && !wxIsShiftDown() && !wxIsAltDown() && + ((wParam == VK_SPACE) || (wParam == VK_RETURN)) ) + { + const HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd()); + if ( htSel ) + { + wxTreeEvent activatedEvent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, + this, htSel); + (void)HandleTreeEvent(activatedEvent); + } + } + + return processed; +} + // we hook into WndProc to process WM_MOUSEMOVE/WM_BUTTONUP messages - as we // only do it during dragging, minimize wxWin overhead (this is important for // WM_MOUSEMOVE as they're a lot of them) by catching Windows messages directly @@ -2577,13 +2679,16 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } // create the event - wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_MENU, this, item); + if ( item.IsOk() ) + { + wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_MENU, this, item); - event.m_pointDrag = pt; + event.m_pointDrag = pt; - if ( HandleTreeEvent(event) ) - processed = true; - //else: continue with generating wxEVT_CONTEXT_MENU in base class code + if ( HandleTreeEvent(event) ) + processed = true; + //else: continue with generating wxEVT_CONTEXT_MENU in base class code + } } else if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) ) { @@ -2605,14 +2710,21 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( !isMultiple ) break; - processed = true; m_htClickedItem.Unset(); - if ( !(tvht.flags & TVHT_ONITEM) ) + if ( !MSWIsOnItem(tvht.flags) ) { - if ( !HandleMouseEvent(nMsg, x, y, wParam) ) + if ( tvht.flags & TVHT_ONITEMBUTTON ) { - if ( tvht.flags & TVHT_ONITEMBUTTON ) + // either it's going to be handled by user code or + // we're going to use it ourselves to toggle the + // branch, in either case don't pass it to the base + // class which would generate another mouse click event + // for it even though it's already handled here + processed = true; + SetFocus(); + + if ( !HandleMouseEvent(nMsg, x, y, wParam) ) { if ( !IsExpanded(htItem) ) { @@ -2625,9 +2737,11 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } } + m_focusLost = false; break; } + processed = true; SetFocus(); m_htClickedItem = (WXHTREEITEM) htItem; m_ptClick = wxPoint(x, y); @@ -2647,9 +2761,9 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( IsTreeEventAllowed(changingEvent) ) { // toggle selected state - ::ToggleItemSelection(GetHwnd(), htItem); + DoToggleItemSelection(wxTreeItemId(htItem)); - ::SetFocus(GetHwnd(), htItem); + SetFocusedItem(wxTreeItemId(htItem)); // reset on any click without Shift m_htSelStart.Unset(); @@ -2704,10 +2818,10 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } else { - ::SelectItem(GetHwnd(), htItem); + DoSelectItem(wxTreeItemId(htItem)); } - ::SetFocus(GetHwnd(), htItem); + SetFocusedItem(wxTreeItemId(htItem)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, htItem); @@ -2738,7 +2852,6 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // clicked outside of the present selection, otherwise, // perform the deselection on mouse-up, this allows // multiple drag and drop to work. - if ( !IsItemSelected(GetHwnd(), htItem)) { wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, @@ -2748,8 +2861,8 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), htItem); - ::SetFocus(GetHwnd(), htItem); + DoSelectItem(wxTreeItemId(htItem)); + SetFocusedItem(wxTreeItemId(htItem)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, htItem); @@ -2759,7 +2872,8 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } else { - ::SetFocus(GetHwnd(), htItem); + SetFocusedItem(wxTreeItemId(htItem)); + m_mouseUpDeselect = true; } } else // click on a single selected item @@ -2774,11 +2888,14 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // since previous click if ( m_focusLost ) { - TreeView_SelectItem(GetHwnd(), 0); - ::SelectItem(GetHwnd(), htItem); + ClearFocusedItem(); + DoSelectItem(wxTreeItemId(htItem)); + SetFocusedItem(wxTreeItemId(htItem)); + } + else + { + processed = false; } - - processed = false; } // reset on any click without Shift @@ -2826,8 +2943,8 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), htItem); - ::SetFocus(GetHwnd(), htItem); + DoSelectItem(wxTreeItemId(htItem)); + SetFocusedItem(wxTreeItemId(htItem)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, htItem); @@ -2906,16 +3023,13 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) case WM_LBUTTONUP: if ( isMultiple ) { - // deselect other items if multiple items selected + // deselect other items if needed if ( htItem ) { - wxArrayTreeItemIds selections; - size_t count = GetSelections(selections); - - if ( count > 1 && - !(wParam & MK_CONTROL) && - !(wParam & MK_SHIFT) ) + if ( m_mouseUpDeselect ) { + m_mouseUpDeselect = false; + wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, htItem); changingEvent.m_itemOld = htOldItem; @@ -2923,8 +3037,8 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), htItem); - ::SetFocus(GetHwnd(), htItem); + DoSelectItem(wxTreeItemId(htItem)); + SetFocusedItem(wxTreeItemId(htItem)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, htItem); @@ -2949,10 +3063,7 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } } - if ( !m_dragStarted && - (tvht.flags & TVHT_ONITEMSTATEICON || - tvht.flags & TVHT_ONITEMICON || - tvht.flags & TVHT_ONITEM) ) + if ( !m_dragStarted && MSWIsOnItem(tvht.flags) ) { processed = true; } @@ -2965,8 +3076,7 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( m_dragImage ) { m_dragImage->EndDrag(); - delete m_dragImage; - m_dragImage = NULL; + wxDELETE(m_dragImage); // generate the drag end event wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, @@ -3006,16 +3116,16 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // do it for the other items itself - help it wxArrayTreeItemIds selections; size_t count = GetSelections(selections); - RECT rect; + TVGetItemRectParam param; for ( size_t n = 0; n < count; n++ ) { // TreeView_GetItemRect() will return false if item is not // visible, which may happen perfectly well - if ( TreeView_GetItemRect(GetHwnd(), HITEM(selections[n]), - &rect, TRUE) ) + if ( wxTreeView_GetItemRect(GetHwnd(), HITEM(selections[n]), + param, TRUE) ) { - ::InvalidateRect(GetHwnd(), &rect, FALSE); + ::InvalidateRect(GetHwnd(), ¶m.rect, FALSE); } } } @@ -3027,39 +3137,38 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } else if ( (nMsg == WM_KEYDOWN || nMsg == WM_SYSKEYDOWN) && isMultiple ) { - wxTreeEvent keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN, this); - - int keyCode = wxCharCodeMSWToWX(wParam); - - if ( !keyCode ) + // normally we want to generate wxEVT_KEY_DOWN events from TVN_KEYDOWN + // notification but for the keys which can be used to change selection + // we need to do it from here so as to not apply the default behaviour + // if the events are handled by the user code + switch ( wParam ) { - // wxCharCodeMSWToWX() returns 0 to indicate that this is a - // simple ASCII key - keyCode = wParam; - } + case VK_RETURN: + case VK_SPACE: + case VK_UP: + case VK_DOWN: + case VK_LEFT: + case VK_RIGHT: + case VK_HOME: + case VK_END: + case VK_PRIOR: + case VK_NEXT: + if ( !HandleKeyDown(wParam, lParam) && + !MSWHandleTreeKeyDownEvent(wParam, lParam) ) + { + // use the key to update the selection if it was left + // unprocessed + MSWHandleSelectionKey(wParam); + } - keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, keyCode, - lParam, wParam); + // pretend that we did process it in any case as we already + // generated an event for it + processed = true; - processed = HandleTreeEvent(keyEvent); - if ( !processed ) - { - // update the selection if key was left unprocessed - processed = MSWHandleSelectionKey(wParam); + //default: for all the other keys leave processed as false so that + // the tree control generates a TVN_KEYDOWN for us } - // generate a separate event for Space/Return - if ( !wxIsCtrlDown() && !wxIsShiftDown() && !wxIsAltDown() && - ((wParam == VK_SPACE) || (wParam == VK_RETURN)) ) - { - const HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd()); - if ( htSel ) - { - wxTreeEvent activatedEvent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, - this, htSel); - (void)HandleTreeEvent(activatedEvent); - } - } } else if ( nMsg == WM_COMMAND ) { @@ -3107,8 +3216,7 @@ wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( m_dragImage ) { m_dragImage->EndDrag(); - delete m_dragImage; - m_dragImage = NULL; + wxDELETE(m_dragImage); // if we don't do it, the tree seems to think that 2 items // are selected simultaneously which is quite weird @@ -3276,45 +3384,9 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // fabricate the lParam and wParam parameters sufficiently // similar to the ones from a "real" WM_KEYDOWN so that // CreateKeyEvent() works correctly - const bool isAltDown = ::GetKeyState(VK_MENU) < 0; - WXLPARAM lParam = (isAltDown ? KF_ALTDOWN : 0) << 16; - - WXWPARAM wParam = info->wVKey; - - int keyCode = wxCharCodeMSWToWX(wParam); - if ( !keyCode ) - { - // wxCharCodeMSWToWX() returns 0 to indicate that this is a - // simple ASCII key - keyCode = wParam; - } - - wxTreeEvent keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN, this); - keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, - keyCode, - lParam, - wParam); - - if ( HandleTreeEvent(keyEvent) ) - { - return true; - } - - wxTreeItemId item = wxTreeItemId(TreeView_GetSelection(GetHwnd())); - - // a separate event for Space/Return - if ( !wxIsCtrlDown() && !wxIsShiftDown() && !isAltDown && - ((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) && - item ) - { - wxTreeEvent activatedEvent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, - this, item); - (void)HandleTreeEvent(activatedEvent); - } - - return false; + return MSWHandleTreeKeyDownEvent( + info->wVKey, (wxIsAltDown() ? KF_ALTDOWN : 0) << 16); } - break; // Vista's tree control has introduced some problems with our @@ -3322,7 +3394,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // the wrong items are deselected. // Fortunately, Vista provides a new notification, TVN_ITEMCHANGING - // that can be used to regulate this incorrect behavior. The + // that can be used to regulate this incorrect behaviour. The // following messages will allow only the unlocked item's selection // state to change @@ -3352,7 +3424,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // we have to handle both messages: case TVN_SELCHANGEDA: case TVN_SELCHANGEDW: - if ( !HasFlag(wxTR_MULTIPLE) ) + if ( !m_changingSelection ) { eventType = wxEVT_COMMAND_TREE_SEL_CHANGED; } @@ -3360,7 +3432,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case TVN_SELCHANGINGA: case TVN_SELCHANGINGW: - if ( !HasFlag(wxTR_MULTIPLE) ) + if ( !m_changingSelection ) { if ( eventType == wxEVT_NULL ) eventType = wxEVT_COMMAND_TREE_SEL_CHANGING; @@ -3392,12 +3464,13 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // // to avoid such surprises, we force the generation of focus events // now, before we generate the selection change ones - SetFocus(); + if ( !m_changingSelection ) + SetFocus(); break; // instead of explicitly checking for _WIN32_IE, check if the // required symbols are available in the headers -#if defined(CDDS_PREPAINT) && !wxUSE_COMCTL32_SAFELY +#if defined(CDDS_PREPAINT) case NM_CUSTOMDRAW: { LPNMTVCUSTOMDRAW lptvcd = (LPNMTVCUSTOMDRAW)lParam; @@ -3424,7 +3497,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) if ( !loaded ) { - wxLoadedDLL dllComCtl32(_T("comctl32.dll")); + wxLoadedDLL dllComCtl32(wxT("comctl32.dll")); if ( dllComCtl32.IsLoaded() ) wxDL_INIT_FUNC(s_pfn, ImageList_Copy, dllComCtl32); } @@ -3572,7 +3645,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) ::ScreenToClient(GetHwnd(), &tvhti.pt); if ( TreeView_HitTest(GetHwnd(), &tvhti) ) { - if ( tvhti.flags & TVHT_ONITEM ) + if ( MSWIsOnItem(tvhti.flags) ) { event.m_item = tvhti.hItem; eventType = (int)hdr->code == NM_DBLCLK @@ -3626,7 +3699,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { // normally this is impossible because the m_dragImage is // deleted once the drag operation is over - wxASSERT_MSG( !m_dragImage, _T("starting to drag once again?") ); + wxASSERT_MSG( !m_dragImage, wxT("starting to drag once again?") ); m_dragImage = new wxDragImage(*this, event.m_item); m_dragImage->BeginDrag(wxPoint(0,0), this);