UPD: Some code for background context menu

This commit is contained in:
Alexander Koblov 2010-09-11 14:35:14 +00:00
commit fe2ca8acf9
3 changed files with 30 additions and 164 deletions

View file

@ -31,7 +31,8 @@ uses
{$IFDEF UNIX}
Graphics, BaseUnix, Unix, fFileProperties, uPixMapManager;
{$ELSE}
FileUtil, Windows, ShlObj, uShlObjAdditional, JwaDbt, uMyWindows;
FileUtil, Windows, ShlObj, ActiveX, uShlObjAdditional,
JwaShlGuid, JwaDbt, uMyWindows;
{$ENDIF}
const
@ -72,7 +73,7 @@ procedure ShowFilePropertiesDialog(aFileSource: IFileSource; const Files: TFiles
@param(X X coordinate)
@param(Y Y coordinate)
}
procedure ShowContextMenu(Owner: TWinControl; var Files : TFiles; X, Y : Integer);
procedure ShowContextMenu(Owner: TWinControl; var Files : TFiles; X, Y : Integer; Background: Boolean);
{en
Show drive context menu
@param(Owner Parent window)
@ -104,7 +105,7 @@ uses
var
{$IFDEF MSWINDOWS}
OldWProc: WNDPROC;
ShellContextMenu: IContextMenu2 = nil;
ICM2: IContextMenu2 = nil;
{$ELSE}
CM : TContextMenu = nil;
{$ENDIF}
@ -122,9 +123,9 @@ begin
WM_DRAWITEM,
WM_MENUCHAR,
WM_MEASUREITEM:
if Assigned(ShellContextMenu) then
if Assigned(ICM2) then
begin
ShellContextMenu.HandleMenuMsg(uiMsg, wParam, lParam);
ICM2.HandleMenuMsg(uiMsg, wParam, lParam);
Result := 0;
end
else
@ -214,7 +215,7 @@ begin
end;
{$ENDIF}
procedure ShowContextMenu(Owner: TWinControl; var Files : TFiles; X, Y : Integer);
procedure ShowContextMenu(Owner: TWinControl; var Files : TFiles; X, Y : Integer; Background: Boolean);
{$IFDEF MSWINDOWS}
const
USER_CMD_ID = $1000;
@ -223,6 +224,7 @@ var
sl: TStringList = nil;
i:Integer;
sAct, sCmd: UTF8String;
contMenu: IContextMenu;
menu: HMENU = 0;
hActionsSubMenu: HMENU = 0;
cmd: UINT = 0;
@ -237,11 +239,12 @@ begin
try
if Files.Count = 0 then Exit;
ShellContextMenu := TShellContextMenu.Create(Owner.Handle, Files, True);
if Assigned(ShellContextMenu) then
contMenu := GetShellContextMenu(Owner.Handle, Files, Background);
if Assigned(contMenu) then
try
menu := CreatePopupMenu;
OleCheckUTF8(ShellContextMenu.QueryContextMenu(menu, 0, 1, USER_CMD_ID - 1, CMF_EXPLORE or CMF_CANRENAME));
OleCheckUTF8(contMenu.QueryContextMenu(menu, 0, 1, USER_CMD_ID - 1, CMF_EXPLORE or CMF_CANRENAME));
contMenu.QueryInterface(IID_IContextMenu2, ICM2); // to handle submenus.
//------------------------------------------------------------------------------
{ Actions submenu }
aFile := Files[0];
@ -300,12 +303,13 @@ begin
DestroyMenu(hActionsSubMenu);
if menu <> 0 then
DestroyMenu(menu);
ICM2 := nil;
end;
if (cmd > 0) and (cmd < USER_CMD_ID) then
begin
iCmd := LongInt(Cmd) - 1;
if Succeeded(ShellContextMenu.GetCommandString(iCmd, GCS_VERBA, nil, ZVerb, SizeOf(ZVerb))) then
if Succeeded(contMenu.GetCommandString(iCmd, GCS_VERBA, nil, ZVerb, SizeOf(ZVerb))) then
begin
sVerb := StrPas(ZVerb);
@ -377,7 +381,7 @@ begin
lpVerb := PChar(cmd - 1);
nShow := SW_NORMAL;
end;
OleCheckUTF8(ShellContextMenu.InvokeCommand(cmici));
OleCheckUTF8(contMenu.InvokeCommand(cmici));
// Reload after possible changes on the filesystem.
if SameText(sVerb, sCmdVerbLink) then
@ -410,7 +414,6 @@ begin
end;
end;
finally
ShellContextMenu := nil;
FreeAndNil(Files);
if Assigned(sl) then
FreeAndNil(sl);
@ -636,7 +639,7 @@ begin
Files:= TFiles.Create(EmptyStr); // free in ShowContextMenu
Files.Add(aFile);
OldErrorMode:= SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
ShowContextMenu(Owner, Files, X, Y);
ShowContextMenu(Owner, Files, X, Y, False);
SetErrorMode(OldErrorMode);
end;
{$ELSE}
@ -694,12 +697,12 @@ end;
{$ELSE}
var
cmici: TCMINVOKECOMMANDINFO;
contMenu: IContextMenu2;
contMenu: IContextMenu;
begin
if Files.Count = 0 then Exit;
try
contMenu := TShellContextMenu.Create(frmMain.Handle, Files, False);
contMenu := GetShellContextMenu(frmMain.Handle, Files, False);
if Assigned(contMenu) then
begin
FillChar(cmici, sizeof(cmici), #0);

View file

@ -27,50 +27,22 @@ unit uShellContextMenu;
interface
uses
Classes, SysUtils, uFile, Windows, ShellAPI, ComObj, ShlObj, ActiveX,
JwaShlGuid, uShlObjAdditional;
Classes, SysUtils, uFile, Windows, ShellAPI, ComObj, ShlObj, ActiveX, JwaShlGuid, uShlObjAdditional;
type
{ TShellContextMenu }
TShellContextMenu = class(TInterfacedObject, IContextMenu2)
private
FShellNew: Boolean;
FShellMenuMin : UINT; // Shell menu first command ID
FShellMenuMax : UINT; // Shell menu last command ID
FShellNewMenuMin : UINT; // New menu first command ID
FShellNewMenuMax : UINT; // New menu last command ID
FShellMenu1 : IContextMenu;
FShellMenu2 : IContextMenu2;
FShellNewMenu2 : IContextMenu2;
FShellNewMenu : HMENU;
public
constructor Create(Handle : THandle; Files : TFiles; ShellNew: Boolean);
destructor Destroy; override;
// *** IContextMenu ***
function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;
function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult; stdcall;
// *** IContextMenu2 ***
function HandleMenuMsg(uMsg: UINT; wParam : WPARAM; lParam : LPARAM): HResult; stdcall;
end;
function GetShellContextMenu(Handle: THandle; Files: TFiles; Background: Boolean): IContextMenu;
implementation
uses
uMyWindows;
{ TShellContextMenu }
constructor TShellContextMenu.Create(Handle: THandle; Files: TFiles; ShellNew: Boolean);
function GetForegroundContextMenu(Handle : THandle; Files : TFiles): IContextMenu;
type
PPIDLArray = ^PItemIDList;
var
Folder,
DesktopFolder: IShellFolder;
newMenu: IShellExtInit;
PathPIDL: PItemIDList = nil;
tmpPIDL: PItemIDList = nil;
S: WideString;
@ -79,13 +51,9 @@ var
pchEaten: ULONG;
dwAttributes: ULONG = 0;
begin
FShellNew:= ShellNew;
FShellMenu1 := nil;
FShellMenu2 := nil;
FShellNewMenu2 := nil;
FShellNewMenu := INVALID_HANDLE_VALUE;
Result := nil;
OleCheckUTF8(SHGetDesktopFolder(DesktopFolder));
OleCheckUTF8(SHGetDesktopFolder(DeskTopFolder));
try
List := CoTaskMemAlloc(SizeOf(PItemIDList)*Files.Count);
ZeroMemory(List, SizeOf(PItemIDList)*Files.Count);
@ -113,24 +81,7 @@ begin
(List + i)^ := tmpPIDL;
end;
Folder.GetUIObjectOf(Handle, Files.Count, PItemIDList(List^), IID_IContextMenu, nil, FShellMenu1);
FShellMenu1.QueryInterface(IID_IContextMenu2, FShellMenu2); // to handle submenus.
// Add "New" submenu if needed
if FShellNew and (Files.Count = 1) and Files[0].IsDirectory then
begin
S:= UTF8Decode(Files[0].FullPath);
OleCheckUTF8(DesktopFolder.ParseDisplayName(Handle, nil, PWideChar(S), pchEaten, PathPIDL, dwAttributes));
try
CoCreateInstance(CLSID_NewMenu, nil, CLSCTX_ALL, IID_IShellExtInit, newMenu);
newMenu.Initialize(PathPIDL, nil, 0);
newMenu.QueryInterface(IID_IContextMenu2, FShellNewMenu2);
finally
newMenu:= nil;
CoTaskMemFree(PathPIDL);
end;
end;
Folder.GetUIObjectOf(Handle, Files.Count, PItemIDList(List^), IID_IContextMenu, nil, Result);
finally
if Assigned(List) then
@ -145,105 +96,17 @@ begin
end;
end;
destructor TShellContextMenu.Destroy;
function GetBackgroundContextMenu(Handle : THandle; Files : TFiles): IContextMenu;
begin
FShellMenu1:= nil;
FShellMenu2:= nil;
FShellNewMenu2 := nil;
if FShellNewMenu <> INVALID_HANDLE_VALUE then
DestroyMenu(FShellNewMenu);
inherited Destroy;
end;
function TShellContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
idCmdLast, uFlags: UINT): HResult; stdcall;
var
iPos: LongInt;
wsText: WideString;
function GetShellContextMenu(Handle: THandle; Files: TFiles; Background: Boolean): IContextMenu; inline;
begin
FShellMenuMin:= idCmdFirst;
FShellMenuMax:= (idCmdLast - idCmdFirst) div 2;
FShellNewMenuMin:= FShellMenuMax + 1;
FShellNewMenuMax:= idCmdLast;
Result:= FShellMenu1.QueryContextMenu(Menu, indexMenu, FShellMenuMin, FShellMenuMax, uFlags);
iPos:= GetMenuItemCount(Menu);
if Assigned(FShellNewMenu2) and (iPos > 0) then
begin
FShellNewMenu:= CreatePopupMenu;
FShellNewMenu2.QueryContextMenu(FShellNewMenu, indexMenu, FShellNewMenuMin, FShellNewMenuMax, uFlags);
if GetMenuItemCount(FShellNewMenu) > 0 then
begin
wsText:= GetMenuItemText(FShellNewMenu, 0, True);
InsertMenuItemEx(Menu, FShellNewMenu, PWideChar(wsText), iPos - 2, FShellMenuMax, MFT_STRING);
end;
end;
end;
function TShellContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
var
idCmd: UINT;
begin
idCmd:= UINT(lpici.lpVerb) + 1;
if (not FShellNew) or ((idCmd >= FShellMenuMin) and (idCmd <= FShellMenuMax)) then
Result:= FShellMenu1.InvokeCommand(lpici)
else if (idCmd >= FShellNewMenuMin) and (idCmd <= FShellNewMenuMax) then
Result:= FShellNewMenu2.InvokeCommand(lpici)
if Background then
Result:= GetBackgroundContextMenu(Handle, Files)
else
Result:= E_NOTIMPL;
end;
function TShellContextMenu.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
pszName: LPSTR; cchMax: UINT): HResult; stdcall;
begin
if (idCmd >= FShellMenuMin) and (idCmd <= FShellMenuMax) then
Result:= FShellMenu1.GetCommandString(idCmd, uType, pwReserved, pszName, cchMax)
else if (idCmd >= FShellNewMenuMin) and (idCmd <= FShellNewMenuMax) then
Result:= FShellNewMenu2.GetCommandString(idCmd, uType, pwReserved, pszName, cchMax)
else
Result:= E_NOTIMPL;
end;
function TShellContextMenu.HandleMenuMsg(uMsg: UINT; wParam: WPARAM; lParam: LPARAM
): HResult; stdcall;
var
itemID : UINT;
begin
Result:= E_NOTIMPL;
case uMsg of
WM_INITMENUPOPUP:
begin
// wParam is a handle to the drop-down menu or submenu
if wParam = FShellNewMenu then
Result:= FShellNewMenu2.HandleMenuMsg(uMsg, wParam, lParam)
else if Assigned(FShellMenu2) then
Result:= FShellMenu2.HandleMenuMsg(uMsg, wParam, lParam);
end;
WM_DRAWITEM:
begin
itemID:= PDRAWITEMSTRUCT(lParam)^.itemID;
if (itemID >= FShellMenuMin) and (itemID <= FShellMenuMax) then
Result:= FShellMenu2.HandleMenuMsg(uMsg, wParam, lParam)
else if (itemID >= FShellNewMenuMin) and (itemID <= FShellNewMenuMax) and Assigned(FShellNewMenu2) then
Result:= FShellNewMenu2.HandleMenuMsg(uMsg, wParam, lParam);
end;
WM_MENUCHAR:
begin
// lParam is a handle to the active menu
if lParam = FShellNewMenu then
Result:= FShellNewMenu2.HandleMenuMsg(uMsg, wParam, lParam)
else if Assigned(FShellMenu2) then
Result:= FShellMenu2.HandleMenuMsg(uMsg, wParam, lParam);
end;
WM_MEASUREITEM:
begin
itemID:= PMEASUREITEMSTRUCT(lParam)^.itemID;
if (itemID >= FShellMenuMin) and (itemID <= FShellMenuMax) then
Result:= FShellMenu2.HandleMenuMsg(uMsg, wParam, lParam)
else if (itemID >= FShellNewMenuMin) and (itemID <= FShellNewMenuMax) and Assigned(FShellNewMenu2) then
Result:= FShellNewMenu2.HandleMenuMsg(uMsg, wParam, lParam);
end;
end;
Result:= GetForegroundContextMenu(Handle, Files);
end;
end.

View file

@ -683,7 +683,7 @@ begin
try
if aFiles.Count > 0 then
try
ShowContextMenu(frmMain, aFiles, X, Y);
ShowContextMenu(frmMain, aFiles, X, Y, Background);
except
on e: EContextMenuException do
ShowException(e);