GXS.XCollection.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. //
  2. // The graphics engine GXScene
  3. //
  4. unit GXS.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 Stage.Defines.inc}
  9. uses
  10. System.Classes,
  11. System.SysUtils,
  12. System.Types,
  13. GXS.PersistentClasses,
  14. Stage.Strings
  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(TgxInterfacedPersistent)
  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. implementation // -------------------------------------------------------------
  154. const
  155. (* Magic is a workaround that will allow us to know
  156. if the archive version is 0 (equivalent to:
  157. there is no ArchiveVersion stored in the DFM file) *)
  158. MAGIC: array [0 .. 3] of AnsiChar = 'XCOL';
  159. var
  160. vXCollectionItemClasses: TList;
  161. vXCollectionDestroyEvent: TNotifyEvent;
  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
  194. then
  195. begin
  196. Result := TXCollectionItemClass(vXCollectionItemClasses[i]);
  197. Break;
  198. end;
  199. end;
  200. function GetXCollectionItemClassesList(baseClass: TXCollectionItemClass = nil): TList;
  201. begin
  202. Result := TList.Create;
  203. GetXCollectionClassesList(Result, baseClass);
  204. end;
  205. procedure GetXCollectionClassesList(var ClassesList: TList;
  206. baseClass: TXCollectionItemClass = nil);
  207. var
  208. i: integer;
  209. begin
  210. if not Assigned(baseClass) then
  211. baseClass := TXCollectionItem;
  212. if Assigned(vXCollectionItemClasses) then
  213. for i := 0 to vXCollectionItemClasses.Count - 1 do
  214. if TXCollectionItemClass(vXCollectionItemClasses[i]).InheritsFrom(baseClass) then
  215. ClassesList.Add(vXCollectionItemClasses[i]);
  216. end;
  217. // ------------------
  218. // ------------------ TXCollectionItem ------------------
  219. // ------------------
  220. constructor TXCollectionItem.Create(aOwner: TXCollection);
  221. begin
  222. inherited Create;
  223. FOwner := aOwner;
  224. if Assigned(aOwner) then
  225. begin
  226. Assert(aOwner.CanAdd(TXCollectionItemClass(Self.ClassType)),
  227. 'Addition of ' + Self.ClassName + ' to ' + aOwner.ClassName +
  228. ' rejected.');
  229. aOwner.FList.Add(Self);
  230. aOwner.FCount := aOwner.FList.Count;
  231. end;
  232. FName := FriendlyName;
  233. end;
  234. destructor TXCollectionItem.Destroy;
  235. begin
  236. if Assigned(FOwner) then
  237. begin
  238. FOwner.FList.Remove(Self);
  239. FOwner.FCount := FOwner.FList.Count;
  240. end;
  241. inherited Destroy;
  242. end;
  243. procedure TXCollectionItem.Assign(Source: TPersistent);
  244. begin
  245. if Source is TXCollectionItem then
  246. begin
  247. FName := TXCollectionItem(Source).Name;
  248. end
  249. else
  250. inherited Assign(Source);
  251. end;
  252. procedure TXCollectionItem.SetName(const val: string);
  253. begin
  254. FName := val;
  255. end;
  256. function TXCollectionItem.GetOwner: TPersistent;
  257. begin
  258. Result := FOwner;
  259. end;
  260. procedure TXCollectionItem.WriteToFiler(writer: TWriter);
  261. begin
  262. with writer do
  263. begin
  264. WriteInteger(0); // Archive Version 0
  265. WriteString(FName);
  266. end;
  267. end;
  268. procedure TXCollectionItem.ReadFromFiler(reader: TReader);
  269. {$IFOPT C+}
  270. var
  271. ver: integer;
  272. {$ENDIF}
  273. begin
  274. with reader do
  275. begin
  276. {$IFOPT C+}
  277. ver := ReadInteger;
  278. Assert(ver = 0);
  279. {$ENDIF}
  280. FName := ReadString;
  281. end;
  282. end;
  283. procedure TXCollectionItem.Loaded;
  284. begin
  285. // does nothing by default
  286. end;
  287. function TXCollectionItem.GetName: string;
  288. begin
  289. Result := FName;
  290. end;
  291. function TXCollectionItem.GetNamePath: string;
  292. begin
  293. if FOwner <> nil then
  294. Result := Format('%s[%d]', [FOwner.GetNamePath, Index])
  295. else
  296. Result := inherited GetNamePath;
  297. end;
  298. procedure TXCollectionItem.MoveUp;
  299. var
  300. i: integer;
  301. begin
  302. if Assigned(Owner) then
  303. begin
  304. i := Owner.FList.IndexOf(Self);
  305. if i > 0 then
  306. Owner.FList.Exchange(i, i - 1);
  307. end;
  308. end;
  309. procedure TXCollectionItem.MoveDown;
  310. var
  311. i: integer;
  312. begin
  313. if Assigned(Owner) then
  314. begin
  315. i := Owner.FList.IndexOf(Self);
  316. if Cardinal(i) < Cardinal(Owner.FList.Count - 1) then
  317. Owner.FList.Exchange(i, i + 1);
  318. end;
  319. end;
  320. function TXCollectionItem.Index: integer;
  321. begin
  322. if Assigned(Owner) then
  323. Result := Owner.FList.IndexOf(Self)
  324. else
  325. Result := -1;
  326. end;
  327. procedure TXCollectionItem.RaiseFilerException(const archiveVersion: integer);
  328. begin
  329. raise EFilerException.Create(ClassName + strUnknownArchiveVersion +
  330. IntToStr(archiveVersion));
  331. end;
  332. class function TXCollectionItem.FriendlyDescription: string;
  333. begin
  334. Result := FriendlyName;
  335. end;
  336. class function TXCollectionItem.ItemCategory: string;
  337. begin
  338. Result := '';
  339. end;
  340. class function TXCollectionItem.UniqueItem: Boolean;
  341. begin
  342. Result := False;
  343. end;
  344. class function TXCollectionItem.CanAddTo(collection: TXCollection): Boolean;
  345. begin
  346. Result := True;
  347. end;
  348. // ------------------
  349. // ------------------ TXCollection ------------------
  350. // ------------------
  351. constructor TXCollection.Create(aOwner: TPersistent);
  352. begin
  353. inherited Create;
  354. FOwner := aOwner;
  355. FList := TList.Create;
  356. end;
  357. destructor TXCollection.Destroy;
  358. begin
  359. if Assigned(vXCollectionDestroyEvent) then
  360. vXCollectionDestroyEvent(Self);
  361. Clear;
  362. FList.Free;
  363. inherited Destroy;
  364. end;
  365. procedure TXCollection.Assign(Source: TPersistent);
  366. var
  367. i: integer;
  368. srcItem, newItem: TXCollectionItem;
  369. begin
  370. if not Assigned(Source) then
  371. begin
  372. Clear;
  373. end
  374. else if Source.ClassType = Self.ClassType then
  375. begin
  376. Clear;
  377. FList.Capacity := TXCollection(Source).FList.Count;
  378. for i := 0 to TXCollection(Source).Count - 1 do
  379. begin
  380. srcItem := TXCollectionItem(TXCollection(Source).FList[i]);
  381. newItem := TXCollectionItemClass(srcItem.ClassType).Create(Self);
  382. newItem.Assign(srcItem);
  383. end;
  384. end
  385. else
  386. inherited Assign(Source);
  387. FCount := FList.Count;
  388. end;
  389. procedure TXCollection.Loaded;
  390. var
  391. i: integer;
  392. begin
  393. for i := 0 to FList.Count - 1 do
  394. TXCollectionItem(FList[i]).Loaded;
  395. end;
  396. procedure TXCollection.WriteToFiler(writer: TWriter);
  397. var
  398. i, n: integer;
  399. classList: TList;
  400. XCollectionItem: TXCollectionItem;
  401. begin
  402. FArchiveVersion := 1;
  403. classList := TList.Create;
  404. try
  405. with writer do
  406. begin
  407. // Magic header and archive version are always written now
  408. WriteInteger(PInteger(@MAGIC[0])^);
  409. WriteInteger(FArchiveVersion);
  410. WriteInteger(FList.Count);
  411. for i := 0 to FList.Count - 1 do
  412. begin
  413. XCollectionItem := TXCollectionItem(FList[i]);
  414. n := classList.IndexOf(XCollectionItem.ClassType);
  415. if n < 0 then
  416. begin
  417. WriteString(XCollectionItem.ClassName);
  418. classList.Add(XCollectionItem.ClassType);
  419. end
  420. else
  421. WriteInteger(n);
  422. XCollectionItem.WriteToFiler(writer);
  423. end;
  424. end;
  425. finally
  426. classList.Free;
  427. end;
  428. end;
  429. procedure TXCollection.ReadFromFiler(reader: TReader);
  430. var
  431. vt: TValueType;
  432. Header: array [0 .. 3] of AnsiChar;
  433. n, lc, lcnum: integer;
  434. classList: TList;
  435. cName: string;
  436. XCollectionItemClass: TXCollectionItemClass;
  437. XCollectionItem: TXCollectionItem;
  438. begin
  439. // see WriteData for a description of what is going on here
  440. Clear;
  441. classList := TList.Create;
  442. try
  443. with reader do
  444. begin
  445. // save current reader position, it will be used to rewind the reader if the DFM is too old
  446. try
  447. vt := NextValue;
  448. if vt in [vaInt32, vaInt16, vaInt8] then
  449. PInteger(@Header[0])^ := ReadInteger
  450. else
  451. begin
  452. Read(Header[0], Length(Header));
  453. end;
  454. except
  455. Header[0] := #0;
  456. Header[1] := #0;
  457. Header[2] := #0;
  458. Header[3] := #0;
  459. end;
  460. // after reading the header, we need to compare it with the MAGIC reference
  461. if (Header[0] = MAGIC[0]) and (Header[1] = MAGIC[1]) and
  462. (Header[2] = MAGIC[2]) and (Header[3] = MAGIC[3]) then
  463. begin
  464. // if its ok we can just read the archive version
  465. FArchiveVersion := ReadInteger;
  466. lc := ReadInteger;
  467. end
  468. else
  469. begin
  470. // if the header is invalid (old DFM) just assume archive version is 0 and rewind reader
  471. FArchiveVersion := 0;
  472. lc := PInteger(@Header[0])^;
  473. end;
  474. for n := 1 to lc do
  475. begin
  476. if NextValue in [vaString, vaLString] then
  477. begin
  478. cName := ReadString;
  479. {$IFDEF DEBUG_XCOLLECTION}
  480. writeln('TXCollection.ReadFromFiler create class entry: ', cName);
  481. {$ENDIF}
  482. XCollectionItemClass := FindXCollectionItemClass(cName);
  483. Assert(Assigned(XCollectionItemClass),
  484. 'Class ' + cName +
  485. ' unknown. Add the relevant unit to your "uses".');
  486. classList.Add(XCollectionItemClass);
  487. end
  488. else
  489. begin
  490. {$IFDEF DEBUG_XCOLLECTION}
  491. Assert(NextValue in [vaInt8, vaInt16, vaInt32],
  492. 'Non-Integer ValueType: ' + GetEnumName(TypeInfo(TValueType),
  493. Ord(NextValue)));
  494. {$ENDIF}
  495. lcnum := ReadInteger;
  496. Assert((lcnum >= 0) and (lcnum < classList.Count),
  497. 'Invalid classlistIndex: ' + IntToStr(lcnum));
  498. XCollectionItemClass := TXCollectionItemClass(classList[lcnum]);
  499. {$IFDEF DEBUG_XCOLLECTION}
  500. writeln('TXCollection.ReadFromFiler create by number: ', lcnum,
  501. ' -> ', XCollectionItemClass.ClassName);
  502. {$ENDIF}
  503. end;
  504. if Assigned(XCollectionItemClass) then
  505. begin
  506. XCollectionItem := XCollectionItemClass.Create(Self);
  507. XCollectionItem.ReadFromFiler(reader);
  508. end;
  509. end;
  510. end;
  511. finally
  512. classList.Free;
  513. end;
  514. FCount := FList.Count;
  515. end;
  516. class function TXCollection.ItemsClass: TXCollectionItemClass;
  517. begin
  518. Result := TXCollectionItem;
  519. end;
  520. function TXCollection.GetItems(Index: integer): TXCollectionItem;
  521. begin
  522. Result := TXCollectionItem(FList[index]);
  523. end;
  524. function TXCollection.GetOwner: TPersistent;
  525. begin
  526. Result := FOwner;
  527. end;
  528. function TXCollection.GetNamePath: String;
  529. var
  530. s: String;
  531. begin
  532. Result := ClassName;
  533. if GetOwner = nil then
  534. exit;
  535. s := GetOwner.GetNamePath;
  536. if s = '' then
  537. exit;
  538. Result := s + '.XCollection';
  539. end;
  540. function TXCollection.Add(anItem: TXCollectionItem): integer;
  541. begin
  542. Assert(anItem.InheritsFrom(ItemsClass));
  543. Assert(CanAdd(TXCollectionItemClass(anItem.ClassType)));
  544. if Assigned(anItem.FOwner) then
  545. begin
  546. anItem.FOwner.FList.Remove(anItem);
  547. anItem.FOwner.FCount := anItem.FOwner.FList.Count;
  548. end;
  549. anItem.FOwner := Self;
  550. Result := FList.Add(anItem);
  551. FCount := FList.Count;
  552. end;
  553. function TXCollection.GetOrCreate(anItem: TXCollectionItemClass)
  554. : TXCollectionItem;
  555. var
  556. i: integer;
  557. begin
  558. Assert(anItem.InheritsFrom(ItemsClass));
  559. i := Self.IndexOfClass(anItem);
  560. if i >= 0 then
  561. Result := TXCollectionItem(Self[i])
  562. else
  563. Result := anItem.Create(Self);
  564. end;
  565. procedure TXCollection.Delete(Index: integer);
  566. begin
  567. Assert(Cardinal(index) < Cardinal(FList.Count));
  568. // doin' it the fast way
  569. with TXCollectionItem(FList[index]) do
  570. begin
  571. FOwner := nil;
  572. Free;
  573. end;
  574. FList.Delete(index);
  575. FCount := FList.Count;
  576. end;
  577. procedure TXCollection.Remove(anItem: TXCollectionItem);
  578. var
  579. i: integer;
  580. begin
  581. i := IndexOf(anItem);
  582. if i >= 0 then
  583. Delete(i);
  584. end;
  585. procedure TXCollection.Clear;
  586. var
  587. i: integer;
  588. begin
  589. // Fast kill of owned XCollection
  590. for i := 0 to FList.Count - 1 do
  591. with TXCollectionItem(FList[i]) do
  592. begin
  593. FOwner := nil;
  594. Free;
  595. end;
  596. FList.Clear;
  597. FCount := 0;
  598. end;
  599. function TXCollection.IndexOf(anItem: TXCollectionItem): integer;
  600. begin
  601. Result := FList.IndexOf(anItem);
  602. end;
  603. function TXCollection.IndexOfClass(aClass: TXCollectionItemClass): integer;
  604. var
  605. i: integer;
  606. begin
  607. Result := -1;
  608. for i := 0 to FList.Count - 1 do
  609. if TXCollectionItem(FList[i]) is aClass then
  610. begin
  611. Result := i;
  612. Break;
  613. end;
  614. end;
  615. function TXCollection.GetByClass(aClass: TXCollectionItemClass): TXCollectionItem;
  616. var
  617. i: integer;
  618. begin
  619. Result := nil;
  620. for i := 0 to FList.Count - 1 do
  621. if TXCollectionItem(FList[i]) is aClass then
  622. begin
  623. Result := TXCollectionItem(FList[i]);
  624. Break;
  625. end;
  626. end;
  627. function TXCollection.IndexOfName(const aName: string): integer;
  628. var
  629. i: integer;
  630. begin
  631. Result := -1;
  632. for i := 0 to FList.Count - 1 do
  633. if TXCollectionItem(FList[i]).Name = aName then
  634. begin
  635. Result := i;
  636. Break;
  637. end;
  638. end;
  639. function TXCollection.CanAdd(aClass: TXCollectionItemClass): Boolean;
  640. var
  641. i: integer;
  642. XCollectionItemClass: TXCollectionItemClass;
  643. begin
  644. Result := True;
  645. // Test if the class allows itself to be added to this collection
  646. if not aClass.CanAddTo(Self) then
  647. begin
  648. Result := False;
  649. Exit;
  650. end;
  651. // is the given class compatible with owned ones ?
  652. if aClass.UniqueItem then
  653. for i := 0 to Count - 1 do
  654. begin
  655. if Items[i] is aClass then
  656. begin
  657. Result := False;
  658. Break;
  659. end;
  660. end;
  661. // are the owned classes compatible with the given one ?
  662. if Result then
  663. for i := 0 to Count - 1 do
  664. begin
  665. XCollectionItemClass := TXCollectionItemClass(Items[i].ClassType);
  666. if (XCollectionItemClass.UniqueItem) and
  667. aClass.InheritsFrom(XCollectionItemClass) then
  668. begin
  669. Result := False;
  670. Break;
  671. end;
  672. end;
  673. end;
  674. initialization // ------------------------------------------------------------
  675. finalization
  676. vXCollectionItemClasses.Free;
  677. end.