Sync brsfolder.c with keyboard shortcut code from wine (#2290)
authorRobert Naumann <gonzomdx@gmail.com>
Wed, 18 Mar 2020 20:57:10 +0000 (21:57 +0100)
committerGitHub <noreply@github.com>
Wed, 18 Mar 2020 20:57:10 +0000 (21:57 +0100)
* [SHELL32] Add shortcut to rename folders with the F2 key.

Sync Wine Commit 2e25a43f3fb6230460447bae6fb5db2edbd4a42f by Fabian Maurer

* [SHELL32] Add shortcut to delete folders with the delete key.

Sync Wine Commit 43f44ffb3779ff23c863d9b3297f92720e7e3733 by Fabian Maurer

* Disable the code for file deletion. The Wine way doesn't work on ROS. Add an explaining comment why we can't use the Wine code, as requested by Amine

dll/win32/shell32/wine/brsfolder.c

index 499e040..be45792 100644 (file)
@@ -41,6 +41,8 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
+#define SHV_CHANGE_NOTIFY (WM_USER + 0x1111)
+
 /* original margins and control size */
 typedef struct tagLAYOUT_DATA
 {
@@ -56,6 +58,7 @@ typedef struct tagbrowse_info
     LPITEMIDLIST  pidlRet;
     LAYOUT_DATA  *layout;  /* filled by LayoutInit, used by LayoutUpdate */
     SIZE          szMin;
+    ULONG         hNotify; /* change notification handle */
 } browse_info;
 
 typedef struct tagTV_ITEMDATA
@@ -615,6 +618,67 @@ static LRESULT BrsFolder_Treeview_Rename(browse_info *info, NMTVDISPINFOW *pnmtv
     return 0;
 }
 
+static HRESULT BrsFolder_Rename(browse_info *info, HTREEITEM rename)
+{
+    SendMessageW(info->hwndTreeView, TVM_SELECTITEM, TVGN_CARET, (LPARAM)rename);
+    SendMessageW(info->hwndTreeView, TVM_EDITLABELW, 0, (LPARAM)rename);
+    return S_OK;
+}
+
+static LRESULT BrsFolder_Treeview_Keydown(browse_info *info, LPNMTVKEYDOWN keydown)
+{
+    HTREEITEM selected_item;
+
+    /* Old dialog doesn't support those advanced features */
+    if (!(info->lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE))
+        return 0;
+
+    selected_item = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CARET, 0);
+
+    switch (keydown->wVKey)
+    {
+    case VK_F2:
+        BrsFolder_Rename(info, selected_item);
+        break;
+    case VK_DELETE:
+        {
+#ifdef __REACTOS__
+            /*********************************************************
+            FIXME: Add a proper alternative implementation for ReactOS
+
+            NOTES: Wine makes use of the ISFHelper interface, which we
+            don't have in ReactOS.
+            It's defined in dlls/shell32/shellfolder.h and implemented
+            in dlls/shell32/shfldr_fs.c on Wine's side.
+            *********************************************************/
+#else
+            const ITEMIDLIST *item_id;
+            ISFHelper *psfhlp;
+            HRESULT hr;
+            TVITEMW item;
+            TV_ITEMDATA *item_data;
+
+            item.mask  = TVIF_PARAM;
+            item.mask  = TVIF_HANDLE|TVIF_PARAM;
+            item.hItem = selected_item;
+            SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
+            item_data = (TV_ITEMDATA *)item.lParam;
+            item_id = item_data->lpi;
+
+            hr = IShellFolder_QueryInterface(item_data->lpsfParent, &IID_ISFHelper, (void**)&psfhlp);
+            if(FAILED(hr))
+                return 0;
+
+            /* perform the item deletion - tree view gets updated over shell notification */
+            ISFHelper_DeleteItems(psfhlp, 1, &item_id);
+            ISFHelper_Release(psfhlp);
+#endif
+        }
+        break;
+    }
+    return 0;
+}
+
 static LRESULT BrsFolder_OnNotify( browse_info *info, UINT CtlID, LPNMHDR lpnmh )
 {
     NMTREEVIEWW *pnmtv = (NMTREEVIEWW *)lpnmh;
@@ -641,6 +705,9 @@ static LRESULT BrsFolder_OnNotify( browse_info *info, UINT CtlID, LPNMHDR lpnmh
     case TVN_ENDLABELEDITA:
     case TVN_ENDLABELEDITW:
         return BrsFolder_Treeview_Rename( info, (LPNMTVDISPINFOW)pnmtv );
+    
+    case TVN_KEYDOWN:
+        return BrsFolder_Treeview_Keydown( info, (LPNMTVKEYDOWN)pnmtv );
 
     default:
         WARN("unhandled (%d)\n", pnmtv->hdr.code);
@@ -653,6 +720,8 @@ static LRESULT BrsFolder_OnNotify( browse_info *info, UINT CtlID, LPNMHDR lpnmh
 
 static BOOL BrsFolder_OnCreate( HWND hWnd, browse_info *info )
 {
+    LPITEMIDLIST computer_pidl;
+    SHChangeNotifyEntry ntreg;
     LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
 
     info->hWnd = hWnd;
@@ -718,18 +787,19 @@ static BOOL BrsFolder_OnCreate( HWND hWnd, browse_info *info )
     else
         ERR("treeview control missing!\n");
 
+    /* Register for change notifications */
+    SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 0, &computer_pidl);
+
+    ntreg.pidl = computer_pidl;
+    ntreg.fRecursive = TRUE;
+
+    info->hNotify = SHChangeNotifyRegister(hWnd, SHCNRF_InterruptLevel, SHCNE_ALLEVENTS, SHV_CHANGE_NOTIFY, 1, &ntreg);
+
     browsefolder_callback( info->lpBrowseInfo, hWnd, BFFM_INITIALIZED, 0 );
 
     return TRUE;
 }
 
-static HRESULT BrsFolder_Rename(browse_info *info, HTREEITEM rename)
-{
-    SendMessageW(info->hwndTreeView, TVM_SELECTITEM, TVGN_CARET, (LPARAM)rename);
-    SendMessageW(info->hwndTreeView, TVM_EDITLABELW, 0, (LPARAM)rename);
-    return S_OK;
-}
-
 static HRESULT BrsFolder_NewFolder(browse_info *info)
 {
     DWORD flags = BrowseFlagsToSHCONTF(info->lpBrowseInfo->ulFlags);
@@ -1024,9 +1094,62 @@ static INT BrsFolder_OnDestroy(browse_info *info)
         info->layout = NULL;
     }
 
+    SHChangeNotifyDeregister(info->hNotify);
+
     return 0;
 }
 
+/* Find a treeview node by recursively walking the treeview */
+static HTREEITEM BrsFolder_FindItemByPidl(browse_info *info, LPCITEMIDLIST pidl, HTREEITEM hItem)
+{
+    TV_ITEMW item;
+    TV_ITEMDATA *item_data;
+    HRESULT hr;
+
+    item.mask = TVIF_HANDLE | TVIF_PARAM;
+    item.hItem = hItem;
+    SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
+    item_data = (TV_ITEMDATA *)item.lParam;
+
+    hr = IShellFolder_CompareIDs(item_data->lpsfParent, 0, item_data->lpifq, pidl);
+    if(SUCCEEDED(hr) && !HRESULT_CODE(hr))
+        return hItem;
+
+    hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);
+
+    while (hItem)
+    {
+        HTREEITEM newItem = BrsFolder_FindItemByPidl(info, pidl, hItem);
+        if (newItem)
+            return newItem;
+        hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem);
+    }
+    return NULL;
+}
+
+static LRESULT BrsFolder_OnChange(browse_info *info, const LPCITEMIDLIST *pidls, LONG event)
+{
+    BOOL ret = TRUE;
+
+    TRACE("(%p)->(%p, %p, 0x%08x)\n", info, pidls[0], pidls[1], event);
+
+    switch (event)
+    {
+        case SHCNE_RMDIR:
+        case SHCNE_DELETE:
+        {
+            HTREEITEM handle_root = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_ROOT, 0);
+            HTREEITEM handle_item = BrsFolder_FindItemByPidl(info, pidls[0], handle_root);
+
+            if (handle_item)
+                SendMessageW(info->hwndTreeView, TVM_DELETEITEM, 0, (LPARAM)handle_item);
+
+            break;
+        }
+    }
+    return ret;
+}
+
 /*************************************************************************
  *             BrsFolderDlgProc32  (not an exported API function)
  */
@@ -1087,6 +1210,9 @@ static INT_PTR CALLBACK BrsFolderDlgProc( HWND hWnd, UINT msg, WPARAM wParam,
     case BFFM_SETEXPANDED: /* unicode only */
         return BrsFolder_OnSetExpanded(info, (LPVOID)lParam, (BOOL)wParam, NULL);
 
+    case SHV_CHANGE_NOTIFY:
+        return BrsFolder_OnChange(info, (const LPCITEMIDLIST*)wParam, (LONG)lParam);
+
     case WM_DESTROY:
         return BrsFolder_OnDestroy(info);
     }