diff --git a/components/idespotter/ReadMe.txt b/components/idespotter/ReadMe.txt
index c6dea5cf85..e204f6304c 100644
--- a/components/idespotter/ReadMe.txt
+++ b/components/idespotter/ReadMe.txt
@@ -11,4 +11,10 @@ If you type multiple words, all words must match.
The key combination can be configured in the key options, search for
spotter.
+You can set
+* the color of the matches,
+* color of shortcut key (and whether it should be shown at all)
+in the IDE options.
+
+Todo: Search recent files/packages
diff --git a/components/idespotter/frmspotter.lfm b/components/idespotter/frmspotter.lfm
index 0dd2f80c42..3fcb7ffd92 100644
--- a/components/idespotter/frmspotter.lfm
+++ b/components/idespotter/frmspotter.lfm
@@ -15,7 +15,7 @@ object SpotterForm: TSpotterForm
OnShow = FormShow
Position = poDefaultSizeOnly
ShowInTaskBar = stNever
- LCLVersion = '1.9.0.0'
+ LCLVersion = '2.1.0.0'
Visible = True
object ECommand: TEdit
Left = 0
@@ -37,12 +37,13 @@ object SpotterForm: TSpotterForm
Align = alClient
ClickOnSelChange = False
ItemHeight = 0
- OnClick = LBMatchesClickC
- OnEnter = LBMatchesEnter
+ OnClick = LBMatchesClick
+ OnDrawItem = LBMatchesDrawItem
OnKeyUp = LBMatchesKeyUp
ParentShowHint = False
ScrollWidth = 495
ShowHint = True
+ Style = lbOwnerDrawFixed
TabOrder = 1
TabStop = False
TopIndex = -1
diff --git a/components/idespotter/frmspotter.pas b/components/idespotter/frmspotter.pas
index 933935b808..049262db04 100644
--- a/components/idespotter/frmspotter.pas
+++ b/components/idespotter/frmspotter.pas
@@ -35,10 +35,32 @@ interface
uses
Classes, SysUtils, FileUtil, LazUTF8, Forms, Controls, Graphics, Dialogs,
- StdCtrls, IDECommands, LazIDEIntf;
+ StdCtrls, IDECommands, LazIDEIntf, Types;
type
+ { TSearchItem }
+ TMatchPos = Array of Integer;
+ TSearchItem = Class(TObject)
+ private
+ FKeyStroke: String;
+ FMatchLen: TMatchPos;
+ FMatchPos: TMatchPos;
+ FOwnsSource: Boolean;
+ FPrefix: String;
+ FSource: TObject;
+ procedure SetSource(AValue: TObject);
+ Public
+ Constructor Create(aSource : TObject;AOwnsSource : Boolean = False);
+ Destructor Destroy; override;
+ published
+ Property OwnsSource: Boolean read FOwnsSource Write FOwnsSource;
+ Property Source : TObject read FSource write FSource;
+ Property KeyStroke : String Read FKeyStroke Write FKeyStroke;
+ Property Prefix : String Read FPrefix Write FPrefix;
+ Property MatchPos : TMatchPos Read FMatchPos Write FMatchPos;
+ Property MatchLen : TMatchPos Read FMatchLen Write FMatchLen;
+ end;
{ TSpotterForm }
TSpotterForm = class(TForm)
@@ -50,36 +72,141 @@ type
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
- procedure LBMatchesClickC(Sender: TObject);
- procedure LBMatchesEnter(Sender: TObject);
+ procedure LBMatchesClick(Sender: TObject);
+ procedure LBMatchesDrawItem(Control: TWinControl; Index: Integer;
+ ARect: TRect; State: TOwnerDrawState);
procedure LBMatchesKeyUp(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState
);
private
+ FKeyStrokeColor: TColor;
+ FMatchColor: TColor;
FShowCategory: Boolean;
- FCommands: TStringList;
+ FSearchItems: TStringList;
FOrgCaption : String;
+ FShowShortCutKey: Boolean;
+ Procedure Initialize;
procedure ExecuteSelected;
Procedure FillCommands;
- procedure FilterCommandList(aCmd: String);
+ procedure FilterList(aSearchTerm: String);
function GetCommandCategoryString(Cmd: TIDECommand): String;
+ procedure RefreshCaption(aCount: Integer);
public
Property ShowCategory : Boolean Read FShowCategory Write FShowCategory;
+ Property ShowShortCutKey : Boolean Read FShowShortCutKey Write FShowShortCutKey;
+ property KeyStrokeColor : TColor Read FKeyStrokeColor Write FKeyStrokeColor;
+ property MatchColor : TColor Read FMatchColor Write FMatchColor;
end;
+Var
+ ShowCmdCategory : Boolean = True;
+ ShowShortCutKey : Boolean = True;
+ MatchColor : TColor = clRed;
+ KeyStrokeColor : TColor = clGreen;
+
+Procedure ShowSpotterForm;
+Procedure ApplySpotterOptions;
+Procedure SaveSpotterOptions;
+procedure LoadSpotterOptions;
+
+implementation
+
+Uses BaseIDEIntf, LazConfigStorage, StrUtils, LCLIntf, LCLType, LCLProc, srceditorintf;
+
+{$R *.lfm}
+
var
SpotterForm: TSpotterForm;
-implementation
-Uses StrUtils,LCLType, LCLProc;
+Const
+ IDESpotterOptsFile = 'idespotter.xml';
-{$R *.lfm}
+ KeyShowCategory = 'showcategory/value';
+ KeyShowShortCut = 'showShortCut/value';
+ KeyShortCutColor = 'ShortCutColor/value';
+ KeyMatchColor = 'MatchColor/value';
+
+procedure LoadSpotterOptions;
+var
+ Cfg: TConfigStorage;
+begin
+ Cfg:=GetIDEConfigStorage(IDESpotterOptsFile,true);
+ try
+ ShowCmdCategory:=Cfg.GetValue(KeyShowCategory,ShowCmdCategory);
+ ShowShortCutKey:=Cfg.GetValue(KeyShowShortCut,ShowShortCutKey);
+ KeyStrokeColor:=TColor(Cfg.GetValue(KeyShortCutColor,Ord(KeyStrokeColor)));
+ MatchColor:=TColor(Cfg.GetValue(KeyMatchColor,Ord(MatchColor)));
+ ApplySpotterOptions;
+ finally
+ Cfg.Free;
+ end;
+end;
+
+
+procedure SaveSpotterOptions;
+var
+ Cfg: TConfigStorage;
+begin
+ Cfg:=GetIDEConfigStorage(IDESpotterOptsFile,false);
+ try
+ Cfg.SetValue(KeyShowCategory,ShowCmdCategory);
+ Cfg.SetValue(KeyShowShortCut,ShowShortCutKey);
+ Cfg.SetValue(KeyShortCutColor,Ord(KeyStrokeColor));
+ Cfg.SetValue(KeyMatchColor,Ord(MatchColor));
+ finally
+ Cfg.Free;
+ end;
+end;
+
+Procedure ApplySpotterOptions;
+
+begin
+ if Assigned(SpotterForm) then
+ begin
+ SpotterForm.ShowCategory:=ShowCmdCategory;
+ SpotterForm.MatchColor:=MatchColor;
+ SpotterForm.KeyStrokeColor:=KeyStrokeColor;
+ SpotterForm.ShowShortCutKey:=ShowShortCutKey;
+ SpotterForm.Initialize;
+ end;
+end;
+
+Procedure ShowSpotterForm;
+
+begin
+ if SpotterForm=Nil then
+ begin
+ SpotterForm:=TSpotterForm.Create(Application);
+ ApplySpotterOptions;
+ end;
+ SpotterForm.Show;
+end;
+{ TSearchItem }
+
+procedure TSearchItem.SetSource(AValue: TObject);
+begin
+ if FSource=AValue then Exit;
+ FSource:=AValue;
+end;
+
+constructor TSearchItem.Create(aSource: TObject; AOwnsSource: Boolean);
+begin
+ FSource:=aSource;
+ FOwnsSource:=AOwnsSource;
+end;
+
+destructor TSearchItem.Destroy;
+begin
+ if OwnsSource then
+ FreeAndNil(FSource);
+ Inherited;
+end;
{ TSpotterForm }
procedure TSpotterForm.ECommandChange(Sender: TObject);
begin
- FilterCommandList(ECommand.Text);
+ FilterList(ECommand.Text);
end;
procedure TSpotterForm.ECommandKeyUp(Sender: TObject; var Key: Word;
@@ -118,14 +245,23 @@ begin
CloseAction:=caHide;
end;
-procedure TSpotterForm.FilterCommandList(aCmd: String);
+procedure TSpotterForm.RefreshCaption(aCount : Integer);
+
+begin
+ if ACount=-1 then
+ Caption:=FOrgCaption+Format(' (%d)',[FSearchItems.Count])
+ else
+ Caption:=FOrgCaption+Format(' (%d/%d)',[aCount,FSearchItems.Count]);
+end;
+
+procedure TSpotterForm.FilterList(aSearchTerm: String);
Var
i : Integer;
- Cmd : TIDECommand;
- pref,ks : string;
+ Itm : TSearchItem;
Words : Array of string;
MatchPos : Array of Integer;
+ MatchLen : Array of Integer;
Function Match(S : String) : Boolean;
@@ -144,30 +280,27 @@ Var
end;
begin
- aCmd:=LowerCase(aCmd);
- Setlength(Words,WordCount(aCmd,[' ']));
+ aSearchTerm:=LowerCase(aSearchTerm);
+ Setlength(Words,WordCount(aSearchTerm,[' ']));
Setlength(MatchPos,Length(Words));
+ Setlength(MatchLen,Length(Words));
For I:=1 to Length(Words) do
- Words[I-1]:=ExtractWord(I,aCmd,[' ']);
+ begin
+ Words[I-1]:=ExtractWord(I,aSearchTerm,[' ']);
+ MatchLen[I-1]:=Length(Words[I-1]);
+ end;
LBMatches.Items.BeginUpdate;
try
LBMatches.Items.Clear;
- For I:=0 to FCommands.Count-1 do
- if Match(FCommands[I]) then
+ For I:=0 to FSearchItems.Count-1 do
+ if Match(FSearchItems[I]) then
begin
- Cmd:=TIDECommand(FCommands.Objects[i]);
- Pref:=GetCommandCategoryString(Cmd);
- ks:='';
- if Cmd.ShortcutA.Key1<>VK_UNKNOWN then
- begin
- ks:=' ('+KeyAndShiftStateToKeyString(Cmd.ShortcutA.Key1,Cmd.ShortcutA.Shift1);
- if Cmd.ShortcutA.Key2<>VK_UNKNOWN then
- ks:=Ks+', '+KeyAndShiftStateToKeyString(Cmd.ShortcutA.Key1,Cmd.ShortcutA.Shift1);
- ks:=ks+')';
- end;
- LBMatches.Items.AddObject(Pref+Cmd.LocalizedName+ks,Cmd);
+ Itm:=FSearchItems.Objects[I] as TSearchItem;
+ Itm.MatchPos:=Copy(MatchPos,0,Length(MatchPos));
+ Itm.MatchLen:=Copy(MatchLen,0,Length(MatchLen));
+ LBMatches.Items.AddObject(FSearchItems[I],Itm);
end;
- Caption:=FOrgCaption+Format(' (%d/%d)',[LBMatches.Items.Count,FCommands.Count]);
+ RefreshCaption(LBMatches.Items.Count);
finally
LBMatches.Items.EndUpdate;
LBMatches.Visible:=LBMatches.Items.Count>0;
@@ -178,9 +311,9 @@ end;
procedure TSpotterForm.FormCreate(Sender: TObject);
begin
- FCommands:=TStringList.Create;
+ FSearchItems:=TStringList.Create;
+ FSearchItems.OwnsObjects:=True;
FOrgCaption:=Caption;
- FillCommands;
end;
procedure TSpotterForm.FormShow(Sender: TObject);
@@ -196,31 +329,88 @@ begin
end;
ECommand.Clear;
LBMatches.Clear;
+ RefreshCaption(-1);
end;
-procedure TSpotterForm.LBMatchesClickC(Sender: TObject);
+procedure TSpotterForm.LBMatchesClick(Sender: TObject);
begin
ExecuteSelected;
end;
+procedure TSpotterForm.LBMatchesDrawItem(Control: TWinControl; Index: Integer;
+ ARect: TRect; State: TOwnerDrawState);
+
+Var
+ LB : TListbox;
+ DS,S : String;
+ Itm : TSearchItem;
+ R : TRect;
+ P,I,SP,W,SW : Integer;
+ FC : TColor;
+
+begin
+ SW := GetSystemMetrics(SM_CXVSCROLL);
+ LB:=Control as TListBox;
+ LB.Canvas.FillRect(ARect);
+ FC:=LB.Canvas.Font.Color;
+ Itm:=LB.Items.Objects[Index] as TSearchItem;
+ S:=LB.Items[Index];
+ R:=ARect;
+ if ShowShortCutKey and (Itm.KeyStroke<>'') then
+ begin
+ W:=LB.Canvas.TextWidth(Itm.KeyStroke);
+ R.Right:=R.Right-W-SW;
+ end;
+ Inc(R.Left,2);
+ SP:=1;
+ For I:=0 to Length(Itm.MatchPos)-1 do
+ begin
+ P:=Itm.MatchPos[i];
+ if (P-SP>0) then
+ begin
+ DS:=Copy(S,SP,P-SP);
+ LB.Canvas.TextRect(R,R.Left,R.Top,DS);
+ Inc(R.Left,LB.Canvas.TextWidth(DS));
+ end;
+ DS:=Copy(S,P,Itm.MatchLen[i]);
+ LB.Canvas.Font.Color:=MatchColor;
+ LB.Canvas.TextRect(R,R.Left,R.Top,DS);
+ LB.Canvas.Font.Color:=FC;
+ Inc(R.Left,LB.Canvas.TextWidth(DS));
+ SP:=P+Itm.MatchLen[i];
+ end;
+ if SP<=Length(S) then
+ begin
+ DS:=Copy(S,SP,Length(S)-SP+1);
+ LB.Canvas.TextRect(R,R.Left,R.Top,DS);
+ end;
+ if Itm.KeyStroke<>'' then
+ begin
+ R.Left:=R.Right+1;
+ R.Right:=aRect.Right;
+ LB.Canvas.Font.Color:=KeyStrokeColor;
+ LB.Canvas.TextRect(R,R.Left,R.Top,Itm.KeyStroke);
+ end;
+end;
+
procedure TSpotterForm.ExecuteSelected;
Var
idx: Integer;
+ itm : TSearchItem;
+
begin
Idx:=LBMatches.ItemIndex;
if (Idx>=0) then
begin
Hide;
- TIDECommand(LBMatches.Items.Objects[Idx]).Execute(Application);
+ Itm:=LBMatches.Items.Objects[Idx] as TSearchItem;
+ if Itm.Source is TIDECommand then
+ TIDECommand(Itm.Source).Execute(SourceEditorManagerIntf.ActiveEditor);
end;
end;
-procedure TSpotterForm.LBMatchesEnter(Sender: TObject);
-begin
-
-end;
procedure TSpotterForm.LBMatchesKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
@@ -229,6 +419,11 @@ begin
Hide;
end;
+procedure TSpotterForm.Initialize;
+begin
+ FillCommands;
+end;
+
function TSpotterForm.GetCommandCategoryString(Cmd: TIDECommand): String;
Const
@@ -259,8 +454,9 @@ end;
procedure TSpotterForm.FillCommands;
var
I, J: Integer;
+ Itm : TSearchItem;
Cmd : TIDECommand;
- Pref : String;
+ Ks,Pref : String;
begin
For I:=0 to IDECommandList.CategoryCount-1 do
@@ -270,11 +466,22 @@ begin
begin
Cmd:=TIDECommand(IDECommandList.Categories[I].Items[J]);
Pref:=GetCommandCategoryString(Cmd);
- FCommands.AddObject(UTF8LowerCase(Pref+Cmd.LocalizedName),Cmd);
+ ks:='';
+ if Cmd.ShortcutA.Key1<>VK_UNKNOWN then
+ begin
+ ks:=' ('+KeyAndShiftStateToKeyString(Cmd.ShortcutA.Key1,Cmd.ShortcutA.Shift1);
+ if Cmd.ShortcutA.Key2<>VK_UNKNOWN then
+ ks:=Ks+', '+KeyAndShiftStateToKeyString(Cmd.ShortcutA.Key1,Cmd.ShortcutA.Shift1);
+ ks:=ks+')';
+ end;
+ Itm:=TSearchItem.Create(Cmd);
+ Itm.Prefix:=Pref;
+ Itm.KeyStroke:=Ks;
+ FSearchItems.AddObject(UTF8LowerCase(Pref+Cmd.LocalizedName),Itm);
end;
end;
- FCommands.Sort;
- Caption:=FOrgCaption+Format(' (%d)',[FCommands.Count]);
+ FSearchItems.Sort;
+ RefreshCaption(-1);
end;
end.
diff --git a/components/idespotter/idespotter.lpk b/components/idespotter/idespotter.lpk
index c570176f67..12efb4942b 100644
--- a/components/idespotter/idespotter.lpk
+++ b/components/idespotter/idespotter.lpk
@@ -13,7 +13,7 @@
-
+
@@ -27,6 +27,10 @@
+
+
+
+
diff --git a/components/idespotter/idespotter.pas b/components/idespotter/idespotter.pas
index d69566184b..48722221ef 100644
--- a/components/idespotter/idespotter.pas
+++ b/components/idespotter/idespotter.pas
@@ -8,13 +8,13 @@ unit idespotter;
interface
uses
- regidespotter, frmspotter, LazarusPackageIntf;
+ RegIDESpotter, frmspotter, IDESPotterOptions, LazarusPackageIntf;
implementation
procedure Register;
begin
- RegisterUnit('regidespotter', @regidespotter.Register);
+ RegisterUnit('RegIDESpotter', @RegIDESpotter.Register);
end;
initialization
diff --git a/components/idespotter/regidespotter.pas b/components/idespotter/regidespotter.pas
index 4c08ee17e6..900a2c78fd 100644
--- a/components/idespotter/regidespotter.pas
+++ b/components/idespotter/regidespotter.pas
@@ -1,3 +1,32 @@
+{ Register IDE Spotter unit
+
+ Copyright (C) 2018 Michael van Canneyt michael@freepascal.org
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version with the following modification:
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent modules,and
+ to copy and distribute the resulting executable under terms of your choice,
+ provided that you also meet, for each linked independent module, the terms
+ and conditions of the license of that module. An independent module is a
+ module which is not derived from or based on this library. If you modify
+ this library, you may extend this exception to your version of the library,
+ but you are not obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version.
+
+ This program 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 Library General Public License
+ for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
+}
unit RegIDESpotter;
{$mode objfpc}{$H+}
@@ -5,27 +34,19 @@ unit RegIDESpotter;
interface
uses
- Classes, SysUtils;
+ // IdeIntf
+ IDEOptionsIntf, IDEOptEditorIntf, IDEUtils, Classes, SysUtils;
Procedure Register;
implementation
-uses forms,lcltype,idecommands,toolbarintf,menuintf, frmspotter;
-
-Var
- SpotterFrm : TSpotterForm;
- ShowCmdCategory : Boolean = True;
+uses IDESpotterOptions,forms, graphics,lcltype,idecommands,toolbarintf,menuintf, frmspotter;
Procedure IdeMenuClicked(Sender : TObject);
begin
- if SpotterFrm=Nil then
- begin
- SpotterFrm:=TSpotterForm.Create(Application);
- SpotterFrm.ShowCategory:=ShowCmdCategory;
- end;
- SpotterFrm.Show;
+ ShowSpotterForm;
end;
Procedure Register;
@@ -42,6 +63,8 @@ var
IDEShortCutX: TIDEShortCut;
IDECommandCategory: TIDECommandCategory;
IDECommand: TIDECommand;
+ IDESpotteroptionsFrameID: Integer = 2000;
+
begin
IDEShortCutX := IDEShortCut(VK_P, ShiftKeys, VK_UNKNOWN, []);
IDECommandCategory := IDECommandList.FindCategoryByName(CommandCategoryViewName);
@@ -52,7 +75,8 @@ begin
if IDECommand <> nil then
RegisterIDEButtonCommand(IDECommand);
end;
-
+ IDESpotteroptionsFrameID:=RegisterIDEOptionsEditor(GroupEnvironment,TIDESpotterOptionsFrame,
+ IDESpotteroptionsFrameID)^.Index;
RegisterIDEMenuCommand(itmViewIDEInternalsWindows, 'Spotter', 'Spotter', nil,
@IDEMenuClicked,IDECommand);
end;