]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/treectrl.cpp
wxBrush::SetColour and wxPen::SetColour unified. Source cleaning.
[wxWidgets.git] / src / msw / treectrl.cpp
index 5e49a101c16784b1fd7f4d46664c40fddb92b4ca..13edcec7c3d447eee797f7ebded6d8392cf55861 100644 (file)
 // headers
 // ----------------------------------------------------------------------------
 
 // 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"
 
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 
 #include "wx/msw/private.h"
 
 
 #include "wx/msw/private.h"
 
+// include <commctrl.h> "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
 // Set this to 1 to be _absolutely_ sure that repainting will work for all
 // comctl32.dll versions
 #define wxUSE_COMCTL32_SAFELY 0
 #include "wx/msw/treectrl.h"
 #include "wx/msw/dragimag.h"
 
 #include "wx/msw/treectrl.h"
 #include "wx/msw/dragimag.h"
 
-// include <commctrl.h> "properly"
-#include "wx/msw/wrapcctl.h"
-
 // macros to hide the cast ugliness
 // --------------------------------
 
 // 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
 
 // 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();
 
         {
             m_selections.Empty();
 
-            DoTraverse(tree->GetRootItem());
+            if (tree->GetCount() > 0)
+                DoTraverse(tree->GetRootItem());
         }
 
     virtual bool OnVisit(const wxTreeItemId& item)
         }
 
     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
 
         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
     // 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++ )
     {
     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"));
         }
         {
             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
         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
 #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
         }
 
 #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);
 
     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 )
     {
     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 );
 
         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
     }
         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
     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);
         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 )
         {
 
         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 !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();
                     if ( wParam & MK_CONTROL )
                     {
                         SetFocus();
@@ -2311,8 +2338,11 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
                             m_htSelStart = TreeView_GetSelection(GetHwnd());
                         }
 
                             m_htSelStart = TreeView_GetSelection(GetHwnd());
                         }
 
-                        SelectRange(GetHwnd(), HITEM(m_htSelStart), htItem,
+                        if ( m_htSelStart )
+                            SelectRange(GetHwnd(), HITEM(m_htSelStart), htItem,
                                     !(wParam & MK_CONTROL));
                                     !(wParam & MK_CONTROL));
+                        else
+                            ::SelectItem(GetHwnd(), htItem);
 
                         ::SetFocus(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
                     {
                         // 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 ||
 
                         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.
 
                         {
                             // 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();
 
                             {
                                 UnselectAll();
 
@@ -2350,6 +2378,8 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
                                 TreeView_SelectItem(GetHwnd(), 0);
                                 ::SelectItem(GetHwnd(), htItem);
                             }
                                 TreeView_SelectItem(GetHwnd(), 0);
                                 ::SelectItem(GetHwnd(), htItem);
                             }
+                            ::SetFocus(GetHwnd(), htItem);
+                            processed = true;
                         }
 
                         // reset on any click without Shift
                         }
 
                         // 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:
 #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));
                 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);
                     {
                         UnselectAll();
                         TreeView_SelectItem(GetHwnd(), htItem);
+                        ::SelectItem(GetHwnd(), htItem);
+                        ::SetFocus(GetHwnd(), htItem);
                     }
                     }
+                    m_htClickedItem.Unset();
                 }
 
                 // fall through
                 }
 
                 // 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
         {
             // 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);
                                       &rect, TRUE) )
             {
                 ::InvalidateRect(GetHwnd(), &rect, FALSE);
@@ -2552,6 +2624,22 @@ wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
     if ( nMsg == WM_RBUTTONDOWN )
         return 0;
 
     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
     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;
             }
 
                 break;
             }
 
+#ifdef TVN_GETINFOTIP
         case TVN_GETINFOTIP:
             {
                 eventType = wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP;
         case TVN_GETINFOTIP:
             {
                 eventType = wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP;
@@ -2664,6 +2753,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
                 break;
             }
 
                 break;
             }
+#endif
 #endif
 
         case TVN_GETDISPINFO:
 #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
                 // 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;
 
 
                 WXWPARAM wParam = info->wVKey;
 
@@ -2739,7 +2829,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                                                 wParam);
 
                 // a separate event for Space/Return
                                                 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,
                      ((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__
             break;
 
 #ifndef __WXWINCE__
+#ifdef TVN_GETINFOTIP
          case TVN_GETINFOTIP:
             {
                 // If the user permitted a tooltip change, change it
          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;
                 }
             }
             break;
+#endif
 #endif
 
         case TVN_SELCHANGING:
 #endif
 
         case TVN_SELCHANGING: