X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/03e7d1397ecbc051c6ee3adbc7928ffd404b57f2..e6733873f5bc5de9ef538533da786250de9dd9c7:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index e9bfaa8961..aeafd9e8ae 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -29,6 +29,8 @@ #include "wx/treectrl.h" #ifndef WX_PRECOMP + #include "wx/msw/wrapcctl.h" // include "properly" + #include "wx/msw/missing.h" #include "wx/dynarray.h" #include "wx/log.h" #include "wx/app.h" @@ -37,11 +39,6 @@ #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 @@ -55,30 +52,10 @@ // 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 -// checkboxes be the selection status (checked == selected) or by really -// emulating everything, i.e. intercepting mouse and key events &c. The first -// approach is much easier but doesn't work with comctl32.dll < 4.71 and also -// looks quite ugly. -#define wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 0 - // ---------------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------------- -// wrapper for TreeView_HitTest -static HTREEITEM GetItemFromPoint(HWND hwndTV, int x, int y) -{ - TV_HITTESTINFO tvht; - tvht.pt.x = x; - tvht.pt.y = y; - - return (HTREEITEM)TreeView_HitTest(hwndTV, &tvht); -} - -#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - // wrappers for TreeView_GetItem/TreeView_SetItem static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem) { @@ -244,8 +221,6 @@ static void SetFocus(HWND hwndTV, HTREEITEM htItem) } } -#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - // ---------------------------------------------------------------------------- // private classes // ---------------------------------------------------------------------------- @@ -293,9 +268,9 @@ class wxTreeItemParam { public: wxTreeItemParam() - : m_item((void*)NULL), - m_data(NULL) { + m_data = NULL; + for ( size_t n = 0; n < WXSIZEOF(m_images); n++ ) { m_images[n] = -1; @@ -451,17 +426,15 @@ public: virtual bool OnVisit(const wxTreeItemId& item) { + const wxTreeCtrl * const tree = GetTree(); + // can't visit a virtual node. - if ( (GetTree()->GetRootItem() == item) && (GetTree()->GetWindowStyle() & wxTR_HIDE_ROOT)) + if ( (tree->GetRootItem() == item) && tree->HasFlag(wxTR_HIDE_ROOT) ) { return true; } -#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - if ( GetTree()->IsItemChecked(item) ) -#else - if ( ::IsItemSelected(GetHwndOf(GetTree()), HITEM(item)) ) -#endif + if ( ::IsItemSelected(GetHwndOf(tree), HITEM(item)) ) { m_selections.Add(item); } @@ -676,7 +649,7 @@ bool wxTreeCtrl::Create(wxWindow *parent, if ( !CreateControl(parent, id, pos, size, style, validator, name) ) return false; - DWORD exStyle = 0; + WXDWORD exStyle = 0; DWORD wstyle = MSWGetStyle(m_windowStyle, & exStyle); wstyle |= WS_TABSTOP | TVS_SHOWSELALWAYS; @@ -697,20 +670,6 @@ bool wxTreeCtrl::Create(wxWindow *parent, wstyle |= TVS_FULLROWSELECT; } - // using TVS_CHECKBOXES for emulation of a multiselection tree control - // doesn't work without the new enough headers -#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE && \ - !defined( __GNUWIN32_OLD__ ) && \ - !defined( __BORLANDC__ ) && \ - !defined( __WATCOMC__ ) && \ - (!defined(__VISUALC__) || (__VISUALC__ > 1010)) - - // we emulate the multiple selection tree controls by using checkboxes: set - // up the image list we need for this if we do have multiple selections - if ( m_windowStyle & wxTR_MULTIPLE ) - wstyle |= TVS_CHECKBOXES; -#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - #if !defined(__WXWINCE__) && defined(TVS_INFOTIP) // Need so that TVN_GETINFOTIP messages will be sent wstyle |= TVS_INFOTIP; @@ -933,6 +892,11 @@ bool wxTreeCtrl::SetForegroundColour(const wxColour &colour) // Item access // ---------------------------------------------------------------------------- +bool wxTreeCtrl::IsHiddenRoot(const wxTreeItemId& item) const +{ + return HITEM(item) == TVI_ROOT && HasFlag(wxTR_HIDE_ROOT); +} + wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") ); @@ -982,7 +946,7 @@ int wxTreeCtrl::GetItemImage(const wxTreeItemId& item, { wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") ); - if ( (HITEM(item) == TVI_ROOT) && (m_windowStyle & wxTR_HIDE_ROOT) ) + if ( IsHiddenRoot(item) ) { // no images for hidden root item return -1; @@ -1002,7 +966,7 @@ void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image, wxT("invalid image index")); - if ( (HITEM(item) == TVI_ROOT) && (m_windowStyle & wxTR_HIDE_ROOT) ) + if ( IsHiddenRoot(item) ) { // no images for hidden root item return; @@ -1198,6 +1162,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); } @@ -1348,36 +1316,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") ); @@ -1539,32 +1477,12 @@ 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 ( m_windowStyle & wxTR_HIDE_ROOT ) + if ( HasFlag(wxTR_HIDE_ROOT) ) { // create a virtual root item, the parent for all the others wxTreeItemParam *param = new wxTreeItemParam; @@ -1669,7 +1587,7 @@ void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag) wxT("Unknown flag in wxTreeCtrl::DoExpand") ); // A hidden root can be neither expanded nor collapsed. - wxCHECK_RET( !(m_windowStyle & wxTR_HIDE_ROOT) || (HITEM(item) != TVI_ROOT), + wxCHECK_RET( !IsHiddenRoot(item), wxT("Can't expand/collapse hidden root node!") ); // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must @@ -1717,15 +1635,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), @@ -1743,11 +1652,7 @@ void wxTreeCtrl::UnselectAll() size_t count = GetSelections(selections); for ( size_t n = 0; n < count; n++ ) { -#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - SetItemCheck(HITEM(selections[n]), false); -#else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE ::UnselectItem(GetHwnd(), HITEM(selections[n])); -#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE } m_htSelStart.Unset(); @@ -1763,12 +1668,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) { if ( m_windowStyle & wxTR_MULTIPLE ) { -#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - // selecting the item means checking it - SetItemCheck(item, select); -#else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE ::SelectItem(GetHwnd(), HITEM(item), select); -#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE } else { @@ -1798,6 +1698,8 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item) { + wxCHECK_RET( !IsHiddenRoot(item), _T("can't show hidden root item") ); + // no error return TreeView_EnsureVisible(GetHwnd(), HITEM(item)); } @@ -2038,31 +1940,46 @@ 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.Inside(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); } + // create the event + wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_MENU, this, item); + + event.m_pointDrag = pt; + if ( GetEventHandler()->ProcessEvent(event) ) processed = true; //else: continue with generating wxEVT_CONTEXT_MENU in base class code @@ -2073,32 +1990,15 @@ 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 ) { - case WM_RBUTTONDOWN: - // if the item we are about to right click on is not already - // selected or if we click outside of any item, remove the - // entire previous selection - if ( !htItem || !::IsItemSelected(GetHwnd(), htItem) ) - { - UnselectAll(); - } - - // select item and set the focus to the - // newly selected item - ::SelectItem(GetHwnd(), htItem); - ::SetFocus(GetHwnd(), htItem); - break; - -#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE case WM_LBUTTONDOWN: if ( htItem && isMultiple && (tvht.flags & TVHT_ONITEM) != 0 ) { @@ -2179,7 +2079,6 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara } } break; -#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE case WM_MOUSEMOVE: #ifndef __WXWINCE__ @@ -2266,6 +2165,8 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara // generate the drag end event wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, this, htItem); + event.m_pointDrag = wxPoint(x, y); + (void)GetEventHandler()->ProcessEvent(event); // if we don't do it, the tree seems to think that 2 items @@ -2275,7 +2176,6 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara break; } } -#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE else if ( (nMsg == WM_SETFOCUS || nMsg == WM_KILLFOCUS) && isMultiple ) { // the tree control greys out the selected item when it loses focus and @@ -2375,7 +2275,6 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara } } } -#endif // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE else if ( nMsg == WM_COMMAND ) { // if we receive a EN_KILLFOCUS command from the in-place edit control @@ -2404,32 +2303,9 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara WXLRESULT wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { - // default WM_RBUTTONDOWN handler enters modal loop inside DefWindowProc() - // waiting for WM_RBUTTONUP and then sends the resulting WM_CONTEXTMENU to - // the parent window, not us, which completely breaks everything so simply - // don't let it see this message at all - 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 + // don't let the control process Space and Return keys because it // doesn't do anything useful with them anyhow but always beeps // annoyingly when it receives them and there is no way to turn it off // simply if you just process TREEITEM_ACTIVATED event to which Space @@ -2540,8 +2416,8 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; } -#endif -#endif +#endif // TVN_GETINFOTIP +#endif // !__WXWINCE__ case TVN_GETDISPINFO: eventType = wxEVT_COMMAND_TREE_GET_INFO; @@ -2812,6 +2688,17 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) *result = false; break; + case NM_RCLICK: + // prevent tree control from sending WM_CONTEXTMENU to our parent + // (which it does if NM_RCLICK is not handled) because we want to + // send it to the control itself + *result = + processed = true; + + ::SendMessage(GetHwnd(), WM_CONTEXTMENU, + (WPARAM)GetHwnd(), ::GetMessagePos()); + break; + case TVN_BEGINDRAG: case TVN_BEGINRDRAG: if ( event.IsAllowed() )