CnActionListHook.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2018 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnActionListHook;
  21. { |<PRE>
  22. ================================================================================
  23. * 软件名称:CnWizards IDE 专家工具包
  24. * 单元名称:ActionList 挂接服务单元
  25. * 单元作者:刘啸(Passion) liuxiao@cnpack.org;
  26. * 备 注:该单元用来实现对 IDE 内部 ActionList 的挂接操作,用户必须先挂接一个
  27. ActionList,才能对其内部的 Action 进行挂接。当挂接管理器的 Active 为
  28. False 的时候,所有挂接的 Action 的事件都会暂时恢复。当 ActionList 或
  29. Action 被释放的时候会自动取消挂接。
  30. * 开发平台:PWin2000Pro + Delphi 5.01
  31. * 兼容测试:PWin9X/2000/XP + Delphi 5/6/7 + C++Builder 5/6
  32. * 本 地 化:该单元中的字符串支持本地化处理方式
  33. * 单元标识:$Id$
  34. * 修改记录:2003.07.15 V1.0
  35. * 创建单元
  36. ================================================================================
  37. |</PRE>}
  38. interface
  39. {$I CnPack.inc}
  40. uses
  41. Windows, Messages, SysUtils, Classes, Forms, ActnList, Contnrs,
  42. CnConsts, CnClasses, CnCompConsts;
  43. type
  44. TCnActionHookObj = class(TObject)
  45. {* 用来描述一被挂接的 Action}
  46. private
  47. FAction: TAction;
  48. FNewOnExecute: TNotifyEvent;
  49. FNewOnUpdate: TNotifyEvent;
  50. FOldOnExecute: TNotifyEvent;
  51. FOldOnUpdate: TNotifyEvent;
  52. procedure SetAction(const Value: TAction);
  53. procedure SetNewOnExecute(const Value: TNotifyEvent);
  54. procedure SetNewOnUpdate(const Value: TNotifyEvent);
  55. procedure SetOldOnExecute(const Value: TNotifyEvent);
  56. procedure SetOldOnUpdate(const Value: TNotifyEvent);
  57. protected
  58. procedure HookAction;
  59. {* 进行具体的 Action 事件替换操作}
  60. procedure RestoreAction;
  61. {* 恢复 Action 的原有事件}
  62. public
  63. constructor Create(AAction: TAction; NewOnExecute, NewOnUpdate: TNotifyEvent);
  64. destructor Destroy; override;
  65. property Action: TAction read FAction write SetAction;
  66. property OldOnUpdate: TNotifyEvent read FOldOnUpdate write SetOldOnUpdate;
  67. property OldOnExecute: TNotifyEvent read FOldOnExecute write SetOldOnExecute;
  68. property NewOnUpdate: TNotifyEvent read FNewOnUpdate write SetNewOnUpdate;
  69. property NewOnExecute: TNotifyEvent read FNewOnExecute write SetNewOnExecute;
  70. end;
  71. //==============================================================================
  72. // ActionList 挂接管理器
  73. //==============================================================================
  74. { TCnActionListHook }
  75. THookActionListEvent = procedure(Sender: TObject; ActionList: TActionList) of object;
  76. TCnActionListHook = class(TCnComponent)
  77. private
  78. FActionListList: TList;
  79. FHookItemList: TObjectList;
  80. FActive: Boolean;
  81. FOnAddActionList: THookActionListEvent;
  82. FOnRemoveActionList: THookActionListEvent;
  83. procedure SetActive(const Value: Boolean);
  84. function GetHookedActionList(Index: Integer): TActionList;
  85. function GetHookedActionListCount: Integer;
  86. function GetHookedAction(Index: Integer): TAction;
  87. function GetHookedActionCount: Integer;
  88. protected
  89. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  90. function GetActionHookObj(AAction: TAction): TCnActionHookObj;
  91. procedure DoRemoveActionList(AActionList: TActionList);
  92. procedure DoAddActionList(AActionList: TActionList);
  93. procedure UpdateHookedActions;
  94. procedure GetComponentInfo(var AName, Author, Email, Comment: string); override;
  95. public
  96. constructor Create(AOwner: TComponent); override;
  97. destructor Destroy; override;
  98. function IsHooked(AActionList: TActionList): Boolean;
  99. {* 判断一 ActionList 是否被 Hook}
  100. function IsActionHooked(AAction: TAction): Boolean;
  101. {* 判断一 Action 是否被 Hook}
  102. procedure UnHookActionItems(ActionList: TActionList);
  103. {* 取消一 ActionList 中的所有 Action 的 Hook}
  104. procedure HookActionList(AActionList: TActionList);
  105. {* 供用户调用:挂接一个 ActionList}
  106. procedure UnHookActionList(AActionList: TActionList);
  107. {* 供用户调用:取消对一个 ActionList 的挂接}
  108. function AddActionNotifier(Action: TAction; NewOnExecute, NewOnUpdate:
  109. TNotifyEvent): Boolean;
  110. {* 供用户调用:挂接一 Action 的 OnExecute 和 OnUpdate 事件}
  111. procedure RemoveNotifiler(Action: TAction);
  112. {* 供用户调用:取消挂接一 Action,恢复其原有的 OnExecute 和 OnUpdate 事件}
  113. { function AddActionNotifier(const ActionName: String; NewOnExecute, NewOnUpdate:
  114. TNotifyEvent): Boolean; overload;
  115. procedure RemoveNotifiler(const ActionName: String); overload; }
  116. property Active: Boolean read FActive write SetActive;
  117. {* 控制本挂接管理器是否有效 }
  118. property HookedActionListCount: Integer read GetHookedActionListCount;
  119. {* 返回被挂接的 ActionList 数目 }
  120. property HookedActionLists[Index: Integer]: TActionList
  121. read GetHookedActionList;
  122. {* 返回被挂接的 ActionList }
  123. property HookedActionCount: Integer read GetHookedActionCount;
  124. {* 返回被挂接的 Action 数目 }
  125. property HookedActions[Index: Integer]: TAction read GetHookedAction;
  126. {* 返回被挂接的 Action }
  127. property OnRemoveActionList: THookActionListEvent
  128. read FOnRemoveActionList write FOnRemoveActionList;
  129. property OnAddActionList: THookActionListEvent
  130. read FOnAddActionList write FOnAddActionList;
  131. end;
  132. implementation
  133. //==============================================================================
  134. // ActionList 挂接管理器
  135. //==============================================================================
  136. { TCnActionListHook }
  137. function TCnActionListHook.AddActionNotifier(Action: TAction; NewOnExecute,
  138. NewOnUpdate: TNotifyEvent): Boolean;
  139. var
  140. HookObj: TCnActionHookObj;
  141. begin
  142. Result := False;
  143. if (Action <> nil) and (FHookItemList.IndexOf(Action) < 0) then
  144. begin
  145. if IsHooked(TActionList(Action.ActionList)) and not IsActionHooked(Action) then
  146. begin
  147. HookObj := TCnActionHookObj.Create(Action, NewOnExecute, NewOnUpdate);
  148. FHookItemList.Add(HookObj);
  149. if Active then
  150. HookObj.HookAction;
  151. Action.FreeNotification(Self);
  152. Result := True;
  153. end;
  154. end;
  155. end;
  156. constructor TCnActionListHook.Create(AOwner: TComponent);
  157. begin
  158. inherited;
  159. FActionListList := TList.Create;
  160. // FActionListList.OwnsObjects := False;
  161. // 不需要控制对 ActionList 的释放。
  162. FHookItemList := TObjectList.Create;
  163. FActive := True;
  164. FOnAddActionList := nil;
  165. FOnRemoveActionList := nil;
  166. end;
  167. destructor TCnActionListHook.Destroy;
  168. begin
  169. FHookItemList.Free;
  170. FActionListList.Free;
  171. inherited;
  172. end;
  173. procedure TCnActionListHook.DoAddActionList(AActionList: TActionList);
  174. begin
  175. if Assigned(FOnAddActionList) then
  176. FOnAddActionList(Self, AActionList);
  177. end;
  178. procedure TCnActionListHook.DoRemoveActionList(AActionList: TActionList);
  179. begin
  180. if Assigned(FOnRemoveActionList) then
  181. FOnRemoveActionList(Self, AActionList);
  182. end;
  183. function TCnActionListHook.GetActionHookObj(
  184. AAction: TAction): TCnActionHookObj;
  185. var
  186. i: Integer;
  187. begin
  188. for i := 0 to FHookItemList.Count - 1 do
  189. if TCnActionHookObj(FHookItemList[i]).Action = AAction then
  190. begin
  191. Result := TCnActionHookObj(FHookItemList[i]);
  192. Exit;
  193. end;
  194. Result := nil;
  195. end;
  196. function TCnActionListHook.GetHookedActionList(Index: Integer): TActionList;
  197. begin
  198. if (Index >= 0) and (Index < FActionListList.Count) then
  199. Result := TActionList(FActionListList[Index])
  200. else
  201. Result := nil;
  202. end;
  203. function TCnActionListHook.GetHookedActionListCount: Integer;
  204. begin
  205. Result := FActionListList.Count;
  206. end;
  207. procedure TCnActionListHook.HookActionList(AActionList: TActionList);
  208. begin
  209. if (AActionList <> nil) and not IsHooked(AActionList) then
  210. begin
  211. DoAddActionList(AActionList);
  212. FActionListList.Add(AActionList);
  213. AActionList.FreeNotification(Self);
  214. end
  215. end;
  216. function TCnActionListHook.IsHooked(AActionList: TActionList): Boolean;
  217. begin
  218. Result := (FActionListList.IndexOf(AActionList) >= 0);
  219. end;
  220. function TCnActionListHook.IsActionHooked(AAction: TAction): Boolean;
  221. begin
  222. Result := GetActionHookObj(AAction) <> nil;
  223. end;
  224. procedure TCnActionListHook.Notification(AComponent: TComponent;
  225. Operation: TOperation);
  226. begin
  227. inherited;
  228. if (Operation = opRemove) and (AComponent is TActionList) then
  229. begin
  230. UnHookActionItems(AComponent as TActionList);
  231. UnHookActionList(AComponent as TActionList);
  232. end
  233. else if (Operation = opRemove) and (AComponent is TAction) then
  234. begin
  235. RemoveNotifiler(AComponent as TAction);
  236. end;
  237. end;
  238. procedure TCnActionListHook.RemoveNotifiler(Action: TAction);
  239. var
  240. HookObj: TCnActionHookObj;
  241. begin
  242. if IsHooked(TActionList(Action.ActionList)) then
  243. if IsActionHooked(Action) then
  244. begin
  245. Action.RemoveFreeNotification(Self);
  246. HookObj := GetActionHookObj(Action);
  247. HookObj.RestoreAction;
  248. FHookItemList.Delete(FHookItemList.IndexOf(HookObj));
  249. HookObj.Free;
  250. end;
  251. end;
  252. procedure TCnActionListHook.SetActive(const Value: Boolean);
  253. begin
  254. FActive := Value;
  255. UpdateHookedActions;
  256. end;
  257. procedure TCnActionListHook.UnHookActionItems(ActionList: TActionList);
  258. var
  259. i: Integer;
  260. begin
  261. for i := 0 to ActionList.ActionCount - 1 do
  262. if GetActionHookObj(ActionList.Actions[i] as TAction) <> nil then
  263. RemoveNotifiler(ActionList.Actions[i] as TAction);
  264. end;
  265. procedure TCnActionListHook.UnHookActionList(AActionList: TActionList);
  266. begin
  267. if IsHooked(AActionList) then
  268. begin
  269. DoRemoveActionList(AActionList);
  270. AActionList.RemoveFreeNotification(Self);
  271. UnHookActionItems(AActionList);
  272. FActionListList.Remove(AActionList);
  273. end;
  274. end;
  275. procedure TCnActionListHook.UpdateHookedActions;
  276. var
  277. i: Integer;
  278. begin
  279. if Active then
  280. for i := 0 to FHookItemList.Count - 1 do
  281. TCnActionHookObj(FHookItemList[i]).HookAction
  282. else
  283. for i := 0 to FHookItemList.Count - 1 do
  284. TCnActionHookObj(FHookItemList[i]).RestoreAction;
  285. end;
  286. {function TCnActionListHook.AddActionNotifier(const ActionName: String;
  287. NewOnExecute, NewOnUpdate: TNotifyEvent): Boolean;
  288. begin
  289. if (FindComponent(ActionName) <> nil) and
  290. (FindComponent(ActionName) is TAction) then
  291. Self.AddActionNotifier((FindComponent(ActionName) as TAction),
  292. NewOnUpdate, NewOnExecute);
  293. end;
  294. procedure TCnActionListHook.RemoveNotifiler(const ActionName: String);
  295. begin
  296. if (FindComponent(ActionName) <> nil) and
  297. (FindComponent(ActionName) is TAction) then
  298. Self.RemoveNotifiler(FindComponent(ActionName) as TAction);
  299. end; }
  300. function TCnActionListHook.GetHookedAction(Index: Integer): TAction;
  301. begin
  302. if (Index >= 0) and (Index < FHookItemList.Count) then
  303. Result := TCnActionHookObj(FHookItemList[Index]).Action
  304. else
  305. Result := nil;
  306. end;
  307. function TCnActionListHook.GetHookedActionCount: Integer;
  308. begin
  309. Result := FHookItemList.Count;
  310. end;
  311. procedure TCnActionListHook.GetComponentInfo(var AName, Author, Email,
  312. Comment: string);
  313. begin
  314. AName := SCnActionListHookName;
  315. Author := SCnPack_Zjy;
  316. Email := SCnPack_ZjyEmail;
  317. Comment := SCnActionListHookComment;
  318. end;
  319. { TCnActionHookObj }
  320. constructor TCnActionHookObj.Create(AAction: TAction; NewOnExecute,
  321. NewOnUpdate: TNotifyEvent);
  322. begin
  323. FAction := AAction;
  324. FOldOnExecute := AAction.OnExecute;
  325. FOldOnUpdate := AAction.OnUpdate;
  326. FNewOnExecute := NewOnExecute;
  327. FNewOnUpdate := NewOnUpdate;
  328. end;
  329. destructor TCnActionHookObj.Destroy;
  330. begin
  331. if Self.FAction <> nil then
  332. Self.RestoreAction;
  333. inherited;
  334. end;
  335. procedure TCnActionHookObj.HookAction;
  336. begin
  337. if FAction <> nil then
  338. begin
  339. if Assigned(FNewOnExecute) then
  340. FAction.OnExecute := NewOnExecute;
  341. if Assigned(FNewOnUpdate) then
  342. FAction.OnUpdate := NewOnUpdate;
  343. end;
  344. end;
  345. procedure TCnActionHookObj.RestoreAction;
  346. begin
  347. if FAction <> nil then
  348. begin
  349. FAction.OnExecute := OldOnExecute;
  350. FAction.OnUpdate := OldOnUpdate;
  351. end;
  352. end;
  353. procedure TCnActionHookObj.SetAction(const Value: TAction);
  354. begin
  355. FAction := Value;
  356. end;
  357. procedure TCnActionHookObj.SetNewOnExecute(const Value: TNotifyEvent);
  358. begin
  359. FNewOnExecute := Value;
  360. end;
  361. procedure TCnActionHookObj.SetNewOnUpdate(const Value: TNotifyEvent);
  362. begin
  363. FNewOnUpdate := Value;
  364. end;
  365. procedure TCnActionHookObj.SetOldOnExecute(const Value: TNotifyEvent);
  366. begin
  367. FOldOnExecute := Value;
  368. end;
  369. procedure TCnActionHookObj.SetOldOnUpdate(const Value: TNotifyEvent);
  370. begin
  371. FOldOnUpdate := Value;
  372. end;
  373. end.