1 {
2 /***************************************************************************
3                              inputhistory.pas
4                              ----------------
5 
6  ***************************************************************************/
7 
8  ***************************************************************************
9  *                                                                         *
10  *   This source is free software; you can redistribute it and/or modify   *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  *   This code is distributed in the hope that it will be useful, but      *
16  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
18  *   General Public License for more details.                              *
19  *                                                                         *
20  *   A copy of the GNU General Public License is available on the World    *
21  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
22  *   obtain it by writing to the Free Software Foundation,                 *
23  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
24  *                                                                         *
25  ***************************************************************************
26 
27   Author: Mattias Gaertner
28 
29   Abstract:
30     History lists for strings and file names.
31 }
32 unit InputHistory;
33 
34 {$mode objfpc}{$H+}
35 
36 interface
37 
38 uses
39   // RTL + LCL
40   Classes, SysUtils, Laz_AVL_Tree,
41   // LCL
42   Dialogs,
43   // LazUtils
44   LazFileCache, LazFileUtils, LazUTF8, AvgLvlTree, Laz2_XMLCfg,
45   // Codetools
46   FileProcs,
47   // IdeIntf
48   ProjectIntf, IDEDialogs,
49   // IDE
50   DiffPatch, LazConf, IDEProcs;
51 
52 {$ifdef Windows}
53 {$define CaseInsensitiveFilenames}
54 {$endif}
55 
56 const
57   // these are the names of the various history lists in the IDE:
58   hlPublishModuleDestDirs = 'PublishModuleDestinationDirectories';
59   hlPublishModuleFileFilter = 'PublishModuleFileFilter';
60   hlMakeResourceStringSections = 'MakeResourceStringSections';
61   hlMakeResourceStringPrefixes = 'MakeResourceStringPrefixes';
62   hlMakeResourceStringLengths = 'MakeResourceStringLengths';
63   hlCodeToolsDirectories = 'CodeToolsDirectories';
64   hlCompilerOptsImExport = 'CompilerOptsImExport';
65   hlCleanBuildFileMask = 'CleanBuildFileMask';
66 
67 type
68   TFileDialogSettings = record
69     InitialDir: string;
70     Width: integer;
71     Height: integer;
72     HistoryList: TStringList;
73     MaxHistory: integer;
74   end;
75 
76 
77   { THistoryList - a TStringList to store a history list }
78 
79   THistoryList = class(TStringList)
80   private
81     FListType: TRecentListType;
82     FMaxCount: integer;
83     FName: string;
84     procedure SetMaxCount(const AValue: integer);
85     procedure SetName(const AValue: string);
86   public
87     constructor Create(TheListType: TRecentListType);
88     destructor Destroy;  override;
Pushnull89     function Push(const Entry: string): integer;
90     procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
91     procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
92     procedure AppendEntry(const Entry: string);
IndexOfnull93     function IndexOf(const S: string): Integer; override;
94   public
95     property Name: string read FName write SetName;
96     property MaxCount: integer read FMaxCount write SetMaxCount;
97     property ListType: TRecentListType read FListType;
98   end;
99 
100 
101   { THistoryLists - list of THistoryList }
102 
103   THistoryLists = class
104   private
105     FItems: TList;
GetItemsnull106     function GetItems(Index: integer): THistoryList;
GetXMLListPathnull107     function GetXMLListPath(const Path: string; i: integer; ALegacyList: Boolean): string;
108   public
109     constructor Create;
110     destructor Destroy;  override;
111     procedure Clear;
Countnull112     function Count: integer;
113     procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
114     procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string; const ALegacyList: Boolean);
IndexOfNamenull115     function IndexOfName(const Name: string): integer;
GetListnull116     function GetList(const Name: string;
117       CreateIfNotExists: boolean; ListType: TRecentListType): THistoryList;
118     procedure Add(const ListName, Entry: string; ListType: TRecentListType);
119     property Items[Index: integer]: THistoryList read GetItems;
120   end;
121 
122 
123   { TInputHistories }
124 
125   TLazFindInFileSearchOption = (
126     fifMatchCase,
127     fifWholeWord,
128     fifRegExpr,
129     fifMultiLine,
130     fifSearchProject,    // search in all project files
131     fifSearchProjectGroup,// search in all files of project group
132     fifSearchOpen,       // search in all open files in editor
133     fifSearchActive,     // search in active open file in editor
134     fifSearchDirectories,// search in directories
135     fifIncludeSubDirs,
136     fifReplace,   // replace and ask user before each replace
137     fifReplaceAll // replace without asking user
138     );
139   TLazFindInFileSearchOptions = set of TLazFindInFileSearchOption;
140 
141 
142   { TIHIgnoreIDEQuestionList }
143 
144   TIHIgnoreIDEQuestionList = class(TIgnoreIDEQuestionList)
145   private
146     FItems: TAvlTree; // tree of TIgnoreIDEQuestionItem
FindNodenull147     function FindNode(const Identifier: string): TAvlTreeNode;
148   public
149     constructor Create;
150     destructor Destroy; override;
151     procedure Clear;
Addnull152     function Add(const Identifier: string;
153                  const Duration: TIgnoreQuestionDuration;
154                  const Flag: string = ''): TIgnoreIDEQuestionItem; override;
155     procedure Delete(const Identifier: string); override;
Findnull156     function Find(const Identifier: string): TIgnoreIDEQuestionItem; override;
157     procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
158     procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
159   end;
160 
161   TInputHistories = class
162   private
163     FCleanOutputFileMask: string;
164     FCleanSourcesFileMask: string;
165     FDiffFlags: TTextDiffFlags;
166     FDiffText2: string;
167     FDiffText2OnlySelection: boolean;
168     FFileDialogSettings: TFileDialogSettings;
169     FFilename: string;
170 
171     // Find- and replace-history
172     FFindHistory: TStringList;
173     FFindInFilesSearchOptions: TLazFindInFileSearchOptions;
174     FFindAutoComplete: boolean;
175     FIgnores: TIHIgnoreIDEQuestionList;
176     FLastConvertDelphiPackage: string;
177     FLastConvertDelphiProject: string;
178     FLastConvertDelphiUnit: string;
179     FViewNeedBuildTarget: string;
180     FNewFileType: string;
181     FNewProjectType: string;
182     FReplaceHistory: TStringList;
183     FFindInFilesPathHistory: TStringList;
184     FFindInFilesMaskHistory: TStringList;
185     FMaxFindHistory: Integer;
186 
187     // various history lists
188     FHistoryLists: THistoryLists;
189     // file encodings
190     fFileEncodings: TStringToStringTree;
191 
192     procedure SetFilename(const AValue: string);
193   protected
194     procedure LoadSearchOptions(XMLConfig: TXMLConfig; const Path: string); virtual; abstract;
195     procedure SaveSearchOptions(XMLConfig: TXMLConfig; const Path: string); virtual; abstract;
196   public
197     constructor Create;
198     destructor Destroy;  override;
199     procedure Clear;
200     procedure Load;
201     procedure Save;
202     procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
203     procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
204     procedure SetLazarusDefaultFilename;
205 
206     // Find- and replace-history
AddToFindHistorynull207     function AddToFindHistory(const AFindStr: string): boolean;
AddToReplaceHistorynull208     function AddToReplaceHistory(const AReplaceStr: String): boolean;
AddToFindInFilesPathHistorynull209     function AddToFindInFilesPathHistory(const APathStr: String): boolean;
AddToFindInFilesMaskHistorynull210     function AddToFindInFilesMaskHistory(const AMaskStr: String): boolean;
211 
212     // filedialog
213     procedure ApplyFileDialogSettings(DestDialog: TFileDialog);
214     procedure StoreFileDialogSettings(SourceDialog: TFileDialog);
215     procedure SetFileDialogSettingsInitialDir(const InitialDir: string);
SelectDirectorynull216     function SelectDirectory(const {%H-}Title: string;
217                              MustExist: boolean = true;
218                              const InitialDir: string = '';
219                              const Directory: string = ''): string;
220   public
221     property Filename: string read FFilename write SetFilename;
222 
223     // Find- and replace-history
224     property MaxFindHistory: Integer read FMaxFindHistory write FMaxFindHistory;
225     property FindHistory: TStringList read FFindHistory write FFindHistory;
226     property ReplaceHistory: TStringList read FReplaceHistory write FReplaceHistory;
227     property FindInFilesPathHistory: TStringList read FFindInFilesPathHistory
228                                                  write FFindInFilesPathHistory;
229     property FindInFilesMaskHistory: TStringList read FFindInFilesMaskHistory
230                                                  write FFindInFilesMaskHistory;
231     property FindInFilesSearchOptions: TLazFindInFileSearchOptions
232                read FFindInFilesSearchOptions write FFindInFilesSearchOptions;
233     property FindAutoComplete: boolean read FFindAutoComplete
234                                        write FFindAutoComplete;
235 
236     // filedialogs
237     property FileDialogSettings: TFileDialogSettings
238       read FFileDialogSettings write FFileDialogSettings;
239     property CleanOutputFileMask: string read FCleanOutputFileMask write FCleanOutputFileMask;
240     property CleanSourcesFileMask: string read FCleanSourcesFileMask write FCleanSourcesFileMask;
241 
242     // various history lists
243     property HistoryLists: THistoryLists read FHistoryLists;
244 
245     // diff dialog
246     property DiffFlags: TTextDiffFlags read FDiffFlags write FDiffFlags;
247     property DiffText2: string read FDiffText2 write FDiffText2;
248     property DiffText2OnlySelection: boolean read FDiffText2OnlySelection
249                                              write FDiffText2OnlySelection;
250     // new dialog
251     property NewProjectType: string read FNewProjectType write FNewProjectType;
252     property NewFileType: string read FNewFileType write FNewFileType;
253 
254     // Delphi conversion
255     property LastConvertDelphiProject: string read FLastConvertDelphiProject
256                                               write FLastConvertDelphiProject;
257     property LastConvertDelphiPackage: string read FLastConvertDelphiPackage
258                                               write FLastConvertDelphiPackage;
259     property LastConvertDelphiUnit: string read FLastConvertDelphiUnit
260                                            write FLastConvertDelphiUnit;
261 
262     // View / internals
263     property ViewNeedBuildTarget: string read FViewNeedBuildTarget
264                                          write FViewNeedBuildTarget;
265 
266     // file encodings
267     property FileEncodings: TStringToStringTree read fFileEncodings write fFileEncodings;
268 
269     // ignores
270     property Ignores: TIHIgnoreIDEQuestionList read FIgnores;
271   end;
272 
273 const
274   LazFindInFileSearchOptionsDefault = [fifSearchOpen, fifIncludeSubDirs];
275   LazFindInFileSearchOptionNames: array[TLazFindInFileSearchOption] of string =(
276     'MatchCase',
277     'WholeWord',
278     'RegExpr',
279     'MultiLine',
280     'SearchProject',
281     'SearchProjectGroup',
282     'SearchOpen',
283     'SearchCurrent',
284     'SearchDirectories',
285     'IncludeSubDirs',
286     'Replace',
287     'ReplaceAll'
288     );
289   IHIgnoreItemDurationNames: array[TIgnoreQuestionDuration] of string = (
290     'IDERestart',
291     '24H',
292     'Forever'
293     );
294 
295 var
296   InputHistories: TInputHistories = nil;
297 
CompareIHIgnoreItemsnull298 function CompareIHIgnoreItems(Item1, Item2: Pointer): integer;
CompareAnsiStringWithIHIgnoreItemnull299 function CompareAnsiStringWithIHIgnoreItem(AString, Item: Pointer): integer;
300 
NameToIHIgnoreItemDurationnull301 function NameToIHIgnoreItemDuration(const s: string): TIgnoreQuestionDuration;
302 
303 
304 implementation
305 
306 
307 const
308   DefaultHistoryFile = 'inputhistory.xml';
309   InputHistoryVersion = 1;
310   DefaultDiffFlags = [tdfIgnoreCase,tdfIgnoreEmptyLineChanges,
311                       tdfIgnoreLineEnds,tdfIgnoreTrailingSpaces];
312 
CompareIHIgnoreItemsnull313 function CompareIHIgnoreItems(Item1, Item2: Pointer): integer;
314 var
315   IgnoreItem1: TIgnoreIDEQuestionItem absolute Item1;
316   IgnoreItem2: TIgnoreIDEQuestionItem absolute Item2;
317 begin
318   Result:=SysUtils.CompareText(IgnoreItem1.Identifier,IgnoreItem2.Identifier);
319 end;
320 
CompareAnsiStringWithIHIgnoreItemnull321 function CompareAnsiStringWithIHIgnoreItem(AString, Item: Pointer): integer;
322 var
323   IgnoreItem: TIgnoreIDEQuestionItem absolute Item;
324 begin
325   Result:=SysUtils.CompareText(AnsiString(AString),IgnoreItem.Identifier);
326 end;
327 
NameToIHIgnoreItemDurationnull328 function NameToIHIgnoreItemDuration(const s: string): TIgnoreQuestionDuration;
329 begin
330   for Result:=low(TIgnoreQuestionDuration) to high(TIgnoreQuestionDuration) do
331     if SysUtils.CompareText(IHIgnoreItemDurationNames[Result],s)=0 then exit;
332   Result:=iiidIDERestart;
333 end;
334 
335 { TInputHistories }
336 
337 procedure TInputHistories.SetFilename(const AValue: string);
338 begin
339   FFilename:=AValue;
340 end;
341 
342 constructor TInputHistories.Create;
343 begin
344   inherited Create;
345   FFilename:='';
346 
347   // Find- and replace-history
348   FMaxFindHistory:=20;
349   FFindAutoComplete:=true;
350   FFindHistory:=TStringList.Create;
351   FReplaceHistory:=TStringList.Create;
352   FFindInFilesPathHistory:=TStringList.Create;
353   FFindInFilesMaskHistory:=TStringList.Create;
354   FFindInFilesSearchOptions:=LazFindInFileSearchOptionsDefault;
355 
356   // file dialog
357   FFileDialogSettings.HistoryList:=TStringList.Create;
358   FFileDialogSettings.MaxHistory:=20;
359 
360   // various history lists
361   FHistoryLists:=THistoryLists.Create;
362   fFileEncodings:=TStringToStringTree.Create({$IFDEF CaseInsensitiveFilenames}false{$ELSE}true{$ENDIF});
363   FIgnores:=TIHIgnoreIDEQuestionList.Create;
364   IgnoreQuestions:=FIgnores;
365 
366   Clear;
367 end;
368 
369 destructor TInputHistories.Destroy;
370 begin
371   IgnoreQuestions:=nil;
372   FreeAndNil(FIgnores);
373   FreeAndNil(FHistoryLists);
374   FreeAndNil(FFileDialogSettings.HistoryList);
375   FreeAndNil(FFindHistory);
376   FreeAndNil(FReplaceHistory);
377   FreeAndNil(FFindInFilesPathHistory);
378   FreeAndNil(FFindInFilesMaskHistory);
379   FreeAndNil(fFileEncodings);
380   inherited Destroy;
381 end;
382 
383 procedure TInputHistories.Clear;
384 begin
385   FHistoryLists.Clear;
386   FFindHistory.Clear;
387   FReplaceHistory.Clear;
388   FFindInFilesPathHistory.Clear;
389   FFindInFilesMaskHistory.Clear;
390   with FFileDialogSettings do begin
391     HistoryList.Clear;
392     Width:=0;
393     Height:=0;
394     InitialDir:='';
395   end;
396   FDiffFlags:=DefaultDiffFlags;
397   FDiffText2:='';
398   FDiffText2OnlySelection:=false;
399   FNewProjectType:='';
400   FNewFileType:='';
401   FLastConvertDelphiProject:='';
402   FLastConvertDelphiUnit:='';
403   FCleanOutputFileMask:=DefaultProjectCleanOutputFileMask;
404   FCleanSourcesFileMask:=DefaultProjectCleanSourcesFileMask;
405   fFileEncodings.Clear;
406   FIgnores.Clear;
407 end;
408 
409 procedure TInputHistories.LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
410 var
411   DiffFlag: TTextDiffFlag;
412   FIFOption: TLazFindInFileSearchOption;
413 begin
414   // Find- and replace-history
415   FMaxFindHistory:=XMLConfig.GetValue(Path+'Find/History/Max',FMaxFindHistory);
416   FFindAutoComplete:=XMLConfig.GetValue(Path+'Find/AutoComplete/Value',FFindAutoComplete);
417   LoadRecentList(XMLConfig,FFindHistory,Path+'Find/History/Find/',rltCaseSensitive);
418   LoadRecentList(XMLConfig,FReplaceHistory,Path+'Find/History/Replace/',rltCaseSensitive);
419   LoadRecentList(XMLConfig,FFindInFilesPathHistory,Path+
420                                           'FindInFiles/History/Paths/',rltFile);
421   LoadRecentList(XMLConfig,FFindInFilesMaskHistory,Path+
422                                           'FindInFiles/History/Masks/',rltFile);
423   FFindInFilesSearchOptions:=[];
424   for FIFOption:=Low(TLazFindInFileSearchOption) to High(TLazFindInFileSearchOption)
425   do begin
426     if XMLConfig.GetValue(
427       Path+'FindInFiles/Options/'+LazFindInFileSearchOptionNames[FIFOption],
428       FIFOption in LazFindInFileSearchOptionsDefault)
429     then
430       Include(FFindInFilesSearchOptions,FIFOption);
431   end;
432   LoadSearchOptions(XMLConfig, Path); // Search Options depend on SynEdit.
433 
434   // file dialog
435   with FFileDialogSettings do begin
436     Width:=XMLConfig.GetValue(Path+'FileDialog/Width',0);
437     Height:=XMLConfig.GetValue(Path+'FileDialog/Height',0);
438     InitialDir:=XMLConfig.GetValue(Path+'FileDialog/InitialDir','');
439     MaxHistory:=XMLConfig.GetValue(Path+'FileDialog/MaxHistory',20);
440     LoadRecentList(XMLConfig,HistoryList,Path+'FileDialog/HistoryList/',rltFile);
441   end;
442   FCleanOutputFileMask:=XMLConfig.GetValue(Path+'Clean/OutputFilemask',
443                                            DefaultProjectCleanOutputFileMask);
444   FCleanSourcesFileMask:=XMLConfig.GetValue(Path+'Clean/SourcesFilemask',
445                                            DefaultProjectCleanSourcesFileMask);
446   // history lists
447   FHistoryLists.LoadFromXMLConfig(XMLConfig,Path+'HistoryLists/');
448   // diff dialog
449   FDiffFlags:=[];
450   for DiffFlag:=Low(TTextDiffFlag) to High(TTextDiffFlag) do begin
451     if XMLConfig.GetValue(
452       Path+'DiffDialog/Options/'+TextDiffFlagNames[DiffFlag],
453       DiffFlag in DefaultDiffFlags)
454     then
455       Include(FDiffFlags,DiffFlag);
456   end;
457   FDiffText2:=XMLConfig.GetValue(Path+'DiffDialog/Text2/Name','');
458   FDiffText2OnlySelection:=
459     XMLConfig.GetValue(Path+'DiffDialog/Text2/OnlySelection',false);
460 
461   // new items
462   FNewProjectType:=XMLConfig.GetValue(Path+'New/Project/Type','');
463   FNewFileType:=XMLConfig.GetValue(Path+'New/File/Type','');
464 
465   // delphi conversion
466   FLastConvertDelphiProject:=XMLConfig.GetValue(Path+'Conversion/Delphi/Project','');
467   FLastConvertDelphiPackage:=XMLConfig.GetValue(Path+'Conversion/Delphi/Package','');
468   FLastConvertDelphiUnit:=XMLConfig.GetValue(Path+'Conversion/Delphi/Unit','');
469 
470   // view internals
471   ViewNeedBuildTarget:=XMLConfig.GetValue(Path+'View/NeedBuild/Target','');
472 
473   // encodings
474   LoadStringToStringTree(XMLConfig,fFileEncodings,Path+'FileEncodings/');
475 
476   Ignores.LoadFromXMLConfig(XMLConfig,Path+'Ignores/');
477 end;
478 
479 procedure TInputHistories.SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
480 var
481   DiffFlag: TTextDiffFlag;
482   FIFOption: TLazFindInFileSearchOption;
483 begin
484   // Find- and replace-history
485   XMLConfig.SetDeleteValue(Path+'Find/History/Max',FMaxFindHistory,20);
486   XMLConfig.SetDeleteValue(Path+'Find/AutoComplete/Value',FFindAutoComplete,true);
487   SaveRecentList(XMLConfig,FFindHistory,Path+'Find/History/Find/');
488   SaveRecentList(XMLConfig,FReplaceHistory,Path+'Find/History/Replace/');
489   SaveRecentList(XMLConfig,FFindInFilesPathHistory,Path+
490                                             'FindInFiles/History/Paths/');
491   SaveRecentList(XMLConfig,FFindInFilesMaskHistory,Path+
492                                             'FindInFiles/History/Masks/');
493   for FIFOption:=Low(TLazFindInFileSearchOption)
494   to High(TLazFindInFileSearchOption) do begin
495     XMLConfig.SetDeleteValue(
496       Path+'FindInFiles/Options/'+LazFindInFileSearchOptionNames[FIFOption],
497       FIFOption in FindInFilesSearchOptions,
498       FIFOption in LazFindInFileSearchOptionsDefault);
499   end;
500   SaveSearchOptions(XMLConfig, Path); // Search Options depend on SynEdit.
501 
502   // file dialog
503   with FFileDialogSettings do begin
504     XMLConfig.SetDeleteValue(Path+'FileDialog/Width',Width,0);
505     XMLConfig.SetDeleteValue(Path+'FileDialog/Height',Height,0);
506     XMLConfig.SetDeleteValue(Path+'FileDialog/InitialDir',InitialDir,'');
507     XMLConfig.SetDeleteValue(Path+'FileDialog/MaxHistory',MaxHistory,20);
508     SaveRecentList(XMLConfig,HistoryList,Path+'FileDialog/HistoryList/');
509   end;
510   XMLConfig.SetDeleteValue(Path+'Clean/OutputFilemask',FCleanOutputFileMask,
511                                            DefaultProjectCleanOutputFileMask);
512   XMLConfig.SetDeleteValue(Path+'Clean/SourcesFilemask',FCleanSourcesFileMask,
513                                            DefaultProjectCleanSourcesFileMask);
514   // history lists
515   FHistoryLists.SaveToXMLConfig(XMLConfig,Path+'HistoryLists/',True);
516   // diff dialog
517   for DiffFlag:=Low(TTextDiffFlag) to High(TTextDiffFlag) do begin
518     XMLConfig.SetDeleteValue(
519       Path+'DiffDialog/Options/'+TextDiffFlagNames[DiffFlag],
520       DiffFlag in DiffFlags,DiffFlag in DefaultDiffFlags);
521   end;
522   XMLConfig.SetDeleteValue(Path+'DiffDialog/Text2/Name',FDiffText2,'');
523   XMLConfig.SetDeleteValue(Path+'DiffDialog/Text2/OnlySelection',
524                            FDiffText2OnlySelection,false);
525 
526   // new items
527   XMLConfig.SetDeleteValue(Path+'New/Project/Type',FNewProjectType,'');
528   XMLConfig.SetDeleteValue(Path+'New/File/Type',FNewFileType,'');
529 
530   // delphi conversion
531   XMLConfig.SetDeleteValue(Path+'Conversion/Delphi/Project',
532                            FLastConvertDelphiProject,'');
533   XMLConfig.SetDeleteValue(Path+'Conversion/Delphi/Package',
534                            FLastConvertDelphiPackage,'');
535   XMLConfig.SetDeleteValue(Path+'Conversion/Delphi/Unit',
536                            FLastConvertDelphiUnit,'');
537 
538   // view internals
539   XMLConfig.SetDeleteValue(Path+'View/NeedBuild/Target',ViewNeedBuildTarget,'');
540 
541   // encodings
542   SaveStringToStringTree(XMLConfig,fFileEncodings,Path+'FileEncodings/');
543 
544   Ignores.SaveToXMLConfig(XMLConfig,Path+'Ignores/');
545 end;
546 
547 procedure TInputHistories.SetLazarusDefaultFilename;
548 var
549   ConfFileName: string;
550 begin
551   ConfFileName:=AppendPathDelim(GetPrimaryConfigPath)+DefaultHistoryFile;
552   CopySecondaryConfigFile(DefaultHistoryFile);
553   FFilename:=ConfFilename;
554 end;
555 
556 procedure TInputHistories.Load;
557 var
558   XMLConfig: TXMLConfig;
559   //FileVersion: integer;
560 begin
561   try
562     XMLConfig:=TXMLConfig.Create(FFileName);
563     //FileVersion:=XMLConfig.GetValue('InputHistory/Version/Value',0);
564     LoadFromXMLConfig(XMLConfig,'InputHistory/');
565     XMLConfig.Free;
566   except
567     on E: Exception do begin
568       DebugLn('[TCodeToolsOptions.Load]  error reading "',FFilename,'" ',E.Message);
569     end;
570   end;
571 end;
572 
573 procedure TInputHistories.Save;
574 var
575   XMLConfig: TXMLConfig;
576 begin
577   try
578     InvalidateFileStateCache;
579     XMLConfig:=TXMLConfig.CreateClean(FFileName);
580     XMLConfig.SetDeleteValue('InputHistory/Version/Value',InputHistoryVersion,0);
581     SaveToXMLConfig(XMLConfig,'InputHistory/');
582     XMLConfig.Flush;
583     XMLConfig.Free;
584   except
585     on E: Exception do begin
586       DebugLn('[TInputHistories.Save]  error writing "',FFilename,'" ',E.Message);
587     end;
588   end;
589 end;
590 
TInputHistories.AddToFindHistorynull591 function TInputHistories.AddToFindHistory(const AFindStr: string): boolean;
592 begin
593   Result:=AddToRecentList(AFindStr,FFindHistory,FMaxFindHistory,rltCaseSensitive);
594 end;
595 
TInputHistories.AddToReplaceHistorynull596 function TInputHistories.AddToReplaceHistory(const AReplaceStr: String): boolean;
597 begin
598   Result:=AddToRecentList(AReplaceStr,FReplaceHistory,FMaxFindHistory,rltCaseSensitive);
599 end;
600 
TInputHistories.AddToFindInFilesPathHistorynull601 function TInputHistories.AddToFindInFilesPathHistory(const APathStr: String): boolean;
602 begin
603   Result:= AddToRecentList(APathStr,FFindInFilesPathHistory,FMaxFindHistory,rltFile);
604 end;
605 
AddToFindInFilesMaskHistorynull606 function TInputHistories.AddToFindInFilesMaskHistory(const AMaskStr: String): boolean;
607 begin
608   Result:= AddToRecentList(AMaskStr,FFindInFilesMaskHistory,FMaxFindHistory,rltFile);
609 end;
610 
611 procedure TInputHistories.ApplyFileDialogSettings(DestDialog: TFileDialog);
612 begin
613   DestDialog.InitialDir:=FFileDialogSettings.InitialDir;
614   DestDialog.Width:=FFileDialogSettings.Width;
615   DestDialog.Height:=FFileDialogSettings.Height;
616 
617   DestDialog.HistoryList:=FFileDialogSettings.HistoryList;
618 end;
619 
620 procedure TInputHistories.StoreFileDialogSettings(SourceDialog: TFileDialog);
621 var s: string;
622 begin
623   FFileDialogSettings.InitialDir:=SourceDialog.InitialDir;
624   FFileDialogSettings.Width:=SourceDialog.Width;
625   FFileDialogSettings.Height:=SourceDialog.Height;
626   s:=ExtractFilePath(FFileDialogSettings.InitialDir);
627   if s<>'' then
628     AddToRecentList(s,FFileDialogSettings.HistoryList,
629                     FFileDialogSettings.MaxHistory,rltFile);
630 end;
631 
632 procedure TInputHistories.SetFileDialogSettingsInitialDir(const InitialDir: string);
633 begin
634   FFileDialogSettings.InitialDir := InitialDir;
635 end;
636 
TInputHistories.SelectDirectorynull637 function TInputHistories.SelectDirectory(const Title: string;
638   MustExist: boolean; const InitialDir: string; const Directory: string): string;
639 var
640   WorkDirectoryDialog: TSelectDirectoryDialog;
641 begin
642   Result:='';
643   WorkDirectoryDialog := TSelectDirectoryDialog.Create(nil);
644   try
645     ApplyFileDialogSettings(WorkDirectoryDialog);
646     if MustExist then
647       WorkDirectoryDialog.Options:=WorkDirectoryDialog.Options+[ofFileMustExist];
648     if InitialDir <> '' then
649       WorkDirectoryDialog.InitialDir := InitialDir;
650     if Directory<>'' then
651       WorkDirectoryDialog.Filename := Directory;
652     if WorkDirectoryDialog.Execute then begin
653       Result := WorkDirectoryDialog.Filename;
654     end;
655     StoreFileDialogSettings(WorkDirectoryDialog);
656   finally
657     WorkDirectoryDialog.Free;
658   end;
659 end;
660 
661 { THistoryList }
662 
663 procedure THistoryList.SetMaxCount(const AValue: integer);
664 begin
665   if FMaxCount=AValue then exit;
666   FMaxCount:=AValue;
667 end;
668 
669 procedure THistoryList.SetName(const AValue: string);
670 begin
671   if FName=AValue then exit;
672   FName:=AValue;
673 end;
674 
675 constructor THistoryList.Create(TheListType: TRecentListType);
676 begin
677   FListType:=TheListType;
678   FMaxCount:=20;
679 end;
680 
681 destructor THistoryList.Destroy;
682 begin
683   inherited Destroy;
684 end;
685 
THistoryList.Pushnull686 function THistoryList.Push(const Entry: string): integer;
687 begin
688   if Entry<>'' then
689     AddToRecentList(Entry,Self,MaxCount,ListType);
690   Result:=-1;
691 end;
692 
693 procedure THistoryList.LoadFromXMLConfig(XMLConfig: TXMLConfig;
694   const Path: string);
695 begin
696   if FName='' then
697     FName:=XMLConfig.GetValue(Path+'Name','');
698   FMaxCount:=XMLConfig.GetValue(Path+'MaxCount',MaxCount);
699   FListType:=StrToRecentListType(XMLConfig.GetValue(Path+'Type',''));
700   LoadRecentList(XMLConfig,Self,Path,ListType);
701 end;
702 
703 procedure THistoryList.SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
704 begin
705   XMLConfig.SetDeleteValue(Path+'Name',Name,'');
706   XMLConfig.SetDeleteValue(Path+'Type',RecentListTypeNames[ListType],
707                            RecentListTypeNames[rltCaseSensitive]);
708   XMLConfig.SetDeleteValue(Path+'MaxCount',MaxCount,20);
709   SaveRecentList(XMLConfig,Self,Path);
710 end;
711 
712 procedure THistoryList.AppendEntry(const Entry: string);
713 begin
714   if (Count<MaxCount) and (IndexOf(Entry)<0) then
715     Add(Entry);
716 end;
717 
IndexOfnull718 function THistoryList.IndexOf(const S: string): Integer;
719 var
720   i: Integer;
721 begin
722   for i:=0 to Count-1 do
723     if CompareRecentListItem(S,Strings[i],ListType) then
724       exit(i);
725   Result:=-1;
726 end;
727 
728 { THistoryLists }
729 
THistoryLists.GetItemsnull730 function THistoryLists.GetItems(Index: integer): THistoryList;
731 begin
732   Result:=THistoryList(FItems[Index]);
733 end;
734 
GetXMLListPathnull735 function THistoryLists.GetXMLListPath(const Path: string; i: integer;
736   ALegacyList: Boolean): string;
737 begin
738   Result:=Path+TXMLConfig.GetListItemXPath('List', i, ALegacyList, False)+'/';
739 end;
740 
741 constructor THistoryLists.Create;
742 begin
743   FItems:=TList.Create;
744 end;
745 
746 destructor THistoryLists.Destroy;
747 begin
748   Clear;
749   FItems.Free;
750   inherited Destroy;
751 end;
752 
753 procedure THistoryLists.Clear;
754 var i: integer;
755 begin
756   for i:=0 to Count-1 do
757     Items[i].Free;
758   FItems.Clear;
759 end;
760 
THistoryLists.Countnull761 function THistoryLists.Count: integer;
762 begin
763   Result:=FItems.Count;
764 end;
765 
766 procedure THistoryLists.LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
767 var
768   MergeCount, i: integer;
769   CurList: THistoryList;
770   ListName, ListPath: string;
771   ListType: TRecentListType;
772   IsLegacyList: Boolean;
773 begin
774   IsLegacyList:=XMLConfig.IsLegacyList(Path);
775   MergeCount:=XMLConfig.GetListItemCount(Path, 'List', IsLegacyList);
776   for i:=0 to MergeCount-1 do begin
777     ListPath:=GetXMLListPath(Path,i,IsLegacyList);
778     ListName:=XMLConfig.GetValue(ListPath+'Name','');
779     if ListName='' then continue;
780     ListType:=StrToRecentListType(XMLConfig.GetValue(ListPath+'Type',''));
781     CurList:=GetList(ListName,true,ListType);
782     CurList.LoadFromXMLConfig(XMLConfig,ListPath);
783   end;
784 end;
785 
786 procedure THistoryLists.SaveToXMLConfig(XMLConfig: TXMLConfig;
787   const Path: string; const ALegacyList: Boolean);
788 var
789   i, CurID: integer;
790 begin
791   XMLConfig.SetListItemCount(Path,Count,ALegacyList);
792   CurID:=0;
793   for i:=0 to Count-1 do begin
794     if Items[i].Count>0 then begin
795       Items[i].SaveToXMLConfig(XMLConfig,GetXMLListPath(Path,CurID,ALegacyList));
796       inc(CurID);
797     end;
798   end;
799 end;
800 
IndexOfNamenull801 function THistoryLists.IndexOfName(const Name: string): integer;
802 begin
803   Result:=Count-1;
804   while (Result>=0) and (UTF8CompareLatinTextFast(Items[Result].Name,Name)<>0) do
805     dec(Result);
806 end;
807 
GetListnull808 function THistoryLists.GetList(const Name: string; CreateIfNotExists: boolean;
809   ListType: TRecentListType): THistoryList;
810 var
811   i: integer;
812 begin
813   i:=IndexOfName(Name);
814   if i>=0 then
815     Result:=Items[i]
816   else if CreateIfNotExists then begin
817     Result:=THistoryList.Create(ListType);
818     Result.Name:=Name;
819     FItems.Add(Result);
820   end else
821     Result:=nil;
822 end;
823 
824 procedure THistoryLists.Add(const ListName, Entry: string;
825   ListType: TRecentListType);
826 begin
827   GetList(ListName,true,ListType).Push(Entry);
828 end;
829 
830 { TIHIgnoreIDEQuestionList }
831 
FindNodenull832 function TIHIgnoreIDEQuestionList.FindNode(const Identifier: string): TAvlTreeNode;
833 begin
834   Result:=FItems.FindKey(Pointer(Identifier),@CompareAnsiStringWithIHIgnoreItem);
835 end;
836 
837 constructor TIHIgnoreIDEQuestionList.Create;
838 begin
839   FItems:=TAvlTree.Create(@CompareIHIgnoreItems);
840 end;
841 
842 destructor TIHIgnoreIDEQuestionList.Destroy;
843 begin
844   FItems.FreeAndClear;
845   FreeAndNil(FItems);
846   inherited Destroy;
847 end;
848 
849 procedure TIHIgnoreIDEQuestionList.Clear;
850 begin
851   FItems.FreeAndClear;
852 end;
853 
Addnull854 function TIHIgnoreIDEQuestionList.Add(const Identifier: string;
855   const Duration: TIgnoreQuestionDuration; const Flag: string): TIgnoreIDEQuestionItem;
856 var
857   Node: TAvlTreeNode;
858 begin
859   Node:=FindNode(Identifier);
860   if Node<>nil then begin
861     Result:=TIgnoreIDEQuestionItem(Node.Data);
862   end else begin
863     Result:=TIgnoreIDEQuestionItem.Create(Identifier);
864     FItems.Add(Result);
865   end;
866   Result.Duration:=Duration;
867   Result.Date:=Now;
868   Result.Flag:=Flag;
869 end;
870 
871 procedure TIHIgnoreIDEQuestionList.Delete(const Identifier: string);
872 var
873   Node: TAvlTreeNode;
874 begin
875   Node:=FindNode(Identifier);
876   if Node<>nil then
877     FItems.FreeAndDelete(Node);
878 end;
879 
TIHIgnoreIDEQuestionList.Findnull880 function TIHIgnoreIDEQuestionList.Find(const Identifier: string): TIgnoreIDEQuestionItem;
881 var
882   Node: TAvlTreeNode;
883 begin
884   Node:=FindNode(Identifier);
885   if Node<>nil then
886     Result:=TIgnoreIDEQuestionItem(Node.Data)
887   else
888     Result:=nil;
889 end;
890 
891 procedure TIHIgnoreIDEQuestionList.LoadFromXMLConfig(XMLConfig: TXMLConfig;
892   const Path: string);
893 var
894   Cnt: longint;
895   i: Integer;
896   SubPath: String;
897   Identifier: String;
898   ADate: TDateTime;
899   ADuration: TIgnoreQuestionDuration;
900   Item: TIgnoreIDEQuestionItem;
901   CurNow: TDateTime;
902 begin
903   Clear;
904   CurNow:=Now;
905   Cnt:=XMLConfig.GetValue(Path+'Count',0);
906   for i:=1 to Cnt do begin
907     SubPath:=Path+'Item'+IntToStr(i)+'/';
908     Identifier:=XMLConfig.GetValue(SubPath+'Name','');
909     if Identifier='' then continue;
910     if not CfgStrToDate(XMLConfig.GetValue(SubPath+'Date',''),ADate) then continue;
911     ADuration:=NameToIHIgnoreItemDuration(XMLConfig.GetValue(SubPath+'Duration',
912                                           IHIgnoreItemDurationNames[iiid24H]));
913     //debugln(['TIHIgnoreIDEQuestionList.LoadFromXMLConfig Identifier="',Identifier,'" Date=',DateTimeToStr(ADate),' Diff=',DateTimeToStr(CurNow-ADate),' Duration=',IHIgnoreItemDurationNames[ADuration]]);
914     case ADuration of
915     iiidIDERestart: continue;
916     iiid24H: if Abs(CurNow-ADate)>1 then continue;
917     iiidForever: ;
918     end;
919     Item:=Add(Identifier,ADuration);
920     Item.Date:=ADate;
921     Item.Flag:=XMLConfig.GetValue(SubPath+'Flag','');
922   end;
923 end;
924 
925 procedure TIHIgnoreIDEQuestionList.SaveToXMLConfig(XMLConfig: TXMLConfig;
926   const Path: string);
927 var
928   i: Integer;
929   Node: TAvlTreeNode;
930   Item: TIgnoreIDEQuestionItem;
931   SubPath: String;
932 begin
933   i:=0;
934   Node:=FItems.FindLowest;
935   while Node<>nil do begin
936     Item:=TIgnoreIDEQuestionItem(Node.Data);
937     if (Item.Duration<>iiidIDERestart) and (Item.Identifier<>'') then begin
938       inc(i);
939       SubPath:=Path+'Item'+IntToStr(i)+'/';
940       XMLConfig.SetDeleteValue(SubPath+'Name',Item.Identifier,'');
941       XMLConfig.SetDeleteValue(SubPath+'Date',DateToCfgStr(Item.Date),'');
942       XMLConfig.SetDeleteValue(SubPath+'Duration',
943                                IHIgnoreItemDurationNames[Item.Duration],
944                                IHIgnoreItemDurationNames[iiid24H]);
945       XMLConfig.SetDeleteValue(SubPath+'Flag',Item.Flag,'');
946     end;
947     Node:=FItems.FindSuccessor(Node);
948   end;
949   XMLConfig.SetDeleteValue(Path+'Count',i,0);
950 end;
951 
952 end.
953 
954