mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 10:57:55 +02:00
520 lines
17 KiB
ObjectPascal
520 lines
17 KiB
ObjectPascal
{-------------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
Alternatively, the contents of this file may be used under the terms of the
|
|
GNU General Public License Version 2 or later (the "GPL"), in which case
|
|
the provisions of the GPL are applicable instead of those above.
|
|
If you wish to allow use of your version of this file only under the terms
|
|
of the GPL and not to allow others to use your version of this file
|
|
under the MPL, indicate your decision by deleting the provisions above and
|
|
replace them with the notice and other provisions required by the GPL.
|
|
If you do not delete the provisions above, a recipient may use your version
|
|
of this file under either the MPL or the GPL.
|
|
|
|
-------------------------------------------------------------------------------}
|
|
unit SynPluginTemplateEdit;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Graphics, LCLType,
|
|
SynPluginSyncronizedEditBase, SynEditKeyCmds, SynEdit, SynEditMiscProcs;
|
|
|
|
type
|
|
|
|
{ TSynEditTemplateEditKeyStrokes }
|
|
|
|
TSynEditTemplateEditKeyStrokes = class(TSynEditKeyStrokes)
|
|
public
|
|
procedure ResetDefaults; override;
|
|
end;
|
|
|
|
{ TSynEditTemplateEditKeyStrokesOffCell }
|
|
|
|
TSynEditTemplateEditKeyStrokesOffCell = class(TSynEditKeyStrokes)
|
|
public
|
|
procedure ResetDefaults; override;
|
|
end;
|
|
|
|
{ TSynPluginTemplateEdit }
|
|
|
|
TSynPluginTemplateEdit = class(TSynPluginCustomSyncroEdit)
|
|
private
|
|
FCellParserEnabled: Boolean;
|
|
FKeystrokes, FKeyStrokesOffCell: TSynEditKeyStrokes;
|
|
FStartPoint: TPoint;
|
|
procedure SetKeystrokes(const AValue: TSynEditKeyStrokes);
|
|
procedure SetKeystrokesOffCell(const AValue: TSynEditKeyStrokes);
|
|
protected
|
|
procedure DoEditorRemoving(AValue: TCustomSynEdit); override;
|
|
procedure DoEditorAdded(AValue: TCustomSynEdit); override;
|
|
procedure TranslateKey(Sender: TObject; Code: word; SState: TShiftState;
|
|
var Data: pointer; var IsStartOfCombo: boolean; var Handled: boolean;
|
|
var Command: TSynEditorCommand; FinishComboOnly: Boolean;
|
|
var ComboKeyStrokes: TSynEditKeyStrokes);
|
|
procedure ProcessSynCommand(Sender: TObject; AfterProcessing: boolean;
|
|
var Handled: boolean; var Command: TSynEditorCommand;
|
|
var AChar: TUTF8Char; Data: pointer; HandlerData: pointer);
|
|
|
|
procedure NextCellOrFinal(SetSelect: Boolean = True; FirstsOnly: Boolean = False);
|
|
procedure SetFinalCaret;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
procedure SetTemplate(aTmpl: String; aLogCaretPos: TPoint); // Replaces current selection
|
|
// Coords relativ to the template. base (1, 1)
|
|
procedure AddEditCells(aCellList: TSynPluginSyncronizedEditList);
|
|
|
|
property CellParserEnabled: Boolean read FCellParserEnabled write FCellParserEnabled;
|
|
property Keystrokes: TSynEditKeyStrokes
|
|
read FKeystrokes write SetKeystrokes;
|
|
property KeystrokesOffCell: TSynEditKeyStrokes
|
|
read FKeystrokesOffCell write SetKeystrokesOffCell;
|
|
end;
|
|
|
|
const
|
|
ecSynPTmplEdNextCell = ecPluginFirstTemplEdit + 0;
|
|
ecSynPTmplEdNextCellSel = ecPluginFirstTemplEdit + 1;
|
|
ecSynPTmplEdNextCellRotate = ecPluginFirstTemplEdit + 2;
|
|
ecSynPTmplEdNextCellSelRotate = ecPluginFirstTemplEdit + 3;
|
|
ecSynPTmplEdPrevCell = ecPluginFirstTemplEdit + 4;
|
|
ecSynPTmplEdPrevCellSel = ecPluginFirstTemplEdit + 5;
|
|
ecSynPTmplEdCellHome = ecPluginFirstTemplEdit + 6;
|
|
ecSynPTmplEdCellEnd = ecPluginFirstTemplEdit + 7;
|
|
ecSynPTmplEdCellSelect = ecPluginFirstTemplEdit + 8;
|
|
ecSynPTmplEdFinish = ecPluginFirstTemplEdit + 9;
|
|
ecSynPTmplEdEscape = ecPluginFirstTemplEdit + 10;
|
|
ecSynPTmplEdNextFirstCell = ecPluginFirstTemplEdit + 11;
|
|
ecSynPTmplEdNextFirstCellSel = ecPluginFirstTemplEdit + 12;
|
|
ecSynPTmplEdNextFirstCellRotate = ecPluginFirstTemplEdit + 13;
|
|
ecSynPTmplEdNextFirstCellSelRotate = ecPluginFirstTemplEdit + 14;
|
|
ecSynPTmplEdPrevFirstCell = ecPluginFirstTemplEdit + 15;
|
|
ecSynPTmplEdPrevFirstCellSel = ecPluginFirstTemplEdit + 16;
|
|
|
|
// If extending the list, reserve space in SynEditKeyCmds
|
|
|
|
ecSynPTmplEdCount = 17;
|
|
|
|
implementation
|
|
|
|
{ TSynPluginTemplateEdit }
|
|
|
|
constructor TSynPluginTemplateEdit.Create(AOwner: TComponent);
|
|
begin
|
|
FKeystrokes := TSynEditTemplateEditKeyStrokes.Create(Self);
|
|
FKeystrokes.ResetDefaults;
|
|
FKeyStrokesOffCell := TSynEditTemplateEditKeyStrokesOffCell.Create(self);
|
|
FKeyStrokesOffCell.ResetDefaults;
|
|
inherited Create(AOwner);
|
|
CellParserEnabled := True;
|
|
end;
|
|
|
|
destructor TSynPluginTemplateEdit.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FKeystrokes);
|
|
FreeAndNil(FKeyStrokesOffCell);
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.DoEditorRemoving(AValue: TCustomSynEdit);
|
|
begin
|
|
if Editor <> nil then begin
|
|
Editor.UnRegisterKeyTranslationHandler(@TranslateKey);
|
|
Editor.UnregisterCommandHandler(@ProcessSynCommand);
|
|
end;
|
|
inherited DoEditorRemoving(AValue);
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.DoEditorAdded(AValue: TCustomSynEdit);
|
|
begin
|
|
inherited DoEditorAdded(AValue);
|
|
if Editor <> nil then begin
|
|
Editor.RegisterCommandHandler(@ProcessSynCommand, nil);
|
|
Editor.RegisterKeyTranslationHandler(@TranslateKey);
|
|
end;
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.SetKeystrokes(const AValue: TSynEditKeyStrokes);
|
|
begin
|
|
if AValue = nil then
|
|
FKeystrokes.Clear
|
|
else
|
|
FKeystrokes.Assign(AValue);
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.SetKeystrokesOffCell(const AValue: TSynEditKeyStrokes);
|
|
begin
|
|
if AValue = nil then
|
|
FKeyStrokesOffCell.Clear
|
|
else
|
|
FKeyStrokesOffCell.Assign(AValue);
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.TranslateKey(Sender: TObject; Code: word;
|
|
SState: TShiftState; var Data: pointer; var IsStartOfCombo: boolean; var Handled: boolean;
|
|
var Command: TSynEditorCommand; FinishComboOnly: Boolean;
|
|
var ComboKeyStrokes: TSynEditKeyStrokes);
|
|
var
|
|
keys: TSynEditKeyStrokes;
|
|
begin
|
|
if (not Active) or Handled then
|
|
exit;
|
|
|
|
if CurrentCell < 0 then
|
|
keys := FKeyStrokesOffCell
|
|
else
|
|
keys := FKeyStrokes;
|
|
|
|
if not FinishComboOnly then
|
|
keys.ResetKeyCombo;
|
|
Command := keys.FindKeycodeEx(Code, SState, Data, IsStartOfCombo, FinishComboOnly, ComboKeyStrokes);
|
|
|
|
Handled := (Command <> ecNone) or IsStartOfCombo;
|
|
if IsStartOfCombo then
|
|
ComboKeyStrokes := keys;
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.ProcessSynCommand(Sender: TObject; AfterProcessing: boolean;
|
|
var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer;
|
|
HandlerData: pointer);
|
|
begin
|
|
if Handled or AfterProcessing or not Active then exit;
|
|
|
|
Handled := True;
|
|
case Command of
|
|
ecSynPTmplEdNextCell: NextCellOrFinal(False);
|
|
ecSynPTmplEdNextCellSel: NextCellOrFinal(True);
|
|
ecSynPTmplEdNextCellRotate: NextCell(False);
|
|
ecSynPTmplEdNextCellSelRotate: NextCell(True);
|
|
ecSynPTmplEdPrevCell: PreviousCell(False);
|
|
ecSynPTmplEdPrevCellSel: PreviousCell(True);
|
|
ecSynPTmplEdNextFirstCell: NextCellOrFinal(False, True);
|
|
ecSynPTmplEdNextFirstCellSel: NextCellOrFinal(True, True);
|
|
ecSynPTmplEdNextFirstCellRotate: NextCell(False, False, True);
|
|
ecSynPTmplEdNextFirstCellSelRotate: NextCell(True, False, True);
|
|
ecSynPTmplEdPrevFirstCell: PreviousCell(False, False, True);
|
|
ecSynPTmplEdPrevFirstCellSel: PreviousCell(True, False, True);
|
|
ecSynPTmplEdCellHome: CellCaretHome;
|
|
ecSynPTmplEdCellEnd: CellCaretEnd;
|
|
ecSynPTmplEdCellSelect: SelectCurrentCell;
|
|
ecSynPTmplEdFinish: SetFinalCaret;
|
|
ecSynPTmplEdEscape:
|
|
begin
|
|
Clear;
|
|
Active := False;
|
|
end;
|
|
else
|
|
Handled := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.NextCellOrFinal(SetSelect: Boolean; FirstsOnly: Boolean);
|
|
var
|
|
Pos: TPoint;
|
|
i: Integer;
|
|
begin
|
|
Pos := CaretObj.LineBytePos;
|
|
i := Cells.IndexOf(Pos.x, Pos.y, True, CurrentCell);
|
|
if i < 0 then begin
|
|
i := Cells.Count - 1;
|
|
while (i >= 0) and
|
|
((Cells[i].Group < 0) or (CompareCarets(Cells[i].LogEnd, Pos) <= 0))
|
|
do
|
|
dec(i);
|
|
end;
|
|
|
|
Repeat
|
|
inc(i);
|
|
if i >= Cells.Count then begin
|
|
SetFinalCaret;
|
|
exit;
|
|
end;
|
|
until (Cells[i].Group >= 0) and
|
|
((not FirstsOnly) or (Cells[i].FirstInGroup));
|
|
CurrentCell := i;
|
|
if CurrentCell < 0 then
|
|
exit;
|
|
CaretObj.LineBytePos := Cells[CurrentCell].LogStart;
|
|
if SetSelect then
|
|
SelectCurrentCell(True)
|
|
else
|
|
Editor.BlockBegin := Cells[CurrentCell].LogStart;
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.SetFinalCaret;
|
|
var
|
|
c: TSynPluginSyncronizedEditCell;
|
|
begin
|
|
if FMarkup <> nil then
|
|
FMarkup.DoInvalidate;
|
|
c := Cells.GroupCell[-2, 0];
|
|
Editor.BlockBegin := c.LogStart;
|
|
CaretObj.IncForcePastEOL;
|
|
CaretObj.LineBytePos := c.LogStart;
|
|
CaretObj.DecForcePastEOL;
|
|
Cells.Clear;
|
|
Active := False;
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.SetTemplate(aTmpl: String; aLogCaretPos: TPoint);
|
|
var
|
|
Temp: TStringList;
|
|
CellStart, StartPos: TPoint;
|
|
i, j, k, XOffs, Grp: Integer;
|
|
s, s2: string;
|
|
begin
|
|
Clear;
|
|
Active := False;
|
|
StartPos := Editor.BlockBegin;
|
|
FStartPoint := StartPos;
|
|
if CellParserEnabled then begin
|
|
Temp := TStringList.Create;
|
|
try
|
|
Temp.Text := aTmpl;
|
|
if (aTmpl <> '') and (aTmpl[length(aTmpl)] in [#10,#13]) then
|
|
Temp.Add('');
|
|
|
|
XOffs := StartPos.X - 1;
|
|
i := 0;
|
|
Grp := 1;
|
|
while i < Temp.Count do begin
|
|
CellStart.y := StartPos.y + i;
|
|
CellStart.x := -1;
|
|
s := Temp[i];
|
|
j := 1;
|
|
k := 1;
|
|
SetLength(s2, length(s));
|
|
while j <= length(s) do begin
|
|
case s[j] of
|
|
'{':
|
|
if (j + 1 <= Length(s)) and (s[j+1] = '{') then begin
|
|
inc(j);
|
|
s2[k] := s[j];
|
|
inc(k);
|
|
end else begin
|
|
CellStart.x := k + XOffs;
|
|
end;
|
|
'}':
|
|
if (j + 1 <= Length(s)) and (s[j+1] = '}') then begin
|
|
inc(j);
|
|
s2[k] := s[j];
|
|
inc(k);
|
|
end else
|
|
if CellStart.x > 0 then begin
|
|
with Cells.AddNew do begin
|
|
LogStart := CellStart;
|
|
LogEnd := Point(k +XOffs, CellStart.y);
|
|
Group := grp;
|
|
FirstInGroup := True;
|
|
end;
|
|
inc(grp);
|
|
CellStart.x := -1;
|
|
end;
|
|
else
|
|
begin
|
|
s2[k] := s[j];
|
|
inc(k);
|
|
end;
|
|
end;
|
|
inc(j);
|
|
end;
|
|
|
|
SetLength(s2, k-1);
|
|
Temp[i] := s2;
|
|
inc(i);
|
|
XOffs := 0;
|
|
end;
|
|
|
|
aTmpl := Temp.Text;
|
|
// strip the trailing #13#10 that was appended by the stringlist
|
|
i := Length(aTmpl);
|
|
if (i >= 1) and (aTmpl[i] in [#10,#13]) then begin
|
|
dec(i);
|
|
if (i >= 1) and (aTmpl[i] in [#10,#13]) and (aTmpl[i] <> aTmpl[i+1]) then
|
|
dec(i);
|
|
SetLength(aTmpl, i);
|
|
end;
|
|
|
|
finally
|
|
Temp.Free;
|
|
end;
|
|
end;
|
|
|
|
Editor.SelText := aTmpl;
|
|
with Cells.AddNew do begin
|
|
Group := -2;
|
|
LogStart := aLogCaretPos;
|
|
LogEnd := aLogCaretPos;
|
|
end;
|
|
if (Cells.Count > 1) then begin
|
|
Active := True;
|
|
CurrentCell := 0;
|
|
CaretObj.LineBytePos := Cells[0].LogStart;
|
|
SelectCurrentCell;
|
|
SetUndoStart;
|
|
end
|
|
else
|
|
Editor.MoveCaretIgnoreEOL(Editor.LogicalToPhysicalPos(aLogCaretPos));
|
|
end;
|
|
|
|
procedure TSynPluginTemplateEdit.AddEditCells(aCellList: TSynPluginSyncronizedEditList);
|
|
var
|
|
i, XOffs, YOffs: Integer;
|
|
CurCell: TSynPluginSyncronizedEditCell;
|
|
CaretPos: TSynPluginSyncronizedEditCell;
|
|
p: TPoint;
|
|
begin
|
|
CaretPos := nil;
|
|
if Cells.GroupCell[-2, 0] <> nil then begin
|
|
CaretPos := TSynPluginSyncronizedEditCell.Create;
|
|
CaretPos.Assign(Cells.GroupCell[-2, 0]);
|
|
end;
|
|
Cells.Clear;
|
|
|
|
XOffs := FStartPoint.x - 1;
|
|
YOffs := FStartPoint.y - 1;
|
|
for i := 0 to aCellList.Count - 1 do begin
|
|
CurCell := aCellList[i];
|
|
with Cells.AddNew do begin;
|
|
Assign(CurCell);
|
|
p := LogStart;
|
|
if p.y = 1 then
|
|
p.x := p.x + XOffs;
|
|
p.y := p.y + YOffs;
|
|
LogStart := p;
|
|
p := LogEnd;
|
|
if p.y = 1 then
|
|
p.x := p.x + XOffs;
|
|
p.y := p.y + YOffs;
|
|
LogEnd := p;
|
|
end;
|
|
end;
|
|
|
|
if CaretPos <> nil then
|
|
Cells.AddNew.Assign(CaretPos);
|
|
FreeAndNil(CaretPos);
|
|
|
|
if aCellList.Count > 0 then begin
|
|
Active := True;
|
|
CurrentCell := 0;
|
|
CaretObj.LineBytePos := Cells[0].LogStart;
|
|
SelectCurrentCell;
|
|
SetUndoStart;
|
|
end;
|
|
end;
|
|
|
|
{ TSynEditTemplateEditKeyStrokes }
|
|
|
|
procedure TSynEditTemplateEditKeyStrokes.ResetDefaults;
|
|
|
|
procedure AddKey(const ACmd: TSynEditorCommand; const AKey: word;
|
|
const AShift: TShiftState);
|
|
begin
|
|
with Add do
|
|
begin
|
|
Key := AKey;
|
|
Shift := AShift;
|
|
Command := ACmd;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Clear;
|
|
AddKey(ecSynPTmplEdNextCellRotate, VK_RIGHT, [ssCtrl]);
|
|
AddKey(ecSynPTmplEdNextCellSel, VK_TAB, []);
|
|
AddKey(ecSynPTmplEdPrevCell, VK_LEFT, [ssCtrl]);
|
|
AddKey(ecSynPTmplEdPrevCellSel, VK_TAB, [ssShift]);
|
|
|
|
AddKey(ecSynPTmplEdCellHome, VK_HOME, []);
|
|
AddKey(ecSynPTmplEdCellEnd, VK_END, []);
|
|
AddKey(ecSynPTmplEdCellSelect, VK_A, [ssCtrl]);
|
|
AddKey(ecSynPTmplEdFinish, VK_RETURN, []);
|
|
AddKey(ecSynPTmplEdEscape, VK_ESCAPE, []);
|
|
end;
|
|
|
|
{ TSynEditTemplateEditKeyStrokesOffCell }
|
|
|
|
procedure TSynEditTemplateEditKeyStrokesOffCell.ResetDefaults;
|
|
|
|
procedure AddKey(const ACmd: TSynEditorCommand; const AKey: word;
|
|
const AShift: TShiftState);
|
|
begin
|
|
with Add do
|
|
begin
|
|
Key := AKey;
|
|
Shift := AShift;
|
|
Command := ACmd;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Clear;
|
|
AddKey(ecSynPTmplEdNextCellRotate, VK_RIGHT, [ssCtrl]);
|
|
AddKey(ecSynPTmplEdNextCellSel, VK_TAB, []);
|
|
AddKey(ecSynPTmplEdPrevCell, VK_LEFT, [ssCtrl]);
|
|
AddKey(ecSynPTmplEdPrevCellSel, VK_TAB, [ssShift]);
|
|
|
|
AddKey(ecSynPTmplEdFinish, VK_RETURN, []);
|
|
AddKey(ecSynPTmplEdEscape, VK_ESCAPE, []);
|
|
end;
|
|
|
|
const
|
|
EditorTmplEditCommandStrs: array[0..16] of TIdentMapEntry = (
|
|
(Value: ecSynPTmplEdNextCell; Name: 'ecSynPTmplEdNextCell'),
|
|
(Value: ecSynPTmplEdNextCellSel; Name: 'ecSynPTmplEdNextCellSel'),
|
|
(Value: ecSynPTmplEdNextCellRotate; Name: 'ecSynPTmplEdNextCellRotate'),
|
|
(Value: ecSynPTmplEdNextCellSelRotate; Name: 'ecSynPTmplEdNextCellSelRotate'),
|
|
(Value: ecSynPTmplEdPrevCell; Name: 'ecSynPTmplEdPrevCell'),
|
|
(Value: ecSynPTmplEdPrevCellSel; Name: 'ecSynPTmplEdPrevCellSel'),
|
|
(Value: ecSynPTmplEdCellHome; Name: 'ecSynPTmplEdCellHome'),
|
|
(Value: ecSynPTmplEdCellEnd; Name: 'ecSynPTmplEdCellEnd'),
|
|
(Value: ecSynPTmplEdCellSelect; Name: 'ecSynPTmplEdCellSelect'),
|
|
(Value: ecSynPTmplEdFinish; Name: 'ecSynPTmplEdFinish'),
|
|
(Value: ecSynPTmplEdEscape; Name: 'ecSynPTmplEdEscape'),
|
|
(Value: ecSynPTmplEdNextFirstCell; Name: 'ecSynPTmplEdNextFirstCell'),
|
|
(Value: ecSynPTmplEdNextFirstCellSel; Name: 'ecSynPTmplEdNextFirstCellSel'),
|
|
(Value: ecSynPTmplEdNextFirstCellRotate; Name: 'ecSynPTmplEdNextFirstCellRotate'),
|
|
(Value: ecSynPTmplEdNextFirstCellSelRotate; Name: 'ecSynPTmplEdNextFirstCellSelRotate'),
|
|
(Value: ecSynPTmplEdPrevFirstCell; Name: 'ecSynPTmplEdPrevFirstCell'),
|
|
(Value: ecSynPTmplEdPrevFirstCellSel; Name: 'ecSynPTmplEdPrevFirstCellSel')
|
|
);
|
|
|
|
function IdentToTmplEditCommand(const Ident: string; var Cmd: longint): boolean;
|
|
begin
|
|
Result := IdentToInt(Ident, Cmd, EditorTmplEditCommandStrs);
|
|
end;
|
|
|
|
function TmplEditCommandToIdent(Cmd: longint; var Ident: string): boolean;
|
|
begin
|
|
Result := (Cmd >= ecPluginFirstTemplEdit) and (Cmd - ecPluginFirstTemplEdit < ecSynPTmplEdCount);
|
|
if not Result then exit;
|
|
Result := IntToIdent(Cmd, Ident, EditorTmplEditCommandStrs);
|
|
end;
|
|
|
|
procedure GetEditorCommandValues(Proc: TGetStrProc);
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := Low(EditorTmplEditCommandStrs) to High(EditorTmplEditCommandStrs) do
|
|
Proc(EditorTmplEditCommandStrs[I].Name);
|
|
end;
|
|
|
|
|
|
initialization
|
|
RegisterKeyCmdIdentProcs(@IdentToTmplEditCommand,
|
|
@TmplEditCommandToIdent);
|
|
RegisterExtraGetEditorCommandValues(@GetEditorCommandValues);
|
|
|
|
end.
|
|
|