XCollection.pas 22 KB

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