GLS.XCollection.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.XCollection;
  5. (* A polymorphism-enabled TCollection-like set of classes *)
  6. { .$DEFINE DEBUG_XCOLLECTION } // on define the most apps will not work
  7. interface
  8. {$I GLScene.inc}
  9. uses
  10. System.Classes,
  11. System.SysUtils,
  12. System.Types,
  13. GLS.Strings,
  14. GLS.PersistentClasses
  15. {$IFDEF DEBUG_XCOLLECTION}, System.TypInfo {$ENDIF};
  16. type
  17. TXCollection = class;
  18. EFilerException = class(Exception)
  19. end;
  20. (* Base class for implementing a XCollection item.
  21. NOTES :
  22. Don't forget to override the ReadFromFiler/WriteToFiler persistence
  23. methods if you add data in a subclass !
  24. Subclasses must be registered using the RegisterXCollectionItemClass
  25. function for proper operation *)
  26. TXCollectionItem = class(TGLInterfacedPersistent)
  27. private
  28. FOwner: TXCollection;
  29. FName: string;
  30. protected
  31. function GetName: string; virtual;
  32. procedure SetName(const val: String); virtual;
  33. function GetOwner: TPersistent; override;
  34. // Override this function to write subclass data
  35. procedure WriteToFiler(writer: TWriter); virtual;
  36. // Override this function to read subclass data
  37. procedure ReadFromFiler(reader: TReader); virtual;
  38. // Override to perform things when owner form has been loaded
  39. procedure Loaded; dynamic;
  40. // Triggers an EFilerException with appropriate version message
  41. procedure RaiseFilerException(const archiveVersion: integer);
  42. public
  43. constructor Create(aOwner: TXCollection); virtual;
  44. destructor Destroy; override;
  45. function GetNamePath: string; override;
  46. property Owner: TXCollection read FOwner;
  47. // Default implementation uses WriteToFiler/ReadFromFiler
  48. procedure Assign(Source: TPersistent); override;
  49. procedure MoveUp;
  50. procedure MoveDown;
  51. function Index: integer;
  52. (* Returns a user-friendly denomination for the class.
  53. This denomination is used for picking a texture image class
  54. in the IDE expert *)
  55. class function FriendlyName: String; virtual; abstract;
  56. (* Returns a user-friendly description for the class.
  57. This denomination is used for helping the user when picking a
  58. texture image class in the IDE expert. If it's not overriden,
  59. takes its value from FriendlyName. *)
  60. class function FriendlyDescription: String; virtual;
  61. (* Category of the item class.
  62. This is a free string, it will used by the XCollectionEditor to
  63. regroup collectionitems and menu items *)
  64. class function ItemCategory: string; virtual;
  65. (* If true only one such XCollectionItem is allowed per BaseSceneObject.
  66. Inheritance is accounted for UniqueXCollectionItem resolution, ie.
  67. if TClassA is unique, and TClassB is a subclass of TClassA,
  68. whatever the unicity of TClassB, TClassA and TClassB won't be allowed
  69. to mix (since TClassB is a TClassA, and TClassA is unique).
  70. Attempting to break the unicity rules will not be possible at
  71. design-time (in Delphi IDE) and will trigger an exception at run-time. *)
  72. class function UniqueItem: Boolean; virtual;
  73. (* Allows the XCollectionItem class to determine if it should be allowed
  74. to be added to the given collection. *)
  75. class function CanAddTo(collection: TXCollection): Boolean; virtual;
  76. published
  77. property Name: string read FName write SetName;
  78. end;
  79. TXCollectionItemClass = class of TXCollectionItem;
  80. (* Holds a list of TXCollectionItem objects.
  81. This class looks a lot like a polymorphic-enabled TCollection, it is
  82. a much stripped down version of a proprietary TObjectList,
  83. if the copyrights are ever partially lifted
  84. on the originals, I'll base this code on them since they are way faster
  85. than VCL's lists and persistence mechanisms with polymorphism-support
  86. and full backward compatibility *)
  87. TXCollection = class(TPersistent)
  88. private
  89. FOwner: TPersistent;
  90. FList: TList;
  91. FCount: integer;
  92. // Archive Version is used to update the way data items is loaded
  93. FArchiveVersion: integer;
  94. protected
  95. function GetItems(Index: integer): TXCollectionItem;
  96. function GetOwner: TPersistent; override;
  97. procedure ReadFromFiler(reader: TReader);
  98. (* Here, we write all listed XCollection through their WriteToFiler methods,
  99. but to be able to restore them, we also write their classname, and to
  100. avoid wasting space if the same class appears multiple times we build up
  101. a lookup table while writing them, if the class is anew, the name is
  102. written, otherwise, only the index in the table is written.
  103. Using a global lookup table (instead of a "per-WriteData" one) could save
  104. more space, but would also increase dependencies, and this I don't want 8)*)
  105. procedure WriteToFiler(writer: TWriter);
  106. public
  107. constructor Create(aOwner: TPersistent); virtual;
  108. destructor Destroy; override;
  109. procedure Assign(Source: TPersistent); override;
  110. procedure Loaded;
  111. property Owner: TPersistent read FOwner write FOwner;
  112. function GetNamePath: string; override;
  113. (* Class of the items.
  114. Unlike TCollection, items can be of ItemsClass OR ANY of its
  115. subclasses, ie. this function is used only for asserting your adding
  116. objects of the right class, and not for persistence. *)
  117. class function ItemsClass: TXCollectionItemClass; virtual;
  118. property Items[index: integer]: TXCollectionItem read GetItems; default;
  119. property Count: integer read FCount;
  120. function Add(anItem: TXCollectionItem): integer;
  121. function GetOrCreate(anItem: TXCollectionItemClass): TXCollectionItem;
  122. procedure Delete(Index: integer);
  123. procedure Remove(anItem: TXCollectionItem);
  124. procedure Clear;
  125. function IndexOf(anItem: TXCollectionItem): integer;
  126. // Returns the index of the first XCollectionItem of the given class (or -1)
  127. function IndexOfClass(aClass: TXCollectionItemClass): integer;
  128. // Returns the first XCollection of the given class (or nil)
  129. function GetByClass(aClass: TXCollectionItemClass): TXCollectionItem;
  130. // Returns the index of the first XCollectionItem of the given name (or -1)
  131. function IndexOfName(const aName: string): integer;
  132. (* Indicates if an object of the given class can be added.
  133. This function is used to enforce Unique XCollection. *)
  134. function CanAdd(aClass: TXCollectionItemClass): Boolean; virtual;
  135. property ArchiveVersion: integer read FArchiveVersion;
  136. end;
  137. // Registers an event to be called when an XCollection is destroyed
  138. procedure RegisterXCollectionDestroyEvent(notifyEvent: TNotifyEvent);
  139. // DeRegisters event
  140. procedure DeRegisterXCollectionDestroyEvent(notifyEvent: TNotifyEvent);
  141. // Registers a TXCollectionItem subclass for persistence requirements
  142. procedure RegisterXCollectionItemClass(aClass: TXCollectionItemClass);
  143. // Removes a TXCollectionItem subclass from the list
  144. procedure UnregisterXCollectionItemClass(aClass: TXCollectionItemClass);
  145. // Retrieves a registered TXCollectionItemClass from its classname
  146. function FindXCollectionItemClass(const ClassName: string): TXCollectionItemClass;
  147. (* Creates and returns a copy of internal list of TXCollectionItem classes.
  148. Returned list should be freed by caller, the parameter defines an ancestor
  149. class filter. If baseClass is left nil, TXCollectionItem is used as ancestor. *)
  150. function GetXCollectionItemClassesList(baseClass: TXCollectionItemClass = nil): TList;
  151. procedure GetXCollectionClassesList(var ClassesList: TList;
  152. baseClass: TXCollectionItemClass = nil);
  153. // ------------------------------------------------------------------
  154. implementation
  155. // ------------------------------------------------------------------
  156. const
  157. (* Magic is a workaround that will allow us to know
  158. if the archive version is 0 (equivalent to:
  159. there is no ArchiveVersion stored in the DFM file) *)
  160. MAGIC: array [0 .. 3] of AnsiChar = 'XCOL';
  161. var
  162. vXCollectionItemClasses: TList;
  163. vXCollectionDestroyEvent: TNotifyEvent;
  164. // ---------- internal global routines (used by xcollection editor) -------------
  165. procedure RegisterXCollectionDestroyEvent(notifyEvent: TNotifyEvent);
  166. begin
  167. vXCollectionDestroyEvent := notifyEvent;
  168. end;
  169. procedure DeRegisterXCollectionDestroyEvent(notifyEvent: TNotifyEvent);
  170. begin
  171. vXCollectionDestroyEvent := nil;
  172. end;
  173. // ------------------------------------------------------------------------------
  174. procedure RegisterXCollectionItemClass(aClass: TXCollectionItemClass);
  175. begin
  176. if not Assigned(vXCollectionItemClasses) then
  177. vXCollectionItemClasses := TList.Create;
  178. if vXCollectionItemClasses.IndexOf(aClass) < 0 then
  179. vXCollectionItemClasses.Add(aClass);
  180. end;
  181. procedure UnregisterXCollectionItemClass(aClass: TXCollectionItemClass);
  182. begin
  183. if not Assigned(vXCollectionItemClasses) then
  184. exit;
  185. if vXCollectionItemClasses.IndexOf(aClass) >= 0 then
  186. vXCollectionItemClasses.Remove(aClass);
  187. end;
  188. function FindXCollectionItemClass(const ClassName: String): TXCollectionItemClass;
  189. var
  190. i: integer;
  191. begin
  192. Result := nil;
  193. if Assigned(vXCollectionItemClasses) then
  194. for i := 0 to vXCollectionItemClasses.Count - 1 do
  195. if TXCollectionItemClass(vXCollectionItemClasses[i]).ClassName = ClassName
  196. then
  197. begin
  198. Result := TXCollectionItemClass(vXCollectionItemClasses[i]);
  199. Break;
  200. end;
  201. end;
  202. function GetXCollectionItemClassesList(baseClass: TXCollectionItemClass = nil): TList;
  203. begin
  204. Result := TList.Create;
  205. GetXCollectionClassesList(Result, baseClass);
  206. end;
  207. procedure GetXCollectionClassesList(var ClassesList: TList;
  208. baseClass: TXCollectionItemClass = nil);
  209. var
  210. i: integer;
  211. begin
  212. if not Assigned(baseClass) then
  213. baseClass := TXCollectionItem;
  214. if Assigned(vXCollectionItemClasses) then
  215. for i := 0 to vXCollectionItemClasses.Count - 1 do
  216. if TXCollectionItemClass(vXCollectionItemClasses[i]).InheritsFrom(baseClass) then
  217. ClassesList.Add(vXCollectionItemClasses[i]);
  218. end;
  219. // ------------------
  220. // ------------------ TXCollectionItem ------------------
  221. // ------------------
  222. constructor TXCollectionItem.Create(aOwner: TXCollection);
  223. begin
  224. inherited Create;
  225. FOwner := aOwner;
  226. if Assigned(aOwner) then
  227. begin
  228. Assert(aOwner.CanAdd(TXCollectionItemClass(Self.ClassType)),
  229. 'Addition of ' + Self.ClassName + ' to ' + aOwner.ClassName +
  230. ' rejected.');
  231. aOwner.FList.Add(Self);
  232. aOwner.FCount := aOwner.FList.Count;
  233. end;
  234. FName := FriendlyName;
  235. end;
  236. destructor TXCollectionItem.Destroy;
  237. begin
  238. if Assigned(FOwner) then
  239. begin
  240. FOwner.FList.Remove(Self);
  241. FOwner.FCount := FOwner.FList.Count;
  242. end;
  243. inherited Destroy;
  244. end;
  245. procedure TXCollectionItem.Assign(Source: TPersistent);
  246. begin
  247. if Source is TXCollectionItem then
  248. begin
  249. FName := TXCollectionItem(Source).Name;
  250. end
  251. else
  252. inherited Assign(Source);
  253. end;
  254. procedure TXCollectionItem.SetName(const val: string);
  255. begin
  256. FName := val;
  257. end;
  258. function TXCollectionItem.GetOwner: TPersistent;
  259. begin
  260. Result := FOwner;
  261. end;
  262. procedure TXCollectionItem.WriteToFiler(writer: TWriter);
  263. begin
  264. with writer do
  265. begin
  266. WriteInteger(0); // Archive Version 0
  267. WriteString(FName);
  268. end;
  269. end;
  270. procedure TXCollectionItem.ReadFromFiler(reader: TReader);
  271. {$IFOPT C+}
  272. var
  273. ver: integer;
  274. {$ENDIF}
  275. begin
  276. with reader do
  277. begin
  278. {$IFOPT C+}
  279. ver := ReadInteger;
  280. Assert(ver = 0);
  281. {$ENDIF}
  282. FName := ReadString;
  283. end;
  284. end;
  285. procedure TXCollectionItem.Loaded;
  286. begin
  287. // does nothing by default
  288. end;
  289. function TXCollectionItem.GetName: string;
  290. begin
  291. Result := FName;
  292. end;
  293. function TXCollectionItem.GetNamePath: string;
  294. begin
  295. if FOwner <> nil then
  296. Result := Format('%s[%d]', [FOwner.GetNamePath, Index])
  297. else
  298. Result := inherited GetNamePath;
  299. end;
  300. procedure TXCollectionItem.MoveUp;
  301. var
  302. i: integer;
  303. begin
  304. if Assigned(Owner) then
  305. begin
  306. i := Owner.FList.IndexOf(Self);
  307. if i > 0 then
  308. Owner.FList.Exchange(i, i - 1);
  309. end;
  310. end;
  311. procedure TXCollectionItem.MoveDown;
  312. var
  313. i: integer;
  314. begin
  315. if Assigned(Owner) then
  316. begin
  317. i := Owner.FList.IndexOf(Self);
  318. if Cardinal(i) < Cardinal(Owner.FList.Count - 1) then
  319. Owner.FList.Exchange(i, i + 1);
  320. end;
  321. end;
  322. function TXCollectionItem.Index: integer;
  323. begin
  324. if Assigned(Owner) then
  325. Result := Owner.FList.IndexOf(Self)
  326. else
  327. Result := -1;
  328. end;
  329. procedure TXCollectionItem.RaiseFilerException(const archiveVersion: integer);
  330. begin
  331. raise EFilerException.Create(ClassName + strUnknownArchiveVersion +
  332. IntToStr(archiveVersion));
  333. end;
  334. class function TXCollectionItem.FriendlyDescription: string;
  335. begin
  336. Result := FriendlyName;
  337. end;
  338. class function TXCollectionItem.ItemCategory: string;
  339. begin
  340. Result := '';
  341. end;
  342. class function TXCollectionItem.UniqueItem: Boolean;
  343. begin
  344. Result := False;
  345. end;
  346. class function TXCollectionItem.CanAddTo(collection: TXCollection): Boolean;
  347. begin
  348. Result := True;
  349. end;
  350. // ------------------
  351. // ------------------ TXCollection ------------------
  352. // ------------------
  353. constructor TXCollection.Create(aOwner: TPersistent);
  354. begin
  355. inherited Create;
  356. FOwner := aOwner;
  357. FList := TList.Create;
  358. end;
  359. destructor TXCollection.Destroy;
  360. begin
  361. if Assigned(vXCollectionDestroyEvent) then
  362. vXCollectionDestroyEvent(Self);
  363. Clear;
  364. FList.Free;
  365. inherited Destroy;
  366. end;
  367. procedure TXCollection.Assign(Source: TPersistent);
  368. var
  369. i: integer;
  370. srcItem, newItem: TXCollectionItem;
  371. begin
  372. if not Assigned(Source) then
  373. begin
  374. Clear;
  375. end
  376. else if Source.ClassType = Self.ClassType then
  377. begin
  378. Clear;
  379. FList.Capacity := TXCollection(Source).FList.Count;
  380. for i := 0 to TXCollection(Source).Count - 1 do
  381. begin
  382. srcItem := TXCollectionItem(TXCollection(Source).FList[i]);
  383. newItem := TXCollectionItemClass(srcItem.ClassType).Create(Self);
  384. newItem.Assign(srcItem);
  385. end;
  386. end
  387. else
  388. inherited Assign(Source);
  389. FCount := FList.Count;
  390. end;
  391. procedure TXCollection.Loaded;
  392. var
  393. i: integer;
  394. begin
  395. for i := 0 to FList.Count - 1 do
  396. TXCollectionItem(FList[i]).Loaded;
  397. end;
  398. procedure TXCollection.WriteToFiler(writer: TWriter);
  399. var
  400. i, n: integer;
  401. classList: TList;
  402. XCollectionItem: TXCollectionItem;
  403. begin
  404. FArchiveVersion := 1;
  405. classList := TList.Create;
  406. try
  407. with writer do
  408. begin
  409. // Magic header and archive version are always written now
  410. WriteInteger(PInteger(@MAGIC[0])^);
  411. WriteInteger(FArchiveVersion);
  412. WriteInteger(FList.Count);
  413. for i := 0 to FList.Count - 1 do
  414. begin
  415. XCollectionItem := TXCollectionItem(FList[i]);
  416. n := classList.IndexOf(XCollectionItem.ClassType);
  417. if n < 0 then
  418. begin
  419. WriteString(XCollectionItem.ClassName);
  420. classList.Add(XCollectionItem.ClassType);
  421. end
  422. else
  423. WriteInteger(n);
  424. XCollectionItem.WriteToFiler(writer);
  425. end;
  426. end;
  427. finally
  428. classList.Free;
  429. end;
  430. end;
  431. procedure TXCollection.ReadFromFiler(reader: TReader);
  432. var
  433. vt: TValueType;
  434. Header: array [0 .. 3] of AnsiChar;
  435. n, lc, lcnum: integer;
  436. classList: TList;
  437. cName: string;
  438. XCollectionItemClass: TXCollectionItemClass;
  439. XCollectionItem: TXCollectionItem;
  440. begin
  441. // see WriteData for a description of what is going on here
  442. Clear;
  443. classList := TList.Create;
  444. try
  445. with reader do
  446. begin
  447. // save current reader position, it will be used to rewind the reader if the DFM is too old
  448. try
  449. vt := NextValue;
  450. if vt in [vaInt32, vaInt16, vaInt8] then
  451. PInteger(@Header[0])^ := ReadInteger
  452. else
  453. begin
  454. Read(Header[0], Length(Header));
  455. end;
  456. except
  457. Header[0] := #0;
  458. Header[1] := #0;
  459. Header[2] := #0;
  460. Header[3] := #0;
  461. end;
  462. // after reading the header, we need to compare it with the MAGIC reference
  463. if (Header[0] = MAGIC[0]) and (Header[1] = MAGIC[1]) and
  464. (Header[2] = MAGIC[2]) and (Header[3] = MAGIC[3]) then
  465. begin
  466. // if its ok we can just read the archive version
  467. FArchiveVersion := ReadInteger;
  468. lc := ReadInteger;
  469. end
  470. else
  471. begin
  472. // if the header is invalid (old DFM) just assume archive version is 0 and rewind reader
  473. FArchiveVersion := 0;
  474. lc := PInteger(@Header[0])^;
  475. end;
  476. for n := 1 to lc do
  477. begin
  478. if NextValue in [vaString, vaLString] then
  479. begin
  480. cName := ReadString;
  481. {$IFDEF DEBUG_XCOLLECTION}
  482. writeln('TXCollection.ReadFromFiler create class entry: ', cName);
  483. {$ENDIF}
  484. XCollectionItemClass := FindXCollectionItemClass(cName);
  485. Assert(Assigned(XCollectionItemClass),
  486. 'Class ' + cName +
  487. ' unknown. Add the relevant unit to your "uses".');
  488. classList.Add(XCollectionItemClass);
  489. end
  490. else
  491. begin
  492. {$IFDEF DEBUG_XCOLLECTION}
  493. Assert(NextValue in [vaInt8, vaInt16, vaInt32],
  494. 'Non-Integer ValueType: ' + GetEnumName(TypeInfo(TValueType),
  495. Ord(NextValue)));
  496. {$ENDIF}
  497. lcnum := ReadInteger;
  498. Assert((lcnum >= 0) and (lcnum < classList.Count),
  499. 'Invalid classlistIndex: ' + IntToStr(lcnum));
  500. XCollectionItemClass := TXCollectionItemClass(classList[lcnum]);
  501. {$IFDEF DEBUG_XCOLLECTION}
  502. writeln('TXCollection.ReadFromFiler create by number: ', lcnum,
  503. ' -> ', XCollectionItemClass.ClassName);
  504. {$ENDIF}
  505. end;
  506. if Assigned(XCollectionItemClass) then
  507. begin
  508. XCollectionItem := XCollectionItemClass.Create(Self);
  509. XCollectionItem.ReadFromFiler(reader);
  510. end;
  511. end;
  512. end;
  513. finally
  514. classList.Free;
  515. end;
  516. FCount := FList.Count;
  517. end;
  518. class function TXCollection.ItemsClass: TXCollectionItemClass;
  519. begin
  520. Result := TXCollectionItem;
  521. end;
  522. function TXCollection.GetItems(Index: integer): TXCollectionItem;
  523. begin
  524. Result := TXCollectionItem(FList[index]);
  525. end;
  526. function TXCollection.GetOwner: TPersistent;
  527. begin
  528. Result := FOwner;
  529. end;
  530. function TXCollection.GetNamePath: String;
  531. var
  532. s: String;
  533. begin
  534. Result := ClassName;
  535. if GetOwner = nil then
  536. exit;
  537. s := GetOwner.GetNamePath;
  538. if s = '' then
  539. exit;
  540. Result := s + '.XCollection';
  541. end;
  542. function TXCollection.Add(anItem: TXCollectionItem): integer;
  543. begin
  544. Assert(anItem.InheritsFrom(ItemsClass));
  545. Assert(CanAdd(TXCollectionItemClass(anItem.ClassType)));
  546. if Assigned(anItem.FOwner) then
  547. begin
  548. anItem.FOwner.FList.Remove(anItem);
  549. anItem.FOwner.FCount := anItem.FOwner.FList.Count;
  550. end;
  551. anItem.FOwner := Self;
  552. Result := FList.Add(anItem);
  553. FCount := FList.Count;
  554. end;
  555. function TXCollection.GetOrCreate(anItem: TXCollectionItemClass)
  556. : TXCollectionItem;
  557. var
  558. i: integer;
  559. begin
  560. Assert(anItem.InheritsFrom(ItemsClass));
  561. i := Self.IndexOfClass(anItem);
  562. if i >= 0 then
  563. Result := TXCollectionItem(Self[i])
  564. else
  565. Result := anItem.Create(Self);
  566. end;
  567. procedure TXCollection.Delete(Index: integer);
  568. begin
  569. Assert(Cardinal(index) < Cardinal(FList.Count));
  570. // doin' it the fast way
  571. with TXCollectionItem(FList[index]) do
  572. begin
  573. FOwner := nil;
  574. Free;
  575. end;
  576. FList.Delete(index);
  577. FCount := FList.Count;
  578. end;
  579. procedure TXCollection.Remove(anItem: TXCollectionItem);
  580. var
  581. i: integer;
  582. begin
  583. i := IndexOf(anItem);
  584. if i >= 0 then
  585. Delete(i);
  586. end;
  587. procedure TXCollection.Clear;
  588. var
  589. i: integer;
  590. begin
  591. // Fast kill of owned XCollection
  592. for i := 0 to FList.Count - 1 do
  593. with TXCollectionItem(FList[i]) do
  594. begin
  595. FOwner := nil;
  596. Free;
  597. end;
  598. FList.Clear;
  599. FCount := 0;
  600. end;
  601. function TXCollection.IndexOf(anItem: TXCollectionItem): integer;
  602. begin
  603. Result := FList.IndexOf(anItem);
  604. end;
  605. function TXCollection.IndexOfClass(aClass: TXCollectionItemClass): integer;
  606. var
  607. i: integer;
  608. begin
  609. Result := -1;
  610. for i := 0 to FList.Count - 1 do
  611. if TXCollectionItem(FList[i]) is aClass then
  612. begin
  613. Result := i;
  614. Break;
  615. end;
  616. end;
  617. function TXCollection.GetByClass(aClass: TXCollectionItemClass): TXCollectionItem;
  618. var
  619. i: integer;
  620. begin
  621. Result := nil;
  622. for i := 0 to FList.Count - 1 do
  623. if TXCollectionItem(FList[i]) is aClass then
  624. begin
  625. Result := TXCollectionItem(FList[i]);
  626. Break;
  627. end;
  628. end;
  629. function TXCollection.IndexOfName(const aName: string): integer;
  630. var
  631. i: integer;
  632. begin
  633. Result := -1;
  634. for i := 0 to FList.Count - 1 do
  635. if TXCollectionItem(FList[i]).Name = aName then
  636. begin
  637. Result := i;
  638. Break;
  639. end;
  640. end;
  641. function TXCollection.CanAdd(aClass: TXCollectionItemClass): Boolean;
  642. var
  643. i: integer;
  644. XCollectionItemClass: TXCollectionItemClass;
  645. begin
  646. Result := True;
  647. // Test if the class allows itself to be added to this collection
  648. if not aClass.CanAddTo(Self) then
  649. begin
  650. Result := False;
  651. Exit;
  652. end;
  653. // is the given class compatible with owned ones ?
  654. if aClass.UniqueItem then
  655. for i := 0 to Count - 1 do
  656. begin
  657. if Items[i] is aClass then
  658. begin
  659. Result := False;
  660. Break;
  661. end;
  662. end;
  663. // are the owned classes compatible with the given one ?
  664. if Result then
  665. for i := 0 to Count - 1 do
  666. begin
  667. XCollectionItemClass := TXCollectionItemClass(Items[i].ClassType);
  668. if (XCollectionItemClass.UniqueItem) and
  669. aClass.InheritsFrom(XCollectionItemClass) then
  670. begin
  671. Result := False;
  672. Break;
  673. end;
  674. end;
  675. end;
  676. // ------------------------------------------------------------------
  677. initialization
  678. // ------------------------------------------------------------------
  679. finalization
  680. vXCollectionItemClasses.Free;
  681. end.