mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 19:58:02 +02:00
413 lines
13 KiB
ObjectPascal
413 lines
13 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. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
TShortCutGrabBox - a control to edit a shortcut
|
|
TShortCutDialog - a dialog to edit ide shortcuts
|
|
}
|
|
unit KeyMapShortCutDlg;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
// LCL
|
|
LCLType, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, Buttons, ButtonPanel,
|
|
// LazUtils
|
|
LazLoggerBase,
|
|
// IdeIntf
|
|
PropEdits, IDECommands, IDEWindowIntf, IDEDialogs,
|
|
// IDE
|
|
KeyMapping, LazarusIDEStrConsts;
|
|
|
|
type
|
|
|
|
{ TShortCutDialog }
|
|
|
|
TShortCutDialog = class(TForm)
|
|
BtnPanel: TButtonPanel;
|
|
PrimaryGroupBox: TGroupBox;
|
|
SecondaryGroupBox: TGroupBox;
|
|
procedure CancelButtonClick(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormShow(Sender: TObject);
|
|
procedure OkButtonClick(Sender: TObject);
|
|
private
|
|
FKeyCommandRelationList: TKeyCommandRelationList;
|
|
FPrimaryKey1Box: TShortCutGrabBox;
|
|
FPrimaryKey2Box: TShortCutGrabBox;
|
|
FRelationIndex: integer;
|
|
FSecondaryKey1Box: TShortCutGrabBox;
|
|
FSecondaryKey2Box: TShortCutGrabBox;
|
|
FShowSecondary: boolean;
|
|
FShowSequence: boolean;
|
|
function GetPrimaryShortCut: TIDEShortCut;
|
|
function GetSecondaryShortCut: TIDEShortCut;
|
|
procedure SetPrimaryShortCut(const AValue: TIDEShortCut);
|
|
procedure SetSecondaryShortCut(const AValue: TIDEShortCut);
|
|
procedure SetShowSecondary(const AValue: boolean);
|
|
procedure SetShowSequence(const AValue: boolean);
|
|
function ResolveConflicts(Key: TIDEShortCut; Scope: TIDECommandScope): TModalResult;
|
|
procedure UpdateCaptions;
|
|
public
|
|
procedure ClearKeys;
|
|
procedure SetRelation(AKeyCommandRelationList: TKeyCommandRelationList;
|
|
Index: integer);
|
|
property KeyCommandRelationList: TKeyCommandRelationList
|
|
read FKeyCommandRelationList write FKeyCommandRelationList;
|
|
property RelationIndex: integer read FRelationIndex write FRelationIndex;
|
|
property ShowSecondary: boolean read FShowSecondary write SetShowSecondary;
|
|
property ShowSequence: boolean read FShowSequence write SetShowSequence;
|
|
property PrimaryKey1Box: TShortCutGrabBox read FPrimaryKey1Box;
|
|
property PrimaryKey2Box: TShortCutGrabBox read FPrimaryKey2Box;
|
|
property SecondaryKey1Box: TShortCutGrabBox read FSecondaryKey1Box;
|
|
property SecondaryKey2Box: TShortCutGrabBox read FSecondaryKey2Box;
|
|
property PrimaryShortCut: TIDEShortCut read GetPrimaryShortCut write SetPrimaryShortCut;
|
|
property SecondaryShortCut: TIDEShortCut read GetSecondaryShortCut write SetSecondaryShortCut;
|
|
end;
|
|
|
|
function ShowKeyMappingEditForm(Index: integer;
|
|
AKeyCommandRelationList: TKeyCommandRelationList): TModalResult;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
function ShowKeyMappingEditForm(Index: integer;
|
|
AKeyCommandRelationList: TKeyCommandRelationList): TModalResult;
|
|
var
|
|
ShortCutDialog: TShortCutDialog;
|
|
begin
|
|
ShortCutDialog:=TShortCutDialog.Create(nil);
|
|
try
|
|
ShortCutDialog.ShowSecondary:=true;
|
|
ShortCutDialog.ShowSequence:=true;
|
|
ShortCutDialog.SetRelation(AKeyCommandRelationList,Index);
|
|
Result:=ShortCutDialog.ShowModal;
|
|
finally
|
|
ShortCutDialog.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TShortCutDialog }
|
|
|
|
procedure TShortCutDialog.FormCreate(Sender: TObject);
|
|
begin
|
|
Caption := srkmEditForCmd;
|
|
BtnPanel.OKButton.OnClick := @OkButtonClick;
|
|
BtnPanel.CancelButton.OnClick := @CancelButtonClick;
|
|
|
|
IDEDialogLayoutList.ApplyLayout(Self, 480, 480);
|
|
|
|
FShowSecondary:=true;
|
|
FShowSequence:=true;
|
|
|
|
FPrimaryKey1Box:=TShortCutGrabBox.Create(Self);
|
|
with FPrimaryKey1Box do begin
|
|
Name:='FPrimaryKey1Box';
|
|
Align:=alClient;
|
|
AutoSize:=true;
|
|
BorderSpacing.Around:=6;
|
|
Parent:=PrimaryGroupBox;
|
|
end;
|
|
FPrimaryKey2Box:=TShortCutGrabBox.Create(Self);
|
|
with FPrimaryKey2Box do begin
|
|
Name:='FPrimaryKey2Box';
|
|
Align:=alBottom;
|
|
AutoSize:=true;
|
|
BorderSpacing.Around:=6;
|
|
Parent:=PrimaryGroupBox;
|
|
end;
|
|
PrimaryGroupBox.AutoSize:=true;
|
|
|
|
FSecondaryKey1Box:=TShortCutGrabBox.Create(Self);
|
|
with FSecondaryKey1Box do begin
|
|
Name:='FSecondaryKey1Box';
|
|
Align:=alClient;
|
|
AutoSize:=true;
|
|
BorderSpacing.Around:=6;
|
|
Parent:=SecondaryGroupBox;
|
|
end;
|
|
FSecondaryKey2Box:=TShortCutGrabBox.Create(Self);
|
|
with FSecondaryKey2Box do begin
|
|
Name:='FSecondaryKey2Box';
|
|
Align:=alBottom;
|
|
AutoSize:=true;
|
|
BorderSpacing.Around:=6;
|
|
Parent:=SecondaryGroupBox;
|
|
end;
|
|
SecondaryGroupBox.AutoSize:=true;
|
|
|
|
UpdateCaptions;
|
|
ClearKeys;
|
|
end;
|
|
|
|
procedure TShortCutDialog.FormShow(Sender: TObject);
|
|
begin
|
|
FPrimaryKey1Box.GrabButton.SetFocus;
|
|
end;
|
|
|
|
procedure TShortCutDialog.CancelButtonClick(Sender: TObject);
|
|
begin
|
|
IDEDialogLayoutList.SaveLayout(Self);
|
|
end;
|
|
|
|
procedure TShortCutDialog.OkButtonClick(Sender: TObject);
|
|
var
|
|
NewKeyA: TIDEShortCut;
|
|
NewKeyB: TIDEShortCut;
|
|
CurRelation: TKeyCommandRelation;
|
|
begin
|
|
IDEDialogLayoutList.SaveLayout(Self);
|
|
|
|
if KeyCommandRelationList=nil then begin
|
|
ModalResult:=mrOk;
|
|
exit;
|
|
end;
|
|
|
|
// set defaults
|
|
NewKeyA:=PrimaryShortCut;
|
|
NewKeyB:=SecondaryShortCut;
|
|
|
|
//debugln('TShortCutDialog.OkButtonClick A ShortcutA=',KeyAndShiftStateToEditorKeyString(NewKeyA),' ShortcutB=',KeyAndShiftStateToEditorKeyString(NewKeyB));
|
|
|
|
// get old relation
|
|
CurRelation:=KeyCommandRelationList.Relations[RelationIndex];
|
|
|
|
case ResolveConflicts(NewKeyA,CurRelation.Category.Scope) of
|
|
mrCancel: begin
|
|
debugln('TShortCutDialog.OkButtonClick ResolveConflicts failed for key1');
|
|
exit;
|
|
end;
|
|
mrRetry: begin
|
|
ModalResult:=mrNone;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
//debugln('TShortCutDialog.OkButtonClick B ShortcutA=',KeyAndShiftStateToEditorKeyString(NewKeyA),' ShortcutB=',KeyAndShiftStateToEditorKeyString(NewKeyB));
|
|
|
|
if (NewKeyA.Key1=NewKeyB.Key1) and (NewKeyA.Shift1=NewKeyB.Shift1) and
|
|
(NewKeyA.Key2=NewKeyB.Key2) and (NewKeyA.Shift2=NewKeyB.Shift2) then
|
|
begin
|
|
NewKeyB.Key1:=VK_UNKNOWN;
|
|
NewKeyB.Shift1:=[];
|
|
NewKeyB.Key2:=VK_UNKNOWN;
|
|
NewKeyB.Shift2:=[];
|
|
end
|
|
else
|
|
case ResolveConflicts(NewKeyB,CurRelation.Category.Scope) of
|
|
mrCancel: begin
|
|
debugln('TShortCutDialog.OkButtonClick ResolveConflicts failed for key1');
|
|
exit;
|
|
end;
|
|
mrRetry: begin
|
|
ModalResult:=mrNone;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
//debugln('TShortCutDialog.OkButtonClick C ShortcutA=',KeyAndShiftStateToEditorKeyString(NewKeyA),' ShortcutB=',KeyAndShiftStateToEditorKeyString(NewKeyB));
|
|
|
|
if NewKeyA.Key1=VK_UNKNOWN then
|
|
begin
|
|
NewKeyA:=NewKeyB;
|
|
NewKeyB.Key1:=VK_UNKNOWN;
|
|
NewKeyB.Shift1:=[];
|
|
NewKeyB.Key2:=VK_UNKNOWN;
|
|
NewKeyB.Shift2:=[];
|
|
end;
|
|
|
|
//debugln('TShortCutDialog.OkButtonClick D ShortcutA=',KeyAndShiftStateToEditorKeyString(NewKeyA),' ShortcutB=',KeyAndShiftStateToEditorKeyString(NewKeyB));
|
|
|
|
CurRelation.ShortcutA:=NewKeyA;
|
|
CurRelation.ShortcutB:=NewKeyB;
|
|
|
|
//debugln('TShortCutDialog.OkButtonClick B ShortcutA=',KeyAndShiftStateToEditorKeyString(NewKeyA),' ShortcutB=',KeyAndShiftStateToEditorKeyString(NewKeyB));
|
|
ModalResult:=mrOk;
|
|
end;
|
|
|
|
procedure TShortCutDialog.SetShowSecondary(const AValue: boolean);
|
|
begin
|
|
if FShowSecondary=AValue then exit;
|
|
FShowSecondary:=AValue;
|
|
SecondaryGroupBox.Visible:=FShowSecondary;
|
|
end;
|
|
|
|
procedure TShortCutDialog.SetShowSequence(const AValue: boolean);
|
|
begin
|
|
if FShowSequence=AValue then exit;
|
|
FShowSequence:=AValue;
|
|
FPrimaryKey2Box.Visible:=FShowSequence;
|
|
FSecondaryKey2Box.Visible:=FShowSequence;
|
|
// With a single key GrabBox focus OK button after keypress.
|
|
if not (FShowSecondary or FShowSequence) then
|
|
FPrimaryKey1Box.MainOkButton:=BtnPanel.OKButton;
|
|
UpdateCaptions;
|
|
end;
|
|
|
|
procedure TShortCutDialog.SetPrimaryShortCut(const AValue: TIDEShortCut);
|
|
var
|
|
APrimaryShortCut: TIDEShortCut;
|
|
begin
|
|
APrimaryShortCut:=GetPrimaryShortCut;
|
|
if CompareIDEShortCuts(@APrimaryShortCut,@AValue)=0 then exit;
|
|
PrimaryKey1Box.Key:=AValue.Key1;
|
|
PrimaryKey1Box.ShiftState:=AValue.Shift1;
|
|
PrimaryKey2Box.Key:=AValue.Key2;
|
|
PrimaryKey2Box.ShiftState:=AValue.Shift2;
|
|
end;
|
|
|
|
function TShortCutDialog.GetPrimaryShortCut: TIDEShortCut;
|
|
begin
|
|
Result.Key1:=PrimaryKey1Box.Key;
|
|
Result.Shift1:=PrimaryKey1Box.ShiftState;
|
|
Result.Key2:=PrimaryKey2Box.Key;
|
|
Result.Shift2:=PrimaryKey2Box.ShiftState;
|
|
end;
|
|
|
|
function TShortCutDialog.GetSecondaryShortCut: TIDEShortCut;
|
|
begin
|
|
Result.Key1:=SecondaryKey1Box.Key;
|
|
Result.Shift1:=SecondaryKey1Box.ShiftState;
|
|
Result.Key2:=SecondaryKey2Box.Key;
|
|
Result.Shift2:=SecondaryKey2Box.ShiftState;
|
|
end;
|
|
|
|
procedure TShortCutDialog.SetSecondaryShortCut(const AValue: TIDEShortCut);
|
|
var
|
|
ASecondaryShortCut: TIDEShortCut;
|
|
begin
|
|
ASecondaryShortCut:=SecondaryShortCut;
|
|
if CompareIDEShortCuts(@ASecondaryShortCut,@AValue)=0 then exit;
|
|
SecondaryKey1Box.Key:=AValue.Key1;
|
|
SecondaryKey1Box.ShiftState:=AValue.Shift1;
|
|
SecondaryKey2Box.Key:=AValue.Key2;
|
|
SecondaryKey2Box.ShiftState:=AValue.Shift2;
|
|
end;
|
|
|
|
function TShortCutDialog.ResolveConflicts(Key: TIDEShortCut;
|
|
Scope: TIDECommandScope): TModalResult;
|
|
type
|
|
TConflictType = (ctNone,ctConflictKeyA,ctConflictKeyB);
|
|
var
|
|
ConflictRelation: TKeyCommandRelation;
|
|
ConflictName: String;
|
|
CurRelation: TKeyCommandRelation;
|
|
CurName: String;
|
|
j: integer;
|
|
conflictType: TConflictType;
|
|
begin
|
|
Result:=mrOK;
|
|
// search for conflict
|
|
CurRelation:=KeyCommandRelationList.Relations[RelationIndex];
|
|
if Key.Key1=VK_UNKNOWN then
|
|
exit;
|
|
//Try to find an IDE command that conflicts
|
|
for j:=0 to KeyCommandRelationList.RelationCount-1 do begin
|
|
conflictType:=ctNone;
|
|
ConflictRelation:=KeyCommandRelationList.Relations[j];
|
|
with ConflictRelation do
|
|
begin
|
|
if (j=RelationIndex) then continue;
|
|
|
|
if not Category.ScopeIntersects(Scope) then continue;
|
|
|
|
if ((Key.Key1=ShortcutA.Key1) and (Key.Shift1=ShortcutA.Shift1))
|
|
and (((Key.Key2=ShortcutA.Key2) and (Key.Shift2=ShortcutA.Shift2))
|
|
or (Key.Key2=VK_UNKNOWN) or (ShortcutA.Key2=VK_UNKNOWN))
|
|
then begin
|
|
conflictType:=ctConflictKeyA; // ShortcutA bites
|
|
end
|
|
else if ((Key.Key1=ShortcutB.Key1) and (Key.Shift1=ShortcutB.Shift1))
|
|
and (((Key.Key2=ShortcutB.Key2) and (Key.Shift2=ShortcutB.Shift2))
|
|
or (Key.Key2=VK_UNKNOWN) or (ShortcutB.Key2=VK_UNKNOWN))
|
|
then begin
|
|
conflictType:=ctConflictKeyB; // ShortcutB bites
|
|
end;
|
|
end;
|
|
if (conflictType<>ctNone) then begin
|
|
CurName:=CurRelation.GetCategoryAndName;
|
|
ConflictName:=ConflictRelation.GetCategoryAndName;
|
|
if conflictType=ctConflictKeyA then
|
|
ConflictName:=ConflictName
|
|
+' ('+KeyAndShiftStateToEditorKeyString(ConflictRelation.ShortcutA)+')'
|
|
else
|
|
ConflictName:=ConflictName
|
|
+' ('+KeyAndShiftStateToEditorKeyString(ConflictRelation.ShortcutB)+')';
|
|
case IDEMessageDialog(lisPEConflictFound,
|
|
Format(lisTheKeyIsAlreadyAssignedToRemoveTheOldAssignmentAnd,
|
|
[KeyAndShiftStateToEditorKeyString(Key),
|
|
LineEnding, ConflictName, LineEnding, LineEnding, CurName]),
|
|
mtConfirmation, [mbYes, mbNo, mbCancel])
|
|
of
|
|
mrYes: Result:=mrOK;
|
|
mrCancel: Result:=mrCancel;
|
|
else Result:=mrRetry;
|
|
end;
|
|
if Result=mrOK then begin
|
|
if (conflictType=ctConflictKeyA) then
|
|
ConflictRelation.ShortcutA:=ConflictRelation.ShortcutB;
|
|
ConflictRelation.ClearShortcutB;
|
|
end
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TShortCutDialog.UpdateCaptions;
|
|
begin
|
|
if ShowSequence then begin
|
|
PrimaryGroupBox.Caption:=lisKeyOr2KeySequence;
|
|
SecondaryGroupBox.Caption:=lisAlternativeKeyOr2KeySequence;
|
|
end else begin
|
|
PrimaryGroupBox.Caption:=lisEdtExtToolKey;
|
|
SecondaryGroupBox.Caption:=lisAlternativeKey;
|
|
end;
|
|
end;
|
|
|
|
procedure TShortCutDialog.ClearKeys;
|
|
begin
|
|
PrimaryShortCut:=CleanIDEShortCut;
|
|
SecondaryShortCut:=CleanIDEShortCut;
|
|
end;
|
|
|
|
procedure TShortCutDialog.SetRelation(
|
|
AKeyCommandRelationList: TKeyCommandRelationList; Index: integer);
|
|
var
|
|
CurRelation: TKeyCommandRelation;
|
|
begin
|
|
KeyCommandRelationList:=AKeyCommandRelationList;
|
|
RelationIndex:=Index;
|
|
CurRelation:=AKeyCommandRelationList.Relations[RelationIndex];
|
|
PrimaryShortCut:=CurRelation.ShortcutA;
|
|
SecondaryShortCut:=CurRelation.ShortcutB;
|
|
Caption:=srkmCommand+' "'+CurRelation.LocalizedName+'"';
|
|
end;
|
|
|
|
end.
|
|
|