X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e0bf68d697a37d75ac9439745a3a2ac55bd25ddf..2bfca191bf2261e385594a369da9aa4e4441aa43:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index d705b7e6a8..3093583d52 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -72,19 +72,38 @@ typedef struct tagNMTVITEMCHANGE #endif -// this global variable is used on vista systems for preventing unwanted -// item state changes in the vista tree control. It is only used in +// this helper class is used on vista systems for preventing unwanted +// item state changes in the vista tree control. It is only effective in // multi-select mode on vista systems. -static HTREEITEM gs_unlockItem = NULL; +// 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 +// solved by using the following class to 'unlock' an item's selection state. class TreeItemUnlocker { public: - TreeItemUnlocker(HTREEITEM item) { gs_unlockItem = item; } - ~TreeItemUnlocker() { gs_unlockItem = NULL; } + // unlock a single item + TreeItemUnlocker(HTREEITEM item) { ms_unlockedItem = item; } + + // unlock all items, don't use unless absolutely necessary + TreeItemUnlocker() { ms_unlockedItem = (HTREEITEM)-1; } + + // lock everything back + ~TreeItemUnlocker() { ms_unlockedItem = NULL; } + + + // check if the item state is currently locked + static bool IsLocked(HTREEITEM item) + { return ms_unlockedItem != (HTREEITEM)-1 && item != ms_unlockedItem; } + +private: + static HTREEITEM ms_unlockedItem; }; +HTREEITEM TreeItemUnlocker::ms_unlockedItem = NULL; // ---------------------------------------------------------------------------- // private functions @@ -1512,12 +1531,27 @@ wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent, 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 ) + { + RECT rect; + TreeView_GetItemRect(GetHwnd(), HITEM(parent), &rect, FALSE); + ::InvalidateRect(GetHwnd(), &rect, FALSE); + } + // associate the application tree item with Win32 tree item handle param->SetItem(id); @@ -1586,6 +1620,10 @@ wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent, 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 ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) ) { wxLogLastError(wxT("TreeView_DeleteItem")); @@ -1595,6 +1633,9 @@ void wxTreeCtrl::Delete(const wxTreeItemId& item) // 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; @@ -1618,6 +1659,9 @@ void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item) void wxTreeCtrl::DeleteAllItems() { + // unlock tree selections on vista for the duration of this call + TreeItemUnlocker unlock_all; + // delete the "virtual" root item. if ( GET_VIRTUAL_ROOT() ) { @@ -1665,7 +1709,7 @@ void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag) : IDX_COLLAPSE] [IDX_DONE], this, item); - (void)GetEventHandler()->ProcessEvent(event); + (void)HandleWindowEvent(event); } //else: change didn't took place, so do nothing at all } @@ -1727,7 +1771,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) _T("SelectItem(false) works only for multiselect") ); wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item); - if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() ) + if ( !HandleWindowEvent(event) || event.IsAllowed() ) { if ( HasFlag(wxTR_MULTIPLE) ) { @@ -1748,7 +1792,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) } event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); - (void)GetEventHandler()->ProcessEvent(event); + (void)HandleWindowEvent(event); } //else: program vetoed the change } @@ -1948,7 +1992,9 @@ bool wxTreeCtrl::MSWShouldPreProcessMessage(WXMSG* msg) { if ( msg->message == WM_KEYDOWN ) { - if ( msg->wParam == VK_RETURN ) + // Only eat VK_RETURN if not being used by the application in + // conjunction with modifiers + if ( (msg->wParam == VK_RETURN) && !wxIsAnyModifierDown() ) { // we need VK_RETURN to generate wxEVT_COMMAND_TREE_ITEM_ACTIVATED return false; @@ -2039,7 +2085,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara event.m_pointDrag = pt; - if ( GetEventHandler()->ProcessEvent(event) ) + if ( HandleWindowEvent(event) ) processed = true; //else: continue with generating wxEVT_CONTEXT_MENU in base class code } @@ -2164,7 +2210,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara // fire EVT_RIGHT_DOWN HandleMouseEvent(nMsg, x, y, wParam); - + // send NM_RCLICK NMHDR nmhdr; nmhdr.hwndFrom = GetHwnd(); @@ -2174,7 +2220,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara nmhdr.idFrom, (LPARAM)&nmhdr); // prevent tree control default processing, as we've - // already done everything + // already done everything processed = true; } break; @@ -2278,7 +2324,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, this, htItem); event.m_pointDrag = wxPoint(x, y); - (void)GetEventHandler()->ProcessEvent(event); + (void)HandleWindowEvent(event); // if we don't do it, the tree seems to think that 2 items // are selected simultaneously which is quite weird @@ -2603,8 +2649,7 @@ 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; + WXLPARAM lParam = (wxIsAltDown() ? KF_ALTDOWN : 0) << 16; WXWPARAM wParam = info->wVKey; @@ -2622,7 +2667,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wParam); // a separate event for Space/Return - if ( !wxIsCtrlDown() && !wxIsShiftDown() && !isAltDown && + if ( !wxIsAnyModifierDown() && ((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) ) { wxTreeItemId item; @@ -2631,7 +2676,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, this, item); - (void)GetEventHandler()->ProcessEvent(event2); + (void)HandleWindowEvent(event2); } } break; @@ -2640,11 +2685,12 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // Vista's tree control has introduced some problems with our // multi-selection tree. When TreeView_SelectItem() is called, // the wrong items are deselected. - + // Fortunately, Vista provides a new notification, TVN_ITEMCHANGING // that can be used to regulate this incorrect behavior. The // following messages will allow only the unlocked item's selection // state to change + case TVN_ITEMCHANGINGA: case TVN_ITEMCHANGINGW: { @@ -2653,11 +2699,11 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { // get info about the item about to be changed NMTVITEMCHANGE* info = (NMTVITEMCHANGE*)lParam; - if (info->hItem != gs_unlockItem) + if (TreeItemUnlocker::IsLocked(info->hItem)) { // item's state is locked, don't allow the change // returning 1 will disallow the change - *result = 1; + *result = 1; return true; } } @@ -2848,7 +2894,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) if ( event.m_item.IsOk() ) event.SetClientObject(GetItemData(event.m_item)); - bool processed = GetEventHandler()->ProcessEvent(event); + bool processed = HandleWindowEvent(event); // post processing switch ( hdr->code )