mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-26 23:03:48 +02:00
790 lines
25 KiB
ObjectPascal
790 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;
|
|
pnlKeys: 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.SelectedIndex:=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;
|
|
|
|
ConflictsTreeView.Visible:=ConflictsTreeView.Items.Count<>0;
|
|
KeyMapSplitter.Visible:=ConflictsTreeView.Visible;
|
|
|
|
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.
|
|
|