unit UExpressionDialog;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, ecSyntMemo, Buttons, ecSyntAnal, ActnList;

type
  TExpressionDialog = class(TForm)
    ClientPanel: TPanel;
    TestEdit: TSyntaxMemo;
    SyntAnalyzer: TSyntAnalyzer;
    ActionList: TActionList;
    ControlKeyAction: TAction;
    TntSpeedButton1: TSpeedButton;
    TntSpeedButton2: TSpeedButton;
    CapitalKeyAction: TAction;
    AnyCharAction: TAction;
    TntSpeedButton3: TSpeedButton;
    TntLabel1: TLabel;
    RangeOpenAction: TAction;
    RangeCloseAction: TAction;
    IteratorOpenAction: TAction;
    IteratorCloseAction: TAction;
    AlternativeOpenAction: TAction;
    AlternativeCloseAction: TAction;
    OneOrMoreAction: TAction;
    ZeroOrMoreAction: TAction;
    TntSpeedButton4: TSpeedButton;
    TntSpeedButton5: TSpeedButton;
    TntLabel2: TLabel;
    TntLabel3: TLabel;
    TntLabel4: TLabel;
    TntSpeedButton6: TSpeedButton;
    TntSpeedButton7: TSpeedButton;
    TntLabel5: TLabel;
    TntSpeedButton8: TSpeedButton;
    TntSpeedButton9: TSpeedButton;
    TntLabel6: TLabel;
    TntSpeedButton10: TSpeedButton;
    TntLabel7: TLabel;
    ZeroOrOneAction: TAction;
    OrAlternativeAction: TAction;
    StartOfLineAction: TAction;
    EndOfLineAction: TAction;
    AlphaNumericAction: TAction;
    NumericAction: TAction;
    SpaceAction: TAction;
    TntSpeedButton12: TSpeedButton;
    TntLabel9: TLabel;
    TntSpeedButton14: TSpeedButton;
    TntLabel11: TLabel;
    TntSpeedButton15: TSpeedButton;
    TntLabel12: TLabel;
    TntSpeedButton16: TSpeedButton;
    TntLabel13: TLabel;
    TntSpeedButton17: TSpeedButton;
    TntLabel14: TLabel;
    TntSpeedButton18: TSpeedButton;
    TntLabel15: TLabel;
    TntLabel16: TLabel;
    TntSpeedButton19: TSpeedButton;
    TntSpeedButton20: TSpeedButton;
    TntLabel17: TLabel;
    TntLabel18: TLabel;
    ExpressionEdit: TSyntaxMemo;
    TntButton1: TButton;
    TestAnalyzer: TSyntAnalyzer;
    ExactCheckBox: TCheckBox;
    TntLabel19: TLabel;
    ErrorEdit: TEdit;
    procedure AlphaNumericActionExecute(Sender: TObject);
    procedure AlphaNumericActionUpdate(Sender: TObject);
    procedure AlternativeCloseActionExecute(Sender: TObject);
    procedure AlternativeCloseActionUpdate(Sender: TObject);
    procedure AlternativeOpenActionExecute(Sender: TObject);
    procedure AlternativeOpenActionUpdate(Sender: TObject);
    procedure AnyCharActionExecute(Sender: TObject);
    procedure AnyCharActionUpdate(Sender: TObject);
    procedure CapitalKeyActionUpdate(Sender: TObject);
    procedure ControlKeyActionUpdate(Sender: TObject);
    procedure EndOfLineActionExecute(Sender: TObject);
    procedure EndOfLineActionUpdate(Sender: TObject);
    procedure ExpressionEditChange(Sender: TObject);
    procedure ExpressionEditInsertChar(Sender: TObject; C: Char; APos: Integer);
    procedure ExpressionEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure IteratorCloseActionExecute(Sender: TObject);
    procedure IteratorCloseActionUpdate(Sender: TObject);
    procedure IteratorOpenActionExecute(Sender: TObject);
    procedure IteratorOpenActionUpdate(Sender: TObject);
    procedure NumericActionExecute(Sender: TObject);
    procedure NumericActionUpdate(Sender: TObject);
    procedure OneOrMoreActionExecute(Sender: TObject);
    procedure OneOrMoreActionUpdate(Sender: TObject);
    procedure OrAlternativeActionExecute(Sender: TObject);
    procedure OrAlternativeActionUpdate(Sender: TObject);
    procedure RangeCloseActionExecute(Sender: TObject);
    procedure RangeCloseActionUpdate(Sender: TObject);
    procedure RangeOpenActionExecute(Sender: TObject);
    procedure RangeOpenActionUpdate(Sender: TObject);
    procedure SpaceActionExecute(Sender: TObject);
    procedure SpaceActionUpdate(Sender: TObject);
    procedure StartOfLineActionExecute(Sender: TObject);
    procedure StartOfLineActionUpdate(Sender: TObject);
    procedure ZeroOrMoreActionExecute(Sender: TObject);
    procedure ZeroOrMoreActionUpdate(Sender: TObject);
    procedure ZeroOrOneActionExecute(Sender: TObject);
    procedure ZeroOrOneActionUpdate(Sender: TObject);
    procedure ExpressionEditBeforeDelete(Sender: TObject; var Pos,
      Count: Integer; var Accept: Boolean);
    procedure ExpressionEditBeforeInsert(Sender: TObject; var Pos: Integer;
      var S: String; var Accept: Boolean);
    procedure ExpressionEditSelectionChanged(Sender: TObject);
  private
    FShift: TShiftState;
    function IsBackSlash(Pos: integer): Boolean;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ExpressionDialog: TExpressionDialog;

implementation

{$R *.DFM}

{  to be implemented !!!!!!

procedure TRegExprEdit.DoAdjustInsertPos(var Pos: Integer);
begin
  inherited;
  if (Pos > 0) and (Text[Pos] = '\') then
    Dec(Pos);
end;

}

function CapitalKeyDown: Boolean;
begin
  Result := GetKeyState(VK_CAPITAL) and $01 <> 0;
end;

function CtrlKeyDown: Boolean;
var
  KeyState: Integer;
begin
  KeyState := GetKeyState(VK_CONTROL);
  Result := KeyState and $80 = $80;
end;

procedure TExpressionDialog.AlphaNumericActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('\w');
end;

procedure TExpressionDialog.AlphaNumericActionUpdate(Sender: TObject);
begin
  AlphaNumericAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.AlternativeCloseActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText(')');
end;

procedure TExpressionDialog.AlternativeCloseActionUpdate(Sender: TObject);
begin
  AlternativeCloseAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.AlternativeOpenActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('(');
end;

procedure TExpressionDialog.AlternativeOpenActionUpdate(Sender: TObject);
begin
  AlternativeOpenAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.AnyCharActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('.');
end;

procedure TExpressionDialog.AnyCharActionUpdate(Sender: TObject);
begin
  AnyCharAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.CapitalKeyActionUpdate(Sender: TObject);
begin
  CapitalKeyAction.Checked := CapitalKeyDown;
  CapitalKeyAction.Enabled := True;
end;

procedure TExpressionDialog.ControlKeyActionUpdate(Sender: TObject);
begin
  ControlKeyAction.Checked := CtrlKeyDown;
  ControlKeyAction.Enabled := True;
end;

procedure TExpressionDialog.EndOfLineActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('$');
end;

procedure TExpressionDialog.EndOfLineActionUpdate(Sender: TObject);
begin
  EndOfLineAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.ExpressionEditChange(Sender: TObject);
begin
  try
    if ExactCheckBox.Checked then
      TestAnalyzer.TokenRules[0].Expression := '(?-ir)' + ExpressionEdit.Text
    else
      TestAnalyzer.TokenRules[0].Expression := '(?ir)' + ExpressionEdit.Text;
    ErrorEdit.Text := '';
  except
    on E: Exception do
      ErrorEdit.Text := E.Message
  end;
end;

procedure TExpressionDialog.ExpressionEditInsertChar(Sender: TObject; C: Char; APos: Integer);
begin
  case C of
    '^',
    '(',
      ')',
      '[',
      ']',
      '{',
      '}',
      '+',
      '-',
      '.',
      '*',
      '?',
      '$': if not (ssCtrl in FShift) then
      begin
        ExpressionEdit.ReplaceText(APos, 1, '\' + C);
        ExpressionEdit.CaretPos :=
          Point(ExpressionEdit.CaretPos.X + 1, ExpressionEdit.CaretPos.Y);
      end;
      '|':
      begin
        ExpressionEdit.ReplaceText(APos, 1, '\' + C);
        ExpressionEdit.CaretPos :=
          Point(ExpressionEdit.CaretPos.X + 1, ExpressionEdit.CaretPos.Y);
      end;
  end;
end;

procedure TExpressionDialog.ExpressionEditKeyDown(Sender: TObject; var Key:
    Word; Shift: TShiftState);
var
  StrPos: Integer;
begin
  FShift := Shift;
  if [ssShift, ssCtrl] * Shift = [ssShift, ssCtrl] then
  begin
    case Key of
      52:
        ExpressionEdit.InsertText('$');
      56:
        ExpressionEdit.InsertText('(');
      57:
        ExpressionEdit.InsertText(')');
      65:
        ExpressionEdit.InsertText('\A');
      66:
        ExpressionEdit.InsertText('\B');
      67:
        ExpressionEdit.InsertText('\C');
      68:
        ExpressionEdit.InsertText('\D');
      71:
        ExpressionEdit.InsertText('\G');
      72:
        ExpressionEdit.InsertText('\H');
      75:
        ExpressionEdit.InsertText('\K');
      76:
        ExpressionEdit.InsertText('\L');
      83:
        ExpressionEdit.InsertText('\S');
      87:
        ExpressionEdit.InsertText('\W');
      88:
        ExpressionEdit.InsertText('\X');
      90:
        ExpressionEdit.InsertText('\Z');
      187:
        ExpressionEdit.InsertText('*');
      219:
        ExpressionEdit.InsertText('?');
    end;
  end
  else if ssCtrl in Shift then
  begin
    case Key of
      65:
        ExpressionEdit.InsertText('\a');
      66:
        ExpressionEdit.InsertText('\b');
      67:
        ExpressionEdit.InsertText('\c');
      68:
        ExpressionEdit.InsertText('\d');
      69:
        ExpressionEdit.InsertText('\e');
      70:
        ExpressionEdit.InsertText('\f');
      71:
        ExpressionEdit.InsertText('\g');
      72:
        ExpressionEdit.InsertText('\h');
      75:
        ExpressionEdit.InsertText('\k');
      76:
        ExpressionEdit.InsertText('\l');
      78:
        ExpressionEdit.InsertText('\n');
      82:
        ExpressionEdit.InsertText('\r');
      83:
        ExpressionEdit.InsertText('\s');
      84:
        ExpressionEdit.InsertText('\t');
      87:
        ExpressionEdit.InsertText('\w');
      88:
        ExpressionEdit.InsertText('\x');
      187:
        ExpressionEdit.InsertText('+');
      189:
        ExpressionEdit.InsertText('-');
      190:
        ExpressionEdit.InsertText('.');
      220:
        ExpressionEdit.InsertText('^');
      226: if not(ssAlt in Shift) then
        ExpressionEdit.InsertText('|');
    end;
  end
  else
  case Key of
     220:
       ExpressionEdit.InsertText('\^');
{    VK_DELETE:
      begin
        StrPos := ExpressionEdit.CaretStrPos;
        if (StrPos > 0) and (Text[StrPos] = '\') then
          ExpressionEdit.DeleteText(-1);
      end;
    VK_BACK:
      begin
        StrPos := ExpressionEdit.CaretStrPos;
        if (StrPos > 1) and (Text[StrPos - 1] = '\') then
          ExpressionEdit.DeleteText(-1);
      end;}
  end;
end;

procedure TExpressionDialog.IteratorCloseActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('}');
end;

procedure TExpressionDialog.IteratorCloseActionUpdate(Sender: TObject);
begin
  IteratorCloseAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.IteratorOpenActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('{');
end;

procedure TExpressionDialog.IteratorOpenActionUpdate(Sender: TObject);
begin
  IteratorOpenAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.NumericActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('\d');
end;

procedure TExpressionDialog.NumericActionUpdate(Sender: TObject);
begin
  NumericAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.OneOrMoreActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('+');
end;

procedure TExpressionDialog.OneOrMoreActionUpdate(Sender: TObject);
begin
  OneOrMoreAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.OrAlternativeActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('|');
end;

procedure TExpressionDialog.OrAlternativeActionUpdate(Sender: TObject);
begin
  OrALternativeAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.RangeCloseActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText(']');
end;

procedure TExpressionDialog.RangeCloseActionUpdate(Sender: TObject);
begin
  RangeCloseAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.RangeOpenActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('[');
end;

procedure TExpressionDialog.RangeOpenActionUpdate(Sender: TObject);
begin
  RangeOpenAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.SpaceActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('\s');
end;

procedure TExpressionDialog.SpaceActionUpdate(Sender: TObject);
begin
  SpaceAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.StartOfLineActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('^');
end;

procedure TExpressionDialog.StartOfLineActionUpdate(Sender: TObject);
begin
  StartOfLineAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.ZeroOrMoreActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('*');
end;

procedure TExpressionDialog.ZeroOrMoreActionUpdate(Sender: TObject);
begin
  ZeroOrMoreAction.Checked := CtrlKeyDown;
end;

procedure TExpressionDialog.ZeroOrOneActionExecute(Sender: TObject);
begin
  ExpressionEdit.InsertText('?');
end;

procedure TExpressionDialog.ZeroOrOneActionUpdate(Sender: TObject);
begin
  ZeroOrOneAction.Checked := CtrlKeyDown;
end;

function TExpressionDialog.IsBackSlash(Pos: integer): Boolean;
begin
  Pos := ExpressionEdit.SyntObj.TokenAtPos(Pos);
  Result := (Pos <> -1) and (ExpressionEdit.SyntObj.Tags[Pos].TokenType = 0);
end;

procedure TExpressionDialog.ExpressionEditBeforeDelete(Sender: TObject;
  var Pos, Count: Integer; var Accept: Boolean);
begin
  if IsBackSlash(Pos + Count - 1) then
    if (Pos = ExpressionEdit.CaretStrPos) and (Count = 1) then
     Inc(Count)
    else
  if Count = 1 then
    Dec(Pos)
  else
    Dec(Count);

  if IsBackSlash(Pos - 1) then
    begin
      Dec(Pos);
      Inc(Count);
    end;
end;

procedure TExpressionDialog.ExpressionEditBeforeInsert(Sender: TObject;
  var Pos: Integer; var S: String; var Accept: Boolean);
begin
  if IsBackSlash(Pos - 1) then
    Dec(Pos);
end;

var SelfSelChange: Boolean = False;
procedure TExpressionDialog.ExpressionEditSelectionChanged(
  Sender: TObject);
var Pos, Count: integer;
begin
  if SelfSelChange then Exit;
  Pos := ExpressionEdit.SelStart;
  Count := ExpressionEdit.SelLength;
  if IsBackSlash(Pos + Count - 1) then
    Dec(Count);
  if IsBackSlash(Pos - 1) then
    begin
      Dec(Pos);
      Inc(Count);
    end;
  SelfSelChange := True;
  try
    ExpressionEdit.SetSelection(Pos, Count, True);
  finally
    SelfSelChange := False;
  end;
end;

end.

