stringl.inc 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363
  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. {* TStringsEnumerator *}
  12. {****************************************************************************}
  13. constructor TStringsEnumerator.Create(AStrings: TStrings);
  14. begin
  15. inherited Create;
  16. FStrings := AStrings;
  17. FPosition := -1;
  18. end;
  19. function TStringsEnumerator.GetCurrent: String;
  20. begin
  21. Result := FStrings[FPosition];
  22. end;
  23. function TStringsEnumerator.MoveNext: Boolean;
  24. begin
  25. Inc(FPosition);
  26. Result := FPosition < FStrings.Count;
  27. end;
  28. {****************************************************************************}
  29. {* TStrings *}
  30. {****************************************************************************}
  31. // Function to quote text. Should move maybe to sysutils !!
  32. // Also, it is not clear at this point what exactly should be done.
  33. { //!! is used to mark unsupported things. }
  34. Function QuoteString (Const S : String; Const Quote : String) : String;
  35. Var
  36. I,J : Integer;
  37. begin
  38. J:=0;
  39. Result:=S;
  40. for i:=1 to length(s) do
  41. begin
  42. inc(j);
  43. if S[i]=Quote then
  44. begin
  45. System.Insert(Quote,Result,J);
  46. inc(j);
  47. end;
  48. end;
  49. Result:=Quote+Result+Quote;
  50. end;
  51. {
  52. For compatibility we can't add a Constructor to TSTrings to initialize
  53. the special characters. Therefore we add a routine which is called whenever
  54. the special chars are needed.
  55. }
  56. Procedure Tstrings.CheckSpecialChars;
  57. begin
  58. If Not FSpecialCharsInited then
  59. begin
  60. FQuoteChar:='"';
  61. FDelimiter:=',';
  62. FNameValueSeparator:='=';
  63. FLBS:=DefaultTextLineBreakStyle;
  64. FSpecialCharsInited:=true;
  65. FLineBreak:=sLineBreak;
  66. end;
  67. end;
  68. Function TStrings.GetSkipLastLineBreak : Boolean;
  69. begin
  70. Result:=not TrailingLineBreak;
  71. end;
  72. procedure TStrings.SetSkipLastLineBreak(const AValue : Boolean);
  73. begin
  74. TrailingLineBreak:=not AValue;
  75. end;
  76. Function TStrings.GetLBS : TTextLineBreakStyle;
  77. begin
  78. CheckSpecialChars;
  79. Result:=FLBS;
  80. end;
  81. Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
  82. begin
  83. CheckSpecialChars;
  84. FLBS:=AValue;
  85. end;
  86. procedure TStrings.SetDelimiter(c:Char);
  87. begin
  88. CheckSpecialChars;
  89. FDelimiter:=c;
  90. end;
  91. Procedure TStrings.SetEncoding(const AEncoding: TEncoding);
  92. begin
  93. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  94. FEncoding.Free;
  95. if TEncoding.IsStandardEncoding(AEncoding) then
  96. FEncoding:=AEncoding
  97. else if AEncoding<>nil then
  98. FEncoding:=AEncoding.Clone
  99. else
  100. FEncoding:=nil;
  101. end;
  102. Function TStrings.GetDelimiter : Char;
  103. begin
  104. CheckSpecialChars;
  105. Result:=FDelimiter;
  106. end;
  107. procedure TStrings.SetLineBreak(Const S : String);
  108. begin
  109. CheckSpecialChars;
  110. FLineBreak:=S;
  111. end;
  112. Function TStrings.GetLineBreak : String;
  113. begin
  114. CheckSpecialChars;
  115. Result:=FLineBreak;
  116. end;
  117. procedure TStrings.SetQuoteChar(c:Char);
  118. begin
  119. CheckSpecialChars;
  120. FQuoteChar:=c;
  121. end;
  122. Function TStrings.GetQuoteChar :Char;
  123. begin
  124. CheckSpecialChars;
  125. Result:=FQuoteChar;
  126. end;
  127. procedure TStrings.SetNameValueSeparator(c:Char);
  128. begin
  129. CheckSpecialChars;
  130. FNameValueSeparator:=c;
  131. end;
  132. Function TStrings.GetNameValueSeparator :Char;
  133. begin
  134. CheckSpecialChars;
  135. Result:=FNameValueSeparator;
  136. end;
  137. function TStrings.GetCommaText: string;
  138. Var
  139. C1,C2 : Char;
  140. FSD : Boolean;
  141. begin
  142. CheckSpecialChars;
  143. FSD:=StrictDelimiter;
  144. C1:=Delimiter;
  145. C2:=QuoteChar;
  146. Delimiter:=',';
  147. QuoteChar:='"';
  148. StrictDelimiter:=False;
  149. Try
  150. Result:=GetDelimitedText;
  151. Finally
  152. Delimiter:=C1;
  153. QuoteChar:=C2;
  154. StrictDelimiter:=FSD;
  155. end;
  156. end;
  157. function TStrings.GetLineBreakCharLBS: string;
  158. begin
  159. CheckSpecialChars;
  160. if FLineBreak<>sLineBreak then
  161. Result:=FLineBreak
  162. else
  163. Case FLBS of
  164. tlbsLF : Result:=#10;
  165. tlbsCRLF : Result:=#13#10;
  166. tlbsCR : Result:=#13;
  167. end;
  168. end;
  169. function TStrings.GetMissingNameValueSeparatorAction: TMissingNameValueSeparatorAction;
  170. begin
  171. CheckSpecialChars;
  172. Result:=FMissingNameValueSeparatorAction;
  173. end;
  174. Function TStrings.GetDelimitedText: string;
  175. Var
  176. I : integer;
  177. p : pchar;
  178. BreakChars : set of char;
  179. S : String;
  180. doQuote : Boolean;
  181. begin
  182. CheckSpecialChars;
  183. result:='';
  184. if StrictDelimiter then
  185. BreakChars:=[#0,QuoteChar,Delimiter]
  186. else
  187. BreakChars:=[#0..' ',QuoteChar,Delimiter];
  188. // Check for break characters and quote if required.
  189. For i:=0 to count-1 do
  190. begin
  191. S:=Strings[i];
  192. doQuote:=FAlwaysQuote;
  193. If not DoQuote then
  194. begin
  195. p:=pchar(S);
  196. //Quote strings that include BreakChars:
  197. while not(p^ in BreakChars) do
  198. inc(p);
  199. DoQuote:=(p<>pchar(S)+length(S));
  200. end;
  201. if DoQuote and (QuoteChar<>#0) then
  202. Result:=Result+QuoteString(S,QuoteChar)
  203. else
  204. Result:=Result+S;
  205. if I<Count-1 then
  206. Result:=Result+Delimiter;
  207. end;
  208. // Quote empty string:
  209. If (Length(Result)=0) and (Count=1) and (QuoteChar<>#0) then
  210. Result:=QuoteChar+QuoteChar;
  211. end;
  212. procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
  213. Var L : longint;
  214. begin
  215. aName:='';
  216. CheckSpecialChars;
  217. AValue:=Strings[Index];
  218. L:=Pos(FNameValueSeparator,AValue);
  219. If L<>0 then
  220. begin
  221. AName:=Copy(AValue,1,L-1);
  222. System.Delete(AValue,1,L);
  223. end
  224. else
  225. case FMissingNameValueSeparatorAction of
  226. mnvaValue : ;
  227. mnvaName :
  228. begin
  229. aName:=aValue;
  230. aValue:='';
  231. end;
  232. mnvaEmpty :
  233. aValue:='';
  234. mnvaError :
  235. Raise EStringListError.CreateFmt(SErrNoNameValuePairAt,[Index]);
  236. end;
  237. end;
  238. function TStrings.ExtractName(const s:String):String;
  239. var
  240. L: Longint;
  241. begin
  242. CheckSpecialChars;
  243. L:=Pos(FNameValueSeparator,S);
  244. If L<>0 then
  245. Result:=Copy(S,1,L-1)
  246. else
  247. Result:='';
  248. end;
  249. procedure TStrings.Filter(aFilter: TStringsFilterMethod; aList: TStrings);
  250. var
  251. S : string;
  252. begin
  253. for S in self do
  254. if aFilter(S) then
  255. aList.Add(S);
  256. end;
  257. procedure TStrings.ForEach(aCallback: TStringsForeachMethod);
  258. var
  259. S : String;
  260. begin
  261. for S in self do
  262. aCallBack(S);
  263. end;
  264. procedure TStrings.ForEach(aCallback: TStringsForeachMethodEx);
  265. var
  266. i: integer;
  267. begin
  268. for i:=0 to Count-1 do
  269. aCallBack(Strings[i],i);
  270. end;
  271. procedure TStrings.ForEach(aCallback: TStringsForeachMethodExObj);
  272. var
  273. i: integer;
  274. begin
  275. for i:=0 to Count-1 do
  276. aCallback(Strings[i],i,Objects[i]);
  277. end;
  278. function TStrings.Filter(aFilter: TStringsFilterMethod): TStrings;
  279. begin
  280. Result:=TStringsClass(Self.ClassType).Create;
  281. try
  282. Filter(aFilter,Result);
  283. except
  284. FreeAndNil(Result);
  285. Raise;
  286. end;
  287. end;
  288. procedure TStrings.Fill(const aValue: String; aStart, aEnd: Integer);
  289. var
  290. i: integer;
  291. begin
  292. if aEnd<0 then
  293. aEnd:=Self.Count+aEnd;
  294. if aEnd>=Count then
  295. aEnd:=Count-1;
  296. for i:=aStart to aEnd do
  297. Strings[i]:=aValue;
  298. end;
  299. Procedure TStrings.Map(aMap: TStringsMapMethod; aList : TStrings);
  300. Var
  301. S : String;
  302. begin
  303. For S in self do
  304. aList.Add(aMap(S));
  305. end;
  306. Function TStrings.Map(aMap: TStringsMapMethod) : TStrings;
  307. begin
  308. Result:=TStringsClass(Self.ClassType).Create;
  309. try
  310. Map(aMap,Result);
  311. except
  312. FreeAndNil(Result);
  313. Raise;
  314. end;
  315. end;
  316. function TStrings.Reduce(aReduceMethod: TStringsReduceMethod; const startingValue: string): string;
  317. var
  318. S : String;
  319. begin
  320. Result:=startingValue;
  321. for S in self do
  322. Result:=aReduceMethod(Result, S);
  323. end;
  324. Function TStrings.Reverse : TStrings;
  325. begin
  326. Result:=TStringsClass(Self.ClassType).Create;
  327. try
  328. Reverse(Result);
  329. except
  330. FreeAndNil(Result);
  331. Raise;
  332. end;
  333. end;
  334. Procedure TStrings.Reverse(aList : TStrings);
  335. Var
  336. I : Integer;
  337. begin
  338. for I:=Count-1 downto 0 do
  339. aList.Add(Strings[i]);
  340. end;
  341. Procedure TStrings.Slice(fromIndex: integer; aList : TStrings);
  342. var
  343. i: integer;
  344. begin
  345. for i:=fromIndex to Count-1 do
  346. aList.Add(Self[i]);
  347. end;
  348. Function TStrings.Slice(fromIndex: integer) : TStrings;
  349. begin
  350. Result:=TStringsClass(Self.ClassType).Create;
  351. try
  352. Slice(FromIndex,Result);
  353. except
  354. FreeAndNil(Result);
  355. Raise;
  356. end;
  357. end;
  358. function TStrings.GetName(Index: Integer): string;
  359. Var
  360. V : String;
  361. begin
  362. GetNameValue(Index,Result,V);
  363. end;
  364. function TStrings.GetStrictDelimiter: Boolean;
  365. begin
  366. Result:=soStrictDelimiter in FOptions;
  367. end;
  368. function TStrings.GetTrailingLineBreak: Boolean;
  369. begin
  370. Result:=soTrailingLineBreak in FOptions;
  371. end;
  372. function TStrings.GetUseLocale: Boolean;
  373. begin
  374. Result:=soUseLocale in FOptions;
  375. end;
  376. function TStrings.GetWriteBOM: Boolean;
  377. begin
  378. Result:=soWriteBOM in FOptions;
  379. end;
  380. Function TStrings.GetValue(const Name: string): string;
  381. Var
  382. L : longint;
  383. N : String;
  384. begin
  385. Result:='';
  386. L:=IndexOfName(Name);
  387. If L<>-1 then
  388. GetNameValue(L,N,Result);
  389. end;
  390. Function TStrings.GetValueFromIndex(Index: Integer): string;
  391. Var
  392. N : String;
  393. begin
  394. GetNameValue(Index,N,Result);
  395. end;
  396. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  397. begin
  398. If (Value='') then
  399. Delete(Index)
  400. else
  401. begin
  402. If (Index<0) then
  403. Index:=Add('');
  404. CheckSpecialChars;
  405. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  406. end;
  407. end;
  408. procedure TStrings.ReadData(Reader: TReader);
  409. begin
  410. Reader.ReadListBegin;
  411. BeginUpdate;
  412. try
  413. Clear;
  414. while not Reader.EndOfList do
  415. Add(Reader.ReadString);
  416. finally
  417. EndUpdate;
  418. end;
  419. Reader.ReadListEnd;
  420. end;
  421. Procedure TStrings.SetDelimitedText(const AValue: string);
  422. begin
  423. CheckSpecialChars;
  424. DoSetDelimitedText(aValue,True,StrictDelimiter,FQuoteChar,FDelimiter);
  425. end;
  426. Procedure TStrings.DoSetDelimitedText(const AValue: string; DoClear,aStrictDelimiter : Boolean; aQuoteChar,aDelimiter : Char);
  427. var
  428. len,i,j: SizeInt;
  429. aNotFirst:boolean;
  430. Procedure AddQuoted;
  431. begin
  432. Add(StringReplace(Copy(AValue,i+1,j-i-1),aQuoteChar+aQuoteChar,aQuoteChar, [rfReplaceAll]));
  433. end;
  434. Function CheckQuoted : Boolean;
  435. { Paraphrased from Delphi XE2 help:
  436. Strings must be separated by Delimiter characters or spaces.
  437. They may be enclosed in QuoteChars.
  438. QuoteChars in the string must be repeated to distinguish them from the QuoteChars enclosing the string.
  439. }
  440. begin
  441. Result:=(AValue[i]=aQuoteChar) and (aQuoteChar<>#0);
  442. If Not Result then
  443. exit;
  444. // next string is quoted
  445. j:=i+1;
  446. while (j<=len) and
  447. ((AValue[j]<>aQuoteChar) or
  448. ((j+1<=len) and (AValue[j+1]=aQuoteChar))) do
  449. begin
  450. if (j<=len) and (AValue[j]=aQuoteChar) then
  451. inc(j,2)
  452. else
  453. inc(j);
  454. end;
  455. AddQuoted;
  456. i:=j+1;
  457. end;
  458. Procedure MaybeSkipSpaces; inline;
  459. begin
  460. if Not aStrictDelimiter then
  461. while (i<=len) and (Ord(AValue[i])<=Ord(' ')) do
  462. inc(i);
  463. end;
  464. begin
  465. BeginUpdate;
  466. i:=1;
  467. j:=1;
  468. aNotFirst:=false;
  469. try
  470. if DoClear then
  471. Clear;
  472. len:=length(AValue);
  473. while i<=len do
  474. begin
  475. // skip delimiter
  476. if aNotFirst and (i<=len) and (AValue[i]=aDelimiter) then
  477. inc(i);
  478. MaybeSkipSpaces;
  479. // read next string
  480. if i>len then
  481. begin
  482. if aNotFirst then Add('');
  483. end
  484. else
  485. begin
  486. // next string is quoted
  487. if not CheckQuoted then
  488. begin
  489. // next string is not quoted; read until control character/space/delimiter
  490. j:=i;
  491. while (j<=len) and
  492. (aStrictDelimiter or (Ord(AValue[j])>Ord(' '))) and
  493. (AValue[j]<>aDelimiter) do
  494. inc(j);
  495. Add( Copy(AValue,i,j-i));
  496. i:=j;
  497. end;
  498. end;
  499. MaybeSkipSpaces;
  500. aNotFirst:=true;
  501. end; // While I<=Len
  502. finally
  503. EndUpdate;
  504. end;
  505. end;
  506. Procedure TStrings.SetCommaText(const Value: string);
  507. begin
  508. CheckSpecialChars;
  509. DoSetDelimitedText(Value,True,StrictDelimiter,'"',',');
  510. end;
  511. procedure TStrings.SetMissingNameValueSeparatorAction(AValue: TMissingNameValueSeparatorAction);
  512. begin
  513. CheckSpecialChars;
  514. FMissingNameValueSeparatorAction:=aValue;
  515. end;
  516. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  517. begin
  518. end;
  519. procedure TStrings.SetStrictDelimiter(AValue: Boolean);
  520. begin
  521. if AValue then
  522. Include(FOptions,soStrictDelimiter)
  523. else
  524. Exclude(FOptions,soStrictDelimiter);
  525. end;
  526. procedure TStrings.SetTrailingLineBreak(AValue: Boolean);
  527. begin
  528. if AValue then
  529. Include(FOptions,soTrailingLineBreak)
  530. else
  531. Exclude(FOptions,soTrailingLineBreak);
  532. end;
  533. procedure TStrings.SetUseLocale(AValue: Boolean);
  534. begin
  535. if AValue then
  536. Include(FOptions,soUseLocale)
  537. else
  538. Exclude(FOptions,soUseLocale);
  539. end;
  540. procedure TStrings.SetWriteBOM(AValue: Boolean);
  541. begin
  542. if AValue then
  543. Include(FOptions,soWriteBOM)
  544. else
  545. Exclude(FOptions,soWriteBOM);
  546. end;
  547. Procedure TStrings.SetDefaultEncoding(const ADefaultEncoding: TEncoding);
  548. begin
  549. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  550. FDefaultEncoding.Free;
  551. if TEncoding.IsStandardEncoding(ADefaultEncoding) then
  552. FDefaultEncoding:=ADefaultEncoding
  553. else if ADefaultEncoding<>nil then
  554. FDefaultEncoding:=ADefaultEncoding.Clone
  555. else
  556. FDefaultEncoding:=TEncoding.Default;
  557. end;
  558. Procedure TStrings.SetValue(const Name, Value: string);
  559. Var L : longint;
  560. begin
  561. CheckSpecialChars;
  562. L:=IndexOfName(Name);
  563. if L=-1 then
  564. Add (Name+FNameValueSeparator+Value)
  565. else
  566. Strings[L]:=Name+FNameValueSeparator+value;
  567. end;
  568. procedure TStrings.WriteData(Writer: TWriter);
  569. var
  570. i: Integer;
  571. begin
  572. Writer.WriteListBegin;
  573. for i := 0 to Count - 1 do
  574. Writer.WriteString(Strings[i]);
  575. Writer.WriteListEnd;
  576. end;
  577. function TStrings.CompareStrings(const s1,s2 : string) : Integer;
  578. begin
  579. Result := DoCompareText(s1, s2);
  580. end;
  581. procedure TStrings.DefineProperties(Filer: TFiler);
  582. var
  583. HasData: Boolean;
  584. begin
  585. if Assigned(Filer.Ancestor) then
  586. // Only serialize if string list is different from ancestor
  587. if Filer.Ancestor.InheritsFrom(TStrings) then
  588. HasData := not Equals(TStrings(Filer.Ancestor))
  589. else
  590. HasData := True
  591. else
  592. HasData := Count > 0;
  593. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  594. end;
  595. Procedure TStrings.Error(const Msg: string; Data: Integer);
  596. begin
  597. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  598. end;
  599. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  600. begin
  601. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  602. end;
  603. Function TStrings.GetCapacity: Integer;
  604. begin
  605. Result:=Count;
  606. end;
  607. Function TStrings.GetObject(Index: Integer): TObject;
  608. begin
  609. Result:=Nil;
  610. end;
  611. Function TStrings.GetTextStr: string;
  612. Var P : Pchar;
  613. I,L,NLS : SizeInt;
  614. S,NL : String;
  615. begin
  616. NL:=GetLineBreakCharLBS;
  617. // Determine needed place
  618. L:=0;
  619. NLS:=Length(NL);
  620. For I:=0 to count-1 do
  621. L:=L+Length(Strings[I])+NLS;
  622. if SkipLastLineBreak then
  623. Dec(L,NLS);
  624. Setlength(Result,L);
  625. P:=Pointer(Result);
  626. For i:=0 To count-1 do
  627. begin
  628. S:=Strings[I];
  629. L:=Length(S);
  630. if L<>0 then
  631. System.Move(Pointer(S)^,P^,L);
  632. P:=P+L;
  633. if (I<Count-1) or Not SkipLastLineBreak then
  634. For L:=1 to NLS do
  635. begin
  636. P^:=NL[L];
  637. inc(P);
  638. end;
  639. end;
  640. end;
  641. Procedure TStrings.Put(Index: Integer; const S: string);
  642. Var Obj : TObject;
  643. begin
  644. Obj:=Objects[Index];
  645. Delete(Index);
  646. InsertObject(Index,S,Obj);
  647. end;
  648. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  649. begin
  650. // Empty.
  651. end;
  652. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  653. begin
  654. // Empty.
  655. end;
  656. Class Function TStrings.GetNextLine (Const Value : String; Var S : String; Var P : SizeInt) : Boolean;
  657. var
  658. LengthOfValue: SizeInt;
  659. StartPos, FuturePos: SizeInt;
  660. begin
  661. LengthOfValue := Length(Value);
  662. StartPos := P;
  663. if (StartPos <= 0) or (StartPos > LengthOfValue) then // True for LengthOfValue <= 0
  664. begin
  665. S := '';
  666. Exit(False);
  667. end;
  668. FuturePos := StartPos;
  669. while (FuturePos <= LengthOfValue) and not (Value[FuturePos] in [#10, #13]) do
  670. Inc(FuturePos);
  671. // If we use S := Copy(Value, StartPos, FuturePos - StartPos); then compiler
  672. // generate TempS := Copy(...); S := TempS to eliminate side effects and
  673. // implicit "try finally" for TempS finalization
  674. // When we use SetString then no TempS, no try finally generated,
  675. // but we must check case when Value and S is same (side effects)
  676. if Pointer(S) = Pointer(Value) then
  677. System.Delete(S, FuturePos, High(FuturePos))
  678. else
  679. begin
  680. SetString(S, @Value[StartPos], FuturePos - StartPos);
  681. if (FuturePos <= LengthOfValue) and (Value[FuturePos] = #13) then
  682. Inc(FuturePos);
  683. if (FuturePos <= LengthOfValue) and (Value[FuturePos] = #10) then
  684. Inc(FuturePos);
  685. end;
  686. P := FuturePos;
  687. Result := True;
  688. end;
  689. Function TStrings.GetNextLineBreak (Const Value : String; Var S : String; Var P : SizeInt) : Boolean;
  690. var
  691. StartPos, FuturePos: SizeInt;
  692. begin
  693. StartPos := P;
  694. if (StartPos <= 0) or (StartPos > Length(Value)) then // True for Length <= 0
  695. begin
  696. S := '';
  697. Exit(False);
  698. end;
  699. FuturePos := Pos(FLineBreak, Value, StartPos); // Use PosEx in old RTL
  700. // Why we don't use Copy but use SetString read in GetNextLine
  701. if FuturePos = 0 then // No line breaks
  702. begin
  703. FuturePos := Length(Value) + 1;
  704. if Pointer(S) = Pointer(Value) then
  705. // Nothing to do
  706. else
  707. SetString(S, @Value[StartPos], FuturePos - StartPos)
  708. end
  709. else
  710. if Pointer(S) = Pointer(Value) then
  711. System.Delete(S, FuturePos, High(FuturePos))
  712. else
  713. begin
  714. SetString(S, @Value[StartPos], FuturePos - StartPos);
  715. Inc(FuturePos, Length(FLineBreak));
  716. end;
  717. P := FuturePos;
  718. Result := True;
  719. end;
  720. {$IF (SizeOf(Integer) < SizeOf(SizeInt)) }
  721. class function TStrings.GetNextLine(const Value: string; var S: string; var P: Integer) : Boolean;
  722. var
  723. LP: SizeInt;
  724. begin
  725. LP := P;
  726. Result := GetNextLine(Value, S, LP);
  727. P := LP;
  728. end;
  729. function TStrings.GetNextLineBreak(const Value: string; var S: string; var P: Integer) : Boolean;
  730. var
  731. LP: SizeInt;
  732. begin
  733. LP := P;
  734. Result := GetNextLineBreak(Value, S, LP);
  735. P := LP;
  736. end;
  737. {$IFEND}
  738. Procedure TStrings.DoSetTextStr(const Value: string; DoClear : Boolean);
  739. Var
  740. S : String;
  741. P : SizeInt;
  742. begin
  743. Try
  744. beginUpdate;
  745. if DoClear then
  746. Clear;
  747. P:=1;
  748. if FLineBreak=sLineBreak then
  749. begin
  750. While GetNextLine (Value,S,P) do
  751. Add(S)
  752. end
  753. else
  754. While GetNextLineBreak (Value,S,P) do
  755. Add(S);
  756. finally
  757. EndUpdate;
  758. end;
  759. end;
  760. Procedure TStrings.SetTextStr(const Value: string);
  761. begin
  762. CheckSpecialChars;
  763. DoSetTextStr(Value,True);
  764. end;
  765. Procedure TStrings.AddText(const S: string);
  766. begin
  767. CheckSpecialChars;
  768. DoSetTextStr(S,False);
  769. end;
  770. procedure TStrings.AddCommaText(const S: String);
  771. begin
  772. DoSetDelimitedText(S,False,StrictDelimiter,'"',',');
  773. end;
  774. procedure TStrings.AddDelimitedText(const S: String; ADelimiter: Char; AStrictDelimiter: Boolean);
  775. begin
  776. CheckSpecialChars;
  777. DoSetDelimitedText(S,False,AStrictDelimiter,FQuoteChar,ADelimiter);
  778. end;
  779. procedure TStrings.AddDelimitedText(const S: String);
  780. begin
  781. CheckSpecialChars;
  782. DoSetDelimitedText(S,False,StrictDelimiter,FQuoteChar,FDelimiter);
  783. end;
  784. Procedure TStrings.SetUpdateState(Updating: Boolean);
  785. begin
  786. FPONotifyObservers(Self,ooChange,Nil);
  787. end;
  788. destructor TSTrings.Destroy;
  789. begin
  790. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  791. FreeAndNil(FEncoding);
  792. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  793. FreeAndNil(FDefaultEncoding);
  794. inherited destroy;
  795. end;
  796. function TStrings.ToObjectArray: TObjectDynArray;
  797. begin
  798. Result:=ToObjectArray(0,Count-1);
  799. end;
  800. function TStrings.ToObjectArray(aStart,aEnd : Integer): TObjectDynArray;
  801. Var
  802. I : Integer;
  803. begin
  804. Result:=Nil;
  805. if aStart>aEnd then exit;
  806. SetLength(Result,aEnd-aStart+1);
  807. For I:=aStart to aEnd do
  808. Result[i-aStart]:=Objects[i];
  809. end;
  810. function TStrings.ToStringArray: TStringDynArray;
  811. begin
  812. Result:=ToStringArray(0,Count-1);
  813. end;
  814. function TStrings.ToStringArray(aStart,aEnd : Integer): TStringDynArray;
  815. Var
  816. I : Integer;
  817. begin
  818. Result:=Nil;
  819. if aStart>aEnd then exit;
  820. SetLength(Result,aEnd-aStart+1);
  821. For I:=aStart to aEnd do
  822. Result[i-aStart]:=Strings[i];
  823. end;
  824. constructor TStrings.Create;
  825. begin
  826. inherited Create;
  827. FDefaultEncoding:=TEncoding.Default;
  828. FEncoding:=nil;
  829. FOptions := [soTrailingLineBreak,soUseLocale,soPreserveBOM];
  830. FAlwaysQuote:=False;
  831. end;
  832. Function TStrings.Add(const S: string): Integer;
  833. begin
  834. Result:=Count;
  835. Insert (Count,S);
  836. end;
  837. function TStrings.Add(const Fmt : string; const Args : Array of const): Integer;
  838. begin
  839. Result:=Add(Format(Fmt,Args));
  840. end;
  841. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  842. begin
  843. Result:=Add(S);
  844. Objects[result]:=AObject;
  845. end;
  846. function TStrings.AddObject(const Fmt: string; Args : Array of const; AObject: TObject): Integer;
  847. begin
  848. Result:=AddObject(Format(Fmt,Args),AObject);
  849. end;
  850. function TStrings.AddPair(const AName, AValue: string): TStrings;
  851. begin
  852. Result:=AddPair(AName,AValue,Nil);
  853. end;
  854. function TStrings.AddPair(const AName, AValue: string; AObject: TObject): TStrings;
  855. begin
  856. Result := Self;
  857. AddObject(Concat(AName, NameValueSeparator, AValue), AObject);
  858. end;
  859. Procedure TStrings.Append(const S: string);
  860. begin
  861. Add (S);
  862. end;
  863. Procedure TStrings.AddStrings(TheStrings: TStrings; ClearFirst : Boolean);
  864. Var Runner : longint;
  865. begin
  866. beginupdate;
  867. try
  868. if ClearFirst then
  869. Clear;
  870. if Count + TheStrings.Count > Capacity then
  871. Capacity := Count + TheStrings.Count;
  872. For Runner:=0 to TheStrings.Count-1 do
  873. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  874. finally
  875. EndUpdate;
  876. end;
  877. end;
  878. Procedure TStrings.AddStrings(TheStrings: TStrings);
  879. begin
  880. AddStrings(TheStrings, False);
  881. end;
  882. Procedure TStrings.AddStrings(const TheStrings: array of string);
  883. begin
  884. AddStrings(TheStrings, False);
  885. end;
  886. Procedure TStrings.AddStrings(const TheStrings: array of string; ClearFirst : Boolean);
  887. Var Runner : longint;
  888. begin
  889. beginupdate;
  890. try
  891. if ClearFirst then
  892. Clear;
  893. if Count + High(TheStrings)+1 > Capacity then
  894. Capacity := Count + High(TheStrings)+1;
  895. For Runner:=Low(TheStrings) to High(TheStrings) do
  896. self.Add(Thestrings[Runner]);
  897. finally
  898. EndUpdate;
  899. end;
  900. end;
  901. procedure TStrings.SetStrings(TheStrings: TStrings);
  902. begin
  903. AddStrings(TheStrings,True);
  904. end;
  905. procedure TStrings.SetStrings(TheStrings: array of string);
  906. begin
  907. AddStrings(TheStrings,True);
  908. end;
  909. Procedure TStrings.Assign(Source: TPersistent);
  910. Var
  911. S : TStrings;
  912. begin
  913. If Source is TStrings then
  914. begin
  915. S:=TStrings(Source);
  916. BeginUpdate;
  917. Try
  918. clear;
  919. FSpecialCharsInited:=S.FSpecialCharsInited;
  920. FQuoteChar:=S.FQuoteChar;
  921. FDelimiter:=S.FDelimiter;
  922. FNameValueSeparator:=S.FNameValueSeparator;
  923. FLBS:=S.FLBS;
  924. FLineBreak:=S.FLineBreak;
  925. FOptions:=S.FOptions;
  926. DefaultEncoding:=S.DefaultEncoding;
  927. SetEncoding(S.Encoding);
  928. AddStrings(S);
  929. finally
  930. EndUpdate;
  931. end;
  932. end
  933. else
  934. Inherited Assign(Source);
  935. end;
  936. Procedure TStrings.BeginUpdate;
  937. begin
  938. if FUpdateCount = 0 then SetUpdateState(true);
  939. inc(FUpdateCount);
  940. end;
  941. Procedure TStrings.EndUpdate;
  942. begin
  943. If FUpdateCount>0 then
  944. Dec(FUpdateCount);
  945. if FUpdateCount=0 then
  946. SetUpdateState(False);
  947. end;
  948. Function TStrings.Equals(Obj: TObject): Boolean;
  949. begin
  950. if Obj is TStrings then
  951. Result := Equals(TStrings(Obj))
  952. else
  953. Result := inherited Equals(Obj);
  954. end;
  955. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  956. Var Runner,Nr : Longint;
  957. begin
  958. Result:=False;
  959. Nr:=Self.Count;
  960. if Nr<>TheStrings.Count then exit;
  961. For Runner:=0 to Nr-1 do
  962. If Strings[Runner]<>TheStrings[Runner] then exit;
  963. Result:=True;
  964. end;
  965. Procedure TStrings.Exchange(Index1, Index2: Integer);
  966. Var
  967. Obj : TObject;
  968. Str : String;
  969. begin
  970. beginUpdate;
  971. Try
  972. Obj:=Objects[Index1];
  973. Str:=Strings[Index1];
  974. Objects[Index1]:=Objects[Index2];
  975. Strings[Index1]:=Strings[Index2];
  976. Objects[Index2]:=Obj;
  977. Strings[Index2]:=Str;
  978. finally
  979. EndUpdate;
  980. end;
  981. end;
  982. function TStrings.GetEnumerator: TStringsEnumerator;
  983. begin
  984. Result:=TStringsEnumerator.Create(Self);
  985. end;
  986. Function TStrings.GetText: PChar;
  987. begin
  988. Result:=StrNew(Pchar(Self.Text));
  989. end;
  990. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  991. begin
  992. if UseLocale then
  993. result:=AnsiCompareText(s1,s2)
  994. else
  995. result:=CompareText(s1,s2);
  996. end;
  997. Function TStrings.IndexOf(const S: string): Integer;
  998. begin
  999. Result:=0;
  1000. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  1001. if Result=Count then Result:=-1;
  1002. end;
  1003. function TStrings.IndexOf(const S: string; aStart: Integer): Integer;
  1004. begin
  1005. if aStart<0 then
  1006. begin
  1007. aStart:=Count+aStart;
  1008. if aStart<0 then
  1009. aStart:=0;
  1010. end;
  1011. Result:=aStart;
  1012. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  1013. if Result=Count then Result:=-1;
  1014. end;
  1015. Function TStrings.IndexOfName(const Name: string): Integer;
  1016. Var
  1017. len : longint;
  1018. S : String;
  1019. begin
  1020. CheckSpecialChars;
  1021. Result:=0;
  1022. while (Result<Count) do
  1023. begin
  1024. S:=Strings[Result];
  1025. len:=pos(FNameValueSeparator,S)-1;
  1026. if (len>=0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  1027. exit;
  1028. inc(result);
  1029. end;
  1030. result:=-1;
  1031. end;
  1032. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  1033. begin
  1034. Result:=0;
  1035. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  1036. If Result=Count then Result:=-1;
  1037. end;
  1038. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  1039. AObject: TObject);
  1040. begin
  1041. Insert (Index,S);
  1042. Objects[Index]:=AObject;
  1043. end;
  1044. function TStrings.LastIndexOf(const S: string): Integer;
  1045. begin
  1046. Result:=LastIndexOf(S,Count-1);
  1047. end;
  1048. function TStrings.LastIndexOf(const S: string; aStart : Integer): Integer;
  1049. begin
  1050. if aStart<0 then
  1051. begin
  1052. aStart:=Count+aStart;
  1053. if aStart<0 then
  1054. aStart:=0;
  1055. end;
  1056. Result:=aStart;
  1057. if Result>=Count-1 then
  1058. Result:=Count-1;
  1059. While (Result>=0) and (DoCompareText(Strings[Result],S)<>0) do
  1060. Result:=Result-1;
  1061. end;
  1062. Procedure TStrings.LoadFromFile(const FileName: string);
  1063. begin
  1064. LoadFromFile(FileName,False)
  1065. end;
  1066. Procedure TStrings.LoadFromFile(const FileName: string; IgnoreEncoding : Boolean);
  1067. Var
  1068. TheStream : TFileStream;
  1069. begin
  1070. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  1071. try
  1072. LoadFromStream(TheStream, IgnoreEncoding);
  1073. finally
  1074. TheStream.Free;
  1075. end;
  1076. end;
  1077. Procedure TStrings.LoadFromFile(const FileName: string; AEncoding: TEncoding);
  1078. Var
  1079. TheStream : TFileStream;
  1080. begin
  1081. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  1082. try
  1083. LoadFromStream(TheStream,AEncoding);
  1084. finally
  1085. TheStream.Free;
  1086. end;
  1087. end;
  1088. Procedure TStrings.LoadFromStream(Stream: TStream);
  1089. begin
  1090. LoadFromStream(Stream,False);
  1091. end;
  1092. Const
  1093. LoadBufSize = 1024;
  1094. LoadMaxGrow = MaxInt Div 2;
  1095. Procedure TStrings.LoadFromStream(Stream: TStream; IgnoreEncoding : Boolean);
  1096. {
  1097. Borlands method is no good, since a pipe for
  1098. instance doesn't have a size.
  1099. So we must do it the hard way.
  1100. }
  1101. Var
  1102. Buffer : AnsiString;
  1103. BufLen : SizeInt;
  1104. BytesRead, I, BufDelta : Longint;
  1105. begin
  1106. if not IgnoreEncoding then
  1107. begin
  1108. LoadFromStream(Stream,Nil);
  1109. Exit;
  1110. end;
  1111. // reread into a buffer
  1112. beginupdate;
  1113. try
  1114. Buffer:='';
  1115. BufLen:=0;
  1116. I:=1;
  1117. Repeat
  1118. BufDelta:=LoadBufSize*I;
  1119. SetLength(Buffer,BufLen+BufDelta);
  1120. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  1121. inc(BufLen,BufDelta);
  1122. If I<LoadMaxGrow then
  1123. I:=I shl 1;
  1124. Until BytesRead<>BufDelta;
  1125. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  1126. SetTextStr(Buffer);
  1127. SetLength(Buffer,0);
  1128. finally
  1129. EndUpdate;
  1130. end;
  1131. if soPreserveBOM in FOptions then
  1132. WriteBOM:=False;
  1133. end;
  1134. Procedure TStrings.LoadFromStream(Stream: TStream; AEncoding: TEncoding);
  1135. {
  1136. Borlands method is no good, since a pipe for
  1137. instance doesn't have a size.
  1138. So we must do it the hard way.
  1139. }
  1140. Var
  1141. Buffer : TBytes;
  1142. T : string;
  1143. BufLen : SizeInt;
  1144. BytesRead, I, BufDelta, PreambleLength : Longint;
  1145. begin
  1146. // reread into a buffer
  1147. beginupdate;
  1148. try
  1149. SetLength(Buffer,0);
  1150. BufLen:=0;
  1151. I:=1;
  1152. Repeat
  1153. BufDelta:=LoadBufSize*I;
  1154. SetLength(Buffer,BufLen+BufDelta);
  1155. BytesRead:=Stream.Read(Buffer[BufLen],BufDelta);
  1156. inc(BufLen,BufDelta);
  1157. If I<LoadMaxGrow then
  1158. I:=I shl 1;
  1159. Until BytesRead<>BufDelta;
  1160. SetLength(Buffer,BufLen-BufDelta+BytesRead);
  1161. PreambleLength:=TEncoding.GetBufferEncoding(Buffer,AEncoding,FDefaultEncoding);
  1162. T:=AEncoding.GetAnsiString(Buffer,PreambleLength,Length(Buffer)-PreambleLength);
  1163. if soPreserveBOM in FOptions then
  1164. WriteBOM:=PreambleLength>0;
  1165. SetEncoding(AEncoding);
  1166. SetLength(Buffer,0);
  1167. SetTextStr(T);
  1168. finally
  1169. EndUpdate;
  1170. end;
  1171. end;
  1172. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  1173. Var
  1174. Obj : TObject;
  1175. Str : String;
  1176. begin
  1177. BeginUpdate;
  1178. Try
  1179. Obj:=Objects[CurIndex];
  1180. Str:=Strings[CurIndex];
  1181. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  1182. Delete(Curindex);
  1183. InsertObject(NewIndex,Str,Obj);
  1184. finally
  1185. EndUpdate;
  1186. end;
  1187. end;
  1188. function TStrings.Pop: string;
  1189. var
  1190. C : Integer;
  1191. begin
  1192. Result:='';
  1193. C:=Count-1;
  1194. if (C>=0) then
  1195. begin
  1196. Result:=Strings[C];
  1197. Delete(C);
  1198. end;
  1199. end;
  1200. function TStrings.Shift: String;
  1201. begin
  1202. Result:='';
  1203. if (Count > 0) then
  1204. begin
  1205. Result:=Strings[0];
  1206. Delete(0);
  1207. end;
  1208. end;
  1209. Procedure TStrings.SaveToFile(const FileName: string);
  1210. Var TheStream : TFileStream;
  1211. begin
  1212. TheStream:=TFileStream.Create(FileName,fmCreate);
  1213. try
  1214. SaveToStream(TheStream);
  1215. finally
  1216. TheStream.Free;
  1217. end;
  1218. end;
  1219. Procedure TStrings.SaveToFile(const FileName: string; IgnoreEncoding : Boolean);
  1220. Var TheStream : TFileStream;
  1221. begin
  1222. TheStream:=TFileStream.Create(FileName,fmCreate);
  1223. try
  1224. SaveToStream(TheStream, IgnoreEncoding);
  1225. finally
  1226. TheStream.Free;
  1227. end;
  1228. end;
  1229. Procedure TStrings.SaveToFile(const FileName: string; AEncoding: TEncoding);
  1230. Var TheStream : TFileStream;
  1231. begin
  1232. TheStream:=TFileStream.Create(FileName,fmCreate);
  1233. try
  1234. SaveToStream(TheStream,AEncoding);
  1235. finally
  1236. TheStream.Free;
  1237. end;
  1238. end;
  1239. Procedure TStrings.SaveToStream(Stream: TStream);
  1240. begin
  1241. SaveToStream(Stream,False)
  1242. end;
  1243. Procedure TStrings.SaveToStream(Stream: TStream; IgnoreEncoding: Boolean);
  1244. Var
  1245. I,L,NLS : SizeInt;
  1246. S,NL : String;
  1247. begin
  1248. if not IgnoreEncoding then
  1249. begin
  1250. SaveToStream(Stream,FEncoding);
  1251. Exit;
  1252. end;
  1253. NL:=GetLineBreakCharLBS;
  1254. NLS:=Length(NL)*SizeOf(Char);
  1255. For i:=0 To count-1 do
  1256. begin
  1257. S:=Strings[I];
  1258. L:=Length(S);
  1259. if L<>0 then
  1260. Stream.WriteBuffer(S[1], L*SizeOf(Char));
  1261. if (I<Count-1) or Not SkipLastLineBreak then
  1262. Stream.WriteBuffer(NL[1], NLS);
  1263. end;
  1264. end;
  1265. Procedure TStrings.SaveToStream(Stream: TStream; AEncoding: TEncoding);
  1266. Var B,BNL : TBytes;
  1267. NL,S: string;
  1268. i,BNLS: SizeInt;
  1269. begin
  1270. if AEncoding=nil then
  1271. AEncoding:=FDefaultEncoding;
  1272. if WriteBOM then
  1273. begin
  1274. B:=AEncoding.GetPreamble;
  1275. if Length(B)>0 then
  1276. Stream.WriteBuffer(B[0],Length(B));
  1277. end;
  1278. NL := GetLineBreakCharLBS;
  1279. BNL:=AEncoding.GetAnsiBytes(NL);
  1280. BNLS:=Length(BNL);
  1281. For i:=0 To count-1 do
  1282. begin
  1283. S:=Strings[I];
  1284. if S<>'' then
  1285. begin
  1286. B:=AEncoding.GetAnsiBytes(S);
  1287. Stream.WriteBuffer(B[0],Length(B));
  1288. end;
  1289. if (I<Count-1) or Not SkipLastLineBreak then
  1290. Stream.WriteBuffer(BNL[0],BNLS);
  1291. end;
  1292. end;
  1293. Procedure TStrings.SetText(TheText: PChar);
  1294. Var S : String;
  1295. begin
  1296. If TheText<>Nil then
  1297. S:=StrPas(TheText)
  1298. else
  1299. S:='';
  1300. SetTextStr(S);
  1301. end;
  1302. {****************************************************************************}
  1303. {* TStringList *}
  1304. {****************************************************************************}
  1305. {$if not defined(FPC_TESTGENERICS)}
  1306. procedure TStringList.ExchangeItemsInt(Index1, Index2: Integer);
  1307. Var P1,P2 : Pointer;
  1308. begin
  1309. P1:=Pointer(Flist^[Index1].FString);
  1310. P2:=Pointer(Flist^[Index1].FObject);
  1311. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  1312. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  1313. Pointer(Flist^[Index2].Fstring):=P1;
  1314. Pointer(Flist^[Index2].FObject):=P2;
  1315. end;
  1316. function TStringList.GetSorted: Boolean;
  1317. begin
  1318. Result:=FSortStyle in [sslUser,sslAuto];
  1319. end;
  1320. procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  1321. begin
  1322. ExchangeItemsInt(Index1, Index2);
  1323. end;
  1324. procedure TStringList.Grow;
  1325. Var
  1326. NC : Integer;
  1327. begin
  1328. NC:=FCapacity;
  1329. If NC>=256 then
  1330. NC:=NC+(NC Div 4)
  1331. else if NC=0 then
  1332. NC:=4
  1333. else
  1334. NC:=NC*4;
  1335. SetCapacity(NC);
  1336. end;
  1337. procedure TStringList.InternalClear(FromIndex: Integer; ClearOnly: Boolean);
  1338. Var
  1339. I: Integer;
  1340. begin
  1341. if FromIndex < FCount then
  1342. begin
  1343. if FOwnsObjects then
  1344. begin
  1345. For I:=FromIndex to FCount-1 do
  1346. begin
  1347. Flist^[I].FString:='';
  1348. freeandnil(Flist^[i].FObject);
  1349. end;
  1350. end
  1351. else
  1352. begin
  1353. For I:=FromIndex to FCount-1 do
  1354. Flist^[I].FString:='';
  1355. end;
  1356. FCount:=FromIndex;
  1357. end;
  1358. if Not ClearOnly then
  1359. SetCapacity(0);
  1360. end;
  1361. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare
  1362. );
  1363. var
  1364. Pivot, vL, vR: Integer;
  1365. ExchangeProc: procedure(Left, Right: Integer) of object;
  1366. begin
  1367. //if ExchangeItems is override call that, else call (faster) ExchangeItemsInt
  1368. if TMethod(@Self.ExchangeItems).Code = CodePointer(@TStringList.ExchangeItems) then
  1369. ExchangeProc := @ExchangeItemsInt
  1370. else
  1371. ExchangeProc := @ExchangeItems;
  1372. if R - L <= 1 then begin // a little bit of time saver
  1373. if L < R then
  1374. if CompareFn(Self, L, R) > 0 then
  1375. ExchangeProc(L, R);
  1376. Exit;
  1377. end;
  1378. vL := L;
  1379. vR := R;
  1380. Pivot := L + Random(R - L); // they say random is best
  1381. while vL < vR do begin
  1382. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  1383. Inc(vL);
  1384. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  1385. Dec(vR);
  1386. ExchangeProc(vL, vR);
  1387. if Pivot = vL then // swap pivot if we just hit it from one side
  1388. Pivot := vR
  1389. else if Pivot = vR then
  1390. Pivot := vL;
  1391. end;
  1392. if Pivot - 1 >= L then
  1393. QuickSort(L, Pivot - 1, CompareFn);
  1394. if Pivot + 1 <= R then
  1395. QuickSort(Pivot + 1, R, CompareFn);
  1396. end;
  1397. procedure TStringList.InsertItem(Index: Integer; const S: string);
  1398. begin
  1399. InsertItem(Index, S, nil);
  1400. end;
  1401. procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  1402. begin
  1403. Changing;
  1404. If FCount=Fcapacity then Grow;
  1405. If Index<FCount then
  1406. System.Move (FList^[Index],FList^[Index+1],
  1407. (FCount-Index)*SizeOf(TStringItem));
  1408. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  1409. Flist^[Index].FString:=S;
  1410. Flist^[Index].FObject:=O;
  1411. Inc(FCount);
  1412. Changed;
  1413. end;
  1414. procedure TStringList.SetSorted(Value: Boolean);
  1415. begin
  1416. If Value then
  1417. SortStyle:=sslAuto
  1418. else
  1419. SortStyle:=sslNone
  1420. end;
  1421. procedure TStringList.Changed;
  1422. begin
  1423. If (FUpdateCount=0) Then
  1424. begin
  1425. If Assigned(FOnChange) then
  1426. FOnchange(Self);
  1427. FPONotifyObservers(Self,ooChange,Nil);
  1428. end;
  1429. end;
  1430. procedure TStringList.Changing;
  1431. begin
  1432. If FUpdateCount=0 then
  1433. if Assigned(FOnChanging) then
  1434. FOnchanging(Self);
  1435. end;
  1436. function TStringList.Get(Index: Integer): string;
  1437. begin
  1438. CheckIndex(Index);
  1439. Result:=Flist^[Index].FString;
  1440. end;
  1441. function TStringList.GetCapacity: Integer;
  1442. begin
  1443. Result:=FCapacity;
  1444. end;
  1445. function TStringList.GetCount: Integer;
  1446. begin
  1447. Result:=FCount;
  1448. end;
  1449. function TStringList.GetObject(Index: Integer): TObject;
  1450. begin
  1451. CheckIndex(Index);
  1452. Result:=Flist^[Index].FObject;
  1453. end;
  1454. procedure TStringList.Put(Index: Integer; const S: string);
  1455. begin
  1456. If Sorted then
  1457. Error(SSortedListError,0);
  1458. CheckIndex(Index);
  1459. Changing;
  1460. Flist^[Index].FString:=S;
  1461. Changed;
  1462. end;
  1463. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1464. begin
  1465. CheckIndex(Index);
  1466. Changing;
  1467. Flist^[Index].FObject:=AObject;
  1468. Changed;
  1469. end;
  1470. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1471. Var NewList : Pointer;
  1472. MSize : Longint;
  1473. begin
  1474. If (NewCapacity<0) then
  1475. Error (SListCapacityError,NewCapacity);
  1476. If NewCapacity>FCapacity then
  1477. begin
  1478. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  1479. If NewList=Nil then
  1480. Error (SListCapacityError,NewCapacity);
  1481. If Assigned(FList) then
  1482. begin
  1483. MSize:=FCapacity*Sizeof(TStringItem);
  1484. System.Move (FList^,NewList^,MSize);
  1485. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*(SizeOf(TStringItem) div SizeOf(Word)), 0);
  1486. FreeMem (Flist,MSize);
  1487. end;
  1488. Flist:=NewList;
  1489. FCapacity:=NewCapacity;
  1490. end
  1491. else if NewCapacity<FCapacity then
  1492. begin
  1493. if NewCapacity = 0 then
  1494. begin
  1495. if FCount > 0 then
  1496. InternalClear(0,True);
  1497. FreeMem(FList);
  1498. FList := nil;
  1499. end else
  1500. begin
  1501. InternalClear(NewCapacity,True);
  1502. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  1503. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  1504. FreeMem(FList);
  1505. FList := NewList;
  1506. end;
  1507. FCapacity:=NewCapacity;
  1508. end;
  1509. end;
  1510. procedure TStringList.SetUpdateState(Updating: Boolean);
  1511. begin
  1512. If Updating then
  1513. Changing
  1514. else
  1515. Changed
  1516. end;
  1517. destructor TStringList.Destroy;
  1518. begin
  1519. InternalClear;
  1520. Inherited destroy;
  1521. end;
  1522. function TStringList.Add(const S: string): Integer;
  1523. begin
  1524. If (SortStyle<>sslAuto) then
  1525. Result:=FCount
  1526. else
  1527. If Find (S,Result) then
  1528. Case DUplicates of
  1529. DupIgnore : Exit;
  1530. DupError : Error(SDuplicateString,0)
  1531. end;
  1532. InsertItem (Result,S);
  1533. end;
  1534. procedure TStringList.Clear;
  1535. begin
  1536. if FCount = 0 then Exit;
  1537. Changing;
  1538. InternalClear;
  1539. Changed;
  1540. end;
  1541. procedure TStringList.Delete(Index: Integer);
  1542. begin
  1543. CheckIndex(Index);
  1544. Changing;
  1545. Flist^[Index].FString:='';
  1546. if FOwnsObjects then
  1547. FreeAndNil(Flist^[Index].FObject);
  1548. Dec(FCount);
  1549. If Index<FCount then
  1550. System.Move(Flist^[Index+1],
  1551. Flist^[Index],
  1552. (Fcount-Index)*SizeOf(TStringItem));
  1553. Changed;
  1554. end;
  1555. procedure TStringList.Exchange(Index1, Index2: Integer);
  1556. begin
  1557. CheckIndex(Index1);
  1558. CheckIndex(Index2);
  1559. Changing;
  1560. ExchangeItemsInt(Index1,Index2);
  1561. changed;
  1562. end;
  1563. procedure TStringList.SetCaseSensitive(b : boolean);
  1564. begin
  1565. if b=FCaseSensitive then
  1566. Exit;
  1567. FCaseSensitive:=b;
  1568. if FSortStyle=sslAuto then
  1569. begin
  1570. FForceSort:=True;
  1571. try
  1572. Sort;
  1573. finally
  1574. FForceSort:=False;
  1575. end;
  1576. end;
  1577. end;
  1578. procedure TStringList.SetSortStyle(AValue: TStringsSortStyle);
  1579. begin
  1580. if FSortStyle=AValue then Exit;
  1581. if (AValue=sslAuto) then
  1582. Sort;
  1583. FSortStyle:=AValue;
  1584. end;
  1585. procedure TStringList.CheckIndex(AIndex: Integer);
  1586. begin
  1587. If (AIndex<0) or (AIndex>=FCount) then
  1588. Error(SListIndexError,AIndex);
  1589. end;
  1590. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1591. begin
  1592. if FCaseSensitive then
  1593. begin
  1594. if UseLocale then
  1595. result:=AnsiCompareStr(s1,s2)
  1596. else
  1597. result:=CompareStr(s1,s2);
  1598. end else
  1599. begin
  1600. if UseLocale then
  1601. result:=AnsiCompareText(s1,s2)
  1602. else
  1603. result:=CompareText(s1,s2);
  1604. end;
  1605. end;
  1606. function TStringList.Find(const S: string; out Index: Integer): Boolean;
  1607. var
  1608. L, R, I: Integer;
  1609. CompareRes: PtrInt;
  1610. begin
  1611. Result := false;
  1612. Index:=-1;
  1613. if Not Sorted then
  1614. Raise EListError.Create(SErrFindNeedsSortedList);
  1615. // Use binary search.
  1616. L := 0;
  1617. R := Count - 1;
  1618. while (L<=R) do
  1619. begin
  1620. I := L + (R - L) div 2;
  1621. CompareRes := DoCompareText(S, Flist^[I].FString);
  1622. if (CompareRes>0) then
  1623. L := I+1
  1624. else begin
  1625. R := I-1;
  1626. if (CompareRes=0) then begin
  1627. Result := true;
  1628. if (Duplicates<>dupAccept) then
  1629. L := I; // forces end of while loop
  1630. end;
  1631. end;
  1632. end;
  1633. Index := L;
  1634. end;
  1635. function TStringList.IndexOf(const S: string): Integer;
  1636. begin
  1637. If Not Sorted then
  1638. Result:=Inherited indexOf(S)
  1639. else
  1640. // faster using binary search...
  1641. If Not Find (S,Result) then
  1642. Result:=-1;
  1643. end;
  1644. procedure TStringList.Insert(Index: Integer; const S: string);
  1645. begin
  1646. If SortStyle=sslAuto then
  1647. Error (SSortedListError,0)
  1648. else
  1649. begin
  1650. If (Index<0) or (Index>FCount) then
  1651. Error(SListIndexError,Index); // Cannot use CheckIndex, because there >= FCount...
  1652. InsertItem (Index,S);
  1653. end;
  1654. end;
  1655. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1656. begin
  1657. If (FCount>1) and (FForceSort or (FSortStyle<>sslAuto)) then
  1658. begin
  1659. Changing;
  1660. QuickSort(0,FCount-1, CompareFn);
  1661. Changed;
  1662. end;
  1663. end;
  1664. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1665. begin
  1666. Result := List.DoCompareText(List.FList^[Index1].FString,
  1667. List.FList^[Index].FString);
  1668. end;
  1669. procedure TStringList.Sort;
  1670. begin
  1671. CustomSort(@StringListAnsiCompare);
  1672. end;
  1673. {$else}
  1674. { generics based implementation of TStringList follows }
  1675. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1676. begin
  1677. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1678. end;
  1679. constructor TStringList.Create;
  1680. begin
  1681. inherited;
  1682. FOwnsObjects:=false;
  1683. FMap := TFPStrObjMap.Create;
  1684. FMap.OnPtrCompare := @MapPtrCompare;
  1685. FOnCompareText := @DefaultCompareText;
  1686. NameValueSeparator:='=';
  1687. CheckSpecialChars;
  1688. end;
  1689. destructor TStringList.Destroy;
  1690. begin
  1691. FMap.Free;
  1692. inherited;
  1693. end;
  1694. function TStringList.GetDuplicates: TDuplicates;
  1695. begin
  1696. Result := FMap.Duplicates;
  1697. end;
  1698. function TStringList.GetSorted: boolean;
  1699. begin
  1700. Result := FMap.Sorted;
  1701. end;
  1702. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1703. begin
  1704. FMap.Duplicates := NewDuplicates;
  1705. end;
  1706. procedure TStringList.SetSorted(NewSorted: Boolean);
  1707. begin
  1708. FMap.Sorted := NewSorted;
  1709. end;
  1710. procedure TStringList.Changed;
  1711. begin
  1712. if FUpdateCount = 0 then
  1713. if Assigned(FOnChange) then
  1714. FOnChange(Self);
  1715. end;
  1716. procedure TStringList.Changing;
  1717. begin
  1718. if FUpdateCount = 0 then
  1719. if Assigned(FOnChanging) then
  1720. FOnChanging(Self);
  1721. end;
  1722. function TStringList.Get(Index: Integer): string;
  1723. begin
  1724. Result := FMap.Keys[Index];
  1725. end;
  1726. function TStringList.GetCapacity: Integer;
  1727. begin
  1728. Result := FMap.Capacity;
  1729. end;
  1730. function TStringList.GetCount: Integer;
  1731. begin
  1732. Result := FMap.Count;
  1733. end;
  1734. function TStringList.GetObject(Index: Integer): TObject;
  1735. begin
  1736. Result := FMap.Data[Index];
  1737. end;
  1738. procedure TStringList.Put(Index: Integer; const S: string);
  1739. begin
  1740. Changing;
  1741. FMap.Keys[Index] := S;
  1742. Changed;
  1743. end;
  1744. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1745. begin
  1746. Changing;
  1747. FMap.Data[Index] := AObject;
  1748. Changed;
  1749. end;
  1750. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1751. begin
  1752. FMap.Capacity := NewCapacity;
  1753. end;
  1754. procedure TStringList.SetUpdateState(Updating: Boolean);
  1755. begin
  1756. if Updating then
  1757. Changing
  1758. else
  1759. Changed
  1760. end;
  1761. function TStringList.Add(const S: string): Integer;
  1762. begin
  1763. Result := FMap.Add(S);
  1764. end;
  1765. procedure TStringList.Clear;
  1766. begin
  1767. if FMap.Count = 0 then exit;
  1768. Changing;
  1769. FMap.Clear;
  1770. Changed;
  1771. end;
  1772. procedure TStringList.Delete(Index: Integer);
  1773. begin
  1774. if (Index < 0) or (Index >= FMap.Count) then
  1775. Error(SListIndexError, Index);
  1776. Changing;
  1777. FMap.Delete(Index);
  1778. Changed;
  1779. end;
  1780. procedure TStringList.Exchange(Index1, Index2: Integer);
  1781. begin
  1782. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1783. Error(SListIndexError, Index1);
  1784. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1785. Error(SListIndexError, Index2);
  1786. Changing;
  1787. FMap.InternalExchange(Index1, Index2);
  1788. Changed;
  1789. end;
  1790. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1791. begin
  1792. if NewSensitive <> FCaseSensitive then
  1793. begin
  1794. FCaseSensitive := NewSensitive;
  1795. if Sorted then
  1796. Sort;
  1797. end;
  1798. end;
  1799. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1800. begin
  1801. Result := FOnCompareText(string(Key1^), string(Key2^));
  1802. end;
  1803. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1804. begin
  1805. if FCaseSensitive then
  1806. Result := AnsiCompareStr(s1, s2)
  1807. else
  1808. Result := AnsiCompareText(s1, s2);
  1809. end;
  1810. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1811. begin
  1812. Result := FOnCompareText(s1, s2);
  1813. end;
  1814. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1815. begin
  1816. Result := FMap.Find(S, Index);
  1817. end;
  1818. function TStringList.IndexOf(const S: string): Integer;
  1819. begin
  1820. Result := FMap.IndexOf(S);
  1821. end;
  1822. procedure TStringList.Insert(Index: Integer; const S: string);
  1823. begin
  1824. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1825. Changing;
  1826. FMap.InsertKey(Index, S);
  1827. Changed;
  1828. end;
  1829. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1830. var
  1831. I, J, Pivot: Integer;
  1832. begin
  1833. repeat
  1834. I := L;
  1835. J := R;
  1836. Pivot := (L + R) div 2;
  1837. repeat
  1838. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1839. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1840. if I <= J then
  1841. begin
  1842. FMap.InternalExchange(I, J); // No check, indices are correct.
  1843. if Pivot = I then
  1844. Pivot := J
  1845. else if Pivot = J then
  1846. Pivot := I;
  1847. Inc(I);
  1848. Dec(j);
  1849. end;
  1850. until I > J;
  1851. if L < J then
  1852. QuickSort(L,J, CompareFn);
  1853. L := I;
  1854. until I >= R;
  1855. end;
  1856. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1857. begin
  1858. if not Sorted and (FMap.Count > 1) then
  1859. begin
  1860. Changing;
  1861. QuickSort(0, FMap.Count-1, CompareFn);
  1862. Changed;
  1863. end;
  1864. end;
  1865. procedure TStringList.Sort;
  1866. begin
  1867. if not Sorted and (FMap.Count > 1) then
  1868. begin
  1869. Changing;
  1870. FMap.Sort;
  1871. Changed;
  1872. end;
  1873. end;
  1874. {$endif}