mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 04:37:54 +02:00
1594 lines
44 KiB
ObjectPascal
1594 lines
44 KiB
ObjectPascal
unit EditorMacroListViewer;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
LazFileUtils, Laz2_XMLCfg, LazUTF8, LazLoggerBase,
|
|
LCLType, Forms, Controls, Dialogs, StdCtrls, ButtonPanel, ComCtrls, ExtCtrls,
|
|
Spin, Menus,
|
|
SynMacroRecorder, SynEdit, SynEditKeyCmds,
|
|
IDEWindowIntf, IDEImagesIntf, SrcEditorIntf, IDEHelpIntf, IDECommands,
|
|
LazIDEIntf,
|
|
LazarusIDEStrConsts, ProjectDefs, LazConf, Project, KeyMapping,
|
|
KeyMapShortCutDlg, MainIntf, IDEDialogs;
|
|
|
|
type
|
|
TSynEditorMacro = class(TSynMacroRecorder) end;
|
|
|
|
{ TIdeEditorMacro }
|
|
|
|
TIdeEditorMacro = class(TEditorMacro)
|
|
private
|
|
FMacroName: String;
|
|
FHasError: Boolean;
|
|
FErrorMsg: String;
|
|
FFailedText: String;
|
|
FSynMacro: TSynEditorMacro;
|
|
FKeyBinding: TEditorMacroKeyBinding;
|
|
|
|
procedure DoMacroRecorderState(Sender: TObject);
|
|
procedure DoMacroRecorderUserCommand({%H-}aSender: TCustomSynMacroRecorder;
|
|
aCmd: TSynEditorCommand; var aEvent: TSynMacroEvent);
|
|
protected
|
|
function GetMacroName: String; override;
|
|
procedure SetMacroName(AValue: string); override;
|
|
function GetState: TEditorMacroState; override;
|
|
function GetErrorMsg: String; override;
|
|
function GetKeyBinding: TEditorMacroKeyBinding; override;
|
|
|
|
procedure DoRecordMacro(aEditor: TWinControl); override;
|
|
procedure DoPlaybackMacro(aEditor: TWinControl); override;
|
|
procedure DoStop; override;
|
|
procedure DoPause; override;
|
|
procedure DoResume; override;
|
|
public
|
|
constructor Create(aOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure AssignEventsFrom(AMacroRecorder: TEditorMacro); override;
|
|
function AddEditor(AValue: TCustomSynEdit): integer;
|
|
|
|
procedure Clear; override;
|
|
|
|
function GetAsSource: String; override;
|
|
procedure SetFromSource(const AText: String); override;
|
|
procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String); override;
|
|
procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String); override;
|
|
|
|
function IsEmpty: Boolean; override;
|
|
function IsInvalid: Boolean; override;
|
|
function IsRecording(AnEditor: TWinControl): Boolean; override;
|
|
end;
|
|
|
|
|
|
{ TIdeEditorMacroKeyBinding }
|
|
|
|
TIdeEditorMacroKeyBinding = class(TEditorMacroKeyBinding)
|
|
protected
|
|
FIdeCmd: TIDECommand;
|
|
function GetIdeCmd: TIDECommand; override;
|
|
procedure ExecMacro(Sender: TObject);
|
|
public
|
|
destructor Destroy; override;
|
|
procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String); override;
|
|
procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String); override;
|
|
procedure MacroNameChanged; override;
|
|
function ShortCutAsText: String; override;
|
|
end;
|
|
|
|
{ TIdeMacroEventWriter }
|
|
|
|
TIdeMacroEventWriter = class(TSynMacroEventWriter)
|
|
private
|
|
FText: String;
|
|
FCmdName, FParams: String;
|
|
FUseLineFeed: Boolean;
|
|
public
|
|
constructor Create;
|
|
procedure BeginEvent;
|
|
procedure FinishEvent;
|
|
procedure WriteEventCommand(const ACmd: TSynEditorCommand); override;
|
|
procedure WriteEventParam(const AParam: string); override;
|
|
procedure WriteEventParam(const AParam: integer); override;
|
|
property Text: String read FText;
|
|
property UseLineFeed: Boolean read FUseLineFeed write FUseLineFeed;
|
|
end;
|
|
|
|
{ TIdeMacroEventReader }
|
|
|
|
TIdeMacroEventReader = class(TSynMacroEventReader)
|
|
private
|
|
FErrorText: String;
|
|
FEventName: String;
|
|
FHasError: Boolean;
|
|
FText, FOrigText: String;
|
|
FPos: Integer;
|
|
FEventCommand: TSynEditorCommand;
|
|
FParams: Array of record
|
|
ParamType: TSynEventParamType;
|
|
Text : String;
|
|
Num: Integer;
|
|
end;
|
|
protected
|
|
function GetParamAsInt(Index: Integer): Integer; override;
|
|
function GetParamAsString(Index: Integer): String; override;
|
|
function GetParamType(Index: Integer): TSynEventParamType; override;
|
|
function PosToXY: TPoint;
|
|
function AddError(AMsg: string): Boolean;
|
|
public
|
|
constructor Create(const Atext: String);
|
|
function EventCommand: TSynEditorCommand; override;
|
|
function ParamCount: Integer; override;
|
|
function ParseNextEvent: Boolean;
|
|
property EventName: String read FEventName;
|
|
property HasError: Boolean read FHasError;
|
|
property ErrorText: String read FErrorText;
|
|
end;
|
|
|
|
{ TEditorMacroList }
|
|
|
|
TEditorMacroList = class;
|
|
TMacroAddedEvent = procedure(AList: TEditorMacroList; AMacro: TEditorMacro) of object;
|
|
|
|
TEditorMacroList = class
|
|
private
|
|
FList: TList;
|
|
FOnAdded: TMacroAddedEvent;
|
|
FOnChange: TNotifyEvent;
|
|
FOnRemove: TMacroAddedEvent;
|
|
function GetMacros(Index: Integer): TEditorMacro;
|
|
procedure DoChanged;
|
|
procedure DoAdded(AMacro: TEditorMacro);
|
|
procedure DoRemove(AMacro: TEditorMacro);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String);
|
|
procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
|
|
procedure ClearAndFreeMacros;
|
|
function Count: Integer;
|
|
function IndexOf(AMacro: TEditorMacro): Integer;
|
|
function IndexOfName(AName: String): Integer;
|
|
function UniqName(AName: String): String;
|
|
function Add(AMacro: TEditorMacro): Integer;
|
|
procedure Delete(AnIndex: Integer);
|
|
procedure Remove(AMacro: TEditorMacro);
|
|
property Macros[Index: Integer]: TEditorMacro read GetMacros;
|
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
|
property OnAdded: TMacroAddedEvent read FOnAdded write FOnAdded;
|
|
property OnRemove: TMacroAddedEvent read FOnRemove write FOnRemove;
|
|
end;
|
|
|
|
{ TMacroListView }
|
|
|
|
TMacroListView = class(TForm)
|
|
btnEdit: TButton;
|
|
btnSetKeys: TButton;
|
|
btnPlay: TButton;
|
|
btnRecord: TButton;
|
|
btnRecordStop: TButton;
|
|
btnDelete: TButton;
|
|
btnSelect: TButton;
|
|
btnRename: TButton;
|
|
ButtonPanel1: TButtonPanel;
|
|
chkRepeat: TCheckBox;
|
|
lbMoveTo: TLabel;
|
|
lbRecordedView: TListView;
|
|
mnExport: TMenuItem;
|
|
mnImport: TMenuItem;
|
|
OpenDialog1: TOpenDialog;
|
|
Panel1: TPanel;
|
|
pnlButtons: TPanel;
|
|
PopupMenu1: TPopupMenu;
|
|
RenameButton: TPanelBitBtn;
|
|
edRepeat: TSpinEdit;
|
|
SaveDialog1: TSaveDialog;
|
|
ToolBar1: TToolBar;
|
|
tbRecorded: TToolButton;
|
|
tbProject: TToolButton;
|
|
tbIDE: TToolButton;
|
|
ToolBar2: TToolBar;
|
|
tbMoveProject: TToolButton;
|
|
tbMoveIDE: TToolButton;
|
|
ToolButton3: TToolButton;
|
|
ToolButton4: TToolButton;
|
|
procedure btnDeleteClick(Sender: TObject);
|
|
procedure btnEditClick(Sender: TObject);
|
|
procedure btnPlayClick(Sender: TObject);
|
|
procedure btnRecordClick(Sender: TObject);
|
|
procedure btnRecordStopClick(Sender: TObject);
|
|
procedure btnRenameClick(Sender: TObject);
|
|
procedure btnSelectClick(Sender: TObject);
|
|
procedure btnSetKeysClick(Sender: TObject);
|
|
procedure FormActivate(Sender: TObject);
|
|
procedure HelpButtonClick(Sender: TObject);
|
|
procedure lbRecordedViewSelectItem(Sender: TObject; {%H-}Item: TListItem; {%H-}Selected: Boolean);
|
|
procedure mnExportClick(Sender: TObject);
|
|
procedure mnImportClick(Sender: TObject);
|
|
procedure tbIDEClick(Sender: TObject);
|
|
procedure tbMoveIDEClick(Sender: TObject);
|
|
procedure tbMoveProjectClick(Sender: TObject);
|
|
procedure tbProjectClick(Sender: TObject);
|
|
procedure tbRecordedClick(Sender: TObject);
|
|
private
|
|
FImageRec: Integer;
|
|
FImagePlay: Integer;
|
|
FImageSel: Integer;
|
|
FImageErr: Integer;
|
|
FIsPlaying: Boolean;
|
|
FIgnoreMacroChanges: Boolean;
|
|
procedure DoOnMacroListChange(Sender: TObject);
|
|
procedure DoMacroContentChanged(Sender: TObject);
|
|
procedure DoMacroStateChanged(Sender: TObject);
|
|
procedure UpdateButtons;
|
|
protected
|
|
procedure DoEditorMacroStateChanged;
|
|
public
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
function MacroByFullName(AName: String): TEditorMacro;
|
|
procedure UpdateDisplay;
|
|
end;
|
|
|
|
function MacroListViewer: TMacroListView;
|
|
procedure ShowMacroListViewer;
|
|
procedure UpdateMacroListViewer;
|
|
procedure DoEditorMacroStateChanged;
|
|
|
|
procedure LoadProjectSpecificInfo(XMLConfig: TXMLConfig);
|
|
procedure SaveProjectSpecificInfo(XMLConfig: TXMLConfig; Flags: TProjectWriteFlags);
|
|
procedure LoadGlobalInfo;
|
|
procedure SaveGlobalInfo;
|
|
|
|
var
|
|
OnKeyMapReloaded: procedure of object;
|
|
OnEditorMacroStateChange: TNotifyEvent;
|
|
// SelectedEditorMacro: Selected, for playing with default shortcut
|
|
SelectedEditorMacro: TEditorMacro = nil;
|
|
|
|
const
|
|
EditorMacroVirtualDrive = '%Macro:|'; // do not use \ or /, they can be converted by the IDE
|
|
|
|
implementation
|
|
|
|
var
|
|
MacroListView: TMacroListView = nil;
|
|
|
|
CurrentEditorMacroList: TEditorMacroList = nil;
|
|
EditorMacroListRec, EditorMacroListProj, EditorMacroListGlob: TEditorMacroList;
|
|
|
|
// CurrentRecordingMacro:
|
|
// The player-macro, to wich the recording in process will be assigned
|
|
CurrentRecordingMacro: TEditorMacro = nil;
|
|
|
|
MacroRecCounter: Integer = 1;
|
|
KeyCategory: TIDECommandCategory = nil;
|
|
|
|
const
|
|
GlobalConfFileName = 'EditorMacros.xml';
|
|
|
|
function MacroListViewer: TMacroListView;
|
|
begin
|
|
if MacroListView = nil then
|
|
MacroListView := TMacroListView.Create(Application);
|
|
Result := MacroListView;
|
|
end;
|
|
|
|
procedure ShowMacroListViewer;
|
|
begin
|
|
IDEWindowCreators.ShowForm(MacroListViewer, True);
|
|
end;
|
|
|
|
procedure UpdateMacroListViewer;
|
|
begin
|
|
if MacroListView <> nil then
|
|
MacroListView.UpdateDisplay;
|
|
end;
|
|
|
|
function MacroListToName(AList: TEditorMacroList): string;
|
|
begin
|
|
Result := '';
|
|
if AList = EditorMacroListRec then Result := 'Rec'
|
|
else if AList = EditorMacroListProj then Result := 'Prj'
|
|
else if AList = EditorMacroListGlob then Result := 'Ide';
|
|
end;
|
|
|
|
function NameToMacroList(AName: string): TEditorMacroList;
|
|
begin
|
|
Result := nil;
|
|
if AName = 'Rec' then Result := EditorMacroListRec
|
|
else if AName = 'Prj' then Result := EditorMacroListProj
|
|
else if AName = 'Ide' then Result := EditorMacroListGlob;
|
|
end;
|
|
|
|
procedure DoEditorMacroStateChanged;
|
|
begin
|
|
if EditorMacroForRecording= nil then exit;
|
|
|
|
if not(EditorMacroForRecording.State in [emRecording, emRecPaused]) and
|
|
(CurrentRecordingMacro <> nil)
|
|
then begin
|
|
// finished recording
|
|
if EditorMacroForRecording.IsEmpty then begin
|
|
EditorMacroListRec.Remove(CurrentRecordingMacro);
|
|
FreeAndNil(CurrentRecordingMacro);
|
|
end else begin
|
|
CurrentRecordingMacro.AssignEventsFrom(EditorMacroForRecording);
|
|
SelectedEditorMacro := CurrentRecordingMacro;
|
|
CurrentRecordingMacro := nil;
|
|
end;
|
|
end;
|
|
|
|
if (EditorMacroForRecording.State = emRecording) and (CurrentRecordingMacro = nil) then begin
|
|
CurrentRecordingMacro := EditorMacroPlayerClass.Create(nil);
|
|
CurrentRecordingMacro.OnStateChange := @MacroListViewer.DoMacroStateChanged;
|
|
CurrentRecordingMacro.OnChange := @MacroListViewer.DoMacroContentChanged;
|
|
CurrentRecordingMacro.MacroName := Format(lisNewMacroName, [MacroRecCounter]);
|
|
inc(MacroRecCounter);
|
|
EditorMacroListRec.Add(CurrentRecordingMacro);
|
|
end;
|
|
|
|
if MacroListView <> nil then
|
|
MacroListView.DoEditorMacroStateChanged;
|
|
end;
|
|
|
|
procedure LoadProjectSpecificInfo(XMLConfig: TXMLConfig);
|
|
begin
|
|
MacroListViewer.FIgnoreMacroChanges := True;
|
|
try
|
|
EditorMacroListProj.ReadFromXmlConf(XMLConfig, '');
|
|
finally
|
|
MacroListViewer.FIgnoreMacroChanges := False;
|
|
end;
|
|
end;
|
|
|
|
procedure SaveProjectSpecificInfo(XMLConfig: TXMLConfig; Flags: TProjectWriteFlags);
|
|
begin
|
|
if not (pwfSkipSeparateSessionInfo in Flags) then
|
|
begin
|
|
EditorMacroListProj.WriteToXmlConf(XMLConfig, '');
|
|
end;
|
|
end;
|
|
|
|
procedure LoadGlobalInfo;
|
|
var
|
|
Filename: String;
|
|
XMLConfig: TXMLConfig;
|
|
begin
|
|
MacroListViewer.FIgnoreMacroChanges := True;
|
|
Filename := TrimFilename(AppendPathDelim(GetPrimaryConfigPath)+GlobalConfFileName);
|
|
try
|
|
XMLConfig := TXMLConfig.Create(Filename);
|
|
try
|
|
EditorMacroListGlob.ReadFromXmlConf(XMLConfig, '');
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
DebugLn('[EditorMacroListViewer.LoadGlobalInfo] error reading "',Filename,'": ',E.Message);
|
|
end;
|
|
end;
|
|
MacroListViewer.FIgnoreMacroChanges := False;
|
|
end;
|
|
|
|
procedure SaveGlobalInfo;
|
|
var
|
|
Filename: String;
|
|
XMLConfig: TXMLConfig;
|
|
begin
|
|
Filename := TrimFilename(AppendPathDelim(GetPrimaryConfigPath)+GlobalConfFileName);
|
|
try
|
|
XMLConfig := TXMLConfig.CreateClean(Filename);
|
|
try
|
|
EditorMacroListGlob.WriteToXmlConf(XMLConfig, '');
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
DebugLn('[EditorMacroListViewer.SaveGlobalInfo] error writing "',Filename,'": ',E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TIdeEditorMacroKeyBinding }
|
|
|
|
function TIdeEditorMacroKeyBinding.GetIdeCmd: TIDECommand;
|
|
begin
|
|
Result := FIdeCmd;
|
|
end;
|
|
|
|
procedure TIdeEditorMacroKeyBinding.ExecMacro(Sender: TObject);
|
|
begin
|
|
if ActiveEditorMacro <> nil then exit;
|
|
FOwner.PlaybackMacro(TCustomSynEdit(SourceEditorManagerIntf.ActiveEditor.EditorControl));
|
|
end;
|
|
|
|
procedure TIdeEditorMacroKeyBinding.MacroNameChanged;
|
|
begin
|
|
if (IDECommandList = nil) then
|
|
exit;
|
|
|
|
if FOwner.IsInvalid then begin
|
|
if FIdeCmd <> nil then begin
|
|
(IDECommandList as TKeyCommandRelationList).RemoveCommand(FIdeCmd);
|
|
FreeAndNil(FIdeCmd);
|
|
end;
|
|
exit;
|
|
end;
|
|
|
|
if KeyCategory = nil then
|
|
KeyCategory := IDECommandList.CreateCategory(nil, 'EditorMacros',
|
|
'Editor Macros', IDECmdScopeSrcEditOnly);
|
|
|
|
if FIdeCmd = nil then begin
|
|
FIdeCmd := (IDECommandList as TKeyCommandRelationList).CreateCommand(
|
|
KeyCategory,
|
|
'EdtMacro'+FOwner.MacroName,
|
|
FOwner.MacroName,
|
|
IDEShortCut(VK_UNKNOWN, [], VK_UNKNOWN, []),
|
|
IDEShortCut(VK_UNKNOWN, [], VK_UNKNOWN, []),
|
|
@ExecMacro, nil
|
|
);
|
|
(FIdeCmd as TKeyCommandRelation).SkipSaving := True;
|
|
end
|
|
else
|
|
FIdeCmd.LocalizedName := FOwner.MacroName;
|
|
|
|
end;
|
|
|
|
destructor TIdeEditorMacroKeyBinding.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
if (FIdeCmd <> nil) and (IDECommandList <> nil) then begin
|
|
(IDECommandList as TKeyCommandRelationList).RemoveCommand(FIdeCmd);
|
|
FreeAndNil(FIdeCmd);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdeEditorMacroKeyBinding.WriteToXmlConf(AConf: TXMLConfig; const APath: String);
|
|
procedure ClearKey(const SubPath: string);
|
|
begin
|
|
AConf.DeleteValue(SubPath+'Key1');
|
|
AConf.DeleteValue(SubPath+'Shift1');
|
|
AConf.DeleteValue(SubPath+'Key2');
|
|
AConf.DeleteValue(SubPath+'Shift2');
|
|
end;
|
|
procedure Store(const SubPath: string; Key: TIDEShortCut);
|
|
var
|
|
s: TShiftState;
|
|
begin
|
|
AConf.SetDeleteValue(SubPath+'Key1', key.Key1, VK_UNKNOWN);
|
|
if key.Key1=VK_UNKNOWN then
|
|
s:=[]
|
|
else
|
|
s:=key.Shift1;
|
|
AConf.SetDeleteValue(SubPath+'Shift1',ShiftStateToCfgStr(s),ShiftStateToCfgStr([]));
|
|
AConf.SetDeleteValue(SubPath+'Key2',key.Key2,VK_UNKNOWN);
|
|
if key.Key2=VK_UNKNOWN then
|
|
s:=[]
|
|
else
|
|
s:=key.Shift2;
|
|
AConf.SetDeleteValue(SubPath+'Shift2',ShiftStateToCfgStr(s),ShiftStateToCfgStr([]));
|
|
end;
|
|
|
|
begin
|
|
if (FIdeCmd = nil) then begin
|
|
ClearKey(APath + 'KeyA/');
|
|
ClearKey(APath + 'KeyB/');
|
|
end else begin
|
|
Store(APath + 'KeyA/', FIdeCmd.ShortcutA);
|
|
Store(APath + 'KeyB/', FIdeCmd.ShortcutB);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdeEditorMacroKeyBinding.ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
|
|
procedure Load(SubPath: string; out Key: TIDEShortCut);
|
|
begin
|
|
key.Key1 := AConf.GetValue(SubPath+'Key1',VK_UNKNOWN);
|
|
key.Shift1 := CfgStrToShiftState(AConf.GetValue(SubPath+'Shift1',''));
|
|
key.Key2 := AConf.GetValue(SubPath+'Key2',VK_UNKNOWN);
|
|
key.Shift2 := CfgStrToShiftState(AConf.GetValue(SubPath+'Shift2',''));
|
|
end;
|
|
var
|
|
SCut: TIDEShortCut;
|
|
begin
|
|
if (FIdeCmd <> nil) then begin
|
|
Load(APath+'KeyA/', SCut);
|
|
if (IDECommandList as TKeyCommandRelationList).Find(SCut, TSourceEditorWindowInterface) = nil then
|
|
FIdeCmd.ShortcutA := SCut;
|
|
|
|
Load(APath+'KeyB/', SCut);
|
|
if (IDECommandList as TKeyCommandRelationList).Find(SCut, TSourceEditorWindowInterface) = nil then
|
|
FIdeCmd.ShortcutB := SCut;
|
|
end;
|
|
end;
|
|
|
|
function TIdeEditorMacroKeyBinding.ShortCutAsText: String;
|
|
begin
|
|
Result := '';
|
|
If FIdeCmd = nil then
|
|
exit;
|
|
if not IDEShortCutEmpty(FIdeCmd.ShortcutA) then
|
|
Result := Result + ' (' + KeyAndShiftStateToEditorKeyString(FIdeCmd.ShortcutA) + ')';
|
|
if not IDEShortCutEmpty(FIdeCmd.ShortcutB) then
|
|
Result := Result + ' (' + KeyAndShiftStateToEditorKeyString(FIdeCmd.ShortcutB) + ')';
|
|
end;
|
|
|
|
{ TIdeEditorMacro }
|
|
|
|
function TIdeEditorMacro.GetMacroName: String;
|
|
begin
|
|
Result := FMacroName;
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.DoMacroRecorderState(Sender: TObject);
|
|
begin
|
|
DoStateChanged;
|
|
CheckStateAndActivated;
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.DoMacroRecorderUserCommand(aSender: TCustomSynMacroRecorder;
|
|
aCmd: TSynEditorCommand; var aEvent: TSynMacroEvent);
|
|
begin
|
|
case aCmd of
|
|
ecSynMacroPlay, ecSynMacroRecord,
|
|
ecToggleFormUnit..ecViewThreads, ecViewHistory,
|
|
ecNextEditor, ecPrevEditor, ecNextWindow, ecPrevWindow,
|
|
ecPrevEditorInHistory, ecNextEditorInHistory,
|
|
ecGotoEditor1..ecGotoEditor0:
|
|
aEvent := TSynSkippedEvent.Create;
|
|
else
|
|
;//
|
|
end;
|
|
end;
|
|
|
|
function TIdeEditorMacro.GetState: TEditorMacroState;
|
|
begin
|
|
case FSynMacro.state of
|
|
msStopped: Result := emStopped;
|
|
msRecording: Result := emRecording;
|
|
msPlaying: Result := emPlaying;
|
|
msPaused: Result := emRecPaused;
|
|
end;
|
|
end;
|
|
|
|
function TIdeEditorMacro.GetErrorMsg: String;
|
|
begin
|
|
Result := FErrorMsg;
|
|
end;
|
|
|
|
function TIdeEditorMacro.GetKeyBinding: TEditorMacroKeyBinding;
|
|
begin
|
|
if FKeyBinding = nil then
|
|
FKeyBinding := GetDefaultKeyBinding;
|
|
Result := FKeyBinding;
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.SetMacroName(AValue: string);
|
|
begin
|
|
FMacroName := AValue;
|
|
FSynMacro.MacroName := AValue;
|
|
DoChanged;
|
|
end;
|
|
|
|
constructor TIdeEditorMacro.Create(aOwner: TComponent);
|
|
begin
|
|
FSynMacro := TSynEditorMacro.Create(aOwner);
|
|
FHasError := False;
|
|
|
|
FSynMacro.OnUserCommand := @DoMacroRecorderUserCommand;
|
|
FSynMacro.OnStateChange := @DoMacroRecorderState;
|
|
FSynMacro.RecordCommandID := ecNone; // ecSynMacroRecord;
|
|
FSynMacro.PlaybackCommandID := ecNone; // ecSynMacroPlay;
|
|
FSynMacro.RecordShortCut := 0;
|
|
FSynMacro.PlaybackShortCut := 0;
|
|
end;
|
|
|
|
destructor TIdeEditorMacro.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FSynMacro);
|
|
FreeAndNil(FKeyBinding);
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.AssignEventsFrom(AMacroRecorder: TEditorMacro);
|
|
begin
|
|
if AMacroRecorder = nil then
|
|
Clear
|
|
else
|
|
if AMacroRecorder is TIdeEditorMacro then
|
|
FSynMacro.AssignEventsFrom(TIdeEditorMacro(AMacroRecorder).FSynMacro)
|
|
else
|
|
SetFromSource(AMacroRecorder.GetAsSource);
|
|
|
|
DoChanged;
|
|
end;
|
|
|
|
function TIdeEditorMacro.AddEditor(AValue: TCustomSynEdit): integer;
|
|
begin
|
|
Result := FSynMacro.AddEditor(AValue);
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.DoRecordMacro(aEditor: TWinControl);
|
|
begin
|
|
FSynMacro.RecordMacro(aEditor as TCustomSynEdit);
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.DoPlaybackMacro(aEditor: TWinControl);
|
|
begin
|
|
FSynMacro.PlaybackMacro(aEditor as TCustomSynEdit);
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.DoStop;
|
|
begin
|
|
FSynMacro.Stop;
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.DoPause;
|
|
begin
|
|
FSynMacro.Pause;
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.DoResume;
|
|
begin
|
|
FSynMacro.Resume;
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.Clear;
|
|
begin
|
|
FSynMacro.Clear;
|
|
|
|
DoChanged;
|
|
end;
|
|
|
|
function TIdeEditorMacro.GetAsSource: String;
|
|
var
|
|
i : integer;
|
|
W: TIdeMacroEventWriter;
|
|
begin
|
|
if FHasError then begin
|
|
Result := FFailedText;
|
|
exit;
|
|
end;
|
|
|
|
W := TIdeMacroEventWriter.Create;
|
|
W.UseLineFeed := True;
|
|
try
|
|
for i := 0 to FSynMacro.EventCount -1 do
|
|
begin
|
|
W.BeginEvent;
|
|
FSynMacro.Events[i].SaveToWriter(W);
|
|
W.FinishEvent;
|
|
end;
|
|
Result := w.Text;
|
|
finally
|
|
W.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.SetFromSource(const AText: String);
|
|
var
|
|
iEvent: TSynMacroEvent;
|
|
R: TIdeMacroEventReader;
|
|
begin
|
|
Stop;
|
|
FSynMacro.Clear;
|
|
FHasError := False;
|
|
|
|
R := TIdeMacroEventReader.Create(AText);
|
|
try
|
|
while R.ParseNextEvent do begin
|
|
iEvent := FSynMacro.CreateMacroEvent(R.EventCommand);
|
|
iEvent.LoadFromReader(R);
|
|
FSynMacro.InsertCustomEvent(FSynMacro.EventCount, iEvent);
|
|
end;
|
|
if R.HasError then begin
|
|
FHasError := True;
|
|
FErrorMsg := R.ErrorText;
|
|
FFailedText := AText;
|
|
MacroName := MacroName;
|
|
end;
|
|
finally
|
|
R.Free;
|
|
end;
|
|
DoChanged;
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.WriteToXmlConf(AConf: TXMLConfig; const APath: String);
|
|
begin
|
|
AConf.SetValue(APath + 'Name', MacroName);
|
|
AConf.SetValue(APath + 'Code/Value', GetAsSource);
|
|
|
|
if (KeyBinding <> nil) then
|
|
KeyBinding.WriteToXmlConf(AConf, APath);
|
|
end;
|
|
|
|
procedure TIdeEditorMacro.ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
|
|
var
|
|
s: String;
|
|
begin
|
|
s := AConf.GetValue(APath + 'Code/Value', '');
|
|
SetFromSource(s);
|
|
s := AConf.GetValue(APath + 'Name', '');
|
|
if s <> '' then MacroName := s;
|
|
|
|
if (not FHasError) and (FSynMacro.EventCount = 0) then begin
|
|
FHasError := True;
|
|
FErrorMsg := 'No content found';
|
|
FFailedText := s;
|
|
end;
|
|
|
|
if (KeyBinding <> nil) then
|
|
KeyBinding.ReadFromXmlConf(AConf, APath);
|
|
|
|
DoChanged;
|
|
end;
|
|
|
|
function TIdeEditorMacro.IsEmpty: Boolean;
|
|
begin
|
|
Result := FSynMacro.IsEmpty;
|
|
end;
|
|
|
|
function TIdeEditorMacro.IsInvalid: Boolean;
|
|
begin
|
|
Result := FHasError;
|
|
end;
|
|
|
|
function TIdeEditorMacro.IsRecording(AnEditor: TWinControl): Boolean;
|
|
begin
|
|
Result := (State in [emRecording, emRecPaused]) and
|
|
(FSynMacro.CurrentEditor = AnEditor);
|
|
end;
|
|
|
|
{ TIdeMacroEventReader }
|
|
|
|
function TIdeMacroEventReader.GetParamAsInt(Index: Integer): Integer;
|
|
begin
|
|
if (Index < 0) or (Index >= Length(FParams)) or (ParamType[Index] <> ptInteger)
|
|
then begin
|
|
FHasError := True;
|
|
AddError('Wrong amount of param');
|
|
exit(0);
|
|
end;
|
|
Result := FParams[Index].Num;
|
|
end;
|
|
|
|
function TIdeMacroEventReader.GetParamAsString(Index: Integer): String;
|
|
begin
|
|
if (Index < 0) or (Index >= Length(FParams)) or (ParamType[Index] <> ptString)
|
|
then begin
|
|
FHasError := True;
|
|
AddError('Wrong amount of param');
|
|
exit('');
|
|
end;
|
|
Result := FParams[Index].Text;
|
|
end;
|
|
|
|
function TIdeMacroEventReader.GetParamType(Index: Integer): TSynEventParamType;
|
|
begin
|
|
if (Index < 0) or (Index >= Length(FParams)) then begin
|
|
FHasError := True;
|
|
AddError('Wrong amount of param');
|
|
exit(ptString); // What to return here?
|
|
end;
|
|
Result := FParams[Index].ParamType;
|
|
end;
|
|
|
|
function TIdeMacroEventReader.PosToXY: TPoint;
|
|
var
|
|
f: TStringList;
|
|
begin
|
|
f := TStringList.Create;
|
|
f.Text := copy(FOrigText,1 ,FPos);
|
|
Result.y := f.Count;
|
|
Result.x := length(f[f.Count-1])+1;
|
|
f.Free;
|
|
end;
|
|
|
|
function TIdeMacroEventReader.AddError(AMsg: string): Boolean;
|
|
var
|
|
p: TPoint;
|
|
begin
|
|
p := PosToXY;
|
|
FErrorText := FErrorText + Format('Error: %s at Line %d, Column %d', [AMsg, p.y, p.x]);
|
|
Result := False;
|
|
end;
|
|
|
|
constructor TIdeMacroEventReader.Create(const Atext: String);
|
|
begin
|
|
FText := Atext;
|
|
FOrigText := Atext;
|
|
FHasError := False;
|
|
FErrorText := '';
|
|
end;
|
|
|
|
function TIdeMacroEventReader.EventCommand: TSynEditorCommand;
|
|
begin
|
|
Result := FEventCommand;
|
|
end;
|
|
|
|
function TIdeMacroEventReader.ParamCount: Integer;
|
|
begin
|
|
Result := Length(FParams);
|
|
end;
|
|
|
|
function TIdeMacroEventReader.ParseNextEvent: Boolean;
|
|
procedure SkipNum(var i: integer);
|
|
begin
|
|
while (i <= Length(FText)) and (FText[i] in ['0'..'9']) do inc (i);
|
|
end;
|
|
procedure SkipSpace(var i: integer);
|
|
begin
|
|
while (i <= Length(FText)) and (FText[i] in [' '..#9, #13, #10]) do inc (i);
|
|
end;
|
|
var
|
|
c,i,j,k: Integer;
|
|
s: String;
|
|
begin
|
|
FEventName := '';
|
|
FText := Trim(FText);
|
|
Result := (FText <> '') and (not FHasError);
|
|
if not Result then exit;
|
|
Result := False;
|
|
FPos := Length(FOrigText) - Length(FText);
|
|
|
|
FHasError := True; // Assume the worst
|
|
|
|
i := 1;
|
|
while (i <= Length(FText)) and (FText[i] in ['a'..'z','A'..'Z','0'..'9','_']) do inc (i);
|
|
if i = 1 then exit(AddError('Expected Command, but found "'+UTF8Copy(FText,1,1)+'"'));
|
|
|
|
s := Copy(FText, 1, i-1);
|
|
j:=0;
|
|
if not IdentToEditorCommand(s, j) then exit(AddError('Unknown Command "'+s+'"'));
|
|
FEventCommand := j;
|
|
FEventName := s;
|
|
|
|
FPos := Length(FOrigText) - Length(FText);
|
|
while (i <= Length(FText)) and (FText[i] in [' ', #9]) do inc (i);
|
|
if (i > Length(FText)) then exit(AddError('Expected "(" or ";" bot got end of file'));
|
|
|
|
SetLength(FParams, 0);
|
|
c := 0;
|
|
|
|
if (FText[i] = '(') then begin
|
|
inc(i);
|
|
repeat
|
|
SkipSpace(i);
|
|
if (i > Length(FText)) then exit(AddError('Unexpected end of file in params'));
|
|
|
|
if FText[i] in ['0'..'9'] then begin
|
|
// Parse number
|
|
j := i;
|
|
SkipNum(i);
|
|
SetLength(FParams, c + 1);
|
|
FParams[c].ParamType := ptInteger;
|
|
FParams[c].Num := StrToInt(copy(FText, i, j-i));
|
|
inc(c);
|
|
end
|
|
else
|
|
if FText[i] in ['#', ''''] then begin
|
|
// Parse string
|
|
s := '';
|
|
repeat
|
|
case FText[i] of
|
|
'#': begin
|
|
j := i;
|
|
SkipNum(i);
|
|
k := StrToInt(copy(FText, i, j-i));
|
|
if k > 255 then exit(AddError('Argument to long'));
|
|
s := s + chr(k);
|
|
end;
|
|
'''': begin
|
|
inc(i);
|
|
repeat
|
|
case FText[i] of
|
|
'''': begin
|
|
if (i+1 <= Length(FText)) and (FText[i+1] = '''') then begin
|
|
s := s + '''';
|
|
inc(i,2);
|
|
end
|
|
else begin
|
|
inc(i);
|
|
break;
|
|
end;
|
|
end;
|
|
else begin
|
|
s := s + FText[i];
|
|
inc(i);
|
|
end;
|
|
end;
|
|
until i > Length(FText);
|
|
end;
|
|
else
|
|
break;
|
|
end;
|
|
until i > Length(FText);
|
|
|
|
SetLength(FParams, c + 1);
|
|
FParams[c].ParamType := ptString;
|
|
FParams[c].Text := s;
|
|
inc(c);
|
|
end
|
|
else
|
|
if FText[i] <> ')' then
|
|
exit(AddError('Unknown Arguent'));
|
|
|
|
SkipSpace(i);
|
|
if (i >= Length(FText)) then exit(AddError('Missing ")"'));
|
|
if FText[i] = ')' then break;
|
|
if not(FText[i] = ',') then exit(AddError('Expected ","'));
|
|
inc(i);
|
|
until i > Length(FText);
|
|
inc(i);
|
|
end;
|
|
|
|
if (i > Length(FText)) then exit(AddError('Missing ";"'));
|
|
if (FText[i] = ';') then begin
|
|
Delete(FText, 1, i);
|
|
Result := True;
|
|
FHasError := False;
|
|
exit;
|
|
end;
|
|
AddError('Unknown Error');
|
|
end;
|
|
|
|
{ TSynMacroEventWriter }
|
|
|
|
constructor TIdeMacroEventWriter.Create;
|
|
begin
|
|
FUseLineFeed := False;
|
|
end;
|
|
|
|
procedure TIdeMacroEventWriter.BeginEvent;
|
|
begin
|
|
FCmdName := '';
|
|
FParams := '';
|
|
end;
|
|
|
|
procedure TIdeMacroEventWriter.FinishEvent;
|
|
begin
|
|
FText := FText + FCmdName;
|
|
if FParams <> '' then
|
|
FText := FText + '(' + FParams + ')';
|
|
FText := FText + ';';
|
|
if FUseLineFeed then
|
|
FText := FText + LineEnding;
|
|
end;
|
|
|
|
procedure TIdeMacroEventWriter.WriteEventCommand(const ACmd: TSynEditorCommand);
|
|
begin
|
|
EditorCommandToIdent(ACmd, FCmdName);
|
|
end;
|
|
|
|
procedure TIdeMacroEventWriter.WriteEventParam(const AParam: string);
|
|
var
|
|
s: String;
|
|
i: Integer;
|
|
InQuotes: Boolean;
|
|
begin
|
|
if FParams <> '' then
|
|
FParams := FParams + ', ';
|
|
s := '';
|
|
InQuotes := False;
|
|
for i := 1 to length(AParam) do
|
|
case AParam[i] of
|
|
#0..#31: begin
|
|
if InQuotes then s := s + '''';
|
|
InQuotes := False;
|
|
s := s + '#' + IntToStr(ord(AParam[i]));
|
|
end;
|
|
'''': begin
|
|
if not InQuotes then s := s + '''';
|
|
InQuotes := True;
|
|
s := s + '''''';
|
|
end;
|
|
else begin
|
|
if not InQuotes then s := s + '''';
|
|
InQuotes := True;
|
|
s := s + AParam[i];
|
|
end;
|
|
end;
|
|
if InQuotes then s := s + '''';
|
|
FParams := FParams + s;
|
|
end;
|
|
|
|
procedure TIdeMacroEventWriter.WriteEventParam(const AParam: integer);
|
|
begin
|
|
if FParams <> '' then
|
|
FParams := FParams + ', ';
|
|
FParams := FParams + IntToStr(AParam);
|
|
end;
|
|
|
|
{ TMacroListView }
|
|
|
|
procedure TMacroListView.btnRenameClick(Sender: TObject);
|
|
var
|
|
s: String;
|
|
M: TEditorMacro;
|
|
begin
|
|
if lbRecordedView.ItemIndex < 0 then exit;
|
|
M := CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex];
|
|
s := M.MacroName;
|
|
if InputQuery(lisNewMacroname2, Format(lisEnterNewNameForMacroS, [m.MacroName]), s)
|
|
then begin
|
|
while (s <> '') and (CurrentEditorMacroList.IndexOfName(s) >= 0) do begin
|
|
case IDEMessageDialog(lisDuplicateName, lisAMacroWithThisNameAlreadyExists, mtWarning,
|
|
mbOKCancel) of
|
|
mrOK:
|
|
if not InputQuery(lisNewMacroname2, Format(lisEnterNewNameForMacroS, [m.MacroName]), s)
|
|
then s := '';
|
|
else
|
|
s := '';
|
|
end;
|
|
end;
|
|
|
|
if s <> '' then
|
|
M.MacroName := s;
|
|
UpdateDisplay;
|
|
end;
|
|
end;
|
|
|
|
procedure TMacroListView.btnPlayClick(Sender: TObject);
|
|
var
|
|
i: Integer;
|
|
M: TEditorMacro;
|
|
se: TSourceEditorInterface;
|
|
begin
|
|
if ActiveEditorMacro <> nil then exit;
|
|
if lbRecordedView.ItemIndex < 0 then exit;
|
|
se := SourceEditorManagerIntf.ActiveEditor;
|
|
if se = nil then Exit;
|
|
|
|
i := 1;
|
|
if chkRepeat.Enabled then i := edRepeat.Value;
|
|
FIsPlaying := True;
|
|
UpdateButtons;
|
|
|
|
M := CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex];
|
|
try
|
|
while i > 0 do begin
|
|
M.PlaybackMacro(TCustomSynEdit(se.EditorControl));
|
|
Application.ProcessMessages;
|
|
dec(i);
|
|
if not FIsPlaying then break;
|
|
end;
|
|
finally
|
|
FIsPlaying := False;
|
|
UpdateButtons;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TMacroListView.btnDeleteClick(Sender: TObject);
|
|
var
|
|
m: TEditorMacro;
|
|
begin
|
|
if lbRecordedView.ItemIndex < 0 then exit;
|
|
if IDEMessageDialog(lisReallyDelete, lisDeleteSelectedMacro, mtConfirmation, [
|
|
mbYes, mbNo]) = mrYes
|
|
then begin
|
|
if SelectedEditorMacro = CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex] then begin
|
|
SelectedEditorMacro := nil;
|
|
end;
|
|
m := CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex];
|
|
CurrentEditorMacroList.Delete(lbRecordedView.ItemIndex);
|
|
m.Free;
|
|
if CurrentEditorMacroList = EditorMacroListProj then Project1.Modified := True;
|
|
if CurrentEditorMacroList = EditorMacroListGlob then MainIDEInterface.SaveEnvironment(False);
|
|
UpdateDisplay;
|
|
end;
|
|
end;
|
|
|
|
procedure TMacroListView.btnEditClick(Sender: TObject);
|
|
var
|
|
M: TEditorMacro;
|
|
begin
|
|
if lbRecordedView.ItemIndex < 0 then exit;
|
|
M := CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex];
|
|
if M = nil then exit;
|
|
LazarusIDE.DoOpenEditorFile(
|
|
EditorMacroVirtualDrive+MacroListToName(CurrentEditorMacroList)+'|'+M.MacroName,
|
|
-1, -1, [ofVirtualFile, ofInternalFile]);
|
|
end;
|
|
|
|
procedure TMacroListView.btnRecordClick(Sender: TObject);
|
|
var
|
|
se: TSourceEditorInterface;
|
|
begin
|
|
se := SourceEditorManagerIntf.ActiveEditor;
|
|
if se = nil then Exit;
|
|
if (ActiveEditorMacro = nil) and (EditorMacroForRecording.State = emStopped) then
|
|
EditorMacroForRecording.RecordMacro(TCustomSynEdit(se.EditorControl))
|
|
else
|
|
if EditorMacroForRecording.State = emRecording then
|
|
EditorMacroForRecording.Pause
|
|
else
|
|
if EditorMacroForRecording.State = emRecPaused then
|
|
EditorMacroForRecording.Resume;
|
|
se.EditorControl.SetFocus;
|
|
end;
|
|
|
|
procedure TMacroListView.btnRecordStopClick(Sender: TObject);
|
|
begin
|
|
FIsPlaying := False;
|
|
EditorMacroForRecording.Stop;
|
|
end;
|
|
|
|
procedure TMacroListView.btnSelectClick(Sender: TObject);
|
|
begin
|
|
if ActiveEditorMacro <> nil then exit;
|
|
if lbRecordedView.ItemIndex >= 0 then
|
|
SelectedEditorMacro := CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex]
|
|
else
|
|
SelectedEditorMacro:= nil;
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TMacroListView.btnSetKeysClick(Sender: TObject);
|
|
var
|
|
i: integer;
|
|
M: TEditorMacro;
|
|
begin
|
|
if lbRecordedView.ItemIndex < 0 then exit;
|
|
M := CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex];
|
|
|
|
if (M.KeyBinding = nil) or (M.KeyBinding.IdeCmd = nil) or
|
|
not(M.KeyBinding.IdeCmd is TKeyCommandRelation)
|
|
then // only for error macros
|
|
exit;
|
|
|
|
i := (IDECommandList as TKeyCommandRelationList).IndexOf(M.KeyBinding.IdeCmd as TKeyCommandRelation);
|
|
if (i >= 0) then
|
|
if ShowKeyMappingEditForm(i, (IDECommandList as TKeyCommandRelationList)) = mrOK then begin
|
|
if CurrentEditorMacroList = EditorMacroListProj then Project1.Modified := True;
|
|
if CurrentEditorMacroList = EditorMacroListGlob then MainIDEInterface.SaveEnvironment(False);
|
|
end;
|
|
UpdateDisplay;
|
|
if OnKeyMapReloaded <> nil then OnKeyMapReloaded();
|
|
end;
|
|
|
|
procedure TMacroListView.DoMacroStateChanged(Sender: TObject);
|
|
begin
|
|
if OnEditorMacroStateChange <> nil then
|
|
OnEditorMacroStateChange(Sender);
|
|
end;
|
|
|
|
procedure TMacroListView.FormActivate(Sender: TObject);
|
|
begin
|
|
lbRecordedView.HideSelection := Active;
|
|
end;
|
|
|
|
procedure TMacroListView.HelpButtonClick(Sender: TObject);
|
|
begin
|
|
LazarusHelp.ShowHelpForIDEControl(Self);
|
|
end;
|
|
|
|
procedure TMacroListView.lbRecordedViewSelectItem(Sender: TObject; Item: TListItem;
|
|
Selected: Boolean);
|
|
begin
|
|
UpdateButtons;
|
|
end;
|
|
|
|
procedure TMacroListView.mnExportClick(Sender: TObject);
|
|
var
|
|
Conf: TXMLConfig;
|
|
begin
|
|
if lbRecordedView.ItemIndex < 0 then exit;
|
|
|
|
if SaveDialog1.Execute then begin
|
|
Conf := TXMLConfig.Create(SaveDialog1.FileName);
|
|
try
|
|
Conf.Clear;
|
|
Conf.SetValue('EditorMacros/Count', 1);
|
|
CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex].WriteToXmlConf(Conf, 'EditorMacros/Macro1/');
|
|
finally
|
|
Conf.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMacroListView.mnImportClick(Sender: TObject);
|
|
var
|
|
Conf: TXMLConfig;
|
|
NewMacro: TEditorMacro;
|
|
begin
|
|
if OpenDialog1.Execute then begin
|
|
Conf := TXMLConfig.Create(OpenDialog1.FileName);
|
|
try
|
|
NewMacro := EditorMacroPlayerClass.Create(nil);
|
|
NewMacro.OnStateChange := @DoMacroStateChanged;
|
|
NewMacro.OnChange := @DoMacroContentChanged;
|
|
NewMacro.ReadFromXmlConf(Conf, 'EditorMacros/Macro1/');
|
|
if not NewMacro.IsEmpty then begin
|
|
CurrentEditorMacroList.Add(NewMacro);
|
|
if CurrentEditorMacroList = EditorMacroListProj then Project1.Modified := True;
|
|
if CurrentEditorMacroList = EditorMacroListGlob then MainIDEInterface.SaveEnvironment(False);
|
|
end
|
|
else
|
|
NewMacro.Free;
|
|
finally
|
|
Conf.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMacroListView.tbIDEClick(Sender: TObject);
|
|
begin
|
|
CurrentEditorMacroList := EditorMacroListGlob;
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TMacroListView.tbMoveIDEClick(Sender: TObject);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if (lbRecordedView.ItemIndex < 0) or (CurrentEditorMacroList = EditorMacroListGlob) then exit;
|
|
i := lbRecordedView.ItemIndex;
|
|
EditorMacroListGlob.Add(CurrentEditorMacroList.Macros[i]);
|
|
CurrentEditorMacroList.Delete(i);
|
|
if CurrentEditorMacroList = EditorMacroListProj then Project1.Modified := True;
|
|
MainIDEInterface.SaveEnvironment(False);
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TMacroListView.tbMoveProjectClick(Sender: TObject);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if (lbRecordedView.ItemIndex < 0) or (CurrentEditorMacroList = EditorMacroListProj) then exit;
|
|
i := lbRecordedView.ItemIndex;
|
|
EditorMacroListProj.Add(CurrentEditorMacroList.Macros[i]);
|
|
CurrentEditorMacroList.Delete(i);
|
|
Project1.Modified := True;
|
|
if CurrentEditorMacroList = EditorMacroListGlob then MainIDEInterface.SaveEnvironment(False);
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TMacroListView.tbProjectClick(Sender: TObject);
|
|
begin
|
|
CurrentEditorMacroList := EditorMacroListProj;
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TMacroListView.tbRecordedClick(Sender: TObject);
|
|
begin
|
|
CurrentEditorMacroList := EditorMacroListRec;
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
procedure TMacroListView.DoOnMacroListChange(Sender: TObject);
|
|
begin
|
|
UpdateDisplay;
|
|
|
|
if Sender = EditorMacroListProj then
|
|
Project1.SessionModified := True;
|
|
end;
|
|
|
|
procedure TMacroListView.DoMacroContentChanged(Sender: TObject);
|
|
begin
|
|
if FIgnoreMacroChanges then exit;
|
|
|
|
if EditorMacroListProj.IndexOf(Sender as TEditorMacro) >= 0 then
|
|
Project1.Modified := True;
|
|
if EditorMacroListGlob.IndexOf(Sender as TEditorMacro) >= 0 then
|
|
MainIDEInterface.SaveEnvironment(False);
|
|
end;
|
|
|
|
procedure TMacroListView.UpdateDisplay;
|
|
var
|
|
NewItem: TListItem;
|
|
i, idx: Integer;
|
|
M: TEditorMacro;
|
|
begin
|
|
idx := lbRecordedView.ItemIndex;
|
|
lbRecordedView.Items.Clear;
|
|
|
|
for i := 0 to CurrentEditorMacroList.Count - 1 do begin
|
|
M := CurrentEditorMacroList.Macros[i];
|
|
NewItem := lbRecordedView.Items.Add;
|
|
|
|
if m.KeyBinding <> nil then
|
|
NewItem.Caption := M.MacroName + M.KeyBinding.ShortCutAsText
|
|
else
|
|
NewItem.Caption := M.MacroName;
|
|
|
|
// Image
|
|
if M.IsInvalid then
|
|
NewItem.ImageIndex := FImageErr
|
|
else
|
|
if (m = CurrentRecordingMacro) then
|
|
NewItem.ImageIndex := FImageRec
|
|
else
|
|
if (CurrentRecordingMacro = nil) then begin // If recording, then recorder is selected
|
|
If (M = ActiveEditorMacro) and (M.State = emPlaying) then
|
|
NewItem.ImageIndex := FImagePlay
|
|
else
|
|
if (M = SelectedEditorMacro) then
|
|
NewItem.ImageIndex := FImageSel;
|
|
end;
|
|
end;
|
|
if idx < lbRecordedView.Items.Count then
|
|
lbRecordedView.ItemIndex := idx
|
|
else
|
|
lbRecordedView.ItemIndex := -1;
|
|
|
|
lbRecordedViewSelectItem(nil, nil, False);
|
|
UpdateButtons;
|
|
end;
|
|
|
|
procedure TMacroListView.UpdateButtons;
|
|
var
|
|
IsSel, IsErr, IsStopped: Boolean;
|
|
M: TEditorMacro;
|
|
RecState: TEditorMacroState;
|
|
begin
|
|
IsSel := (lbRecordedView.ItemIndex >= 0);
|
|
IsStopped := (ActiveEditorMacro = nil);
|
|
if IsSel then
|
|
M := CurrentEditorMacroList.Macros[lbRecordedView.ItemIndex];
|
|
IsErr := IsSel and M.IsInvalid;
|
|
RecState := emStopped;
|
|
if EditorMacroForRecording <> nil then
|
|
RecState := EditorMacroForRecording.State;
|
|
|
|
btnSelect.Enabled := IsStopped and IsSel and (not FIsPlaying) and (not IsErr);
|
|
btnRename.Enabled := IsStopped and IsSel and (not FIsPlaying) and (not IsErr);
|
|
btnEdit.Enabled := IsStopped and IsSel and (not FIsPlaying);
|
|
btnSetKeys.Enabled := IsStopped and IsSel and (not FIsPlaying) and (not IsErr);
|
|
btnDelete.Enabled := IsStopped and IsSel and (not FIsPlaying);
|
|
|
|
btnPlay.Enabled := IsStopped and IsSel and (not FIsPlaying) and (not IsErr);
|
|
chkRepeat.Enabled := IsStopped and (not FIsPlaying);
|
|
edRepeat.Enabled := IsStopped and (not FIsPlaying);
|
|
|
|
btnRecord.Enabled := Assigned(SourceEditorManagerIntf.ActiveEditor)
|
|
and (RecState in [emStopped, emRecPaused, emRecording])
|
|
and (not FIsPlaying);
|
|
btnRecordStop.Enabled := (not IsStopped) or FIsPlaying;
|
|
|
|
if (RecState = emRecording) then
|
|
btnRecord.Caption := lisPause
|
|
else if (RecState = emRecPaused) then
|
|
btnRecord.Caption := lisContinue
|
|
else
|
|
btnRecord.Caption := lisRecord;
|
|
|
|
mnImport.Enabled := IsStopped and (not FIsPlaying);
|
|
mnExport.Enabled := IsStopped and IsSel and (not FIsPlaying);
|
|
|
|
tbMoveProject.Visible := CurrentEditorMacroList <> EditorMacroListProj;
|
|
tbMoveProject.Enabled := IsStopped and IsSel and (not FIsPlaying);
|
|
tbMoveIDE.Visible := CurrentEditorMacroList <> EditorMacroListGlob;
|
|
tbMoveIDE.Enabled := IsStopped and IsSel and (not FIsPlaying);
|
|
|
|
Update;
|
|
end;
|
|
|
|
function TMacroListView.MacroByFullName(AName: String): TEditorMacro;
|
|
const
|
|
FolderStart = length(EditorMacroVirtualDrive)+1;
|
|
NameStart = FolderStart+length('PRJ|');
|
|
var
|
|
Alist: TEditorMacroList;
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
If (copy(AName, 1, length(EditorMacroVirtualDrive)) <> EditorMacroVirtualDrive) or
|
|
(copy(AName, NameStart-1, 1) <> '|')
|
|
then exit;
|
|
Alist := NameToMacroList(copy(AName, FolderStart, 3));
|
|
if (Alist = nil) then exit;
|
|
i := Alist.IndexOfName(copy(AName, NameStart, length(AName)));
|
|
if i < 0 then exit;
|
|
Result := Alist.Macros[i];
|
|
end;
|
|
|
|
procedure TMacroListView.DoEditorMacroStateChanged;
|
|
begin
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
constructor TMacroListView.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
FIgnoreMacroChanges := False;
|
|
|
|
Caption := lisEditorMacros;
|
|
EditorMacroListRec.OnChange := @DoOnMacroListChange;
|
|
EditorMacroListProj.OnChange := @DoOnMacroListChange;
|
|
EditorMacroListGlob.OnChange := @DoOnMacroListChange;
|
|
|
|
tbRecorded.Caption := lisRecordedMacros;
|
|
tbProject.Caption := lisProjectMacro;
|
|
tbIDE.Caption := lisIDE;
|
|
tbRecorded.Hint := lisNewRecordedMacrosNotToBeSaved;
|
|
tbProject.Hint := lisSavedWithProjectSession;
|
|
tbIDE.Hint := lisSavedWithIDESettings;
|
|
tbMoveProject.Caption := lisProjectMacro;
|
|
tbMoveIDE.Caption := lisIDE;
|
|
lbMoveTo.Caption := lisMoveTo + ' '; // Anchors do not work here. Use spaces.
|
|
|
|
btnSelect.Caption := lisMenuSelect;
|
|
btnRename.Caption := lisRename2;
|
|
btnSetKeys.Caption := lisEditKey;
|
|
btnEdit.Caption := lisEdit;
|
|
btnDelete.Caption := lisDelete;
|
|
btnPlay.Caption := lisPlay;
|
|
chkRepeat.Caption := lisRepeat;
|
|
btnRecord.Caption := lisRecord;
|
|
btnRecordStop.Caption := lisStop;
|
|
|
|
SaveDialog1.Title := lisSaveMacroAs;
|
|
OpenDialog1.Title := lisLoadMacroFrom;
|
|
mnImport.Caption := lisDlgImport;
|
|
mnExport.Caption := lisDlgExport;
|
|
|
|
lbRecordedView.SmallImages := IDEImages.Images_16;
|
|
FImageRec := IDEImages.LoadImage(16, 'Record'); // red dot
|
|
FImagePlay := IDEImages.LoadImage(16, 'menu_run'); // green triangle
|
|
FImageSel := IDEImages.LoadImage(16, 'arrow_right');
|
|
FImageErr := IDEImages.LoadImage(16, 'state_error');
|
|
FIsPlaying := False;
|
|
|
|
UpdateDisplay;
|
|
end;
|
|
|
|
destructor TMacroListView.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TEditorMacroList }
|
|
|
|
function TEditorMacroList.GetMacros(Index: Integer): TEditorMacro;
|
|
begin
|
|
Result := TEditorMacro(FList[Index]);
|
|
end;
|
|
|
|
procedure TEditorMacroList.DoChanged;
|
|
begin
|
|
if Assigned(FOnChange) then
|
|
FOnChange(Self);
|
|
end;
|
|
|
|
procedure TEditorMacroList.DoAdded(AMacro: TEditorMacro);
|
|
begin
|
|
if Assigned(FOnAdded) then
|
|
FOnAdded(Self, AMacro);
|
|
end;
|
|
|
|
procedure TEditorMacroList.DoRemove(AMacro: TEditorMacro);
|
|
begin
|
|
if Assigned(FOnRemove) then
|
|
FOnRemove(Self, AMacro);
|
|
end;
|
|
|
|
constructor TEditorMacroList.Create;
|
|
begin
|
|
FList := TList.Create;
|
|
end;
|
|
|
|
destructor TEditorMacroList.Destroy;
|
|
begin
|
|
FOnChange := nil;
|
|
ClearAndFreeMacros;
|
|
FreeAndNil(FList);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TEditorMacroList.WriteToXmlConf(AConf: TXMLConfig; const APath: String);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
AConf.SetDeleteValue(APath + 'EditorMacros/Count', Count, 0);
|
|
for i := 0 to Count - 1 do
|
|
Macros[i].WriteToXmlConf(AConf, 'EditorMacros/Macro'+IntToStr(i+1)+'/');
|
|
end;
|
|
|
|
procedure TEditorMacroList.ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
|
|
var
|
|
c, i: Integer;
|
|
NewMacro: TEditorMacro;
|
|
begin
|
|
ClearAndFreeMacros;
|
|
c := AConf.GetValue(APath + 'EditorMacros/Count', 0);
|
|
for i := 0 to c -1 do begin
|
|
NewMacro := EditorMacroPlayerClass.Create(nil);
|
|
NewMacro.OnStateChange := @MacroListViewer.DoMacroStateChanged;
|
|
NewMacro.OnChange := @MacroListViewer.DoMacroContentChanged;
|
|
try
|
|
NewMacro.ReadFromXmlConf(AConf, 'EditorMacros/Macro'+IntToStr(i+1)+'/');
|
|
finally
|
|
Add(NewMacro)
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorMacroList.ClearAndFreeMacros;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to Count - 1 do Macros[i].Free;
|
|
FList.Clear;
|
|
end;
|
|
|
|
function TEditorMacroList.Count: Integer;
|
|
begin
|
|
Result := FList.Count;
|
|
end;
|
|
|
|
function TEditorMacroList.IndexOf(AMacro: TEditorMacro): Integer;
|
|
begin
|
|
Result := FList.IndexOf(AMacro);
|
|
end;
|
|
|
|
function TEditorMacroList.IndexOfName(AName: String): Integer;
|
|
begin
|
|
Result := Count - 1;
|
|
while Result >= 0 do
|
|
if Macros[Result].MacroName = AName
|
|
then break
|
|
else dec(Result);
|
|
end;
|
|
|
|
function TEditorMacroList.UniqName(AName: String): String;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := AName;
|
|
if IndexOfName(AName) < 0 then exit;
|
|
i := 1;
|
|
while IndexOfName(AName+'_'+IntToStr(i)) >= 0 do inc(i);
|
|
Result := AName+'_'+IntToStr(i);
|
|
end;
|
|
|
|
function TEditorMacroList.Add(AMacro: TEditorMacro): Integer;
|
|
begin
|
|
AMacro.MacroName := UniqName(AMacro.MacroName);
|
|
Result := FList.Add(AMacro);
|
|
DoAdded(AMacro);
|
|
DoChanged;
|
|
end;
|
|
|
|
procedure TEditorMacroList.Delete(AnIndex: Integer);
|
|
begin
|
|
DoRemove(Macros[AnIndex]);
|
|
FList.Delete(AnIndex);
|
|
DoChanged;
|
|
end;
|
|
|
|
procedure TEditorMacroList.Remove(AMacro: TEditorMacro);
|
|
begin
|
|
DoRemove(AMacro);
|
|
FList.Remove(AMacro);
|
|
DoChanged;
|
|
end;
|
|
|
|
// itmMacroListView.enabled
|
|
|
|
{$R *.lfm}
|
|
|
|
initialization
|
|
if EditorMacroPlayerClass = nil then
|
|
EditorMacroPlayerClass := TIdeEditorMacro;
|
|
if DefaultBindingClass = nil then
|
|
DefaultBindingClass := TIdeEditorMacroKeyBinding;
|
|
EditorMacroListRec := TEditorMacroList.Create;
|
|
EditorMacroListProj := TEditorMacroList.Create;
|
|
EditorMacroListGlob := TEditorMacroList.Create;
|
|
CurrentEditorMacroList := EditorMacroListRec;
|
|
|
|
finalization
|
|
CurrentEditorMacroList := nil;
|
|
FreeAndNil(EditorMacroListRec);
|
|
FreeAndNil(EditorMacroListProj);
|
|
FreeAndNil(EditorMacroListGlob);
|
|
|
|
end.
|
|
|