From dde99ecd0c144690621cad7496fa1d348031f88e Mon Sep 17 00:00:00 2001
From: Basile Burg <basile.burg@gmx.com>
Date: Wed, 25 Jun 2014 14:04:00 +0200
Subject: [PATCH] r10

---
 lazproj/coedit.lpi        |  10 +-
 lazproj/coedit.lpr        |   2 +-
 src/ce_common.pas         | 145 +++++++++++-------
 src/ce_d2syn.pas          |  22 ++-
 src/ce_main.pas           |  50 ++++---
 src/ce_search.lfm         | 305 ++++++++++++++++++++++++++++++++++++++
 src/ce_search.pas         | 186 +++++++++++++++++++++++
 src/ce_staticexplorer.lfm |   1 +
 src/ce_staticexplorer.pas |   8 +-
 9 files changed, 635 insertions(+), 94 deletions(-)
 create mode 100644 src/ce_search.lfm
 create mode 100644 src/ce_search.pas

diff --git a/lazproj/coedit.lpi b/lazproj/coedit.lpi
index 5e24ff97..0e50dd57 100644
--- a/lazproj/coedit.lpi
+++ b/lazproj/coedit.lpi
@@ -126,7 +126,7 @@
         <PackageName Value="LCL"/>
       </Item5>
     </RequiredPackages>
-    <Units Count="14">
+    <Units Count="15">
       <Unit0>
         <Filename Value="coedit.lpr"/>
         <IsPartOfProject Value="True"/>
@@ -218,6 +218,14 @@
         <ResourceBaseClass Value="Form"/>
         <UnitName Value="ce_staticexplorer"/>
       </Unit13>
+      <Unit14>
+        <Filename Value="..\src\ce_search.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="CESearchWidget"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+        <UnitName Value="ce_search"/>
+      </Unit14>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
diff --git a/lazproj/coedit.lpr b/lazproj/coedit.lpr
index a2265302..4098f904 100644
--- a/lazproj/coedit.lpr
+++ b/lazproj/coedit.lpr
@@ -9,7 +9,7 @@ uses
   Interfaces, Forms, lazcontrols, runtimetypeinfocontrols, ce_widget,
   ce_dmdwrap, ce_common, ce_synmemo, ce_main, ce_messages, ce_editor,
   ce_projinspect, ce_projconf, jsonparser, ce_project, 
-  ce_widgettypes, ce_staticexplorer;
+  ce_widgettypes, ce_staticexplorer, ce_search;
 
 {$R *.res}
 
diff --git a/src/ce_common.pas b/src/ce_common.pas
index 94740ac0..bda20ffa 100644
--- a/src/ce_common.pas
+++ b/src/ce_common.pas
@@ -7,6 +7,33 @@ interface
 uses
   Classes, SysUtils, ActnList, dialogs, forms;
 
+type
+
+  (**
+   * MRU list for strings
+   *)
+  TMRUList = class(TStringList)
+  private
+    fMaxCount: Integer;
+  protected
+    procedure setMaxCount(aValue: Integer);
+    function checkItem(const S: string): boolean; virtual;
+    procedure Put(Index: Integer; const S: string); override;
+  published
+    property maxCount: Integer read fMaxCount write setMaxCount;
+  public
+    constructor Create;
+    procedure Insert(Index: Integer; const S: string); override;
+  end;
+
+  (**
+   * MRU list for filenames
+   *)
+  TMRUFileList = class(TMRUList)
+  protected
+    function checkItem(const S: string): boolean; override;
+  end;
+
   (**
    * Save a component with a readable aspect.
    *)
@@ -45,9 +72,45 @@ uses
     *)
    function uniqueObjStr(const aObject: Tobject): string;
 
-
 implementation
 
+constructor TMRUList.Create;
+begin
+  fMaxCount := 10;
+end;
+
+procedure TMRUList.setMaxCount(aValue: Integer);
+begin
+  if aValue < 0 then aValue := 0;
+  if fMaxCount = aValue then exit;
+  while Count > fMaxCount do delete(Count-1);
+end;
+
+function TMRUList.checkItem(const S: string): boolean;
+begin
+  exit( indexOf(S) = -1 );
+end;
+
+procedure TMRUList.Put(Index: Integer; const S: string);
+begin
+  if not (checkItem(S)) then exit;
+  inherited;
+  while Count > fMaxCount do delete(Count-1);
+end;
+
+procedure TMRUList.Insert(Index: Integer; const S: string);
+begin
+  if not (checkItem(S)) then exit;
+  inherited;
+  while Count > fMaxCount do delete(Count-1);
+end;
+
+function TMRUFileList.checkItem(const S: string): boolean;
+begin
+  exit( inherited checkItem(S) and fileExists(S));
+end;
+
+
 procedure saveCompToTxtFile(const aComp: TComponent; const aFilename: string);
 var
   str1, str2: TMemoryStream;
@@ -102,71 +165,34 @@ begin
 end;
 
 function patchPlateformPath(const aPath: string): string;
+function patchProc(const src: string; const invalid: char): string;
 var
   i: Integer;
+begin
+  result := src;
+  i := pos(invalid, result);
+  if i <> 0 then
+  begin
+    repeat
+      result[i] := directorySeparator;
+      i := pos(invalid,result);
+    until
+      i = 0;
+  end;
+end;
 begin
   result := aPath;
   {$IFDEF MSWINDOWS}
-  i := pos('/',result);
-  if i <> 0 then
-  begin
-    repeat
-      result[i] := directorySeparator;
-      i := pos('/',result);
-    until
-      i = 0;
-  end;
-  i := pos(':',result);
-  if i <> 0 then
-  begin
-    repeat
-      result[i] := directorySeparator;
-      i := pos(':',result);
-    until
-      i = 0;
-  end;
+  result := patchProc(result,'/');
+  result := patchProc(result,':');
   {$ENDIF}
-
   {$IFDEF LINUX}
-  i := pos('\',result);
-  if i <> 0 then
-  begin
-    repeat
-      result[i] := directorySeparator;
-      i := pos('\',result);
-    until
-      i = 0;
-  end;
-  i := pos(':',result);
-  if i <> 0 then
-  begin
-    repeat
-      result[i] := directorySeparator;
-      i := pos(':',result);
-    until
-      i = 0;
-  end;
+  result := patchProc(result,'\');
+  result := patchProc(result,':');
   {$ENDIF}
-
   {$IFDEF MACOS}
-  i := pos('\',result);
-  if i <> 0 then
-  begin
-    repeat
-      result[i] := directorySeparator;
-      i := pos('\',result);
-    until
-      i = 0;
-  end;
-  i := pos('/',result);
-  if i <> 0 then
-  begin
-    repeat
-      result[i] := directorySeparator;
-      i := pos('/',result);
-    until
-      i = 0;
-  end;
+  result := patchProc(result,'\');
+  result := patchProc(result,'/');
   {$ENDIF}
 end;
 
@@ -249,4 +275,9 @@ begin
   {$HINTS ON}{$WARNINGS ON}
 end;
 
+operator =(lhs,rhs: TPoint): boolean;
+begin
+  exit( (lhs.x = rhs.x) and (lhs.y = rhs.y) );
+end;
+
 end.
diff --git a/src/ce_d2syn.pas b/src/ce_d2syn.pas
index 14631ef0..c83ce6ff 100644
--- a/src/ce_d2syn.pas
+++ b/src/ce_d2syn.pas
@@ -44,11 +44,12 @@ type
     values: array of string;
   end;
 
+  // TODO: rather gperf ?
   TD2Dictionary = object
   private
     fLongest: NativeInt;
-    fEntries: array[0..1023] of TD2DictionaryEntry;
-    function toHash(const aValue: string): word;
+    fEntries: array[0..255] of TD2DictionaryEntry;
+    function toHash(const aValue: string): Byte;
     procedure addEntry(const aValue: string);
   public
     constructor create;
@@ -227,20 +228,14 @@ begin
 end;
 
 {$IFDEF DEBUG}{$R-}{$ENDIF}
-function TD2Dictionary.toHash(const aValue: string): word;
+function TD2Dictionary.toHash(const aValue: string): Byte;
 var
   i, len: Integer;
-  prev: word;
 begin
   result := 0;
-  prev := 0;
   len := length(aValue);
 	for i := 1 to len do
-  begin
-	  result += ((Byte(aValue[i]) + 64) shl i) xor prev;
-		prev := Byte(aValue[i]);
-	end;
-  result := result and 1023;
+	  result += (Byte(aValue[i]) shl i) xor 63;
 end;
 {$IFDEF DEBUG}{$R+}{$ENDIF}
 
@@ -458,6 +453,8 @@ TODO:
 - comments: correct nested comments handling.
 }
 procedure TSynD2Syn.next;
+label
+  _postString1;
 begin
 
   fTokStart := fTokStop;
@@ -613,8 +610,8 @@ begin
       begin
         if not (readNext = '"') then
         begin
-          fTokKind := tkIdent;
-          exit; // warning: a goto is avoided but any other r/x is not detectable since it's truncated as tkIdent
+          Dec(fTokStop);
+          goto _postString1;
         end;
       end;
       // go to end of string/eol
@@ -631,6 +628,7 @@ begin
       exit;
     end;
   end;
+  _postString1:
 
   // string 2
   if fRange = rkString2 then
diff --git a/src/ce_main.pas b/src/ce_main.pas
index 19fa510e..0c9003fe 100644
--- a/src/ce_main.pas
+++ b/src/ce_main.pas
@@ -7,9 +7,9 @@ interface
 uses
   Classes, SysUtils, FileUtil, SynEditKeyCmds, SynHighlighterLFM, Forms,
   AnchorDocking, AnchorDockStorage, AnchorDockOptionsDlg, Controls, Graphics,
-  Dialogs, Menus, ActnList, ExtCtrls, ComCtrls, process,
+  Dialogs, Menus, ActnList, ExtCtrls, process,
   ce_common, ce_dmdwrap, ce_project, ce_synmemo, ce_widget, ce_messages,
-  ce_editor, ce_projinspect, ce_projconf, ce_staticexplorer;
+  ce_editor, ce_projinspect, ce_projconf, ce_staticexplorer, ce_search;
 
 type
 
@@ -142,6 +142,7 @@ type
     fProjWidg: TCEProjectInspectWidget;
     fPrjCfWidg: TCEProjectConfigurationWidget;
     fStExpWidg: TCEStaticExplorerWidget;
+    fFindWidg:  TCESearchWidget;
 
     // widget interfaces subroutines
     procedure checkWidgetActions(const aWidget: TCEWidget);
@@ -207,12 +208,14 @@ begin
   fProjWidg := TCEProjectInspectWidget.create(nil);
   fPrjCfWidg:= TCEProjectConfigurationWidget.create(nil);
   fStExpWidg:= TCEStaticExplorerWidget.create(nil);
+  fFindWidg := TCESearchWidget.create(nil);
 
   fWidgList.addWidget(@fMesgWidg);
   fWidgList.addWidget(@fEditWidg);
   fWidgList.addWidget(@fProjWidg);
   fWidgList.addWidget(@fPrjCfWidg);
   fWidgList.addWidget(@fStExpWidg);
+  fWidgList.addWidget(@fFindWidg);
 
   for widg in fWidgList do widg.Show;
 
@@ -252,6 +255,7 @@ begin
   fProjWidg.Free;
   fPrjCfWidg.Free;
   fStExpWidg.Free;
+  fFindWidg.Free;
   fProject.Free;
   //
   inherited;
@@ -698,23 +702,28 @@ begin
     {$IFDEF RELEASE}
     dmdProc.ShowWindow := swoHIDE;
     {$ENDIF}
-    dmdproc.Options:= [poWaitOnExit, poStdErrToOutput, poUsePipes];
+    dmdproc.Options:= [poStdErrToOutput, poUsePipes];
     dmdproc.Executable:= 'dmd';
     dmdproc.Parameters.Add(fname + '.d');
     try
       dmdproc.Execute;
+      while dmdproc.Running do if dmdproc.ExitStatus <> 0 then break;
       ProcessOutputToMsg(dmdproc);
     finally
       DeleteFile(fname + '.d');
     end;
 
+    {$IFDEF MSWINDOWS}
+    if (dmdProc.ExitStatus = 0) or (dmdProc.ExitStatus = 259) then
+    {$ELSE}
     if dmdProc.ExitStatus = 0 then
+    {$ENDIF}
     begin
 
       fMesgWidg.addCeInf( fEditWidg.editor[edIndex].fileName
         + ' successfully compiled' );
 
-      runproc.Options:= [poWaitOnExit, poStderrToOutPut, poUsePipes];
+      runproc.Options:= [poStderrToOutPut, poUsePipes];
       {$IFDEF MSWINDOWS}
       runproc.Executable := fname + '.exe';
       runproc.Parameters.Text := runArgs;
@@ -722,10 +731,8 @@ begin
       runproc.Executable := fname;
       {$ENDIF}
       runproc.Execute;
-      repeat
-        ProcessOutputToMsg(runproc);
-      until
-        not runproc.Active;
+      while runproc.Running do if runproc.ExitStatus <> 0 then break;
+      ProcessOutputToMsg(runproc);
       {$IFDEF MSWINDOWS}
       DeleteFile(fname + '.exe');
       DeleteFile(fname + '.obj');
@@ -749,12 +756,6 @@ procedure TCEMainForm.compileProject(const aProject: TCEProject);
 var
   dmdproc: TProcess;
   olddir, prjpath: string;
-const
-  // option -v causes a hang if poWaitOnExit is included
-  procopts: array[boolean] of TProcessOptions = (
-    [poWaitOnExit, poStdErrToOutput, poUsePipes],
-    [poStdErrToOutput, poUsePipes]
-  );
 begin
 
   if aProject.Sources.Count = 0 then
@@ -776,16 +777,20 @@ begin
     {$IFDEF RELEASE}
     dmdProc.ShowWindow := swoHIDE;
     {$ENDIF}
-    dmdproc.Options :=
-      procopts[aProject.currentConfiguration.messagesOptions.verbose];
+    dmdproc.Options := [poStdErrToOutput, poUsePipes];
 
     dmdproc.Executable := 'dmd';
     aProject.getOpts(dmdproc.Parameters);
     try
       dmdproc.Execute;
+      while dmdproc.Running do if dmdproc.ExitStatus <> 0 then break;
       ProcessOutputToMsg(dmdproc);
     finally
+      {$IFDEF MSWINDOWS} //  STILL_ACTIVE ambiguity
+      if (dmdProc.ExitStatus = 0) or (dmdProc.ExitStatus = 259) then
+      {$ELSE}
       if dmdProc.ExitStatus = 0 then
+      {$ENDIF}
         fMesgWidg.addCeInf( aProject.fileName
           + ' successfully compiled' )
       else
@@ -833,6 +838,7 @@ begin
     runproc.Executable := procname;
     runproc.Parameters.Text := runArgs;
     runproc.Execute;
+    while runproc.Running do if runproc.ExitStatus <> 0 then break;
 
   finally
     runproc.Free;
@@ -856,8 +862,8 @@ begin
   if fEditWidg.editorIndex < 0 then exit;
   //
   runargs := '';
-  if InputQuery('Execution arguments', 'enter switches and arguments',
-    runargs) then compileAndRunFile(fEditWidg.editorIndex, runargs);
+  if InputQuery('Execution arguments', '', runargs) then
+    compileAndRunFile(fEditWidg.editorIndex, runargs);
 end;
 
 procedure TCEMainForm.actProjCompileExecute(Sender: TObject);
@@ -878,8 +884,8 @@ begin
   compileProject(fProject);
   //
   runargs := '';
-  if InputQuery('Execution arguments', 'enter switches and arguments',
-    runargs) then runProject(fProject, runargs);
+  if InputQuery('Execution arguments', '', runargs) then
+    runProject(fProject, runargs);
 end;
 
 procedure TCEMainForm.actProjRunExecute(Sender: TObject);
@@ -892,8 +898,8 @@ var
   runargs: string;
 begin
   runargs := '';
-  if InputQuery('Execution arguments', 'enter switches and arguments',
-    runargs) then runProject(fProject, runargs);
+  if InputQuery('Execution arguments', '', runargs) then
+    runProject(fProject, runargs);
 end;
 {$ENDREGION}
 
diff --git a/src/ce_search.lfm b/src/ce_search.lfm
new file mode 100644
index 00000000..6730f897
--- /dev/null
+++ b/src/ce_search.lfm
@@ -0,0 +1,305 @@
+inherited CESearchWidget: TCESearchWidget
+  Left = 1338
+  Height = 276
+  Top = 697
+  Width = 405
+  Caption = 'Search & replace'
+  ClientHeight = 276
+  ClientWidth = 405
+  inherited Back: TPanel
+    Height = 276
+    Width = 405
+    ClientHeight = 276
+    ClientWidth = 405
+    inherited Content: TPanel
+      Height = 276
+      Width = 405
+      ClientHeight = 276
+      ClientWidth = 405
+      object cbToFind: TComboBox[0]
+        Left = 4
+        Height = 23
+        Top = 4
+        Width = 397
+        Align = alTop
+        BorderSpacing.Around = 4
+        ItemHeight = 15
+        OnChange = cbToFindChange
+        TabOrder = 0
+      end
+      object cbReplaceWth: TComboBox[1]
+        Left = 4
+        Height = 23
+        Top = 31
+        Width = 397
+        Align = alTop
+        BorderSpacing.Around = 4
+        ItemHeight = 15
+        OnChange = cbReplaceWthChange
+        TabOrder = 1
+      end
+      object btnFind: TBitBtn[2]
+        Left = 4
+        Height = 24
+        Top = 192
+        Width = 397
+        Align = alBottom
+        BorderSpacing.Around = 4
+        Caption = 'btnFind'
+        Glyph.Data = {
+          36040000424D3604000000000000360000002800000010000000100000000100
+          2000000000000004000064000000640000000000000000000000FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003E39
+          34FF393430FF332F2BFF2C2925FF272421FF201D1BFF1716141A110F0EDB0B0A
+          09FF070706FF040403FF000000FF000000FFFFFFFF00FFFFFF00FFFFFF004641
+          3BFF857A70FFC3B8AEFF7C7268FF7F756BFF36322DFF1E1C190F282522D49589
+          7DFFBAAEA2FF7C7268FF7F756BFF010101FFFFFFFF00FFFFFF00FFFFFF004D47
+          41FF83786FFFCCC3BAFF786F65FF7B7167FF2F2B28F9272421011D1B18EE9589
+          7DFFC2B8ADFF786F65FF7C7268FF060505FFFFFFFF00FFFFFF00FFFFFF00534C
+          46FC83786FFFCCC3BAFF797066FF71685FFF37332ED5FFFFFF00252220D5857A
+          70FFC2B8ADFF786F65FF7B7167FF0A0908FCFFFFFF00FFFFFF00FFFFFF005A52
+          4CC39F9286FFCCC3BAFFC0B4AAFFA6988BFF3E3934A8FFFFFF002C2925A89084
+          79FFC2B8ADFFC0B4AAFFA89B8EFF110F0EC3FFFFFF00FFFFFF00797066055C55
+          4EF9423D38FF58514AFF3D3833FF332F2BFF23201DE5171614301E1C19B51A18
+          16FF252220FF191715FF0F0E0DFF010101EE00000002FFFFFF009F9286059D91
+          85FFB1A396FF7F756BFF7C7268FF776D64FF6C635BFF2E2A26FF564F48FF8076
+          6CFF7C7268FF776D64FF70675EFF000000FE00000005FFFFFF00AB9D9004AFA1
+          94E1BAAEA2FF82776DFF82776DFFAA917BFFBAA794FFB7A48EFAB09781FF9F8D
+          7DFF836D5BFF716357FF95897DFF040403E000000003FFFFFF00B9ACA008877D
+          72489B8E82FF9D9185FF867B71FF564F48FF504A44FF80766CFF6E665DFF826C
+          58FFA6917DFF948474FF564F48FF0C0B0B7A07070601FFFFFF00FFFFFF00FFFF
+          FF00746B62FFA4978AFF95897DFF9F9286FF3E3934FFFFFFFF004C4640FF7E74
+          6AFF857A70FF3E3934FF453F3AA72522200C15131102FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF008E8378E2C3B8AEFF655D55FFFFFFFF007C7268FFA89B
+          8EFF9C8F83E4FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF009C8F83E2BCB0A4FF9D9185FFFFFFFF00AEA093FF9D91
+          85FF655D55DAFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00
+        }
+        TabOrder = 2
+      end
+      object btnReplace: TBitBtn[3]
+        Left = 4
+        Height = 24
+        Top = 220
+        Width = 397
+        Align = alBottom
+        BorderSpacing.Around = 4
+        Caption = 'btnReplace'
+        Glyph.Data = {
+          36040000424D3604000000000000360000002800000010000000100000000100
+          2000000000000004000064000000640000000000000000000000FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00DB772511DB742199D9701FFFD96E1CFFD86C
+          1BCCD8691A77D8671A77D6651AEED6621AEED4621999D4601911FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00DD7B2A66E18F4CFFEBB183FFE3914EFFDA73
+          23FFDA7428FFE08848FFE7A270FFE3935CFFDB7533FFD6621A44FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00DF823044E28D44FFECB588FFDC7A2CFFDB74
+          2311DB721F22D9701EDDE6A471FFDC7A34FFD8671A44FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00DF8231CCE69959FFE49755FFDB7B
+          2877FFFFFF00DB742177E18C4CFFE18B49FFD86C1B77FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E1873711DF8435BBE59856FFE595
+          53FFDD7B2ACCDB7727AAE69D63FFE8A46DFFD9701ECCFFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00419149E539853FFF317A37FF7F7A32FF9579
+          2EFCDF8230BBDF7E2CFFE39250FFEBB386FFDC7728FFDB721F11FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00419149F9BCB272FFE8A05EFFBD82
+          37FEDF853511FFFFFF00DF7E2E99E69E61FFE1924EFFDB772566FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF0051A75A8A71B67AFFB6C590FFE69B55FFE8A4
+          65FFE2893988FFFFFF00DF843588E69F61FFE59858FFDD7B2A77DB721F99D96E
+          1CDDD86C1BDDD8691AFF919749F080C588FFB0DCB6FF75B87CFF8E9146FAE496
+          4AFFE8A361FFE28B3CFFE69E5CFFE9A96FFFE49956FFDF823066DB772755E087
+          42FFDB721F88FFFFFF0083AA59D988CB90FF83C68BFF51A75AA8499C52035991
+          48E5E2924377E29040BBE28D3CBBE28B3AAAE1873966FFFFFF00DF7E2E11DE7E
+          2FFFDB7727AAFFFFFF00DB721F1190A352FF85A251F958B16203FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00DF82
+          31BBDF7E2EFFFFFFFF00FFFFFF00DB7423CCDE833AFFD96E1C44FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E189
+          3977E59B59FFDF8231BBDF7E2E88E39250FFDD7F32FFDB742322FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E28E
+          4033E5944CFFE18939BBDF8535AADF8231BBDF7E2E55FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00E29041FFE28E40DDFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E697
+          4766E69547FFE69A4EFFE2904122FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00
+        }
+        TabOrder = 3
+      end
+      object grpOpts: TGroupBox[4]
+        Left = 4
+        Height = 130
+        Top = 58
+        Width = 397
+        Align = alClient
+        BorderSpacing.Around = 4
+        Caption = 'Options'
+        ClientHeight = 112
+        ClientWidth = 393
+        TabOrder = 4
+        object chkWWord: TCheckBox
+          Left = 8
+          Height = 19
+          Top = 0
+          Width = 82
+          Caption = 'whole word'
+          TabOrder = 0
+        end
+        object chkBack: TCheckBox
+          Left = 8
+          Height = 19
+          Top = 24
+          Width = 71
+          Caption = 'backward'
+          TabOrder = 1
+        end
+        object chkFromCur: TCheckBox
+          Left = 8
+          Height = 19
+          Top = 48
+          Width = 82
+          Caption = 'from cursor'
+          Checked = True
+          State = cbChecked
+          TabOrder = 2
+        end
+        object chkCaseSens: TCheckBox
+          Left = 128
+          Height = 19
+          Top = 0
+          Width = 91
+          Caption = 'case sensitive'
+          TabOrder = 3
+        end
+        object chkPrompt: TCheckBox
+          Left = 128
+          Height = 19
+          Top = 24
+          Width = 60
+          Caption = 'Prompt'
+          TabOrder = 4
+        end
+      end
+      object btnReplaceAll: TBitBtn[5]
+        Left = 4
+        Height = 24
+        Top = 248
+        Width = 397
+        Align = alBottom
+        BorderSpacing.Around = 4
+        Caption = 'btnReplaceAll'
+        Glyph.Data = {
+          36040000424D3604000000000000360000002800000010000000100000000100
+          2000000000000004000064000000640000000000000000000000FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00DB772511DB742199D9701FFFD96E1CFFD86C
+          1BCCD8691A77D8671A77D6651AEED6621AEED4621999D4601911FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00DD7B2A66E18F4CFFEBB183FFE3914EFFDA73
+          23FFDA7428FFE08848FFE7A270FFE3935CFFDB7533FFD6621A44FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00DF823044E28D44FFECB588FFDC7A2CFFDB74
+          2311DB721F22D9701EDDE6A471FFDC7A34FFD8671A44FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00DF8231CCE69959FFE49755FFDB7B
+          2877FFFFFF00DB742177E18C4CFFE18B49FFD86C1B77FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E1873711DF8435BBE59856FFE595
+          53FFDD7B2ACCDB7727AAE69D63FFE8A46DFFD9701ECCFFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00419149E539853FFF317A37FF7F7A32FF9579
+          2EFCDF8230BBDF7E2CFFE39250FFEBB386FFDC7728FFDB721F11FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00419149F9BCB272FFE8A05EFFBD82
+          37FEDF853511FFFFFF00DF7E2E99E69E61FFE1924EFFDB772566FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF0051A75A8A71B67AFFB6C590FFE69B55FFE8A4
+          65FFE2893988FFFFFF00DF843588E69F61FFE59858FFDD7B2A77DB721F99D96E
+          1CDDD86C1BDDD8691AFF919749F080C588FFB0DCB6FF75B87CFF8E9146FAE496
+          4AFFE8A361FFE28B3CFFE69E5CFFE9A96FFFE49956FFDF823066DB772755E087
+          42FFDB721F88FFFFFF0083AA59D988CB90FF83C68BFF51A75AA8499C52035991
+          48E5E2924377E29040BBE28D3CBBE28B3AAAE1873966FFFFFF00DF7E2E11DE7E
+          2FFFDB7727AAFFFFFF00DB721F1190A352FF85A251F958B16203FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00DF82
+          31BBDF7E2EFFFFFFFF00FFFFFF00DB7423CCDE833AFFD96E1C44FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E189
+          3977E59B59FFDF8231BBDF7E2E88E39250FFDD7F32FFDB742322FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E28E
+          4033E5944CFFE18939BBDF8535AADF8231BBDF7E2E55FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00E29041FFE28E40DDFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E697
+          4766E69547FFE69A4EFFE2904122FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+          FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00
+        }
+        TabOrder = 5
+      end
+    end
+  end
+  object imgList: TImageList[2]
+    left = 32
+    Bitmap = {
+      4C690200000010000000100000007B7977007B7977007B7977FF73716FFF6D6B
+      69FF696665FF625F5EFF615E5D007E7C7A007B7977FF73716FFF6D6B69FF6966
+      65FF63605FFF64616000646160007B7977007B7977007A7976FF908D8CFF908E
+      8CFF656461FF615E5DFF615E5D007D7B7900797775FF8E8B8AFF918F8DFF6866
+      64FF63605FFF64616000646160007B7977007B79770000000033787574FFCBCA
+      C7FF9B9A99FF625E5DFF625F5E007D7A7900787574FFCAC8C6FF989796FF605D
+      5CFF0000003364616000646160007B797700797875AE716E6DFF696665FF6460
+      5FFF625E5DFF545251AA7B7A7700656361006A6967AA6F6C6BFF696665FF6764
+      63FF656261FF63605FAE646160007C797800787574FFBDBBB9FFCECCCBFFA5A3
+      A2FF7E7C7BFF5E5B5AFF7B7977FF646260FF797675FFBCBBB8FFCECCCBFFA6A4
+      A3FF807E7DFF605D5CFF63605F007B787700777473FFB9B7B5FFC9C7C6FFA2A0
+      9FFF7D7B7AFF5C5858FF777472FF615F5DFF777473FFB9B8B5FFC9C7C6FFA2A0
+      9FFF7E7C7BFF605D5CFF63605F007B797700777473FFBCBAB8FFCDCBCAFFA5A3
+      A2FF7E7C7BFF595655FFD6D5D3FF8C8C89FF767372FFBCBAB7FFCCCAC9FFA4A2
+      A1FF7E7C7BFF5F5C5BFF625F5E007A7876FF6F6D6BFF666261FF605D5CFF5E5C
+      5AFF5E5A5AFF4946469BD3D2D0FF8A8987FF62615F9B6C6A68FF646160FF625F
+      5EFF615F5DFF615E5DFF615E5DA8777472FFDEDDDDFFEBEBE9FFDBDAD9FFCAC9
+      C8FFBBBAB9FF575453FFD6D5D3FF8C8B89FF757371FFDEDDDCFFEBEBE9FFDBDA
+      D9FFCAC9C8FFBCBCBBFF5E5B5AFF767472FFB3B0AEFFCAC9C7FFB0ADADFF9190
+      8EFF757472FF5B5857FF767472FF615F5DFF767472FFB3B1AFFFCAC9C7FFB0AD
+      ADFF91908EFF757573FF5F5C5BFF767472FFB3B1AFFFCAC9C7FFB1AEAEFF9392
+      90FF777674FF5E5A59FF7A7876FF646260FF777573FFB3B1AFFFCAC9C7FFB1AE
+      AEFF939290FF777674FF605D5CFF767472FFB3B1AFFFCAC9C7FFB0AEADFF9190
+      8EFF757472FF5E5A59FF0000003300000033777573FFB3B1AFFFCAC9C7FFB0AE
+      ADFF91908EFF757572FF5F5C5BFF777573FFCFCFCFFFD9D7D6FFD0CFCEFFC8C8
+      C7FFC3C1C1FF5E5A59FF5F5C5B007E7C7A00777573FFD0CFCFFFD9D7D6FFD0CF
+      CEFFC8C8C7FFC3C1C1FF5E5B5AFF7A7876FF817E7DFF9C9996FF898583FF716F
+      6CFF595756FF605D5CFF605D5C007F7D7B007A7876FF817E7DFF9C9996FF8985
+      83FF716F6CFF595756FF615E5DFF716F6DC0767472FF706D6CFF6D6A69FF6967
+      65FF666362FF5A5756C0605D5C007E7C7A00716F6DC0767472FF706D6CFF6D6A
+      69FF696765FF666362FF5A5756C0000000230000003300000033000000330000
+      0033000000330000002300000000000000000000002300000033000000330000
+      0033000000330000003300000023FFFFFF00E6974766E69547FFE69A4EFFE290
+      4122FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E29041FFE28E40DDFFFF
+      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E28E4033E5944CFFE18939BBDF85
+      35AADF8231BBDF7E2E55FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E1893977E59B59FFDF8231BBDF7E
+      2E88E39250FFDD7F32FFDB742322FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00DF8231BBDF7E2EFFFFFFFF00FFFF
+      FF00DB7423CCDE833AFFD96E1C44FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00FFFFFF00DF7E2E11DE7E2FFFDB7727AAFFFFFF00DB72
+      1F1190A352FF85A251F958B16203FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00FFFFFF00DB772755E08742FFDB721F88FFFFFF0083AA
+      59D988CB90FF83C68BFF51A75AA8499C5203599148E5E2924377E29040BBE28D
+      3CBBE28B3AAAE1873966FFFFFF00DB721F99D96E1CDDD86C1BDDD8691AFF9197
+      49F080C588FFB0DCB6FF75B87CFF8E9146FAE4964AFFE8A361FFE28B3CFFE69E
+      5CFFE9A96FFFE49956FFDF823066FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF0051A75A8A71B67AFFB6C590FFE69B55FFE8A465FFE2893988FFFFFF00DF84
+      3588E69F61FFE59858FFDD7B2A77FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00419149F9BCB272FFE8A05EFFBD8237FEDF853511FFFFFF00DF7E
+      2E99E69E61FFE1924EFFDB772566FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00419149E539853FFF317A37FF7F7A32FF95792EFCDF8230BBDF7E2CFFE392
+      50FFEBB386FFDC7728FFDB721F11FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00E1873711DF8435BBE59856FFE59553FFDD7B2ACCDB7727AAE69D
+      63FFE8A46DFFD9701ECCFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00DF8231CCE69959FFE49755FFDB7B2877FFFFFF00DB742177E18C
+      4CFFE18B49FFD86C1B77FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00DF823044E28D44FFECB588FFDC7A2CFFDB742311DB721F22D9701EDDE6A4
+      71FFDC7A34FFD8671A44FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00DD7B2A66E18F4CFFEBB183FFE3914EFFDA7323FFDA7428FFE08848FFE7A2
+      70FFE3935CFFDB7533FFD6621A44FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+      FF00DB772511DB742199D9701FFFD96E1CFFD86C1BCCD8691A77D8671A77D665
+      1AEED6621AEED4621999D4601911
+    }
+  end
+end
diff --git a/src/ce_search.pas b/src/ce_search.pas
new file mode 100644
index 00000000..3ffa2ef7
--- /dev/null
+++ b/src/ce_search.pas
@@ -0,0 +1,186 @@
+unit ce_search;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
+  Menus, StdCtrls, actnList, Buttons, SynEdit, SynEditSearch, SynEditTypes, ce_common,
+  ce_widget, ce_synmemo;
+
+type
+
+  { TCESearchWidget }
+
+  TCESearchWidget = class(TCEWidget)
+    btnFind: TBitBtn;
+    btnReplace: TBitBtn;
+    btnReplaceAll: TBitBtn;
+    cbToFind: TComboBox;
+    cbReplaceWth: TComboBox;
+    chkPrompt: TCheckBox;
+    chkWWord: TCheckBox;
+    chkBack: TCheckBox;
+    chkFromCur: TCheckBox;
+    chkCaseSens: TCheckBox;
+    grpOpts: TGroupBox;
+    imgList: TImageList;
+    procedure cbReplaceWthChange(Sender: TObject);
+    procedure cbToFindChange(Sender: TObject);
+  private
+    fEditor: TCESynMemo;
+    fToFind: string;
+    fReplaceWth: string;
+    fActFindNext, fActReplaceNext: TAction;
+    fActReplaceAll: TAction;
+    fSearchMru, fReplaceMru: TMruList;
+    function getOptions: TSynSearchOptions;
+    procedure actFindNextExecute(sender: TObject);
+    procedure actReplaceAllExecute(sender: TObject);
+    procedure actReplaceNextExecute(sender: TObject);
+    procedure replaceEvent(Sender: TObject; const ASearch, AReplace:
+      string; Line, Column: integer; var ReplaceAction: TSynReplaceAction);
+  protected
+    procedure UpdateByEvent; override;
+  public
+    constructor Create(aOwner: TComponent); override;
+    destructor Destroy; override;
+    //
+    procedure docFocused(const aDoc: TCESynMemo); override;
+    procedure docClose(const aDoc: TCESynMemo); override;
+  end;
+
+implementation
+{$R *.lfm}
+
+constructor TCESearchWidget.Create(aOwner: TComponent);
+begin
+  inherited;
+  fID := 'ID_FIND';
+  //
+  fSearchMru := TMruList.Create;
+  fReplaceMru:= TMruList.Create;
+  //
+  fActFindNext := TAction.Create(self);
+  fActFindNext.Caption := 'Find';
+  fActFindNext.OnExecute := @actFindNextExecute;
+  btnFind.Action := fActFindNext;
+  fActReplaceNext := TAction.Create(self);
+  fActReplaceNext.Caption := 'Replace';
+  fActReplaceNext.OnExecute := @actReplaceNextExecute;
+  btnReplace.Action := fActReplaceNext;
+  fActReplaceAll := TAction.Create(self);
+  fActReplaceAll.Caption := 'Replace all';
+  fActReplaceAll.OnExecute := @actReplaceAllExecute;
+  btnReplaceAll.Action := fActReplaceAll;
+end;
+
+destructor TCESearchWidget.Destroy;
+begin
+  fSearchMru.Free;
+  fReplaceMru.Free;
+  inherited;
+end;
+
+procedure TCESearchWidget.docFocused(const aDoc: TCESynMemo);
+begin
+  fEditor := aDoc;
+  UpdateByEvent;
+end;
+
+procedure TCESearchWidget.docClose(const aDoc: TCESynMemo);
+begin
+  if fEditor = aDoc then fEditor := nil;
+  UpdateByEvent;
+end;
+
+procedure TCESearchWidget.cbToFindChange(Sender: TObject);
+begin
+  if Updating then exit;
+  fToFind := cbToFind.Text;
+end;
+
+procedure TCESearchWidget.cbReplaceWthChange(Sender: TObject);
+begin
+  if Updating then exit;
+  fReplaceWth := cbReplaceWth.Text;
+end;
+
+function TCESearchWidget.getOptions: TSynSearchOptions;
+begin
+  result := [];
+  if chkWWord.Checked then result += [ssoWholeWord];
+  if chkBack.Checked then result += [ssoBackwards];
+  if chkCaseSens.Checked then result += [ssoMatchCase];
+  if chkPrompt.Checked then result += [ssoPrompt];
+end;
+
+procedure TCESearchWidget.actFindNextExecute(sender: TObject);
+begin
+  if fEditor = nil then exit;
+  //
+  fSearchMru.Insert(0,fToFind);
+  if not chkFromCur.Checked then if chkBack.Checked then
+    fEditor.CaretXY := Point(high(Integer), high(Integer)) else
+      fEditor.CaretXY := Point(0,0);
+  fEditor.SearchReplace(fToFind, '', getOptions);
+  UpdateByEvent;
+end;
+
+procedure TCESearchWidget.actReplaceNextExecute(sender: TObject);
+begin
+  if fEditor = nil then exit;
+  //
+  fSearchMru.Insert(0, fToFind);
+  fReplaceMru.Insert(0, fReplaceWth);
+  if chkPrompt.Checked then
+    fEditor.OnReplaceText := @replaceEvent;
+  if not chkFromCur.Checked then if chkBack.Checked then
+    fEditor.CaretXY := Point(high(Integer), high(Integer)) else
+      fEditor.CaretXY := Point(0,0);
+  fEditor.SearchReplace(fToFind, fReplaceWth, getOptions + [ssoReplace]);
+  fEditor.OnReplaceText := nil;
+  UpdateByEvent;
+end;
+
+procedure TCESearchWidget.actReplaceAllExecute(sender: TObject);
+var
+  opts: TSynSearchOptions;
+begin
+  if fEditor = nil then exit;
+  opts := getOptions + [ssoReplace];
+  opts -= [ssoBackwards];
+  //
+  fSearchMru.Insert(0, fToFind);
+  fReplaceMru.Insert(0, fReplaceWth);
+  if chkPrompt.Checked then fEditor.OnReplaceText := @replaceEvent;
+  fEditor.CaretXY := Point(0,0);
+  while(true) do
+  begin
+    if fEditor.SearchReplace(fToFind, fReplaceWth, opts) = 0
+      then break;
+  end;
+  fEditor.OnReplaceText := nil;
+  UpdateByEvent;
+end;
+
+procedure TCESearchWidget.replaceEvent(Sender: TObject; const ASearch, AReplace:
+      string; Line, Column: integer; var ReplaceAction: TSynReplaceAction);
+begin
+  ReplaceAction := raSkip;
+  if dlgOkCancel('Replace this match ?') = mrOK then
+    ReplaceAction := raReplace;
+end;
+
+procedure TCESearchWidget.UpdateByEvent;
+begin
+  fActFindNext.Enabled := fEditor <> nil;
+  fActReplaceNext.Enabled := fEditor <> nil;
+  //
+  cbToFind.Items.Assign(fSearchMru);
+  cbReplaceWth.Items.Assign(fReplaceMru);
+end;
+
+end.
+
diff --git a/src/ce_staticexplorer.lfm b/src/ce_staticexplorer.lfm
index 51492770..6dd5d9c2 100644
--- a/src/ce_staticexplorer.lfm
+++ b/src/ce_staticexplorer.lfm
@@ -34,6 +34,7 @@ inherited CEStaticExplorerWidget: TCEStaticExplorerWidget
         SelectionColor = clActiveBorder
         TabOrder = 0
         OnDeletion = TreeDeletion
+        OnKeyPress = TreeKeyPress
         Options = [tvoAutoItemHeight, tvoKeepCollapsedNodes, tvoReadOnly, tvoRowSelect, tvoShowButtons, tvoShowLines, tvoShowRoot, tvoToolTips, tvoThemedDraw]
         Items.Data = {
           F9FFFFFF020009000000000000000000000000000000FFFFFFFF000000000000
diff --git a/src/ce_staticexplorer.pas b/src/ce_staticexplorer.pas
index cabb1d30..b59718ee 100644
--- a/src/ce_staticexplorer.pas
+++ b/src/ce_staticexplorer.pas
@@ -18,6 +18,7 @@ type
     Tree: TTreeView;
     TreeFilterEdit1: TTreeFilterEdit;
     procedure TreeDeletion(Sender: TObject; Node: TTreeNode);
+    procedure TreeKeyPress(Sender: TObject; var Key: char);
   private
     fActRefresh: TAction;
     fActAutoRefresh: TAction;
@@ -168,6 +169,11 @@ begin
     FreeMem(node.Data)
 end;
 
+procedure TCEStaticExplorerWidget.TreeKeyPress(Sender: TObject; var Key: char);
+begin
+  if Key = #13 then TreeDblClick(nil);
+end;
+
 procedure TCEStaticExplorerWidget.TreeDblClick(Sender: TObject);
 var
   line: Int64;
@@ -178,7 +184,7 @@ begin
   //
   line := PInt64(Tree.Selected.Data)^;
   fDoc.CaretY := line;
-  fDoc.SelectLine();
+  fDoc.SelectLine;
 end;
 
 procedure TCEStaticExplorerWidget.Rescan;