mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-27 17:34:55 +02:00
458 lines
15 KiB
ObjectPascal
458 lines
15 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
* *
|
|
***************************************************************************
|
|
}
|
|
unit editor_keymapping_options;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, FileUtil, Forms, StdCtrls, ComCtrls, Controls,
|
|
Dialogs, LCLType, LCLProc,
|
|
EditorOptions, LazarusIDEStrConsts, IDEOptionsIntf, IDEImagesIntf,
|
|
editor_general_options,
|
|
KeymapSchemeDlg, KeyMapping, IDECommands, KeyMapShortCutDlg, SrcEditorIntf;
|
|
|
|
type
|
|
|
|
{ TEditorKeymappingOptionsFrame }
|
|
|
|
TEditorKeymappingOptionsFrame = class(TAbstractIDEOptionsEditor)
|
|
KeyMappingClearButton: TButton;
|
|
KeyMappingChooseSchemeButton: TButton;
|
|
KeyMappingConsistencyCheckButton: TButton;
|
|
KeyMappingFilterEdit: TEdit;
|
|
KeyMappingFindKeyButton: TButton;
|
|
KeyMappingHelpLabel: TLabel;
|
|
KeyMappingTreeView: TTreeView;
|
|
procedure KeyMappingChooseSchemeButtonClick(Sender: TObject);
|
|
procedure KeyMappingClearButtonClick(Sender: TObject);
|
|
procedure KeyMappingConsistencyCheckButtonClick(Sender: TObject);
|
|
procedure KeyMappingFilterEditChange(Sender: TObject);
|
|
procedure KeyMappingFilterEditEnter(Sender: TObject);
|
|
procedure KeyMappingFilterEditExit(Sender: TObject);
|
|
procedure KeyMappingFindKeyButtonClick(Sender: TObject);
|
|
procedure KeyMappingTreeViewDblClick(Sender: TObject);
|
|
procedure KeyMappingTreeViewSelectionChanged(Sender: TObject);
|
|
private
|
|
FDialog: TAbstractOptionsEditorDialog;
|
|
KeyMapNameFilter: string;
|
|
EditingKeyMap: TKeyCommandRelationList;
|
|
KeyMapKeyFilter: TIDEShortCut;
|
|
|
|
function GeneralPage: TEditorGeneralOptionsFrame; inline;
|
|
procedure FillKeyMappingTreeView;
|
|
function KeyMappingRelationToString(Index: Integer): String;
|
|
function KeyMappingRelationToString(KeyRelation: TKeyCommandRelation): String;
|
|
procedure UpdateKeyFilterButton;
|
|
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;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
var
|
|
imgKeyCategory, imgKeyItem: Integer;
|
|
|
|
type
|
|
TKeyMapErrorsForm = class(TForm)
|
|
ListBox: TListBox;
|
|
BackButton: TButton;
|
|
procedure BackButtonClick(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;
|
|
ListBox.AnchorToNeighbour(akBottom,6,BackButton);
|
|
end;
|
|
|
|
procedure TKeyMapErrorsForm.BackButtonClick(Sender: TObject);
|
|
begin
|
|
ModalResult := mrOk;
|
|
end;
|
|
|
|
{ TEditorKeymappingOptionsFrame }
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingFilterEditChange(Sender: TObject);
|
|
var
|
|
Filter: String;
|
|
begin
|
|
if [csLoading, csDestroying] * ComponentState <> [] then
|
|
Exit;
|
|
Filter := KeyMappingFilterEdit.Text;
|
|
if (Filter = lisFilter2) or (Filter = KeyMappingFilterEdit.Name) then
|
|
Filter := '';
|
|
KeyMapNameFilter := Filter;
|
|
FillKeyMappingTreeView;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingChooseSchemeButtonClick(
|
|
Sender: TObject);
|
|
var
|
|
NewScheme: String;
|
|
begin
|
|
NewScheme := EditorOpts.KeyMappingScheme;
|
|
if ShowChooseKeySchemeDialog(NewScheme) <> mrOk then
|
|
Exit;
|
|
EditorOpts.KeyMappingScheme := NewScheme;
|
|
EditingKeyMap.LoadScheme(NewScheme);
|
|
FillKeyMappingTreeView;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingClearButtonClick(
|
|
Sender: TObject);
|
|
var
|
|
i: integer;
|
|
ARelation: TKeyCommandRelation;
|
|
ANode: TTreeNode;
|
|
begin
|
|
ANode := KeyMappingTreeView.Selected;
|
|
if (ANode <> nil) and (ANode.Data <> nil) and
|
|
(TObject(ANode.Data) is TKeyCommandRelation) then
|
|
begin
|
|
ARelation := TKeyCommandRelation(ANode.Data);
|
|
i := EditingKeyMap.IndexOf(ARelation);
|
|
if (i >= 0) {and (ShowKeyMappingEditForm(i, EditingKeyMap) = mrOk)} then
|
|
begin
|
|
ARelation.ShortcutA := IDEShortCut(VK_UNKNOWN, []);
|
|
ARelation.ShortcutB := IDEShortCut(VK_UNKNOWN, []);
|
|
FillKeyMappingTreeView;
|
|
with GeneralPage do
|
|
for i := Low(PreviewEdits) to High(PreviewEdits) do
|
|
if PreviewEdits[i] <> nil then
|
|
EditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingConsistencyCheckButtonClick(
|
|
Sender: TObject);
|
|
var
|
|
Protocol: TStringList;
|
|
ErrorCount, Index1, Index2: Integer;
|
|
ACaption, AText: String;
|
|
KeyMapErrorsForm: TKeyMapErrorsForm;
|
|
begin
|
|
Protocol := TStringList.Create;
|
|
try
|
|
ErrorCount := FindKeymapConflicts(EditingKeyMap, Protocol, Index1, Index2);
|
|
if ErrorCount > 0 then
|
|
begin
|
|
KeyMapErrorsForm := TKeyMapErrorsForm.Create(nil);
|
|
try
|
|
KeyMapErrorsForm.ListBox.Items.Assign(Protocol);
|
|
KeyMapErrorsForm.ShowModal;
|
|
finally
|
|
KeyMapErrorsForm.Free;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
ACaption := dlgReport;
|
|
AText := dlgEdNoErr;
|
|
MessageDlg(ACaption, AText, mtInformation, [mbOk], 0);
|
|
end;
|
|
finally
|
|
Protocol.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingFilterEditEnter(Sender: TObject);
|
|
begin
|
|
if KeyMappingFilterEdit.Text = lisFilter2 then
|
|
KeyMappingFilterEdit.Text := '';
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingFilterEditExit(Sender: TObject);
|
|
begin
|
|
if KeyMappingFilterEdit.Text = '' then
|
|
KeyMappingFilterEdit.Text := lisFilter2;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingFindKeyButtonClick(Sender: TObject);
|
|
var
|
|
KeyFilter: TIDEShortCut;
|
|
begin
|
|
if ShowKeyMappingGrabForm(KeyFilter) <> mrOK then
|
|
Exit;
|
|
//debugln(['TEditorOptionsForm.KeyMappingFindKeyButtonClick ',KeyAndShiftStateToEditorKeyString(KeyFilter)]);
|
|
KeyMapKeyFilter := KeyFilter;
|
|
UpdateKeyFilterButton;
|
|
FillKeyMappingTreeView;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingTreeViewDblClick(
|
|
Sender: TObject);
|
|
var
|
|
P: TPoint;
|
|
i: integer;
|
|
ARelation: TKeyCommandRelation;
|
|
ANode: TTreeNode;
|
|
begin
|
|
P := KeyMappingTreeView.ScreenToClient(Mouse.CursorPos);
|
|
ANode := KeyMappingTreeView.GetNodeAt(P.X, P.Y);
|
|
if (ANode <> nil) and (ANode.Data <> nil) and
|
|
(TObject(ANode.Data) is TKeyCommandRelation) then
|
|
begin
|
|
ARelation := TKeyCommandRelation(ANode.Data);
|
|
i := EditingKeyMap.IndexOf(ARelation);
|
|
if (i >= 0) and (ShowKeyMappingEditForm(i, EditingKeyMap) = mrOk) then
|
|
begin
|
|
FillKeyMappingTreeView;
|
|
with GeneralPage do
|
|
for i := Low(PreviewEdits) to High(PreviewEdits) do
|
|
if PreviewEdits[i] <> nil then
|
|
EditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.KeyMappingTreeViewSelectionChanged(
|
|
Sender: TObject);
|
|
var
|
|
ANode: TTreeNode;
|
|
begin
|
|
ANode := KeyMappingTreeView.Selected;
|
|
KeyMappingClearButton.Enabled:=(ANode <> nil) and (ANode.Data <> nil)
|
|
and (TObject(ANode.Data) is TKeyCommandRelation);
|
|
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;
|
|
KeyMappingChooseSchemeButton.Caption := lisEdOptsLoadAScheme;
|
|
KeyMappingConsistencyCheckButton.Caption := dlgCheckConsistency;
|
|
KeyMappingHelpLabel.Caption := lisHintDoubleClickOnTheCommandYouWantToEdit;
|
|
KeyMappingFilterEdit.Text := lisFilter2;
|
|
KeyMappingFindKeyButton.Caption := lisFindKeyCombination;
|
|
KeyMappingTreeView.Images := IDEImages.Images_16;
|
|
KeyMappingClearButton.Caption := lisClearKeyMapping;
|
|
imgKeyCategory := IDEImages.LoadImage(16, 'item_keyboard');
|
|
imgKeyItem := IDEImages.LoadImage(16, 'item_character');
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.ReadSettings(AOptions: TAbstractIDEOptions);
|
|
var
|
|
i: integer;
|
|
begin
|
|
with AOptions as TEditorOptions do
|
|
EditingKeyMap.Assign(KeyMap);
|
|
FillKeyMappingTreeView;
|
|
|
|
with GeneralPage do
|
|
for i := Low(PreviewEdits) to High(PreviewEdits) do
|
|
if PreviewEdits[i] <> nil then
|
|
EditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.WriteSettings(AOptions: TAbstractIDEOptions);
|
|
begin
|
|
with AOptions as TEditorOptions do
|
|
KeyMap.Assign(EditingKeyMap);
|
|
end;
|
|
|
|
class function TEditorKeymappingOptionsFrame.SupportedOptionsClass: TAbstractIDEOptionsClass;
|
|
begin
|
|
Result := TEditorOptions;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.FillKeyMappingTreeView;
|
|
var
|
|
i, j: Integer;
|
|
NewCategoryNode, NewKeyNode: TTreeNode;
|
|
CurCategory: TIDECommandCategory;
|
|
CurKeyRelation: TKeyCommandRelation;
|
|
ChildNodeIndex: Integer;
|
|
CategoryNodeIndex: Integer;
|
|
HasFilter: Boolean;
|
|
ItemCaption: String;
|
|
NameFilterUp: String;
|
|
begin
|
|
HasFilter:=(KeyMapNameFilter<>'');
|
|
NameFilterUp:=uppercase(KeyMapNameFilter);
|
|
with KeyMappingTreeView do
|
|
begin
|
|
BeginUpdate;
|
|
CategoryNodeIndex:=0;
|
|
for i := 0 to EditingKeyMap.CategoryCount - 1 do
|
|
begin
|
|
CurCategory := EditingKeyMap.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:=KeyMappingRelationToString(CurKeyRelation);
|
|
if (NameFilterUp<>'')
|
|
and (System.Pos(NameFilterUp,UpperCase(ItemCaption))=0) then
|
|
continue;
|
|
if (KeyMapKeyFilter.Key1<>VK_UNKNOWN)
|
|
and (CompareIDEShortCutKey1s(@KeyMapKeyFilter,@CurKeyRelation.ShortcutA)<>0)
|
|
and (CompareIDEShortCutKey1s(@KeyMapKeyFilter,@CurKeyRelation.ShortcutB)<>0)
|
|
then
|
|
continue;
|
|
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 := imgKeyItem;
|
|
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 begin
|
|
if HasFilter then
|
|
NewCategoryNode.Expanded:=true;
|
|
inc(CategoryNodeIndex);
|
|
end;
|
|
end;
|
|
while Items.TopLvlCount > CategoryNodeIndex do
|
|
Items.TopLvlItems[Items.TopLvlCount - 1].Delete;
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.KeyMappingRelationToString(Index: Integer): String;
|
|
begin
|
|
Result := KeyMappingRelationToString(EditingKeyMap.Relations[Index]);
|
|
end;
|
|
|
|
function TEditorKeymappingOptionsFrame.KeyMappingRelationToString(
|
|
KeyRelation: TKeyCommandRelation): String;
|
|
const
|
|
MaxLength = 60;
|
|
|
|
function AddBrakets(S: String): String;
|
|
begin
|
|
Result := '[' + S + ']';
|
|
end;
|
|
|
|
begin
|
|
with KeyRelation do
|
|
begin
|
|
Result := LocalizedName;
|
|
if UTF8Length(Result)>MaxLength then
|
|
Result := UTF8Copy(LocalizedName, 1, MaxLength)+'...';
|
|
if Result <> '' then
|
|
Result := Result + ' ';
|
|
if (ShortcutA.Key1 = VK_UNKNOWN) and (ShortcutB.Key1 = VK_UNKNOWN) then
|
|
Result := Result{ + lisNone2 }
|
|
else
|
|
if (ShortcutA.Key1 = VK_UNKNOWN) then
|
|
Result := Result + AddBrakets(KeyAndShiftStateToEditorKeyString(ShortcutB))
|
|
else
|
|
if (ShortcutB.Key1 = VK_UNKNOWN) then
|
|
Result := Result + AddBrakets(KeyAndShiftStateToEditorKeyString(ShortcutA))
|
|
else
|
|
Result := Result + AddBrakets(KeyAndShiftStateToEditorKeyString(ShortcutA))
|
|
+ ' '+lisOr+' ' +
|
|
AddBrakets(KeyAndShiftStateToEditorKeyString(ShortcutB));
|
|
end;
|
|
end;
|
|
|
|
procedure TEditorKeymappingOptionsFrame.UpdateKeyFilterButton;
|
|
begin
|
|
if IDEShortCutEmpty(KeyMapKeyFilter) then
|
|
KeyMappingFindKeyButton.Caption:=lisFindKeyCombination
|
|
else
|
|
KeyMappingFindKeyButton.Caption:=
|
|
Format(lisFilter3, [KeyAndShiftStateToEditorKeyString(KeyMapKeyFilter)]);
|
|
end;
|
|
|
|
constructor TEditorKeymappingOptionsFrame.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
EditingKeyMap := TKeyCommandRelationList.Create;
|
|
KeyMappingClearButton.Enabled:=false;
|
|
end;
|
|
|
|
destructor TEditorKeymappingOptionsFrame.Destroy;
|
|
begin
|
|
EditingKeyMap.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
initialization
|
|
RegisterIDEOptionsEditor(GroupEditor, TEditorKeymappingOptionsFrame, EdtOptionsKeys);
|
|
end.
|
|
|