X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/823b140b6546935cddafe8f9466a22142ab3f13d..9d5507f7a2701395e1d5c121bd877bb9066ee6ea:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 92dc2fce7d..8c48543587 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -52,19 +52,43 @@ // get HTREEITEM from wxTreeItemId #define HITEM(item) ((HTREEITEM)(((item).m_pItem))) -// ---------------------------------------------------------------------------- -// private functions -// ---------------------------------------------------------------------------- -// wrapper for TreeView_HitTest -static HTREEITEM GetItemFromPoint(HWND hwndTV, int x, int y) +// older SDKs are missing these +#ifndef TVN_ITEMCHANGINGA + +#define TVN_ITEMCHANGINGA (TVN_FIRST-16) +#define TVN_ITEMCHANGINGW (TVN_FIRST-17) + +typedef struct tagNMTVITEMCHANGE { - TV_HITTESTINFO tvht; - tvht.pt.x = x; - tvht.pt.y = y; + NMHDR hdr; + UINT uChanged; + HTREEITEM hItem; + UINT uStateNew; + UINT uStateOld; + LPARAM lParam; +} NMTVITEMCHANGE; - return (HTREEITEM)TreeView_HitTest(hwndTV, &tvht); -} +#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 +// ---------------------------------------------------------------------------- // wrappers for TreeView_GetItem/TreeView_SetItem static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem) @@ -75,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")); @@ -91,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")); @@ -105,11 +133,6 @@ static inline void UnselectItem(HWND hwndTV, HTREEITEM htItem) SelectItem(hwndTV, htItem, false); } -static inline void ToggleItemSelection(HWND hwndTV, HTREEITEM htItem) -{ - SelectItem(hwndTV, htItem, !IsItemSelected(hwndTV, htItem)); -} - // helper function which selects all items in a range and, optionally, // unselects all others static void SelectRange(HWND hwndTV, @@ -178,57 +201,56 @@ static void SelectRange(HWND hwndTV, // helper function which tricks the standard control into changing the focused // item without changing anything else (if someone knows why Microsoft doesn't // allow to do it by just setting TVIS_FOCUSED flag, please tell me!) -static void SetFocus(HWND hwndTV, HTREEITEM htItem) +// +// returns true if the focus was changed, false if the given item was already +// the focused one +static bool SetFocus(HWND hwndTV, HTREEITEM htItem) { // the current focus HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(hwndTV); + if ( htItem == htFocus ) + return false; + if ( htItem ) { - // set the focus - if ( htItem != htFocus ) - { - // remember the selection state of the item - bool wasSelected = IsItemSelected(hwndTV, htItem); + // remember the selection state of the item + bool wasSelected = IsItemSelected(hwndTV, htItem); - if ( htFocus && IsItemSelected(hwndTV, htFocus) ) - { - // prevent the tree from unselecting the old focus which it - // would do by default (TreeView_SelectItem unselects the - // focused item) - TreeView_SelectItem(hwndTV, 0); - SelectItem(hwndTV, htFocus); - } + if ( htFocus && IsItemSelected(hwndTV, htFocus) ) + { + // prevent the tree from unselecting the old focus which it + // would do by default (TreeView_SelectItem unselects the + // focused item) + TreeView_SelectItem(hwndTV, 0); + SelectItem(hwndTV, htFocus); + } - TreeView_SelectItem(hwndTV, htItem); + TreeView_SelectItem(hwndTV, htItem); - if ( !wasSelected ) - { - // need to clear the selection which TreeView_SelectItem() gave - // us - UnselectItem(hwndTV, htItem); - } - //else: was selected, still selected - ok + if ( !wasSelected ) + { + // need to clear the selection which TreeView_SelectItem() gave + // us + UnselectItem(hwndTV, htItem); } - //else: nothing to do, focus already there + //else: was selected, still selected - ok } - else + else // reset focus { - if ( htFocus ) - { - bool wasFocusSelected = IsItemSelected(hwndTV, htFocus); + bool wasFocusSelected = IsItemSelected(hwndTV, htFocus); - // just clear the focus - TreeView_SelectItem(hwndTV, 0); + // just clear the focus + TreeView_SelectItem(hwndTV, 0); - if ( wasFocusSelected ) - { - // restore the selection state - SelectItem(hwndTV, htFocus); - } + if ( wasFocusSelected ) + { + // restore the selection state + SelectItem(hwndTV, htFocus); } - //else: nothing to do, no focus already } + + return true; } // ---------------------------------------------------------------------------- @@ -536,7 +558,9 @@ wxBEGIN_FLAGS( wxTreeCtrlStyle ) 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 ) @@ -632,7 +656,9 @@ void wxTreeCtrl::Init() { m_textCtrl = NULL; m_hasAnyAttr = false; +#if wxUSE_DRAGIMAGE m_dragImage = NULL; +#endif m_pVirtualRoot = NULL; // initialize the global array of events now as it can't be done statically @@ -816,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")); @@ -933,7 +961,7 @@ void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) return; wxTreeViewItem tvItem(item, TVIF_TEXT); - tvItem.pszText = (wxChar *)text.c_str(); // conversion is ok + tvItem.pszText = (wxChar *)text.wx_str(); // conversion is ok DoSetItem(&tvItem); // when setting the text of the item being edited, the text control should @@ -946,7 +974,7 @@ void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) { if ( item == m_idEdited ) { - ::SetWindowText(hwndEdit, text); + ::SetWindowText(hwndEdit, text.wx_str()); } } } @@ -1172,6 +1200,10 @@ void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font) 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); } @@ -1322,36 +1354,6 @@ wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item), return item; } -#if WXWIN_COMPATIBILITY_2_4 - -wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, - long& cookie) const -{ - wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); - - cookie = (long)TreeView_GetChild(GetHwnd(), HITEM(item)); - - return wxTreeItemId((void *)cookie); -} - -wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item), - long& cookie) const -{ - wxTreeItemId fromCookie((void *)cookie); - - HTREEITEM hitem = HITEM(fromCookie); - - hitem = TreeView_GetNextSibling(GetHwnd(), hitem); - - wxTreeItemId item(hitem); - - cookie = (long)item.m_pItem; - - return item; -} - -#endif // WXWIN_COMPATIBILITY_2_4 - wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); @@ -1392,7 +1394,15 @@ 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!")); - return wxTreeItemId(TreeView_GetNextVisible(GetHwnd(), HITEM(item))); + 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 @@ -1400,7 +1410,15 @@ 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!")); - return wxTreeItemId(TreeView_GetPrevVisible(GetHwnd(), HITEM(item))); + 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; } // ---------------------------------------------------------------------------- @@ -1470,7 +1488,7 @@ wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent, if ( !text.empty() ) { mask |= TVIF_TEXT; - tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok + tvIns.item.pszText = (wxChar *)text.wx_str(); // cast is ok } else { @@ -1513,33 +1531,14 @@ wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent, return wxTreeItemId(id); } -// for compatibility only -#if WXWIN_COMPATIBILITY_2_4 - -void wxTreeCtrl::SetImageList(wxImageList *imageList, int) -{ - SetImageList(imageList); -} - -int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const -{ - return GetItemImage(item, wxTreeItemIcon_Selected); -} - -void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image) -{ - SetItemImage(item, image, wxTreeItemIcon_Selected); -} - -#endif // WXWIN_COMPATIBILITY_2_4 - wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text, int image, int selectedImage, wxTreeItemData *data) { - if ( HasFlag(wxTR_HIDE_ROOT) ) { + wxASSERT_MSG( !m_pVirtualRoot, _T("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); @@ -1666,7 +1665,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 } @@ -1691,15 +1690,6 @@ void wxTreeCtrl::Toggle(const wxTreeItemId& item) DoExpand(item, TVE_TOGGLE); } -#if WXWIN_COMPATIBILITY_2_4 - -void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action) -{ - DoExpand(item, action); -} - -#endif - void wxTreeCtrl::Unselect() { wxASSERT_MSG( !(m_windowStyle & wxTR_MULTIPLE), @@ -1731,34 +1721,36 @@ void wxTreeCtrl::UnselectAll() void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) { - if ( m_windowStyle & wxTR_MULTIPLE ) - { - ::SelectItem(GetHwnd(), HITEM(item), select); - } - else - { - wxASSERT_MSG( select, - _T("SelectItem(false) works only for multiselect") ); + wxCHECK_RET( !IsHiddenRoot(item), _T("can't select hidden root item") ); - // inspite 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 + wxASSERT_MSG( select || HasFlag(wxTR_MULTIPLE), + _T("SelectItem(false) works only for multiselect") ); - wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item); - if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() ) + wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item); + if ( !HandleWindowEvent(event) || event.IsAllowed() ) + { + if ( HasFlag(wxTR_MULTIPLE) ) { - if ( !TreeView_SelectItem(GetHwnd(), HITEM(item)) ) + if ( !::SelectItem(GetHwnd(), HITEM(item), select) ) { wxLogLastError(wxT("TreeView_SelectItem")); + return; } - else // ok + } + else // single selection + { + // use TreeView_SelectItem() to deselect the previous selection + if ( !TreeView_SelectItem(GetHwnd(), HITEM(item)) ) { - event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); - (void)GetEventHandler()->ProcessEvent(event); + wxLogLastError(wxT("TreeView_SelectItem")); + return; } } - //else: program vetoed the change + + event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); + (void)HandleWindowEvent(event); } + //else: program vetoed the change } void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item) @@ -1956,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; @@ -1966,8 +1960,10 @@ bool wxTreeCtrl::MSWShouldPreProcessMessage(WXMSG* msg) return wxTreeCtrlBase::MSWShouldPreProcessMessage(msg); } -bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id) +bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id_) { + const int id = (signed short)id_; + if ( cmd == EN_UPDATE ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id); @@ -2005,32 +2001,47 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara { int x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam); - // Convert the screen point to a client point - wxPoint MenuPoint = ScreenToClient(wxPoint(x, y)); - - // can't use GetSelection() here as it would assert in multiselect mode - wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_MENU, this, - wxTreeItemId(TreeView_GetSelection(GetHwnd()))); - - // Get the bounding rectangle for the item, including the non-text areas - wxRect ItemRect; - GetBoundingRect(event.m_item, ItemRect, false); - // If the point is inside the bounding rectangle, use it as the click position. - // This should be the case for WM_CONTEXTMENU as the result of a right-click - if (ItemRect.Contains(MenuPoint)) + + // the item for which the menu should be shown + wxTreeItemId item; + + // the position where the menu should be shown in client coordinates + // (so that it can be passed directly to PopupMenu()) + wxPoint pt; + + if ( x == -1 || y == -1 ) { - event.m_pointDrag = MenuPoint; + // this means that the event was generated from keyboard (e.g. with + // Shift-F10 or special Windows menu key) + // + // use the Explorer standard of putting the menu at the left edge + // of the text, in the vertical middle of the text + item = wxTreeItemId(TreeView_GetSelection(GetHwnd())); + if ( item.IsOk() ) + { + // Use the bounding rectangle of only the text part + wxRect rect; + GetBoundingRect(item, rect, true); + pt = wxPoint(rect.GetX(), rect.GetY() + rect.GetHeight() / 2); + } } - // Use the Explorer standard of putting the menu at the left edge of the text, - // in the vertical middle of the text. Should be the case for the "menu" key - else + else // event from mouse, use mouse position { - // Use the bounding rectangle of only the text part - GetBoundingRect(event.m_item, ItemRect, true); - event.m_pointDrag = wxPoint(ItemRect.GetX(), ItemRect.GetY() + ItemRect.GetHeight() / 2); + pt = ScreenToClient(wxPoint(x, y)); + + TV_HITTESTINFO tvhti; + tvhti.pt.x = pt.x; + tvhti.pt.y = pt.y; + if ( TreeView_HitTest(GetHwnd(), &tvhti) ) + item = wxTreeItemId(tvhti.hItem); } - if ( GetEventHandler()->ProcessEvent(event) ) + // create the event + wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_MENU, this, item); + + event.m_pointDrag = pt; + + if ( HandleWindowEvent(event) ) processed = true; //else: continue with generating wxEVT_CONTEXT_MENU in base class code } @@ -2040,13 +2051,12 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara // same meaning for all of them int x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam); - HTREEITEM htItem = GetItemFromPoint(GetHwnd(), x, y); TV_HITTESTINFO tvht; tvht.pt.x = x; tvht.pt.y = y; - (void) TreeView_HitTest(GetHwnd(), &tvht); + HTREEITEM htItem = TreeView_HitTest(GetHwnd(), &tvht); switch ( nMsg ) { @@ -2061,7 +2071,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara SetFocus(); // toggle selected state - ::ToggleItemSelection(GetHwnd(), htItem); + ToggleItemSelection(htItem); ::SetFocus(GetHwnd(), htItem); @@ -2124,6 +2134,14 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara ::SetFocus(GetHwnd(), htItem); processed = true; } + else // click on a single selected item + { + // don't interfere with the default processing in + // WM_MOUSEMOVE handler below as the default window + // proc will start the drag itself if we let have + // WM_LBUTTONDOWN + m_htClickedItem.Unset(); + } // reset on any click without Shift m_htSelStart.Unset(); @@ -2131,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 ) @@ -2138,39 +2188,49 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara int cx = abs(m_ptClick.x - x); int cy = abs(m_ptClick.y - y); - if ( cx > GetSystemMetrics( SM_CXDRAG ) || cy > GetSystemMetrics( SM_CYDRAG ) ) + if ( cx > ::GetSystemMetrics(SM_CXDRAG) || + cy > ::GetSystemMetrics(SM_CYDRAG) ) { - HWND pWnd = ::GetParent( GetHwnd() ); - if ( pWnd ) - { - NM_TREEVIEW tv; + NM_TREEVIEW tv; + wxZeroMemory(tv); - tv.hdr.hwndFrom = GetHwnd(); - tv.hdr.idFrom = ::GetWindowLong( GetHwnd(), GWL_ID ); - tv.hdr.code = TVN_BEGINDRAG; + tv.hdr.hwndFrom = GetHwnd(); + tv.hdr.idFrom = ::GetWindowLong(GetHwnd(), GWL_ID); + tv.hdr.code = TVN_BEGINDRAG; - tv.itemNew.hItem = HITEM(m_htClickedItem); + tv.itemNew.hItem = HITEM(m_htClickedItem); - TVITEM tviAux; - ZeroMemory(&tviAux, sizeof(tviAux)); - tviAux.hItem = HITEM(m_htClickedItem); - tviAux.mask = TVIF_STATE | TVIF_PARAM; - tviAux.stateMask = 0xffffffff; - TreeView_GetItem( GetHwnd(), &tviAux ); - tv.itemNew.state = tviAux.state; - tv.itemNew.lParam = tviAux.lParam; + TVITEM tviAux; + wxZeroMemory(tviAux); - tv.ptDrag.x = x; - tv.ptDrag.y = y; + tviAux.hItem = HITEM(m_htClickedItem); + tviAux.mask = TVIF_STATE | TVIF_PARAM; + tviAux.stateMask = 0xffffffff; + TreeView_GetItem(GetHwnd(), &tviAux); - ::SendMessage( pWnd, WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv ); - } + tv.itemNew.state = tviAux.state; + tv.itemNew.lParam = tviAux.lParam; + + tv.ptDrag.x = x; + tv.ptDrag.y = y; + + // do it before SendMessage() call below to avoid + // reentrancies here if there is another WM_MOUSEMOVE + // in the queue already m_htClickedItem.Unset(); + + ::SendMessage(GetHwndOf(GetParent()), WM_NOTIFY, + tv.hdr.idFrom, (LPARAM)&tv ); + + // don't pass it to the default window proc, it would + // start dragging again + processed = true; } } #endif // __WXWINCE__ +#if wxUSE_DRAGIMAGE if ( m_dragImage ) { m_dragImage->Move(wxPoint(x, y)); @@ -2183,6 +2243,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara m_dragImage->Show(); } } +#endif // wxUSE_DRAGIMAGE break; case WM_LBUTTONUP: @@ -2208,6 +2269,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara // fall through case WM_RBUTTONUP: +#if wxUSE_DRAGIMAGE if ( m_dragImage ) { m_dragImage->EndDrag(); @@ -2216,12 +2278,15 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara // generate the drag end event wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, this, htItem); - (void)GetEventHandler()->ProcessEvent(event); + event.m_pointDrag = wxPoint(x, y); + + (void)HandleWindowEvent(event); // if we don't do it, the tree seems to think that 2 items // are selected simultaneously which is quite weird TreeView_SelectDropTarget(GetHwnd(), 0); } +#endif // wxUSE_DRAGIMAGE break; } } @@ -2255,7 +2320,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara case VK_SPACE: if ( bCtrl ) { - ::ToggleItemSelection(GetHwnd(), htSel); + ToggleItemSelection(htSel); } else { @@ -2362,6 +2427,24 @@ wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( wParam == VK_SPACE || wParam == VK_RETURN ) return 0; } +#if wxUSE_DRAGIMAGE + else if ( nMsg == WM_KEYDOWN ) + { + if ( wParam == VK_ESCAPE ) + { + if ( m_dragImage ) + { + m_dragImage->EndDrag(); + delete m_dragImage; + m_dragImage = NULL; + + // if we don't do it, the tree seems to think that 2 items + // are selected simultaneously which is quite weird + TreeView_SelectDropTarget(GetHwnd(), 0); + } + } + } +#endif // wxUSE_DRAGIMAGE return wxControl::MSWDefWindowProc(nMsg, wParam, lParam); } @@ -2522,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; @@ -2541,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; @@ -2550,11 +2632,41 @@ 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; + + // 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: @@ -2584,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 @@ -2724,7 +2849,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 ) @@ -2750,6 +2875,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case TVN_BEGINDRAG: case TVN_BEGINRDRAG: +#if wxUSE_DRAGIMAGE if ( event.IsAllowed() ) { // normally this is impossible because the m_dragImage is @@ -2760,6 +2886,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) m_dragImage->BeginDrag(wxPoint(0,0), this); m_dragImage->Show(); } +#endif // wxUSE_DRAGIMAGE break; case TVN_DELETEITEM: @@ -2848,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;