classes.inc 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196
  1. {
  2. $Id$
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {**********************************************************************
  12. * Class implementations are in separate files. *
  13. **********************************************************************}
  14. var
  15. ClassList : TThreadlist;
  16. ClassAliasList : TStringList;
  17. {
  18. Include all message strings
  19. Add a language with IFDEF LANG_NAME
  20. just befor the final ELSE. This way English will always be the default.
  21. }
  22. {$IFDEF LANG_GERMAN}
  23. {$i constsg.inc}
  24. {$ELSE}
  25. {$IFDEF LANG_SPANISH}
  26. {$i constss.inc}
  27. {$ELSE}
  28. {$i constse.inc}
  29. {$ENDIF}
  30. {$ENDIF}
  31. { Utility routines }
  32. {$i util.inc}
  33. { TBits implementation }
  34. {$i bits.inc}
  35. { All streams implementations: }
  36. { Tstreams THandleStream TFileStream TResourcseStreams TStringStream }
  37. { TCustomMemoryStream TMemoryStream }
  38. {$i streams.inc}
  39. { TParser implementation}
  40. {$i parser.inc}
  41. { TCollection and TCollectionItem implementations }
  42. {$i collect.inc}
  43. { TList and TThreadList implementations }
  44. {$i lists.inc}
  45. { TStrings and TStringList implementations }
  46. {$i stringl.inc}
  47. { TThread implementation }
  48. {$i thread.inc}
  49. { TPersistent implementation }
  50. {$i persist.inc }
  51. { TComponent implementation }
  52. {$i compon.inc}
  53. { Class and component registration routines }
  54. {$I cregist.inc}
  55. {**********************************************************************
  56. * Miscellaneous procedures and functions *
  57. **********************************************************************}
  58. { Point and rectangle constructors }
  59. function Point(AX, AY: Integer): TPoint;
  60. begin
  61. with Result do
  62. begin
  63. X := AX;
  64. Y := AY;
  65. end;
  66. end;
  67. function SmallPoint(AX, AY: SmallInt): TSmallPoint;
  68. begin
  69. with Result do
  70. begin
  71. X := AX;
  72. Y := AY;
  73. end;
  74. end;
  75. function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect;
  76. begin
  77. with Result do
  78. begin
  79. Left := ALeft;
  80. Top := ATop;
  81. Right := ARight;
  82. Bottom := ABottom;
  83. end;
  84. end;
  85. function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect;
  86. begin
  87. with Result do
  88. begin
  89. Left := ALeft;
  90. Top := ATop;
  91. Right := ALeft + AWidth;
  92. Bottom := ATop + AHeight;
  93. end;
  94. end;
  95. { Object filing routines }
  96. var
  97. IntConstList: TThreadList;
  98. type
  99. TIntConst = class
  100. IntegerType: PTypeInfo; // The integer type RTTI pointer
  101. IdentToIntFn: TIdentToInt; // Identifier to Integer conversion
  102. IntToIdentFn: TIntToIdent; // Integer to Identifier conversion
  103. constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
  104. AIntToIdent: TIntToIdent);
  105. end;
  106. constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
  107. AIntToIdent: TIntToIdent);
  108. begin
  109. IntegerType := AIntegerType;
  110. IdentToIntFn := AIdentToInt;
  111. IntToIdentFn := AIntToIdent;
  112. end;
  113. procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToIntFn: TIdentToInt;
  114. IntToIdentFn: TIntToIdent);
  115. begin
  116. IntConstList.Add(TIntConst.Create(IntegerType, IdentToIntFn, IntToIdentFn));
  117. end;
  118. function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
  119. var
  120. i: Integer;
  121. begin
  122. with IntConstList.LockList do
  123. try
  124. for i := 0 to Count - 1 do
  125. if TIntConst(Items[i]).IntegerType = AIntegerType then
  126. exit(TIntConst(Items[i]).IntToIdentFn);
  127. Result := nil;
  128. finally
  129. IntConstList.UnlockList;
  130. end;
  131. end;
  132. function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
  133. var
  134. i: Integer;
  135. begin
  136. with IntConstList.LockList do
  137. try
  138. for i := 0 to Count - 1 do
  139. with TIntConst(Items[I]) do
  140. if TIntConst(Items[I]).IntegerType = AIntegerType then
  141. exit(IdentToIntFn);
  142. Result := nil;
  143. finally
  144. IntConstList.UnlockList;
  145. end;
  146. end;
  147. function IdentToInt(const Ident: String; var Int: LongInt;
  148. const Map: array of TIdentMapEntry): Boolean;
  149. var
  150. i: Integer;
  151. begin
  152. for i := Low(Map) to High(Map) do
  153. if CompareText(Map[i].Name, Ident) = 0 then
  154. begin
  155. Int := Map[i].Value;
  156. exit(True);
  157. end;
  158. Result := False;
  159. end;
  160. function IntToIdent(Int: LongInt; var Ident: String;
  161. const Map: array of TIdentMapEntry): Boolean;
  162. var
  163. i: Integer;
  164. begin
  165. for i := Low(Map) to High(Map) do
  166. if Map[i].Value = Int then
  167. begin
  168. Ident := Map[i].Name;
  169. exit(True);
  170. end;
  171. Result := False;
  172. end;
  173. { TPropFixup }
  174. type
  175. TPropFixup = class
  176. FInstance: TPersistent;
  177. FInstanceRoot: TComponent;
  178. FPropInfo: PPropInfo;
  179. FRootName: string;
  180. FName: string;
  181. constructor Create(AInstance: TPersistent; AInstanceRoot: TComponent;
  182. APropInfo: PPropInfo; const ARootName, AName: String);
  183. function MakeGlobalReference: Boolean;
  184. end;
  185. var
  186. GlobalFixupList: TThreadList;
  187. constructor TPropFixup.Create(AInstance: TPersistent; AInstanceRoot: TComponent;
  188. APropInfo: PPropInfo; const ARootName, AName: String);
  189. begin
  190. FInstance := AInstance;
  191. FInstanceRoot := AInstanceRoot;
  192. FPropInfo := APropInfo;
  193. FRootName := ARootName;
  194. FName := AName;
  195. end;
  196. function TPropFixup.MakeGlobalReference: Boolean;
  197. var
  198. i: Integer;
  199. s, p: PChar;
  200. begin
  201. i := Pos('.', FName);
  202. if i = 0 then
  203. exit(False);
  204. FRootName := Copy(FName, 1, i - 1);
  205. FName := Copy(FName, i + 1, Length(FName));
  206. Result := True;
  207. end;
  208. function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
  209. function DoInitClass(ClassType: TClass): Boolean;
  210. begin
  211. Result := False;
  212. if (ClassType <> TComponent) and (ClassType <> RootAncestor) then
  213. begin
  214. { Init the parent class first }
  215. Result := DoInitClass(ClassType.ClassParent);
  216. { !!!: This would work only on Win32, how should we do this multiplatform?
  217. Result := InternalReadComponentRes(ClassType.ClassName,
  218. FindResourceHInstance(FindClassHInstance(ClassType)), Instance)
  219. or Result;}
  220. Result := False;
  221. end;
  222. end;
  223. begin
  224. {!!!: GlobalNameSpace.BeginWrite;
  225. try}
  226. if (Instance.ComponentState * [csLoading, csInline]) = [] then
  227. begin
  228. BeginGlobalLoading;
  229. try
  230. Result := DoInitClass(Instance.ClassType);
  231. NotifyGlobalLoading;
  232. finally
  233. EndGlobalLoading;
  234. end;
  235. end else
  236. Result := DoInitClass(Instance.ClassType);
  237. {finally
  238. GlobalNameSpace.EndWrite;
  239. end;}
  240. end;
  241. function InitComponentRes(const ResName: String; Instance: TComponent): Boolean;
  242. begin
  243. { !!!: Too Win32-specific }
  244. InitComponentRes := False;
  245. end;
  246. function ReadComponentRes(const ResName: String; Instance: TComponent): TComponent;
  247. begin
  248. { !!!: Too Win32-specific }
  249. ReadComponentRes := nil;
  250. end;
  251. function ReadComponentResEx(HInstance: THandle; const ResName: String): TComponent;
  252. begin
  253. { !!!: Too Win32-specific in VCL }
  254. ReadComponentResEx := nil;
  255. end;
  256. function ReadComponentResFile(const FileName: String; Instance: TComponent): TComponent;
  257. var
  258. FileStream: TStream;
  259. begin
  260. FileStream := TFileStream.Create(FileName, fmOpenRead {!!!:or fmShareDenyWrite});
  261. try
  262. Result := FileStream.ReadComponentRes(Instance);
  263. finally
  264. FileStream.Free;
  265. end;
  266. end;
  267. procedure WriteComponentResFile(const FileName: String; Instance: TComponent);
  268. var
  269. FileStream: TStream;
  270. begin
  271. FileStream := TFileStream.Create(FileName, fmCreate);
  272. try
  273. FileStream.WriteComponentRes(Instance.ClassName, Instance);
  274. finally
  275. FileStream.Free;
  276. end;
  277. end;
  278. procedure GlobalFixupReferences;
  279. var
  280. GlobalList, DoneList, ToDoList: TList;
  281. I, Index: Integer;
  282. Root: TComponent;
  283. Instance: TPersistent;
  284. Reference: Pointer;
  285. begin
  286. if not Assigned(FindGlobalComponent) then
  287. exit;
  288. {!!!: GlobalNameSpace.BeginWrite;
  289. try}
  290. GlobalList := GlobalFixupList.LockList;
  291. try
  292. if GlobalList.Count > 0 then
  293. begin
  294. ToDoList := nil;
  295. DoneList := TList.Create;
  296. ToDoList := TList.Create;
  297. try
  298. i := 0;
  299. while i < GlobalList.Count do
  300. with TPropFixup(GlobalList[i]) do
  301. begin
  302. Root := FindGlobalComponent(FRootName);
  303. if Assigned(Root) or (GetOrdProp(FInstance, FPropInfo) <> 0) then
  304. begin
  305. if Assigned(Root) then
  306. begin
  307. Reference := FindNestedComponent(Root, FName);
  308. SetOrdProp(FInstance, FPropInfo, Longint(Reference));
  309. end;
  310. // Move component to list of done components, if necessary
  311. if (DoneList.IndexOf(FInstance) < 0) and
  312. (ToDoList.IndexOf(FInstance) >= 0) then
  313. DoneList.Add(FInstance);
  314. GlobalList.Delete(i);
  315. Free; // ...the fixup
  316. end else
  317. begin
  318. // Move component to list of components to process, if necessary
  319. Index := DoneList.IndexOf(FInstance);
  320. if Index <> -1 then
  321. DoneList.Delete(Index);
  322. if ToDoList.IndexOf(FInstance) < 0 then
  323. ToDoList.Add(FInstance);
  324. Inc(i);
  325. end;
  326. end;
  327. for i := 0 to DoneList.Count - 1 do
  328. begin
  329. Instance := TPersistent(DoneList[I]);
  330. if Instance.InheritsFrom(TComponent) then
  331. Exclude(TComponent(Instance).FComponentState, csFixups);
  332. end;
  333. finally
  334. ToDoList.Free;
  335. DoneList.Free;
  336. end;
  337. end;
  338. finally
  339. GlobalFixupList.UnlockList;
  340. end;
  341. {finally
  342. GlobalNameSpace.EndWrite;
  343. end;}
  344. end;
  345. function IsStringInList(const AString: String; AList: TStrings): Boolean;
  346. var
  347. i: Integer;
  348. begin
  349. for i := 0 to AList.Count - 1 do
  350. if CompareText(AList[i], AString) = 0 then
  351. exit(True);
  352. Result := False;
  353. end;
  354. procedure GetFixupReferenceNames(Root: TComponent; Names: TStrings);
  355. var
  356. i: Integer;
  357. CurFixup: TPropFixup;
  358. begin
  359. with GlobalFixupList.LockList do
  360. try
  361. for i := 0 to Count - 1 do
  362. begin
  363. CurFixup := TPropFixup(Items[i]);
  364. if ((not Assigned(Root)) or (CurFixup.FInstanceRoot = Root)) and
  365. not IsStringInList(CurFixup.FRootName, Names) then
  366. Names.Add(CurFixup.FRootName);
  367. end;
  368. finally
  369. GlobalFixupList.UnlockList;
  370. end;
  371. end;
  372. procedure GetFixupInstanceNames(Root: TComponent;
  373. const ReferenceRootName: string; Names: TStrings);
  374. var
  375. i: Integer;
  376. CurFixup: TPropFixup;
  377. begin
  378. with GlobalFixupList.LockList do
  379. try
  380. for i := 0 to Count - 1 do
  381. begin
  382. CurFixup := TPropFixup(Items[i]);
  383. if (CurFixup.FInstanceRoot = Root) and
  384. (UpperCase(ReferenceRootName) = UpperCase(CurFixup.FRootName)) and
  385. not IsStringInList(CurFixup.FName, Names) then
  386. Names.Add(CurFixup.FName);
  387. end;
  388. finally
  389. GlobalFixupList.UnlockList;
  390. end;
  391. end;
  392. procedure RedirectFixupReferences(Root: TComponent; const OldRootName,
  393. NewRootName: string);
  394. var
  395. i: Integer;
  396. CurFixup: TPropFixup;
  397. begin
  398. with GlobalFixupList.LockList do
  399. try
  400. for i := 0 to Count - 1 do
  401. begin
  402. CurFixup := TPropFixup(Items[i]);
  403. if ((not Assigned(Root)) or (CurFixup.FInstanceRoot = Root)) and
  404. (UpperCase(OldRootName) = UpperCase(CurFixup.FRootName)) then
  405. CurFixup.FRootName := NewRootName;
  406. end;
  407. GlobalFixupReferences;
  408. finally
  409. GlobalFixupList.Unlocklist;
  410. end;
  411. end;
  412. procedure RemoveFixupReferences(Root: TComponent; const RootName: string);
  413. var
  414. i: Integer;
  415. CurFixup: TPropFixup;
  416. begin
  417. if not Assigned(GlobalFixupList) then
  418. exit;
  419. with GlobalFixupList.LockList do
  420. try
  421. for i := Count - 1 downto 0 do
  422. begin
  423. CurFixup := TPropFixup(Items[i]);
  424. if ((not Assigned(Root)) or (CurFixup.FInstanceRoot = Root)) and
  425. ((Length(RootName) = 0) or
  426. (UpperCase(RootName) = UpperCase(CurFixup.FRootName))) then
  427. begin
  428. Delete(i);
  429. CurFixup.Free;
  430. end;
  431. end;
  432. finally
  433. GlobalFixupList.UnlockList;
  434. end;
  435. end;
  436. procedure RemoveFixups(Instance: TPersistent);
  437. var
  438. i: Integer;
  439. CurFixup: TPropFixup;
  440. begin
  441. if not Assigned(GlobalFixupList) then
  442. exit;
  443. with GlobalFixupList.LockList do
  444. try
  445. for i := Count - 1 downto 0 do
  446. begin
  447. CurFixup := TPropFixup(Items[i]);
  448. if (CurFixup.FInstance = Instance) then
  449. begin
  450. Delete(i);
  451. CurFixup.Free;
  452. end;
  453. end;
  454. finally
  455. GlobalFixupList.UnlockList;
  456. end;
  457. end;
  458. function FindNestedComponent(Root: TComponent; const NamePath: string): TComponent;
  459. var
  460. Current, Found: TComponent;
  461. s, p: PChar;
  462. Name: String;
  463. begin
  464. Result := nil;
  465. if Length(NamePath) > 0 then
  466. begin
  467. Current := Root;
  468. p := PChar(NamePath);
  469. while p[0] <> #0 do
  470. begin
  471. s := p;
  472. while not (p^ in ['.', '-', #0]) do
  473. Inc(p);
  474. SetString(Name, s, p - s);
  475. Found := Current.FindComponent(Name);
  476. if (not Assigned(Found)) and (UpperCase(Name) = 'OWNER') then
  477. Found := Current;
  478. if not Assigned(Found) then exit;
  479. // Remove the dereference operator from the name
  480. if p[0] = '.' then
  481. Inc(P);
  482. if p[0] = '-' then
  483. Inc(P);
  484. if p[0] = '>' then
  485. Inc(P);
  486. Current := Found;
  487. end;
  488. end;
  489. Result := Current;
  490. end;
  491. {!!!: Should be threadvar - doesn't work for all platforms yet!}
  492. var
  493. GlobalLoaded, GlobalLists: TList;
  494. procedure BeginGlobalLoading;
  495. begin
  496. if not Assigned(GlobalLists) then
  497. GlobalLists := TList.Create;
  498. GlobalLists.Add(GlobalLoaded);
  499. GlobalLoaded := TList.Create;
  500. end;
  501. { Notify all global components that they have been loaded completely }
  502. procedure NotifyGlobalLoading;
  503. var
  504. i: Integer;
  505. begin
  506. for i := 0 to GlobalLoaded.Count - 1 do
  507. TComponent(GlobalLoaded[i]).Loaded;
  508. end;
  509. procedure EndGlobalLoading;
  510. begin
  511. { Free the memory occupied by BeginGlobalLoading }
  512. GlobalLoaded.Free;
  513. GlobalLoaded := TList(GlobalLists.Last);
  514. GlobalLists.Delete(GlobalLists.Count - 1);
  515. if GlobalLists.Count = 0 then
  516. begin
  517. GlobalLists.Free;
  518. GlobalLists := nil;
  519. end;
  520. end;
  521. function CollectionsEqual(C1, C2: TCollection): Boolean;
  522. begin
  523. // !!!: Implement this
  524. CollectionsEqual:=false;
  525. end;
  526. { Object conversion routines }
  527. procedure ObjectBinaryToText(Input, Output: TStream);
  528. procedure OutStr(s: String);
  529. begin
  530. if Length(s) > 0 then
  531. Output.Write(s[1], Length(s));
  532. end;
  533. procedure OutLn(s: String);
  534. begin
  535. OutStr(s + #10);
  536. end;
  537. procedure OutString(s: String);
  538. var
  539. res, NewStr: String;
  540. i: Integer;
  541. InString, NewInString: Boolean;
  542. begin
  543. res := '';
  544. InString := False;
  545. for i := 1 to Length(s) do begin
  546. NewInString := InString;
  547. case s[i] of
  548. #0..#31: begin
  549. if InString then
  550. NewInString := False;
  551. NewStr := '#' + IntToStr(Ord(s[i]));
  552. end;
  553. '''':
  554. if InString then NewStr := ''''''
  555. else NewStr := '''''''';
  556. else begin
  557. if not InString then
  558. NewInString := True;
  559. NewStr := s[i];
  560. end;
  561. end;
  562. if NewInString <> InString then begin
  563. NewStr := '''' + NewStr;
  564. InString := NewInString;
  565. end;
  566. res := res + NewStr;
  567. end;
  568. if InString then res := res + '''';
  569. OutStr(res);
  570. end;
  571. function ReadInt(ValueType: TValueType): LongInt;
  572. begin
  573. case ValueType of
  574. vaInt8: Result := ShortInt(Input.ReadByte);
  575. vaInt16: Result := SmallInt(Input.ReadWord);
  576. vaInt32: Result := LongInt(Input.ReadDWord);
  577. end;
  578. end;
  579. function ReadInt: LongInt;
  580. begin
  581. Result := ReadInt(TValueType(Input.ReadByte));
  582. end;
  583. function ReadSStr: String;
  584. var
  585. len: Byte;
  586. begin
  587. len := Input.ReadByte;
  588. SetLength(Result, len);
  589. Input.Read(Result[1], len);
  590. end;
  591. procedure ReadPropList(indent: String);
  592. procedure ProcessValue(ValueType: TValueType; Indent: String);
  593. procedure Stop(s: String);
  594. begin
  595. WriteLn(s);
  596. Halt;
  597. end;
  598. procedure ProcessBinary;
  599. var
  600. ToDo, DoNow, i: LongInt;
  601. lbuf: array[0..31] of Byte;
  602. s: String;
  603. begin
  604. ToDo := Input.ReadDWord;
  605. OutLn('{');
  606. while ToDo > 0 do begin
  607. DoNow := ToDo;
  608. if DoNow > 32 then DoNow := 32;
  609. Dec(ToDo, DoNow);
  610. s := Indent + ' ';
  611. Input.Read(lbuf, DoNow);
  612. for i := 0 to DoNow - 1 do
  613. s := s + IntToHex(lbuf[i], 2);
  614. OutLn(s);
  615. end;
  616. OutLn(indent + '}');
  617. end;
  618. var
  619. s: String;
  620. len: LongInt;
  621. IsFirst: Boolean;
  622. ext: Extended;
  623. begin
  624. OutStr('(' + IntToStr(Ord(Valuetype)) + ') ');
  625. case ValueType of
  626. vaList: begin
  627. OutStr('(');
  628. IsFirst := True;
  629. while True do begin
  630. ValueType := TValueType(Input.ReadByte);
  631. if ValueType = vaNull then break;
  632. if IsFirst then begin
  633. OutLn('');
  634. IsFirst := False;
  635. end;
  636. OutStr(Indent + ' ');
  637. ProcessValue(ValueType, Indent + ' ');
  638. end;
  639. OutLn(Indent + ')');
  640. end;
  641. vaInt8: OutLn(IntToStr(ShortInt(Input.ReadByte)));
  642. vaInt16: OutLn( IntToStr(SmallInt(Input.ReadWord)));
  643. vaInt32: OutLn(IntToStr(LongInt(Input.ReadDWord)));
  644. vaExtended: begin
  645. Input.Read(ext, SizeOf(ext));
  646. OutLn(FloatToStr(ext));
  647. end;
  648. vaString: begin
  649. OutString(ReadSStr);
  650. OutLn('');
  651. end;
  652. vaIdent: OutLn(ReadSStr);
  653. vaFalse: OutLn('False');
  654. vaTrue: OutLn('True');
  655. vaBinary: ProcessBinary;
  656. vaSet: begin
  657. OutStr('[');
  658. IsFirst := True;
  659. while True do begin
  660. s := ReadSStr;
  661. if Length(s) = 0 then break;
  662. if not IsFirst then OutStr(', ');
  663. IsFirst := False;
  664. OutStr(s);
  665. end;
  666. OutLn(']');
  667. end;
  668. vaLString: Stop('!!LString!!');
  669. vaNil: Stop('nil');
  670. vaCollection: begin
  671. OutStr('<');
  672. while Input.ReadByte <> 0 do begin
  673. OutLn(Indent);
  674. Input.Seek(-1, soFromCurrent);
  675. OutStr(indent + ' item');
  676. ValueType := TValueType(Input.ReadByte);
  677. if ValueType <> vaList then
  678. OutStr('[' + IntToStr(ReadInt(ValueType)) + ']');
  679. OutLn('');
  680. ReadPropList(indent + ' ');
  681. OutStr(indent + ' end');
  682. end;
  683. OutLn('>');
  684. end;
  685. {vaSingle: begin OutLn('!!Single!!'); exit end;
  686. vaCurrency: begin OutLn('!!Currency!!'); exit end;
  687. vaDate: begin OutLn('!!Date!!'); exit end;
  688. vaWString: begin OutLn('!!WString!!'); exit end;}
  689. else
  690. Stop(IntToStr(Ord(ValueType)));
  691. end;
  692. end;
  693. begin
  694. while Input.ReadByte <> 0 do begin
  695. Input.Seek(-1, soFromCurrent);
  696. OutStr(indent + ReadSStr + ' = ');
  697. ProcessValue(TValueType(Input.ReadByte), Indent);
  698. end;
  699. end;
  700. procedure ReadObject(indent: String);
  701. var
  702. b: Byte;
  703. ObjClassName, ObjName: String;
  704. ChildPos: LongInt;
  705. begin
  706. // Check for FilerFlags
  707. b := Input.ReadByte;
  708. if (b and $f0) = $f0 then begin
  709. if (b and 2) <> 0 then ChildPos := ReadInt;
  710. end else begin
  711. b := 0;
  712. Input.Seek(-1, soFromCurrent);
  713. end;
  714. ObjClassName := ReadSStr;
  715. ObjName := ReadSStr;
  716. OutStr(Indent);
  717. if (b and 1) <> 0 then OutStr('inherited')
  718. else OutStr('object');
  719. OutStr(' ');
  720. if ObjName <> '' then
  721. OutStr(ObjName + ': ');
  722. OutStr(ObjClassName);
  723. if (b and 2) <> 0 then OutStr('[' + IntToStr(ChildPos) + ']');
  724. OutLn('');
  725. ReadPropList(indent + ' ');
  726. while Input.ReadByte <> 0 do begin
  727. Input.Seek(-1, soFromCurrent);
  728. ReadObject(indent + ' ');
  729. end;
  730. OutLn(indent + 'end');
  731. end;
  732. type
  733. PLongWord = ^LongWord;
  734. const
  735. signature: PChar = 'TPF0';
  736. begin
  737. if Input.ReadDWord <> PLongWord(Pointer(signature))^ then
  738. raise EReadError.Create('Illegal stream image' {###SInvalidImage});
  739. ReadObject('');
  740. end;
  741. procedure ObjectTextToBinary(Input, Output: TStream);
  742. var
  743. parser: TParser;
  744. procedure WriteString(s: String);
  745. begin
  746. Output.WriteByte(Length(s));
  747. Output.Write(s[1], Length(s));
  748. end;
  749. procedure WriteInteger(value: LongInt);
  750. begin
  751. if (value >= -128) and (value <= 127) then begin
  752. Output.WriteByte(Ord(vaInt8));
  753. Output.WriteByte(Byte(value));
  754. end else if (value >= -32768) and (value <= 32767) then begin
  755. Output.WriteByte(Ord(vaInt16));
  756. Output.WriteWord(Word(value));
  757. end else begin
  758. Output.WriteByte(ord(vaInt32));
  759. Output.WriteDWord(LongWord(value));
  760. end;
  761. end;
  762. procedure ProcessProperty; forward;
  763. procedure ProcessValue;
  764. var
  765. flt: Extended;
  766. s: String;
  767. stream: TMemoryStream;
  768. begin
  769. case parser.Token of
  770. toInteger:
  771. begin
  772. WriteInteger(parser.TokenInt);
  773. parser.NextToken;
  774. end;
  775. toFloat:
  776. begin
  777. Output.WriteByte(Ord(vaExtended));
  778. flt := Parser.TokenFloat;
  779. Output.Write(flt, SizeOf(flt));
  780. parser.NextToken;
  781. end;
  782. toString:
  783. begin
  784. s := parser.TokenString;
  785. while parser.NextToken = '+' do
  786. begin
  787. parser.NextToken; // Get next string fragment
  788. parser.CheckToken(toString);
  789. s := s + parser.TokenString;
  790. end;
  791. Output.WriteByte(Ord(vaString));
  792. WriteString(s);
  793. end;
  794. toSymbol:
  795. begin
  796. if CompareText(parser.TokenString, 'True') = 0 then
  797. Output.WriteByte(Ord(vaTrue))
  798. else if CompareText(parser.TokenString, 'False') = 0 then
  799. Output.WriteByte(Ord(vaFalse))
  800. else if CompareText(parser.TokenString, 'nil') = 0 then
  801. Output.WriteByte(Ord(vaNil))
  802. else
  803. begin
  804. Output.WriteByte(Ord(vaIdent));
  805. WriteString(parser.TokenString);
  806. end;
  807. Parser.NextToken;
  808. end;
  809. // Set
  810. '[':
  811. begin
  812. parser.NextToken;
  813. Output.WriteByte(Ord(vaSet));
  814. if parser.Token <> ']' then
  815. while True do
  816. begin
  817. parser.CheckToken(toSymbol);
  818. WriteString(parser.TokenString);
  819. parser.NextToken;
  820. if parser.Token = ']' then
  821. break;
  822. parser.CheckToken(',');
  823. parser.NextToken;
  824. end;
  825. Output.WriteByte(0);
  826. parser.NextToken;
  827. end;
  828. // List
  829. '(':
  830. begin
  831. parser.NextToken;
  832. Output.WriteByte(Ord(vaList));
  833. while parser.Token <> ')' do
  834. ProcessValue;
  835. Output.WriteByte(0);
  836. parser.NextToken;
  837. end;
  838. // Collection
  839. '<':
  840. begin
  841. parser.NextToken;
  842. Output.WriteByte(Ord(vaCollection));
  843. while parser.Token <> '>' do
  844. begin
  845. parser.CheckTokenSymbol('item');
  846. parser.NextToken;
  847. // ConvertOrder
  848. Output.WriteByte(Ord(vaList));
  849. while not parser.TokenSymbolIs('end') do
  850. ProcessProperty;
  851. parser.NextToken; // Skip 'end'
  852. Output.WriteByte(0);
  853. end;
  854. Output.WriteByte(0);
  855. parser.NextToken;
  856. end;
  857. // Binary data
  858. '{':
  859. begin
  860. Output.WriteByte(Ord(vaBinary));
  861. stream := TMemoryStream.Create;
  862. try
  863. parser.HexToBinary(stream);
  864. Output.WriteDWord(stream.Size);
  865. Output.Write(Stream.Memory^, stream.Size);
  866. finally
  867. stream.Free;
  868. end;
  869. parser.NextToken;
  870. end;
  871. else
  872. parser.Error(SInvalidProperty);
  873. end;
  874. end;
  875. procedure ProcessProperty;
  876. var
  877. name: String;
  878. begin
  879. // Get name of property
  880. parser.CheckToken(toSymbol);
  881. name := parser.TokenString;
  882. while True do begin
  883. parser.NextToken;
  884. if parser.Token <> '.' then break;
  885. parser.NextToken;
  886. parser.CheckToken(toSymbol);
  887. name := name + '.' + parser.TokenString;
  888. end;
  889. WriteString(name);
  890. parser.CheckToken('=');
  891. parser.NextToken;
  892. ProcessValue;
  893. end;
  894. procedure ProcessObject;
  895. var
  896. IsInherited: Boolean;
  897. ObjectName, ObjectType: String;
  898. begin
  899. if parser.TokenSymbolIs('OBJECT') then
  900. IsInherited := False
  901. else begin
  902. parser.CheckTokenSymbol('INHERITED');
  903. IsInherited := True;
  904. end;
  905. parser.NextToken;
  906. parser.CheckToken(toSymbol);
  907. ObjectName := '';
  908. ObjectType := parser.TokenString;
  909. parser.NextToken;
  910. if parser.Token = ':' then begin
  911. parser.NextToken;
  912. parser.CheckToken(toSymbol);
  913. ObjectName := ObjectType;
  914. ObjectType := parser.TokenString;
  915. parser.NextToken;
  916. end;
  917. WriteString(ObjectType);
  918. WriteString(ObjectName);
  919. // Convert property list
  920. while not (parser.TokenSymbolIs('END') or
  921. parser.TokenSymbolIs('OBJECT') or
  922. parser.TokenSymbolIs('INHERITED')) do
  923. ProcessProperty;
  924. Output.WriteByte(0); // Terminate property list
  925. // Convert child objects
  926. while not parser.TokenSymbolIs('END') do ProcessObject;
  927. parser.NextToken; // Skip end token
  928. Output.WriteByte(0); // Terminate property list
  929. end;
  930. const
  931. signature: PChar = 'TPF0';
  932. begin
  933. parser := TParser.Create(Input);
  934. try
  935. Output.Write(signature[0], 4);
  936. ProcessObject;
  937. finally
  938. parser.Free;
  939. end;
  940. end;
  941. procedure ObjectResourceToText(Input, Output: TStream);
  942. begin
  943. Input.ReadResHeader;
  944. ObjectBinaryToText(Input, Output);
  945. end;
  946. procedure ObjectTextToResource(Input, Output: TStream);
  947. var
  948. StartPos, SizeStartPos, BinSize: LongInt;
  949. parser: TParser;
  950. name: String;
  951. begin
  952. // Get form type name
  953. StartPos := Input.Position;
  954. parser := TParser.Create(Input);
  955. try
  956. if not parser.TokenSymbolIs('OBJECT') then parser.CheckTokenSymbol('INHERITED');
  957. parser.NextToken;
  958. parser.CheckToken(toSymbol);
  959. parser.NextToken;
  960. parser.CheckToken(':');
  961. parser.NextToken;
  962. parser.CheckToken(toSymbol);
  963. name := parser.TokenString;
  964. finally
  965. parser.Free;
  966. Input.Position := StartPos;
  967. end;
  968. // Write resource header
  969. name := UpperCase(name);
  970. Output.WriteByte($ff);
  971. Output.WriteByte(10);
  972. Output.WriteByte(0);
  973. Output.Write(name[1], Length(name) + 1); // Write null-terminated form type name
  974. Output.WriteWord($1030);
  975. SizeStartPos := Output.Position;
  976. Output.WriteDWord(0); // Placeholder for data size
  977. ObjectTextToBinary(Input, Output); // Convert the stuff!
  978. BinSize := Output.Position - SizeStartPos - 4;
  979. Output.Position := SizeStartPos;
  980. Output.WriteDWord(BinSize); // Insert real resource data size
  981. end;
  982. { Utility routines }
  983. function LineStart(Buffer, BufPos: PChar): PChar;
  984. begin
  985. Result := BufPos;
  986. while Result > Buffer do begin
  987. Dec(Result);
  988. if Result[0] = #10 then break;
  989. end;
  990. end;
  991. procedure CommonInit;
  992. begin
  993. IntConstList := TThreadList.Create;
  994. GlobalFixupList := TThreadList.Create;
  995. ClassList := TThreadList.Create;
  996. ClassAliasList := TStringList.Create;
  997. end;
  998. procedure CommonCleanup;
  999. var
  1000. i: Integer;
  1001. begin
  1002. // !!!: GlobalNameSpace.BeginWrite;
  1003. with IntConstList.LockList do
  1004. try
  1005. for i := 0 to Count - 1 do
  1006. TIntConst(Items[I]).Free;
  1007. finally
  1008. IntConstList.UnlockList;
  1009. end;
  1010. IntConstList.Free;
  1011. ClassList.Free;
  1012. ClassAliasList.Free;
  1013. RemoveFixupReferences(nil, '');
  1014. GlobalFixupList.Free;
  1015. GlobalFixupList := nil;
  1016. GlobalLists.Free;
  1017. {!!!: GlobalNameSpace.Free;
  1018. GlobalNameSpace := nil;}
  1019. end;
  1020. { TFiler implementation }
  1021. {$i filer.inc}
  1022. { TReader implementation }
  1023. {$i reader.inc}
  1024. { TWriter implementations }
  1025. {$i writer.inc}
  1026. {$i twriter.inc}
  1027. {
  1028. $Log$
  1029. Revision 1.5 2000-10-15 09:27:48 peter
  1030. + Added some index checking. Centralized error handling (merged)
  1031. Revision 1.4 2000/10/13 12:33:23 sg
  1032. * Some small cosmetic changes and minor fixes
  1033. Revision 1.3 2000/07/22 14:55:56 sg
  1034. * Fixed some DFM parser bugs
  1035. Revision 1.2 2000/07/13 11:32:59 michael
  1036. + removed logs
  1037. }