2
0
Эх сурвалжийг харах

ADD: Show context menu for multiple objects from different path

Alexander Koblov 3 жил өмнө
parent
commit
5464a345c3

+ 36 - 11
src/platform/win/ushellcontextmenu.pas

@@ -79,7 +79,8 @@ uses
   graphtype, intfgraphics, Graphics, uPixMapManager, Dialogs, uLng, uMyWindows,
   graphtype, intfgraphics, Graphics, uPixMapManager, Dialogs, uLng, uMyWindows,
   uShellExecute, fMain, uDCUtils, uFormCommands, DCOSUtils, uOSUtils, uShowMsg,
   uShellExecute, fMain, uDCUtils, uFormCommands, DCOSUtils, uOSUtils, uShowMsg,
   uExts, uFileSystemFileSource, DCConvertEncoding, LazUTF8, uOSForms, uGraphics,
   uExts, uFileSystemFileSource, DCConvertEncoding, LazUTF8, uOSForms, uGraphics,
-  Forms, DCWindows, DCStrUtils, Clipbrd, uFileSystemWatcher;
+  Forms, DCWindows, DCStrUtils, Clipbrd, uFileSystemWatcher, uShellFolder,
+  uOleDragDrop;
 
 
 const
 const
   USER_CMD_ID = $1000;
   USER_CMD_ID = $1000;
@@ -127,18 +128,20 @@ begin
 end;
 end;
 
 
 function GetForegroundContextMenu(Handle: HWND; Files: TFiles): IContextMenu;
 function GetForegroundContextMenu(Handle: HWND; Files: TFiles): IContextMenu;
-type
-  PPIDLArray = ^PItemIDList;
-
 var
 var
-  Folder, DesktopFolder: IShellFolder;
-  PathPIDL: PItemIDList = nil;
-  tmpPIDL: PItemIDList = nil;
-  S: WideString;
-  List: PPIDLArray = nil;
-  I: integer;
+  I: Integer;
   pchEaten: ULONG;
   pchEaten: ULONG;
+  S: UnicodeString;
+  APath: UnicodeString;
+  AFolder: TShellFolder;
+  AMenu: TDefContextMenu;
   dwAttributes: ULONG = 0;
   dwAttributes: ULONG = 0;
+  List: PPItemIDList = nil;
+  ASamePath: Boolean = True;
+  tmpPIDL: PItemIDList = nil;
+  PathPIDL: PItemIDList = nil;
+  ADataObject: THDropDataObject;
+  Folder, DesktopFolder: IShellFolder;
 begin
 begin
   Result := nil;
   Result := nil;
 
 
@@ -146,6 +149,7 @@ begin
   try
   try
     List := CoTaskMemAlloc(SizeOf(PItemIDList) * Files.Count);
     List := CoTaskMemAlloc(SizeOf(PItemIDList) * Files.Count);
     ZeroMemory(List, SizeOf(PItemIDList) * Files.Count);
     ZeroMemory(List, SizeOf(PItemIDList) * Files.Count);
+    APath:= CeUtf8ToUtf16(Files[0].Path);
 
 
     for I := 0 to Files.Count - 1 do
     for I := 0 to Files.Count - 1 do
     begin
     begin
@@ -154,6 +158,11 @@ begin
       else
       else
         S := CeUtf8ToUtf16(Files[I].Path);
         S := CeUtf8ToUtf16(Files[I].Path);
 
 
+      if ASamePath then
+      begin
+        ASamePath:= UnicodeSameText(S, APath);
+      end;
+
       OleCheckUTF8(DeskTopFolder.ParseDisplayName(Handle, nil, PWideChar(S), pchEaten, PathPIDL, dwAttributes));
       OleCheckUTF8(DeskTopFolder.ParseDisplayName(Handle, nil, PWideChar(S), pchEaten, PathPIDL, dwAttributes));
       try
       try
         OleCheckUTF8(DeskTopFolder.BindToObject(PathPIDL, nil, IID_IShellFolder, Folder));
         OleCheckUTF8(DeskTopFolder.BindToObject(PathPIDL, nil, IID_IShellFolder, Folder));
@@ -170,8 +179,24 @@ begin
       (List + i)^ := tmpPIDL;
       (List + i)^ := tmpPIDL;
     end;
     end;
 
 
-    Folder.GetUIObjectOf(Handle, Files.Count, PItemIDList(List^), IID_IContextMenu, nil, Result);
+    if (Win32MajorVersion < 6) or (ASamePath) then
+      Folder.GetUIObjectOf(Handle, Files.Count, PItemIDList(List^), IID_IContextMenu, nil, Result)
+    else begin
+      AMenu:= Default(TDefContextMenu);
+      AMenu.hwnd:= Handle;
+      ADataObject:= THDropDataObject.Create(DROPEFFECT_NONE);
+      AFolder:= TShellFolder.Create(DeskTopFolder, ADataObject);
+
+      for I := 0 to Files.Count - 1 do
+      begin
+        ADataObject.Add(Files[I].FullPath);
+      end;
+      AMenu.psf:= AFolder;
+      AMenu.cidl:= Files.Count;
+      AMenu.apidl:= PPItemIDList(List);
 
 
+      OleCheckUTF8(CreateDefaultContextMenu(AMenu, IID_IContextMenu, Result));
+    end;
   finally
   finally
     if Assigned(List) then
     if Assigned(List) then
     begin
     begin

+ 156 - 7
src/platform/win/ushellfolder.pas

@@ -5,7 +5,7 @@ unit uShellFolder;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, Windows, ShlObj, ActiveX, ComObj, uShlObjAdditional;
+  Classes, SysUtils, Windows, ShlObj, ActiveX, ComObj, ShlWapi, uShlObjAdditional;
 
 
 const
 const
   FOLDERID_AccountPictures: TGUID = '{008ca0b1-55b4-4c56-b8a8-4de4b299d3be}';
   FOLDERID_AccountPictures: TGUID = '{008ca0b1-55b4-4c56-b8a8-4de4b299d3be}';
@@ -55,16 +55,67 @@ const
   FOLDERID_UserProgramFiles: TGUID = '{5CD7AEE2-2219-4A67-B85D-6C9CE15660CB}';
   FOLDERID_UserProgramFiles: TGUID = '{5CD7AEE2-2219-4A67-B85D-6C9CE15660CB}';
   FOLDERID_UserProgramFilesCommon: TGUID = '{BCBD3057-CA5C-4622-B42D-BC56DB0AE516}';
   FOLDERID_UserProgramFilesCommon: TGUID = '{BCBD3057-CA5C-4622-B42D-BC56DB0AE516}';
 
 
+type
+
+  PPItemIDList = ^PItemIDList;
+
+  TDefContextMenu = record
+    hwnd                  : HWND;
+    pcmcb                 : IUnknown;
+    pidlFolder            : PCIDLIST_ABSOLUTE;
+    psf                   : IShellFolder;
+    cidl                  : UINT;
+    apidl                 : PPItemIDList;
+    punkAssociationInfo   : IUnknown;
+    cKeys                 : UINT;
+    aKeys                 : PHKEY;
+  end;
+
+  { TShellFolder }
+
+  TShellFolder = class(TInterfacedObject, IShellFolder)
+  private
+    FFolder: IShellFolder;
+    FDataObject: IDataObject;
+  protected
+    function QueryInterface(constref iid : tguid; out obj) : longint; stdcall;
+  public
+    constructor Create(AFolder: IShellFolder; DataObject: IDataObject);
+  public
+    function ParseDisplayName(hwndOwner: HWND; pbcReserved: Pointer; lpszDisplayName: POLESTR; out pchEaten: ULONG; out ppidl: PItemIDList; var dwAttributes: ULONG): HRESULT; stdcall;
+    function EnumObjects(hwndOwner: HWND; grfFlags: DWORD; out EnumIDList: IEnumIDList): HRESULT; stdcall;
+    function BindToObject(pidl: PItemIDList; pbcReserved: Pointer; const riid: TIID; out ppvOut): HRESULT; stdcall;
+    function BindToStorage(pidl: PItemIDList; pbcReserved: Pointer; const riid: TIID; out ppvObj): HRESULT; stdcall;
+    function CompareIDs(lParam: LPARAM; pidl1, pidl2: PItemIDList): HRESULT; stdcall;
+    function CreateViewObject(hwndOwner: HWND; const riid: TIID; out ppvOut): HRESULT; stdcall;
+    function GetAttributesOf(cidl: UINT; var apidl: PItemIDList; var rgfInOut: UINT): HRESULT; stdcall;
+    function GetUIObjectOf(hwndOwner: HWND; cidl: UINT; var apidl: PItemIDList; const riid: TIID; prgfInOut: Pointer; out ppvOut): HRESULT; stdcall;
+    function GetDisplayNameOf(pidl: PItemIDList; uFlags: DWORD; var lpName: TStrRet): HRESULT; stdcall;
+    function SetNameOf(hwndOwner: HWND; pidl: PItemIDList; lpszName: POLEStr; uFlags: DWORD; var ppidlOut: PItemIDList): HRESULT; stdcall;
+  end;
+
 function GetKnownFolderPath(const rfid: TGUID; out APath: String): Boolean;
 function GetKnownFolderPath(const rfid: TGUID; out APath: String): Boolean;
 
 
+function MultiFileProperties(pdtobj: IDataObject; dwFlags: DWORD): HRESULT;
+
 function GetDisplayName(AFolder: IShellFolder; PIDL: PItemIDList; Flags: DWORD): String;
 function GetDisplayName(AFolder: IShellFolder; PIDL: PItemIDList; Flags: DWORD): String;
 function GetDetails(AFolder: IShellFolder2; PIDL: PItemIDList; const pscid: SHCOLUMNID): OleVariant;
 function GetDetails(AFolder: IShellFolder2; PIDL: PItemIDList; const pscid: SHCOLUMNID): OleVariant;
 
 
+function CreateDefaultContextMenu(constref pdcm: TDefContextMenu; const riid: REFIID; out ppv): HRESULT;
+
 implementation
 implementation
 
 
 uses
 uses
   ShellApi, LazUTF8, DCConvertEncoding;
   ShellApi, LazUTF8, DCConvertEncoding;
 
 
+const
+  KF_FLAG_DEFAULT = $00000000;
+
+var
+  SHMultiFileProperties: function(pdtobj: IDataObject; dwFlags: DWORD): HRESULT; stdcall;
+  SHCreateDefaultContextMenu: function(constref pdcm: TDefContextMenu; const riid: REFIID; out ppv): HRESULT; stdcall;
+  SHGetKnownFolderPath: function(const rfid: TGUID; dwFlags: DWORD; hToken: HANDLE; out ppszPath: LPCWSTR): HRESULT; stdcall;
+
 function StrRetToString(PIDL: PItemIDList; StrRet: TStrRet): String;
 function StrRetToString(PIDL: PItemIDList; StrRet: TStrRet): String;
 var
 var
   S: array[0..MAX_PATH] of WideChar;
   S: array[0..MAX_PATH] of WideChar;
@@ -75,6 +126,11 @@ begin
     Result:= CeUtf16ToUtf8(UnicodeString(S));
     Result:= CeUtf16ToUtf8(UnicodeString(S));
 end;
 end;
 
 
+function MultiFileProperties(pdtobj: IDataObject; dwFlags: DWORD): HRESULT;
+begin
+  Result:= SHMultiFileProperties(pdtobj, dwFlags);
+end;
+
 function GetDisplayName(AFolder: IShellFolder; PIDL: PItemIDList;
 function GetDisplayName(AFolder: IShellFolder; PIDL: PItemIDList;
                         Flags: DWORD): String;
                         Flags: DWORD): String;
 var
 var
@@ -98,11 +154,11 @@ begin
    Result:= Unassigned;
    Result:= Unassigned;
 end;
 end;
 
 
-const
-  KF_FLAG_DEFAULT = $00000000;
-
-var
-  SHGetKnownFolderPath: function(const rfid: TGUID; dwFlags: DWORD; hToken: HANDLE; out ppszPath: LPCWSTR): HRESULT; stdcall;
+function CreateDefaultContextMenu(constref pdcm: TDefContextMenu;
+  const riid: REFIID; out ppv): HRESULT;
+begin
+  Result:= SHCreateDefaultContextMenu(pdcm, riid, ppv);
+end;
 
 
 function GetKnownFolderPath(const rfid: TGUID; out APath: String): Boolean;
 function GetKnownFolderPath(const rfid: TGUID; out APath: String): Boolean;
 var
 var
@@ -113,7 +169,100 @@ begin
   CoTaskMemFree(ppszPath);
   CoTaskMemFree(ppszPath);
 end;
 end;
 
 
+{ TShellFolder }
+
+function TShellFolder.QueryInterface(constref iid: tguid; out obj): longint;
+  stdcall;
+begin
+  Result:= FFolder.QueryInterface(iid, obj);
+end;
+
+constructor TShellFolder.Create(AFolder: IShellFolder; DataObject: IDataObject);
+begin
+  FFolder:= AFolder;
+  FDataObject:= DataObject;
+end;
+
+function TShellFolder.ParseDisplayName(hwndOwner: HWND; pbcReserved: Pointer;
+  lpszDisplayName: POLESTR; out pchEaten: ULONG; out ppidl: PItemIDList;
+  var dwAttributes: ULONG): HRESULT; stdcall;
+begin
+  Result:= FFolder.ParseDisplayName(hwndOwner, pbcReserved, lpszDisplayName, pchEaten, ppidl, dwAttributes);
+end;
+
+function TShellFolder.EnumObjects(hwndOwner: HWND; grfFlags: DWORD; out
+  EnumIDList: IEnumIDList): HRESULT; stdcall;
+begin
+  Result:= EnumObjects(hwndOwner, grfFlags, EnumIDList);
+end;
+
+function TShellFolder.BindToObject(pidl: PItemIDList; pbcReserved: Pointer;
+  const riid: TIID; out ppvOut): HRESULT; stdcall;
+begin
+  Result:= FFolder.BindToObject(pidl, pbcReserved, riid, ppvOut);
+end;
+
+function TShellFolder.BindToStorage(pidl: PItemIDList; pbcReserved: Pointer;
+  const riid: TIID; out ppvObj): HRESULT; stdcall;
+begin
+  Result:= FFolder.BindToStorage(pidl, pbcReserved, riid, ppvObj);
+end;
+
+function TShellFolder.CompareIDs(lParam: LPARAM; pidl1, pidl2: PItemIDList): HRESULT; stdcall;
+begin
+  Result:= FFolder.CompareIDs(lParam, pidl1, pidl2);
+end;
+
+function TShellFolder.CreateViewObject(hwndOwner: HWND; const riid: TIID; out
+  ppvOut): HRESULT; stdcall;
+begin
+  Result:= FFolder.CreateViewObject(hwndOwner, riid, ppvOut);
+end;
+
+function TShellFolder.GetAttributesOf(cidl: UINT; var apidl: PItemIDList;
+  var rgfInOut: UINT): HRESULT; stdcall;
+begin
+  Result:= FFolder.GetAttributesOf(cidl, apidl, rgfInOut);
+end;
+
+function TShellFolder.GetUIObjectOf(hwndOwner: HWND; cidl: UINT;
+  var apidl: PItemIDList; const riid: TIID; prgfInOut: Pointer; out ppvOut
+  ): HRESULT; stdcall;
+begin
+  if (IsEqualGUID(riid, IID_IDataObject)) then
+    Result:= FDataObject.QueryInterface(riid, ppvOut)
+  else begin
+    Result:= FFolder.GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
+  end;
+end;
+
+function TShellFolder.GetDisplayNameOf(pidl: PItemIDList; uFlags: DWORD;
+  var lpName: TStrRet): HRESULT; stdcall;
+begin
+  Result:= FFolder.GetDisplayNameOf(pidl, uFlags, lpName);
+end;
+
+function TShellFolder.SetNameOf(hwndOwner: HWND; pidl: PItemIDList;
+  lpszName: POLEStr; uFlags: DWORD; var ppidlOut: PItemIDList): HRESULT;
+  stdcall;
+begin
+  Result:= FFolder.SetNameOf(hwndOwner, pidl, lpszName, uFlags, ppidlOut);
+end;
+
+var
+  AModule: HMODULE;
+
 initialization
 initialization
-  if Win32MajorVersion > 5 then @SHGetKnownFolderPath:= GetProcAddress(GetModuleHandleW(shell32), 'SHGetKnownFolderPath');
+  if CheckWin32Version(5, 1) then
+  begin
+    AModule:= GetModuleHandleW(shell32);
+    @SHMultiFileProperties:= GetProcAddress(AModule, 'SHMultiFileProperties');
+
+    if Win32MajorVersion > 5 then
+    begin
+      @SHGetKnownFolderPath:= GetProcAddress(AModule, 'SHGetKnownFolderPath');
+      @SHCreateDefaultContextMenu:= GetProcAddress(AModule, 'SHCreateDefaultContextMenu');
+    end;
+  end;
 end.
 end.