stringl.inc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {****************************************************************************}
  11. {* TStrings *}
  12. {****************************************************************************}
  13. // Function to quote text. Should move maybe to sysutils !!
  14. // Also, it is not clear at this point what exactly should be done.
  15. { //!! is used to mark unsupported things. }
  16. Function QuoteString (Const S : String; Quote : String) : String;
  17. Var
  18. I,J : Integer;
  19. begin
  20. J:=0;
  21. Result:=S;
  22. for i:=1to length(s) do
  23. begin
  24. inc(j);
  25. if S[i]=Quote then
  26. begin
  27. System.Insert(Quote,Result,J);
  28. inc(j);
  29. end;
  30. end;
  31. Result:=Quote+Result+Quote;
  32. end;
  33. {
  34. For compatibility we can't add a Constructor to TSTrings to initialize
  35. the special characters. Therefore we add a routine which is called whenever
  36. the special chars are needed.
  37. }
  38. Procedure Tstrings.CheckSpecialChars;
  39. begin
  40. If Not FSpecialCharsInited then
  41. begin
  42. FQuoteChar:='"';
  43. FDelimiter:=',';
  44. FNameValueSeparator:='=';
  45. FSpecialCharsInited:=true;
  46. FLBS:=DefaultTextLineBreakStyle;
  47. end;
  48. end;
  49. Function TStrings.GetLBS : TTextLineBreakStyle;
  50. begin
  51. CheckSpecialChars;
  52. Result:=FLBS;
  53. end;
  54. Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
  55. begin
  56. CheckSpecialChars;
  57. FLBS:=AValue;
  58. end;
  59. procedure TStrings.SetDelimiter(c:Char);
  60. begin
  61. CheckSpecialChars;
  62. FDelimiter:=c;
  63. end;
  64. procedure TStrings.SetQuoteChar(c:Char);
  65. begin
  66. CheckSpecialChars;
  67. FQuoteChar:=c;
  68. end;
  69. procedure TStrings.SetNameValueSeparator(c:Char);
  70. begin
  71. CheckSpecialChars;
  72. FNameValueSeparator:=c;
  73. end;
  74. function TStrings.GetCommaText: string;
  75. Var
  76. C1,C2 : Char;
  77. FSD : Boolean;
  78. begin
  79. CheckSpecialChars;
  80. FSD:=StrictDelimiter;
  81. C1:=Delimiter;
  82. C2:=QuoteChar;
  83. Delimiter:=',';
  84. QuoteChar:='"';
  85. StrictDelimiter:=False;
  86. Try
  87. Result:=GetDelimitedText;
  88. Finally
  89. Delimiter:=C1;
  90. QuoteChar:=C2;
  91. StrictDelimiter:=FSD;
  92. end;
  93. end;
  94. Function TStrings.GetDelimitedText: string;
  95. Var
  96. I : integer;
  97. p : pchar;
  98. c : set of char;
  99. S : String;
  100. begin
  101. CheckSpecialChars;
  102. result:='';
  103. if StrictDelimiter then
  104. c:=[#0,Delimiter]
  105. else
  106. c:=[#0..' ',QuoteChar,Delimiter];
  107. For i:=0 to count-1 do
  108. begin
  109. S:=Strings[i];
  110. p:=pchar(S);
  111. while not(p^ in c) do
  112. inc(p);
  113. // strings in list may contain #0
  114. if (p<>pchar(S)+length(S)) and not StrictDelimiter then
  115. Result:=Result+QuoteString(S,QuoteChar)
  116. else
  117. Result:=Result+S;
  118. if I<Count-1 then
  119. Result:=Result+Delimiter;
  120. end;
  121. If (Length(Result)=0) and (Count=1) then
  122. Result:=QuoteChar+QuoteChar;
  123. end;
  124. procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
  125. Var L : longint;
  126. begin
  127. CheckSpecialChars;
  128. AValue:=Strings[Index];
  129. L:=Pos(FNameValueSeparator,AValue);
  130. If L<>0 then
  131. begin
  132. AName:=Copy(AValue,1,L-1);
  133. System.Delete(AValue,1,L);
  134. end
  135. else
  136. AName:='';
  137. end;
  138. function TStrings.ExtractName(const s:String):String;
  139. var
  140. L: Longint;
  141. begin
  142. CheckSpecialChars;
  143. L:=Pos(FNameValueSeparator,S);
  144. If L<>0 then
  145. Result:=Copy(S,1,L-1)
  146. else
  147. Result:='';
  148. end;
  149. function TStrings.GetName(Index: Integer): string;
  150. Var
  151. V : String;
  152. begin
  153. GetNameValue(Index,Result,V);
  154. end;
  155. Function TStrings.GetValue(const Name: string): string;
  156. Var
  157. L : longint;
  158. N : String;
  159. begin
  160. Result:='';
  161. L:=IndexOfName(Name);
  162. If L<>-1 then
  163. GetNameValue(L,N,Result);
  164. end;
  165. Function TStrings.GetValueFromIndex(Index: Integer): string;
  166. Var
  167. N : String;
  168. begin
  169. GetNameValue(Index,N,Result);
  170. end;
  171. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  172. begin
  173. If (Value='') then
  174. Delete(Index)
  175. else
  176. begin
  177. If (Index<0) then
  178. Index:=Add('');
  179. CheckSpecialChars;
  180. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  181. end;
  182. end;
  183. procedure TStrings.ReadData(Reader: TReader);
  184. begin
  185. Reader.ReadListBegin;
  186. BeginUpdate;
  187. try
  188. Clear;
  189. while not Reader.EndOfList do
  190. Add(Reader.ReadString);
  191. finally
  192. EndUpdate;
  193. end;
  194. Reader.ReadListEnd;
  195. end;
  196. Procedure TStrings.SetDelimitedText(const AValue: string);
  197. Procedure SetStrictDelimited;
  198. Var
  199. P,PL : Integer;
  200. begin
  201. PL:=1;
  202. P:=1;
  203. {
  204. While Length(S)>0 do
  205. begin
  206. P:=Pos(FDelimiter,S);
  207. If (P=0) then
  208. P:=Length(S)+1;
  209. Add(Copy(S,1,P-1));
  210. System.Delete(S,1,P));
  211. end;
  212. } end;
  213. var i,j:integer;
  214. aNotFirst:boolean;
  215. begin
  216. CheckSpecialChars;
  217. BeginUpdate;
  218. i:=1;
  219. j:=1;
  220. aNotFirst:=false;
  221. try
  222. Clear;
  223. If StrictDelimiter then
  224. begin
  225. // Easier, faster loop.
  226. While I<=Length(AValue) do
  227. begin
  228. If (AValue[I] in [FDelimiter,#0]) then
  229. begin
  230. Add(Copy(AValue,J,I-J));
  231. J:=I+1;
  232. end;
  233. Inc(i);
  234. end;
  235. If (Length(AValue)>0) then
  236. Add(Copy(AValue,J,I-J));
  237. end
  238. else
  239. begin
  240. while i<=length(AValue) do begin
  241. // skip delimiter
  242. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  243. // skip spaces
  244. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  245. // read next string
  246. if i<=length(AValue) then begin
  247. if AValue[i]=FQuoteChar then begin
  248. // next string is quoted
  249. j:=i+1;
  250. while (j<=length(AValue)) and
  251. ( (AValue[j]<>FQuoteChar) or
  252. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  253. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  254. else inc(j);
  255. end;
  256. // j is position of closing quote
  257. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  258. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  259. i:=j+1;
  260. end else begin
  261. // next string is not quoted
  262. j:=i;
  263. while (j<=length(AValue)) and
  264. (Ord(AValue[j])>Ord(' ')) and
  265. (AValue[j]<>FDelimiter) do inc(j);
  266. Add( Copy(AValue,i,j-i));
  267. i:=j;
  268. end;
  269. end else begin
  270. if aNotFirst then Add('');
  271. end;
  272. // skip spaces
  273. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  274. aNotFirst:=true;
  275. end;
  276. end;
  277. finally
  278. EndUpdate;
  279. end;
  280. end;
  281. Procedure TStrings.SetCommaText(const Value: string);
  282. Var
  283. C1,C2 : Char;
  284. begin
  285. CheckSpecialChars;
  286. C1:=Delimiter;
  287. C2:=QuoteChar;
  288. Delimiter:=',';
  289. QuoteChar:='"';
  290. Try
  291. SetDelimitedText(Value);
  292. Finally
  293. Delimiter:=C1;
  294. QuoteChar:=C2;
  295. end;
  296. end;
  297. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  298. begin
  299. end;
  300. Procedure TStrings.SetValue(const Name, Value: string);
  301. Var L : longint;
  302. begin
  303. CheckSpecialChars;
  304. L:=IndexOfName(Name);
  305. if L=-1 then
  306. Add (Name+FNameValueSeparator+Value)
  307. else
  308. Strings[L]:=Name+FNameValueSeparator+value;
  309. end;
  310. procedure TStrings.WriteData(Writer: TWriter);
  311. var
  312. i: Integer;
  313. begin
  314. Writer.WriteListBegin;
  315. for i := 0 to Count - 1 do
  316. Writer.WriteString(Strings[i]);
  317. Writer.WriteListEnd;
  318. end;
  319. procedure TStrings.DefineProperties(Filer: TFiler);
  320. var
  321. HasData: Boolean;
  322. begin
  323. if Assigned(Filer.Ancestor) then
  324. // Only serialize if string list is different from ancestor
  325. if Filer.Ancestor.InheritsFrom(TStrings) then
  326. HasData := not Equals(TStrings(Filer.Ancestor))
  327. else
  328. HasData := True
  329. else
  330. HasData := Count > 0;
  331. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  332. end;
  333. Procedure TStrings.Error(const Msg: string; Data: Integer);
  334. begin
  335. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame);
  336. end;
  337. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  338. begin
  339. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame);
  340. end;
  341. Function TStrings.GetCapacity: Integer;
  342. begin
  343. Result:=Count;
  344. end;
  345. Function TStrings.GetObject(Index: Integer): TObject;
  346. begin
  347. Result:=Nil;
  348. end;
  349. Function TStrings.GetTextStr: string;
  350. Var P : Pchar;
  351. I,L,NLS : Longint;
  352. S,NL : String;
  353. begin
  354. CheckSpecialChars;
  355. // Determine needed place
  356. Case FLBS of
  357. tlbsLF : NL:=#10;
  358. tlbsCRLF : NL:=#13#10;
  359. tlbsCR : NL:=#13;
  360. end;
  361. L:=0;
  362. NLS:=Length(NL);
  363. For I:=0 to count-1 do
  364. L:=L+Length(Strings[I])+NLS;
  365. Setlength(Result,L);
  366. P:=Pointer(Result);
  367. For i:=0 To count-1 do
  368. begin
  369. S:=Strings[I];
  370. L:=Length(S);
  371. if L<>0 then
  372. System.Move(Pointer(S)^,P^,L);
  373. P:=P+L;
  374. For L:=1 to NLS do
  375. begin
  376. P^:=NL[L];
  377. inc(P);
  378. end;
  379. end;
  380. end;
  381. Procedure TStrings.Put(Index: Integer; const S: string);
  382. Var Obj : TObject;
  383. begin
  384. Obj:=Objects[Index];
  385. Delete(Index);
  386. InsertObject(Index,S,Obj);
  387. end;
  388. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  389. begin
  390. // Empty.
  391. end;
  392. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  393. begin
  394. // Empty.
  395. end;
  396. Function GetNextLine (Const Value : String; Var S : String; Var P : Integer) : Boolean;
  397. Var
  398. PS : PChar;
  399. IP,L : Integer;
  400. begin
  401. L:=Length(Value);
  402. S:='';
  403. Result:=False;
  404. If ((L-P)<0) then
  405. exit;
  406. if ((L-P)=0) and (not (value[P] in [#10,#13])) Then
  407. Begin
  408. s:=value[P];
  409. inc(P);
  410. Exit(True);
  411. End;
  412. PS:=PChar(Value)+P-1;
  413. IP:=P;
  414. While ((L-P)>=0) and (not (PS^ in [#10,#13])) do
  415. begin
  416. P:=P+1;
  417. Inc(PS);
  418. end;
  419. SetLength (S,P-IP);
  420. System.Move (Value[IP],Pointer(S)^,P-IP);
  421. If (P<=L) and (Value[P]=#13) then
  422. Inc(P);
  423. If (P<=L) and (Value[P]=#10) then
  424. Inc(P); // Point to character after #10(#13)
  425. Result:=True;
  426. end;
  427. Procedure TStrings.SetTextStr(const Value: string);
  428. Var
  429. S : String;
  430. P : Integer;
  431. begin
  432. Try
  433. beginUpdate;
  434. Clear;
  435. P:=1;
  436. While GetNextLine (Value,S,P) do
  437. Add(S);
  438. finally
  439. EndUpdate;
  440. end;
  441. end;
  442. Procedure TStrings.SetUpdateState(Updating: Boolean);
  443. begin
  444. end;
  445. destructor TSTrings.Destroy;
  446. begin
  447. inherited destroy;
  448. end;
  449. Function TStrings.Add(const S: string): Integer;
  450. begin
  451. Result:=Count;
  452. Insert (Count,S);
  453. end;
  454. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  455. begin
  456. Result:=Add(S);
  457. Objects[result]:=AObject;
  458. end;
  459. Procedure TStrings.Append(const S: string);
  460. begin
  461. Add (S);
  462. end;
  463. Procedure TStrings.AddStrings(TheStrings: TStrings);
  464. Var Runner : longint;
  465. begin
  466. try
  467. beginupdate;
  468. For Runner:=0 to TheStrings.Count-1 do
  469. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  470. finally
  471. EndUpdate;
  472. end;
  473. end;
  474. Procedure TStrings.Assign(Source: TPersistent);
  475. Var
  476. S : TStrings;
  477. begin
  478. If Source is TStrings then
  479. begin
  480. S:=TStrings(Source);
  481. BeginUpdate;
  482. Try
  483. clear;
  484. FSpecialCharsInited:=S.FSpecialCharsInited;
  485. FQuoteChar:=S.FQuoteChar;
  486. FDelimiter:=S.FDelimiter;
  487. FNameValueSeparator:=S.FNameValueSeparator;
  488. FLBS:=S.FLBS;
  489. AddStrings(S);
  490. finally
  491. EndUpdate;
  492. end;
  493. end
  494. else
  495. Inherited Assign(Source);
  496. end;
  497. Procedure TStrings.BeginUpdate;
  498. begin
  499. if FUpdateCount = 0 then SetUpdateState(true);
  500. inc(FUpdateCount);
  501. end;
  502. Procedure TStrings.EndUpdate;
  503. begin
  504. If FUpdateCount>0 then
  505. Dec(FUpdateCount);
  506. if FUpdateCount=0 then
  507. SetUpdateState(False);
  508. end;
  509. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  510. Var Runner,Nr : Longint;
  511. begin
  512. Result:=False;
  513. Nr:=Self.Count;
  514. if Nr<>TheStrings.Count then exit;
  515. For Runner:=0 to Nr-1 do
  516. If Strings[Runner]<>TheStrings[Runner] then exit;
  517. Result:=True;
  518. end;
  519. Procedure TStrings.Exchange(Index1, Index2: Integer);
  520. Var
  521. Obj : TObject;
  522. Str : String;
  523. begin
  524. Try
  525. beginUpdate;
  526. Obj:=Objects[Index1];
  527. Str:=Strings[Index1];
  528. Objects[Index1]:=Objects[Index2];
  529. Strings[Index1]:=Strings[Index2];
  530. Objects[Index2]:=Obj;
  531. Strings[Index2]:=Str;
  532. finally
  533. EndUpdate;
  534. end;
  535. end;
  536. Function TStrings.GetText: PChar;
  537. begin
  538. Result:=StrNew(Pchar(Self.Text));
  539. end;
  540. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  541. begin
  542. result:=CompareText(s1,s2);
  543. end;
  544. Function TStrings.IndexOf(const S: string): Integer;
  545. begin
  546. Result:=0;
  547. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  548. if Result=Count then Result:=-1;
  549. end;
  550. Function TStrings.IndexOfName(const Name: string): Integer;
  551. Var
  552. len : longint;
  553. S : String;
  554. begin
  555. CheckSpecialChars;
  556. Result:=0;
  557. while (Result<Count) do
  558. begin
  559. S:=Strings[Result];
  560. len:=pos(FNameValueSeparator,S)-1;
  561. if (len>0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  562. exit;
  563. inc(result);
  564. end;
  565. result:=-1;
  566. end;
  567. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  568. begin
  569. Result:=0;
  570. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  571. If Result=Count then Result:=-1;
  572. end;
  573. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  574. AObject: TObject);
  575. begin
  576. Insert (Index,S);
  577. Objects[Index]:=AObject;
  578. end;
  579. Procedure TStrings.LoadFromFile(const FileName: string);
  580. Var
  581. TheStream : TFileStream;
  582. begin
  583. TheStream:=TFileStream.Create(FileName,fmOpenRead);
  584. LoadFromStream(TheStream);
  585. TheStream.Free;
  586. end;
  587. Procedure TStrings.LoadFromStream(Stream: TStream);
  588. {
  589. Borlands method is no good, since a pipe for
  590. instance doesn't have a size.
  591. So we must do it the hard way.
  592. }
  593. Const
  594. BufSize = 1024;
  595. MaxGrow = 1 shl 29;
  596. Var
  597. Buffer : AnsiString;
  598. BytesRead,
  599. BufLen,
  600. I,BufDelta : Longint;
  601. begin
  602. // reread into a buffer
  603. try
  604. beginupdate;
  605. Buffer:='';
  606. BufLen:=0;
  607. I:=1;
  608. Repeat
  609. BufDelta:=BufSize*I;
  610. SetLength(Buffer,BufLen+BufDelta);
  611. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  612. inc(BufLen,BufDelta);
  613. If I<MaxGrow then
  614. I:=I shl 1;
  615. Until BytesRead<>BufDelta;
  616. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  617. SetTextStr(Buffer);
  618. SetLength(Buffer,0);
  619. finally
  620. EndUpdate;
  621. end;
  622. end;
  623. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  624. Var
  625. Obj : TObject;
  626. Str : String;
  627. begin
  628. BeginUpdate;
  629. Obj:=Objects[CurIndex];
  630. Str:=Strings[CurIndex];
  631. Delete(Curindex);
  632. InsertObject(NewIndex,Str,Obj);
  633. EndUpdate;
  634. end;
  635. Procedure TStrings.SaveToFile(const FileName: string);
  636. Var TheStream : TFileStream;
  637. begin
  638. TheStream:=TFileStream.Create(FileName,fmCreate);
  639. SaveToStream(TheStream);
  640. TheStream.Free;
  641. end;
  642. Procedure TStrings.SaveToStream(Stream: TStream);
  643. Var
  644. S : String;
  645. begin
  646. S:=Text;
  647. Stream.WriteBuffer(Pointer(S)^,Length(S));
  648. end;
  649. Procedure TStrings.SetText(TheText: PChar);
  650. Var S : String;
  651. begin
  652. If TheText<>Nil then
  653. S:=StrPas(TheText)
  654. else
  655. S:='';
  656. SetTextStr(S);
  657. end;
  658. {****************************************************************************}
  659. {* TStringList *}
  660. {****************************************************************************}
  661. {$if defined(VER2_0) or not defined(FPC_TESTGENERICS)}
  662. Procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  663. Var P1,P2 : Pointer;
  664. begin
  665. P1:=Pointer(Flist^[Index1].FString);
  666. P2:=Pointer(Flist^[Index1].FObject);
  667. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  668. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  669. Pointer(Flist^[Index2].Fstring):=P1;
  670. Pointer(Flist^[Index2].FObject):=P2;
  671. end;
  672. Procedure TStringList.Grow;
  673. Var
  674. NC : Integer;
  675. begin
  676. NC:=FCapacity;
  677. If NC>=256 then
  678. NC:=NC+(NC Div 4)
  679. else if NC=0 then
  680. NC:=4
  681. else
  682. NC:=NC*4;
  683. SetCapacity(NC);
  684. end;
  685. Procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  686. var
  687. Pivot, vL, vR: Integer;
  688. begin
  689. if R - L <= 1 then begin // a little bit of time saver
  690. if L < R then
  691. if CompareFn(Self, L, R) > 0 then
  692. ExchangeItems(L, R);
  693. Exit;
  694. end;
  695. vL := L;
  696. vR := R;
  697. Pivot := L + Random(R - L); // they say random is best
  698. while vL < vR do begin
  699. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  700. Inc(vL);
  701. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  702. Dec(vR);
  703. ExchangeItems(vL, vR);
  704. if Pivot = vL then // swap pivot if we just hit it from one side
  705. Pivot := vR
  706. else if Pivot = vR then
  707. Pivot := vL;
  708. end;
  709. if Pivot - 1 >= L then
  710. QuickSort(L, Pivot - 1, CompareFn);
  711. if Pivot + 1 <= R then
  712. QuickSort(Pivot + 1, R, CompareFn);
  713. end;
  714. Procedure TStringList.InsertItem(Index: Integer; const S: string);
  715. begin
  716. Changing;
  717. If FCount=Fcapacity then Grow;
  718. If Index<FCount then
  719. System.Move (FList^[Index],FList^[Index+1],
  720. (FCount-Index)*SizeOf(TStringItem));
  721. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  722. Flist^[Index].FString:=S;
  723. Flist^[Index].Fobject:=Nil;
  724. Inc(FCount);
  725. Changed;
  726. end;
  727. Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  728. begin
  729. Changing;
  730. If FCount=Fcapacity then Grow;
  731. If Index<FCount then
  732. System.Move (FList^[Index],FList^[Index+1],
  733. (FCount-Index)*SizeOf(TStringItem));
  734. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  735. Flist^[Index].FString:=S;
  736. Flist^[Index].FObject:=O;
  737. Inc(FCount);
  738. Changed;
  739. end;
  740. Procedure TStringList.SetSorted(Value: Boolean);
  741. begin
  742. If FSorted<>Value then
  743. begin
  744. If Value then sort;
  745. FSorted:=VAlue
  746. end;
  747. end;
  748. Procedure TStringList.Changed;
  749. begin
  750. If (FUpdateCount=0) Then
  751. If Assigned(FOnChange) then
  752. FOnchange(Self);
  753. end;
  754. Procedure TStringList.Changing;
  755. begin
  756. If FUpdateCount=0 then
  757. if Assigned(FOnChanging) then
  758. FOnchanging(Self);
  759. end;
  760. Function TStringList.Get(Index: Integer): string;
  761. begin
  762. If (Index<0) or (INdex>=Fcount) then
  763. Error (SListIndexError,Index);
  764. Result:=Flist^[Index].FString;
  765. end;
  766. Function TStringList.GetCapacity: Integer;
  767. begin
  768. Result:=FCapacity;
  769. end;
  770. Function TStringList.GetCount: Integer;
  771. begin
  772. Result:=FCount;
  773. end;
  774. Function TStringList.GetObject(Index: Integer): TObject;
  775. begin
  776. If (Index<0) or (INdex>=Fcount) then
  777. Error (SListIndexError,Index);
  778. Result:=Flist^[Index].FObject;
  779. end;
  780. Procedure TStringList.Put(Index: Integer; const S: string);
  781. begin
  782. If Sorted then
  783. Error(SSortedListError,0);
  784. If (Index<0) or (INdex>=Fcount) then
  785. Error (SListIndexError,Index);
  786. Changing;
  787. Flist^[Index].FString:=S;
  788. Changed;
  789. end;
  790. Procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  791. begin
  792. If (Index<0) or (INdex>=Fcount) then
  793. Error (SListIndexError,Index);
  794. Changing;
  795. Flist^[Index].FObject:=AObject;
  796. Changed;
  797. end;
  798. Procedure TStringList.SetCapacity(NewCapacity: Integer);
  799. Var NewList : Pointer;
  800. MSize : Longint;
  801. begin
  802. If (NewCapacity<0) then
  803. Error (SListCapacityError,NewCapacity);
  804. If NewCapacity>FCapacity then
  805. begin
  806. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  807. If NewList=Nil then
  808. Error (SListCapacityError,NewCapacity);
  809. If Assigned(FList) then
  810. begin
  811. MSize:=FCapacity*Sizeof(TStringItem);
  812. System.Move (FList^,NewList^,MSize);
  813. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*WordRatio, 0);
  814. FreeMem (Flist,MSize);
  815. end;
  816. Flist:=NewList;
  817. FCapacity:=NewCapacity;
  818. end
  819. else if NewCapacity<FCapacity then
  820. begin
  821. if NewCapacity = 0 then
  822. begin
  823. FreeMem(FList);
  824. FList := nil;
  825. end else
  826. begin
  827. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  828. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  829. FreeMem(FList);
  830. FList := NewList;
  831. end;
  832. FCapacity:=NewCapacity;
  833. end;
  834. end;
  835. Procedure TStringList.SetUpdateState(Updating: Boolean);
  836. begin
  837. If Updating then
  838. Changing
  839. else
  840. Changed
  841. end;
  842. destructor TStringList.Destroy;
  843. Var I : Longint;
  844. begin
  845. FOnChange:=Nil;
  846. FOnChanging:=Nil;
  847. // This will force a dereference. Can be done better...
  848. For I:=0 to FCount-1 do
  849. FList^[I].FString:='';
  850. FCount:=0;
  851. SetCapacity(0);
  852. Inherited destroy;
  853. end;
  854. Function TStringList.Add(const S: string): Integer;
  855. begin
  856. If Not Sorted then
  857. Result:=FCount
  858. else
  859. If Find (S,Result) then
  860. Case DUplicates of
  861. DupIgnore : Exit;
  862. DupError : Error(SDuplicateString,0)
  863. end;
  864. InsertItem (Result,S);
  865. end;
  866. Procedure TStringList.Clear;
  867. Var I : longint;
  868. begin
  869. if FCount = 0 then Exit;
  870. Changing;
  871. For I:=0 to FCount-1 do
  872. Flist^[I].FString:='';
  873. FCount:=0;
  874. SetCapacity(0);
  875. Changed;
  876. end;
  877. Procedure TStringList.Delete(Index: Integer);
  878. begin
  879. If (Index<0) or (Index>=FCount) then
  880. Error(SlistINdexError,Index);
  881. Changing;
  882. Flist^[Index].FString:='';
  883. Dec(FCount);
  884. If Index<FCount then
  885. System.Move(Flist^[Index+1],
  886. Flist^[Index],
  887. (Fcount-Index)*SizeOf(TStringItem));
  888. Changed;
  889. end;
  890. Procedure TStringList.Exchange(Index1, Index2: Integer);
  891. begin
  892. If (Index1<0) or (Index1>=FCount) then
  893. Error(SListIndexError,Index1);
  894. If (Index2<0) or (Index2>=FCount) then
  895. Error(SListIndexError,Index2);
  896. Changing;
  897. ExchangeItems(Index1,Index2);
  898. changed;
  899. end;
  900. procedure TStringList.SetCaseSensitive(b : boolean);
  901. begin
  902. if b<>FCaseSensitive then
  903. begin
  904. FCaseSensitive:=b;
  905. if FSorted then
  906. sort;
  907. end;
  908. end;
  909. Function TStringList.DoCompareText(const s1,s2 : string) : PtrInt;
  910. begin
  911. if FCaseSensitive then
  912. result:=AnsiCompareStr(s1,s2)
  913. else
  914. result:=AnsiCompareText(s1,s2);
  915. end;
  916. Function TStringList.Find(const S: string; var Index: Integer): Boolean;
  917. var
  918. L, R, I: Integer;
  919. CompareRes: PtrInt;
  920. begin
  921. Result := false;
  922. // Use binary search.
  923. L := 0;
  924. R := Count - 1;
  925. while (L<=R) do
  926. begin
  927. I := L + (R - L) div 2;
  928. CompareRes := DoCompareText(S, Flist^[I].FString);
  929. if (CompareRes>0) then
  930. L := I+1
  931. else begin
  932. R := I-1;
  933. if (CompareRes=0) then begin
  934. Result := true;
  935. if (Duplicates<>dupAccept) then
  936. L := I; // forces end of while loop
  937. end;
  938. end;
  939. end;
  940. Index := L;
  941. end;
  942. Function TStringList.IndexOf(const S: string): Integer;
  943. begin
  944. If Not Sorted then
  945. Result:=Inherited indexOf(S)
  946. else
  947. // faster using binary search...
  948. If Not Find (S,Result) then
  949. Result:=-1;
  950. end;
  951. Procedure TStringList.Insert(Index: Integer; const S: string);
  952. begin
  953. If Sorted then
  954. Error (SSortedListError,0)
  955. else
  956. If (Index<0) or (Index>FCount) then
  957. Error (SListIndexError,Index)
  958. else
  959. InsertItem (Index,S);
  960. end;
  961. Procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  962. begin
  963. If Not Sorted and (FCount>1) then
  964. begin
  965. Changing;
  966. QuickSort(0,FCount-1, CompareFn);
  967. Changed;
  968. end;
  969. end;
  970. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  971. begin
  972. Result := List.DoCompareText(List.FList^[Index1].FString,
  973. List.FList^[Index].FString);
  974. end;
  975. Procedure TStringList.Sort;
  976. begin
  977. CustomSort(@StringListAnsiCompare);
  978. end;
  979. {$else}
  980. { generics based implementation of TStringList follows }
  981. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  982. begin
  983. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  984. end;
  985. constructor TStringList.Create;
  986. begin
  987. inherited;
  988. FMap := TFPStrObjMap.Create;
  989. FMap.OnPtrCompare := @MapPtrCompare;
  990. FOnCompareText := @DefaultCompareText;
  991. end;
  992. destructor TStringList.Destroy;
  993. begin
  994. FMap.Free;
  995. inherited;
  996. end;
  997. function TStringList.GetDuplicates: TDuplicates;
  998. begin
  999. Result := FMap.Duplicates;
  1000. end;
  1001. function TStringList.GetSorted: boolean;
  1002. begin
  1003. Result := FMap.Sorted;
  1004. end;
  1005. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1006. begin
  1007. FMap.Duplicates := NewDuplicates;
  1008. end;
  1009. procedure TStringList.SetSorted(NewSorted: Boolean);
  1010. begin
  1011. FMap.Sorted := NewSorted;
  1012. end;
  1013. procedure TStringList.Changed;
  1014. begin
  1015. if FUpdateCount = 0 then
  1016. if Assigned(FOnChange) then
  1017. FOnChange(Self);
  1018. end;
  1019. procedure TStringList.Changing;
  1020. begin
  1021. if FUpdateCount = 0 then
  1022. if Assigned(FOnChanging) then
  1023. FOnChanging(Self);
  1024. end;
  1025. function TStringList.Get(Index: Integer): string;
  1026. begin
  1027. Result := FMap.Keys[Index];
  1028. end;
  1029. function TStringList.GetCapacity: Integer;
  1030. begin
  1031. Result := FMap.Capacity;
  1032. end;
  1033. function TStringList.GetCount: Integer;
  1034. begin
  1035. Result := FMap.Count;
  1036. end;
  1037. function TStringList.GetObject(Index: Integer): TObject;
  1038. begin
  1039. Result := FMap.Data[Index];
  1040. end;
  1041. procedure TStringList.Put(Index: Integer; const S: string);
  1042. begin
  1043. Changing;
  1044. FMap.Keys[Index] := S;
  1045. Changed;
  1046. end;
  1047. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1048. begin
  1049. Changing;
  1050. FMap.Data[Index] := AObject;
  1051. Changed;
  1052. end;
  1053. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1054. begin
  1055. FMap.Capacity := NewCapacity;
  1056. end;
  1057. procedure TStringList.SetUpdateState(Updating: Boolean);
  1058. begin
  1059. if Updating then
  1060. Changing
  1061. else
  1062. Changed
  1063. end;
  1064. function TStringList.Add(const S: string): Integer;
  1065. begin
  1066. Result := FMap.Add(S);
  1067. end;
  1068. procedure TStringList.Clear;
  1069. begin
  1070. if FMap.Count = 0 then exit;
  1071. Changing;
  1072. FMap.Clear;
  1073. Changed;
  1074. end;
  1075. procedure TStringList.Delete(Index: Integer);
  1076. begin
  1077. if (Index < 0) or (Index >= FMap.Count) then
  1078. Error(SListIndexError, Index);
  1079. Changing;
  1080. FMap.Delete(Index);
  1081. Changed;
  1082. end;
  1083. procedure TStringList.Exchange(Index1, Index2: Integer);
  1084. begin
  1085. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1086. Error(SListIndexError, Index1);
  1087. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1088. Error(SListIndexError, Index2);
  1089. Changing;
  1090. FMap.InternalExchange(Index1, Index2);
  1091. Changed;
  1092. end;
  1093. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1094. begin
  1095. if NewSensitive <> FCaseSensitive then
  1096. begin
  1097. FCaseSensitive := NewSensitive;
  1098. if Sorted then
  1099. Sort;
  1100. end;
  1101. end;
  1102. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1103. begin
  1104. Result := FOnCompareText(string(Key1^), string(Key2^));
  1105. end;
  1106. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1107. begin
  1108. if FCaseSensitive then
  1109. Result := AnsiCompareStr(s1, s2)
  1110. else
  1111. Result := AnsiCompareText(s1, s2);
  1112. end;
  1113. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1114. begin
  1115. Result := FOnCompareText(s1, s2);
  1116. end;
  1117. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1118. begin
  1119. Result := FMap.Find(S, Index);
  1120. end;
  1121. function TStringList.IndexOf(const S: string): Integer;
  1122. begin
  1123. Result := FMap.IndexOf(S);
  1124. end;
  1125. procedure TStringList.Insert(Index: Integer; const S: string);
  1126. begin
  1127. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1128. Changing;
  1129. FMap.InsertKey(Index, S);
  1130. Changed;
  1131. end;
  1132. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1133. var
  1134. I, J, Pivot: Integer;
  1135. begin
  1136. repeat
  1137. I := L;
  1138. J := R;
  1139. Pivot := (L + R) div 2;
  1140. repeat
  1141. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1142. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1143. if I <= J then
  1144. begin
  1145. FMap.InternalExchange(I, J); // No check, indices are correct.
  1146. if Pivot = I then
  1147. Pivot := J
  1148. else if Pivot = J then
  1149. Pivot := I;
  1150. Inc(I);
  1151. Dec(j);
  1152. end;
  1153. until I > J;
  1154. if L < J then
  1155. QuickSort(L,J, CompareFn);
  1156. L := I;
  1157. until I >= R;
  1158. end;
  1159. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1160. begin
  1161. if not Sorted and (FMap.Count > 1) then
  1162. begin
  1163. Changing;
  1164. QuickSort(0, FMap.Count-1, CompareFn);
  1165. Changed;
  1166. end;
  1167. end;
  1168. procedure TStringList.Sort;
  1169. begin
  1170. if not Sorted and (FMap.Count > 1) then
  1171. begin
  1172. Changing;
  1173. FMap.Sort;
  1174. Changed;
  1175. end;
  1176. end;
  1177. {$endif}