unit ReplacePair;


{
[ReplacePair] [1.1]
Delphi 2005
March 2008


LICENSE

The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
"http://www.mozilla.org/MPL/"

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.

The Original Code is "[ReplacePair.pas / ReplacePair.dfm]".

The Initial Developer of the Original Code is Martin Holmes (Victoria,
BC, Canada, "http://www.mholmes.com/"). Copyright (C) 2005-8 Martin Holmes
and the University of Victoria Computing and Media Centre. The code was 
co-developed for university and personal projects, and rights are shared
by Martin Holmes and the University of Victoria. All Rights Reserved.
}

{
  Written by Martin Holmes, October 2005 - March 2008.

  This unit and associated form constitutes a dialog box used to create and
  edit a "replace pair" (find text and replace text) used in the Transformer
  application. Each replace pair constitutes an item in a TransformItems
  list; when a replace pair is executed, it does a simple search-and-replace
  operation. There is rudimentary handling for regular expressions, but this
  should probably be avoided because the TURESearch object used for regexp
  replacement is a bit buggy, and in any case the script items which are
  also available can handle regexps much better, through the Mozilla SpiderMonkey
  JavaScript engine.

  Dependencies:

 FormState (to save and reload form state, and also to use its AppDirPath
 property).
 TntUnicode libraries (Troy Wolbrink).
 JEDI Code Library (Project JEDI)
}

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, TntForms, TntStdCtrls, Buttons, TntButtons, ExtCtrls,
  TntExtCtrls, FormState, TntDialogs, jclUnicode, AppEvnts;

type
  TufrmReplacePair = class(TTntForm)
    upnTop: TTntPanel;
    umFindThis: TTntMemo;
    ustFindThis: TTntStaticText;
    usplHorizontal: TTntSplitter;
    upnlBottom: TTntPanel;
    ustReplaceWithThis: TTntStaticText;
    umReplaceWithThis: TTntMemo;
    ubnOK: TTntBitBtn;
    ubnCancel: TTntBitBtn;
    ustPairName: TTntStaticText;
    uePairName: TTntEdit;
    ucbIgnoreCase: TTntCheckBox;
    ucbUseRegularExpressions: TTntCheckBox;
    ustmsgRegExpError: TTntStaticText;
    aeReplacePair: TApplicationEvents;
    ustmsgContainsControlChars: TTntStaticText;
    procedure aeReplacePairIdle(Sender: TObject; var Done: Boolean);
    procedure ubnOKClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
    FFormStateSaver: TFormStateSaver;
    function CheckRegularExpression: Boolean;
    function HasNoControlChars: Boolean;
  public
    { Public declarations }
    procedure Clear;
  end;

var
  ufrmReplacePair: TufrmReplacePair;

implementation

{$R *.dfm}

procedure TufrmReplacePair.FormShow(Sender: TObject);
begin
  FFormStateSaver := TFormStateSaver.Create(Self, True);  //To use XML
  uePairName.SetFocus;
end;

procedure TufrmReplacePair.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FFormStateSaver);
end;

procedure TufrmReplacePair.Clear;
begin
  uePairName.Text := '';
  umFindThis.Text := '';
  umReplaceWithThis.Text := '';
  ucbIgnoreCase.Checked := False;
  ucbUseRegularExpressions.Checked := False;
end;

procedure TufrmReplacePair.ubnOKClick(Sender: TObject);
begin
  if (CheckRegularExpression = False) {or (HasNoControlChars = False)} then
    ModalResult := mrNone
  else
    ModalResult := mrOK;
end;

//This function not called, because we're going to handle this problem
//in the file i/o using escapes.
function TufrmReplacePair.HasNoControlChars: Boolean;
var
wsFindThis, wsReplaceWithThis, wsName: WideString;

  function DoCheck(wsInput: WideString): Boolean;
  var
  i: integer;
  begin
    Result := True;
    if Length(wsInput) > 0 then
      for i := 1 to Length(wsInput) do
        if Ord(wsInput[i]) in [$01..$08, $0b..$0c, $0e..$1f, $7f..$84, $86..$9f] then
          begin
            Result := False;
            Break;
          end;
  end;
begin
  Result := True; //default
  wsFindThis := umFindThis.Text;
  wsReplaceWithThis := umReplaceWithThis.Text;
  wsName := uePairName.Text;
  Result := DoCheck(wsFindThis) and DoCheck(wsReplaceWithThis) and DoCheck(wsName);
  if Result = False then
    WideMessageDlg(ustmsgContainsControlChars.Caption, mtWarning, [mbOK], 0);
end;

function TufrmReplacePair.CheckRegularExpression: Boolean;
var
URESearch: TURESearch;
URESearchFlags: TSearchFlags;
wsFindText: WideString;

begin
  Result := False; //default -- be wary because of nasty bugs in TURESearch
  if ucbUseRegularExpressions.Checked = False then
    begin
      Result := True;
      Exit;
    end;
  wsFindText := umFindThis.Text;
  if wsFindText[1] in [WideChar('+'), WideChar('?'), WideChar('*')] then
    begin
      WideMessageDlg(ustmsgRegExpError.Caption, mtWarning, [mbOK], 0);
      Exit;
    end;
  URESearch := TURESearch.Create(nil);
  try
    try
      if ucbIgnoreCase.Checked then
        URESearchFlags := []
      else
        URESearchFlags := [sfCaseSensitive];
      URESearch.FindPrepare(wsFindText, URESearchFlags);
    except
      WideMessageDlg(ustmsgRegExpError.Caption, mtWarning, [mbOK], 0);
      Exit;
    end;
  finally
    FreeAndNil(URESearch);
  end;
  Result := True;
end;

procedure TufrmReplacePair.aeReplacePairIdle(Sender: TObject;
  var Done: Boolean);
begin
  ubnOK.Enabled := (Length(umFindThis.Text) > 0);
end;

end.
