X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/25f701e6324fe5b1cf4c9b87621183ed4b863f64..dcc3f1a5e3f29c5a885cd9b93208e3e9bbb452c0:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 1f45e5b01e..c33fade6f8 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -106,6 +106,30 @@ 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 // ---------------------------------------------------------------------------- @@ -724,6 +748,7 @@ void wxTreeCtrl::Init() m_pVirtualRoot = NULL; m_dragStarted = false; m_focusLost = true; + m_changingSelection = false; m_triggerStateImageClick = false; // initialize the global array of events now as it can't be done statically @@ -1622,10 +1647,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 ) @@ -1644,8 +1672,8 @@ void wxTreeCtrl::Delete(const wxTreeItemId& item) } else { - ::SelectItem(GetHwnd(), HITEM(next), false); - TreeView_SelectItem(GetHwnd(), 0); + DoUnselectItem(next); + ClearFocusedItem(); } } } @@ -1787,7 +1815,7 @@ void wxTreeCtrl::Unselect() if ( IsTreeEventAllowed(changingEvent) ) { - TreeView_SelectItem(GetHwnd(), 0); + ClearFocusedItem(); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, wxTreeItemId()); @@ -1797,7 +1825,7 @@ void wxTreeCtrl::Unselect() } else { - TreeView_SelectItem(GetHwnd(), 0); + ClearFocusedItem(); } } @@ -1808,7 +1836,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 +1855,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,6 +1867,13 @@ void wxTreeCtrl::UnselectAll() } } +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") ); @@ -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, @@ -1887,7 +1921,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) } else // ok { - ::SetFocus(GetHwnd(), HITEM(item)); + SetFocusedItem(item); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, item); @@ -2029,6 +2063,37 @@ 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) +{ + 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 // ---------------------------------------------------------------------------- @@ -2152,7 +2217,7 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey) if ( IsTreeEventAllowed(changingEvent) ) { - ::ToggleItemSelection(GetHwnd(), htSel); + DoToggleItemSelection(wxTreeItemId(htSel)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, htSel); @@ -2174,7 +2239,7 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey) if ( IsTreeEventAllowed(changingEvent) ) { DoUnselectAll(); - ::SelectItem(GetHwnd(), htSel); + DoSelectItem(wxTreeItemId(htSel)); wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, htSel); @@ -2190,10 +2255,9 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned 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 +2269,6 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned 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 +2283,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned 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 +2325,7 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey) } } - ::SetFocus(GetHwnd(), HITEM(next)); + SetFocusedItem(next); } break; @@ -2291,8 +2347,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned 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 +2382,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned 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 +2442,7 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned 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 +2460,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned 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 +2563,8 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned 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 +2581,40 @@ bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey) return true; } +bool wxTreeCtrl::MSWHandleTreeKeyDownEvent(WXWPARAM wParam, WXLPARAM lParam) +{ + wxTreeEvent keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN, this); + + int keyCode = wxCharCodeMSWToWX(wParam); + + if ( !keyCode ) + { + // wxCharCodeMSWToWX() returns 0 to indicate that this is a + // simple ASCII key + keyCode = wParam; + } + + keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, keyCode, + lParam, wParam); + + 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 @@ -2605,7 +2695,6 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( !isMultiple ) break; - processed = true; m_htClickedItem.Unset(); if ( !(tvht.flags & TVHT_ONITEM) ) @@ -2623,11 +2712,15 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) Collapse(htItem); } } + + processed = true; } + m_focusLost = false; break; } + processed = true; SetFocus(); m_htClickedItem = (WXHTREEITEM) htItem; m_ptClick = wxPoint(x, y); @@ -2647,9 +2740,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 +2797,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 +2831,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 +2840,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 +2851,7 @@ wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } else { - ::SetFocus(GetHwnd(), htItem); + SetFocusedItem(wxTreeItemId(htItem)); } } else // click on a single selected item @@ -2774,11 +2866,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 +2921,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); @@ -2923,8 +3018,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); @@ -3027,39 +3122,37 @@ 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 ( !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 ) { @@ -3276,43 +3369,8 @@ 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; @@ -3352,7 +3410,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 ( !HasFlag(wxTR_MULTIPLE) || !m_changingSelection ) { eventType = wxEVT_COMMAND_TREE_SEL_CHANGED; } @@ -3360,7 +3418,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case TVN_SELCHANGINGA: case TVN_SELCHANGINGW: - if ( !HasFlag(wxTR_MULTIPLE) ) + if ( !HasFlag(wxTR_MULTIPLE) || !m_changingSelection ) { if ( eventType == wxEVT_NULL ) eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;