]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/treectrl.cpp
fixed LastRead() after Read(wxOutputStream&) (patch 1658301)
[wxWidgets.git] / src / msw / treectrl.cpp
index c6bd87b0863698f2ace511a5bf7790a97076ee87..f314a7a873d8563cdc05a3070225477b06684d26 100644 (file)
 // 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)
 {
@@ -241,8 +221,6 @@ static void SetFocus(HWND hwndTV, HTREEITEM htItem)
     }
 }
 
-#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
-
 // ----------------------------------------------------------------------------
 // private classes
 // ----------------------------------------------------------------------------
@@ -456,11 +434,7 @@ public:
             return true;
         }
 
-#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
-        if ( tree->IsItemChecked(item) )
-#else
         if ( ::IsItemSelected(GetHwndOf(tree), HITEM(item)) )
-#endif
         {
             m_selections.Add(item);
         }
@@ -675,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;
 
@@ -696,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;
@@ -1202,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);
 }
 
@@ -1747,11 +1711,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();
@@ -1767,12 +1727,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
     {
@@ -2044,31 +1999,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.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);
         }
 
+        // 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
@@ -2079,32 +2049,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 )
                 {
@@ -2185,7 +2138,6 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
                     }
                 }
                 break;
-#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
 
             case WM_MOUSEMOVE:
 #ifndef __WXWINCE__
@@ -2272,6 +2224,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
@@ -2281,7 +2235,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
@@ -2381,7 +2334,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
@@ -2410,32 +2362,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
@@ -2546,8 +2475,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;
@@ -2818,6 +2747,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() )