Menu

[r111]: / trunk / Src / UWBPopupMenus.pas  Maximize  Restore  History

Download this file

569 lines (509 with data), 19.0 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
{
* UWBPopupMenus.pas
*
* Classes that manage, configure and display popup menu items for web browser
* controls.
*
* v1.0 of 03 Nov 2007 - Original version.
* v1.1 of 14 Sep 2008 - Prevented invisible menu links from being added to
* menus.
* - Added images to menu items gleaned from menu links in
* HTML where there is a GIF image associated with link.
* - Now use for..in construct to iterate IDispatchList.
* v1.2 of 04 Oct 2008 - Changed to use UBaseObjects instead of UIntfObjects.
* v1.3 of 15 Dec 2008 - Flagged TAbstractWBPopupMenu as abstract class.
* - Now use enumerator to loop through menu items.
* - Made various private sections strict.
* v1.4 of 23 May 2009 - Images associated with menu-item options can now be
* parented by span tags in addition to div tags.
*
*
* ***** BEGIN LICENSE BLOCK *****
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is UWBPopupMenus.pas
*
* The Initial Developer of the Original Code is Peter Johnson
* (http://www.delphidabbler.com/).
*
* Portions created by the Initial Developer are Copyright (C) 2007-2009 Peter
* Johnson. All Rights Reserved.
*
* ***** END LICENSE BLOCK *****
}
unit UWBPopupMenus;
interface
uses
// Delphi
Windows, Menus, Controls, ActnList,
// Project
IntfWBPopupMenus, UBaseObjects;
type
{
TWBPopupMenus:
Class that manages, configures and displays popup menus for we browser
controls. Can act as an aggregated object that implements IWBPopupMenus and
IWBPopupMenuConfig.
}
TWBPopupMenus = class(TAggregatedOrLoneObject,
IWBPopupMenus, IWBPopupMenuConfig
)
strict private
fMenus: array[TWBPopupMenuKind] of TPopupMenu;
{Array of menus displayed for each kind of popup menu supported by browser
control}
function HasVisibleItems(const Kind: TWBPopupMenuKind): Boolean;
{Checks if a menu has any visible menu items.
@param Kind [in] Specifies menu to be checked.
@return True if menu has visible items, False otherwise.
}
protected // must not be strict
{ IWBPopupMenus }
function Popup(const Pt: TPoint; const Kind: TWBPopupMenuKind;
const Elem: IDispatch): Boolean;
{Pops up menu of required kind for a specified HTML element.
@param Pt [in] Point on screen where menu to be displayed.
@param Kind [in] Kind of menu to be displayed.
@param Elem [in] Element under mouse cursor when menu display requested.
@return True if menu has visible items and was displayed, False if no
visible items and was not displayed.
}
{ IWBPopupMenuConfig }
procedure AddAction(const Action: TCustomAction;
const Kind: TWBPopupMenuKind);
{Adds a menu item with an associated action to a popup menu.
@param Action [in] Action to be associated with menu item.
@param Kind [in] Specifies menu to add menu item to.
}
procedure AddSpacer(const Kind: TWBPopupMenuKind);
{Adds a spacer to a pop-up menu.
@param Kind [in] Specifies menu to add spacer to.
}
procedure SetImages(const Images: TImageList);
{Specifies image list to be used by menus.
@param Images [in] Image list to be used.
}
public
constructor Create(const Controller: IInterface); override;
{Class constructor. Creates either an aggregated or stand-alone object and
sets up object.
@param Controller [in] IInterface reference to containing object if
aggregated or nil if not aggregated.
}
destructor Destroy; override;
{Class destructor. Tears down object.
}
end;
implementation
uses
// Delphi
SysUtils, StrUtils,
// Project
UAnchors, UDispatchList, UGIFImageList, UHTMLDocHelper, UImageTags,
ULinkAction;
var
// Global image list used by menus
pvtImages: TGIFImageList = nil;
type
{
TWBPopupMenuClass:
Class reference for custom pop-up menu classes.
}
TWBPopupMenuClass = class of TAbstractWBPopupMenu;
{
TAbstractWBPopupMenu:
Abstract base class for browser popup menus.
}
TAbstractWBPopupMenu = class abstract(TPopupMenu)
public
procedure Initialise(const Elem: IDispatch); virtual; abstract;
{Initialises menu. To be called just before menu displayed. NOTE: Can't
override DoPopup since a reference to the selected HTML element is
required.
@param Elem [in] IDispatch interface of HTML element under mouse cursor.
}
end;
{
TWBNulPopupMenu:
Popup menu class that makes no changed to menu when initialised.
}
TWBNulPopupMenu = class(TAbstractWBPopupMenu)
public
procedure Initialise(const Elem: IDispatch); override;
{Initialises menu. Does nothing.
@param Elem [in] IDispatch interface of HTML element under mouse cursor.
Not used.
}
end;
{
TWBPopupMenu:
Class of popup menu that can be initialised before being displayed. Ensures
that any menu items that trigger links are updated for selected link.
}
TWBPopupMenu = class(TAbstractWBPopupMenu)
public
procedure Initialise(const Elem: IDispatch); override;
{Initialises menu. Stores selected HTML element in any link actions
associated with menu items.
@param Elem [in] IDispatch interface of HTML element under mouse cursor.
}
end;
{
TWBDefaultPopupMenu:
Class of popup menu used for default popup menu kinds. Initialises menu to
include item for all links in document that are designated menu items.
}
TWBDefaultPopupMenu = class(TWBPopupMenu)
strict private
procedure ClearTempMenuItems;
{Clears temporary menu items from menu.
}
procedure GetLinkMenuItems(const Doc: IDispatch; out CommandItems,
HelpItems: IDispatchList);
{Gets all command and help links from document that are designated as menu
items.
@param Doc [in] IDispatch interface of document containing links.
@param CommandItems [out] List of command links.
@param HelpItems [out] List of help links.
}
procedure AddLinksToMenu(const Links: IDispatchList);
{Adds menu items to menu that can trigger links from a link list.
@param Links [in] List of links to be added to menu.
}
function GetImageIndex(const Link: IDispatch): Integer;
{Gets index of any image associated with a link in image list used by
menu. If image doesn't exist in list it is added to it.
@param Link [in] Link for which image needed.
@return Index of image in image or -1 if there is no associated image.
}
public
procedure Initialise(const Elem: IDispatch); override;
{Initialises menu. Adds items to menu for any command and help links in
current HTML document.
@param Elem [in] IDispatch interface of HTML element under mouse cursor.
}
end;
{
TWBMenuItemClass:
Class reference to menu item classes used in popup menus.
}
TWBMenuItemClass = class of TMenuItem;
{
TWBTempMenuItem:
Custom menu item type used for temporary menu items added to menus by
TWBDefaultPopupMenu. Class type is used to identify this type of menu item
for special processing. Associated action is freed when menu item is freed.
}
TWBTempMenuItem = class(TMenuItem)
public
destructor Destroy; override;
{Class destructor. Tears down object.
}
end;
const
// Map of menu kinds to classes implementing menu for each kind
cPopupMenuClassMap: array[TWBPopupMenuKind] of TWBPopupMenuClass = (
TWBDefaultPopupMenu, // pmkDefault
TWBPopupMenu, // pmkImage
TWBNulPopupMenu, // pmkControl
TWBNulPopupMenu, // pmkTable
TWBPopupMenu, // pmkTextSelect
TWBPopupMenu, // pmkAnchor
TWBNulPopupMenu // pmkUnknown
);
function CreateActionItem(const MIClass: TWBMenuItemClass;
const Action: TCustomAction): TMenuItem;
{Creates a menu item that triggers an action.
@param MIClass [in] Class of required menu item.
@param Action [in] Action to be associated with menu item.
@return New menu item.
}
begin
Result := MIClass.Create(nil);
Result.Action := Action;
end;
function CreateSpacer(const MIClass: TWBMenuItemClass): TMenuItem;
{Creates a spacer menu item.
@param MIClass [in] Class of required menu item.
@return New spacer menu item.
}
begin
Result := MIClass.Create(nil);
Result.Caption := '-';
end;
{ TWBPopupMenus }
procedure TWBPopupMenus.AddAction(const Action: TCustomAction;
const Kind: TWBPopupMenuKind);
{Adds a menu item with an associated action to a popup menu.
@param Action [in] Action to be associated with menu item.
@param Kind [in] Specifies menu to add menu item to.
}
begin
fMenus[Kind].Items.Add(CreateActionItem(TMenuItem, Action));
end;
procedure TWBPopupMenus.AddSpacer(const Kind: TWBPopupMenuKind);
{Adds a spacer to a pop-up menu.
@param Kind [in] Specifies menu to add spacer to.
}
begin
fMenus[Kind].Items.Add(CreateSpacer(TMenuItem));
end;
constructor TWBPopupMenus.Create(const Controller: IInterface);
{Class constructor. Creates either an aggregated or stand-alone object and
sets up object.
@param Controller [in] IInterface reference to containing object if
aggregated or nil if not aggregated.
}
var
MenuId: TWBPopupMenuKind; // loops through popup menu array
begin
inherited;
// Create all popup menus
for MenuId := Low(TWBPopupMenuKind) to High(TWBPopupMenuKind) do
fMenus[MenuId] := cPopupMenuClassMap[MenuId].Create(nil);
end;
destructor TWBPopupMenus.Destroy;
{Class destructor. Tears down object.
}
var
MenuId: TWBPopupMenuKind; // loops through popup menu array
begin
// Free all menus
for MenuId := Low(TWBPopupMenuKind) to High(TWBPopupMenuKind) do
FreeAndNil(fMenus[MenuId]);
inherited;
end;
function TWBPopupMenus.HasVisibleItems(const Kind: TWBPopupMenuKind): Boolean;
{Checks if a menu has any visible menu items.
@param Kind [in] Specifies menu to be checked.
@return True if menu has visible items, False otherwise.
}
var
VisibleCount: Integer; // counts visible menu items
MI: TMenuItem; // references a menu item
begin
VisibleCount := 0;
for MI in fMenus[Kind].Items do
begin
// update menu item's action before checking for visibility
if (MI.Action is TCustomAction) then
(MI.Action as TCustomAction).Update;
if MI.Visible then
Inc(VisibleCount);
end;
Result := VisibleCount > 0;
end;
function TWBPopupMenus.Popup(const Pt: TPoint; const Kind: TWBPopupMenuKind;
const Elem: IDispatch): Boolean;
{Pops up menu of required kind for a specified HTML element.
@param Pt [in] Point on screen where menu to be displayed.
@param Kind [in] Kind of menu to be displayed.
@param Elem [in] Element under mouse cursor when menu display requested.
@return True if menu has visible items and was displayed, False if no
visible items and was not displayed.
}
begin
(fMenus[Kind] as TWBPopupMenu).Initialise(Elem);
Result := HasVisibleItems(Kind);
if Result then
fMenus[Kind].Popup(Pt.X, Pt.Y);
end;
procedure TWBPopupMenus.SetImages(const Images: TImageList);
{Specifies image list to be used by menus.
@param Images [in] Image list to be used.
}
var
MenuId: TWBPopupMenuKind; // loops through different menu kinds
begin
// Reset internal image list to store only items from Images
pvtImages.Clear;
pvtImages.AddImages(Images);
// Make each menu use the internal image list
for MenuId := Low(TWBPopupMenuKind) to High(TWBPopupMenuKind) do
fMenus[MenuId].Images := pvtImages;
end;
{ TWBNulPopupMenu }
procedure TWBNulPopupMenu.Initialise(const Elem: IDispatch);
{Initialises menu. Does nothing.
@param Elem [in] IDispatch interface of HTML element under mouse cursor. Not
used.
}
begin
// Do nothing
end;
{ TWBPopupMenu }
procedure TWBPopupMenu.Initialise(const Elem: IDispatch);
{Initialises menu. Stores selected HTML element in any link actions associated
with menu items.
@param Elem [in] IDispatch interface of HTML element under mouse cursor.
}
var
MI: TMenuItem; // references a menu item
begin
for MI in Items do
if MI.Action is TLinkAction then
// menu item associated with a link action: store reference to Elem in it
(MI.Action as TLinkAction).Link := Elem;
end;
{ TWBDefaultPopupMenu }
procedure TWBDefaultPopupMenu.AddLinksToMenu(const Links: IDispatchList);
{Adds menu items to menu that can trigger links from a link list.
@param Links [in] List of links to be added to menu.
}
var
Action: TLinkAction; // action to trigger a link
Link: IDispatch; // references all links in Links
begin
// Do nothing if list is empty
if Links.Count = 0 then
Exit;
// Start list with a spacer
Items.Add(CreateSpacer(TWBTempMenuItem));
// Add menu items
for Link in Links do
begin
// Create action for menu item: store link reference and get caption from
// link text. If link is not visible it is not added to menu.
if THTMLDocHelper.ElemIsVisible(Link) then
begin
Action := TLinkAction.Create(nil);
Action.Link := Link;
Action.ImageIndex := GetImageIndex(Link);
Action.Caption := TAnchors.GetInnerText(Link);
Items.Add(CreateActionItem(TWBTempMenuItem, Action));
end;
end;
end;
procedure TWBDefaultPopupMenu.ClearTempMenuItems;
{Clears temporary menu items from menu.
}
var
MI: TMenuItem; // references each menu item in menu
begin
for MI in Items do
if MI is TWBTempMenuItem then
MI.Free; // only free temp menu items
end;
function TWBDefaultPopupMenu.GetImageIndex(const Link: IDispatch): Integer;
{Gets index of any image associated with a link in image list used by menu.
If image doesn't exist in list it is added to it.
@param Link [in] Link for which image needed.
@return Index of image in image or -1 if there is no associated image.
}
// ---------------------------------------------------------------------------
function URLBaseName(const URL: string): string;
{Extracts a base resource name from a URL.
@param URL [in] URL containing resource name.
@return Required base name.
}
var
Pos: Integer; // position of last path delimiter in URL
begin
Pos := LastDelimiter('/', URL);
if Pos > 0 then
Result := AnsiRightStr(URL, Length(URL) - Pos)
else
Result := URL;
end;
// ---------------------------------------------------------------------------
var
ParentDiv: IDispatch; // parent <div> or <span> tag that contains Link
ImgTags: IDispatchList; // all <img> children of parent
ImgTag: IDispatch; // <img> child of parent that contains required GIF
Src: string; // resource URL of GIF file
begin
Result := -1;
// Check if parent elem is a <div> or <span> with class "option"
ParentDiv := THTMLDocHelper.ParentElem(Link, 'div', 'option');
if not Assigned(ParentDiv) then
ParentDiv := THTMLDocHelper.ParentElem(Link, 'span', 'option');
if not Assigned(ParentDiv) then
Exit;
// So see if there's an child <img> of parent with class "option-img"
ImgTags := TImageTags.GetAllImageTags(ParentDiv);
ImgTag := nil;
for ImgTag in ImgTags do
if THTMLDocHelper.ElemHasClass(ImgTag, 'option-img') then
Break;
if not Assigned(ImgTag) then
Exit;
// Get resource name of image from <img> tag's "src" attribute
Src := URLBaseName(TImageTags.GetSrc(ImgTag));
// Get matching bitmap from image list: add one from GIF file if not found
Result := pvtImages.ImageIndex(Src);
if Result = -1 then
Result := pvtImages.AddGIFImage(Src);
end;
procedure TWBDefaultPopupMenu.GetLinkMenuItems(const Doc: IDispatch;
out CommandItems, HelpItems: IDispatchList);
{Gets all command and help links from document that are designated as menu
items.
@param Doc [in] IDispatch interface of document containing links.
@param CommandItems [out] List of command links.
@param HelpItems [out] List of help links.
}
var
AllLinks: IDispatchList; // list of all links in document
Link: IDispatch; // referenced each link in AllLinks
begin
CommandItems := TDispatchList.Create;
HelpItems := TDispatchList.Create;
// Get all links from document
AllLinks := TAnchors.GetAllAnchors(Doc);
// Scan all links
for Link in AllLinks do
begin
// To have a link on menu it must have 'menu-item' class
if THTMLDocHelper.ElemHasClass(Link, 'menu-item') then
begin
case TAnchors.AnchorKind(Link) of
akCommand: CommandItems.Add(Link);
akHelp: HelpItems.Add(Link);
end;
end;
end;
end;
procedure TWBDefaultPopupMenu.Initialise(const Elem: IDispatch);
{Initialises menu. Adds items to menu for any command and help links in
current HTML document.
@param Elem [in] IDispatch interface of HTML element under mouse cursor.
}
var
CommandLinks: IDispatchList; // list of command links in document
HelpLinks: IDispatchList; // list of help links in document
begin
// Removes any pre-existing menu items from menu
ClearTempMenuItems;
inherited;
// Get list of command and help links from current document
GetLinkMenuItems(
THTMLDocHelper.DocumentFromElem(Elem), CommandLinks, HelpLinks
);
// Add the required menu items to the menu
AddLinksToMenu(CommandLinks);
AddLinksToMenu(HelpLinks);
end;
{ TWBTempMenuItem }
destructor TWBTempMenuItem.Destroy;
{Class destructor. Tears down object.
}
begin
Action.Free; // free the associated action
inherited;
end;
initialization
// Create private image list
pvtImages := TGIFImageList.Create(nil);
finalization
// Dispose of private image list
FreeAndNil(pvtImages);
end.
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.