]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/treectrl.cpp
Menubar accelerators are now preserved
[wxWidgets.git] / src / msw / treectrl.cpp
index f3733657d919e55438873dba35c8695b4cffc7db..631b4ebce3435c605e48cd9be4fbabf139c6a78a 100644 (file)
 // get HTREEITEM from wxTreeItemId
 #define HITEM(item)     ((HTREEITEM)(((item).m_pItem)))
 
+
+// older SDKs are missing these
+#ifndef TVN_ITEMCHANGINGA
+
+#define TVN_ITEMCHANGINGA (TVN_FIRST-16)
+#define TVN_ITEMCHANGINGW (TVN_FIRST-17)
+
+typedef struct tagNMTVITEMCHANGE
+{
+    NMHDR hdr;
+    UINT uChanged;
+    HTREEITEM hItem;
+    UINT uStateNew;
+    UINT uStateOld;
+    LPARAM lParam;
+} NMTVITEMCHANGE;
+
+#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
+// multi-select mode on vista systems.
+
+static HTREEITEM gs_unlockItem = NULL;
+
+class TreeItemUnlocker
+{
+public:
+    TreeItemUnlocker(HTREEITEM item) { gs_unlockItem = item; }
+    ~TreeItemUnlocker() { gs_unlockItem = NULL; }
+};
+
+
 // ----------------------------------------------------------------------------
 // private functions
 // ----------------------------------------------------------------------------
@@ -65,6 +99,8 @@ static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem)
     tvi.stateMask = TVIS_SELECTED;
     tvi.hItem = hItem;
 
+    TreeItemUnlocker unlocker(hItem);
+
     if ( !TreeView_GetItem(hwndTV, &tvi) )
     {
         wxLogLastError(wxT("TreeView_GetItem"));
@@ -81,6 +117,8 @@ static bool SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = true)
     tvi.state = select ? TVIS_SELECTED : 0;
     tvi.hItem = hItem;
 
+    TreeItemUnlocker unlocker(hItem);
+
     if ( TreeView_SetItem(hwndTV, &tvi) == -1 )
     {
         wxLogLastError(wxT("TreeView_SetItem"));
@@ -804,6 +842,8 @@ bool wxTreeCtrl::DoGetItem(wxTreeViewItem *tvItem) const
 
 void wxTreeCtrl::DoSetItem(wxTreeViewItem *tvItem)
 {
+    TreeItemUnlocker unlocker(tvItem->hItem);
+
     if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 )
     {
         wxLogLastError(wxT("TreeView_SetItem"));
@@ -934,7 +974,7 @@ void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
     {
         if ( item == m_idEdited )
         {
-            ::SetWindowText(hwndEdit, text);
+            ::SetWindowText(hwndEdit, text.wx_str());
         }
     }
 }
@@ -1354,7 +1394,15 @@ wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
     wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() for must be visible itself!"));
 
-    return wxTreeItemId(TreeView_GetNextVisible(GetHwnd(), HITEM(item)));
+    wxTreeItemId next(TreeView_GetNextVisible(GetHwnd(), HITEM(item)));
+    if ( next.IsOk() && !IsVisible(next) )
+    {
+        // Win32 considers that any non-collapsed item is visible while we want
+        // to return only really visible items
+        next.Unset();
+    }
+
+    return next;
 }
 
 wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
@@ -1362,7 +1410,15 @@ wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
     wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() for must be visible itself!"));
 
-    return wxTreeItemId(TreeView_GetPrevVisible(GetHwnd(), HITEM(item)));
+    wxTreeItemId prev(TreeView_GetPrevVisible(GetHwnd(), HITEM(item)));
+    if ( prev.IsOk() && !IsVisible(prev) )
+    {
+        // just as above, Win32 function will happily return the previous item
+        // in the tree for the first visible item too
+        prev.Unset();
+    }
+
+    return prev;
 }
 
 // ----------------------------------------------------------------------------
@@ -1902,8 +1958,10 @@ bool wxTreeCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
     return wxTreeCtrlBase::MSWShouldPreProcessMessage(msg);
 }
 
-bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id)
+bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id_)
 {
+    const int id = (signed short)id_;
+
     if ( cmd == EN_UPDATE )
     {
         wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
@@ -2546,6 +2604,36 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             }
             break;
 
+
+        // 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:
+            {
+                // we only need to handles these in multi-select trees
+                if ( HasFlag(wxTR_MULTIPLE) )
+                {
+                    // get info about the item about to be changed
+                    NMTVITEMCHANGE* info = (NMTVITEMCHANGE*)lParam;
+                    if (info->hItem != gs_unlockItem)
+                    {
+                        // item's state is locked, don't allow the change
+                        // returning 1 will disallow the change
+                        *result = 1; 
+                        return true;
+                    }
+                }
+
+                // allow the state change
+            }
+            return false;
+
         // NB: MSLU is broken and sends TVN_SELCHANGEDA instead of
         //     TVN_SELCHANGEDW in Unicode mode under Win98. Therefore
         //     we have to handle both messages:
@@ -2575,6 +2663,19 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     event.m_itemOld = tv->itemOld.hItem;
                 }
             }
+
+            // we receive this message from WM_LBUTTONDOWN handler inside
+            // comctl32.dll and so before the click is passed to
+            // DefWindowProc() which sets the focus to the window which was
+            // clicked and this can lead to unexpected event sequences: for
+            // example, we may get a "selection change" event from the tree
+            // before getting a "kill focus" event for the text control which
+            // had the focus previously, thus breaking user code doing input
+            // validation
+            //
+            // to avoid such surprises, we force the generation of focus events
+            // now, before we generate the selection change ones
+            SetFocus();
             break;
 
             // instead of explicitly checking for _WIN32_IE, check if the
@@ -2841,17 +2942,40 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             break;
 
         case TVN_ITEMEXPANDED:
-            // the item is not refreshed properly after expansion when it has
-            // an image depending on the expanded/collapsed state - bug in
-            // comctl32.dll or our code?
             {
                 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
-                wxTreeItemId id(tv->itemNew.hItem);
+                const wxTreeItemId id(tv->itemNew.hItem);
 
-                int image = GetItemImage(id, wxTreeItemIcon_Expanded);
-                if ( image != -1 )
+                if ( tv->action == TVE_COLLAPSE )
                 {
-                    RefreshItem(id);
+                    if ( wxApp::GetComCtl32Version() >= 600 )
+                    {
+                        // for some reason the item selection rectangle depends
+                        // on whether it is expanded or collapsed (at least
+                        // with comctl32.dll v6): it is wider (by 3 pixels) in
+                        // the expanded state, so when the item collapses and
+                        // then is deselected the rightmost 3 pixels of the
+                        // previously drawn selection are left on the screen
+                        //
+                        // it's not clear if it's a bug in comctl32.dll or in
+                        // our code (because it does not happen in Explorer but
+                        // OTOH we don't do anything which could result in this
+                        // AFAICS) but we do need to work around it to avoid
+                        // ugly artifacts
+                        RefreshItem(id);
+                    }
+                }
+                else // expand
+                {
+                    // the item is also not refreshed properly after expansion when
+                    // it has an image depending on the expanded/collapsed state:
+                    // again, it's not clear if the bug is in comctl32.dll or our
+                    // code...
+                    int image = GetItemImage(id, wxTreeItemIcon_Expanded);
+                    if ( image != -1 )
+                    {
+                        RefreshItem(id);
+                    }
                 }
             }
             break;