Subversion Repositories decoder

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 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.