1 unit EditorMacroListViewer;
2
3 {$mode objfpc}{$H+}
4
5 interface
6
7 uses
8 Classes, SysUtils,
9 // LCL
10 LCLType, Forms, Controls, Dialogs, StdCtrls, ButtonPanel, ComCtrls, ExtCtrls,
11 Spin, Menus,
12 // LazUtils
13 LazFileUtils, Laz2_XMLCfg, LazUTF8, LazLoggerBase,
14 // SynEdit
15 SynMacroRecorder, SynEdit, SynEditKeyCmds,
16 // IdeIntf
17 IDEWindowIntf, IDEImagesIntf, SrcEditorIntf, IDEHelpIntf, IDECommands,
18 LazIDEIntf, IDEDialogs,
19 // IDE
20 LazarusIDEStrConsts, ProjectDefs, LazConf, Project, KeyMapping,
21 KeyMapShortCutDlg, MainIntf;
22
23 type
24 TSynEditorMacro = class(TSynMacroRecorder)
25 end;
26
27 { TIdeEditorMacro }
28
29 TIdeEditorMacro = class(TEditorMacro)
30 private
31 FMacroName: String;
32 FHasError: Boolean;
33 FErrorMsg: String;
34 FFailedText: String;
35 FSynMacro: TSynEditorMacro;
36 FKeyBinding: TEditorMacroKeyBinding;
37
38 procedure DoMacroRecorderState(Sender: TObject);
39 procedure DoMacroRecorderUserCommand({%H-}aSender: TCustomSynMacroRecorder;
40 aCmd: TSynEditorCommand; var aEvent: TSynMacroEvent);
41 protected
GetMacroNamenull42 function GetMacroName: String; override;
43 procedure SetMacroName(AValue: string); override;
GetStatenull44 function GetState: TEditorMacroState; override;
GetErrorMsgnull45 function GetErrorMsg: String; override;
GetKeyBindingnull46 function GetKeyBinding: TEditorMacroKeyBinding; override;
47
48 procedure DoRecordMacro(aEditor: TWinControl); override;
49 procedure DoPlaybackMacro(aEditor: TWinControl); override;
50 procedure DoStop; override;
51 procedure DoPause; override;
52 procedure DoResume; override;
53 public
54 constructor Create(aOwner: TComponent); override;
55 destructor Destroy; override;
56 procedure AssignEventsFrom(AMacroRecorder: TEditorMacro); override;
AddEditornull57 function AddEditor(AValue: TCustomSynEdit): integer;
58
59 procedure Clear; override;
60
GetAsSourcenull61 function GetAsSource: String; override;
62 procedure SetFromSource(const AText: String); override;
63 procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String); override;
64 procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String); override;
65
IsEmptynull66 function IsEmpty: Boolean; override;
IsInvalidnull67 function IsInvalid: Boolean; override;
IsRecordingnull68 function IsRecording(AnEditor: TWinControl): Boolean; override;
69 end;
70
71
72 { TIdeEditorMacroKeyBinding }
73
74 TIdeEditorMacroKeyBinding = class(TEditorMacroKeyBinding)
75 protected
76 FIdeCmd: TIDECommand;
GetIdeCmdnull77 function GetIdeCmd: TIDECommand; override;
78 procedure ExecMacro(Sender: TObject);
79 public
80 destructor Destroy; override;
81 procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String); override;
82 procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String); override;
83 procedure MacroNameChanged; override;
ShortCutAsTextnull84 function ShortCutAsText: String; override;
85 end;
86
87 { TIdeMacroEventWriter }
88
89 TIdeMacroEventWriter = class(TSynMacroEventWriter)
90 private
91 FText: String;
92 FCmdName, FParams: String;
93 FUseLineFeed: Boolean;
94 public
95 constructor Create;
96 procedure BeginEvent;
97 procedure FinishEvent;
98 procedure WriteEventCommand(const ACmd: TSynEditorCommand); override;
99 procedure WriteEventParam(const AParam: string); override;
100 procedure WriteEventParam(const AParam: integer); override;
101 property Text: String read FText;
102 property UseLineFeed: Boolean read FUseLineFeed write FUseLineFeed;
103 end;
104
105 { TIdeMacroEventReader }
106
107 TIdeMacroEventReader = class(TSynMacroEventReader)
108 private
109 FErrorText: String;
110 FEventName: String;
111 FHasError: Boolean;
112 FText, FOrigText: String;
113 FPos, FPosCompensate: Integer;
114 FEventCommand: TSynEditorCommand;
115 FParams: Array of record
116 ParamType: TSynEventParamType;
117 Text : String;
118 Num: Integer;
119 end;
120 protected
GetParamAsIntnull121 function GetParamAsInt(Index: Integer): Integer; override;
GetParamAsStringnull122 function GetParamAsString(Index: Integer): String; override;
GetParamTypenull123 function GetParamType(Index: Integer): TSynEventParamType; override;
PosToXYnull124 function PosToXY: TPoint;
AddErrornull125 function AddError(AMsg: string): Boolean;
126 public
127 constructor Create(const Atext: String);
EventCommandnull128 function EventCommand: TSynEditorCommand; override;
ParamCountnull129 function ParamCount: Integer; override;
ParseNextEventnull130 function ParseNextEvent: Boolean;
131 property EventName: String read FEventName;
132 property HasError: Boolean read FHasError;
133 property ErrorText: String read FErrorText;
134 end;
135
136 { TEditorMacroList }
137
138 TEditorMacroList = class;
139 TMacroAddedEvent = procedure(AList: TEditorMacroList; AMacro: TEditorMacro) of object;
140
141 TEditorMacroList = class
142 private
143 FList: TList;
144 FOnAdded: TMacroAddedEvent;
145 FOnChange: TNotifyEvent;
146 FOnRemove: TMacroAddedEvent;
GetMacrosnull147 function GetMacros(Index: Integer): TEditorMacro;
148 procedure DoChanged;
149 procedure DoAdded(AMacro: TEditorMacro);
150 procedure DoRemove(AMacro: TEditorMacro);
151 public
152 constructor Create;
153 destructor Destroy; override;
154 procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String);
155 procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
156 procedure ClearAndFreeMacros;
Countnull157 function Count: Integer;
IndexOfnull158 function IndexOf(AMacro: TEditorMacro): Integer;
IndexOfNamenull159 function IndexOfName(AName: String): Integer;
UniqNamenull160 function UniqName(AName: String): String;
Addnull161 function Add(AMacro: TEditorMacro): Integer;
162 procedure Delete(AnIndex: Integer);
163 procedure Remove(AMacro: TEditorMacro);
164 property Macros[Index: Integer]: TEditorMacro read GetMacros;
165 property OnChange: TNotifyEvent read FOnChange write FOnChange;
166 property OnAdded: TMacroAddedEvent read FOnAdded write FOnAdded;
167 property OnRemove: TMacroAddedEvent read FOnRemove write FOnRemove;
168 end;
169
170 { TMacroListView }
171
172 TMacroListView = class(TForm)
173 btnEdit: TButton;
174 btnSetKeys: TButton;
175 btnPlay: TButton;
176 btnRecord: TButton;
177 btnRecordStop: TButton;
178 btnDelete: TButton;
179 btnSelect: TButton;
180 btnRename: TButton;
181 ButtonPanel1: TButtonPanel;
182 chkRepeat: TCheckBox;
183 GroupBox1: TGroupBox;
184 lbMoveTo: TLabel;
185 lbMacroView: TListView;
186 mnExport: TMenuItem;
187 mnImport: TMenuItem;
188 OpenDialog1: TOpenDialog;
189 Panel1: TPanel;
190 PanelRepeat: TPanel;
191 pnlButtons: TPanel;
192 PopupMenu1: TPopupMenu;
193 RenameButton: TPanelBitBtn;
194 edRepeat: TSpinEdit;
195 SaveDialog1: TSaveDialog;
196 ToolBar1: TToolBar;
197 tbRecorded: TToolButton;
198 tbProject: TToolButton;
199 tbIDE: TToolButton;
200 ToolBar2: TToolBar;
201 tbMoveProject: TToolButton;
202 tbMoveIDE: TToolButton;
203 ToolButton3: TToolButton;
204 ToolButton4: TToolButton;
205 procedure btnDeleteClick(Sender: TObject);
206 procedure btnEditClick(Sender: TObject);
207 procedure btnPlayClick(Sender: TObject);
208 procedure btnRecordClick(Sender: TObject);
209 procedure btnRecordStopClick(Sender: TObject);
210 procedure btnRenameClick(Sender: TObject);
211 procedure btnSelectClick(Sender: TObject);
212 procedure btnSetKeysClick(Sender: TObject);
213 procedure FormActivate(Sender: TObject);
214 procedure HelpButtonClick(Sender: TObject);
215 procedure lbMacroViewSelectItem(Sender: TObject; {%H-}Item: TListItem; {%H-}Selected: Boolean);
216 procedure mnExportClick(Sender: TObject);
217 procedure mnImportClick(Sender: TObject);
218 procedure tbIDEClick(Sender: TObject);
219 procedure tbMoveIDEClick(Sender: TObject);
220 procedure tbMoveProjectClick(Sender: TObject);
221 procedure tbProjectClick(Sender: TObject);
222 procedure tbRecordedClick(Sender: TObject);
223 private
224 FImageRec: Integer;
225 FImagePlay: Integer;
226 FImageSel: Integer;
227 FImageErr: Integer;
228 FIsPlaying: Boolean;
229 FIgnoreMacroChanges: Boolean;
230 procedure DoOnMacroListChange(Sender: TObject);
231 procedure DoMacroContentChanged(Sender: TObject);
232 procedure DoMacroStateChanged(Sender: TObject);
233 procedure UpdateButtons;
234 protected
235 procedure DoEditorMacroStateChanged;
236 public
237 constructor Create(TheOwner: TComponent); override;
238 destructor Destroy; override;
MacroByFullNamenull239 function MacroByFullName(AName: String): TEditorMacro;
240 procedure UpdateDisplay;
241 end;
242
MacroListViewernull243 function MacroListViewer: TMacroListView;
244 procedure ShowMacroListViewer;
245 procedure UpdateMacroListViewer;
246 procedure DoEditorMacroStateChanged;
247
248 procedure LoadProjectSpecificInfo(XMLConfig: TXMLConfig);
249 procedure SaveProjectSpecificInfo(XMLConfig: TXMLConfig; Flags: TProjectWriteFlags);
250 procedure LoadGlobalInfo;
251 procedure SaveGlobalInfo;
252
253 var
254 OnKeyMapReloaded: procedure of object;
255 OnEditorMacroStateChange: TNotifyEvent;
256 // SelectedEditorMacro: Selected, for playing with default shortcut
257 SelectedEditorMacro: TEditorMacro = nil;
258
259 const
260 EditorMacroVirtualDrive = '%Macro:|'; // do not use \ or /, they can be converted by the IDE
261
262 implementation
263
264 var
265 MacroListView: TMacroListView = nil;
266
267 CurrentEditorMacroList: TEditorMacroList = nil;
268 EditorMacroListRec, EditorMacroListProj, EditorMacroListGlob: TEditorMacroList;
269
270 // CurrentRecordingMacro:
271 // The player-macro, to wich the recording in process will be assigned
272 CurrentRecordingMacro: TEditorMacro = nil;
273
274 MacroRecCounter: Integer = 1;
275 KeyCategory: TIDECommandCategory = nil;
276
277 const
278 GlobalConfFileName = 'EditorMacros.xml';
279
MacroListViewernull280 function MacroListViewer: TMacroListView;
281 begin
282 if MacroListView = nil then
283 MacroListView := TMacroListView.Create(Application);
284 Result := MacroListView;
285 end;
286
287 procedure ShowMacroListViewer;
288 begin
289 IDEWindowCreators.ShowForm(MacroListViewer, True);
290 end;
291
292 procedure UpdateMacroListViewer;
293 begin
294 if MacroListView <> nil then
295 MacroListView.UpdateDisplay;
296 end;
297
MacroListToNamenull298 function MacroListToName(AList: TEditorMacroList): string;
299 begin
300 Result := '';
301 if AList = EditorMacroListRec then Result := 'Rec'
302 else if AList = EditorMacroListProj then Result := 'Prj'
303 else if AList = EditorMacroListGlob then Result := 'Ide';
304 end;
305
NameToMacroListnull306 function NameToMacroList(AName: string): TEditorMacroList;
307 begin
308 Result := nil;
309 if AName = 'Rec' then Result := EditorMacroListRec
310 else if AName = 'Prj' then Result := EditorMacroListProj
311 else if AName = 'Ide' then Result := EditorMacroListGlob;
312 end;
313
314 procedure DoEditorMacroStateChanged;
315 begin
316 if EditorMacroForRecording= nil then exit;
317
318 if not(EditorMacroForRecording.State in [emRecording, emRecPaused]) and
319 (CurrentRecordingMacro <> nil)
320 then begin
321 // finished recording
322 if EditorMacroForRecording.IsEmpty then begin
323 EditorMacroListRec.Remove(CurrentRecordingMacro);
324 FreeAndNil(CurrentRecordingMacro);
325 end else begin
326 CurrentRecordingMacro.AssignEventsFrom(EditorMacroForRecording);
327 SelectedEditorMacro := CurrentRecordingMacro;
328 CurrentRecordingMacro := nil;
329 end;
330 end;
331
332 if (EditorMacroForRecording.State = emRecording) and (CurrentRecordingMacro = nil) then begin
333 CurrentRecordingMacro := EditorMacroPlayerClass.Create(nil);
334 CurrentRecordingMacro.OnStateChange := @MacroListViewer.DoMacroStateChanged;
335 CurrentRecordingMacro.OnChange := @MacroListViewer.DoMacroContentChanged;
336 CurrentRecordingMacro.MacroName := Format(lisNewMacroName, [MacroRecCounter]);
337 inc(MacroRecCounter);
338 EditorMacroListRec.Add(CurrentRecordingMacro);
339 end;
340
341 if MacroListView <> nil then
342 MacroListView.DoEditorMacroStateChanged;
343 end;
344
345 procedure LoadProjectSpecificInfo(XMLConfig: TXMLConfig);
346 begin
347 MacroListViewer.FIgnoreMacroChanges := True;
348 try
349 EditorMacroListProj.ReadFromXmlConf(XMLConfig, '');
350 finally
351 MacroListViewer.FIgnoreMacroChanges := False;
352 end;
353 end;
354
355 procedure SaveProjectSpecificInfo(XMLConfig: TXMLConfig; Flags: TProjectWriteFlags);
356 begin
357 if not (pwfSkipSeparateSessionInfo in Flags) then
358 begin
359 EditorMacroListProj.WriteToXmlConf(XMLConfig, '');
360 end;
361 end;
362
363 procedure LoadGlobalInfo;
364 var
365 Filename: String;
366 XMLConfig: TXMLConfig;
367 begin
368 MacroListViewer.FIgnoreMacroChanges := True;
369 Filename := TrimFilename(AppendPathDelim(GetPrimaryConfigPath)+GlobalConfFileName);
370 try
371 XMLConfig := TXMLConfig.Create(Filename);
372 try
373 EditorMacroListGlob.ReadFromXmlConf(XMLConfig, '');
374 finally
375 XMLConfig.Free;
376 end;
377 except
378 on E: Exception do begin
379 DebugLn('[EditorMacroListViewer.LoadGlobalInfo] error reading "',Filename,'": ',E.Message);
380 end;
381 end;
382 MacroListViewer.FIgnoreMacroChanges := False;
383 end;
384
385 procedure SaveGlobalInfo;
386 var
387 Filename: String;
388 XMLConfig: TXMLConfig;
389 begin
390 Filename := TrimFilename(AppendPathDelim(GetPrimaryConfigPath)+GlobalConfFileName);
391 try
392 XMLConfig := TXMLConfig.CreateClean(Filename);
393 try
394 EditorMacroListGlob.WriteToXmlConf(XMLConfig, '');
395 finally
396 XMLConfig.Free;
397 end;
398 except
399 on E: Exception do begin
400 DebugLn('[EditorMacroListViewer.SaveGlobalInfo] error writing "',Filename,'": ',E.Message);
401 end;
402 end;
403 end;
404
405 { TIdeEditorMacroKeyBinding }
406
GetIdeCmdnull407 function TIdeEditorMacroKeyBinding.GetIdeCmd: TIDECommand;
408 begin
409 Result := FIdeCmd;
410 end;
411
412 procedure TIdeEditorMacroKeyBinding.ExecMacro(Sender: TObject);
413 begin
414 if ActiveEditorMacro <> nil then exit;
415 FOwner.PlaybackMacro(TCustomSynEdit(SourceEditorManagerIntf.ActiveEditor.EditorControl));
416 end;
417
418 procedure TIdeEditorMacroKeyBinding.MacroNameChanged;
419 begin
420 if (IDECommandList = nil) then
421 exit;
422
423 if FOwner.IsInvalid then begin
424 if FIdeCmd <> nil then begin
425 (IDECommandList as TKeyCommandRelationList).RemoveCommand(FIdeCmd);
426 FreeAndNil(FIdeCmd);
427 end;
428 exit;
429 end;
430
431 if KeyCategory = nil then
432 KeyCategory := IDECommandList.CreateCategory(nil, 'EditorMacros',
433 'Editor Macros', IDECmdScopeSrcEditOnly);
434
435 if FIdeCmd = nil then begin
436 FIdeCmd := (IDECommandList as TKeyCommandRelationList).CreateCommand(
437 KeyCategory,
438 'EdtMacro'+FOwner.MacroName,
439 FOwner.MacroName,
440 IDEShortCut(VK_UNKNOWN, [], VK_UNKNOWN, []),
441 IDEShortCut(VK_UNKNOWN, [], VK_UNKNOWN, []),
442 @ExecMacro, nil
443 );
444 (FIdeCmd as TKeyCommandRelation).SkipSaving := True;
445 end
446 else
447 FIdeCmd.LocalizedName := FOwner.MacroName;
448
449 end;
450
451 destructor TIdeEditorMacroKeyBinding.Destroy;
452 begin
453 inherited Destroy;
454 if (FIdeCmd <> nil) and (IDECommandList <> nil) then begin
455 (IDECommandList as TKeyCommandRelationList).RemoveCommand(FIdeCmd);
456 FreeAndNil(FIdeCmd);
457 end;
458 end;
459
460 procedure TIdeEditorMacroKeyBinding.WriteToXmlConf(AConf: TXMLConfig; const APath: String);
461 procedure ClearKey(const SubPath: string);
462 begin
463 AConf.DeleteValue(SubPath+'Key1');
464 AConf.DeleteValue(SubPath+'Shift1');
465 AConf.DeleteValue(SubPath+'Key2');
466 AConf.DeleteValue(SubPath+'Shift2');
467 end;
468 procedure Store(const SubPath: string; Key: TIDEShortCut);
469 var
470 s: TShiftState;
471 begin
472 AConf.SetDeleteValue(SubPath+'Key1', key.Key1, VK_UNKNOWN);
473 if key.Key1=VK_UNKNOWN then
474 s:=[]
475 else
476 s:=key.Shift1;
477 AConf.SetDeleteValue(SubPath+'Shift1',ShiftStateToCfgStr(s),ShiftStateToCfgStr([]));
478 AConf.SetDeleteValue(SubPath+'Key2',key.Key2,VK_UNKNOWN);
479 if key.Key2=VK_UNKNOWN then
480 s:=[]
481 else
482 s:=key.Shift2;
483 AConf.SetDeleteValue(SubPath+'Shift2',ShiftStateToCfgStr(s),ShiftStateToCfgStr([]));
484 end;
485
486 begin
487 if (FIdeCmd = nil) then begin
488 ClearKey(APath + 'KeyA/');
489 ClearKey(APath + 'KeyB/');
490 end else begin
491 Store(APath + 'KeyA/', FIdeCmd.ShortcutA);
492 Store(APath + 'KeyB/', FIdeCmd.ShortcutB);
493 end;
494 end;
495
496 procedure TIdeEditorMacroKeyBinding.ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
497 procedure Load(SubPath: string; out Key: TIDEShortCut);
498 begin
499 key.Key1 := AConf.GetValue(SubPath+'Key1',VK_UNKNOWN);
500 key.Shift1 := CfgStrToShiftState(AConf.GetValue(SubPath+'Shift1',''));
501 key.Key2 := AConf.GetValue(SubPath+'Key2',VK_UNKNOWN);
502 key.Shift2 := CfgStrToShiftState(AConf.GetValue(SubPath+'Shift2',''));
503 end;
504 var
505 SCut: TIDEShortCut;
506 begin
507 if (FIdeCmd <> nil) then begin
508 Load(APath+'KeyA/', SCut);
509 if (IDECommandList as TKeyCommandRelationList).Find(SCut, TSourceEditorWindowInterface) = nil then
510 FIdeCmd.ShortcutA := SCut;
511
512 Load(APath+'KeyB/', SCut);
513 if (IDECommandList as TKeyCommandRelationList).Find(SCut, TSourceEditorWindowInterface) = nil then
514 FIdeCmd.ShortcutB := SCut;
515 end;
516 end;
517
ShortCutAsTextnull518 function TIdeEditorMacroKeyBinding.ShortCutAsText: String;
519 begin
520 Result := '';
521 If FIdeCmd = nil then
522 exit;
523 if not IDEShortCutEmpty(FIdeCmd.ShortcutA) then
524 Result := Result + ' (' + KeyAndShiftStateToEditorKeyString(FIdeCmd.ShortcutA) + ')';
525 if not IDEShortCutEmpty(FIdeCmd.ShortcutB) then
526 Result := Result + ' (' + KeyAndShiftStateToEditorKeyString(FIdeCmd.ShortcutB) + ')';
527 end;
528
529 { TIdeEditorMacro }
530
GetMacroNamenull531 function TIdeEditorMacro.GetMacroName: String;
532 begin
533 Result := FMacroName;
534 end;
535
536 procedure TIdeEditorMacro.DoMacroRecorderState(Sender: TObject);
537 begin
538 DoStateChanged;
539 CheckStateAndActivated;
540 end;
541
542 procedure TIdeEditorMacro.DoMacroRecorderUserCommand(aSender: TCustomSynMacroRecorder;
543 aCmd: TSynEditorCommand; var aEvent: TSynMacroEvent);
544 begin
545 case aCmd of
546 ecSynMacroPlay, ecSynMacroRecord,
547 ecToggleFormUnit..ecViewThreads, ecViewHistory,
548 ecNextEditor, ecPrevEditor, ecNextWindow, ecPrevWindow,
549 ecPrevEditorInHistory, ecNextEditorInHistory,
550 ecGotoEditor1..ecGotoEditor0:
551 aEvent := TSynSkippedEvent.Create;
552 else
553 ;//
554 end;
555 end;
556
GetStatenull557 function TIdeEditorMacro.GetState: TEditorMacroState;
558 begin
559 case FSynMacro.state of
560 msStopped: Result := emStopped;
561 msRecording: Result := emRecording;
562 msPlaying: Result := emPlaying;
563 msPaused: Result := emRecPaused;
564 end;
565 end;
566
GetErrorMsgnull567 function TIdeEditorMacro.GetErrorMsg: String;
568 begin
569 Result := FErrorMsg;
570 end;
571
GetKeyBindingnull572 function TIdeEditorMacro.GetKeyBinding: TEditorMacroKeyBinding;
573 begin
574 if FKeyBinding = nil then
575 FKeyBinding := GetDefaultKeyBinding;
576 Result := FKeyBinding;
577 end;
578
579 procedure TIdeEditorMacro.SetMacroName(AValue: string);
580 begin
581 FMacroName := AValue;
582 FSynMacro.MacroName := AValue;
583 DoChanged;
584 end;
585
586 constructor TIdeEditorMacro.Create(aOwner: TComponent);
587 begin
588 FSynMacro := TSynEditorMacro.Create(aOwner);
589 FHasError := False;
590
591 FSynMacro.OnUserCommand := @DoMacroRecorderUserCommand;
592 FSynMacro.OnStateChange := @DoMacroRecorderState;
593 FSynMacro.RecordCommandID := ecNone; // ecSynMacroRecord;
594 FSynMacro.PlaybackCommandID := ecNone; // ecSynMacroPlay;
595 FSynMacro.RecordShortCut := 0;
596 FSynMacro.PlaybackShortCut := 0;
597 end;
598
599 destructor TIdeEditorMacro.Destroy;
600 begin
601 inherited Destroy;
602 FreeAndNil(FSynMacro);
603 FreeAndNil(FKeyBinding);
604 end;
605
606 procedure TIdeEditorMacro.AssignEventsFrom(AMacroRecorder: TEditorMacro);
607 begin
608 if AMacroRecorder = nil then
609 Clear
610 else
611 if AMacroRecorder is TIdeEditorMacro then
612 FSynMacro.AssignEventsFrom(TIdeEditorMacro(AMacroRecorder).FSynMacro)
613 else
614 SetFromSource(AMacroRecorder.GetAsSource);
615
616 DoChanged;
617 end;
618
TIdeEditorMacro.AddEditornull619 function TIdeEditorMacro.AddEditor(AValue: TCustomSynEdit): integer;
620 begin
621 Result := FSynMacro.AddEditor(AValue);
622 end;
623
624 procedure TIdeEditorMacro.DoRecordMacro(aEditor: TWinControl);
625 begin
626 FSynMacro.RecordMacro(aEditor as TCustomSynEdit);
627 end;
628
629 procedure TIdeEditorMacro.DoPlaybackMacro(aEditor: TWinControl);
630 begin
631 FSynMacro.PlaybackMacro(aEditor as TCustomSynEdit);
632 end;
633
634 procedure TIdeEditorMacro.DoStop;
635 begin
636 FSynMacro.Stop;
637 end;
638
639 procedure TIdeEditorMacro.DoPause;
640 begin
641 FSynMacro.Pause;
642 end;
643
644 procedure TIdeEditorMacro.DoResume;
645 begin
646 FSynMacro.Resume;
647 end;
648
649 procedure TIdeEditorMacro.Clear;
650 begin
651 FSynMacro.Clear;
652
653 DoChanged;
654 end;
655
TIdeEditorMacro.GetAsSourcenull656 function TIdeEditorMacro.GetAsSource: String;
657 var
658 i : integer;
659 W: TIdeMacroEventWriter;
660 begin
661 if FHasError then begin
662 Result := FFailedText;
663 exit;
664 end;
665
666 W := TIdeMacroEventWriter.Create;
667 W.UseLineFeed := True;
668 try
669 for i := 0 to FSynMacro.EventCount -1 do
670 begin
671 W.BeginEvent;
672 FSynMacro.Events[i].SaveToWriter(W);
673 W.FinishEvent;
674 end;
675 Result := w.Text;
676 finally
677 W.Free;
678 end;
679 end;
680
681 procedure TIdeEditorMacro.SetFromSource(const AText: String);
682 var
683 iEvent: TSynMacroEvent;
684 R: TIdeMacroEventReader;
685 begin
686 Stop;
687 FSynMacro.Clear;
688 FHasError := False;
689
690 R := TIdeMacroEventReader.Create(AText);
691 try
692 while R.ParseNextEvent do begin
693 iEvent := FSynMacro.CreateMacroEvent(R.EventCommand);
694 iEvent.LoadFromReader(R);
695 FSynMacro.InsertCustomEvent(FSynMacro.EventCount, iEvent);
696 end;
697 if R.HasError then begin
698 FHasError := True;
699 FErrorMsg := R.ErrorText;
700 FFailedText := AText;
701 MacroName := MacroName;
702 end;
703 finally
704 R.Free;
705 end;
706 DoChanged;
707 end;
708
709 procedure TIdeEditorMacro.WriteToXmlConf(AConf: TXMLConfig; const APath: String);
710 begin
711 AConf.SetValue(APath + 'Name', MacroName);
712 AConf.SetValue(APath + 'Code/Value', GetAsSource);
713
714 if (KeyBinding <> nil) then
715 KeyBinding.WriteToXmlConf(AConf, APath);
716 end;
717
718 procedure TIdeEditorMacro.ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
719 var
720 s: String;
721 begin
722 s := AConf.GetValue(APath + 'Code/Value', '');
723 SetFromSource(s);
724 s := AConf.GetValue(APath + 'Name', '');
725 if s <> '' then MacroName := s;
726
727 if (not FHasError) and (FSynMacro.EventCount = 0) then begin
728 FHasError := True;
729 FErrorMsg := 'No content found';
730 FFailedText := s;
731 end;
732
733 if (KeyBinding <> nil) then
734 KeyBinding.ReadFromXmlConf(AConf, APath);
735
736 DoChanged;
737 end;
738
IsEmptynull739 function TIdeEditorMacro.IsEmpty: Boolean;
740 begin
741 Result := FSynMacro.IsEmpty;
742 end;
743
IsInvalidnull744 function TIdeEditorMacro.IsInvalid: Boolean;
745 begin
746 Result := FHasError;
747 end;
748
TIdeEditorMacro.IsRecordingnull749 function TIdeEditorMacro.IsRecording(AnEditor: TWinControl): Boolean;
750 begin
751 Result := (State in [emRecording, emRecPaused]) and
752 (FSynMacro.CurrentEditor = AnEditor);
753 end;
754
755 { TIdeMacroEventReader }
756
TIdeMacroEventReader.GetParamAsIntnull757 function TIdeMacroEventReader.GetParamAsInt(Index: Integer): Integer;
758 begin
759 if (Index < 0) or (Index >= Length(FParams)) or (ParamType[Index] <> ptInteger)
760 then begin
761 FHasError := True;
762 AddError('Wrong amount of param');
763 exit(0);
764 end;
765 Result := FParams[Index].Num;
766 end;
767
GetParamAsStringnull768 function TIdeMacroEventReader.GetParamAsString(Index: Integer): String;
769 begin
770 if (Index < 0) or (Index >= Length(FParams)) or (ParamType[Index] <> ptString)
771 then begin
772 FHasError := True;
773 AddError('Wrong amount of param');
774 exit('');
775 end;
776 Result := FParams[Index].Text;
777 end;
778
TIdeMacroEventReader.GetParamTypenull779 function TIdeMacroEventReader.GetParamType(Index: Integer): TSynEventParamType;
780 begin
781 if (Index < 0) or (Index >= Length(FParams)) then begin
782 FHasError := True;
783 AddError('Wrong amount of param');
784 exit(ptString); // What to return here?
785 end;
786 Result := FParams[Index].ParamType;
787 end;
788
PosToXYnull789 function TIdeMacroEventReader.PosToXY: TPoint;
790 var
791 f: TStringList;
792 begin
793 f := TStringList.Create;
794 f.Text := copy(FOrigText,1 ,FPos);
795 if f.Count = 0 then begin
796 Result.y := 1;
797 Result.x := 1;
798 end
799 else begin
800 Result.y := f.Count;
801 Result.x := length(f[f.Count-1])+1;
802 end;
803 f.Free;
804 end;
805
AddErrornull806 function TIdeMacroEventReader.AddError(AMsg: string): Boolean;
807 var
808 p: TPoint;
809 begin
810 p := PosToXY;
811 FErrorText := FErrorText + Format('Error: %s at Line %d, Column %d', [AMsg, p.y, p.x]);
812 Result := False;
813 end;
814
815 constructor TIdeMacroEventReader.Create(const Atext: String);
816 var
817 i: Integer;
818 s: String;
819 begin
820 FText := Atext;
821 FOrigText := Atext;
822 FHasError := False;
823 FErrorText := '';
824
825 FText := TrimRight(FText);
826 FPosCompensate := Length(FOrigText) - Length(FText);
827
828 FText := TrimLeft(FText);
829 i := length(FText);
830 if (i > 11) and
831 (lowercase(copy(FText, 1, 5)) = 'begin') and
832 (FText[6] in [#9,#10,#13,' ']) and
833 (lowercase(copy(FText, i-3, 4)) = 'end.') and
834 (FText[i-4] in [#9,#10,#13,' '])
835 then begin
836 FText := copy(FText, 7, i-11);
837 FPosCompensate := FPosCompensate + 4;
838 s := TrimRight(FText);
839 FPosCompensate := FPosCompensate + Length(FText) - Length(s);
840 FText := Trim(FText);
841 end;
842 end;
843
EventCommandnull844 function TIdeMacroEventReader.EventCommand: TSynEditorCommand;
845 begin
846 Result := FEventCommand;
847 end;
848
ParamCountnull849 function TIdeMacroEventReader.ParamCount: Integer;
850 begin
851 Result := Length(FParams);
852 end;
853
TIdeMacroEventReader.ParseNextEventnull854 function TIdeMacroEventReader.ParseNextEvent: Boolean;
855 procedure SkipNum(var i: integer);
856 begin
857 while (i <= Length(FText)) and (FText[i] in ['0'..'9']) do
858 inc(i);
859 end;
860 procedure SkipSpace(var i: integer);
861 begin
862 while (i <= Length(FText)) and (FText[i] in [' ', #9, #13, #10]) do
863 inc(i);
864 end;
865 var
866 c,i,j,k: Integer;
867 s: String;
868 begin
869 FEventName := '';
870 FText := TrimLeft(FText);
871
872 Result := (FText <> '') and (not FHasError);
873 if not Result then exit;
874 Result := False;
875 FPos := Length(FOrigText) - Length(FText) - FPosCompensate;
876
877 FHasError := True; // Assume the worst
878
879 i := 1;
880 while (i <= Length(FText)) and (FText[i] in ['a'..'z','A'..'Z','0'..'9','_']) do inc (i);
881 if i = 1 then exit(AddError('Expected Command, but found "'+UTF8Copy(FText,1,1)+'"'));
882
883 s := Copy(FText, 1, i-1);
884 j:=0;
885 if not IdentToEditorCommand(s, j) then exit(AddError('Unknown Command "'+s+'"'));
886 FEventCommand := j;
887 FEventName := s;
888
889 FPos := Length(FOrigText) - Length(FText) - FPosCompensate;
890 while (i <= Length(FText)) and (FText[i] in [' ', #9]) do inc (i);
891 if (i > Length(FText)) then exit(AddError('Expected "(" or ";" bot got end of file'));
892
893 SetLength(FParams, 0);
894 c := 0;
895
896 if (FText[i] = '(') then begin
897 inc(i);
898 repeat
899 SkipSpace(i);
900 if (i > Length(FText)) then exit(AddError('Unexpected end of file in params'));
901
902 if FText[i] in ['0'..'9'] then begin
903 // Parse number
904 j := i;
905 SkipNum(i);
906 SetLength(FParams, c + 1);
907 FParams[c].ParamType := ptInteger;
908 FParams[c].Num := StrToInt(copy(FText, j, i-j));
909 inc(c);
910 end
911 else
912 if FText[i] in ['#', ''''] then begin
913 // Parse string
914 s := '';
915 repeat
916 case FText[i] of
917 ' ',#9,#10,#13: inc(i);
918 '+': begin
919 inc(i);
920 if not (FText[i] in ['''', '#']) then exit(AddError('Expected string or char after +'));
921 end;
922 '#': begin
923 inc(i);
924 j := i;
925 SkipNum(i);
926 if i = j then exit(AddError('Expected number in string after #'));
927 k := StrToInt(copy(FText, j, i-j));
928 if k > 255 then exit(AddError('Argument to long'));
929 s := s + chr(k);
930 end;
931 '''': begin
932 inc(i);
933 repeat
934 case FText[i] of
935 '''': begin
936 if (i+1 <= Length(FText)) and (FText[i+1] = '''') then begin
937 s := s + '''';
938 inc(i,2);
939 end
940 else begin
941 inc(i);
942 break;
943 end;
944 end;
945 else begin
946 s := s + FText[i];
947 inc(i);
948 end;
949 end;
950 until i > Length(FText);
951 end;
952 else
953 break;
954 end;
955 until i > Length(FText);
956
957 SetLength(FParams, c + 1);
958 FParams[c].ParamType := ptString;
959 FParams[c].Text := s;
960 inc(c);
961 end
962 else
963 if FText[i] <> ')' then
964 exit(AddError('Unknown Arguent'));
965
966 SkipSpace(i);
967 if (i >= Length(FText)) then exit(AddError('Missing ")"'));
968 if FText[i] = ')' then break;
969 if not(FText[i] = ',') then exit(AddError('Expected ","'));
970 inc(i);
971 until i > Length(FText);
972 inc(i);
973 end;
974
975 if (i > Length(FText)) then exit(AddError('Missing ";"'));
976 if (FText[i] = ';') then begin
977 Delete(FText, 1, i);
978 Result := True;
979 FHasError := False;
980 exit;
981 end;
982 AddError('Unknown Error');
983 end;
984
985 { TSynMacroEventWriter }
986
987 constructor TIdeMacroEventWriter.Create;
988 begin
989 FUseLineFeed := False;
990 end;
991
992 procedure TIdeMacroEventWriter.BeginEvent;
993 begin
994 FCmdName := '';
995 FParams := '';
996 end;
997
998 procedure TIdeMacroEventWriter.FinishEvent;
999 begin
1000 FText := FText + FCmdName;
1001 if FParams <> '' then
1002 FText := FText + '(' + FParams + ')';
1003 FText := FText + ';';
1004 if FUseLineFeed then
1005 FText := FText + LineEnding;
1006 end;
1007
1008 procedure TIdeMacroEventWriter.WriteEventCommand(const ACmd: TSynEditorCommand);
1009 begin
1010 EditorCommandToIdent(ACmd, FCmdName);
1011 end;
1012
1013 procedure TIdeMacroEventWriter.WriteEventParam(const AParam: string);
1014 var
1015 s: String;
1016 i: Integer;
1017 InQuotes: Boolean;
1018 begin
1019 if FParams <> '' then
1020 FParams := FParams + ', ';
1021 s := '';
1022 InQuotes := False;
1023 for i := 1 to length(AParam) do
1024 case AParam[i] of
1025 #0..#31: begin
1026 if InQuotes then s := s + '''';
1027 InQuotes := False;
1028 s := s + '#' + IntToStr(ord(AParam[i]));
1029 end;
1030 '''': begin
1031 if not InQuotes then s := s + '''';
1032 InQuotes := True;
1033 s := s + '''''';
1034 end;
1035 else begin
1036 if not InQuotes then s := s + '''';
1037 InQuotes := True;
1038 s := s + AParam[i];
1039 end;
1040 end;
1041 if InQuotes then s := s + '''';
1042 FParams := FParams + s;
1043 end;
1044
1045 procedure TIdeMacroEventWriter.WriteEventParam(const AParam: integer);
1046 begin
1047 if FParams <> '' then
1048 FParams := FParams + ', ';
1049 FParams := FParams + IntToStr(AParam);
1050 end;
1051
1052 { TMacroListView }
1053
1054 procedure TMacroListView.btnRenameClick(Sender: TObject);
1055 var
1056 s: String;
1057 M: TEditorMacro;
1058 begin
1059 if lbMacroView.ItemIndex < 0 then exit;
1060 M := CurrentEditorMacroList.Macros[lbMacroView.ItemIndex];
1061 s := M.MacroName;
1062 if InputQuery(lisNewMacroname2, Format(lisEnterNewNameForMacroS, [m.MacroName]), s)
1063 then begin
1064 while (s <> '') and (CurrentEditorMacroList.IndexOfName(s) >= 0) do begin
1065 case IDEMessageDialog(lisDuplicateName, lisAMacroWithThisNameAlreadyExists, mtWarning,
1066 mbOKCancel) of
1067 mrOK:
1068 if not InputQuery(lisNewMacroname2, Format(lisEnterNewNameForMacroS, [m.MacroName]), s)
1069 then s := '';
1070 else
1071 s := '';
1072 end;
1073 end;
1074
1075 if s <> '' then
1076 M.MacroName := s;
1077 UpdateDisplay;
1078 end;
1079 end;
1080
1081 procedure TMacroListView.btnPlayClick(Sender: TObject);
1082 var
1083 i: Integer;
1084 M: TEditorMacro;
1085 se: TSourceEditorInterface;
1086 begin
1087 if ActiveEditorMacro <> nil then exit;
1088 if lbMacroView.ItemIndex < 0 then exit;
1089 se := SourceEditorManagerIntf.ActiveEditor;
1090 if se = nil then Exit;
1091
1092 i := 1;
1093 if chkRepeat.Checked then i := edRepeat.Value;
1094 FIsPlaying := True;
1095 UpdateButtons;
1096
1097 M := CurrentEditorMacroList.Macros[lbMacroView.ItemIndex];
1098 try
1099 while i > 0 do begin
1100 M.PlaybackMacro(TCustomSynEdit(se.EditorControl));
1101 Application.ProcessMessages;
1102 dec(i);
1103 if not FIsPlaying then break;
1104 end;
1105 finally
1106 FIsPlaying := False;
1107 UpdateButtons;
1108 end;
1109
1110 end;
1111
1112 procedure TMacroListView.btnDeleteClick(Sender: TObject);
1113 var
1114 m: TEditorMacro;
1115 begin
1116 if lbMacroView.ItemIndex < 0 then exit;
1117 if IDEMessageDialog(lisReallyDelete, lisDeleteSelectedMacro, mtConfirmation, [
1118 mbYes, mbNo]) = mrYes
1119 then begin
1120 if SelectedEditorMacro = CurrentEditorMacroList.Macros[lbMacroView.ItemIndex] then begin
1121 SelectedEditorMacro := nil;
1122 end;
1123 m := CurrentEditorMacroList.Macros[lbMacroView.ItemIndex];
1124 CurrentEditorMacroList.Delete(lbMacroView.ItemIndex);
1125 m.Free;
1126 if CurrentEditorMacroList = EditorMacroListProj then Project1.Modified := True;
1127 if CurrentEditorMacroList = EditorMacroListGlob then MainIDEInterface.SaveEnvironment(False);
1128 UpdateDisplay;
1129 end;
1130 end;
1131
1132 procedure TMacroListView.btnEditClick(Sender: TObject);
1133 var
1134 M: TEditorMacro;
1135 begin
1136 if lbMacroView.ItemIndex < 0 then exit;
1137 M := CurrentEditorMacroList.Macros[lbMacroView.ItemIndex];
1138 if M = nil then exit;
1139 LazarusIDE.DoOpenEditorFile(
1140 EditorMacroVirtualDrive+MacroListToName(CurrentEditorMacroList)+'|'+M.MacroName,
1141 -1, -1, [ofVirtualFile, ofInternalFile]);
1142 end;
1143
1144 procedure TMacroListView.btnRecordClick(Sender: TObject);
1145 var
1146 se: TSourceEditorInterface;
1147 begin
1148 se := SourceEditorManagerIntf.ActiveEditor;
1149 if se = nil then Exit;
1150 if (ActiveEditorMacro = nil) and (EditorMacroForRecording.State = emStopped) then
1151 EditorMacroForRecording.RecordMacro(TCustomSynEdit(se.EditorControl))
1152 else
1153 if EditorMacroForRecording.State = emRecording then
1154 EditorMacroForRecording.Pause
1155 else
1156 if EditorMacroForRecording.State = emRecPaused then
1157 EditorMacroForRecording.Resume;
1158 se.EditorControl.SetFocus;
1159 end;
1160
1161 procedure TMacroListView.btnRecordStopClick(Sender: TObject);
1162 begin
1163 FIsPlaying := False;
1164 EditorMacroForRecording.Stop;
1165 end;
1166
1167 procedure TMacroListView.btnSelectClick(Sender: TObject);
1168 begin
1169 if ActiveEditorMacro <> nil then exit;
1170 if lbMacroView.ItemIndex >= 0 then
1171 SelectedEditorMacro := CurrentEditorMacroList.Macros[lbMacroView.ItemIndex]
1172 else
1173 SelectedEditorMacro:= nil;
1174 UpdateDisplay;
1175 end;
1176
1177 procedure TMacroListView.btnSetKeysClick(Sender: TObject);
1178 var
1179 i: integer;
1180 M: TEditorMacro;
1181 begin
1182 if lbMacroView.ItemIndex < 0 then exit;
1183 M := CurrentEditorMacroList.Macros[lbMacroView.ItemIndex];
1184
1185 if (M.KeyBinding = nil) or (M.KeyBinding.IdeCmd = nil) or
1186 not(M.KeyBinding.IdeCmd is TKeyCommandRelation)
1187 then // only for error macros
1188 exit;
1189
1190 i := (IDECommandList as TKeyCommandRelationList).IndexOf(M.KeyBinding.IdeCmd as TKeyCommandRelation);
1191 if (i >= 0) then
1192 if ShowKeyMappingEditForm(i, (IDECommandList as TKeyCommandRelationList)) = mrOK then begin
1193 if CurrentEditorMacroList = EditorMacroListProj then Project1.Modified := True;
1194 if CurrentEditorMacroList = EditorMacroListGlob then MainIDEInterface.SaveEnvironment(False);
1195 end;
1196 UpdateDisplay;
1197 if OnKeyMapReloaded <> nil then OnKeyMapReloaded();
1198 end;
1199
1200 procedure TMacroListView.DoMacroStateChanged(Sender: TObject);
1201 begin
1202 if OnEditorMacroStateChange <> nil then
1203 OnEditorMacroStateChange(Sender);
1204 end;
1205
1206 procedure TMacroListView.FormActivate(Sender: TObject);
1207 begin
1208 lbMacroView.HideSelection := Active;
1209 end;
1210
1211 procedure TMacroListView.HelpButtonClick(Sender: TObject);
1212 begin
1213 LazarusHelp.ShowHelpForIDEControl(Self);
1214 end;
1215
1216 procedure TMacroListView.lbMacroViewSelectItem(Sender: TObject; Item: TListItem;
1217 Selected: Boolean);
1218 begin
1219 UpdateButtons;
1220 end;
1221
1222 procedure TMacroListView.mnExportClick(Sender: TObject);
1223 var
1224 Conf: TXMLConfig;
1225 begin
1226 if lbMacroView.ItemIndex < 0 then exit;
1227
1228 if SaveDialog1.Execute then begin
1229 Conf := TXMLConfig.Create(SaveDialog1.FileName);
1230 try
1231 Conf.Clear;
1232 Conf.SetValue('EditorMacros/Count', 1);
1233 CurrentEditorMacroList.Macros[lbMacroView.ItemIndex].WriteToXmlConf(Conf, 'EditorMacros/Macro1/');
1234 finally
1235 Conf.Free;
1236 end;
1237 end;
1238 end;
1239
1240 procedure TMacroListView.mnImportClick(Sender: TObject);
1241 var
1242 Conf: TXMLConfig;
1243 NewMacro: TEditorMacro;
1244 begin
1245 if OpenDialog1.Execute then begin
1246 Conf := TXMLConfig.Create(OpenDialog1.FileName);
1247 try
1248 NewMacro := EditorMacroPlayerClass.Create(nil);
1249 NewMacro.OnStateChange := @DoMacroStateChanged;
1250 NewMacro.OnChange := @DoMacroContentChanged;
1251 NewMacro.ReadFromXmlConf(Conf, 'EditorMacros/Macro1/');
1252 if not NewMacro.IsEmpty then begin
1253 CurrentEditorMacroList.Add(NewMacro);
1254 if CurrentEditorMacroList = EditorMacroListProj then Project1.Modified := True;
1255 if CurrentEditorMacroList = EditorMacroListGlob then MainIDEInterface.SaveEnvironment(False);
1256 end
1257 else
1258 NewMacro.Free;
1259 finally
1260 Conf.Free;
1261 end;
1262 end;
1263 end;
1264
1265 procedure TMacroListView.tbIDEClick(Sender: TObject);
1266 begin
1267 CurrentEditorMacroList := EditorMacroListGlob;
1268 UpdateDisplay;
1269 end;
1270
1271 procedure TMacroListView.tbMoveIDEClick(Sender: TObject);
1272 var
1273 i: Integer;
1274 begin
1275 if (lbMacroView.ItemIndex < 0) or (CurrentEditorMacroList = EditorMacroListGlob) then exit;
1276 i := lbMacroView.ItemIndex;
1277 EditorMacroListGlob.Add(CurrentEditorMacroList.Macros[i]);
1278 CurrentEditorMacroList.Delete(i);
1279 if CurrentEditorMacroList = EditorMacroListProj then Project1.Modified := True;
1280 MainIDEInterface.SaveEnvironment(False);
1281 UpdateDisplay;
1282 end;
1283
1284 procedure TMacroListView.tbMoveProjectClick(Sender: TObject);
1285 var
1286 i: Integer;
1287 begin
1288 if (lbMacroView.ItemIndex < 0) or (CurrentEditorMacroList = EditorMacroListProj) then exit;
1289 i := lbMacroView.ItemIndex;
1290 EditorMacroListProj.Add(CurrentEditorMacroList.Macros[i]);
1291 CurrentEditorMacroList.Delete(i);
1292 Project1.Modified := True;
1293 if CurrentEditorMacroList = EditorMacroListGlob then MainIDEInterface.SaveEnvironment(False);
1294 UpdateDisplay;
1295 end;
1296
1297 procedure TMacroListView.tbProjectClick(Sender: TObject);
1298 begin
1299 CurrentEditorMacroList := EditorMacroListProj;
1300 UpdateDisplay;
1301 end;
1302
1303 procedure TMacroListView.tbRecordedClick(Sender: TObject);
1304 begin
1305 CurrentEditorMacroList := EditorMacroListRec;
1306 UpdateDisplay;
1307 end;
1308
1309 procedure TMacroListView.DoOnMacroListChange(Sender: TObject);
1310 begin
1311 UpdateDisplay;
1312
1313 if Sender = EditorMacroListProj then
1314 Project1.SessionModified := True;
1315 end;
1316
1317 procedure TMacroListView.DoMacroContentChanged(Sender: TObject);
1318 begin
1319 if FIgnoreMacroChanges then exit;
1320
1321 if EditorMacroListProj.IndexOf(Sender as TEditorMacro) >= 0 then
1322 Project1.Modified := True;
1323 if EditorMacroListGlob.IndexOf(Sender as TEditorMacro) >= 0 then
1324 MainIDEInterface.SaveEnvironment(False);
1325 end;
1326
1327 procedure TMacroListView.UpdateDisplay;
1328 var
1329 NewItem: TListItem;
1330 i, idx: Integer;
1331 M: TEditorMacro;
1332 begin
1333 idx := lbMacroView.ItemIndex;
1334 lbMacroView.Items.Clear;
1335
1336 for i := 0 to CurrentEditorMacroList.Count - 1 do begin
1337 M := CurrentEditorMacroList.Macros[i];
1338 NewItem := lbMacroView.Items.Add;
1339
1340 if m.KeyBinding <> nil then
1341 NewItem.Caption := M.MacroName + M.KeyBinding.ShortCutAsText
1342 else
1343 NewItem.Caption := M.MacroName;
1344
1345 // Image
1346 if M.IsInvalid then
1347 NewItem.ImageIndex := FImageErr
1348 else
1349 if (m = CurrentRecordingMacro) then
1350 NewItem.ImageIndex := FImageRec
1351 else
1352 if (CurrentRecordingMacro = nil) then begin // If recording, then recorder is selected
1353 If (M = ActiveEditorMacro) and (M.State = emPlaying) then
1354 NewItem.ImageIndex := FImagePlay
1355 else
1356 if (M = SelectedEditorMacro) then
1357 NewItem.ImageIndex := FImageSel;
1358 end;
1359 end;
1360 if idx < lbMacroView.Items.Count then
1361 lbMacroView.ItemIndex := idx
1362 else
1363 lbMacroView.ItemIndex := -1;
1364
1365 lbMacroViewSelectItem(nil, nil, False);
1366 UpdateButtons;
1367 end;
1368
1369 procedure TMacroListView.UpdateButtons;
1370 var
1371 IsSel, IsErr, IsStopped: Boolean;
1372 M: TEditorMacro;
1373 RecState: TEditorMacroState;
1374 begin
1375 IsSel := (lbMacroView.ItemIndex >= 0);
1376 IsStopped := (ActiveEditorMacro = nil);
1377 if IsSel then
1378 M := CurrentEditorMacroList.Macros[lbMacroView.ItemIndex];
1379 IsErr := IsSel and M.IsInvalid;
1380 RecState := emStopped;
1381 if EditorMacroForRecording <> nil then
1382 RecState := EditorMacroForRecording.State;
1383
1384 btnSelect.Enabled := IsStopped and IsSel and (not FIsPlaying) and (not IsErr);
1385 btnRename.Enabled := IsStopped and IsSel and (not FIsPlaying) and (not IsErr);
1386 btnEdit.Enabled := IsStopped and IsSel and (not FIsPlaying);
1387 btnSetKeys.Enabled := IsStopped and IsSel and (not FIsPlaying) and (not IsErr);
1388 btnDelete.Enabled := IsStopped and IsSel and (not FIsPlaying);
1389
1390 btnPlay.Enabled := IsStopped and IsSel and (not FIsPlaying) and (not IsErr);
1391 chkRepeat.Enabled := IsStopped and (not FIsPlaying);
1392 edRepeat.Enabled := IsStopped and (not FIsPlaying);
1393
1394 btnRecord.Enabled := Assigned(SourceEditorManagerIntf.ActiveEditor)
1395 and (RecState in [emStopped, emRecPaused, emRecording])
1396 and (not FIsPlaying);
1397 btnRecordStop.Enabled := (not IsStopped) or FIsPlaying;
1398
1399 if (RecState = emRecording) then
1400 btnRecord.Caption := lisPause
1401 else if (RecState = emRecPaused) then
1402 btnRecord.Caption := lisContinue
1403 else
1404 btnRecord.Caption := lisRecord;
1405
1406 mnImport.Enabled := IsStopped and (not FIsPlaying);
1407 mnExport.Enabled := IsStopped and IsSel and (not FIsPlaying);
1408
1409 tbMoveProject.Visible := CurrentEditorMacroList <> EditorMacroListProj;
1410 tbMoveProject.Enabled := IsStopped and IsSel and (not FIsPlaying);
1411 tbMoveIDE.Visible := CurrentEditorMacroList <> EditorMacroListGlob;
1412 tbMoveIDE.Enabled := IsStopped and IsSel and (not FIsPlaying);
1413
1414 Update;
1415 end;
1416
MacroByFullNamenull1417 function TMacroListView.MacroByFullName(AName: String): TEditorMacro;
1418 const
1419 FolderStart = length(EditorMacroVirtualDrive)+1;
1420 NameStart = FolderStart+length('PRJ|');
1421 var
1422 Alist: TEditorMacroList;
1423 i: Integer;
1424 begin
1425 Result := nil;
1426 If (copy(AName, 1, length(EditorMacroVirtualDrive)) <> EditorMacroVirtualDrive) or
1427 (copy(AName, NameStart-1, 1) <> '|')
1428 then exit;
1429 Alist := NameToMacroList(copy(AName, FolderStart, 3));
1430 if (Alist = nil) then exit;
1431 i := Alist.IndexOfName(copy(AName, NameStart, length(AName)));
1432 if i < 0 then exit;
1433 Result := Alist.Macros[i];
1434 end;
1435
1436 procedure TMacroListView.DoEditorMacroStateChanged;
1437 begin
1438 UpdateDisplay;
1439 end;
1440
1441 constructor TMacroListView.Create(TheOwner: TComponent);
1442 begin
1443 inherited Create(TheOwner);
1444 FIgnoreMacroChanges := False;
1445
1446 Caption := lisEditorMacros;
1447 EditorMacroListRec.OnChange := @DoOnMacroListChange;
1448 EditorMacroListProj.OnChange := @DoOnMacroListChange;
1449 EditorMacroListGlob.OnChange := @DoOnMacroListChange;
1450
1451 tbRecorded.Caption := lisRecordedMacros;
1452 tbProject.Caption := lisProjectMacro;
1453 tbIDE.Caption := lisIDE;
1454 tbRecorded.Hint := lisNewRecordedMacrosNotToBeSaved;
1455 tbProject.Hint := lisSavedWithProjectSession;
1456 tbIDE.Hint := lisSavedWithIDESettings;
1457 tbMoveProject.Caption := lisProjectMacro;
1458 tbMoveIDE.Caption := lisIDE;
1459 lbMoveTo.Caption := lisMoveTo + ' '; // Anchors do not work here. Use spaces.
1460
1461 btnSelect.Caption := lisMakeCurrent;
1462 btnRename.Caption := lisRename2;
1463 btnSetKeys.Caption := lisEditKey;
1464 btnEdit.Caption := lisEdit;
1465 btnDelete.Caption := lisDelete;
1466 btnPlay.Caption := lisPlay;
1467 chkRepeat.Caption := lisRepeat;
1468 btnRecord.Caption := lisRecord;
1469 btnRecordStop.Caption := lisStop;
1470
1471 SaveDialog1.Title := lisSaveMacroAs;
1472 OpenDialog1.Title := lisLoadMacroFrom;
1473 mnImport.Caption := lisDlgImport;
1474 mnExport.Caption := lisDlgExport;
1475
1476 lbMacroView.SmallImages := IDEImages.Images_16;
1477 FImageRec := IDEImages.LoadImage('Record'); // red dot
1478 FImagePlay := IDEImages.LoadImage('menu_run'); // green triangle
1479 FImageSel := IDEImages.LoadImage('arrow_right');
1480 FImageErr := IDEImages.LoadImage('state_error');
1481 FIsPlaying := False;
1482
1483 UpdateDisplay;
1484 end;
1485
1486 destructor TMacroListView.Destroy;
1487 begin
1488 inherited Destroy;
1489 end;
1490
1491 { TEditorMacroList }
1492
TEditorMacroList.GetMacrosnull1493 function TEditorMacroList.GetMacros(Index: Integer): TEditorMacro;
1494 begin
1495 Result := TEditorMacro(FList[Index]);
1496 end;
1497
1498 procedure TEditorMacroList.DoChanged;
1499 begin
1500 if Assigned(FOnChange) then
1501 FOnChange(Self);
1502 end;
1503
1504 procedure TEditorMacroList.DoAdded(AMacro: TEditorMacro);
1505 begin
1506 if Assigned(FOnAdded) then
1507 FOnAdded(Self, AMacro);
1508 end;
1509
1510 procedure TEditorMacroList.DoRemove(AMacro: TEditorMacro);
1511 begin
1512 if Assigned(FOnRemove) then
1513 FOnRemove(Self, AMacro);
1514 end;
1515
1516 constructor TEditorMacroList.Create;
1517 begin
1518 FList := TList.Create;
1519 end;
1520
1521 destructor TEditorMacroList.Destroy;
1522 begin
1523 FOnChange := nil;
1524 ClearAndFreeMacros;
1525 FreeAndNil(FList);
1526 inherited Destroy;
1527 end;
1528
1529 procedure TEditorMacroList.WriteToXmlConf(AConf: TXMLConfig; const APath: String);
1530 var
1531 i: Integer;
1532 begin
1533 AConf.SetDeleteValue(APath + 'EditorMacros/Count', Count, 0);
1534 for i := 0 to Count - 1 do
1535 Macros[i].WriteToXmlConf(AConf, 'EditorMacros/Macro'+IntToStr(i+1)+'/');
1536 end;
1537
1538 procedure TEditorMacroList.ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
1539 var
1540 c, i: Integer;
1541 NewMacro: TEditorMacro;
1542 begin
1543 ClearAndFreeMacros;
1544 c := AConf.GetValue(APath + 'EditorMacros/Count', 0);
1545 for i := 0 to c -1 do begin
1546 NewMacro := EditorMacroPlayerClass.Create(nil);
1547 NewMacro.OnStateChange := @MacroListViewer.DoMacroStateChanged;
1548 NewMacro.OnChange := @MacroListViewer.DoMacroContentChanged;
1549 try
1550 NewMacro.ReadFromXmlConf(AConf, 'EditorMacros/Macro'+IntToStr(i+1)+'/');
1551 finally
1552 Add(NewMacro)
1553 end;
1554 end;
1555 end;
1556
1557 procedure TEditorMacroList.ClearAndFreeMacros;
1558 var
1559 i: Integer;
1560 begin
1561 for i := 0 to Count - 1 do Macros[i].Free;
1562 FList.Clear;
1563 end;
1564
Countnull1565 function TEditorMacroList.Count: Integer;
1566 begin
1567 Result := FList.Count;
1568 end;
1569
IndexOfnull1570 function TEditorMacroList.IndexOf(AMacro: TEditorMacro): Integer;
1571 begin
1572 Result := FList.IndexOf(AMacro);
1573 end;
1574
IndexOfNamenull1575 function TEditorMacroList.IndexOfName(AName: String): Integer;
1576 begin
1577 Result := Count - 1;
1578 while Result >= 0 do
1579 if Macros[Result].MacroName = AName
1580 then break
1581 else dec(Result);
1582 end;
1583
UniqNamenull1584 function TEditorMacroList.UniqName(AName: String): String;
1585 var
1586 i: Integer;
1587 begin
1588 Result := AName;
1589 if IndexOfName(AName) < 0 then exit;
1590 i := 1;
1591 while IndexOfName(AName+'_'+IntToStr(i)) >= 0 do inc(i);
1592 Result := AName+'_'+IntToStr(i);
1593 end;
1594
Addnull1595 function TEditorMacroList.Add(AMacro: TEditorMacro): Integer;
1596 begin
1597 AMacro.MacroName := UniqName(AMacro.MacroName);
1598 Result := FList.Add(AMacro);
1599 DoAdded(AMacro);
1600 DoChanged;
1601 end;
1602
1603 procedure TEditorMacroList.Delete(AnIndex: Integer);
1604 begin
1605 DoRemove(Macros[AnIndex]);
1606 FList.Delete(AnIndex);
1607 DoChanged;
1608 end;
1609
1610 procedure TEditorMacroList.Remove(AMacro: TEditorMacro);
1611 begin
1612 DoRemove(AMacro);
1613 FList.Remove(AMacro);
1614 DoChanged;
1615 end;
1616
1617 // itmMacroListView.enabled
1618
1619 {$R *.lfm}
1620
1621 initialization
1622 if EditorMacroPlayerClass = nil then
1623 EditorMacroPlayerClass := TIdeEditorMacro;
1624 if DefaultBindingClass = nil then
1625 DefaultBindingClass := TIdeEditorMacroKeyBinding;
1626 EditorMacroListRec := TEditorMacroList.Create;
1627 EditorMacroListProj := TEditorMacroList.Create;
1628 EditorMacroListGlob := TEditorMacroList.Create;
1629 CurrentEditorMacroList := EditorMacroListRec;
1630
1631 finalization
1632 CurrentEditorMacroList := nil;
1633 FreeAndNil(EditorMacroListRec);
1634 FreeAndNil(EditorMacroListProj);
1635 FreeAndNil(EditorMacroListGlob);
1636
1637 end.
1638
1639