XCollection.pas 22 KB

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