mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-12-01 13:27:18 +01:00
Instead, `Export` button now opens file save dialog in user key scheme subdirectory ('userkeyschemes') of Lazarus settings directory. This behavior is similar to how color schemes are handled.
Thus, user is able to edit scheme, export it and then immediately select it if desired (even without IDE restart). To load an external scheme, user should put it to key scheme subdirectory of Lazarus settings directory and select it in Lazarus settings (no IDE restart needed).
789 lines
25 KiB
ObjectPascal
789 lines
25 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
}
|
|
unit editor_keymapping_options;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
// LCL
|
|
Forms, StdCtrls, ComCtrls, Controls, Dialogs, LCLType, LazUTF8, Menus, Buttons,
|
|
Clipbrd, EditBtn, ExtCtrls,
|
|
// LazControls
|
|
TreeFilterEdit,
|
|
// BuildIntf
|
|
IDEOptionsIntf,
|
|
// IdeIntf
|
|
IDEOptEditorIntf, IDEImagesIntf, SrcEditorIntf, IDECommands,
|
|
// IDE
|
|
EditorOptions, LazarusIDEStrConsts, editor_general_options,
|
|
KeymapSchemeDlg, KeyMapping, KeyMapShortCutDlg, Laz2_XMLCfg,
|
|
FileUtil;
|
|
|
|
type
|
|
|
|
{ TEditorKeymappingOptionsFrame }
|
|
|
|
TEditorKeymappingOptionsFrame = class(TAbstractIDEOptionsEditor)
|
|
BtnPanel: TPanel;
|
|
ExportButton: TButton;
|
|
ChooseSchemeButton: TBitBtn;
|
|
ClearButton: TBitBtn;
|
|
EditButton: TBitBtn;
|
|
FilterEdit: TTreeFilterEdit;
|
|
FindKeyButton: TBitBtn;
|
|
CommandLabel: TLabel;
|
|
ConflictsTreeView: TTreeView;
|
|
KeyMapSplitter: TSplitter;
|
|
KeyMapTreePanel: TPanel;
|
|
SchemeLabel: TLabel;
|
|
ResetKeyFilterBtn: TSpeedButton;
|
|
TreeView: TTreeView;
|
|
EditMenuItem: TMenuItem;
|
|
ClearMenuItem: TMenuItem;
|
|
PopupMenu1: TPopupMenu;
|
|
procedure ClearMenuItemClick(Sender: TObject);
|
|
procedure ConflictsTreeViewMouseDown(Sender: TObject; Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
procedure EditButtonClick(Sender: TObject);
|
|
procedure EditMenuItemClick(Sender: TObject);
|
|
procedure ChooseSchemeButtonClick(Sender: TObject);
|
|
procedure ClearButtonClick(Sender: TObject);
|
|
procedure ExportButtonClick(Sender: TObject);
|
|
procedure FilterEditAfterFilter(Sender: TObject);
|
|
function FilterEditFilterItem(ItemData: Pointer; out Done: Boolean): Boolean;
|
|
procedure FilterEditKeyPress(Sender: TObject; var {%H-}Key: char);
|
|
procedure FindKeyButtonClick(Sender: TObject);
|
|
procedure KeyMapSplitterMoved(Sender: TObject);
|
|
procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
|
|
procedure ResetKeyFilterBtnClick(Sender: TObject);
|
|
procedure TreeViewDblClick(Sender: TObject);
|
|
procedure TreeViewKeyPress(Sender: TObject; var Key: char);
|
|
procedure TreeViewSelectionChanged(Sender: TObject);
|
|
procedure PopupMenu1Popup(Sender: TObject);
|
|
private
|
|
FDialog: TAbstractOptionsEditorDialog;
|
|
FEditingKeyMap: TKeyCommandRelationList;
|
|
FIdleConnected: boolean;
|
|
KeyMapKeyFilter: TIDEShortCut;
|
|
fModified: Boolean;
|
|
function GeneralPage: TEditorGeneralOptionsFrame; inline;
|
|
procedure FillKeyMappingTreeView;
|
|
procedure EditCommandMapping(ANode: TTreeNode);
|
|
procedure EditConflict(ANode: TTreeNode);
|
|
procedure EditCommandRelation(ARelation: TKeyCommandRelation);
|
|
procedure ClearCommandMapping(ANode: TTreeNode);
|
|
procedure ClearConflict(ANode: TTreeNode);
|
|
procedure ClearCommandRelation(ARelation: TKeyCommandRelation);
|
|
function KeyMappingRelationToCaption(Index: Integer): String;
|
|
function KeyMappingRelationToCaption(KeyRelation: TKeyCommandRelation): String;
|
|
function KeyShortCutToCaption(const aKey: TKeyCommandRelation): string;
|
|
function CaptionToKeyMappingRelation(aCaption: string): TKeyCommandRelation;
|
|
procedure SetIdleConnected(AValue: boolean);
|
|
procedure UpdateKeyFilterButton;
|
|
procedure UpdateSchemeLabel;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
function GetTitle: String; override;
|
|
procedure Setup(ADialog: TAbstractOptionsEditorDialog); override;
|
|
procedure ReadSettings(AOptions: TAbstractIDEOptions); override;
|
|
procedure WriteSettings(AOptions: TAbstractIDEOptions); override;
|
|
class function SupportedOptionsClass: TAbstractIDEOptionsClass; override;
|
|
procedure SelectByIdeCommand(ACmd: word);
|
|
procedure UpdateTree;
|
|
procedure UpdateConflictTree;
|
|
property EditingKeyMap: TKeyCommandRelationList read FEditingKeyMap;
|
|
property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
var
|
|
imgKeyCategory, imgKeyItem: Integer;
|
|
|
|
type
|
|
|
|
{ TKeyMapErrorsForm }
|
|
|
|
TKeyMapErrorsForm = class(TForm)
|
|
ListBox: TListBox;
|
|
BackButton: TButton;
|
|
ErrorsPopupMenu: TPopupMenu;
|
|
CopyMenuItem: TMenuItem;
|
|
procedure BackButtonClick(Sender: TObject);
|
|
procedure CopyMenuItemClick(Sender: TObject);
|
|
public
|
|
constructor Create(AnOwner: TComponent); override;
|
|
end;
|
|
|
|
constructor TKeyMapErrorsForm.Create(AnOwner: TComponent);
|
|
begin
|
|
inherited CreateNew(AnOwner);
|
|
SetBounds((Screen.Width - 410) div 2, (Screen.Height - 260) div 2, 400, 250);
|
|
Caption := dlgKeyMappingErrors;
|
|
|
|
ListBox := TListBox.Create(Self);
|
|
with ListBox do
|
|
begin
|
|
Name := 'ListBox';
|
|
Align := alTop;
|
|
Parent := Self;
|
|
end;
|
|
|
|
BackButton := TButton.Create(Self);
|
|
with BackButton do
|
|
begin
|
|
Name := 'BackButton';
|
|
AutoSize := true;
|
|
Anchors := [akLeft,akBottom];
|
|
Parent := Self;
|
|
AnchorParallel(akBottom,6,Self);
|
|
AnchorParallel(akLeft,6,Self);
|
|
Caption := dlgEdBack;
|
|
OnClick := @BackButtonClick;
|
|
end;
|
|
|
|
ErrorsPopupMenu := TPopupMenu.Create(Self);
|
|
ErrorsPopupMenu.Name := 'ErrorsPopupMenu';
|
|
CopyMenuItem := TMenuItem.Create(Self);
|
|
CopyMenuItem.Caption := lisCopyAllItemsToClipboard;
|
|
CopyMenuItem.OnClick := @CopyMenuItemClick;
|
|
CopyMenuItem.ImageIndex := IDEImages.LoadImage('laz_copy');
|
|
ErrorsPopupMenu.Items.Add(CopyMenuItem);
|
|
|
|
ListBox.AnchorToNeighbour(akBottom,6,BackButton);
|
|
ListBox.PopupMenu := ErrorsPopupMenu;
|
|
end;
|
|
|
|
procedure TKeyMapErrorsForm.BackButtonClick(Sender: TObject);
|
|
begin
|
|
ModalResult := mrOk;
|
|
end;
|
|
|
|
procedure TKeyMapErrorsForm.CopyMenuItemClick(Sender: TObject);
|
|
begin
|
|
Clipboard.AsText := ListBox.Items.Text;
|
|
end;
|
|
|
|
{ TEditorKeymappingOptionsFrame }
|
|
|
|
constructor TEditorKeymappingOptionsFrame.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FEditingKeyMap := TKeyCommandRelationList.Create;
|
|
EditButton.Enabled:=false;
|
|
ClearButton.Enabled:=false;
|
|
fModified:=False;
|
|
end;
|
|
|
|
destructor TEditorKeymappingOptionsFrame.Destroy;
|
|
begin
|
|
IdleConnected:=false;
|
|
FEditingKeyMap.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ChooseSchemeButtonClick(Sender: TObject);
|
|
var
|
|
NewScheme: String;
|
|
begin
|
|
NewScheme := EditorOpts.KeyMappingScheme;
|
|
if ShowChooseKeySchemeDialog(NewScheme) = mrOk then begin
|
|
EditorOpts.KeyMappingScheme := NewScheme;
|
|
FEditingKeyMap.LoadScheme(NewScheme);
|
|
FillKeyMappingTreeView;
|
|
fModified:=False;
|
|
UpdateSchemeLabel;
|
|
UpdateConflictTree;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.EditButtonClick(Sender: TObject);
|
|
begin
|
|
EditCommandMapping(TreeView.Selected)
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ClearButtonClick(Sender: TObject);
|
|
begin
|
|
ClearCommandMapping(TreeView.Selected)
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ExportButtonClick(Sender: TObject);
|
|
var
|
|
dlg : TSaveDialog;
|
|
xml : TXMLConfig;
|
|
exp : TKeyCommandRelationList;
|
|
i : integer;
|
|
begin
|
|
xml := nil;
|
|
dlg := TSaveDialog.Create(Self);
|
|
exp := TKeyCommandRelationList.Create;
|
|
try
|
|
dlg.InitialDir := UserKeySchemeDirectory(True);
|
|
dlg.DefaultExt:='xml';
|
|
dlg.Filter := dlgFilterXML + '|*.xml|' + dlgFilterAll +'|'+GetAllFilesMask;
|
|
dlg.FilterIndex := 0;
|
|
if not dlg.Execute then Exit;
|
|
xml := TXMLConfig.CreateClean(dlg.FileName);
|
|
exp.Assign(FEditingKeyMap);
|
|
for i:=0 to exp.RelationCount-1 do begin
|
|
exp.Relations[i].SkipSaving := false;
|
|
// default must be reset to zero, otherwise it will not be saved by SaveToXmlConfig
|
|
// aveToXMLConfig omits any shortcuts matching defaults
|
|
exp.Relations[i].DefaultShortcutA := CleanIDEShortCut;
|
|
exp.Relations[i].DefaultShortcutB := CleanIDEShortCut;
|
|
end;
|
|
exp.SaveToXMLConfig(xml, 'KeyMapping/', true);
|
|
finally
|
|
exp.Free;
|
|
dlg.Free;
|
|
xml.Free;
|
|
end;
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.FilterEditFilterItem(ItemData: Pointer;
|
|
out Done: Boolean): Boolean;
|
|
var
|
|
KeyRel: TKeyCommandRelation;
|
|
begin
|
|
Done:=True;
|
|
Result:=False;
|
|
if TObject(ItemData) is TKeyCommandRelation then
|
|
begin
|
|
KeyRel:=TKeyCommandRelation(ItemData); // Tree item is actual key command.
|
|
Done:=False;
|
|
Result:=KeyMapKeyFilter.Key1<>VK_UNKNOWN;
|
|
if Result then begin // Key filter is defined.
|
|
Done:=True;
|
|
Result:=(CompareIDEShortCutKey1s(@KeyMapKeyFilter,@KeyRel.ShortcutA)=0)
|
|
or (CompareIDEShortCutKey1s(@KeyMapKeyFilter,@KeyRel.ShortcutB)=0);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.FilterEditKeyPress(Sender: TObject; var Key: char);
|
|
begin
|
|
ResetKeyFilterBtnClick(Nil);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.FindKeyButtonClick(Sender: TObject);
|
|
var
|
|
ShortCutDialog: TShortCutDialog;
|
|
begin
|
|
ShortCutDialog := TShortCutDialog.Create(nil);
|
|
try
|
|
ShortCutDialog.ShowSecondary:=False;
|
|
ShortCutDialog.ShowSequence:=False;
|
|
ShortCutDialog.Caption:=lisChooseAKey;
|
|
ShortCutDialog.PrimaryShortCut := KeyMapKeyFilter;
|
|
if ShortCutDialog.ShowModal = mrOK then begin
|
|
KeyMapKeyFilter := ShortCutDialog.PrimaryShortCut;
|
|
UpdateKeyFilterButton;
|
|
FilterEdit.ResetFilter; // Allow only one of the filters to be active.
|
|
FilterEdit.InvalidateFilter; // The edit may have been empty. Must invalidate.
|
|
end;
|
|
finally
|
|
ShortCutDialog.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMapSplitterMoved(Sender: TObject);
|
|
begin
|
|
TreeView.Update;
|
|
ConflictsTreeView.Update;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.OnIdle(Sender: TObject;
|
|
var Done: Boolean);
|
|
begin
|
|
IdleConnected:=false;
|
|
UpdateConflictTree;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ResetKeyFilterBtnClick(Sender: TObject);
|
|
begin
|
|
KeyMapKeyFilter.Key1 := VK_UNKNOWN;
|
|
KeyMapKeyFilter.Key2 := VK_UNKNOWN;
|
|
UpdateKeyFilterButton; // Allow only one of the filters to be active.
|
|
FilterEdit.InvalidateFilter;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.TreeViewDblClick(Sender: TObject);
|
|
var
|
|
P: TPoint;
|
|
ANode: TTreeNode;
|
|
begin
|
|
P := TreeView.ScreenToClient(Mouse.CursorPos);
|
|
ANode := TreeView.GetNodeAt(P.X, P.Y);
|
|
if (ANode<>nil) and (ANode.Data<>nil) and (TObject(ANode.Data) is TKeyCommandRelation) then
|
|
EditCommandMapping(ANode);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.TreeViewKeyPress(Sender: TObject; var Key: char);
|
|
begin
|
|
if (Key = char(VK_RETURN)) and (TreeView.Selected<>nil) then
|
|
EditCommandMapping(TreeView.Selected);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.TreeViewSelectionChanged(Sender: TObject);
|
|
var
|
|
ANode: TTreeNode;
|
|
begin
|
|
ANode := TreeView.Selected;
|
|
EditButton.Enabled:=
|
|
(ANode<>nil) and (ANode.Data<>nil) and (TObject(ANode.Data) is TKeyCommandRelation);
|
|
ClearButton.Enabled:=EditButton.Enabled;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.PopupMenu1Popup(Sender: TObject);
|
|
var
|
|
ANode: TTreeNode;
|
|
pop: TPopupMenu;
|
|
begin
|
|
pop := Sender as TPopupMenu;
|
|
if pop.PopupComponent = TreeView then begin
|
|
ANode := TreeView.Selected;
|
|
EditMenuItem.Enabled:=
|
|
(ANode<>nil) and (ANode.Data<>nil) and (TObject(ANode.Data) is TKeyCommandRelation);
|
|
end else if pop.PopupComponent=ConflictsTreeView then begin
|
|
ANode:=ConflictsTreeView.Selected;
|
|
EditMenuItem.Enabled := (ANode<>nil) and (CaptionToKeyMappingRelation(ANode.Text)<>nil);
|
|
end else
|
|
EditMenuItem.Enabled := False;
|
|
ClearMenuItem.Enabled := EditMenuItem.Enabled;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.EditMenuItemClick(Sender: TObject);
|
|
begin
|
|
if PopupMenu1.PopupComponent=TreeView then
|
|
EditCommandMapping(TreeView.Selected)
|
|
else
|
|
EditConflict(ConflictsTreeView.Selected);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ClearMenuItemClick(Sender: TObject);
|
|
begin
|
|
if PopupMenu1.PopupComponent=TreeView then
|
|
ClearCommandMapping(TreeView.Selected)
|
|
else
|
|
ClearConflict(ConflictsTreeView.Selected);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ConflictsTreeViewMouseDown(
|
|
Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
Node: TTreeNode;
|
|
begin
|
|
if (Button=mbLeft) and (ssDouble in Shift) then begin
|
|
Node:=ConflictsTreeView.GetNodeAt(X,Y);
|
|
EditConflict(Node);
|
|
end;
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.GeneralPage: TEditorGeneralOptionsFrame; inline;
|
|
begin
|
|
Result := TEditorGeneralOptionsFrame(FDialog.FindEditor(TEditorGeneralOptionsFrame));
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.GetTitle: String;
|
|
begin
|
|
Result := dlgKeyMapping;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.Setup(ADialog: TAbstractOptionsEditorDialog);
|
|
begin
|
|
FDialog := ADialog;
|
|
ChooseSchemeButton.Caption := lisEdOptsLoadAScheme;
|
|
FindKeyButton.Caption := lisFindKeyCombination;
|
|
CommandLabel.Caption := lisSelectedCommandsMapping;
|
|
EditButton.Caption := lisEdit;
|
|
ClearButton.Caption := lisClear;
|
|
EditMenuItem.Caption := lisEdit;
|
|
ClearMenuItem.Caption := lisClear;
|
|
ExportButton.Caption := lisExport;
|
|
|
|
TreeView.Images := IDEImages.Images_16;
|
|
ConflictsTreeView.Images := IDEImages.Images_16;
|
|
imgKeyCategory := IDEImages.LoadImage('item_keyboard');
|
|
imgKeyItem := IDEImages.LoadImage('item_character');
|
|
IDEImages.AssignImage(ChooseSchemeButton, 'item_keyboard'); // keymapcategory
|
|
IDEImages.AssignImage(FindKeyButton, 'menu_search_find');
|
|
IDEImages.AssignImage(EditButton, 'laz_edit');
|
|
IDEImages.AssignImage(ClearButton, 'menu_clean');
|
|
PopupMenu1.Images := IDEImages.Images_16;
|
|
EditMenuItem.ImageIndex := IDEImages.LoadImage('laz_edit');
|
|
ClearMenuItem.ImageIndex := IDEImages.LoadImage('menu_clean');
|
|
|
|
IDEImages.AssignImage(ResetKeyFilterBtn, ResBtnListFilter);
|
|
ResetKeyFilterBtn.Enabled := not IDEShortCutEmpty(KeyMapKeyFilter);
|
|
|
|
// FillKeyMappingTreeView; ... Done in ReadSettings.
|
|
// UpdateSchemeLabel;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ReadSettings(AOptions: TAbstractIDEOptions);
|
|
var
|
|
i: integer;
|
|
begin
|
|
with AOptions as TEditorOptions do
|
|
FEditingKeyMap.Assign(KeyMap);
|
|
FillKeyMappingTreeView;
|
|
UpdateSchemeLabel;
|
|
with GeneralPage do
|
|
for i := Low(PreviewEdits) to High(PreviewEdits) do
|
|
if PreviewEdits[i] <> nil then
|
|
FEditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
|
|
IdleConnected:=true;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.WriteSettings(AOptions: TAbstractIDEOptions);
|
|
begin
|
|
with AOptions as TEditorOptions do
|
|
KeyMap.Assign(FEditingKeyMap);
|
|
end;
|
|
|
|
class function TEditorKeymappingOptionsFrame.SupportedOptionsClass: TAbstractIDEOptionsClass;
|
|
begin
|
|
Result := TEditorOptions;
|
|
end;
|
|
|
|
function GetIDECommandImageIndex(AKeyRelation: TKeyCommandRelation): Integer;
|
|
var
|
|
IDECommand: TIDECommand;
|
|
begin
|
|
IDECommand:=IDECommandList.FindIDECommand(AKeyRelation.Command);
|
|
if (IDECommand=nil) or (IDECommand.UserCount=0) then
|
|
Exit(imgKeyItem);
|
|
Result:=IDECommand.Users[0].ImageIndex;
|
|
if Result=-1 then
|
|
Result:=imgKeyItem;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.FillKeyMappingTreeView;
|
|
var
|
|
i, j: Integer;
|
|
NewCategoryNode, NewKeyNode: TTreeNode;
|
|
CurCategory: TIDECommandCategory;
|
|
CurKeyRelation: TKeyCommandRelation;
|
|
ChildNodeIndex: Integer;
|
|
CategoryNodeIndex: Integer;
|
|
ItemCaption: String;
|
|
begin
|
|
with TreeView do
|
|
begin
|
|
BeginUpdate;
|
|
CategoryNodeIndex:=0;
|
|
for i := 0 to FEditingKeyMap.CategoryCount - 1 do
|
|
begin
|
|
CurCategory := FEditingKeyMap.Categories[i];
|
|
if Items.TopLvlCount > CategoryNodeIndex then
|
|
begin
|
|
NewCategoryNode := Items.TopLvlItems[CategoryNodeIndex];
|
|
NewCategoryNode.Text := CurCategory.Description;
|
|
NewCategoryNode.Data := CurCategory;
|
|
end
|
|
else
|
|
NewCategoryNode := Items.AddObject(Nil, CurCategory.Description, CurCategory);
|
|
NewCategoryNode.ImageIndex := imgKeyCategory;
|
|
NewCategoryNode.SelectedIndex := NewCategoryNode.ImageIndex;
|
|
ChildNodeIndex:=0;
|
|
for j := 0 to CurCategory.Count - 1 do
|
|
begin
|
|
CurKeyRelation := TKeyCommandRelation(CurCategory[j]);
|
|
ItemCaption:=KeyMappingRelationToCaption(CurKeyRelation);
|
|
if NewCategoryNode.Count > ChildNodeIndex then
|
|
begin
|
|
NewKeyNode := NewCategoryNode.Items[ChildNodeIndex];
|
|
NewKeyNode.Text := ItemCaption;
|
|
NewKeyNode.Data := CurKeyRelation;
|
|
end
|
|
else
|
|
NewKeyNode := Items.AddChildObject(NewCategoryNode,ItemCaption, CurKeyRelation);
|
|
NewKeyNode.ImageIndex := GetIDECommandImageIndex(CurKeyRelation);
|
|
NewKeyNode.SelectedIndex := NewKeyNode.ImageIndex;
|
|
inc(ChildNodeIndex);
|
|
end;
|
|
// delete unneeded ones
|
|
while NewCategoryNode.Count > ChildNodeIndex do
|
|
NewCategoryNode[NewCategoryNode.Count - 1].Delete;
|
|
if NewCategoryNode.Count>0 then
|
|
inc(CategoryNodeIndex);
|
|
end;
|
|
while Items.TopLvlCount > CategoryNodeIndex do
|
|
Items.TopLvlItems[Items.TopLvlCount - 1].Delete;
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.FilterEditAfterFilter(Sender: TObject);
|
|
begin
|
|
TreeView.MakeSelectionVisible;
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.KeyMappingRelationToCaption(Index: Integer): String;
|
|
begin
|
|
Result := KeyMappingRelationToCaption(FEditingKeyMap.Relations[Index]);
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.KeyMappingRelationToCaption(
|
|
KeyRelation: TKeyCommandRelation): String;
|
|
const
|
|
MaxLength = 60;
|
|
begin
|
|
with KeyRelation do
|
|
begin
|
|
Result := LocalizedName;
|
|
if UTF8Length(Result)>MaxLength then
|
|
Result := UTF8Copy(LocalizedName, 1, MaxLength)+'...';
|
|
if Result <> '' then
|
|
Result := Result + ' ';
|
|
Result := Result + KeyValuesToCaptionStr(ShortcutA, ShortcutB, '[');
|
|
end;
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.KeyShortCutToCaption(
|
|
const aKey: TKeyCommandRelation): string;
|
|
begin
|
|
Result:=aKey.Category.Description+'/'
|
|
+KeyMappingRelationToCaption(aKey);
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.CaptionToKeyMappingRelation(
|
|
aCaption: string): TKeyCommandRelation;
|
|
var
|
|
c: Integer;
|
|
aCategory: TIDECommandCategory;
|
|
CatStr: String;
|
|
i: Integer;
|
|
aKey: TKeyCommandRelation;
|
|
s: String;
|
|
begin
|
|
for c:=0 to FEditingKeyMap.CategoryCount-1 do begin
|
|
aCategory:=FEditingKeyMap.Categories[c];
|
|
CatStr:=aCategory.Description+'/';
|
|
if LeftStr(aCaption,length(CatStr))<>CatStr then continue;
|
|
for i:=0 to aCategory.Count-1 do begin
|
|
aKey:=TObject(aCategory[i]) as TKeyCommandRelation;
|
|
s:=CatStr+EditorCommandToDescriptionString(aKey.Command);
|
|
if LeftStr(aCaption,length(s))<>s then continue;
|
|
Result:=aKey;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.SetIdleConnected(AValue: boolean);
|
|
begin
|
|
if FIdleConnected=AValue then Exit;
|
|
FIdleConnected:=AValue;
|
|
if IdleConnected then
|
|
Application.AddOnIdleHandler(@OnIdle)
|
|
else
|
|
Application.RemoveOnIdleHandler(@OnIdle);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.UpdateKeyFilterButton;
|
|
begin
|
|
if IDEShortCutEmpty(KeyMapKeyFilter) then
|
|
FindKeyButton.Caption := lisFindKeyCombination
|
|
else
|
|
FindKeyButton.Caption:=
|
|
Format(lisFilter3, [KeyAndShiftStateToEditorKeyString(KeyMapKeyFilter)]);
|
|
ResetKeyFilterBtn.Enabled := not IDEShortCutEmpty(KeyMapKeyFilter);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.UpdateSchemeLabel;
|
|
var
|
|
s: String;
|
|
begin
|
|
s:=lisNowLoadedScheme+EditorOpts.KeyMappingScheme;
|
|
if fModified then
|
|
s:=s+' (*)';
|
|
SchemeLabel.Caption:=s;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.SelectByIdeCommand(ACmd: word);
|
|
var
|
|
Node: TTreeNode;
|
|
begin
|
|
Node := TreeView.Items.GetFirstNode;
|
|
while node <> nil do begin
|
|
if (node.Data <> nil) and (TObject(Node.Data) is TKeyCommandRelation) and
|
|
(TKeyCommandRelation(Node.Data).Command = ACmd)
|
|
then
|
|
break;
|
|
node := Node.GetNext;
|
|
end;
|
|
if node <> nil then begin
|
|
Node.MakeVisible;
|
|
Node.Selected := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.UpdateTree;
|
|
begin
|
|
FillKeyMappingTreeView;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.UpdateConflictTree;
|
|
var
|
|
ConflictCount: integer;
|
|
Key1: TKeyCommandRelation;
|
|
Key2: TKeyCommandRelation;
|
|
|
|
procedure Check(const ShortCut1, ShortCut2: TIDEShortCut);
|
|
// check if ShortCut1 hides ShortCut2
|
|
var
|
|
ConflictNode: TTreeNode;
|
|
KeyNode: TTreeNode;
|
|
begin
|
|
if (ShortCut1.Key1=VK_UNKNOWN)
|
|
or (ShortCut1.Key1<>ShortCut2.Key1)
|
|
or (ShortCut1.Shift1<>ShortCut2.Shift1) then
|
|
exit; // first key differ
|
|
|
|
if (ShortCut1.Key2=VK_UNKNOWN) or (ShortCut2.Key2=VK_UNKNOWN)
|
|
or ((ShortCut1.Key2=ShortCut2.Key2) and (ShortCut1.Shift2=ShortCut2.Shift2))
|
|
then begin
|
|
// conflict found, add node with a sub node for each key
|
|
inc(ConflictCount);
|
|
ConflictNode:=ConflictsTreeView.Items.Add(nil,srkmConflic+IntToStr(ConflictCount));
|
|
ConflictNode.ImageIndex:=imgKeyItem;
|
|
ConflictNode.StateIndex:=imgKeyItem;
|
|
KeyNode:=ConflictsTreeView.Items.AddChild(ConflictNode,
|
|
KeyShortCutToCaption(Key1));
|
|
KeyNode.ImageIndex := imgKeyItem;
|
|
KeyNode.SelectedIndex := imgKeyItem;
|
|
KeyNode:=ConflictsTreeView.Items.AddChild(ConflictNode,
|
|
KeyShortCutToCaption(Key2));
|
|
KeyNode.ImageIndex := imgKeyItem;
|
|
KeyNode.SelectedIndex := imgKeyItem;
|
|
ConflictNode.Expanded:=true;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
j: Integer;
|
|
begin
|
|
ConflictsTreeView.BeginUpdate;
|
|
ConflictsTreeView.Items.Clear;
|
|
|
|
ConflictCount:=0;
|
|
for i:=0 to FEditingKeyMap.Count-1 do begin
|
|
Key1:=FEditingKeyMap[i];
|
|
for j:=i+1 to FEditingKeyMap.Count-1 do begin
|
|
Key2:=FEditingKeyMap[j];
|
|
if (not Key1.Category.ScopeIntersects(Key2.Category.Scope)) then
|
|
continue;
|
|
Check(Key1.ShortcutA,Key2.ShortcutA);
|
|
Check(Key1.ShortcutA,Key2.ShortcutB);
|
|
Check(Key1.ShortcutB,Key2.ShortcutA);
|
|
Check(Key1.ShortcutB,Key2.ShortcutB);
|
|
end;
|
|
end;
|
|
|
|
if ConflictsTreeView.Items.Count=0 then
|
|
ConflictsTreeView.Items.Add(nil, lisThereAreNoConflictingKeys);
|
|
|
|
ConflictsTreeView.EndUpdate;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.EditCommandMapping(ANode: TTreeNode);
|
|
begin
|
|
if ANode=nil then exit;
|
|
EditCommandRelation(TKeyCommandRelation(ANode.Data));
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.EditConflict(ANode: TTreeNode);
|
|
var
|
|
ARelation: TKeyCommandRelation;
|
|
begin
|
|
if ANode=nil then exit;
|
|
ARelation:=CaptionToKeyMappingRelation(ANode.Text);
|
|
EditCommandRelation(ARelation);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.EditCommandRelation(
|
|
ARelation: TKeyCommandRelation);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if ARelation=nil then exit;
|
|
i := FEditingKeyMap.IndexOf(ARelation);
|
|
if (i < 0) or (ShowKeyMappingEditForm(i, FEditingKeyMap) <> mrOk) then exit;
|
|
FillKeyMappingTreeView;
|
|
fModified:=True;
|
|
UpdateSchemeLabel;
|
|
with GeneralPage do
|
|
for i := Low(PreviewEdits) to High(PreviewEdits) do
|
|
if PreviewEdits[i] <> nil then
|
|
FEditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
|
|
UpdateConflictTree;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ClearCommandMapping(ANode: TTreeNode);
|
|
begin
|
|
if ANode=nil then exit;
|
|
ClearCommandRelation(TKeyCommandRelation(ANode.Data));
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ClearConflict(ANode: TTreeNode);
|
|
var
|
|
ARelation: TKeyCommandRelation;
|
|
begin
|
|
if ANode=nil then exit;
|
|
ARelation:=CaptionToKeyMappingRelation(ANode.Text);
|
|
ClearCommandRelation(ARelation);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ClearCommandRelation(
|
|
ARelation: TKeyCommandRelation);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if ARelation=nil then exit;
|
|
i := FEditingKeyMap.IndexOf(ARelation);
|
|
if (i < 0) then exit;
|
|
ARelation.ShortcutA := IDEShortCut(VK_UNKNOWN, []);
|
|
ARelation.ShortcutB := IDEShortCut(VK_UNKNOWN, []);
|
|
FillKeyMappingTreeView;
|
|
fModified:=True;
|
|
UpdateSchemeLabel;
|
|
with GeneralPage do
|
|
for i := Low(PreviewEdits) to High(PreviewEdits) do
|
|
if PreviewEdits[i] <> nil then
|
|
FEditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
|
|
UpdateConflictTree;
|
|
end;
|
|
|
|
initialization
|
|
RegisterIDEOptionsEditor(GroupEditor, TEditorKeymappingOptionsFrame, EdtOptionsKeys);
|
|
end.
|
|
|