X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f73eddd2a5af3c58fff4e2a6154d9a395b7631a5..c944775f72435d2c0493113e05445898ab8baf1b:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index f7e310659d..63019b5e22 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -52,6 +52,40 @@ // get HTREEITEM from wxTreeItemId #define HITEM(item) ((HTREEITEM)(((item).m_pItem))) + +// older SDKs are missing these +#ifndef TVN_ITEMCHANGINGA + +#define TVN_ITEMCHANGINGA (TVN_FIRST-16) +#define TVN_ITEMCHANGINGW (TVN_FIRST-17) + +typedef struct tagNMTVITEMCHANGE +{ + NMHDR hdr; + UINT uChanged; + HTREEITEM hItem; + UINT uStateNew; + UINT uStateOld; + LPARAM lParam; +} NMTVITEMCHANGE; + +#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 +// multi-select mode on vista systems. + +static HTREEITEM gs_unlockItem = NULL; + +class TreeItemUnlocker +{ +public: + TreeItemUnlocker(HTREEITEM item) { gs_unlockItem = item; } + ~TreeItemUnlocker() { gs_unlockItem = NULL; } +}; + + // ---------------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------------- @@ -65,6 +99,8 @@ static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem) tvi.stateMask = TVIS_SELECTED; tvi.hItem = hItem; + TreeItemUnlocker unlocker(hItem); + if ( !TreeView_GetItem(hwndTV, &tvi) ) { wxLogLastError(wxT("TreeView_GetItem")); @@ -81,6 +117,8 @@ static bool SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = true) tvi.state = select ? TVIS_SELECTED : 0; tvi.hItem = hItem; + TreeItemUnlocker unlocker(hItem); + if ( TreeView_SetItem(hwndTV, &tvi) == -1 ) { wxLogLastError(wxT("TreeView_SetItem")); @@ -804,6 +842,8 @@ bool wxTreeCtrl::DoGetItem(wxTreeViewItem *tvItem) const void wxTreeCtrl::DoSetItem(wxTreeViewItem *tvItem) { + TreeItemUnlocker unlocker(tvItem->hItem); + if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 ) { wxLogLastError(wxT("TreeView_SetItem")); @@ -1908,7 +1948,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; @@ -2107,6 +2149,38 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara } break; + case WM_RBUTTONDOWN: + // default handler removes the highlight from the currently + // focused item when right mouse button is pressed on another + // one but keeps the remaining items highlighted, which is + // confusing, so override this default behaviour for tree with + // multiple selections + if ( isMultiple ) + { + if ( !IsItemSelected(GetHwnd(), htItem) ) + { + UnselectAll(); + SelectItem(htItem); + ::SetFocus(GetHwnd(), htItem); + } + + // fire EVT_RIGHT_DOWN + HandleMouseEvent(nMsg, x, y, wParam); + + // send NM_RCLICK + NMHDR nmhdr; + nmhdr.hwndFrom = GetHwnd(); + nmhdr.idFrom = ::GetWindowLong(GetHwnd(), GWL_ID); + nmhdr.code = NM_RCLICK; + ::SendMessage(::GetParent(GetHwnd()), WM_NOTIFY, + nmhdr.idFrom, (LPARAM)&nmhdr); + + // prevent tree control default processing, as we've + // already done everything + processed = true; + } + break; + case WM_MOUSEMOVE: #ifndef __WXWINCE__ if ( m_htClickedItem ) @@ -2531,8 +2605,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; @@ -2550,7 +2623,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; @@ -2564,6 +2637,36 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } break; + + // 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: + { + // we only need to handles these in multi-select trees + if ( HasFlag(wxTR_MULTIPLE) ) + { + // get info about the item about to be changed + NMTVITEMCHANGE* info = (NMTVITEMCHANGE*)lParam; + if (info->hItem != gs_unlockItem) + { + // item's state is locked, don't allow the change + // returning 1 will disallow the change + *result = 1; + return true; + } + } + + // allow the state change + } + return false; + // NB: MSLU is broken and sends TVN_SELCHANGEDA instead of // TVN_SELCHANGEDW in Unicode mode under Win98. Therefore // we have to handle both messages: @@ -2593,6 +2696,19 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) event.m_itemOld = tv->itemOld.hItem; } } + + // we receive this message from WM_LBUTTONDOWN handler inside + // comctl32.dll and so before the click is passed to + // DefWindowProc() which sets the focus to the window which was + // clicked and this can lead to unexpected event sequences: for + // example, we may get a "selection change" event from the tree + // before getting a "kill focus" event for the text control which + // had the focus previously, thus breaking user code doing input + // validation + // + // to avoid such surprises, we force the generation of focus events + // now, before we generate the selection change ones + SetFocus(); break; // instead of explicitly checking for _WIN32_IE, check if the @@ -2859,17 +2975,40 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; case TVN_ITEMEXPANDED: - // the item is not refreshed properly after expansion when it has - // an image depending on the expanded/collapsed state - bug in - // comctl32.dll or our code? { NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam; - wxTreeItemId id(tv->itemNew.hItem); + const wxTreeItemId id(tv->itemNew.hItem); - int image = GetItemImage(id, wxTreeItemIcon_Expanded); - if ( image != -1 ) + if ( tv->action == TVE_COLLAPSE ) + { + if ( wxApp::GetComCtl32Version() >= 600 ) + { + // for some reason the item selection rectangle depends + // on whether it is expanded or collapsed (at least + // with comctl32.dll v6): it is wider (by 3 pixels) in + // the expanded state, so when the item collapses and + // then is deselected the rightmost 3 pixels of the + // previously drawn selection are left on the screen + // + // it's not clear if it's a bug in comctl32.dll or in + // our code (because it does not happen in Explorer but + // OTOH we don't do anything which could result in this + // AFAICS) but we do need to work around it to avoid + // ugly artifacts + RefreshItem(id); + } + } + else // expand { - RefreshItem(id); + // the item is also not refreshed properly after expansion when + // it has an image depending on the expanded/collapsed state: + // again, it's not clear if the bug is in comctl32.dll or our + // code... + int image = GetItemImage(id, wxTreeItemIcon_Expanded); + if ( image != -1 ) + { + RefreshItem(id); + } } } break;