1 {
2  ***************************************************************************
3  *                                                                         *
4  *   This source is free software; you can redistribute it and/or modify   *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This code is distributed in the hope that it will be useful, but      *
10  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
12  *   General Public License for more details.                              *
13  *                                                                         *
14  *   A copy of the GNU General Public License is available on the World    *
15  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
16  *   obtain it by writing to the Free Software Foundation,                 *
17  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
18  *                                                                         *
19  ***************************************************************************
20 
21   Author: Mattias Gaertner
22 
23   Abstract:
24     Defines the TExternalToolOptions which stores the settings of a single
25     external tool. (= Programfilename and parameters)
26     All TExternalToolOptions are stored in a TExternalToolList
27     (see exttooldialog.pas).
28     And this unit provides TExternalToolOptionDlg which is a dialog for editing
29     a single external tool.
30 }
31 unit ExtToolEditDlg;
32 
33 {$mode objfpc}
34 {$H+}
35 
36 {$I ide.inc}
37 
38 interface
39 
40 uses
41   {$IFDEF IDE_MEM_CHECK}
42   MemCheck,
43   {$ENDIF}
44   Classes, SysUtils, contnrs,
45   // LCL
46   LCLType, Controls, Forms, StdCtrls, Dialogs, LCLProc, ButtonPanel, EditBtn,
47   // LazUtils
48   FileUtil, LazFileUtils, LazUTF8, LazConfigStorage,
49   // Codetools
50   FileProcs,
51   // IdeIntf
52   IDEExternToolIntf, IDEHelpIntf, PropEdits, IDEDialogs, IDECommands, IDEUtils,
53   // IDE
54   TransferMacros, LazarusIDEStrConsts, EnvironmentOpts, KeyMapping;
55 
56 const
57   ExternalToolOptionsVersion = 3;
58   // 3: changed ScanOutputForFPCMessages to parser SubToolFPC
59   //    changed ScanOutputForMakeMessages to parser SubToolMake
60 type
61 
62   { TExternalUserTool - the options of an external tool in the IDE menu Tools }
63 
64   TExternalUserTool = class(TComponent)
65   private
66     FChangeStamp: integer;
67     fCmdLineParams: string;
68     FEnvironmentOverrides: TStringList;
69     fFilename: string;
70     FHideWindow: boolean;
71     FKey: word;
72     FParsers: TStrings;
73     FShift: TShiftState;
74     FShowConsole: boolean;
75     fTitle: string;
76     fWorkingDirectory: string;
77     fSavedChangeStamp: integer;
GetHasParsernull78     function GetHasParser(aName: string): boolean;
GetModifiednull79     function GetModified: boolean;
80     procedure SetChangeStamp(AValue: integer);
81     procedure SetCmdLineParams(AValue: string);
82     procedure SetEnvironmentOverrides(AValue: TStringList);
83     procedure SetFilename(AValue: string);
84     procedure SetHasParser(aName: string; AValue: boolean);
85     procedure SetHideWindow(AValue: boolean);
86     procedure SetModified(AValue: boolean);
87     procedure SetParsers(AValue: TStrings);
88     procedure SetShowConsole(AValue: boolean);
89     procedure SetTitle(AValue: string);
90     procedure SetWorkingDirectory(AValue: string);
91   public
92     constructor Create(AOwner: TComponent); override;
93     destructor Destroy; override;
94     procedure Clear;
Equalsnull95     function Equals(Obj: TObject): boolean; override;
96     procedure Assign(Source: TPersistent); override;
Loadnull97     function Load(Config: TConfigStorage; CfgVersion: integer): TModalResult; virtual;
Savenull98     function Save(Config: TConfigStorage): TModalResult; virtual;
99     procedure IncreaseChangeStamp; inline;
100     property CmdLineParams: string read fCmdLineParams write SetCmdLineParams;
101     property Filename: string read fFilename write SetFilename;
102     property Title: string read fTitle write SetTitle;
103     property WorkingDirectory: string read fWorkingDirectory write SetWorkingDirectory;
104     property EnvironmentOverrides: TStringList read FEnvironmentOverrides write SetEnvironmentOverrides;
105     property Parsers: TStrings read FParsers write SetParsers;
106     property HasParser[aName: string]: boolean read GetHasParser write SetHasParser;
107     property ShowConsole: boolean read FShowConsole write SetShowConsole;
108     property HideWindow: boolean read FHideWindow write SetHideWindow;
109     property Modified: boolean read GetModified write SetModified;
110     property ChangeStamp: integer read FChangeStamp write SetChangeStamp;
111   public
112     // these properties are saved in the keymappings, not in the config
113     property Key: word read FKey write FKey;
114     property Shift: TShiftState read FShift write FShift;
115   end;
116 
117   { TExternalUserTools }
118 
119   TExternalUserTools = class(TBaseExternalUserTools)
120   private
121     fItems: TObjectList; // list of TExternalUserTool
GetItemsnull122     function GetItems(Index: integer): TExternalUserTool; inline;
123   public
124     constructor Create; override;
125     destructor Destroy; override;
126     procedure Clear; inline;
Equalsnull127     function Equals(Obj: TObject): boolean; override;
128     procedure Assign(Src: TExternalUserTools);
Countnull129     function Count: integer; inline;
130     property Items[Index: integer]: TExternalUserTool read GetItems; default;
131     procedure Add(Item: TExternalUserTool);
132     procedure Insert(Index: integer; Item: TExternalUserTool);
133     procedure Delete(Index: integer);
134     procedure Move(CurIndex, NewIndex: integer);
135     // run
Runnull136     function Run(Index: integer; {%H-}ShowAbort: boolean): TModalResult;
137     // load/save
Loadnull138     function Load(Config: TConfigStorage): TModalResult;
Loadnull139     function Load(Config: TConfigStorage; const Path: string): TModalResult; override;
Savenull140     function Save(Config: TConfigStorage): TModalResult;
Savenull141     function Save(Config: TConfigStorage; const Path: string): TModalResult; override;
142     procedure LoadShortCuts(KeyCommandRelationList: TKeyCommandRelationList);
143     procedure SaveShortCuts(KeyCommandRelationList: TKeyCommandRelationList);
144   end;
145 
146 var
147   ExternalUserTools: TExternalUserTools = nil;
148 
149 type
150   { TExternalToolOptionDlg - the editor dialog for a single external tool }
151 
152   TExternalToolOptionDlg = class(TForm)
153     ButtonPanel: TButtonPanel;
154     FileNameEdit: TFileNameEdit;
155     FilenameLabel: TLabel;
156     HideWindowCheckBox: TCheckBox;
157     KeyGroupBox: TGroupBox;
158     MacrosGroupbox: TGroupbox;
159     MacrosInsertButton: TButton;
160     MacrosListbox: TListbox;
161     MemoParameters: TMemo;
162     OptionsGroupBox: TGroupBox;
163     ParametersLabel: TLabel;
164     ScannersButton: TButton;
165     ScanOutputForFPCMessagesCheckBox: TCheckBox;
166     ScanOutputForMakeMessagesCheckBox: TCheckBox;
167     ShowConsoleCheckBox: TCheckBox;
168     TitleEdit: TEdit;
169     TitleLabel: TLabel;
170     WorkingDirEdit: TDirectoryEdit;
171     WorkingDirLabel: TLabel;
172     procedure FormCreate(Sender: TObject);
173     procedure FormDestroy(Sender: TObject);
174     procedure HelpButtonClick(Sender: TObject);
175     procedure MacrosInsertButtonClick(Sender: TObject);
176     procedure MacrosListboxClick(Sender: TObject);
177     procedure MacrosListboxDblClick(Sender: TObject);
178     procedure OKButtonClick(Sender: TObject);
179     procedure ShowConsoleCheckBoxChange(Sender: TObject);
180   private
181     fAllKeys: TKeyCommandRelationList;
182     fOptions: TExternalUserTool;
183     fTransferMacros: TTransferMacroList;
184     fScanners: TStrings;
185     fKeyBox: TShortCutGrabBox;
186     procedure FillMacroList;
KeyConflictsnull187     function KeyConflicts(Key:word; Shift:TShiftState): TModalResult;
188     procedure LoadFromOptions;
189     procedure SaveToOptions;
190     procedure UpdateButtons;
ScannersToStringnull191     function ScannersToString(List: TStrings): string;
192     procedure SetComboBox(AComboBox: TComboBox; const AValue: string);
193     procedure SetOptions(TheOptions: TExternalUserTool);
194     procedure SetTransferMacros(TransferMacroList: TTransferMacroList);
195   public
196     property Options: TExternalUserTool read fOptions write SetOptions;
197     property MacroList: TTransferMacroList
198            read fTransferMacros write SetTransferMacros;
199   end;
200 
201 
ShowExtToolOptionDlgnull202 function ShowExtToolOptionDlg(TransferMacroList: TTransferMacroList;
203   ExternalToolMenuItem: TExternalUserTool;
204   AllKeys: TKeyCommandRelationList):TModalResult;
205 
206 implementation
207 
208 {$R *.lfm}
209 
ShowExtToolOptionDlgnull210 function ShowExtToolOptionDlg(TransferMacroList: TTransferMacroList;
211   ExternalToolMenuItem: TExternalUserTool;
212   AllKeys: TKeyCommandRelationList):TModalResult;
213 var
214   ExternalToolOptionDlg: TExternalToolOptionDlg;
215 begin
216   Result:=mrCancel;
217   ExternalToolOptionDlg:=TExternalToolOptionDlg.Create(nil);
218   try
219     ExternalToolOptionDlg.fAllKeys:=AllKeys;
220     ExternalToolOptionDlg.Options:=ExternalToolMenuItem;
221     ExternalToolOptionDlg.MacroList:=TransferMacroList;
222     Result:=ExternalToolOptionDlg.ShowModal;
223     if Result=mrOk then
224       ExternalToolMenuItem.Assign(ExternalToolOptionDlg.Options);
225   finally
226     ExternalToolOptionDlg.Free;
227   end;
228 end;
229 
230 { TExternalUserTool }
231 
232 // inline
233 procedure TExternalUserTool.IncreaseChangeStamp;
234 begin
235   CTIncreaseChangeStamp(FChangeStamp);
236 end;
237 
TExternalUserTool.GetModifiednull238 function TExternalUserTool.GetModified: boolean;
239 begin
240   Result:=FChangeStamp=fSavedChangeStamp;
241 end;
242 
GetHasParsernull243 function TExternalUserTool.GetHasParser(aName: string): boolean;
244 begin
245   Result:=IndexInStringList(FParsers,cstCaseInsensitive,aName)>=0;
246 end;
247 
248 procedure TExternalUserTool.SetChangeStamp(AValue: integer);
249 begin
250   if FChangeStamp=AValue then Exit;
251   FChangeStamp:=AValue;
252 end;
253 
254 procedure TExternalUserTool.SetCmdLineParams(AValue: string);
255 begin
256   AValue:=UTF8Trim(AValue,[]);
257   if fCmdLineParams=AValue then Exit;
258   fCmdLineParams:=AValue;
259   IncreaseChangeStamp;
260 end;
261 
262 procedure TExternalUserTool.SetEnvironmentOverrides(AValue: TStringList);
263 begin
264   if (FEnvironmentOverrides=AValue) or FEnvironmentOverrides.Equals(AValue) then Exit;
265   FEnvironmentOverrides.Assign(AValue);
266   IncreaseChangeStamp;
267 end;
268 
269 procedure TExternalUserTool.SetFilename(AValue: string);
270 begin
271   AValue:=TrimFilename(AValue);
272   if fFilename=AValue then Exit;
273   fFilename:=AValue;
274   IncreaseChangeStamp;
275 end;
276 
277 procedure TExternalUserTool.SetHasParser(aName: string; AValue: boolean);
278 var
279   i: Integer;
280 begin
281   i:=IndexInStringList(FParsers,cstCaseInsensitive,aName);
282   if i>=0 then begin
283     if AValue then exit;
284     FParsers.Delete(i);
285   end else begin
286     if not AValue then exit;
287     FParsers.Add(aName);
288   end;
289   IncreaseChangeStamp;
290 end;
291 
292 procedure TExternalUserTool.SetHideWindow(AValue: boolean);
293 begin
294   if FHideWindow=AValue then Exit;
295   FHideWindow:=AValue;
296   IncreaseChangeStamp;
297 end;
298 
299 procedure TExternalUserTool.SetModified(AValue: boolean);
300 begin
301   if AValue then
302     IncreaseChangeStamp
303   else
304     fSavedChangeStamp:=FChangeStamp;
305 end;
306 
307 procedure TExternalUserTool.SetParsers(AValue: TStrings);
308 begin
309   if (FParsers=AValue) or FParsers.Equals(AValue) then Exit;
310   FParsers.Assign(AValue);
311   IncreaseChangeStamp;
312 end;
313 
314 procedure TExternalUserTool.SetShowConsole(AValue: boolean);
315 begin
316   if FShowConsole=AValue then Exit;
317   FShowConsole:=AValue;
318   IncreaseChangeStamp;
319 end;
320 
321 procedure TExternalUserTool.SetTitle(AValue: string);
322 begin
323   AValue:=UTF8Trim(AValue,[]);
324   if fTitle=AValue then Exit;
325   fTitle:=AValue;
326   IncreaseChangeStamp;
327 end;
328 
329 procedure TExternalUserTool.SetWorkingDirectory(AValue: string);
330 begin
331   AValue:=TrimFilename(AValue);
332   if fWorkingDirectory=AValue then Exit;
333   fWorkingDirectory:=AValue;
334   IncreaseChangeStamp;
335 end;
336 
337 constructor TExternalUserTool.Create(AOwner: TComponent);
338 begin
339   inherited Create(AOwner);
340   FEnvironmentOverrides:=TStringList.Create;
341   FParsers:=TStringList.Create;
342   fSavedChangeStamp:=CTInvalidChangeStamp;
343   Clear;
344 end;
345 
346 destructor TExternalUserTool.Destroy;
347 begin
348   FreeAndNil(FEnvironmentOverrides);
349   FreeAndNil(FParsers);
350   inherited Destroy;
351 end;
352 
353 procedure TExternalUserTool.Clear;
354 begin
355   CmdLineParams:='';
356   if FEnvironmentOverrides.Count>0 then
357   begin
358     IncreaseChangeStamp;
359     FEnvironmentOverrides.Clear;
360   end;
361   Filename:='';
362   if FParsers.Count>0 then
363   begin
364     FParsers.Clear;
365     IncreaseChangeStamp;
366   end;
367   Title:='';
368   WorkingDirectory:='';
369   ShowConsole:=false;
370   HideWindow:=true;
371 end;
372 
Equalsnull373 function TExternalUserTool.Equals(Obj: TObject): boolean;
374 var
375   Src: TExternalUserTool;
376 begin
377   if Obj is TExternalUserTool then begin
378     Src:=TExternalUserTool(Obj);
379     Result:=(CmdLineParams=Src.CmdLineParams)
380       and EnvironmentOverrides.Equals(Src.EnvironmentOverrides)
381       and (Filename=Src.Filename)
382       and Parsers.Equals(Src.Parsers)
383       and (ShowConsole=Src.ShowConsole)
384       and (HideWindow=Src.HideWindow)
385       and (Title=Src.Title)
386       and (WorkingDirectory=Src.WorkingDirectory)
387       and (Key=Src.Key)
388       and (Shift=Src.Shift);
389   end else
390     Result:=inherited;
391 end;
392 
393 procedure TExternalUserTool.Assign(Source: TPersistent);
394 var
395   Src: TExternalUserTool;
396 begin
397   if Equals(Source) then exit;
398   if Source is TExternalUserTool then begin
399     Src:=TExternalUserTool(Source);
400     CmdLineParams:=Src.CmdLineParams;
401     WorkingDirectory:=Src.WorkingDirectory;
402     EnvironmentOverrides:=Src.EnvironmentOverrides;
403     Filename:=Src.Filename;
404     Parsers:=Src.Parsers;
405     ShowConsole:=Src.ShowConsole;
406     HideWindow:=Src.HideWindow;
407     Title:=Src.Title;
408     Key:=Src.Key;
409     Shift:=Src.Shift;
410   end else
411     inherited;
412 end;
413 
Loadnull414 function TExternalUserTool.Load(Config: TConfigStorage; CfgVersion: integer
415   ): TModalResult;
416 begin
417   Clear;
418   fTitle:=Config.GetValue('Title/Value','');
419   fFilename:=Config.GetValue('Filename/Value','');
420   fCmdLineParams:=Config.GetValue('CmdLineParams/Value','');
421   fWorkingDirectory:=Config.GetValue('WorkingDirectory/Value','');
422   Config.GetValue('EnvironmentOverrides/',FEnvironmentOverrides);
423   ShowConsole:=Config.GetValue('ShowConsole/Value',false);
424   HideWindow:=Config.GetValue('HideWindow/Value',true);
425 
426   if CfgVersion<3 then
427   begin
428     if Config.GetValue('ScanOutputForFPCMessages/Value',false) then
429       FParsers.Add(SubToolFPC);
430     if Config.GetValue('ScanOutputForMakeMessages/Value',false) then
431       FParsers.Add(SubToolMake);
432     if Config.GetValue('ShowAllOutput/Value',false) then
433       FParsers.Add(SubToolDefault);
434   end else
435     Config.GetValue('Scanners/',FParsers);
436 
437   Modified:=false;
438   Result:=mrOk;
439 end;
440 
Savenull441 function TExternalUserTool.Save(Config: TConfigStorage): TModalResult;
442 begin
443   Config.SetDeleteValue('Title/Value',Title,'');
444   Config.SetDeleteValue('Filename/Value',Filename,'');
445   Config.SetDeleteValue('CmdLineParams/Value',CmdLineParams,'');
446   Config.SetDeleteValue('WorkingDirectory/Value',WorkingDirectory,'');
447   Config.SetValue('EnvironmentOverrides/',FEnvironmentOverrides);
448   Config.SetValue('Scanners/',FParsers);
449   Config.SetDeleteValue('ShowConsole/Value',ShowConsole,false);
450   Config.SetDeleteValue('HideWindow/Value',HideWindow,true);
451   Modified:=false;
452   Result:=mrOk;
453 end;
454 
455 { TExternalUserTools }
456 
457 // inline
TExternalUserTools.Countnull458 function TExternalUserTools.Count: integer;
459 begin
460   Result:=fItems.Count;
461 end;
462 
463 // inline
TExternalUserTools.GetItemsnull464 function TExternalUserTools.GetItems(Index: integer): TExternalUserTool;
465 begin
466   Result:=TExternalUserTool(fItems[Index]);
467 end;
468 
469 // inline
470 procedure TExternalUserTools.Clear;
471 begin
472   fItems.Clear;
473 end;
474 
475 constructor TExternalUserTools.Create;
476 begin
477   fItems:=TObjectList.Create(true);
478 end;
479 
480 destructor TExternalUserTools.Destroy;
481 begin
482   if ExternalUserTools=Self then
483     ExternalUserTools:=nil;
484   Clear;
485   FreeAndNil(fItems);
486   inherited Destroy;
487 end;
488 
Equalsnull489 function TExternalUserTools.Equals(Obj: TObject): boolean;
490 var
491   Src: TExternalUserTools;
492   i: Integer;
493 begin
494   if Obj=Self then exit(true);
495   if Obj is TExternalUserTools then begin
496     Src:=TExternalUserTools(Obj);
497     Result:=false;
498     if Count<>Src.Count then exit;
499     for i:=0 to Count-1 do
500       if not Items[i].Equals(Src[i]) then exit;
501     Result:=true;
502   end else
503     Result:=inherited Equals(Obj);
504 end;
505 
506 procedure TExternalUserTools.Assign(Src: TExternalUserTools);
507 var
508   Item: TExternalUserTool;
509   i: Integer;
510 begin
511   if Equals(Src) then exit;
512   Clear;
513   for i:=0 to Src.Count-1 do begin
514     Item:=TExternalUserTool.Create(nil);
515     Item.Assign(Src[i]);
516     Add(Item);
517   end;
518 end;
519 
520 procedure TExternalUserTools.Add(Item: TExternalUserTool);
521 begin
522   fItems.Add(Item);
523 end;
524 
525 procedure TExternalUserTools.Insert(Index: integer;
526   Item: TExternalUserTool);
527 begin
528   fItems.Insert(Index,Item);
529 end;
530 
531 procedure TExternalUserTools.Delete(Index: integer);
532 begin
533   fItems.Delete(Index);
534 end;
535 
536 procedure TExternalUserTools.Move(CurIndex, NewIndex: integer);
537 begin
538   fItems.Move(CurIndex,NewIndex);
539 end;
540 
TExternalUserTools.Runnull541 function TExternalUserTools.Run(Index: integer; ShowAbort: boolean
542   ): TModalResult;
543 var
544   Item: TExternalUserTool;
545   Tool: TIDEExternalToolOptions;
546 begin
547   Result:=mrCancel;
548   Item:=Items[Index];
549 
550   Tool:=TIDEExternalToolOptions.Create;
551   try
552     Tool.Title:=Item.Title;
553     Tool.Hint:='External tool '+IntToStr(Index+1);
554     Tool.Executable:=Item.Filename;
555     Tool.WorkingDirectory:=Item.WorkingDirectory;
556     Tool.CmdLineParams:=Item.CmdLineParams;
557     Tool.EnvironmentOverrides:=Item.EnvironmentOverrides;
558     Tool.Parsers:=Item.Parsers;
559     Tool.ShowConsole:=Item.ShowConsole;
560     Tool.HideWindow:=Item.HideWindow;
561     Tool.ResolveMacros:=true;
562     if not RunExternalTool(Tool) then exit;
563   finally
564     Tool.Free;
565   end;
566 end;
567 
Loadnull568 function TExternalUserTools.Load(Config: TConfigStorage): TModalResult;
569 var
570   i: integer;
571   NewTool: TExternalUserTool;
572   NewCount: Integer;
573   CfgVersion: Integer;
574 begin
575   Clear;
576   NewCount:=Config.GetValue('Count',0);
577   CfgVersion:=Config.GetValue('Version',0);
578   for i:=1 to NewCount do begin
579     NewTool:=TExternalUserTool.Create(nil);
580     fItems.Add(NewTool);
581     Config.AppendBasePath('Tool'+IntToStr(i)+'/');
582     try
583       if NewTool.Load(Config,CfgVersion)<>mrOk then exit;
584     finally
585       Config.UndoAppendBasePath;
586     end;
587   end;
588   Result:=mrOk;
589 end;
590 
Loadnull591 function TExternalUserTools.Load(Config: TConfigStorage; const Path: string
592   ): TModalResult;
593 begin
594   if Path<>'' then
595     Config.AppendBasePath(Path);
596   try
597     Result:=Load(Config);
598   finally
599     if Path<>'' then
600       Config.UndoAppendBasePath;
601   end;
602 end;
603 
TExternalUserTools.Savenull604 function TExternalUserTools.Save(Config: TConfigStorage): TModalResult;
605 var
606   i: integer;
607 begin
608   Config.SetValue('Version',ExternalToolOptionsVersion);
609   Config.SetDeleteValue('Count',Count,0);
610   for i:=1 to Count do begin
611     Config.AppendBasePath('Tool'+IntToStr(i)+'/');
612     try
613       if Items[i-1].Save(Config)<>mrOk then exit;
614     finally
615       Config.UndoAppendBasePath;
616     end;
617   end;
618   Result:=mrOk;
619 end;
620 
TExternalUserTools.Savenull621 function TExternalUserTools.Save(Config: TConfigStorage; const Path: string
622   ): TModalResult;
623 begin
624   if Path<>'' then
625     Config.AppendBasePath(Path);
626   try
627     Result:=Save(Config);
628   finally
629     if Path<>'' then
630       Config.UndoAppendBasePath;
631   end;
632 end;
633 
634 procedure TExternalUserTools.LoadShortCuts(
635   KeyCommandRelationList: TKeyCommandRelationList);
636 var
637   i: integer;
638   KeyCommandRelation: TKeyCommandRelation;
639 begin
640   //debugln(['TExternalUserTools.LoadShortCuts ',KeyCommandRelationList.ExtToolCount,' ',Count]);
641   KeyCommandRelationList.ExtToolCount:=Count;
642   for i:=0 to Count-1 do begin
643     KeyCommandRelation:=KeyCommandRelationList.FindByCommand(ecExtToolFirst+i);
644     //debugln(['TExternalUserTools.LoadShortCuts ',i,'/',Count,' ',KeyCommandRelation.Name]);
645     if KeyCommandRelation<>nil then begin
646       Items[i].Key:=KeyCommandRelation.ShortcutA.Key1;
647       Items[i].Shift:=KeyCommandRelation.ShortcutA.Shift1;
648     end else begin
649       Items[i].Key:=VK_UNKNOWN;
650       Items[i].Shift:=[];
651     end;
652   end;
653 end;
654 
655 procedure TExternalUserTools.SaveShortCuts(
656   KeyCommandRelationList: TKeyCommandRelationList);
657 var
658   i: integer;
659   KeyCommandRelation: TKeyCommandRelation;
660 begin
661   //debugln(['TExternalUserTools.SaveShortCuts ',KeyCommandRelationList.ExtToolCount,' ',Count]);
662   KeyCommandRelationList.ExtToolCount:=Count;
663   for i:=0 to Count-1 do begin
664     KeyCommandRelation:=KeyCommandRelationList.FindByCommand(ecExtToolFirst+i);
665     //debugln(['TExternalUserTools.SaveShortCuts ',i,'/',Count,' ',KeyCommandRelation.Name]);
666     if KeyCommandRelation<>nil then begin
667       KeyCommandRelation.ShortcutA:=IDEShortCut(Items[i].Key,Items[i].Shift,
668                                            VK_UNKNOWN,[]);
669     end else begin
670       DebugLn('[TExternalToolMenuItems.SaveShortCuts] Error: '
671         +'unable to save shortcut for external tool "',Items[i].Title,'"');
672     end;
673   end;
674 end;
675 
676 { TExternalToolOptionDlg }
677 
678 procedure TExternalToolOptionDlg.LoadFromOptions;
679 begin
680   TitleEdit.Text:=fOptions.Title;
681   FilenameEdit.Text:=fOptions.Filename;
682   MemoParameters.Lines.Text:=fOptions.CmdLineParams;
683   WorkingDirEdit.Text:=fOptions.WorkingDirectory;
684   fKeyBox.Key:=fOptions.Key;
685   fKeyBox.ShiftState:=fOptions.Shift;
686   ScanOutputForFPCMessagesCheckBox.Checked:=fOptions.HasParser[SubToolFPC];
687   ScanOutputForMakeMessagesCheckBox.Checked:=fOptions.HasParser[SubToolMake];
688   ShowConsoleCheckBox.Checked:=FOptions.ShowConsole;
689   HideWindowCheckBox.Checked:=FOptions.HideWindow;
690   fScanners.Assign(fOptions.Parsers);
691   UpdateButtons;
692 end;
693 
694 procedure TExternalToolOptionDlg.SaveToOptions;
695 begin
696   fOptions.Title:=TitleEdit.Text;
697   fOptions.Filename:=FilenameEdit.Text;
698   fOptions.CmdLineParams:=MemoParameters.Lines.Text;
699   fOptions.WorkingDirectory:=WorkingDirEdit.Text;
700   fOptions.Key:=fKeyBox.Key;
701   fOptions.Shift:=fKeyBox.ShiftState;
702   FOptions.ShowConsole := ShowConsoleCheckBox.Checked;
703   FOptions.HideWindow := HideWindowCheckBox.Checked;
704   fOptions.HasParser[SubToolFPC]:=ScanOutputForFPCMessagesCheckBox.Checked;
705   fOptions.HasParser[SubToolMake]:=ScanOutputForMakeMessagesCheckBox.Checked;
706 end;
707 
708 procedure TExternalToolOptionDlg.UpdateButtons;
709 begin
710   ScannersButton.Visible:=false;
711   {$IFDEF Windows}
712   HideWindowCheckBox.Visible:=true;
713   ShowConsoleCheckBox.Visible:=true;
714   {$ENDIF}
715 end;
716 
TExternalToolOptionDlg.ScannersToStringnull717 function TExternalToolOptionDlg.ScannersToString(List: TStrings): string;
718 var
719   i: Integer;
720 begin
721   if (List=nil) or (List.Count=0) then begin
722     Result:='none';
723   end else begin
724     Result:='';
725     for i:=0 to List.Count-1 do begin
726       if Result<>'' then
727         Result:=Result+',';
728       Result:=Result+List[i];
729       if length(Result)>20 then begin
730         Result:=copy(Result,1,20);
731         break;
732       end;
733     end;
734   end;
735 end;
736 
737 procedure TExternalToolOptionDlg.FormCreate(Sender: TObject);
738 begin
739   fScanners:=TStringList.Create;
740   Caption:=lisEdtExtToolEditTool;
741   TitleLabel.Caption:=dlgPOTitle;
742   FilenameLabel.Caption:=lisEdtExtToolProgramfilename;
743   FilenameEdit.ButtonHint:=lisClickHereToBrowseTheFileHint;
744 
745   FilenameEdit.DialogTitle:=lisSelectFile;
746   FilenameEdit.Filter:=dlgFilterAll+' ('+GetAllFilesMask+')|'+GetAllFilesMask
747       +'|'+dlgFilterPrograms+' (*.exe)|*.exe';
748 
749   ParametersLabel.Caption:=lisEdtExtToolParameters;
750   WorkingDirLabel.Caption:=lisEdtExtToolWorkingDirectory;
751   OptionsGroupBox.Caption:=lisLazBuildOptions;
752 
753   with ScanOutputForFPCMessagesCheckBox do
754     Caption:=lisEdtExtToolScanOutputForFreePascalCompilerMessages;
755   with ScanOutputForMakeMessagesCheckBox do
756     Caption:=lisEdtExtToolScanOutputForMakeMessages;
757   ShowConsoleCheckBox.Caption:=lisShowConsole;
758   ShowConsoleCheckBox.Hint:=lisOnlyAvailableOnWindowsRunToolInANewConsole;
759   HideWindowCheckBox.Caption:=lisHideWindow;
760   HideWindowCheckBox.Hint:=lisOnlyAvailableOnWindowsRunTheToolHidden;
761 
762   with KeyGroupBox do
763     Caption:=lisEdtExtToolKey;
764 
765   fKeyBox:=TShortCutGrabBox.Create(Self);
766   with fKeyBox do begin
767     Name:='fKeyBox';
768     Align:=alClient;
769     BorderSpacing.Around:=6;
770     Parent:=KeyGroupBox;
771   end;
772 
773   with MacrosGroupbox do
774     Caption:=lisEdtExtToolMacros;
775 
776   with MacrosInsertButton do
777     Caption:=lisAdd;
778 
779   ButtonPanel.OKButton.Caption:=lisMenuOk;
780   ButtonPanel.HelpButton.Caption:=lisMenuHelp;
781   ButtonPanel.CancelButton.Caption:=lisCancel;
782 
783   fOptions:=TExternalUserTool.Create(nil);
784 end;
785 
786 procedure TExternalToolOptionDlg.FormDestroy(Sender: TObject);
787 begin
788   FreeAndNil(fOptions);
789   FreeAndNil(fScanners);
790 end;
791 
792 procedure TExternalToolOptionDlg.HelpButtonClick(Sender: TObject);
793 begin
794   LazarusHelp.ShowHelpForIDEControl(Self);
795 end;
796 
797 procedure TExternalToolOptionDlg.SetOptions(TheOptions: TExternalUserTool);
798 begin
799   if fOptions=TheOptions then exit;
800   fOptions.Assign(TheOptions);
801   LoadFromOptions;
802 end;
803 
804 procedure TExternalToolOptionDlg.SetTransferMacros(
805   TransferMacroList: TTransferMacroList);
806 begin
807   if fTransferMacros=TransferMacroList then exit;
808   fTransferMacros:=TransferMacroList;
809   if MacrosListbox=nil then exit;
810   FillMacroList;
811 end;
812 
813 procedure TExternalToolOptionDlg.FillMacroList;
814 var i: integer;
815 begin
816   MacrosListbox.Items.BeginUpdate;
817   MacrosListbox.Items.Clear;
818   if fTransferMacros<>nil then begin
819     for i:=0 to fTransferMacros.Count-1 do begin
nilnull820       if fTransferMacros[i].MacroFunction=nil then begin
821         MacrosListbox.Items.Add('$('+fTransferMacros[i].Name+') - '
822                     +fTransferMacros[i].Description);
823       end else begin
824         MacrosListbox.Items.Add('$'+fTransferMacros[i].Name+'() - '
825                     +fTransferMacros[i].Description);
826       end;
827     end;
828   end;
829   MacrosListbox.Items.EndUpdate;
830 end;
831 
KeyConflictsnull832 function TExternalToolOptionDlg.KeyConflicts(Key: word; Shift: TShiftState
833   ): TModalResult;
834 type
835   TConflictType = (ctNone,ctConflictKeyA,ctConflictKeyB);
836 var
837   i: Integer;
838   ct:TConflictType;
839   CurName: TCaption;
840   ConflictName: String;
841 begin
842   Result:=mrOK;
843   // look if we have already this key
844   if Key=VK_UNKNOWN then
845     exit;
846   i:=0;
847   for i:=0 to fAllKeys.RelationCount-1 do
848     begin
849     with fAllKeys.Relations[i] do
850       begin
851       ct:=ctnone;
852       if (ShortcutA.Key1=Key) and (ShortcutA.Shift1=Shift) then
853         ct:=ctConflictKeyA
854       else if (ShortcutB.Key1=Key) and (ShortcutB.Shift1=Shift) then
855         ct:=ctConflictKeyB;
856       if (ct<>ctNone) then begin
857         CurName:=TitleEdit.Text;
858         ConflictName:=GetCategoryAndName;
859         if ct=ctConflictKeyA then
860           ConflictName:=ConflictName
861                     +' ('+KeyAndShiftStateToEditorKeyString(ShortcutA)+')'
862         else
863           ConflictName:=ConflictName
864                    +' ('+KeyAndShiftStateToEditorKeyString(ShortcutB)+')';
865         case IDEMessageDialog(lisPEConflictFound,
866            Format(lisTheKeyIsAlreadyAssignedToRemoveTheOldAssignmentAnd,
867                   [KeyAndShiftStateToKeyString(Key,Shift), LineEnding,
868                    ConflictName, LineEnding, LineEnding, CurName]),
869            mtConfirmation, [mbYes, mbNo, mbCancel])
870         of
871           mrYes:    Result:=mrOK;
872           mrCancel: Result:=mrCancel;
873           else      Result:=mrRetry;
874         end;
875         if Result=mrOK then begin
876           if (ct=ctConflictKeyA) then
877             ShortcutA:=ShortcutB;
878           ClearShortcutB;
879         end
880         else
881           break;
882       end;
883       end;
884     end;
885 end;
886 
887 procedure TExternalToolOptionDlg.SetComboBox(
888   AComboBox: TComboBox; const AValue: string);
889 var i: integer;
890 begin
891   i:=AComboBox.Items.IndexOf(AValue);
892   if i>=0 then
893     AComboBox.ItemIndex:=i
894   else begin
895     AComboBox.Items.Add(AValue);
896     AComboBox.ItemIndex:=AComboBox.Items.IndexOf(AValue);
897   end;
898 end;
899 
900 procedure TExternalToolOptionDlg.MacrosInsertButtonClick(Sender: TObject);
901 var i, ALine: integer;
902   s, AStr: string;
903 begin
904   i:=MacrosListbox.ItemIndex;
905   if i<0 then exit;
nilnull906   if fTransferMacros[i].MacroFunction=nil then
907     s:='$('+fTransferMacros[i].Name+')'
908   else
909     s:='$'+fTransferMacros[i].Name+'()';
910   ALine := MemoParameters.CaretPos.Y;
911   if ALine >= MemoParameters.Lines.Count then
912     MemoParameters.Lines.Add('');
913   AStr := MemoParameters.Lines[Aline];
914   MemoParameters.Lines[Aline] := AStr + s;
915 end;
916 
917 procedure TExternalToolOptionDlg.MacrosListboxClick(Sender: TObject);
918 begin
919   MacrosInsertButton.Enabled:=(MacrosListbox.ItemIndex>=0);
920 end;
921 
922 procedure TExternalToolOptionDlg.MacrosListboxDblClick(Sender: TObject);
923 begin
924   MacrosInsertButtonClick(nil);
925 end;
926 
927 procedure TExternalToolOptionDlg.OKButtonClick(Sender: TObject);
928 begin
929   case KeyConflicts(fKeyBox.Key,fKeyBox.ShiftState) of
930     mrCancel:   begin
931         debugln('TExternalToolOptionDlg.OkButtonClick KeyConflicts failed for key1');
932         ModalResult:=mrCancel;
933         exit;
934       end;
935     mrRetry: begin
936         ModalResult:=mrNone;
937         exit;
938       end;
939     end;
940   if (TitleEdit.Text<>'') and (FilenameEdit.Text<>'') then
941     SaveToOptions
942   else begin
943     IDEMessageDialog(lisEdtExtToolTitleAndFilenameRequired,
944                   lisEdtExtToolAValidToolNeedsAtLeastATitleAndAFilename,
945                   mtError, [mbCancel]);
946     ModalResult:=mrCancel;
947   end;
948 end;
949 
950 procedure TExternalToolOptionDlg.ShowConsoleCheckBoxChange(Sender: TObject);
951 begin
952   if ShowConsoleCheckBox.Checked then
953     HideWindowCheckBox.Checked:=false;
954 end;
955 
956 initialization
957   ExternalUserToolsClass := TExternalUserTools;
958 
959 end.
960