Subversion Repositories decoder

Rev

Blame | Last modification | View Log | RSS feed

  1. unit DragDropContext;
  2.  
  3. // -----------------------------------------------------------------------------
  4. // Project:         Drag and Drop Component Suite.
  5. // Module:          DragDropContext
  6. // Description:     Implements Context Menu Handler Shell Extensions.
  7. // Version:         4.0
  8. // Date:            18-MAY-2001
  9. // Target:          Win32, Delphi 5-6
  10. // Authors:         Anders Melander, anders@melander.dk, http://www.melander.dk
  11. // Copyright        © 1997-2001 Angus Johnson & Anders Melander
  12. // -----------------------------------------------------------------------------
  13. interface
  14.  
  15. uses
  16.   DragDrop,
  17.   DragDropComObj,
  18.   Menus,
  19.   ShlObj,
  20.   ActiveX,
  21.   Windows,
  22.   Classes;
  23.  
  24. {$include DragDrop.inc}
  25.  
  26. type
  27. ////////////////////////////////////////////////////////////////////////////////
  28. //
  29. //              TDropContextMenu
  30. //
  31. ////////////////////////////////////////////////////////////////////////////////
  32. // Partially based on Borland's ShellExt demo.
  33. ////////////////////////////////////////////////////////////////////////////////
  34. // A typical shell context menu handler session goes like this:
  35. // 1. User selects one or more files and right clicks on them.
  36. //    The files must of a file type which has a context menu handler registered.
  37. // 2. The shell loads the context menu handler module.
  38. // 3. The shell instantiates the registered context menu handler object as an
  39. //    in-process COM server.
  40. // 4. The IShellExtInit.Initialize method is called with a data object which
  41. //    contains the dragged data.
  42. // 5. The IContextMenu.QueryContextMenu method is called to populate the popup
  43. //    menu.
  44. //    TDropContextMenu uses the PopupMenu property to populate the shell context
  45. //    menu.
  46. // 6. If the user chooses one of the context menu menu items we have supplied,
  47. //    the IContextMenu.InvokeCommand method is called.
  48. //    TDropContextMenu locates the corresponding TMenuItem and fires the menu
  49. //    items OnClick event.
  50. // 7. The shell unloads the context menu handler module (usually after a few
  51. //    seconds).
  52. ////////////////////////////////////////////////////////////////////////////////
  53.   TDropContextMenu = class(TInterfacedComponent, IShellExtInit, IContextMenu)
  54.   private
  55.     FContextMenu: TPopupMenu;
  56.     FMenuOffset: integer;
  57.     FDataObject: IDataObject;
  58.     FOnPopup: TNotifyEvent;
  59.     FFiles: TStrings;
  60.     procedure SetContextMenu(const Value: TPopupMenu);
  61.   protected
  62.     procedure Notification(AComponent: TComponent;
  63.       Operation: TOperation); override;
  64.     { IShellExtInit }
  65.      function Initialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
  66.       hKeyProgID: HKEY): HResult; stdcall;
  67.     { IContextMenu }
  68.     function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast,
  69.       uFlags: UINT): HResult; stdcall;
  70.     function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
  71.     function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
  72.       pszName: LPSTR; cchMax: UINT): HResult; stdcall;
  73.   public
  74.     constructor Create(AOwner: TComponent); override;
  75.     destructor Destroy; override;
  76.     property DataObject: IDataObject read FDataObject;
  77.     property Files: TStrings read FFiles;
  78.   published
  79.     property ContextMenu: TPopupMenu read FContextMenu write SetContextMenu;
  80.     property OnPopup: TNotifyEvent read FOnPopup write FOnPopup;
  81.   end;
  82.  
  83. ////////////////////////////////////////////////////////////////////////////////
  84. //
  85. //              TDropContextMenuFactory
  86. //
  87. ////////////////////////////////////////////////////////////////////////////////
  88. // COM Class factory for TDropContextMenu.
  89. ////////////////////////////////////////////////////////////////////////////////
  90.   TDropContextMenuFactory = class(TShellExtFactory)
  91.   protected
  92.     function HandlerRegSubKey: string; virtual;
  93.   public
  94.     procedure UpdateRegistry(Register: Boolean); override;
  95.   end;
  96.  
  97. ////////////////////////////////////////////////////////////////////////////////
  98. //
  99. //              Component registration
  100. //
  101. ////////////////////////////////////////////////////////////////////////////////
  102. procedure Register;
  103.  
  104.  
  105. ////////////////////////////////////////////////////////////////////////////////
  106. //
  107. //              Misc.
  108. //
  109. ////////////////////////////////////////////////////////////////////////////////
  110.  
  111.  
  112. ////////////////////////////////////////////////////////////////////////////////
  113. ////////////////////////////////////////////////////////////////////////////////
  114. //
  115. //                      IMPLEMENTATION
  116. //
  117. ////////////////////////////////////////////////////////////////////////////////
  118. ////////////////////////////////////////////////////////////////////////////////
  119. implementation
  120.  
  121. uses
  122.   DragDropFile,
  123.   DragDropPIDL,
  124.   Registry,
  125.   ComObj,
  126.   SysUtils;
  127.  
  128. ////////////////////////////////////////////////////////////////////////////////
  129. //
  130. //              Component registration
  131. //
  132. ////////////////////////////////////////////////////////////////////////////////
  133.  
  134. procedure Register;
  135. begin
  136.   RegisterComponents(DragDropComponentPalettePage, [TDropContextMenu]);
  137. end;
  138.  
  139.  
  140. ////////////////////////////////////////////////////////////////////////////////
  141. //
  142. //              Utilities
  143. //
  144. ////////////////////////////////////////////////////////////////////////////////
  145.  
  146.  
  147. ////////////////////////////////////////////////////////////////////////////////
  148. //
  149. //              TDropContextMenu
  150. //
  151. ////////////////////////////////////////////////////////////////////////////////
  152. constructor TDropContextMenu.Create(AOwner: TComponent);
  153. begin
  154.   inherited Create(AOwner);
  155.   FFiles := TStringList.Create;
  156. end;
  157.  
  158. destructor TDropContextMenu.Destroy;
  159. begin
  160.   FFiles.Free;
  161.   inherited Destroy;
  162. end;
  163.  
  164. function TDropContextMenu.GetCommandString(idCmd, uType: UINT;
  165.   pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
  166. var
  167.   ItemIndex: integer;
  168. begin
  169.   ItemIndex := integer(idCmd);
  170.   // Make sure we aren't being passed an invalid argument number
  171.   if (ItemIndex >= 0) and (ItemIndex < FContextMenu.Items.Count) then
  172.   begin
  173.     if (uType = GCS_HELPTEXT) then
  174.       // return help string for menu item.
  175.       StrLCopy(pszName, PChar(FContextMenu.Items[ItemIndex].Hint), cchMax);
  176.     Result := NOERROR;
  177.   end else
  178.     Result := E_INVALIDARG;
  179. end;
  180.  
  181. function TDropContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
  182. var
  183.   ItemIndex: integer;
  184. begin
  185.   Result := E_FAIL;
  186.  
  187.   // Make sure we are not being called by an application
  188.   if (FContextMenu = nil) or (HiWord(Integer(lpici.lpVerb)) <> 0) then
  189.     Exit;
  190.  
  191.   ItemIndex := LoWord(lpici.lpVerb);
  192.   // Make sure we aren't being passed an invalid argument number
  193.   if (ItemIndex < 0) or (ItemIndex >= FContextMenu.Items.Count) then
  194.   begin
  195.     Result := E_INVALIDARG;
  196.     Exit;
  197.   end;
  198.  
  199.   // Execute the menu item specified by lpici.lpVerb.
  200.   try
  201.     try
  202.       FContextMenu.Items[ItemIndex].Click;
  203.       Result := NOERROR;
  204.     except
  205.       on E: Exception do
  206.       begin
  207.         Windows.MessageBox(0, PChar(E.Message), 'Error',
  208.           MB_OK or MB_ICONEXCLAMATION or MB_SYSTEMMODAL);
  209.         Result := E_UNEXPECTED;
  210.       end;
  211.     end;
  212.   finally
  213.     FDataObject := nil;
  214.     FFiles.Clear;
  215.   end;
  216. end;
  217.  
  218. function TDropContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
  219.   idCmdLast, uFlags: UINT): HResult;
  220. var
  221.   i: integer;
  222.   Last: integer;
  223.   Flags: UINT;
  224.  
  225.   function IsLine(Item: TMenuItem): boolean;
  226.   begin
  227.   {$ifdef VER13_PLUS}
  228.     Result := Item.IsLine;
  229.   {$else}
  230.     Result := Item.Caption = '-';
  231.   {$endif}
  232.   end;
  233.  
  234. begin
  235.   Last := 0;
  236.  
  237.   if (FContextMenu <> nil) and (((uFlags and $0000000F) = CMF_NORMAL) or
  238.      ((uFlags and CMF_EXPLORE) <> 0)) then
  239.   begin
  240.     FMenuOffset := idCmdFirst;
  241.     for i := 0 to FContextMenu.Items.Count-1 do
  242.       if (FContextMenu.Items[i].Visible) then
  243.       begin
  244.         Flags := MF_STRING or MF_BYPOSITION;
  245.         if (not FContextMenu.Items[i].Enabled) then
  246.           Flags := Flags or MF_GRAYED;
  247.         if (IsLine(FContextMenu.Items[i])) then
  248.           Flags := Flags or MF_SEPARATOR;
  249.         // Add one menu item to context menu
  250.         InsertMenu(Menu, indexMenu, Flags, FMenuOffset+i,
  251.           PChar(FContextMenu.Items[i].Caption));
  252.         inc(indexMenu);
  253.         Last := i+1;
  254.       end;
  255.   end else
  256.     FMenuOffset := 0;
  257.  
  258.   // Return number of menu items added
  259.   Result := MakeResult(SEVERITY_SUCCESS, FACILITY_NULL, Last)
  260. end;
  261.  
  262. function TDropContextMenu.Initialize(pidlFolder: PItemIDList;
  263.   lpdobj: IDataObject; hKeyProgID: HKEY): HResult;
  264. begin
  265.   FFiles.Clear;
  266.  
  267.   if (lpdobj = nil) then
  268.   begin
  269.     Result := E_INVALIDARG;
  270.     Exit;
  271.   end;
  272.  
  273.   // Save a reference to the source data object.
  274.   FDataObject := lpdobj;
  275.  
  276.   // Extract source file names and store them in a string list.
  277.   with TFileDataFormat.Create(nil) do
  278.     try
  279.       if GetData(DataObject) then
  280.         FFiles.Assign(Files);
  281.     finally
  282.       Free;
  283.     end;
  284.  
  285.   if (Assigned(FOnPopup)) then
  286.     FOnPopup(Self);
  287.  
  288.   Result := NOERROR;
  289. end;
  290.  
  291. procedure TDropContextMenu.SetContextMenu(const Value: TPopupMenu);
  292. begin
  293.   if (Value <> FContextMenu) then
  294.   begin
  295.     if (FContextMenu <> nil) then
  296.       FContextMenu.RemoveFreeNotification(Self);
  297.     FContextMenu := Value;
  298.     if (Value <> nil) then
  299.       Value.FreeNotification(Self);
  300.   end;
  301. end;
  302.  
  303. procedure TDropContextMenu.Notification(AComponent: TComponent;
  304.   Operation: TOperation);
  305. begin
  306.   if (Operation = opRemove) and (AComponent = FContextMenu) then
  307.     FContextMenu := nil;
  308.   inherited;
  309. end;
  310.  
  311. ////////////////////////////////////////////////////////////////////////////////
  312. //
  313. //              TDropContextMenuFactory
  314. //
  315. ////////////////////////////////////////////////////////////////////////////////
  316. function TDropContextMenuFactory.HandlerRegSubKey: string;
  317. begin
  318.   Result := 'ContextMenuHandlers';
  319. end;
  320.  
  321. procedure TDropContextMenuFactory.UpdateRegistry(Register: Boolean);
  322. var
  323.   ClassIDStr: string;
  324. begin
  325.   ClassIDStr := GUIDToString(ClassID);
  326.   if Register then
  327.   begin
  328.     inherited UpdateRegistry(Register);
  329.     CreateRegKey(FileClass+'\shellex\'+HandlerRegSubKey+'\'+ClassName, '', ClassIDStr);
  330.  
  331.     if (Win32Platform = VER_PLATFORM_WIN32_NT) then
  332.       with TRegistry.Create do
  333.         try
  334.           RootKey := HKEY_LOCAL_MACHINE;
  335.           OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions', True);
  336.           OpenKey('Approved', True);
  337.           WriteString(ClassIDStr, Description);
  338.         finally
  339.           Free;
  340.         end;
  341.   end else
  342.   begin
  343.     if (Win32Platform = VER_PLATFORM_WIN32_NT) then
  344.       with TRegistry.Create do
  345.         try
  346.           RootKey := HKEY_LOCAL_MACHINE;
  347.           OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions', True);
  348.           OpenKey('Approved', True);
  349.           DeleteKey(ClassIDStr);
  350.         finally
  351.           Free;
  352.         end;
  353.     DeleteRegKey(FileClass+'\shellex\'+HandlerRegSubKey+'\'+ClassName);
  354.     inherited UpdateRegistry(Register);
  355.   end;
  356. end;
  357.  
  358. end.
  359.