/// unit zaoberajuci sa zobrazovanim editovacich okien pre gramatiku a vstupne subory
unit grammaredit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ToolWin, ImgList, constants, log, main, ExtCtrls,
  Menus;

const
      EM_EXLINEFROMCHAR	= WM_USER + 54;
      EM_SETUNDOLIMIT = WM_USER + 82;
      EM_REDO	= WM_USER + 84;
      EM_CANREDO = WM_USER + 85;
      EM_GETUNDONAME = WM_USER + 86;
      EM_GETREDONAME = WM_USER + 87;
      EM_STOPGROUPTYPING = WM_USER + 88;
      EM_SETTEXTMODE = WM_USER + 89;
      EM_GETTEXTMODE = WM_USER + 90;

      TM_PLAINTEXT = 1;
      TM_RICHTEXT	= 2;
      TM_SINGLELEVELUNDO = 4;
      TM_MULTILEVELUNDO	= 8;
      TM_SINGLECODEPAGE	= 16;
      TM_MULTICODEPAGE = 32;

type
  //trieda zobrazujuca okno s textovym editorom
  TGrammarEditForm = class(TForm)
    RichEdit1: TRichEdit;
    StatusBar1: TStatusBar;
    ToolBar1: TToolBar;
    FindDialog1: TFindDialog;
    ReplaceDialog1: TReplaceDialog;
    SaveDialog1: TSaveDialog;
    FindTB: TToolButton;
    ReplaceTB: TToolButton;
    OpenDialog1: TOpenDialog;
    UndoTB: TToolButton;
    RedoTB: TToolButton;
    CutTB: TToolButton;
    CopyTB: TToolButton;
    PasteTB: TToolButton;
    ToolButton6: TToolButton;
    ToolButton7: TToolButton;
    OpenTB: TToolButton;
    SaveTB: TToolButton;
    ToolButton10: TToolButton;
    ImageList1: TImageList;
    NewTB: TToolButton;
    EnterGrammarButton: TButton;
    ToolButton1: TToolButton;
    ListBox1: TListBox;
    Splitter1: TSplitter;
    ExtraButton: TButton;
    procedure ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure StatusBar1DblClick(Sender: TObject);
    procedure FormDeactivate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure ListBox1DblClick(Sender: TObject);
    procedure EnterGrammarButtonClick(Sender: TObject);
    procedure SaveTBClick(Sender: TObject);
    procedure OpenTBClick(Sender: TObject);
    procedure NewTBClick(Sender: TObject);
    procedure PasteTBClick(Sender: TObject);
    procedure CopyTBClick(Sender: TObject);
    procedure CutTBClick(Sender: TObject);
    procedure RedoTBClick(Sender: TObject);
    procedure UndoTBClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ReplaceDialog1Close(Sender: TObject);
    procedure ReplaceDialog1Replace(Sender: TObject);
    procedure ReplaceDialog1Find(Sender: TObject);
    procedure FindDialog1Close(Sender: TObject);
    procedure FindDialog1Find(Sender: TObject);
    procedure ReplaceTBClick(Sender: TObject);
    procedure FindTBClick(Sender: TObject);
    procedure RichEdit1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure RichEdit1KeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormCreate(Sender: TObject);
  private
    function GetCaretPos(handle: Integer): Longint;
    function GetCaretSEPos(handle: Integer): TPoint;
    function GetCaretXYPos(handle: Integer): TPoint;
    function LineToPos(R, handle: Integer): Integer;
    function PosToXY(P, handle: Integer): TPoint;
    procedure AdjustCaretProperties;
    procedure FreeStructures;
    function ReloadConfirm(Sender: TObject): Boolean;
    procedure FileAgeCheck(Sender: TObject);
    { Private declarations }
  public
    FInsertMode: Boolean;
    FLoadedPath: xString;
    FAge: Integer;
    Flog: TLog;
    FGFL: TGrammarFileLexer;
    FDFA: TDFA;
    FParser: TParser;
    FIsGrammar: Boolean;
    FRoot: PToken;
    FTreeSize: Integer;
    bPBDone: Boolean;
    procedure CaretGotoPos(X: Integer);
    procedure CaretGotoLine(X: Integer);
//    procedure InsertTextAtPos(S: xString; pos: Integer; Sender: TObject);
    procedure InternalOpen(Sender: TObject; Path: xString);
    function LooseChangesConfirm(Sender: TObject): Boolean;
    constructor createNamed(owner: TComponent; name: TCaption; isGrammar: Boolean);
    procedure BuildParseTreeButtonClick(Sender: TObject);
    procedure BuildParserButtonClick(Sender: TObject);
    procedure ButtonsEnabling;
    { Public declarations }
  end;

var
  GrammarEditForm: TGrammarEditForm;

implementation

{$R *.dfm}

uses filereader,mainframe,richedit,treedist,tableframe, LALRtable, DFAtable,
  filediff;

(*
procedure SetSelBgColor(RichEdit: TRichEdit; AColor: TColor);
var Format: CHARFORMAT2;
begin
  FillChar(Format, SizeOf(Format), 0);
  with Format do begin
    cbSize := SizeOf(Format);
    dwMask := CFM_BACKCOLOR;
    crBackColor := AColor;
    Richedit.Perform(EM_SETCHARFORMAT, SCF_SELECTION, Longint(@Format));
  end;
end;
*)

(*
procedure TGrammarEditForm.InsertTextAtPos(S: xString; pos: Integer; Sender: TObject);
var Key: Word;
    CaretPos: TPoint;
begin
  CaretPos := GetCaretSEPos(RichEdit1.Handle);
  if CaretPos.x <> CaretPos.y then begin
    SendMessage(RichEdit1.handle,WM_KEYDOWN,VK_DELETE,1);
    Key := VK_DELETE;
    RichEdit1KeyUp(Sender, Key, []);
  end;
  RichEdit1.SelText := S;
//  CaretPos.x := CaretPos.x + 9;
//  CaretPos.y := CaretPos.y + 9;
  RichEdit1.SelStart := CaretPos.x;
  RichEdit1.SelLength := 0;
//  CaretDelta := 12;
  Key := 0;
  RichEdit1KeyUp(Sender, Key, []);
end;
*)

/// funkcia vracajuca poziciu v tvare riadok,stlpec zo vstupnej pozicie P
function TGrammarEditForm.PosToXY(P,handle: Longint): TPoint;
var C,R: Longint;
begin
  R := SendMessage(handle, EM_EXLINEFROMCHAR, 0, P);
  C := P - SendMessage(handle, EM_LINEINDEX, R, 0);
  result.x := C;
  result.y := R;
end;

/// funkcia vracajuca poziciu zaciatku daneho riadku R
function TGrammarEditForm.LineToPos(R,handle: Integer): Integer;
begin
  result := SendMessage(handle, EM_LINEINDEX, R, 0);
end;

/// funkcia zistujuca poziciu kurzora
function TGrammarEditForm.GetCaretPos(handle: Longint): Longint;
var S,E: Longint;
begin
  SendMessage(handle, EM_GETSEL, Longint(@S), Longint(@E));

  if S <> E then result := -1
  else result := S;
end;

/// funkcia zistujuca poziciu vyberu v editore v suradnici x je pozicia zaciatku, v y je pozicia konca
function TGrammarEditForm.GetCaretSEPos(handle: Longint): TPoint;
var S,E: Longint;
begin
  SendMessage(handle, EM_GETSEL, Longint(@S), Longint(@E));

  result.x := S;
  result.y := E;
end;

/// funkcia zistujuca poziciu kurzora v tvare riadok,stlpec
function TGrammarEditForm.GetCaretXYPos(handle: Longint): TPoint;
var X,Y: Longint;
    S,E: Longint;
begin
  SendMessage(handle, EM_GETSEL, Longint(@S), Longint(@E));

  if S < E then X := S
  else X := E;

  Y := SendMessage(handle, EM_LINEFROMCHAR, -1, 0);
  X := X - SendMessage(handle, EM_LINEINDEX, -1, 0);

  result.x := X;
  result.y := Y;

//  Caption := IntToStr(X) + ' , ' + IntToStr(Y) + '|' + IntToStr(S) + ' , ' + IntToStr(E) + ' of ' + IntToStr(RichEdit1.MaxLength);
end;

/// funkcia zobrazujuca poziciu kurzora a mod kurzora (insert/overwrite) a pod. v statusbare
procedure TGrammarEditForm.AdjustCaretProperties;  //!TEXT!
var X: TPoint;
begin
  X := GetCaretXYPos(RichEdit1.Handle);

  StatusBar1.Panels[0].Text := IntToStr(X.y) + ' : ' + IntToStr(X.x);
  if FInsertMode then
    StatusBar1.Panels[1].Text := 'Insert'
  else
    StatusBar1.Panels[1].Text := 'Overwrite';
  if RichEdit1.Modified then begin
    StatusBar1.Panels[2].Text := 'Modified';
  end;
  if (Flog <> nil) and (Flog.getNumOfErrors + Flog.getNumOfWarnings > 0) then begin
    StatusBar1.Panels[3].Text := 'Grammar Processing - Errors: ' + IntToStr(Flog.getNumOfErrors) + ' Warnings: ' + IntToStr(Flog.getNumOfWarnings);
  end
  else begin
    StatusBar1.Panels[3].Text := '';
  end;

  if ListBox1.Count > 0 then begin
    if ListBox1.Height <= 0 then
      ListBox1.Height := 50;
  end
  else
    ListBox1.Height := 0;
end;

/// procedura na presun kurzora na poziciu X
procedure TGrammarEditForm.CaretGotoPos(X: Longint);
begin
  RichEdit1.SelStart := X;
  RichEdit1.SelLength := 0;
  RichEdit1.SetFocus;
  AdjustCaretProperties;
  SendMessage(RichEdit1.handle,EM_SCROLLCARET,0,0);
end;

/// procedura na presun kurzora na riadok X
procedure TGrammarEditForm.CaretGotoLine(X: Longint);
begin
  X := LineToPos(X,RichEdit1.handle);
  RichEdit1.SelStart := X;
  RichEdit1.SelLength := 0;
  RichEdit1.SetFocus;
  AdjustCaretProperties;
  SendMessage(RichEdit1.handle,EM_SCROLLCARET,0,0);
end;

/// procedura obsluhujuca stlacenie tlacidla pre vyhladavanie textu, zobrazuje Find Dialog
procedure TGrammarEditForm.FindTBClick(Sender: TObject);
begin
//  FindDialog1 := TFindDialog.Create(self);
  FindDialog1.Execute(RichEdit1.handle);
end;

/// procedura obsluhujuca stlacenie tlacidla find vo Find Dialogu
procedure TGrammarEditForm.FindDialog1Find(Sender: TObject);  //!TEXT!
var S: TSearchTypes;
    I: Integer;
begin
  if (frMatchCase in FindDialog1.Options) then
    S := S + [stMatchCase];
  if (frWholeWord in FindDialog1.Options) then
    S := S + [stWholeWord];
  RichEdit1.HideSelection := False;
  if FindDialog1.Tag = -1 then
    I := RichEdit1.FindText(FindDialog1.FindText,RichEdit1.SelStart,Length(RichEdit1.Text),S)
  else
    I := RichEdit1.FindText(FindDialog1.FindText,FindDialog1.Tag,Length(RichEdit1.Text),S);
  FindDialog1.Tag := I + Length(FindDialog1.FindText);
  if I >= 0 then begin
    RichEdit1.SelStart := I;
    RichEdit1.SelLength := Length(FindDialog1.FindText);
  end
  else begin
    MessageDlg('Search string ''' + FindDialog1.FindText + ''' not found',mtInformation,[mbOK],0);
  end;
end;

/// procedura obsluhujuca zavretie Find Dialogu
procedure TGrammarEditForm.FindDialog1Close(Sender: TObject);
begin
  RichEdit1.HideSelection := True;
  FindDialog1.Tag := -1;
  RichEdit1.SetFocus;
  //FindDialog1.CloseDialog;// := TFindDialog.Create(self);
//  FindDialog1.Free;
end;

/// procedura obsluhujuca stlacenie tlacidla pre vyhladavanie a prepisovanie textu, zobrazi sa Replace Dialog
procedure TGrammarEditForm.ReplaceTBClick(Sender: TObject);
begin
  ReplaceDialog1.Execute;
end;

/// procedura obsluhujuca stlacenie tlacidla Find v Replace Dialogu
procedure TGrammarEditForm.ReplaceDialog1Find(Sender: TObject);  //!TEXT!
var S: TSearchTypes;
    I: Integer;
begin
  if (frMatchCase in ReplaceDialog1.Options) then
    S := S + [stMatchCase];
  if (frWholeWord in ReplaceDialog1.Options) then
    S := S + [stWholeWord];
  RichEdit1.HideSelection := False;
  if ReplaceDialog1.Tag = -1 then
    I := RichEdit1.FindText(ReplaceDialog1.FindText,RichEdit1.SelStart,Length(RichEdit1.Text),S)
  else
    I := RichEdit1.FindText(ReplaceDialog1.FindText,ReplaceDialog1.Tag,Length(RichEdit1.Text),S);
  ReplaceDialog1.Tag := I + Length(ReplaceDialog1.FindText);
  if I >= 0 then begin
    RichEdit1.SelStart := I;
    RichEdit1.SelLength := Length(ReplaceDialog1.FindText);
  end
  else begin
    MessageDlg('Search string ''' + ReplaceDialog1.FindText + ''' not found',mtInformation,[mbOK],0);
  end;
end;

/// procedura obsluhujuca stlacenie tlacidla Replace v Replace Dialogu
procedure TGrammarEditForm.ReplaceDialog1Replace(Sender: TObject);  //!TEXT!
var S: TSearchTypes;
    I: Integer;
begin
  if not (frReplaceAll in ReplaceDialog1.Options) then begin
    if RichEdit1.SelLength <> 0 then begin
      RichEdit1.SelText := ReplaceDialog1.ReplaceText;
    end;
  end
  else begin
    if (frMatchCase in ReplaceDialog1.Options) then
      S := S + [stMatchCase];
    if (frWholeWord in ReplaceDialog1.Options) then
      S := S + [stWholeWord];
    RichEdit1.HideSelection := False;
    repeat
      if ReplaceDialog1.Tag = -1 then
        I := RichEdit1.FindText(ReplaceDialog1.FindText,RichEdit1.SelStart,Length(RichEdit1.Text),S)
      else
        I := RichEdit1.FindText(ReplaceDialog1.FindText,ReplaceDialog1.Tag,Length(RichEdit1.Text),S);
      ReplaceDialog1.Tag := I + Length(ReplaceDialog1.ReplaceText);
      if I >= 0 then begin
        RichEdit1.SelStart := I;
        RichEdit1.SelLength := Length(ReplaceDialog1.FindText);
        RichEdit1.SelText := ReplaceDialog1.ReplaceText;
      end
      else begin
//          if not (frReplaceAll in ReplaceDialog1.Options) then
//            MessageDlg('To be replaced string ''' + ReplaceDialog1.FindText + ''' not found',mtInformation,[mbOK],0);
      end;
    until I < 0;
  end;
end;

/// procedura obsluhujuca zavretie Replace Dialogu
procedure TGrammarEditForm.ReplaceDialog1Close(Sender: TObject);
begin
  RichEdit1.HideSelection := True;
  ReplaceDialog1.Tag := -1;
  RichEdit1.SetFocus;
end;


/// procedura obsluhujuca stlacenie Undo tlacidla v toolbare
procedure TGrammarEditForm.UndoTBClick(Sender: TObject);
begin
  RichEdit1.Undo;
end;

/// procedura obsluhujuca stlacenie Redo tlacidla v toolbare
procedure TGrammarEditForm.RedoTBClick(Sender: TObject);
var P: PCHAR;
    H: HWND;
    R: Integer;
begin
  RichEdit1.Undo;
{  P := '';
  H := RichEdit1.Handle;
  SendMessage(H, WM_SETTEXT, 0, Longint(P));  //just to make sure, the text is ''

//  R := SendMessage(H, EM_SETTEXTMODE, 0, TM_RICHTEXT + TM_MULTILEVELUNDO + TM_MULTICODEPAGE);
  R := SendMessage(H, EM_GETTEXTMODE, 0, 0);
//  R := SendMessage(H, EM_SETUNDOLIMIT, 2, 0);
  Caption := 'RichEdit error: ' + IntToStr(R);}
end;

/// procedura obsluhujuca stlacenie Copy tlacidla v toolbare
procedure TGrammarEditForm.CopyTBClick(Sender: TObject);
var Key: Word;
begin
  RichEdit1.CopyToClipBoard;
  Key := 0;
  RichEdit1KeyUp(Sender, Key, []);
end;

/// procedura obsluhujuca stlacenie Cut tlacidla v toolbare
procedure TGrammarEditForm.CutTBClick(Sender: TObject);
var Key: Word;
begin
  RichEdit1.CutToClipBoard;
  Key := 0;
  RichEdit1KeyUp(Sender, Key, []);
end;

/// procedura obsluhujuca stlacenie Paste tlacidla v toolbare
procedure TGrammarEditForm.PasteTBClick(Sender: TObject);
var Key: Word;
begin
  RichEdit1.PasteFromClipBoard;
  Key := 0;
  RichEdit1KeyUp(Sender, Key, []);
end;

(*
procedure TGrammarEditForm.DeleteTBClick(Sender: TObject);
var Key: Word;
begin
  RichEdit1.ClearSelection;
  Key := 0;
  RichEdit1KeyUp(Sender, Key, []);
end;

procedure TGrammarEditForm.SelectAllTBClick(Sender: TObject);
var Key: Word;
begin
  RichEdit1.SelectAll;
  Key := 0;
  RichEdit1KeyUp(Sender, Key, []);
end;
*)

/// procedura, ktora enabluje a disabluje tlacidla enter grammer, build parser a build parse tree, podla
/// toho v akej faze budovania parseru sme
procedure TGrammarEditForm.ButtonsEnabling;
var editor: TGrammarEditForm;
begin
  if FIsGrammar then begin
    EnterGrammarButton.Caption := 'Enter &Grammar';
    EnterGrammarButton.OnClick := EnterGrammarButtonClick;
    EnterGrammarButton.Enabled := RichEdit1.Lines <> nil;
    ExtraButton.Caption := '&Build Parser';
    ExtraButton.OnClick := BuildParserButtonClick;
    ExtraButton.Enabled := (FGFL <> nil) and (not bPBDone);
    MainForm.EnterGrammar1.Enabled := EnterGrammarButton.Enabled;
    MainForm.BuildParser1.Enabled := ExtraButton.Enabled;
  end
  else begin
    EnterGrammarButton.Caption := '&Build Parse Tree';
    EnterGrammarButton.OnClick := BuildParseTreeButtonClick;
    EnterGrammarButton.Enabled := GrammarEditForm.FParser <> nil;
    MainForm.BuildParseTree1.Enabled := EnterGrammarButton.Enabled;
    ExtraButton.Hide;
  end;
(*    MainForm.BuildParseTree1.Enabled := EnterGrammarButton.Enabled;
    MainForm.BuildParseTree1.Visible := false;
    MainForm.BuildParseTree1.Visible := EnterGrammarButton.Visible and (not FIsGrammar);
    MainForm.BuildParseTree1.Visible := false;*)
(*  MainForm.BuildParser1.Visible := ExtraButton.Visible;
  MainForm.EnterGrammar1.Enabled := EnterGrammarButton.Enabled;
  MainForm.BuildParser1.Enabled := ExtraButton.Enabled;
  MainForm.EnterGrammar1.Caption := EnterGrammarButton.Caption;
  MainForm.BuildParser1.Caption := ExtraButton.Caption;*)
end;

/// procedura inicializujuca editovacie okno
procedure TGrammarEditForm.FormCreate(Sender: TObject);
begin
  FInsertMode := true;
  FLoadedPath := '';
  FAge := 0;
  FLog := nil;
  FGFL := nil;
  FDFA := nil;
  FParser := nil;
  FRoot := nil;

(*  with RichEdit1 do begin
    Font.Pitch := fpFixed;
    Font.Name := 'Courier New';
    Font.Size := 10;
    Canvas.Font.Assign(Font);
  end;*)

  ButtonsEnabling;

  SendMessage(ListBox1.Handle, LB_SetHorizontalExtent, 5000, 0);
end;

/// procedura uvolnujuca pamat tesne pred uvolnenim objektu z pamate
procedure TGrammarEditForm.FormDestroy(Sender: TObject);
begin
  FindDialog1.Free;
  ReplaceDialog1.Free;
  FreeStructures;
end;

/// procedura obsluhujuca stlacenia klaves v editore, sledujuca hlavne ci
/// sa nezmenil stav kurzora s vkladajuceho na prepisujuci a naopak (insert/overwrite)
procedure TGrammarEditForm.RichEdit1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key = VK_INSERT) and (not ((ssShift in Shift) or (ssAlt in Shift) or (ssCtrl in Shift))) then begin
    FInsertMode := not FInsertMode;
  end
  else if (Key = $46{F}) and (ssCtrl in Shift) then begin
    FindTBClick(Sender);
  end
  else if (Key = $52{R}) and (ssCtrl in Shift) then begin
    ReplaceTBClick(Sender);
  end;

  AdjustCaretProperties;
end;


/// procedura obsluhujuca kliknutia mysou v editore
procedure TGrammarEditForm.RichEdit1MouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  AdjustCaretProperties;
end;

/// funkcia vyzadujuca si potvrdenie, ak chceme nacitat zo subora a pritom
/// uz mame nacitany nejaky iny subor, v ktorom sme spravili zmeny,
/// funkcia ponukne dialog s moznostami ulozit zmenu, neulozit zmenu a zrusit operaciu
function TGrammarEditForm.LooseChangesConfirm(Sender: TObject): Boolean;  //!TEXT!
var Response: Integer;
begin
  result := true;
  if RichEdit1.Modified then begin
    if FLoadedPath <> '' then
      Response := MessageDlg('Save changes to ' + FLoadedPath + ' ?',mtInformation,mbYesNoCancel,0)
    else
      Response := MessageDlg('Save changes to (new file) ?',mtInformation,mbYesNoCancel,0);
    case Response of
         mrYes: begin
                  SaveTBClick(Sender);
                  result := true;
                end;
          mrNo: begin
                  result := true;
                end;
      mrCancel: begin
                  result := false;
                end;
    end;
  end;
end;

/// funkcia vyzadujuca si potvrdenie, ci chceme nacitat znova zo suboru, ktory bol externe zmeneny
/// odkedy sme ho posledne nacitali, funkcia ponukne dialog s moznostami znova nacitat, nenacitat a zrusit dialog (opyta sa znova neskor)
function TGrammarEditForm.ReloadConfirm(Sender: TObject): Boolean;  //!TEXT!
var Response: Integer;
begin
  result := true;
    Response := MessageDlg('File ' + FLoadedPath + ' has been altered, outside of this application. Reload ?',mtInformation,mbYesNoCancel,0);
    case Response of
         mrYes: begin
                  if RichEdit1.Modified then begin
                    result := LooseChangesConfirm(Sender);
                  end;
                end;
          mrNo: begin
                  FAge := FileAge(FLoadedPath);
                  result := false;
                end;
      mrCancel: begin
                  result := false;
                end;
    end;
end;

/// procedura uvolnujuca z pamate alokovane struktury
procedure TGrammarEditForm.FreeStructures;
begin
  if FIsGrammar then begin
    if FGFL <> nil then begin
      FGFL.Free;
      FGFL := nil;
    end;
    if FDFA <> nil then begin
      FDFA.Free;
      FDFA := nil;
    end;
    if FParser <> nil then begin
      FParser.Free;
      FParser := nil;
    end;
  end
  else begin
    if FRoot <> nil then begin
      freeSubTree(FRoot);
      FRoot := nil;
    end;
  end;
  ListBox1.Clear;
  if Flog <> nil then begin
    Flog.Free;
    Flog := nil;
  end;
end;

/// procedura na nacitanie zo subora s cestou path
procedure TGrammarEditForm.InternalOpen(Sender: TObject; Path: xString);
var Key: Word;
begin
  FreeStructures;
  if Path <> '' then begin
    RichEdit1.PlainText := True;
    RichEdit1.Lines.LoadFromFile(Path);
    RichEdit1.PlainText := False;
    FAge := fileAge(Path);
  end;
  FLoadedPath := Path;
  Caption := FLoadedPath;
  RichEdit1.Modified := false;
  if FDisableCount = 0 then begin
    ButtonsEnabling;
    LForm.ButtonsEnabling;
    RForm.ButtonsEnabling;
  end;
  Key := 0;
  RichEdit1KeyUp(Sender, Key, []);
end;

/// procedura na nacitanie zo subora, otvori dialog, v ktorom sa da vybrat cesta
procedure TGrammarEditForm.OpenTBClick(Sender: TObject);  //!TEXT!
var I,F: Integer;
begin
  if not LooseChangesConfirm(Sender) then
    Exit;
  if FIsGrammar then begin
    OpenDialog1.Title := 'Load Grammar File';
    OpenDialog1.DefaultExt := 'grm';
    OpenDialog1.Filter := 'Grammar Files (*.grm)|*.grm|Other Files (*.*)|*.*';
  end
  else begin
    OpenDialog1.Title := 'Load Input File';
    OpenDialog1.Filter := 'Text Files (*.*)|*.*';
  end;
  if OpenDialog1.Execute then begin
    InternalOpen(Sender,OpenDialog1.FileName);
  end;
end;

/// procedura na ulozenie textu v editore do subora, otvori dialog, v ktorom sa da vybrat cesta
procedure TGrammarEditForm.SaveTBClick(Sender: TObject);  //!TEXT!
begin
  SaveDialog1.FileName := FLoadedPath;
  if FIsGrammar then begin
    SaveDialog1.Title := 'Save Grammar File';
    SaveDialog1.DefaultExt := 'grm';
    SaveDialog1.Filter := 'Grammar Files (*.grm)|*.grm|Other Files (*.*)|*.*';
  end
  else begin
    SaveDialog1.Title := 'Save Input File';
    SaveDialog1.Filter := 'Text Files (*.*)|*.*';
  end;
  if SaveDialog1.Execute then begin
    RichEdit1.PlainText := True;
    RichEdit1.Lines.SaveToFile(SaveDialog1.FileName);
    RichEdit1.PlainText := False;
//    if FLoadedPath = '' then
      FLoadedPath := SaveDialog1.FileName;
    RichEdit1.Modified := false;
    Caption := FLoadedPath;
    FAge := FileAge(FLoadedPath);
  end;
end;

/// procedura vymaze cely text v editore
procedure TGrammarEditForm.NewTBClick(Sender: TObject);
var Key: Word;
begin
  if not LooseChangesConfirm(Sender) then
    Exit;
  FLoadedPath := '';
  RichEdit1.Clear;
  FreeStructures;
  ButtonsEnabling;
  LForm.ButtonsEnabling;
  RForm.ButtonsEnabling;
  Key := 0;
  RichEdit1KeyUp(Sender, Key, []);
end;

/// procedura, ktora zanalyzuje vstupnu gramatiku v editore a sucasne vytvori NFA automat;
/// ak su v danej gramatike chyby, tak tieto vypise do listboxu na spodku okna
procedure TGrammarEditForm.EnterGrammarButtonClick(Sender: TObject);
var reader: TSimpleReader;
    b: Boolean;
    stopwatch: Integer;
begin
  FileDiffForm.DisableAll;
  FreeStructures;
  reader := TSimpleStringReader.create;
  b := reader.open(FLoadedPath,RichEdit1.Text);
  if b then begin
      FGFL := TGrammarFileLexer.create(reader);

      stopwatch := getcurrenttime;
      FGFL.parse;
      stopwatch := getcurrenttime - stopwatch;

      last_lexer_build_time := stopwatch;
      Flog := FGFL.FLog.clone;
//      gfl.Flog.updateListBox(ListBox1);

  end
  else begin
    Flog := TLog.create;
    Flog.addSFR2(nil,0,E_GF_LOAD_ERROR_IN_FILE_1,FLoadedPath);
//    log.updateListBox(ListBox1);
  end;

  Flog.updateListBox(ListBox1);
  AdjustCaretProperties;

  reader.Free;

//  ExtraButton.Enabled := FGFL <> nil;
  if FGFL <> nil then begin
    TokenTableForm.fillAsTokenTable(FGFL.getTokenNameTypeTable,FGFL.getStateNameTypeTable);
    RuleTableForm.fillAsRuleTable(FGFL.getTokenNameTypeTable,FGFL.getStateNameTypeTable,FGFL.getRuleTable);
  end;

  bPBDone := false;
  FileDiffForm.EnableAll;
//  ButtonsEnabling;
end;

/// procedura na vytvorenie DFA automatu (z NFA automatu) a LALR parsera
/// ak sa pri tejto operacii vyskytnu chyby, tak su vypisane v listboxe na spodku okna
procedure TGrammarEditForm.BuildParserButtonClick(Sender: TObject);
var b: Boolean;
    pb: TParserBuilder;
begin
  if FGFL = nil then begin
    ButtonsEnabling;
    Exit;
  end;

  FileDiffForm.DisableAll;

  if FDFA <> nil then begin
    FDFA.Free;
    FDFA := nil;
  end;

  if FParser <> nil then begin
    FParser.Free;
    FParser := nil;
  end;

  if FAlwaysConvertToDFA then begin
    FDFA := TDFA.create(FGFL.getNFA);
    last_DFA_build_time := getcurrenttime;
    b := FDFA.build3;
    last_DFA_build_time := getcurrenttime - last_DFA_build_time;

    if not b then begin
      FDFA.Free;
      FDFA := nil;
      Exit;
    end;
  end;

  pb := TParserBuilder.create(nil,FGFL.getTokenNameTypeTable,FGFL.getStateNameTypeTable,FGFL.getRuleTable);

  last_LALR_build_time := getcurrenttime;
  pb.buildLALRParser;
  last_LALR_build_time := getcurrenttime - last_LALR_build_time;

  FParser := pb.getParser;

  Flog.addLog(pb.Flog);

  Flog.updateListBox(ListBox1);
  AdjustCaretProperties;

  pb.free;

(*
  FGFL.Free;
  FGFL := nil;
*)
  DFATableForm.newlexer(FDFA);
  LALRTableForm.newparser(FParser.FConflictStates);

  bPBDone := true;
  FileDiffForm.EnableAll;
//  ButtonsEnabling;
//  LForm.ButtonsEnabling;
//  RForm.ButtonsEnabling;
end;

/// procedura na vytvorenie syntaktickeho stromu z textu v editore podla daneho NFA/DFA automatu a LALR parsera
/// ak sa pri tejto operacii vyskytnu chyby, tak su vypisane v listboxe na spodku okna
procedure TGrammarEditForm.BuildParseTreeButtonClick(Sender: TObject);
var lexer: TLexer;
    reader: TSimpleReader;
    b: Boolean;
    stopwatch: Integer;
begin
  GLOBAL_CHANGE := true;
  FileDiffForm.DisableAll;
  if (GrammarEditForm.FGFL = nil) or (GrammarEditForm.FParser = nil) then begin
    ButtonsEnabling;
    Exit;
  end;

  FreeStructures;

  reader := TSimpleStringReader.create;

  b := reader.open(FLoadedPath,RichEdit1.Text);
  if not b then begin
    Flog := TLog.create;
    Flog.addSFR2(nil,0,E_GF_LOAD_ERROR_IN_FILE_1,FLoadedPath);
  end
  else begin
    if GrammarEditForm.FDFA = nil then begin
      lexer := TLexer.create(reader,GrammarEditForm.FGFL.getNFA,nil);
    end
    else begin
      lexer := TLexer.create(reader,nil,GrammarEditForm.FDFA);
    end;

(*    if FRoot <> nil then begin
      freesubtree(FRoot);
    end;*)

    GrammarEditForm.FParser.linkwithLexer(lexer);

    stopwatch := getcurrenttime;
    GrammarEditForm.FParser.parse(FRoot,FTreeSize);
    stopwatch := getcurrenttime - stopwatch;

    Flog := GrammarEditForm.FParser.Flog.clone;
  end;

  ListBox1.Items.BeginUpdate;
  Flog.updateListBox(ListBox1);
  ListBox1.Items.EndUpdate;
  AdjustCaretProperties;

  if self = LForm then
    last_parse1_build_time := getcurrenttime - last_parse1_build_time
  else
    last_parse2_build_time := getcurrenttime - last_parse2_build_time;

  reader.Free;
  FileDiffForm.EnableAll;
end;

/// procedura obsluhujuca dvojklik v listboxe na spodku okna, kurzor v editore sa nastavi na
/// miesto chyby
procedure TGrammarEditForm.ListBox1DblClick(Sender: TObject);
var plogitm: PLogItem;
begin
  plogitm := PLogItem(ListBox1.Items.Objects[ListBox1.ItemIndex]);
  if plogitm <> nil then
    CaretGotoPos(plogitm.p);  //ked -1 tak sa nepohne
end;

/// konstruktor vytvarajuci objekt typu TGrammarEditForm s captionom name, vlastneny ownerom,
/// ak je isGrammar = true, tak sa vytvara editor gramatiky, inak editor vstupnych suborov
constructor TGrammarEditForm.createNamed(owner: TComponent; name: TCaption; isGrammar: Boolean);
begin
  inherited create(owner);
  caption := name;
  FIsGrammar := isGrammar;
end;

/// procedura zistujuca ci sa dany nacitany subor medzicasom nezmenil mimo tejto aplikacie, ak ano
/// tak ponukne moznosti na opatovane nacitanie
procedure TGrammarEditForm.FileAgeCheck(Sender: TObject);
begin
  if (FDisableCount = 0) and (FLoadedPath <> '') then begin
    if FAge <> FileAge(FLoadedPath) then begin
      if ReloadConfirm(Sender) then begin
        InternalOpen(Sender,FLoadedPath);
      end;
    end;
  end;
end;

/// procedura zobrazujuca tu cast hlavneho menu, ktora suvisi s Grammar Edit, resp. File Edit oknom
/// pri ziskani focusu
procedure TGrammarEditForm.FormActivate(Sender: TObject);
begin
(*  if FIsGrammar then begin
    MainForm.EnterGrammar1.Enabled := EnterGrammarButton.Enabled;
    MainForm.BuildParser1.Enabled := ExtraButton.Enabled;
    MainForm.BuildParser1.Visible := true;
  end
  else begin
    MainForm.EnterGrammar1.Enabled := EnterGrammarButton.Enabled;
    MainForm.BuildParser1.Enabled := ExtraButton.Enabled;
    MainForm.BuildParser1.Visible := false;
  end;     *)
  FileAgeCheck(Sender);
//  ButtonsEnabling;
(*  if FIsGrammar then begin
    MainForm.EnterGrammar1.Visible := true;
    MainForm.BuildParser1.Visible := true;
    MainForm.BuildParseTree1.Visible := false;
  end
  else begin
    MainForm.EnterGrammar1.Visible := false;
    MainForm.BuildParser1.Visible := false;
    MainForm.BuildParseTree1.Visible := true;
  end;*)
  MainForm.BuildParser1.Visible := ExtraButton.Visible and FIsGrammar;
  MainForm.EnterGrammar1.Visible := EnterGrammarButton.Visible and FIsGrammar;
  MainForm.BuildParseTree1.Visible := EnterGrammarButton.Visible and (not FIsGrammar);

  MainForm.Editor1.Visible := true;
end;

/// procedura skryvajuca tu cast hlavneho menu, ktora suvisi s Grammar Edit, resp. File Edit oknom
/// po strate focusu
procedure TGrammarEditForm.FormDeactivate(Sender: TObject);
begin
  MainForm.Editor1.Visible := false;
end;

/// procedura spracuvajuca dvojklik na statusbar, nasledne sa velkost listboxu zobrazujuceho chyby
/// zmeni na stvrtinu vysky celeho okna
procedure TGrammarEditForm.StatusBar1DblClick(Sender: TObject);
begin
  ListBox1.Height := ClientHeight shr 2;
end;

/// procedura obsluhujuca presuny mysou ponad listbox a nastavuje hint text, ktory sa zobrazi
/// po ponechani mysi nad danym prvkom listboxu na par sekund
procedure TGrammarEditForm.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var index: Integer;
begin
  if ListBox1.ItemHeight = 0 then
    index := 0
  else
    index := trunc(Y / ListBox1.ItemHeight);
  index := index + ListBox1.TopIndex;
  if index < ListBox1.Count then begin
    ListBox1.Hint := ListBox1.Items[index];
  end
  else
    ListBox1.Hint := '';
//  Caption := IntToStr(index);
end;

end.
