stringl.inc 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425
  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:AnsiChar);
  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 : AnsiChar;
  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:AnsiChar);
  118. begin
  119. CheckSpecialChars;
  120. FQuoteChar:=c;
  121. end;
  122. Function TStrings.GetQuoteChar :AnsiChar;
  123. begin
  124. CheckSpecialChars;
  125. Result:=FQuoteChar;
  126. end;
  127. procedure TStrings.SetNameValueSeparator(c:AnsiChar);
  128. begin
  129. CheckSpecialChars;
  130. FNameValueSeparator:=c;
  131. end;
  132. Function TStrings.GetNameValueSeparator :AnsiChar;
  133. begin
  134. CheckSpecialChars;
  135. Result:=FNameValueSeparator;
  136. end;
  137. function TStrings.GetCommaText: string;
  138. Var
  139. C1,C2 : AnsiChar;
  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 : PAnsiChar;
  178. BreakChars : set of AnsiChar;
  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:=PAnsiChar(S);
  196. //Quote strings that include BreakChars:
  197. while not(p^ in BreakChars) do
  198. inc(p);
  199. DoQuote:=(p<>PAnsiChar(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 : AnsiChar);
  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 : PAnsiChar;
  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: AnsiChar; 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: PAnsiChar;
  987. begin
  988. Result:=StrNew(PAnsiChar(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. if (CurIndex=NewIndex) then
  1178. Exit;
  1179. BeginUpdate;
  1180. Try
  1181. Obj:=Objects[CurIndex];
  1182. Str:=Strings[CurIndex];
  1183. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  1184. Delete(Curindex);
  1185. InsertObject(NewIndex,Str,Obj);
  1186. finally
  1187. EndUpdate;
  1188. end;
  1189. end;
  1190. function TStrings.Pop: string;
  1191. var
  1192. C : Integer;
  1193. begin
  1194. Result:='';
  1195. C:=Count-1;
  1196. if (C>=0) then
  1197. begin
  1198. Result:=Strings[C];
  1199. Delete(C);
  1200. end;
  1201. end;
  1202. function TStrings.Shift: String;
  1203. begin
  1204. Result:='';
  1205. if (Count > 0) then
  1206. begin
  1207. Result:=Strings[0];
  1208. Delete(0);
  1209. end;
  1210. end;
  1211. Procedure TStrings.SaveToFile(const FileName: string);
  1212. Var TheStream : TFileStream;
  1213. begin
  1214. TheStream:=TFileStream.Create(FileName,fmCreate);
  1215. try
  1216. SaveToStream(TheStream);
  1217. finally
  1218. TheStream.Free;
  1219. end;
  1220. end;
  1221. Procedure TStrings.SaveToFile(const FileName: string; IgnoreEncoding : Boolean);
  1222. Var TheStream : TFileStream;
  1223. begin
  1224. TheStream:=TFileStream.Create(FileName,fmCreate);
  1225. try
  1226. SaveToStream(TheStream, IgnoreEncoding);
  1227. finally
  1228. TheStream.Free;
  1229. end;
  1230. end;
  1231. Procedure TStrings.SaveToFile(const FileName: string; AEncoding: TEncoding);
  1232. Var TheStream : TFileStream;
  1233. begin
  1234. TheStream:=TFileStream.Create(FileName,fmCreate);
  1235. try
  1236. SaveToStream(TheStream,AEncoding);
  1237. finally
  1238. TheStream.Free;
  1239. end;
  1240. end;
  1241. Procedure TStrings.SaveToStream(Stream: TStream);
  1242. begin
  1243. SaveToStream(Stream,False)
  1244. end;
  1245. Procedure TStrings.SaveToStream(Stream: TStream; IgnoreEncoding: Boolean);
  1246. Var
  1247. I,L,NLS : SizeInt;
  1248. S,NL : String;
  1249. begin
  1250. if not IgnoreEncoding then
  1251. begin
  1252. SaveToStream(Stream,FEncoding);
  1253. Exit;
  1254. end;
  1255. NL:=GetLineBreakCharLBS;
  1256. NLS:=Length(NL)*SizeOf(AnsiChar);
  1257. For i:=0 To count-1 do
  1258. begin
  1259. S:=Strings[I];
  1260. L:=Length(S);
  1261. if L<>0 then
  1262. Stream.WriteBuffer(S[1], L*SizeOf(AnsiChar));
  1263. if (I<Count-1) or Not SkipLastLineBreak then
  1264. Stream.WriteBuffer(NL[1], NLS);
  1265. end;
  1266. end;
  1267. Procedure TStrings.SaveToStream(Stream: TStream; AEncoding: TEncoding);
  1268. Var B,BNL : TBytes;
  1269. NL,S: string;
  1270. i,BNLS: SizeInt;
  1271. begin
  1272. if AEncoding=nil then
  1273. AEncoding:=FDefaultEncoding;
  1274. if WriteBOM then
  1275. begin
  1276. B:=AEncoding.GetPreamble;
  1277. if Length(B)>0 then
  1278. Stream.WriteBuffer(B[0],Length(B));
  1279. end;
  1280. NL := GetLineBreakCharLBS;
  1281. BNL:=AEncoding.GetAnsiBytes(NL);
  1282. BNLS:=Length(BNL);
  1283. For i:=0 To count-1 do
  1284. begin
  1285. S:=Strings[I];
  1286. if S<>'' then
  1287. begin
  1288. B:=AEncoding.GetAnsiBytes(S);
  1289. Stream.WriteBuffer(B[0],Length(B));
  1290. end;
  1291. if (I<Count-1) or Not SkipLastLineBreak then
  1292. Stream.WriteBuffer(BNL[0],BNLS);
  1293. end;
  1294. end;
  1295. Procedure TStrings.SetText(TheText: PAnsiChar);
  1296. Var S : String;
  1297. begin
  1298. If TheText<>Nil then
  1299. S:=StrPas(TheText)
  1300. else
  1301. S:='';
  1302. SetTextStr(S);
  1303. end;
  1304. {****************************************************************************}
  1305. {* TStringList *}
  1306. {****************************************************************************}
  1307. {$if not defined(FPC_TESTGENERICS)}
  1308. procedure TStringList.ExchangeItemsInt(Index1, Index2: Integer);
  1309. Var P1,P2 : Pointer;
  1310. begin
  1311. P1:=Pointer(Flist^[Index1].FString);
  1312. P2:=Pointer(Flist^[Index1].FObject);
  1313. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  1314. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  1315. Pointer(Flist^[Index2].Fstring):=P1;
  1316. Pointer(Flist^[Index2].FObject):=P2;
  1317. end;
  1318. function TStringList.GetSorted: Boolean;
  1319. begin
  1320. Result:=FSortStyle in [sslUser,sslAuto];
  1321. end;
  1322. procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  1323. begin
  1324. ExchangeItemsInt(Index1, Index2);
  1325. end;
  1326. procedure TStringList.Grow;
  1327. Var
  1328. NC : Integer;
  1329. begin
  1330. NC:=FCapacity;
  1331. If NC>=256 then
  1332. NC:=NC+(NC Div 4)
  1333. else if NC=0 then
  1334. NC:=4
  1335. else
  1336. NC:=NC*4;
  1337. SetCapacity(NC);
  1338. end;
  1339. procedure TStringList.InternalClear(FromIndex: Integer; ClearOnly: Boolean);
  1340. Var
  1341. I: Integer;
  1342. begin
  1343. if FromIndex < FCount then
  1344. begin
  1345. if FOwnsObjects then
  1346. begin
  1347. For I:=FromIndex to FCount-1 do
  1348. begin
  1349. Flist^[I].FString:='';
  1350. freeandnil(Flist^[i].FObject);
  1351. end;
  1352. end
  1353. else
  1354. begin
  1355. For I:=FromIndex to FCount-1 do
  1356. Flist^[I].FString:='';
  1357. end;
  1358. FCount:=FromIndex;
  1359. end;
  1360. if Not ClearOnly then
  1361. SetCapacity(0);
  1362. end;
  1363. procedure TStringList.InsertItem(Index: Integer; const S: string);
  1364. begin
  1365. InsertItem(Index, S, nil);
  1366. end;
  1367. procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  1368. begin
  1369. Changing;
  1370. If FCount=Fcapacity then Grow;
  1371. If Index<FCount then
  1372. System.Move (FList^[Index],FList^[Index+1],
  1373. (FCount-Index)*SizeOf(TStringItem));
  1374. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  1375. Flist^[Index].FString:=S;
  1376. Flist^[Index].FObject:=O;
  1377. Inc(FCount);
  1378. Changed;
  1379. end;
  1380. procedure TStringList.SetSorted(Value: Boolean);
  1381. begin
  1382. If Value then
  1383. SortStyle:=sslAuto
  1384. else
  1385. SortStyle:=sslNone
  1386. end;
  1387. procedure TStringList.Changed;
  1388. begin
  1389. If (FUpdateCount=0) Then
  1390. begin
  1391. If Assigned(FOnChange) then
  1392. FOnchange(Self);
  1393. FPONotifyObservers(Self,ooChange,Nil);
  1394. end;
  1395. end;
  1396. procedure TStringList.Changing;
  1397. begin
  1398. If FUpdateCount=0 then
  1399. if Assigned(FOnChanging) then
  1400. FOnchanging(Self);
  1401. end;
  1402. function TStringList.Get(Index: Integer): string;
  1403. begin
  1404. CheckIndex(Index);
  1405. Result:=Flist^[Index].FString;
  1406. end;
  1407. function TStringList.GetCapacity: Integer;
  1408. begin
  1409. Result:=FCapacity;
  1410. end;
  1411. function TStringList.GetCount: Integer;
  1412. begin
  1413. Result:=FCount;
  1414. end;
  1415. function TStringList.GetObject(Index: Integer): TObject;
  1416. begin
  1417. CheckIndex(Index);
  1418. Result:=Flist^[Index].FObject;
  1419. end;
  1420. procedure TStringList.Put(Index: Integer; const S: string);
  1421. begin
  1422. If Sorted then
  1423. Error(SSortedListError,0);
  1424. CheckIndex(Index);
  1425. Changing;
  1426. Flist^[Index].FString:=S;
  1427. Changed;
  1428. end;
  1429. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1430. begin
  1431. CheckIndex(Index);
  1432. Changing;
  1433. Flist^[Index].FObject:=AObject;
  1434. Changed;
  1435. end;
  1436. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1437. Var NewList : Pointer;
  1438. MSize : Longint;
  1439. begin
  1440. If (NewCapacity<0) then
  1441. Error (SListCapacityError,NewCapacity);
  1442. If NewCapacity>FCapacity then
  1443. begin
  1444. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  1445. If NewList=Nil then
  1446. Error (SListCapacityError,NewCapacity);
  1447. If Assigned(FList) then
  1448. begin
  1449. MSize:=FCapacity*Sizeof(TStringItem);
  1450. System.Move (FList^,NewList^,MSize);
  1451. FillWord (PAnsiChar(NewList)[MSize],(NewCapacity-FCapacity)*(SizeOf(TStringItem) div SizeOf(Word)), 0);
  1452. FreeMem (Flist,MSize);
  1453. end;
  1454. Flist:=NewList;
  1455. FCapacity:=NewCapacity;
  1456. end
  1457. else if NewCapacity<FCapacity then
  1458. begin
  1459. if NewCapacity = 0 then
  1460. begin
  1461. if FCount > 0 then
  1462. InternalClear(0,True);
  1463. FreeMem(FList);
  1464. FList := nil;
  1465. end else
  1466. begin
  1467. InternalClear(NewCapacity,True);
  1468. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  1469. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  1470. FreeMem(FList);
  1471. FList := NewList;
  1472. end;
  1473. FCapacity:=NewCapacity;
  1474. end;
  1475. end;
  1476. procedure TStringList.SetUpdateState(Updating: Boolean);
  1477. begin
  1478. If Updating then
  1479. Changing
  1480. else
  1481. Changed
  1482. end;
  1483. Constructor TStringList.Create;
  1484. begin
  1485. inherited Create;
  1486. end;
  1487. Constructor TStringList.Create(anOwnsObjects : Boolean);
  1488. begin
  1489. inherited Create;
  1490. FOwnsObjects:=anOwnsObjects;
  1491. end;
  1492. destructor TStringList.Destroy;
  1493. begin
  1494. InternalClear;
  1495. Inherited destroy;
  1496. end;
  1497. function TStringList.Add(const S: string): Integer;
  1498. begin
  1499. If (SortStyle<>sslAuto) then
  1500. Result:=FCount
  1501. else
  1502. If Find (S,Result) then
  1503. Case DUplicates of
  1504. DupIgnore : Exit;
  1505. DupError : Error(SDuplicateString,0)
  1506. end;
  1507. InsertItem (Result,S);
  1508. end;
  1509. procedure TStringList.Clear;
  1510. begin
  1511. if FCount = 0 then Exit;
  1512. Changing;
  1513. InternalClear;
  1514. Changed;
  1515. end;
  1516. procedure TStringList.Delete(Index: Integer);
  1517. begin
  1518. CheckIndex(Index);
  1519. Changing;
  1520. Flist^[Index].FString:='';
  1521. if FOwnsObjects then
  1522. FreeAndNil(Flist^[Index].FObject);
  1523. Dec(FCount);
  1524. If Index<FCount then
  1525. System.Move(Flist^[Index+1],
  1526. Flist^[Index],
  1527. (Fcount-Index)*SizeOf(TStringItem));
  1528. Changed;
  1529. end;
  1530. procedure TStringList.Exchange(Index1, Index2: Integer);
  1531. begin
  1532. CheckIndex(Index1);
  1533. CheckIndex(Index2);
  1534. Changing;
  1535. ExchangeItemsInt(Index1,Index2);
  1536. changed;
  1537. end;
  1538. procedure TStringList.SetCaseSensitive(b : boolean);
  1539. begin
  1540. if b=FCaseSensitive then
  1541. Exit;
  1542. FCaseSensitive:=b;
  1543. if FSortStyle=sslAuto then
  1544. begin
  1545. FForceSort:=True;
  1546. try
  1547. Sort;
  1548. finally
  1549. FForceSort:=False;
  1550. end;
  1551. end;
  1552. end;
  1553. procedure TStringList.SetSortStyle(AValue: TStringsSortStyle);
  1554. begin
  1555. if FSortStyle=AValue then Exit;
  1556. if (AValue=sslAuto) then
  1557. Sort;
  1558. FSortStyle:=AValue;
  1559. end;
  1560. procedure TStringList.CheckIndex(AIndex: Integer);
  1561. begin
  1562. If (AIndex<0) or (AIndex>=FCount) then
  1563. Error(SListIndexError,AIndex);
  1564. end;
  1565. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1566. begin
  1567. if FCaseSensitive then
  1568. begin
  1569. if UseLocale then
  1570. result:=AnsiCompareStr(s1,s2)
  1571. else
  1572. result:=CompareStr(s1,s2);
  1573. end else
  1574. begin
  1575. if UseLocale then
  1576. result:=AnsiCompareText(s1,s2)
  1577. else
  1578. result:=CompareText(s1,s2);
  1579. end;
  1580. end;
  1581. function TStringList.Find(const S: string; out Index: Integer): Boolean;
  1582. var
  1583. L, R, I: Integer;
  1584. CompareRes: PtrInt;
  1585. begin
  1586. Result := false;
  1587. Index:=-1;
  1588. if Not Sorted then
  1589. Raise EListError.Create(SErrFindNeedsSortedList);
  1590. // Use binary search.
  1591. L := 0;
  1592. R := Count - 1;
  1593. while (L<=R) do
  1594. begin
  1595. I := L + (R - L) div 2;
  1596. CompareRes := DoCompareText(S, Flist^[I].FString);
  1597. if (CompareRes>0) then
  1598. L := I+1
  1599. else begin
  1600. R := I-1;
  1601. if (CompareRes=0) then begin
  1602. Result := true;
  1603. if (Duplicates<>dupAccept) then
  1604. L := I; // forces end of while loop
  1605. end;
  1606. end;
  1607. end;
  1608. Index := L;
  1609. end;
  1610. function TStringList.IndexOf(const S: string): Integer;
  1611. begin
  1612. If Not Sorted then
  1613. Result:=Inherited indexOf(S)
  1614. else
  1615. // faster using binary search...
  1616. If Not Find (S,Result) then
  1617. Result:=-1;
  1618. end;
  1619. procedure TStringList.Insert(Index: Integer; const S: string);
  1620. begin
  1621. If SortStyle=sslAuto then
  1622. Error (SSortedListError,0)
  1623. else
  1624. begin
  1625. If (Index<0) or (Index>FCount) then
  1626. Error(SListIndexError,Index); // Cannot use CheckIndex, because there >= FCount...
  1627. InsertItem (Index,S);
  1628. end;
  1629. end;
  1630. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1631. begin
  1632. CustomSort(CompareFn, SortBase.DefaultSortingAlgorithm);
  1633. end;
  1634. type
  1635. PStringList_CustomSort_Context = ^TStringList_CustomSort_Context;
  1636. TStringList_CustomSort_Context = record
  1637. List: TStringList;
  1638. ListStartPtr: Pointer;
  1639. CompareFn: TStringListSortCompare;
  1640. end;
  1641. function TStringList_CustomSort_Comparer(Item1, Item2, Context: Pointer): Integer;
  1642. begin
  1643. with PStringList_CustomSort_Context(Context)^ do
  1644. Result := CompareFn(List,
  1645. (Item1 - ListStartPtr) div SizeOf(TStringItem),
  1646. (Item2 - ListStartPtr) div SizeOf(TStringItem));
  1647. end;
  1648. procedure TStringList_CustomSort_Exchanger(Item1, Item2, Context: Pointer);
  1649. begin
  1650. with PStringList_CustomSort_Context(Context)^ do
  1651. List.ExchangeItems((Item1 - ListStartPtr) div SizeOf(TStringItem),
  1652. (Item2 - ListStartPtr) div SizeOf(TStringItem));
  1653. end;
  1654. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare; SortingAlgorithm: PSortingAlgorithm);
  1655. var
  1656. Context: TStringList_CustomSort_Context;
  1657. begin
  1658. If (FCount>1) and (FForceSort or (FSortStyle<>sslAuto)) then
  1659. begin
  1660. Changing;
  1661. Context.List := Self;
  1662. Context.ListStartPtr := FList;
  1663. Context.CompareFn := CompareFn;
  1664. //if ExchangeItems is overriden call that, else call (faster) ItemListSorter_ContextComparer
  1665. if TMethod(@Self.ExchangeItems).Code = CodePointer(@TStringList.ExchangeItems) then
  1666. SortingAlgorithm^.ItemListSorter_ContextComparer(
  1667. FList, FCount, SizeOf(TStringItem), @TStringList_CustomSort_Comparer,
  1668. @Context)
  1669. else
  1670. SortingAlgorithm^.ItemListSorter_CustomItemExchanger_ContextComparer(
  1671. FList, FCount, SizeOf(TStringItem), @TStringList_CustomSort_Comparer,
  1672. @TStringList_CustomSort_Exchanger, @Context);
  1673. Changed;
  1674. end;
  1675. end;
  1676. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1677. begin
  1678. Result := List.DoCompareText(List.FList^[Index1].FString,
  1679. List.FList^[Index].FString);
  1680. end;
  1681. procedure TStringList.Sort;
  1682. begin
  1683. CustomSort(@StringListAnsiCompare);
  1684. end;
  1685. procedure TStringList.Sort(SortingAlgorithm: PSortingAlgorithm);
  1686. begin
  1687. CustomSort(@StringListAnsiCompare, SortingAlgorithm);
  1688. end;
  1689. {$else}
  1690. { generics based implementation of TStringList follows }
  1691. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1692. begin
  1693. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1694. end;
  1695. constructor TStringList.Create;
  1696. begin
  1697. inherited;
  1698. FOwnsObjects:=false;
  1699. FMap := TFPStrObjMap.Create;
  1700. FMap.OnPtrCompare := @MapPtrCompare;
  1701. FOnCompareText := @DefaultCompareText;
  1702. NameValueSeparator:='=';
  1703. CheckSpecialChars;
  1704. end;
  1705. destructor TStringList.Destroy;
  1706. begin
  1707. FMap.Free;
  1708. inherited;
  1709. end;
  1710. function TStringList.GetDuplicates: TDuplicates;
  1711. begin
  1712. Result := FMap.Duplicates;
  1713. end;
  1714. function TStringList.GetSorted: boolean;
  1715. begin
  1716. Result := FMap.Sorted;
  1717. end;
  1718. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1719. begin
  1720. FMap.Duplicates := NewDuplicates;
  1721. end;
  1722. procedure TStringList.SetSorted(NewSorted: Boolean);
  1723. begin
  1724. FMap.Sorted := NewSorted;
  1725. end;
  1726. procedure TStringList.Changed;
  1727. begin
  1728. if FUpdateCount = 0 then
  1729. if Assigned(FOnChange) then
  1730. FOnChange(Self);
  1731. end;
  1732. procedure TStringList.Changing;
  1733. begin
  1734. if FUpdateCount = 0 then
  1735. if Assigned(FOnChanging) then
  1736. FOnChanging(Self);
  1737. end;
  1738. function TStringList.Get(Index: Integer): string;
  1739. begin
  1740. Result := FMap.Keys[Index];
  1741. end;
  1742. function TStringList.GetCapacity: Integer;
  1743. begin
  1744. Result := FMap.Capacity;
  1745. end;
  1746. function TStringList.GetCount: Integer;
  1747. begin
  1748. Result := FMap.Count;
  1749. end;
  1750. function TStringList.GetObject(Index: Integer): TObject;
  1751. begin
  1752. Result := FMap.Data[Index];
  1753. end;
  1754. procedure TStringList.Put(Index: Integer; const S: string);
  1755. begin
  1756. Changing;
  1757. FMap.Keys[Index] := S;
  1758. Changed;
  1759. end;
  1760. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1761. begin
  1762. Changing;
  1763. FMap.Data[Index] := AObject;
  1764. Changed;
  1765. end;
  1766. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1767. begin
  1768. FMap.Capacity := NewCapacity;
  1769. end;
  1770. procedure TStringList.SetUpdateState(Updating: Boolean);
  1771. begin
  1772. if Updating then
  1773. Changing
  1774. else
  1775. Changed
  1776. end;
  1777. function TStringList.Add(const S: string): Integer;
  1778. begin
  1779. Result := FMap.Add(S);
  1780. end;
  1781. procedure TStringList.Clear;
  1782. begin
  1783. if FMap.Count = 0 then exit;
  1784. Changing;
  1785. FMap.Clear;
  1786. Changed;
  1787. end;
  1788. procedure TStringList.Delete(Index: Integer);
  1789. begin
  1790. if (Index < 0) or (Index >= FMap.Count) then
  1791. Error(SListIndexError, Index);
  1792. Changing;
  1793. FMap.Delete(Index);
  1794. Changed;
  1795. end;
  1796. procedure TStringList.Exchange(Index1, Index2: Integer);
  1797. begin
  1798. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1799. Error(SListIndexError, Index1);
  1800. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1801. Error(SListIndexError, Index2);
  1802. Changing;
  1803. FMap.InternalExchange(Index1, Index2);
  1804. Changed;
  1805. end;
  1806. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1807. begin
  1808. if NewSensitive <> FCaseSensitive then
  1809. begin
  1810. FCaseSensitive := NewSensitive;
  1811. if Sorted then
  1812. Sort;
  1813. end;
  1814. end;
  1815. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1816. begin
  1817. Result := FOnCompareText(string(Key1^), string(Key2^));
  1818. end;
  1819. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1820. begin
  1821. if FCaseSensitive then
  1822. Result := AnsiCompareStr(s1, s2)
  1823. else
  1824. Result := AnsiCompareText(s1, s2);
  1825. end;
  1826. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1827. begin
  1828. Result := FOnCompareText(s1, s2);
  1829. end;
  1830. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1831. begin
  1832. Result := FMap.Find(S, Index);
  1833. end;
  1834. function TStringList.IndexOf(const S: string): Integer;
  1835. begin
  1836. Result := FMap.IndexOf(S);
  1837. end;
  1838. procedure TStringList.Insert(Index: Integer; const S: string);
  1839. begin
  1840. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1841. Changing;
  1842. FMap.InsertKey(Index, S);
  1843. Changed;
  1844. end;
  1845. type
  1846. PStringList_CustomSort_Context = ^TStringList_CustomSort_Context;
  1847. TStringList_CustomSort_Context = record
  1848. List: TStringList;
  1849. ListStartPtr: Pointer;
  1850. ItemSize: SizeUInt;
  1851. IndexBase: Integer;
  1852. CompareFn: TStringListSortCompare;
  1853. end;
  1854. function TStringList_CustomSort_Comparer(Item1, Item2, Context: Pointer): Integer;
  1855. begin
  1856. with PStringList_CustomSort_Context(Context)^ do
  1857. Result := CompareFn(List,
  1858. ((Item1 - ListStartPtr) div ItemSize) + IndexBase,
  1859. ((Item2 - ListStartPtr) div ItemSize) + IndexBase);
  1860. end;
  1861. procedure TStringList_CustomSort_Exchanger(Item1, Item2, Context: Pointer);
  1862. begin
  1863. with PStringList_CustomSort_Context(Context)^ do
  1864. List.Exchange(((Item1 - ListStartPtr) div ItemSize) + IndexBase,
  1865. ((Item2 - ListStartPtr) div ItemSize) + IndexBase);
  1866. end;
  1867. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1868. var
  1869. Context: TStringList_CustomSort_Context;
  1870. begin
  1871. if L > R then
  1872. exit;
  1873. Context.List := Self;
  1874. Context.ListStartPtr := FMap.Items[L];
  1875. Context.CompareFn := CompareFn;
  1876. Context.ItemSize := FMap.KeySize + FMap.DataSize;
  1877. Context.IndexBase := L;
  1878. DefaultSortingAlgorithm^.ItemListSorter_CustomItemExchanger_ContextComparer(
  1879. Context.ListStartPtr, R - L + 1, Context.ItemSize, @TStringList_CustomSort_Comparer,
  1880. @TStringList_CustomSort_Exchanger, @Context);
  1881. end;
  1882. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1883. begin
  1884. if not Sorted and (FMap.Count > 1) then
  1885. begin
  1886. Changing;
  1887. QuickSort(0, FMap.Count-1, CompareFn);
  1888. Changed;
  1889. end;
  1890. end;
  1891. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare; SortingAlgorithm: PSortingAlgorithm);
  1892. var
  1893. Context: TStringList_CustomSort_Context;
  1894. begin
  1895. if not Sorted and (FMap.Count > 1) then
  1896. begin
  1897. Changing;
  1898. Context.List := Self;
  1899. Context.ListStartPtr := FMap.Items[0];
  1900. Context.CompareFn := CompareFn;
  1901. Context.ItemSize := FMap.KeySize + FMap.DataSize;
  1902. Context.IndexBase := 0;
  1903. SortingAlgorithm^.ItemListSorter_CustomItemExchanger_ContextComparer(
  1904. Context.ListStartPtr, FMap.Count, Context.ItemSize, @TStringList_CustomSort_Comparer,
  1905. @TStringList_CustomSort_Exchanger, @Context);
  1906. Changed;
  1907. end;
  1908. end;
  1909. procedure TStringList.Sort;
  1910. begin
  1911. if not Sorted and (FMap.Count > 1) then
  1912. begin
  1913. Changing;
  1914. FMap.Sort;
  1915. Changed;
  1916. end;
  1917. end;
  1918. procedure TStringList.Sort(SortingAlgorithm: PSortingAlgorithm);
  1919. begin
  1920. if not Sorted and (FMap.Count > 1) then
  1921. begin
  1922. Changing;
  1923. FMap.Sort(SortingAlgorithm);
  1924. Changed;
  1925. end;
  1926. end;
  1927. {$endif}