X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/068b764a5a7cc2929b1fd8ea5643f43b5cbc8762..46562151eb21235344768ea4b53c204d0c03fcee:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 5e49a101c1..13edcec7c3 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -17,10 +17,6 @@ // headers // ---------------------------------------------------------------------------- -#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) - #pragma implementation "treectrl.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -32,6 +28,11 @@ #include "wx/msw/private.h" +// include "properly" +#include "wx/msw/wrapcctl.h" + +#include "wx/msw/missing.h" + // Set this to 1 to be _absolutely_ sure that repainting will work for all // comctl32.dll versions #define wxUSE_COMCTL32_SAFELY 0 @@ -44,17 +45,11 @@ #include "wx/msw/treectrl.h" #include "wx/msw/dragimag.h" -// include "properly" -#include "wx/msw/wrapcctl.h" - // macros to hide the cast ugliness // -------------------------------- -// ptr is the real item id, i.e. wxTreeItemId::m_pItem -#define HITEM_PTR(ptr) (HTREEITEM)(ptr) - -// item here is a wxTreeItemId -#define HITEM(item) HITEM_PTR((item).m_pItem) +// get HTREEITEM from wxTreeItemId +#define HITEM(item) ((HTREEITEM)(((item).m_pItem))) // the native control doesn't support multiple selections under MSW and we // have 2 ways to emulate them: either using TVS_CHECKBOXES style and let @@ -346,7 +341,8 @@ public: { m_selections.Empty(); - DoTraverse(tree->GetRootItem()); + if (tree->GetCount() > 0) + DoTraverse(tree->GetRootItem()); } virtual bool OnVisit(const wxTreeItemId& item) @@ -677,7 +673,7 @@ bool wxTreeCtrl::Create(wxWindow *parent, wstyle |= TVS_CHECKBOXES; #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE -#ifndef __WXWINCE__ +#if !defined(__WXWINCE__) && defined(TVS_INFOTIP) // Need so that TVN_GETINFOTIP messages will be sent wstyle |= TVS_INFOTIP; #endif @@ -1834,7 +1830,7 @@ void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item) size_t nCount = children.Count(); for ( size_t n = 0; n < nCount; n++ ) { - if ( !TreeView_DeleteItem(GetHwnd(), HITEM_PTR(children[n])) ) + if ( !TreeView_DeleteItem(GetHwnd(), HITEM(children[n])) ) { wxLogLastError(wxT("TreeView_DeleteItem")); } @@ -1946,9 +1942,9 @@ void wxTreeCtrl::UnselectAll() for ( size_t n = 0; n < count; n++ ) { #if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - SetItemCheck(HITEM_PTR(selections[n]), false); + SetItemCheck(HITEM(selections[n]), false); #else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - ::UnselectItem(GetHwnd(), HITEM_PTR(selections[n])); + ::UnselectItem(GetHwnd(), HITEM(selections[n])); #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE } @@ -2243,20 +2239,42 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara WXLRESULT rc = 0; bool isMultiple = HasFlag(wxTR_MULTIPLE); -#ifdef WM_CONTEXTMENU + // This message is sent after a right-click, or when the "menu" key is pressed if ( nMsg == WM_CONTEXTMENU ) { + 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)); + wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_MENU, GetId() ); // can't use GetSelection() here as it would assert in multiselect mode event.m_item = wxTreeItemId(TreeView_GetSelection(GetHwnd())); event.SetEventObject( this ); + // 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.Inside(MenuPoint)) + { + event.m_pointDrag = MenuPoint; + } + // 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 + { + // 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); + } + if ( GetEventHandler()->ProcessEvent(event) ) processed = true; //else: continue with generating wxEVT_CONTEXT_MENU in base class code } -#endif // WM_CONTEXTMENU else if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) ) { // we only process mouse messages here and these parameters have the @@ -2264,6 +2282,12 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara 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; + + TreeView_HitTest(GetHwnd(), &tvht); switch ( nMsg ) { @@ -2284,8 +2308,11 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara #if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE case WM_LBUTTONDOWN: - if ( htItem && isMultiple ) + if ( htItem && isMultiple && (tvht.flags & TVHT_ONITEM) != 0 ) { + m_htClickedItem = (WXHTREEITEM) htItem; + m_ptClick = wxPoint(x, y); + if ( wParam & MK_CONTROL ) { SetFocus(); @@ -2311,8 +2338,11 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara m_htSelStart = TreeView_GetSelection(GetHwnd()); } - SelectRange(GetHwnd(), HITEM(m_htSelStart), htItem, + if ( m_htSelStart ) + SelectRange(GetHwnd(), HITEM(m_htSelStart), htItem, !(wParam & MK_CONTROL)); + else + ::SelectItem(GetHwnd(), htItem); ::SetFocus(GetHwnd(), htItem); @@ -2322,23 +2352,21 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara { // avoid doing anything if we click on the only // currently selected item + + SetFocus(); wxArrayTreeItemIds selections; size_t count = GetSelections(selections); if ( count == 0 || count > 1 || - HITEM_PTR(selections[0]) != htItem ) + HITEM(selections[0]) != htItem ) { // clear the previously selected items, if the // user 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)) - { - ::SetFocus(GetHwnd(), htItem); - } - else + if (!IsItemSelected(GetHwnd(), htItem)) { UnselectAll(); @@ -2350,6 +2378,8 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara TreeView_SelectItem(GetHwnd(), 0); ::SelectItem(GetHwnd(), htItem); } + ::SetFocus(GetHwnd(), htItem); + processed = true; } // reset on any click without Shift @@ -2360,6 +2390,45 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE case WM_MOUSEMOVE: +#ifndef __WXWINCE__ + if ( m_htClickedItem ) + { + int cx = abs(m_ptClick.x - x); + int cy = abs(m_ptClick.y - y); + + if ( cx > GetSystemMetrics( SM_CXDRAG ) || cy > GetSystemMetrics( SM_CYDRAG ) ) + { + HWND pWnd = ::GetParent( GetHwnd() ); + if ( pWnd ) + { + NM_TREEVIEW tv; + + tv.hdr.hwndFrom = GetHwnd(); + tv.hdr.idFrom = ::GetWindowLong( GetHwnd(), GWL_ID ); + tv.hdr.code = TVN_BEGINDRAG; + + 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; + + tv.ptDrag.x = x; + tv.ptDrag.y = y; + + ::SendMessage( pWnd, WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv ); + } + m_htClickedItem.Unset(); + } + } +#endif // __WXWINCE__ + if ( m_dragImage ) { m_dragImage->Move(wxPoint(x, y)); @@ -2388,7 +2457,10 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara { UnselectAll(); TreeView_SelectItem(GetHwnd(), htItem); + ::SelectItem(GetHwnd(), htItem); + ::SetFocus(GetHwnd(), htItem); } + m_htClickedItem.Unset(); } // fall through @@ -2429,7 +2501,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara { // TreeView_GetItemRect() will return false if item is not visible, // which may happen perfectly well - if ( TreeView_GetItemRect(GetHwnd(), HITEM_PTR(selections[n]), + if ( TreeView_GetItemRect(GetHwnd(), HITEM(selections[n]), &rect, TRUE) ) { ::InvalidateRect(GetHwnd(), &rect, FALSE); @@ -2552,6 +2624,22 @@ wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) if ( nMsg == WM_RBUTTONDOWN ) return 0; + // but because of the above we don't get NM_RCLICK which is normally + // generated by tree window proc when the modal loop mentioned above ends + // because the mouse is released -- synthesize it ourselves instead + if ( nMsg == WM_RBUTTONUP ) + { + NMHDR hdr; + hdr.hwndFrom = GetHwnd(); + hdr.idFrom = GetId(); + hdr.code = NM_RCLICK; + + WXLPARAM rc; + MSWOnNotify(GetId(), (LPARAM)&hdr, &rc); + + // continue as usual + } + if ( nMsg == WM_CHAR ) { // also don't let the control process Space and Return keys because it @@ -2654,6 +2742,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; } +#ifdef TVN_GETINFOTIP case TVN_GETINFOTIP: { eventType = wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP; @@ -2664,6 +2753,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; } +#endif #endif case TVN_GETDISPINFO: @@ -2720,8 +2810,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 - WXLPARAM lParam = - (::GetKeyState(VK_MENU) < 0 ? KF_ALTDOWN : 0) << 16; + const bool isAltDown = ::GetKeyState(VK_MENU) < 0; + WXLPARAM lParam = (isAltDown ? KF_ALTDOWN : 0) << 16; WXWPARAM wParam = info->wVKey; @@ -2739,7 +2829,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wParam); // a separate event for Space/Return - if ( !wxIsCtrlDown() && !wxIsShiftDown() && + if ( !wxIsCtrlDown() && !wxIsShiftDown() && !isAltDown && ((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) ) { wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, @@ -3018,6 +3108,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; #ifndef __WXWINCE__ +#ifdef TVN_GETINFOTIP case TVN_GETINFOTIP: { // If the user permitted a tooltip change, change it @@ -3027,6 +3118,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } } break; +#endif #endif case TVN_SELCHANGING: