system.messaging.pp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2023 the Free Pascal development team
  4. Generic messaging service class.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. }
  11. unit System.Messaging;
  12. {$MODE OBJFPC}
  13. {$H+}
  14. {$modeswitch functionreferences}
  15. {$modeswitch advancedrecords}
  16. interface
  17. uses
  18. {$IFDEF FPC_DOTTEDUNITS}
  19. System.SysUtils, System.Classes, System.Generics.Collections;
  20. {$ELSE}
  21. SysUtils, Classes, Generics.Collections;
  22. {$ENDIF}
  23. type
  24. TMessageBase = class abstract;
  25. // TMessage = TMessageBase;
  26. generic TMessage<T> = class (TMessageBase)
  27. protected
  28. FValue: T;
  29. public
  30. constructor Create(const AValue: T);
  31. destructor Destroy; override;
  32. property Value: T read FValue;
  33. end;
  34. generic TObjectMessage<T: class> = class(specialize TMessage<T>)
  35. protected
  36. FOwnsObject: Boolean;
  37. public
  38. constructor Create(const AValue: T; aOwnsObject: Boolean = True);
  39. destructor Destroy; override;
  40. end;
  41. TMessageListener = reference to procedure(const Sender: TObject; const M: TMessageBase);
  42. TMessageListenerMethod = procedure (const Sender: TObject; const M: TMessageBase) of object;
  43. {$IFNDEF CPU64}
  44. TMessageSubscriptionId = LongInt;
  45. {$ELSE}
  46. TMessageSubscriptionId = Int64;
  47. {$ENDIF}
  48. TBaseMessageManager = Class;
  49. TBaseMessageManagerClass = Class of TBaseMessageManager;
  50. { TBaseMessageManager }
  51. TBaseMessageManager = class
  52. Private
  53. FNextID : TMessageSubscriptionId;
  54. Private
  55. class var _instance: TBaseMessageManager;
  56. class function GetInstance: TBaseMessageManager; static;
  57. Public
  58. Class Destructor Done;
  59. class var DefaultManagerClass: TBaseMessageManagerClass;
  60. Protected
  61. Function GenerateClientID : TMessageSubscriptionId;
  62. Public
  63. Constructor Create; virtual;
  64. function SubscribeToMessage(const aMessageClass: TClass; const aListener: TMessageListener): TMessageSubscriptionId; virtual; abstract; overload;
  65. function SubscribeToMessage(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod): TMessageSubscriptionId; virtual; abstract; overload;
  66. procedure Unsubscribe(const aMessageClass: TClass; SubscriptionId: TMessageSubscriptionId; Immediate: Boolean = False); virtual; abstract; overload;
  67. procedure Unsubscribe(const aMessageClass: TClass; const aListener: TMessageListener; Immediate: Boolean = False); virtual; abstract; overload;
  68. procedure Unsubscribe(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod; Immediate: Boolean = False); virtual; abstract; overload;
  69. procedure SendMessage(const Sender: TObject; AMessage: TMessageBase); overload;
  70. procedure SendMessage(const Sender: TObject; AMessage: TMessageBase; ADispose: Boolean); virtual; abstract; overload;
  71. class property DefaultManager: TBaseMessageManager read GetInstance;
  72. end;
  73. { TMessageManager }
  74. // Default, delphi compatible implementation
  75. TMessageManager = class(TBaseMessageManager)
  76. protected
  77. type
  78. { TListenerWithId }
  79. TListenerWithId = class
  80. Id: TMessageSubscriptionId;
  81. Listener: TMessageListener;
  82. ListenerMethod: TMessageListenerMethod;
  83. MarkedAsRemoved : Boolean;
  84. Public
  85. Function Matches(aListener : TMessageListener) : Boolean; inline;
  86. Function Matches(aListener : TMessageListenerMethod) : Boolean; inline;
  87. Function Matches(aID : TMessageSubscriptionId) : Boolean; inline;
  88. constructor Create(const AId: TMessageSubscriptionId; const AListenerMethod: TMessageListenerMethod); overload;
  89. constructor Create(const AId: TMessageSubscriptionId; const AListener: TMessageListener); overload;
  90. procedure MarkAsRemoved;
  91. end;
  92. PListenerWithId = ^TListenerWithId;
  93. TListenerWithIdList = specialize TObjectList<TListenerWithId>;
  94. { TListenerList }
  95. TListenerList = class
  96. Private
  97. FList : TListenerWithIdList;
  98. FUpdateCount : Integer;
  99. FUnSubscribeCount : Integer;
  100. Procedure BeginUpdate; inline;
  101. Procedure EndUpdate; inline;
  102. function Updating : Boolean; inline;
  103. procedure DoUnsubscribe(Index: Integer);
  104. Public
  105. constructor Create;
  106. destructor destroy; override;
  107. procedure RemoveEmpty;
  108. procedure CheckRemoveEmpty; inline;
  109. function Subscribe(const AId: TMessageSubscriptionId; const AListener: TMessageListener): TMessageSubscriptionId; overload;
  110. function Subscribe(const AId: TMessageSubscriptionId; const AListenerMethod: TMessageListenerMethod): TMessageSubscriptionId; overload;
  111. procedure Unsubscribe(Index: TMessageSubscriptionId);
  112. procedure Unsubscribe(aListener: TMessageListener);
  113. procedure Unsubscribe(aListener: TMessageListenerMethod);
  114. procedure SendMessage(const Sender: TObject; const AMessage: TMessageBase);
  115. end;
  116. TListenerRegistry = specialize TObjectDictionary<TClass, TListenerList>;
  117. private
  118. protected
  119. FListeners: TListenerRegistry;
  120. function Add(const aMessageClass: TClass;
  121. const aListener: TMessageListener; aListenerMethod: TMessageListenerMethod
  122. ): Integer;
  123. procedure RegisterMessageClass(const AMessageClass: TClass);
  124. public
  125. constructor Create; override;
  126. destructor Destroy; override;
  127. function SubscribeToMessage(const aMessageClass: TClass; const aListener: TMessageListener): TMessageSubscriptionId; override;
  128. function SubscribeToMessage(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod): TMessageSubscriptionId; override;
  129. procedure Unsubscribe(const aMessageClass: TClass; SubscriptionId: TMessageSubscriptionId; Immediate: Boolean = False); override;
  130. procedure Unsubscribe(const aMessageClass: TClass; const aListener: TMessageListener; Immediate: Boolean = False); override;
  131. procedure Unsubscribe(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod; Immediate: Boolean = False); override;
  132. procedure SendMessage(const Sender: TObject; AMessage: TMessageBase; ADispose: Boolean); override;
  133. end;
  134. TMessageManagerClass = class of TMessageManager;
  135. { ---------------------------------------------------------------------
  136. TMessageClientList
  137. ---------------------------------------------------------------------}
  138. // FPC implementation, designed to be extensible.
  139. // Used as default.
  140. // Set TBaseMessageManager.DefaultManagerClass if you want to change the default.
  141. { TMessageClient }
  142. TMessageClient = class (TCollectionItem)
  143. Public
  144. Disabled : boolean; // Unsubscribed but not yet deleted...
  145. ClientID : TMessageSubscriptionId;
  146. Public
  147. constructor Create(aCollection : TCollection; aClientID : TMessageSubscriptionId); overload;
  148. Procedure CallNotify(Sender : TObject; aMessage : TMessageBase); virtual; abstract;
  149. end;
  150. { TMessageListenerClient }
  151. TMessageListenerClient = class(TMessageClient)
  152. Private
  153. FListener: TMessageListener;
  154. Protected
  155. Property Listener : TMessageListener Read FListener;
  156. Public
  157. constructor Create(aCollection : TCollection; aClientID : TMessageSubscriptionId; aListener: TMessageListener); overload;
  158. Procedure CallNotify(Sender : TObject; aMessage : TMessageBase); override;
  159. end;
  160. { TMessageListenerMethodClient }
  161. TMessageListenerMethodClient = class(TMessageClient)
  162. Private
  163. FListener: TMessageListenerMethod;
  164. Protected
  165. Property Listener : TMessageListenerMethod Read FListener;
  166. Public
  167. constructor Create(aCollection : TCollection; aClientID : TMessageSubscriptionId; aListener: TMessageListenerMethod); overload;
  168. Procedure CallNotify(Sender : TObject; aMessage : TMessageBase); override;
  169. end;
  170. { TMessageClientList }
  171. TMessageClientList = class(TCollection)
  172. private
  173. FBusy : Boolean;
  174. Protected
  175. Procedure Update(aItem: TCollectionItem); override;
  176. procedure RemoveDisabled; virtual;
  177. Property Busy : Boolean Read FBusy Write FBusy;
  178. public
  179. constructor Create(aItemClass : TCollectionItemClass);
  180. function Add(aId : Integer; const aListener: TMessageListener) : TMessageClient; virtual;
  181. function Add(aId : Integer; const aListenerMethod: TMessageListenerMethod): TMessageClient; virtual;
  182. procedure NotifyClients(const Sender: TObject; const aMessage: TMessageBase);
  183. // These should be improved to be faster ?
  184. function IndexOf(const aClientID: TMessageSubscriptionId) : integer; virtual; overload;
  185. function IndexOf(const aListener: TMessageListener): integer; virtual; overload;
  186. function IndexOf(const aListenerMethod: TMessageListenerMethod): integer; virtual; overload;
  187. procedure Remove(aIndex : Integer);
  188. end;
  189. { TSimpleMessageManager }
  190. TSimpleMessageManager = class(TBaseMessageManager)
  191. protected
  192. Type
  193. TMessageClientListDict = specialize TObjectDictionary<TClass, TMessageClientList>;
  194. Private
  195. FMessageClients: TMessageClientListDict;
  196. Protected
  197. function CreateMessageTypeDict: TMessageClientListDict; virtual;
  198. function CreateMessageClientList: TMessageClientList; virtual;
  199. Function GetList(const aMessageClass: TClass; Out aList : TMessageClientList) : Boolean;
  200. Function GetOrCreateList(const aMessageClass: TClass) : TMessageClientList;
  201. public
  202. constructor Create; override;
  203. destructor Destroy; override;
  204. function SubscribeToMessage(const aMessageClass: TClass; const aListener: TMessageListener): TMessageSubscriptionId; override;
  205. function SubscribeToMessage(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod): TMessageSubscriptionId; override;
  206. // Immediate not used, it will break during sending of message
  207. procedure Unsubscribe(const aMessageClass: TClass; SubscriptionId: TMessageSubscriptionId; Immediate: Boolean = False); override;
  208. procedure Unsubscribe(const aMessageClass: TClass; const aListener: TMessageListener; Immediate: Boolean = False); override;
  209. procedure Unsubscribe(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod; Immediate: Boolean = False); override;
  210. procedure SendMessage(const Sender: TObject; aMessage: TMessageBase; aDispose: Boolean); override;
  211. end;
  212. implementation
  213. { TMessageListenerMethodClient }
  214. constructor TMessageListenerMethodClient.Create(aCollection: TCollection; aClientID: TMessageSubscriptionId; aListener: TMessageListenerMethod);
  215. begin
  216. Inherited Create(aCollection,aClientID);
  217. FListener:=aListener;
  218. end;
  219. procedure TMessageListenerMethodClient.CallNotify(Sender: TObject;
  220. aMessage: TMessageBase);
  221. begin
  222. FListener(Sender,aMessage);
  223. end;
  224. { TMessageListenerClient }
  225. constructor TMessageListenerClient.Create(aCollection: TCollection;
  226. aClientID: TMessageSubscriptionId; aListener: TMessageListener);
  227. begin
  228. Inherited Create(aCollection,aClientID);
  229. FListener:=aListener;
  230. end;
  231. procedure TMessageListenerClient.CallNotify(Sender: TObject; aMessage: TMessageBase);
  232. begin
  233. FListener(Sender,aMessage);
  234. end;
  235. { TSimpleMessageManager }
  236. constructor TSimpleMessageManager.Create;
  237. begin
  238. FMessageClients:=CreateMessageTypeDict;
  239. end;
  240. destructor TSimpleMessageManager.Destroy;
  241. begin
  242. FreeAndNil(FMessageClients);
  243. inherited;
  244. end;
  245. function TSimpleMessageManager.GetList(const aMessageClass: TClass; out
  246. aList: TMessageClientList): Boolean;
  247. begin
  248. aList:=Nil;
  249. Result:=FMessageClients.TryGetValue(aMessageClass,aList);
  250. end;
  251. function TSimpleMessageManager.CreateMessageTypeDict: TMessageClientListDict;
  252. begin
  253. Result:=TMessageClientListDict.Create([doOwnsValues]);
  254. end;
  255. function TSimpleMessageManager.CreateMessageClientList :TMessageClientList;
  256. begin
  257. Result:=TMessageClientList.Create(TMessageClient);
  258. end;
  259. function TSimpleMessageManager.GetOrCreateList(const aMessageClass: TClass): TMessageClientList;
  260. begin
  261. if GetList(aMessageClass,Result) then
  262. exit;
  263. Result:=CreateMessageClientList;
  264. FMessageClients.Add(AMessageClass, Result);
  265. end;
  266. function TSimpleMessageManager.SubscribeToMessage(const aMessageClass: TClass; const aListener: TMessageListener) : TMessageSubscriptionId;
  267. var
  268. Clients: TMessageClientList;
  269. begin
  270. Clients:=GetOrCreateList(aMessageClass);
  271. Result:=GenerateClientID;
  272. Clients.Add(Result,AListener);
  273. end;
  274. function TSimpleMessageManager.SubscribeToMessage(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod): TMessageSubscriptionId;
  275. var
  276. Clients: TMessageClientList;
  277. begin
  278. Clients:=GetOrCreateList(aMessageClass);
  279. Result:=GenerateClientID;
  280. Clients.Add(Result,AListenerMethod);
  281. end;
  282. procedure TSimpleMessageManager.Unsubscribe(const aMessageClass: TClass; const aListener: TMessageListener; Immediate: Boolean);
  283. var
  284. Clients : TMessageClientList;
  285. Idx : Integer;
  286. begin
  287. if Not FMessageClients.TryGetValue(aMessageClass,Clients) then
  288. exit;
  289. Idx:=Clients.IndexOf(aListener);
  290. if Idx<>-1 then
  291. Clients.Remove(Idx);
  292. end;
  293. procedure TSimpleMessageManager.Unsubscribe(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod; Immediate: Boolean);
  294. var
  295. Clients : TMessageClientList;
  296. Idx : Integer;
  297. begin
  298. if Not FMessageClients.TryGetValue(aMessageClass,Clients) then
  299. exit;
  300. Idx:=Clients.IndexOf(aListenerMethod);
  301. if Idx<>-1 then
  302. Clients.Remove(Idx);
  303. end;
  304. procedure TSimpleMessageManager.Unsubscribe(const aMessageClass: TClass; SubscriptionId: TMessageSubscriptionId; Immediate: Boolean);
  305. var
  306. Clients : TMessageClientList;
  307. Idx : Integer;
  308. begin
  309. if Not FMessageClients.TryGetValue(aMessageClass,Clients) then
  310. exit;
  311. Idx:=Clients.IndexOf(SubscriptionId);
  312. Clients.Remove(Idx);
  313. end;
  314. procedure TSimpleMessageManager.SendMessage(const Sender: TObject;
  315. aMessage: TMessageBase; aDispose: Boolean);
  316. var
  317. Clients: TMessageClientList;
  318. begin
  319. if (AMessage=nil) then exit;
  320. try
  321. if not GetList(aMessage.ClassType,Clients) then
  322. exit;
  323. Clients.NotifyClients(Sender,AMessage);
  324. finally
  325. if ADispose then
  326. AMessage.Free;
  327. end;
  328. end;
  329. { TClientList }
  330. procedure TMessageClientList.Update(aItem: TCollectionItem);
  331. begin
  332. inherited Update(aItem);
  333. if (aItem=Nil) and not Busy then
  334. RemoveDisabled;
  335. end;
  336. constructor TMessageClientList.Create(aItemClass : TCollectionItemClass);
  337. begin
  338. Inherited Create(aItemClass);
  339. end;
  340. function TMessageClientList.Add(aId: Integer; const aListener: TMessageListener ): TMessageClient;
  341. begin
  342. FBusy:=True;// Prevent cleaning
  343. try
  344. Result:=TMessageListenerClient.Create(Self,aId,aListener);
  345. finally
  346. FBusy:=False;
  347. end;
  348. end;
  349. function TMessageClientList.Add(aId: Integer; const aListenerMethod: TMessageListenerMethod): TMessageClient;
  350. begin
  351. FBusy:=True;// Prevent cleaning
  352. try
  353. Result:=TMessageListenerMethodClient.Create(Self,aID,aListenerMethod);
  354. finally
  355. FBusy:=False;
  356. end;
  357. end;
  358. procedure TMessageClientList.NotifyClients(const Sender: TObject;
  359. const aMessage: TMessageBase);
  360. var
  361. Listener: TMessageClient;
  362. I : integer;
  363. begin
  364. BeginUpdate;
  365. try
  366. for I:=0 to Count-1 do
  367. begin
  368. Listener:=TMessageClient(Items[I]);
  369. if Not Listener.Disabled then
  370. Listener.CallNotify(Sender, AMessage)
  371. end;
  372. finally
  373. EndUpdate;
  374. end;
  375. end;
  376. function TMessageClientList.IndexOf(const aClientID: TMessageSubscriptionId): integer;
  377. begin
  378. Result:=Count-1;
  379. While (Result>=0) and (TMessageClient(Items[Result]).ClientID<>aClientID) do
  380. Dec(Result);
  381. end;
  382. function TMessageClientList.IndexOf(const aListener: TMessageListener): integer;
  383. Function IsMatch(C : TMessageClient) : Boolean;
  384. var
  385. L : TMessageListenerClient absolute C;
  386. begin
  387. Result:=(C is TMessageListenerClient) and (L.Listener=aListener);
  388. end;
  389. begin
  390. Result:=Count-1;
  391. While (Result>=0) and Not IsMatch(TMessageClient(Items[Result])) do
  392. Dec(Result);
  393. end;
  394. function TMessageClientList.IndexOf(const aListenerMethod: TMessageListenerMethod): integer;
  395. Function IsMatch(C : TMessageClient) : Boolean;
  396. var
  397. L : TMessageListenerMethodClient absolute C;
  398. begin
  399. Result:=(C is TMessageListenerMethodClient) and (L.Listener=aListenerMethod);
  400. end;
  401. begin
  402. Result:=Count-1;
  403. While (Result>=0) and Not IsMatch(TMessageClient(Items[Result])) do
  404. Dec(Result);
  405. end;
  406. procedure TMessageClientList.Remove(aIndex: Integer);
  407. {
  408. We cannot just remove clients at once: when sending messages they must be
  409. sent in order of listener registration.
  410. But sending a message can result in a listener being deleted.
  411. This can change the indexes in the list if done incorrectly.
  412. So we can only delete when all messages have been processed.
  413. We use the standard TCollection Begin/EndUdpate mechansim for this.
  414. }
  415. begin
  416. if (aIndex<0) or (aIndex>=Count) then exit;
  417. BeginUpdate;
  418. try
  419. TMessageClient(Items[aIndex]).Disabled:=True;
  420. finally
  421. EndUpdate;
  422. end;
  423. end;
  424. procedure TMessageClientList.RemoveDisabled;
  425. var
  426. I : Integer;
  427. begin
  428. FBusy:=True;
  429. BeginUpdate;
  430. try
  431. for I:=Count-1 downto 0 do
  432. if TMessageClient(Items[I]).Disabled then
  433. Delete(I);
  434. finally
  435. EndUpdate;
  436. FBusy:=False;
  437. end;
  438. end;
  439. { TClient }
  440. constructor TMessageClient.Create(aCollection: TCollection; aClientID: TMessageSubscriptionId);
  441. begin
  442. Disabled:=False; // Safety: set before inherited, make sure cleanup does not happen.
  443. Inherited Create(aCollection);
  444. ClientID:=aClientID;
  445. end;
  446. { TBaseMessageManager }
  447. class function TBaseMessageManager.GetInstance: TBaseMessageManager;
  448. begin
  449. if _Instance=Nil then
  450. begin
  451. if DefaultManagerClass=Nil then
  452. DefaultManagerClass:=TSimpleMessageManager;
  453. _Instance:=DefaultManagerClass.Create;
  454. end;
  455. Result:=_Instance;
  456. end;
  457. class destructor TBaseMessageManager.Done;
  458. begin
  459. FreeAndNil(_Instance);
  460. end;
  461. function TBaseMessageManager.GenerateClientID: TMessageSubscriptionId;
  462. begin
  463. Result:=AtomicIncrement(FNextID);
  464. end;
  465. constructor TBaseMessageManager.Create;
  466. begin
  467. // Do nothing. Need virtual constructor
  468. end;
  469. procedure TBaseMessageManager.SendMessage(const Sender: TObject;
  470. AMessage: TMessageBase);
  471. begin
  472. SendMessage(Sender,aMessage,True);
  473. end;
  474. { TMessageManager.TListenerWithId }
  475. constructor TMessageManager.TListenerWithId.Create(const aId: TMessageSubscriptionId; const aListenerMethod: TMessageListenerMethod);
  476. begin
  477. Id:=aID;
  478. ListenerMethod:=aListenerMethod;
  479. end;
  480. constructor TMessageManager.TListenerWithId.Create(const AId: TMessageSubscriptionId; const AListener: TMessageListener);
  481. begin
  482. Id:=aId;
  483. Listener:=aListener;
  484. end;
  485. Function TMessageManager.TListenerWithId.Matches(aID : TMessageSubscriptionId) : Boolean;
  486. begin
  487. Result:=(aId=ID);
  488. end;
  489. function TMessageManager.TListenerWithId.Matches(aListener: TMessageListener): Boolean;
  490. begin
  491. Result:=(Pointer(aListener)=Pointer(Listener));
  492. end;
  493. function TMessageManager.TListenerWithId.Matches(aListener: TMessageListenerMethod): Boolean;
  494. Var
  495. M1,M2 : TMethod;
  496. begin
  497. M1:=TMethod(aListener);
  498. M2:=TMethod(ListenerMethod);
  499. Result:=(M1.Code=M2.Code) and (M2.Data=M2.Data);
  500. end;
  501. procedure TMessageManager.TListenerWithId.MarkAsRemoved;
  502. begin
  503. MarkedAsRemoved:=True;
  504. Id:=0;
  505. Listener:=Nil;
  506. ListenerMethod:=Nil;
  507. end;
  508. { TMessageManager.TListenerList }
  509. procedure TMessageManager.TListenerList.BeginUpdate;
  510. begin
  511. AtomicIncrement(FUpdateCount);
  512. end;
  513. procedure TMessageManager.TListenerList.EndUpdate;
  514. begin
  515. AtomicDecrement(FUpdateCount);
  516. end;
  517. function TMessageManager.TListenerList.Updating: Boolean;
  518. begin
  519. Result:=(FUpdateCount>0);
  520. end;
  521. constructor TMessageManager.TListenerList.Create;
  522. begin
  523. FList:=TListenerWithIdList.Create(True);
  524. end;
  525. destructor TMessageManager.TListenerList.destroy;
  526. begin
  527. FreeAndNil(Flist);
  528. inherited destroy;
  529. end;
  530. procedure TMessageManager.TListenerList.SendMessage(const Sender: TObject; const AMessage: TMessageBase);
  531. var
  532. I : Integer;
  533. L : TListenerWithId;
  534. begin
  535. BeginUpdate;
  536. try
  537. for I:=0 to FList.Count-1 do
  538. begin
  539. L:=FList[I];
  540. if L.MarkedAsRemoved then
  541. continue;
  542. if Assigned(L.Listener) then
  543. L.Listener(Sender, AMessage)
  544. else if Assigned(L.ListenerMethod) then
  545. L.ListenerMethod(Sender,AMessage);
  546. end;
  547. finally
  548. EndUpdate;
  549. end;
  550. end;
  551. procedure TMessageManager.TListenerList.CheckRemoveEmpty;
  552. begin
  553. if FUnSubscribeCount>10 then
  554. RemoveEmpty;
  555. end;
  556. function TMessageManager.TListenerList.Subscribe(const AId: TMessageSubscriptionId; const AListener: TMessageListener): TMessageSubscriptionId;
  557. var
  558. Obj : TListenerWithId;
  559. begin
  560. Obj:=TListenerWithId.Create(aId,aListener);
  561. FList.Add(Obj);
  562. Result:=Obj.Id;
  563. end;
  564. function TMessageManager.TListenerList.Subscribe(const AId: TMessageSubscriptionId; const AListenerMethod: TMessageListenerMethod): TMessageSubscriptionId;
  565. var
  566. Obj : TListenerWithId;
  567. begin
  568. Obj:=TListenerWithId.Create(aId,aListenerMethod);
  569. FList.Add(Obj);
  570. Result:=Obj.Id;
  571. end;
  572. procedure TMessageManager.TListenerList.Unsubscribe(Index: TMessageSubscriptionId);
  573. var
  574. Idx : integer;
  575. begin
  576. Idx:=FList.Count-1;
  577. While (Idx>=0) and not FList[Idx].Matches(Index) do
  578. Dec(Idx);
  579. If Idx>0 then
  580. DoUnsubscribe(Idx);
  581. end;
  582. procedure TMessageManager.TListenerList.Unsubscribe(aListener: TMessageListener);
  583. var
  584. Idx : integer;
  585. begin
  586. Idx:=FList.Count-1;
  587. While (Idx>=0) and not FList[Idx].Matches(aListener) do
  588. Dec(Idx);
  589. If Idx>0 then
  590. DoUnsubscribe(Idx);
  591. end;
  592. procedure TMessageManager.TListenerList.Unsubscribe(aListener: TMessageListenerMethod);
  593. var
  594. Idx : integer;
  595. begin
  596. Idx:=FList.Count-1;
  597. While (Idx>=0) and not FList[Idx].Matches(aListener) do
  598. Dec(Idx);
  599. If Idx>0 then
  600. DoUnsubscribe(Idx);
  601. end;
  602. procedure TMessageManager.TListenerList.DoUnsubscribe(Index: Integer);
  603. begin
  604. FList[Index].MarkAsRemoved;
  605. Inc(FUnSubscribeCount);
  606. if Not Updating then
  607. CheckRemoveEmpty;
  608. end;
  609. procedure TMessageManager.TListenerList.RemoveEmpty;
  610. var
  611. I, N: Integer;
  612. L : TListenerWithId;
  613. begin
  614. N:=0;
  615. for I:=0 to FList.Count-1 do
  616. begin
  617. L:=FList[I];
  618. if Not L.MarkedAsRemoved then
  619. begin
  620. if N<I then
  621. FList[N]:=L;
  622. Inc(N);
  623. end;
  624. end;
  625. FList.Count:=N;
  626. FUnSubscribeCount:=0;
  627. end;
  628. { TMessageManager }
  629. constructor TMessageManager.Create;
  630. begin
  631. FListeners := TListenerRegistry.Create([doOwnsValues]);
  632. end;
  633. destructor TMessageManager.Destroy;
  634. begin
  635. FListeners.Free;
  636. inherited;
  637. end;
  638. procedure TMessageManager.RegisterMessageClass(const aMessageClass: TClass);
  639. begin
  640. if not FListeners.ContainsKey(AMessageClass) then
  641. FListeners.Add(AMessageClass, TListenerList.Create);
  642. end;
  643. function TMessageManager.Add(const aMessageClass: TClass; const aListener: TMessageListener; aListenerMethod: TMessageListenerMethod) : Integer;
  644. var
  645. List: TListenerList;
  646. begin
  647. Result := -1;
  648. RegisterMessageClass(aMessageClass);
  649. if Not FListeners.TryGetValue(aMessageClass,List) then
  650. Exit;
  651. Result:=GenerateClientID;
  652. If Assigned(aListener) then
  653. List.Subscribe(Result,aListener)
  654. else
  655. List.SubScribe(Result,aListenerMethod);
  656. end;
  657. function TMessageManager.SubscribeToMessage(const aMessageClass: TClass; const aListener: TMessageListener) : TMessageSubscriptionID;
  658. begin
  659. Result:=Add(aMessageClass,aListener,Nil);
  660. end;
  661. function TMessageManager.SubscribeToMessage(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod): TMessageSubscriptionID;
  662. begin
  663. Result:=Add(aMessageClass,Nil,aListenerMethod);
  664. end;
  665. procedure TMessageManager.Unsubscribe(const aMessageClass: TClass; const aListener: TMessageListener; Immediate: Boolean);
  666. var
  667. List : TListenerList;
  668. begin
  669. if Not FListeners.TryGetValue(AMessageClass,List) then
  670. Exit;
  671. List.Unsubscribe(AListener);
  672. end;
  673. procedure TMessageManager.Unsubscribe(const aMessageClass: TClass; const aListenerMethod: TMessageListenerMethod; Immediate: Boolean);
  674. var
  675. List : TListenerList;
  676. begin
  677. if Not FListeners.TryGetValue(AMessageClass,List) then
  678. Exit;
  679. List.Unsubscribe(aListenerMethod);
  680. end;
  681. procedure TMessageManager.Unsubscribe(const aMessageClass: TClass; SubscriptionId: TMessageSubscriptionId; Immediate: Boolean);
  682. var
  683. List: TListenerList;
  684. begin
  685. if not FListeners.TryGetValue(AMessageClass,List) then
  686. Exit;
  687. List.Unsubscribe(SubscriptionID);
  688. end;
  689. procedure TMessageManager.SendMessage(const Sender: TObject; AMessage: TMessageBase; ADispose: Boolean);
  690. var
  691. List: TListenerList;
  692. begin
  693. if aMessage=nil then
  694. Exit;
  695. try
  696. if FListeners.TryGetValue(aMessage.ClassType,List) then
  697. List.SendMessage(Sender,aMessage);
  698. finally
  699. if aDispose then
  700. aMessage.Free;
  701. end
  702. end;
  703. constructor TMessage.Create(const aValue: T);
  704. begin
  705. FValue := AValue;
  706. end;
  707. destructor TMessage.Destroy;
  708. begin
  709. inherited;
  710. end;
  711. { TObjectMessage<T> }
  712. constructor TObjectMessage.Create(const aValue: T; aOwnsObject: Boolean);
  713. begin
  714. inherited Create(AValue);
  715. FOwnsObject:=aOwnsObject;
  716. end;
  717. destructor TObjectMessage.Destroy;
  718. begin
  719. if FOwnsObject then
  720. FValue.Free;
  721. inherited Destroy;
  722. end;
  723. end.