]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/treectrl.cpp
[ 1936700 ] wxCAL_SHOW_WEEK_NUMBERS, slightly modified
[wxWidgets.git] / src / msw / treectrl.cpp
index d705b7e6a85f3c91b2094a1a9f849299d2a9c172..3093583d52ad26a2a4852699c2211ab5fc47f431 100644 (file)
@@ -72,19 +72,38 @@ typedef struct tagNMTVITEMCHANGE
 #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
+// this helper class is used on vista systems for preventing unwanted
+// item state changes in the vista tree control.  It is only effective in
 // multi-select mode on vista systems.
 
-static HTREEITEM gs_unlockItem = NULL;
+// The vista tree control includes some new code that originally broke the
+// multi-selection tree, causing seemingly spurious item selection state changes
+// during Shift or Ctrl-click item selection. (To witness the original broken
+// behavior, simply make IsLocked() below always return false). This problem was
+// solved by using the following class to 'unlock' an item's selection state.
 
 class TreeItemUnlocker
 {
 public:
-    TreeItemUnlocker(HTREEITEM item) { gs_unlockItem = item; }
-    ~TreeItemUnlocker() { gs_unlockItem = NULL; }
+    // unlock a single item
+    TreeItemUnlocker(HTREEITEM item) { ms_unlockedItem = item; }
+
+    // unlock all items, don't use unless absolutely necessary
+    TreeItemUnlocker() { ms_unlockedItem = (HTREEITEM)-1; }
+
+    // lock everything back
+    ~TreeItemUnlocker() { ms_unlockedItem = NULL; }
+
+
+    // check if the item state is currently locked
+    static bool IsLocked(HTREEITEM item)
+        { return ms_unlockedItem != (HTREEITEM)-1 && item != ms_unlockedItem; }
+
+private:
+    static HTREEITEM ms_unlockedItem;
 };
 
+HTREEITEM TreeItemUnlocker::ms_unlockedItem = NULL;
 
 // ----------------------------------------------------------------------------
 // private functions
@@ -1512,12 +1531,27 @@ wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent,
     tvIns.item.lParam = (LPARAM)param;
     tvIns.item.mask = mask;
 
+    // don't use the hack below for the children of hidden root: this results
+    // in a crash inside comctl32.dll when we call TreeView_GetItemRect()
+    const bool firstChild = !IsHiddenRoot(parent) &&
+                                !TreeView_GetChild(GetHwnd(), HITEM(parent));
+
     HTREEITEM id = TreeView_InsertItem(GetHwnd(), &tvIns);
     if ( id == 0 )
     {
         wxLogLastError(wxT("TreeView_InsertItem"));
     }
 
+    // apparently some Windows versions (2000 and XP are reported to do this)
+    // sometimes don't refresh the tree after adding the first child and so we
+    // need this to make the "[+]" appear
+    if ( firstChild )
+    {
+        RECT rect;
+        TreeView_GetItemRect(GetHwnd(), HITEM(parent), &rect, FALSE);
+        ::InvalidateRect(GetHwnd(), &rect, FALSE);
+    }
+
     // associate the application tree item with Win32 tree item handle
     param->SetItem(id);
 
@@ -1586,6 +1620,10 @@ wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent,
 
 void wxTreeCtrl::Delete(const wxTreeItemId& item)
 {
+    // unlock tree selections on vista, without this the
+    // tree ctrl will eventually crash after item deletion
+    TreeItemUnlocker unlock_all;
+
     if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
     {
         wxLogLastError(wxT("TreeView_DeleteItem"));
@@ -1595,6 +1633,9 @@ void wxTreeCtrl::Delete(const wxTreeItemId& item)
 // delete all children (but don't delete the item itself)
 void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
 {
+    // unlock tree selections on vista for the duration of this call
+    TreeItemUnlocker unlock_all;
+
     wxTreeItemIdValue cookie;
 
     wxArrayTreeItemIds children;
@@ -1618,6 +1659,9 @@ void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
 
 void wxTreeCtrl::DeleteAllItems()
 {
+    // unlock tree selections on vista for the duration of this call
+    TreeItemUnlocker unlock_all;
+
     // delete the "virtual" root item.
     if ( GET_VIRTUAL_ROOT() )
     {
@@ -1665,7 +1709,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
 }
@@ -1727,7 +1771,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select)
                   _T("SelectItem(false) works only for multiselect") );
 
     wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
-    if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
+    if ( !HandleWindowEvent(event) || event.IsAllowed() )
     {
         if ( HasFlag(wxTR_MULTIPLE) )
         {
@@ -1748,7 +1792,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select)
         }
 
         event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
-        (void)GetEventHandler()->ProcessEvent(event);
+        (void)HandleWindowEvent(event);
     }
     //else: program vetoed the change
 }
@@ -1948,7 +1992,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;
@@ -2039,7 +2085,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
 
         event.m_pointDrag = pt;
 
-        if ( GetEventHandler()->ProcessEvent(event) )
+        if ( HandleWindowEvent(event) )
             processed = true;
         //else: continue with generating wxEVT_CONTEXT_MENU in base class code
     }
@@ -2164,7 +2210,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
 
                     // fire EVT_RIGHT_DOWN
                     HandleMouseEvent(nMsg, x, y, wParam);
-                    
+
                     // send NM_RCLICK
                     NMHDR nmhdr;
                     nmhdr.hwndFrom = GetHwnd();
@@ -2174,7 +2220,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
                                   nmhdr.idFrom, (LPARAM)&nmhdr);
 
                     // prevent tree control default processing, as we've
-                    // already done everything                   
+                    // already done everything
                     processed = true;
                 }
                 break;
@@ -2278,7 +2324,7 @@ WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
                     wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, this, htItem);
                     event.m_pointDrag = wxPoint(x, y);
 
-                    (void)GetEventHandler()->ProcessEvent(event);
+                    (void)HandleWindowEvent(event);
 
                     // if we don't do it, the tree seems to think that 2 items
                     // are selected simultaneously which is quite weird
@@ -2603,8 +2649,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;
 
@@ -2622,7 +2667,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;
@@ -2631,7 +2676,7 @@ 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;
@@ -2640,11 +2685,12 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
         // 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:
             {
@@ -2653,11 +2699,11 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                 {
                     // get info about the item about to be changed
                     NMTVITEMCHANGE* info = (NMTVITEMCHANGE*)lParam;
-                    if (info->hItem != gs_unlockItem)
+                    if (TreeItemUnlocker::IsLocked(info->hItem))
                     {
                         // item's state is locked, don't allow the change
                         // returning 1 will disallow the change
-                        *result = 1; 
+                        *result = 1;
                         return true;
                     }
                 }
@@ -2848,7 +2894,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 )