PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/Source/Core/DSharp.Core.Events.pas

http://delphisorcery.googlecode.com/
Pascal | 832 lines | 682 code | 73 blank | 77 comment | 43 complexity | 9966bf97e258fd06dc1df96cf02da2a2 MD5 | raw file
  1. (*
  2. Copyright (c) 2011, Stefan Glienke
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. - Redistributions of source code must retain the above copyright notice,
  7. this list of conditions and the following disclaimer.
  8. - Redistributions in binary form must reproduce the above copyright notice,
  9. this list of conditions and the following disclaimer in the documentation
  10. and/or other materials provided with the distribution.
  11. - Neither the name of this library nor the names of its contributors may be
  12. used to endorse or promote products derived from this software without
  13. specific prior written permission.
  14. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  18. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  19. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  20. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  21. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  22. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  23. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  24. POSSIBILITY OF SUCH DAMAGE.
  25. *)
  26. unit DSharp.Core.Events;
  27. interface
  28. uses
  29. Classes,
  30. DSharp.Core.NotificationHandler,
  31. Generics.Collections,
  32. ObjAuto,
  33. {$IF CompilerVersion < 23}
  34. ObjAutoPatch,
  35. {$IFEND}
  36. Rtti,
  37. TypInfo;
  38. type
  39. PDelegate = ^IDelegate;
  40. IDelegate = interface
  41. procedure Invoke;
  42. end;
  43. IEvent = interface
  44. function GetCount: Integer;
  45. function GetEnabled: Boolean;
  46. function GetInvoke: TMethod;
  47. procedure Add(const AEvent: TMethod);
  48. procedure Remove(const AEvent: TMethod);
  49. procedure SetEnabled(const AValue: Boolean);
  50. property Count: Integer read GetCount;
  51. property Enabled: Boolean read GetEnabled write SetEnabled;
  52. property Invoke: TMethod read GetInvoke;
  53. end;
  54. TEvent = class abstract(TInterfacedObject, IEvent, IDelegate)
  55. strict private
  56. FEnabled: Boolean;
  57. FInternalDispatcher: TMethod;
  58. FMethods: TList<TMethod>;
  59. function IEvent.GetInvoke = GetInvokeBase;
  60. procedure InternalInvoke(Params: PParameters; StackSize: Integer);
  61. procedure InternalNotify(Sender: TObject; const Item: TMethod;
  62. Action: TCollectionNotification);
  63. procedure Invoke;
  64. strict protected
  65. {$IF CompilerVersion > 22}
  66. FCallingConvention: TCallConv;
  67. FParameters: TArray<TRttiParameter>;
  68. {$IFEND}
  69. function GetInvokeBase: TMethod; virtual; abstract;
  70. procedure MethodAdded(const AMethod: TMethod); virtual; abstract;
  71. procedure MethodRemoved(const AMethod: TMethod); virtual; abstract;
  72. procedure Add(const AEvent: TMethod);
  73. function GetCount: Integer;
  74. function GetEnabled: Boolean;
  75. function IndexOf(const AEvent: TMethod): Integer;
  76. function IndexOfInstance(const AInstance: TObject): Integer;
  77. procedure Remove(const AEvent: TMethod);
  78. procedure RemoveInstanceReferences(const AInstance: TObject);
  79. procedure SetDispatcher(out AMethod: TMethod; ATypeData: PTypeData);
  80. procedure SetEnabled(const AValue: Boolean);
  81. public
  82. constructor Create;
  83. destructor Destroy; override;
  84. end;
  85. IEvent<T> = interface(IEvent)
  86. function GetCount: Integer;
  87. function GetInvoke: T;
  88. function GetOnChanged: TNotifyEvent;
  89. procedure Add(AEvent: T);
  90. procedure Remove(AEvent: T);
  91. procedure SetOnChanged(const Value: TNotifyEvent);
  92. property Count: Integer read GetCount;
  93. property Invoke: T read GetInvoke;
  94. property OnChanged: TNotifyEvent read GetOnChanged write SetOnChanged;
  95. end;
  96. TEvent<T> = class(TEvent, IEvent<T>)
  97. strict private
  98. FInvoke: T;
  99. FNotificationHandler: TNotificationHandler<TEvent<T>>;
  100. FOwner: TComponent;
  101. FOnChanged: TNotifyEvent;
  102. function Cast(const Value: T): TMethod;
  103. function GetInvoke: T;
  104. function GetOnChanged: TNotifyEvent;
  105. procedure Notification(AComponent: TComponent; Operation: TOperation);
  106. procedure SetEventDispatcher(out ADispatcher: T; ATypeData: PTypeData);
  107. procedure SetOnChanged(const Value: TNotifyEvent);
  108. strict protected
  109. function GetInvokeBase: TMethod; override;
  110. procedure MethodAdded(const AMethod: TMethod); override;
  111. procedure MethodRemoved(const AMethod: TMethod); override;
  112. public
  113. constructor Create(AOwner: TComponent); overload;
  114. constructor Create(AOwner: TComponent; AEvents: array of T); overload;
  115. {$IF CompilerVersion > 21}
  116. class function Create<TDelegate>(AOwner: TComponent;
  117. ADelegates: array of TDelegate): TEvent<T>; overload;
  118. {$IFEND}
  119. class function Create<TDelegate>(AOwner: TComponent;
  120. ADelegates: TArray<TDelegate>): TEvent<T>; overload;
  121. destructor Destroy; override;
  122. procedure Add(AEvent: T); overload;
  123. procedure Add<TDelegate>(ADelegate: TDelegate); overload;
  124. procedure Remove(AEvent: T); overload;
  125. procedure Remove<TDelegate>(ADelegate: TDelegate); overload;
  126. function IndexOf(AEvent: T): Integer;
  127. property Count: Integer read GetCount;
  128. property Enabled: Boolean read GetEnabled write SetEnabled;
  129. property Invoke: T read GetInvoke;
  130. property Owner: TComponent read FOwner;
  131. end;
  132. Event<T> = record
  133. strict private
  134. FEventHandler: IEvent<T>;
  135. FInitialized: Boolean;
  136. function GetCount: Integer;
  137. function GetEnabled: Boolean;
  138. function GetEventHandler: IEvent<T>;
  139. function GetInvoke: T;
  140. function GetOnChanged: TNotifyEvent;
  141. procedure SetEnabled(const Value: Boolean);
  142. procedure SetOnChanged(const Value: TNotifyEvent);
  143. public
  144. constructor Create(AEventHandler: IEvent<T>);
  145. procedure Add(const AEvent: T);
  146. procedure Remove(const AEvent: T);
  147. property Count: Integer read GetCount;
  148. property Enabled: Boolean read GetEnabled write SetEnabled;
  149. property EventHandler: IEvent<T> read GetEventHandler;
  150. property Invoke: T read GetInvoke;
  151. property OnChanged: TNotifyEvent read GetOnChanged write SetOnChanged;
  152. class operator Implicit(const AValue: Event<T>): IEvent<T>;
  153. class operator Implicit(const AValue: Event<T>): T;
  154. class operator Implicit(const AValue: IEvent<T>): Event<T>;
  155. end;
  156. procedure GetMethodTypeData(Method: TRttiMethod; var TypeData: PTypeData);
  157. function IsValid(AObject: TObject): Boolean;
  158. procedure MethodReferenceToMethodPointer(const AMethodReference; var AMethodPointer);
  159. var
  160. Context: TRttiContext;
  161. implementation
  162. procedure GetMethodTypeData(Method: TRttiMethod; var TypeData: PTypeData);
  163. type
  164. PParameterInfos = ^TParameterInfos;
  165. TParameterInfos = array[0..255] of PPTypeInfo;
  166. var
  167. params: TArray<TRttiParameter>;
  168. i: Integer;
  169. p: PByte;
  170. begin
  171. TypeData.MethodKind := Method.MethodKind;
  172. params := Method.GetParameters;
  173. TypeData.ParamCount := Length(params);
  174. p := @TypeData.ParamList;
  175. for i := Low(params) to High(params) do
  176. begin
  177. TParamFlags(p[0]) := params[i].Flags;
  178. Inc(p);
  179. PShortString(p)^ := ShortString(params[i].Name);
  180. Inc(p, p[0] + 1);
  181. PShortString(p)^ := ShortString(params[i].ParamType.Name);
  182. Inc(p, p[0] + 1);
  183. end;
  184. if method.MethodKind = mkFunction then
  185. begin
  186. PShortString(p)^ := ShortString(method.ReturnType.Name);
  187. Inc(p, p[0] + 1);
  188. PPTypeInfo(p)^ := method.ReturnType.Handle;
  189. Inc(p, 4);
  190. end;
  191. TCallConv(p[0]) := method.CallingConvention;
  192. Inc(p);
  193. for i := Low(params) to High(params) do
  194. begin
  195. PParameterInfos(Cardinal(p))[i] := PPTypeInfo(Cardinal(params[i].ParamType.Handle) - 4);
  196. end;
  197. end;
  198. function IsValid(AObject: TObject): Boolean;
  199. {$IFDEF VER210}
  200. type
  201. PNativeInt = ^NativeInt;
  202. {$ENDIF}
  203. begin
  204. Result := False;
  205. if Assigned(AObject) then
  206. try
  207. if PNativeInt(AObject)^ > $FFFF then // "hotfix" to prevent some access violations (no clue if this works) :)
  208. Result := PNativeInt(AObject)^ = PNativeInt(PNativeInt(AObject)^ + vmtSelfPtr)^;
  209. except
  210. end;
  211. end;
  212. procedure MethodReferenceToMethodPointer(const AMethodReference; var AMethodPointer);
  213. type
  214. TVtable = array[0..3] of Pointer;
  215. PVtable = ^TVtable;
  216. PPVtable = ^PVtable;
  217. begin
  218. // 3 is offset of Invoke, after QI, AddRef, Release
  219. TMethod(AMethodPointer).Code := PPVtable(AMethodReference)^^[3];
  220. TMethod(AMethodPointer).Data := Pointer(AMethodReference);
  221. end;
  222. procedure PassArg(Par: TRttiParameter; Params: PParameters; var Dest: TValue;
  223. CC: TCallConv; const Index: Integer; var Offset: Byte);
  224. const
  225. PointerSize = SizeOf(Pointer);
  226. begin
  227. {$IFDEF CPUX64}
  228. if Par.Flags * [pfVar, pfConst, pfOut] <> [] then
  229. begin
  230. Dest := TValue.From<Pointer>(PPointer(@Params.Stack[Offset])^);
  231. end
  232. else
  233. begin
  234. TValue.Make(Pointer(@Params.Stack[Offset]), Par.ParamType.Handle, Dest);
  235. end;
  236. Inc(Offset, PointerSize);
  237. {$ENDIF}
  238. {$IFDEF CPUX86}
  239. if (CC = ccReg) and (Index < 2) then
  240. begin
  241. if Par.Flags * [pfVar, pfConst, pfOut] <> [] then
  242. begin
  243. Dest := TValue.From<Pointer>(Pointer(Params.Registers[Index + 1]));
  244. end
  245. else
  246. begin
  247. TValue.Make(NativeInt(Params.Registers[Index + 1]), Par.ParamType.Handle, Dest);
  248. end
  249. end
  250. else
  251. begin
  252. Dec(Offset, PointerSize);
  253. if Par.Flags * [pfVar, pfConst, pfOut] <> [] then
  254. begin
  255. Dest := TValue.From<Pointer>(PPointer(@Params.Stack[Offset])^);
  256. end
  257. else
  258. begin
  259. TValue.Make(Pointer(@Params.Stack[Offset]), Par.ParamType.Handle, Dest);
  260. end;
  261. end;
  262. {$ENDIF}
  263. end;
  264. { TEventHandler }
  265. constructor TEvent.Create;
  266. begin
  267. FEnabled := True;
  268. FMethods := TList<TMethod>.Create();
  269. FMethods.OnNotify := InternalNotify;
  270. end;
  271. destructor TEvent.Destroy;
  272. begin
  273. FMethods.Free();
  274. ReleaseMethodPointer(FInternalDispatcher);
  275. inherited;
  276. end;
  277. procedure TEvent.InternalInvoke(Params: PParameters; StackSize: Integer);
  278. const
  279. PointerSize = SizeOf(Pointer);
  280. var
  281. LMethod: TMethod;
  282. {$IF CompilerVersion < 23}
  283. begin
  284. if FEnabled then
  285. begin
  286. for LMethod in FMethods do
  287. begin
  288. // "Push" parameters on stack
  289. if StackSize > 0 then
  290. asm
  291. // Put StackSize as third parameter
  292. MOV ECX,StackSize
  293. // stack address alignment
  294. ADD ECX,PointerSize-1
  295. AND ECX,NOT(PointerSize-1)
  296. AND ECX,$FFFF
  297. SUB ESP,ECX
  298. // Put Stack Address as second parameter
  299. MOV EDX,ESP
  300. // Put Params on Stack as first parameter
  301. MOV EAX,Params
  302. LEA EAX,[EAX].TParameters.Stack[8]
  303. CALL System.Move
  304. end;
  305. asm
  306. MOV EAX,Params
  307. MOV EDX,[EAX].TParameters.Registers.DWORD[0]
  308. MOV ECX,[EAX].TParameters.Registers.DWORD[4]
  309. MOV EAX,LMethod.Data
  310. CALL LMethod.Code
  311. end;
  312. end;
  313. end;
  314. end;
  315. {$ELSE}
  316. i: Integer;
  317. LArgs: TArray<TValue>;
  318. LOffset: Byte;
  319. begin
  320. if FEnabled and (FMethods.Count > 0) then
  321. begin
  322. {$IFDEF CPUX86}
  323. LOffset := StackSize;
  324. {$ENDIF}
  325. {$IFDEF CPUX64}
  326. LOffset := PointerSize;
  327. {$ENDIF}
  328. SetLength(LArgs, Length(FParameters) + 1);
  329. for i := Low(FParameters) to High(FParameters) do
  330. begin
  331. PassArg(FParameters[i], Params, LArgs[i + 1],
  332. FCallingConvention, i, LOffset);
  333. end;
  334. for LMethod in FMethods do
  335. begin
  336. LArgs[0] := TValue.From<TObject>(LMethod.Data);
  337. // workaround for incorrect type guess in Rtti.pas
  338. TValueData(LArgs[0]).FTypeInfo := TypeInfo(TObject);
  339. Rtti.Invoke(LMethod.Code, LArgs, FCallingConvention, nil);
  340. end;
  341. end;
  342. end;
  343. {$IFEND}
  344. procedure TEvent.InternalNotify(Sender: TObject; const Item: TMethod;
  345. Action: TCollectionNotification);
  346. begin
  347. if Assigned(Item.Data) and not IsValid(Item.Data) then
  348. begin
  349. case Action of
  350. cnAdded: IInterface(Item.Data)._AddRef();
  351. cnRemoved: IInterface(Item.Data)._Release();
  352. end;
  353. end;
  354. end;
  355. procedure TEvent.Invoke;
  356. asm
  357. {$IFDEF CPUX64}
  358. MOV RAX,RCX
  359. MOV RCX,[RAX].FInternalDispatcher.Data
  360. JMP [RAX].FInternalDispatcher.Code
  361. {$ELSE}
  362. MOV EBX,EAX
  363. MOV EAX,[EBX].FInternalDispatcher.Data
  364. JMP [EBX].FInternalDispatcher.Code
  365. {$ENDIF}
  366. end;
  367. procedure TEvent.Add(const AEvent: TMethod);
  368. begin
  369. FMethods.Add(AEvent);
  370. MethodAdded(AEvent);
  371. end;
  372. function TEvent.GetCount: Integer;
  373. begin
  374. Result := FMethods.Count;
  375. end;
  376. function TEvent.GetEnabled: Boolean;
  377. begin
  378. Result := FEnabled;
  379. end;
  380. function TEvent.IndexOf(const AEvent: TMethod): Integer;
  381. var
  382. i: Integer;
  383. begin
  384. Result := -1;
  385. for i := 0 to Pred(FMethods.Count) do
  386. begin
  387. if (FMethods[i].Code = TMethod(AEvent).Code)
  388. and (FMethods[i].Data = TMethod(AEvent).Data) then
  389. begin
  390. Result := i;
  391. Break;
  392. end;
  393. end;
  394. end;
  395. function TEvent.IndexOfInstance(const AInstance: TObject): Integer;
  396. var
  397. i: Integer;
  398. begin
  399. Result := -1;
  400. for i := 0 to Pred(FMethods.Count) do
  401. begin
  402. if TObject(FMethods[i].Data) = AInstance then
  403. begin
  404. Result := i;
  405. Break;
  406. end;
  407. end;
  408. end;
  409. procedure TEvent.Remove(const AEvent: TMethod);
  410. var
  411. i: Integer;
  412. begin
  413. i := IndexOf(AEvent);
  414. if i > -1 then
  415. begin
  416. FMethods.Delete(i);
  417. end;
  418. MethodRemoved(AEvent);
  419. end;
  420. procedure TEvent.RemoveInstanceReferences(const AInstance: TObject);
  421. var
  422. i: Integer;
  423. begin
  424. repeat
  425. i := IndexOfInstance(AInstance);
  426. if i > -1 then
  427. begin
  428. FMethods.Delete(i);
  429. end;
  430. until i = -1;
  431. end;
  432. procedure TEvent.SetDispatcher(out AMethod: TMethod; ATypeData: PTypeData);
  433. begin
  434. if Assigned(FInternalDispatcher.Code)
  435. and Assigned(FInternalDispatcher.Data) then
  436. begin
  437. ReleaseMethodPointer(FInternalDispatcher);
  438. end;
  439. FInternalDispatcher := CreateMethodPointer(InternalInvoke, ATypeData);
  440. AMethod := FInternalDispatcher;
  441. end;
  442. procedure TEvent.SetEnabled(const AValue: Boolean);
  443. begin
  444. FEnabled := AValue;
  445. end;
  446. { TEventHandler<T> }
  447. constructor TEvent<T>.Create(AOwner: TComponent);
  448. var
  449. MethInfo: PTypeInfo;
  450. TypeData: PTypeData;
  451. RttiType: TRttiType;
  452. Methods: TArray<TRttiMethod>;
  453. begin
  454. MethInfo := TypeInfo(T);
  455. TypeData := GetTypeData(MethInfo);
  456. inherited Create();
  457. if MethInfo.Kind = tkInterface then
  458. begin
  459. RttiType := Context.GetType(MethInfo);
  460. Methods := RttiType.GetMethods;
  461. Assert(Length(Methods) > 0, string(MethInfo.Name) + ' must contain extended RTTI');
  462. New(TypeData);
  463. try
  464. GetMethodTypeData(Methods[0], TypeData);
  465. Assert(TypeData.MethodKind = mkProcedure, string(MethInfo.Name) + ' must not be a function');
  466. {$IF CompilerVersion > 22}
  467. FCallingConvention := Methods[0].CallingConvention;
  468. FParameters := Methods[0].GetParameters();
  469. {$IFEND}
  470. SetEventDispatcher(FInvoke, TypeData);
  471. finally
  472. Dispose(TypeData);
  473. end;
  474. end
  475. else
  476. begin
  477. Assert(MethInfo.Kind = tkMethod, string(MethInfo.Name) + ' must be a method pointer type');
  478. Assert(TypeData.MethodKind = mkProcedure, string(MethInfo.Name) + ' must not be a function');
  479. {$IF CompilerVersion > 22}
  480. RttiType := Context.GetType(MethInfo);
  481. FCallingConvention := TRttiInvokableType(RttiType).CallingConvention;
  482. FParameters := TRttiInvokableType(RttiType).GetParameters();
  483. {$IFEND}
  484. SetEventDispatcher(FInvoke, TypeData);
  485. end;
  486. FNotificationHandler := TNotificationHandler<TEvent<T>>.Create(Self, Notification);
  487. if Assigned(AOwner) then
  488. begin
  489. FOwner := AOwner;
  490. FOwner.FreeNotification(FNotificationHandler);
  491. end;
  492. end;
  493. constructor TEvent<T>.Create(AOwner: TComponent; AEvents: array of T);
  494. var
  495. LEvent: T;
  496. begin
  497. Create(AOwner);
  498. for LEvent in AEvents do
  499. begin
  500. Add(LEvent);
  501. end;
  502. end;
  503. {$IF CompilerVersion > 21}
  504. class function TEvent<T>.Create<TDelegate>(AOwner: TComponent;
  505. ADelegates: array of TDelegate): TEvent<T>;
  506. var
  507. LDelegate: TDelegate;
  508. begin
  509. Result := Create(AOwner);
  510. for LDelegate in ADelegates do
  511. begin
  512. Result.Add<TDelegate>(LDelegate);
  513. end;
  514. end;
  515. {$IFEND}
  516. class function TEvent<T>.Create<TDelegate>(AOwner: TComponent;
  517. ADelegates: TArray<TDelegate>): TEvent<T>;
  518. var
  519. LDelegate: TDelegate;
  520. begin
  521. Result := Create(AOwner);
  522. for LDelegate in ADelegates do
  523. begin
  524. Result.Add<TDelegate>(LDelegate);
  525. end;
  526. end;
  527. destructor TEvent<T>.Destroy;
  528. begin
  529. FNotificationHandler.Free();
  530. inherited;
  531. end;
  532. procedure TEvent<T>.Add(AEvent: T);
  533. begin
  534. inherited Add(Cast(AEvent));
  535. end;
  536. procedure TEvent<T>.Add<TDelegate>(ADelegate: TDelegate);
  537. var
  538. LEvent: T;
  539. LTypeInfo: PTypeInfo;
  540. LTypeData: PTypeData;
  541. {$IF CompilerVersion > 22}
  542. // LMethod: TRttiMethod;
  543. // LParams: TArray<TRttiParameter>;
  544. {$IFEND}
  545. begin
  546. LTypeInfo := TypeInfo(TDelegate);
  547. Assert(LTypeInfo.Kind = tkInterface, 'TDelegate must be a method reference');
  548. LTypeInfo := TypeInfo(T);
  549. LTypeData := GetTypeData(LTypeInfo);
  550. {$IF CompilerVersion > 22}
  551. // Does not work right now because method references are missing RTTI
  552. // LMethod := Context.GetType(TypeInfo(TDelegate)).GetMethod('Invoke');
  553. // Assert(LMethod.MethodKind = LTypeData.MethodKind, 'MethodKind does not match');
  554. // LParams := LMethod.GetParameters();
  555. // Assert(Length(LParams) = LTypeData.ParamCount, 'ParamCount does not match');
  556. {$IFEND}
  557. MethodReferenceToMethodPointer(ADelegate, LEvent);
  558. Add(LEvent);
  559. end;
  560. function TEvent<T>.Cast(const Value: T): TMethod;
  561. begin
  562. if PTypeInfo(TypeInfo(T)).Kind = tkInterface then
  563. begin
  564. MethodReferenceToMethodPointer(Value, Result);
  565. end
  566. else
  567. begin
  568. Result := TMethod(Pointer(@Value)^);
  569. end;
  570. end;
  571. function TEvent<T>.GetInvoke: T;
  572. begin
  573. Result := FInvoke;
  574. end;
  575. function TEvent<T>.GetInvokeBase: TMethod;
  576. begin
  577. Result := TMethod(Pointer(@FInvoke)^);
  578. end;
  579. function TEvent<T>.GetOnChanged: TNotifyEvent;
  580. begin
  581. Result := FOnChanged;
  582. end;
  583. function TEvent<T>.IndexOf(AEvent: T): Integer;
  584. begin
  585. Result := inherited IndexOf(Cast(AEvent));
  586. end;
  587. procedure TEvent<T>.MethodAdded(const AMethod: TMethod);
  588. begin
  589. inherited;
  590. if IsValid(AMethod.Data) and (TObject(AMethod.Data) is TComponent) then
  591. begin
  592. FNotificationHandler.FreeNotification(TComponent(AMethod.Data));
  593. end;
  594. end;
  595. procedure TEvent<T>.MethodRemoved(const AMethod: TMethod);
  596. begin
  597. inherited;
  598. if IsValid(AMethod.Data) and (TObject(AMethod.Data) is TComponent)
  599. and (IndexOfInstance(TObject(AMethod.Data)) < 0) then
  600. begin
  601. FNotificationHandler.RemoveFreeNotification(TComponent(AMethod.Data));
  602. end;
  603. end;
  604. procedure TEvent<T>.Notification(
  605. AComponent: TComponent; Operation: TOperation);
  606. begin
  607. inherited;
  608. if Operation = opRemove then
  609. begin
  610. RemoveInstanceReferences(AComponent);
  611. if (AComponent = FOwner) and (RefCount = 0) then
  612. begin
  613. Free();
  614. end;
  615. end;
  616. end;
  617. procedure TEvent<T>.Remove(AEvent: T);
  618. begin
  619. inherited Remove(Cast(AEvent));
  620. end;
  621. procedure TEvent<T>.Remove<TDelegate>(ADelegate: TDelegate);
  622. var
  623. LEvent: T;
  624. begin
  625. MethodReferenceToMethodPointer(ADelegate, LEvent);
  626. Remove(LEvent);
  627. end;
  628. procedure TEvent<T>.SetEventDispatcher(out ADispatcher: T; ATypeData: PTypeData);
  629. var
  630. LMethod: TMethod;
  631. begin
  632. inherited SetDispatcher(LMethod, ATypeData);
  633. if PTypeInfo(TypeInfo(T)).Kind = tkInterface then
  634. begin
  635. PDelegate(@FInvoke)^ := Self;
  636. Self._Release;
  637. end
  638. else
  639. begin
  640. TMethod(Pointer(@ADispatcher)^) := LMethod;
  641. end;
  642. end;
  643. procedure TEvent<T>.SetOnChanged(const Value: TNotifyEvent);
  644. begin
  645. FOnChanged := Value;
  646. end;
  647. { TEvent<T> }
  648. constructor Event<T>.Create(AEventHandler: IEvent<T>);
  649. begin
  650. FEventHandler := AEventHandler;
  651. FInitialized := Assigned(FEventHandler);
  652. end;
  653. procedure Event<T>.Add(const AEvent: T);
  654. var
  655. LEventHandler: IEvent<T>;
  656. begin
  657. LEventHandler := EventHandler;
  658. if Assigned(LEventHandler) then
  659. begin
  660. LEventHandler.Add(AEvent);
  661. end;
  662. end;
  663. function Event<T>.GetCount: Integer;
  664. var
  665. LEventHandler: IEvent<T>;
  666. begin
  667. Result := 0;
  668. LEventHandler := EventHandler;
  669. if Assigned(LEventHandler) then
  670. begin
  671. Result := LEventHandler.Count;
  672. end;
  673. end;
  674. function Event<T>.GetEnabled: Boolean;
  675. var
  676. LEventHandler: IEvent<T>;
  677. begin
  678. Result := False;
  679. LEventHandler := EventHandler;
  680. if Assigned(LEventHandler) then
  681. begin
  682. Result := LEventHandler.Enabled;
  683. end;
  684. end;
  685. function Event<T>.GetEventHandler: IEvent<T>;
  686. begin
  687. if not FInitialized then
  688. begin
  689. FEventHandler := TEvent<T>.Create(nil);
  690. FInitialized := True;
  691. end;
  692. Result := FEventHandler;
  693. end;
  694. function Event<T>.GetInvoke: T;
  695. var
  696. LEventHandler: IEvent<T>;
  697. begin
  698. LEventHandler := EventHandler;
  699. if Assigned(LEventHandler) then
  700. begin
  701. Result := LEventHandler.Invoke;
  702. end;
  703. end;
  704. function Event<T>.GetOnChanged: TNotifyEvent;
  705. var
  706. LEventHandler: IEvent<T>;
  707. begin
  708. LEventHandler := EventHandler;
  709. if Assigned(LEventHandler) then
  710. begin
  711. Result := LEventHandler.OnChanged;
  712. end;
  713. end;
  714. procedure Event<T>.Remove(const AEvent: T);
  715. var
  716. LEventHandler: IEvent<T>;
  717. begin
  718. LEventHandler := EventHandler;
  719. if Assigned(LEventHandler) then
  720. begin
  721. LEventHandler.Remove(AEvent);
  722. end;
  723. end;
  724. procedure Event<T>.SetEnabled(const Value: Boolean);
  725. var
  726. LEventHandler: IEvent<T>;
  727. begin
  728. LEventHandler := EventHandler;
  729. if Assigned(LEventHandler) then
  730. begin
  731. LEventHandler.Enabled := Value;
  732. end;
  733. end;
  734. procedure Event<T>.SetOnChanged(const Value: TNotifyEvent);
  735. var
  736. LEventHandler: IEvent<T>;
  737. begin
  738. LEventHandler := EventHandler;
  739. if Assigned(LEventHandler) then
  740. begin
  741. LEventHandler.OnChanged := Value;
  742. end;
  743. end;
  744. class operator Event<T>.Implicit(const AValue: Event<T>): IEvent<T>;
  745. begin
  746. Result := AValue.EventHandler;
  747. end;
  748. class operator Event<T>.Implicit(const AValue: Event<T>): T;
  749. begin
  750. Result := AValue.EventHandler.Invoke;
  751. end;
  752. class operator Event<T>.Implicit(const AValue: IEvent<T>): Event<T>;
  753. begin
  754. Result := Event<T>.Create(AValue);
  755. end;
  756. end.