29
29
Controls, Dialogs, LCLType, LCLProc, Menus, Buttons, Clipbrd, EditorOptions,
30
30
LazarusIDEStrConsts, IDEOptionsIntf, IDEImagesIntf, editor_general_options,
31
31
KeymapSchemeDlg, KeyMapping, IDECommands, KeyMapShortCutDlg, SrcEditorIntf,
36
36
{ TEditorKeymappingOptionsFrame }
38
38
TEditorKeymappingOptionsFrame = class(TAbstractIDEOptionsEditor)
39
40
ChooseSchemeButton: TBitBtn;
40
41
ClearButton: TBitBtn;
41
42
EditButton: TBitBtn;
42
ConsistencyCheckButton: TBitBtn;
43
43
FilterEdit: TTreeFilterEdit;
44
44
FindKeyButton: TBitBtn;
45
45
CommandLabel: TLabel;
46
ConflictsTreeView: TTreeView;
47
KeyMapSplitter: TSplitter;
48
KeyMapTreePanel: TPanel;
46
49
SchemeLabel: TLabel;
47
50
ResetKeyFilterBtn: TSpeedButton;
48
51
TreeView: TTreeView;
50
53
ClearMenuItem: TMenuItem;
51
54
PopupMenu1: TPopupMenu;
52
55
procedure ClearMenuItemClick(Sender: TObject);
56
procedure ConflictsTreeViewMouseDown(Sender: TObject; Button: TMouseButton;
57
Shift: TShiftState; X, Y: Integer);
53
58
procedure EditButtonClick(Sender: TObject);
54
59
procedure EditMenuItemClick(Sender: TObject);
55
60
procedure ChooseSchemeButtonClick(Sender: TObject);
56
61
procedure ClearButtonClick(Sender: TObject);
57
procedure ConsistencyCheckButtonClick(Sender: TObject);
58
62
function FilterEditFilterItem(Item: TObject; out Done: Boolean): Boolean;
59
63
procedure FilterEditKeyPress(Sender: TObject; var Key: char);
60
64
procedure FindKeyButtonClick(Sender: TObject);
65
procedure OnIdle(Sender: TObject; var Done: Boolean);
61
66
procedure ResetKeyFilterBtnClick(Sender: TObject);
62
67
procedure TreeViewDblClick(Sender: TObject);
63
68
procedure TreeViewKeyPress(Sender: TObject; var Key: char);
65
70
procedure PopupMenu1Popup(Sender: TObject);
67
72
FDialog: TAbstractOptionsEditorDialog;
68
EditingKeyMap: TKeyCommandRelationList;
73
FEditingKeyMap: TKeyCommandRelationList;
74
FIdleConnected: boolean;
69
75
KeyMapKeyFilter: TIDEShortCut;
70
76
fModified: Boolean;
71
77
function GeneralPage: TEditorGeneralOptionsFrame; inline;
72
78
procedure FillKeyMappingTreeView;
73
79
procedure EditCommandMapping(ANode: TTreeNode);
80
procedure EditConflict(ANode: TTreeNode);
81
procedure EditCommandRelation(ARelation: TKeyCommandRelation);
74
82
procedure ClearCommandMapping(ANode: TTreeNode);
75
function KeyMappingRelationToString(Index: Integer): String;
76
function KeyMappingRelationToString(KeyRelation: TKeyCommandRelation): String;
83
procedure ClearConflict(ANode: TTreeNode);
84
procedure ClearCommandRelation(ARelation: TKeyCommandRelation);
85
function KeyMappingRelationToCaption(Index: Integer): String;
86
function KeyMappingRelationToCaption(KeyRelation: TKeyCommandRelation): String;
87
function KeyShortCutToCaption(const aKey: TKeyCommandRelation;
88
const aShortCut: TIDEShortCut): string;
89
function CaptionToKeyMappingRelation(aCaption: string): TKeyCommandRelation;
90
procedure SetIdleConnected(AValue: boolean);
77
91
procedure UpdateKeyFilterButton;
78
92
procedure UpdateSchemeLabel;
182
202
NewScheme := EditorOpts.KeyMappingScheme;
183
203
if ShowChooseKeySchemeDialog(NewScheme) = mrOk then begin
184
204
EditorOpts.KeyMappingScheme := NewScheme;
185
EditingKeyMap.LoadScheme(NewScheme);
205
FEditingKeyMap.LoadScheme(NewScheme);
186
206
FillKeyMappingTreeView;
187
207
fModified:=False;
188
208
UpdateSchemeLabel;
192
213
procedure TEditorKeymappingOptionsFrame.EditButtonClick(Sender: TObject);
194
EditCommandMapping(TreeView.Selected);
215
EditCommandMapping(TreeView.Selected)
197
218
procedure TEditorKeymappingOptionsFrame.ClearButtonClick(Sender: TObject);
199
ClearCommandMapping(TreeView.Selected);
202
procedure TEditorKeymappingOptionsFrame.ConsistencyCheckButtonClick(Sender: TObject);
204
Protocol: TStringList;
205
ErrorCount, Index1, Index2: Integer;
206
ACaption, AText: String;
207
KeyMapErrorsForm: TKeyMapErrorsForm;
209
Protocol := TStringList.Create;
211
ErrorCount := FindKeymapConflicts(EditingKeyMap, Protocol, Index1, Index2);
212
if ErrorCount > 0 then
214
KeyMapErrorsForm := TKeyMapErrorsForm.Create(nil);
216
KeyMapErrorsForm.ListBox.Items.Assign(Protocol);
217
KeyMapErrorsForm.ShowModal;
219
KeyMapErrorsForm.Free;
224
ACaption := dlgReport;
226
MessageDlg(ACaption, AText, mtInformation, [mbOk], 0);
220
ClearCommandMapping(TreeView.Selected)
233
223
function TEditorKeymappingOptionsFrame.FilterEditFilterItem(Item: TObject; out Done: Boolean): Boolean;
256
246
procedure TEditorKeymappingOptionsFrame.FindKeyButtonClick(Sender: TObject);
258
KeyFilter: TIDEShortCut;
248
ShortCutDialog: TShortCutDialog;
260
if ShowKeyMappingGrabForm(KeyFilter) = mrOK then begin
261
KeyMapKeyFilter := KeyFilter;
262
UpdateKeyFilterButton;
263
FilterEdit.Filter:=''; // Allow only one of the filters to be active.
264
FilterEdit.InvalidateFilter;
250
ShortCutDialog := TShortCutDialog.Create(nil);
252
ShortCutDialog.ShowSecondary:=False;
253
ShortCutDialog.ShowSequence:=False;
254
ShortCutDialog.Caption:=lisChooseAKey;
255
ShortCutDialog.PrimaryShortCut := KeyMapKeyFilter;
256
if ShortCutDialog.ShowModal = mrOK then begin
257
KeyMapKeyFilter := ShortCutDialog.PrimaryShortCut;
258
UpdateKeyFilterButton;
259
FilterEdit.Filter:=''; // Allow only one of the filters to be active.
260
FilterEdit.InvalidateFilter;
267
procedure TEditorKeymappingOptionsFrame.OnIdle(Sender: TObject;
270
IdleConnected:=false;
268
274
procedure TEditorKeymappingOptionsFrame.ResetKeyFilterBtnClick(Sender: TObject);
270
276
KeyMapKeyFilter.Key1 := VK_UNKNOWN;
303
309
procedure TEditorKeymappingOptionsFrame.PopupMenu1Popup(Sender: TObject);
305
311
ANode: TTreeNode;
307
ANode := TreeView.Selected;
308
EditMenuItem.Enabled:=
309
(ANode<>nil) and (ANode.Data<>nil) and (TObject(ANode.Data) is TKeyCommandRelation);
314
pop := Sender as TPopupMenu;
315
if pop.PopupComponent = TreeView then begin
316
ANode := TreeView.Selected;
317
EditMenuItem.Enabled:=
318
(ANode<>nil) and (ANode.Data<>nil) and (TObject(ANode.Data) is TKeyCommandRelation);
319
end else if pop.PopupComponent=ConflictsTreeView then begin
320
ANode:=ConflictsTreeView.Selected;
321
EditMenuItem.Enabled := (ANode<>nil) and (CaptionToKeyMappingRelation(ANode.Text)<>nil);
323
EditMenuItem.Enabled := False;
310
324
ClearMenuItem.Enabled := EditMenuItem.Enabled;
313
327
procedure TEditorKeymappingOptionsFrame.EditMenuItemClick(Sender: TObject);
315
EditCommandMapping(TreeView.Selected);
329
if PopupMenu1.PopupComponent=TreeView then
330
EditCommandMapping(TreeView.Selected)
332
EditConflict(ConflictsTreeView.Selected);
318
335
procedure TEditorKeymappingOptionsFrame.ClearMenuItemClick(Sender: TObject);
320
ClearCommandMapping(TreeView.Selected);
337
if PopupMenu1.PopupComponent=TreeView then
338
ClearCommandMapping(TreeView.Selected)
340
ClearConflict(ConflictsTreeView.Selected);
343
procedure TEditorKeymappingOptionsFrame.ConflictsTreeViewMouseDown(
344
Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
348
if (Button=mbLeft) and (ssDouble in Shift) then begin
349
Node:=ConflictsTreeView.GetNodeAt(X,Y);
323
354
function TEditorKeymappingOptionsFrame.GeneralPage: TEditorGeneralOptionsFrame; inline;
514
function TEditorKeymappingOptionsFrame.KeyShortCutToCaption(
515
const aKey: TKeyCommandRelation; const aShortCut: TIDEShortCut): string;
517
Result:=aKey.Category.Description+'/'
518
+EditorCommandToDescriptionString(aKey.Command)
519
+'->'+KeyAndShiftStateToEditorKeyString(aShortCut);
522
function TEditorKeymappingOptionsFrame.CaptionToKeyMappingRelation(
523
aCaption: string): TKeyCommandRelation;
526
aCategory: TIDECommandCategory;
529
aKey: TKeyCommandRelation;
532
for c:=0 to FEditingKeyMap.CategoryCount-1 do begin
533
aCategory:=FEditingKeyMap.Categories[c];
534
CatStr:=aCategory.Description+'/';
535
if LeftStr(aCaption,length(CatStr))<>CatStr then continue;
536
for i:=0 to aCategory.Count-1 do begin
537
aKey:=TObject(aCategory[i]) as TKeyCommandRelation;
538
s:=CatStr+EditorCommandToDescriptionString(aKey.Command);
539
if LeftStr(aCaption,length(s))<>s then continue;
547
procedure TEditorKeymappingOptionsFrame.SetIdleConnected(AValue: boolean);
549
if FIdleConnected=AValue then Exit;
550
FIdleConnected:=AValue;
551
if IdleConnected then
552
Application.AddOnIdleHandler(@OnIdle)
554
Application.RemoveOnIdleHandler(@OnIdle);
483
557
procedure TEditorKeymappingOptionsFrame.UpdateKeyFilterButton;
485
559
if IDEShortCutEmpty(KeyMapKeyFilter) then
486
FindKeyButton.Caption:=lisFindKeyCombination
560
FindKeyButton.Caption := lisFindKeyCombination
488
562
FindKeyButton.Caption:=
489
563
Format(lisFilter3, [KeyAndShiftStateToEditorKeyString(KeyMapKeyFilter)]);
500
574
SchemeLabel.Caption:=s;
577
procedure TEditorKeymappingOptionsFrame.SelectByIdeCommand(ACmd: word);
581
Node := TreeView.Items.GetFirstNode;
582
while node <> nil do begin
583
if (node.Data <> nil) and (TObject(Node.Data) is TKeyCommandRelation) and
584
(TKeyCommandRelation(Node.Data).Command = ACmd)
587
node := Node.GetNext;
589
if node <> nil then begin
591
Node.Selected := True;
595
procedure TEditorKeymappingOptionsFrame.UpdateTree;
597
FillKeyMappingTreeView;
600
procedure TEditorKeymappingOptionsFrame.UpdateConflictTree;
602
ConflictCount: integer;
603
Key1: TKeyCommandRelation;
604
Key2: TKeyCommandRelation;
606
procedure Check(const ShortCut1, ShortCut2: TIDEShortCut);
607
// check if ShortCut1 hides ShortCut2
609
ConflictNode: TTreeNode;
612
if (ShortCut1.Key1=VK_UNKNOWN)
613
or (ShortCut1.Key1<>ShortCut2.Key1)
614
or (ShortCut1.Shift1<>ShortCut2.Shift1) then
615
exit; // first keys differ
617
if (ShortCut1.Key2=VK_UNKNOWN) or (ShortCut2.Key2=VK_UNKNOWN)
618
or ((ShortCut1.Key2=ShortCut2.Key2) and (ShortCut1.Shift2=ShortCut2.Shift2))
620
// conflict found, add node with a sub node for each key
622
ConflictNode:=ConflictsTreeView.Items.Add(nil,srkmConflic+IntToStr(ConflictCount));
623
ConflictNode.ImageIndex:=imgKeyItem;
624
ConflictNode.StateIndex:=imgKeyItem;
625
KeyNode:=ConflictsTreeView.Items.AddChild(ConflictNode,
626
KeyShortCutToCaption(Key1,ShortCut1));
627
KeyNode.ImageIndex := imgKeyItem;
628
KeyNode.SelectedIndex := imgKeyItem;
629
KeyNode:=ConflictsTreeView.Items.AddChild(ConflictNode,
630
KeyShortCutToCaption(Key2,ShortCut2));
631
KeyNode.ImageIndex := imgKeyItem;
632
KeyNode.SelectedIndex := imgKeyItem;
633
ConflictNode.Expanded:=true;
641
ConflictsTreeView.BeginUpdate;
642
ConflictsTreeView.Items.Clear;
645
for i:=0 to FEditingKeyMap.Count-1 do begin
646
Key1:=FEditingKeyMap[i];
647
for j:=i+1 to FEditingKeyMap.Count-1 do begin
648
Key2:=FEditingKeyMap[j];
649
if (not Key1.Category.ScopeIntersects(Key2.Category.Scope)) then
651
Check(Key1.ShortcutA,Key2.ShortcutA);
652
Check(Key1.ShortcutA,Key2.ShortcutB);
653
Check(Key1.ShortcutB,Key2.ShortcutA);
654
Check(Key1.ShortcutB,Key2.ShortcutB);
658
if ConflictsTreeView.Items.Count=0 then
659
ConflictsTreeView.Items.Add(nil,'There are no conflicting keys.');
661
ConflictsTreeView.EndUpdate;
503
664
procedure TEditorKeymappingOptionsFrame.EditCommandMapping(ANode: TTreeNode);
666
if ANode=nil then exit;
667
EditCommandRelation(TKeyCommandRelation(ANode.Data));
670
procedure TEditorKeymappingOptionsFrame.EditConflict(ANode: TTreeNode);
506
672
ARelation: TKeyCommandRelation;
508
ARelation := TKeyCommandRelation(ANode.Data);
509
i := EditingKeyMap.IndexOf(ARelation);
510
if (i >= 0) and (ShowKeyMappingEditForm(i, EditingKeyMap) = mrOk) then
512
FillKeyMappingTreeView;
516
for i := Low(PreviewEdits) to High(PreviewEdits) do
517
if PreviewEdits[i] <> nil then
518
EditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
674
if ANode=nil then exit;
675
ARelation:=CaptionToKeyMappingRelation(ANode.Text);
676
EditCommandRelation(ARelation);
679
procedure TEditorKeymappingOptionsFrame.EditCommandRelation(
680
ARelation: TKeyCommandRelation);
684
if ARelation=nil then exit;
685
i := FEditingKeyMap.IndexOf(ARelation);
686
if (i < 0) or (ShowKeyMappingEditForm(i, FEditingKeyMap) <> mrOk) then exit;
687
FillKeyMappingTreeView;
691
for i := Low(PreviewEdits) to High(PreviewEdits) do
692
if PreviewEdits[i] <> nil then
693
FEditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
522
697
procedure TEditorKeymappingOptionsFrame.ClearCommandMapping(ANode: TTreeNode);
699
if ANode=nil then exit;
700
ClearCommandRelation(TKeyCommandRelation(ANode.Data));
703
procedure TEditorKeymappingOptionsFrame.ClearConflict(ANode: TTreeNode);
525
705
ARelation: TKeyCommandRelation;
527
ARelation := TKeyCommandRelation(ANode.Data);
528
i := EditingKeyMap.IndexOf(ARelation);
529
if (i >= 0) {and (ShowKeyMappingEditForm(i, EditingKeyMap) = mrOk)} then
531
ARelation.ShortcutA := IDEShortCut(VK_UNKNOWN, []);
532
ARelation.ShortcutB := IDEShortCut(VK_UNKNOWN, []);
533
FillKeyMappingTreeView;
537
for i := Low(PreviewEdits) to High(PreviewEdits) do
538
if PreviewEdits[i] <> nil then
539
EditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);
707
if ANode=nil then exit;
708
ARelation:=CaptionToKeyMappingRelation(ANode.Text);
709
ClearCommandRelation(ARelation);
712
procedure TEditorKeymappingOptionsFrame.ClearCommandRelation(
713
ARelation: TKeyCommandRelation);
717
if ARelation=nil then exit;
718
i := FEditingKeyMap.IndexOf(ARelation);
719
if (i < 0) then exit;
720
ARelation.ShortcutA := IDEShortCut(VK_UNKNOWN, []);
721
ARelation.ShortcutB := IDEShortCut(VK_UNKNOWN, []);
722
FillKeyMappingTreeView;
726
for i := Low(PreviewEdits) to High(PreviewEdits) do
727
if PreviewEdits[i] <> nil then
728
FEditingKeyMap.AssignTo(PreviewEdits[i].KeyStrokes, TSourceEditorWindowInterface);