/// unit zaoberajuci sa zobrazovanim okna s porovnavanymi subormi
{$I main.inc}
unit filediff;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, constants, simple, ExtCtrls, ToolWin, Menus,
  ImgList, ExtDlgs, structures;

type
  PLineRecord = ^TLineRecord;
  /// record obsahujuci informacie o danom riadku v MyEdit-e;
  /// diffres je vysledok po porovnavani riadkov, toto sa zobrazuje uplne vpravo v zhustenej podobe;
  /// colorStrips je usporiadany zoznam usekov zafarbenych inak ako default v danom riadku
  TLineRecord = record
    linenum: Integer;
    tag: Integer;
    diffres: Integer;
    hash: DWORD;
    changed: Boolean;
    colorStrips: TSortedDLinkedList;
  end;

  /// trieda zobrazujuca dany text, s tym ze zobrazi rozdiely medzi nimi ak su vypocitane
  TMyEdit = class(TCustomControl)
  private
    procedure Scroll(Sender: TObject);
    procedure ColumnDiff(lineindex: Integer);
    function SaveToFile(filepath: xString): Boolean;
    procedure FontChange;
  public
    FCharWidth,FCharHeight: Integer;
    FNumOfVisibleLines, FNumOfVisibleCharsInLine: Integer;
    FGutterWidth: Integer;
    FWindowCharPos: TPoint;
    FCaretCharPos: TPoint;
    FGutterBackgroundColor: TColor;
    FBackgroundColor: TColor;
    FGutterFont: TFont;
    FMaxLineLength: Integer;
    FLines: TStringList;
    FLineRecords: array of PLineRecord;
    FLabel: TPanel;
    FVScroll: TScrollBar;
    FHScroll: TScrollBar;
    FCompared: Boolean;
    FStructCompared: Boolean;
    FChangedFont: Boolean;
    FPath: xString;
    FAge: Integer;
    Fselectedlineindex: Integer;
    FLCSLine: xString;
    FLCSRecord: PLineRecord;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
//    procedure Main(var Msg: TMessage); message WM_USER;
//    procedure WMPaint(var Msg: TMessage); message WM_PAINT;
    procedure Paint; override;
    procedure Resize; override;
    procedure MouseButtonDown(var Msg: TMessage); message WM_LBUTTONDOWN;
    procedure MouseDoubleClick(var Msg: TMessage); message WM_LBUTTONDBLCLK;

    function LoadFromFile(filepath: xString; bRedraw: Boolean = true): Boolean;
    procedure AssignControls(lbl: TPanel; hscr: TScrollBar; vscr: TScrollBar);
    procedure ClearLines;
    procedure ClearObjects;
  end;

  /// trieda zobrazujuca obdlznik vpravo na File Difference okne, ktory zobrazuje rozdiely medzi subormi v zhustenej podobe
  TMyPB = class(TCustomControl)
  private
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Paint; override;
    procedure Resize; override;
    procedure MouseButtonDown(var Msg: TMessage); message WM_LBUTTONDOWN;
  end;

  /// trieda zobrazujuca File Difference okno
  TFileDiffForm = class(TForm)
    Panel1: TPanel;
    StatusBar1: TStatusBar;
    ToolBar1: TToolBar;
    ResetLayoutTB: TToolButton;
    Splitter1: TSplitter;
    Panel2: TPanel;
    Panel1Top: TPanel;
    Panel2Top: TPanel;
    HScroll1: TScrollBar;
    VScroll1: TScrollBar;
    VScroll2: TScrollBar;
    HScroll2: TScrollBar;
    CompareFilesTB: TToolButton;
    ImageList1: TImageList;
    ToolButton3: TToolButton;
    SynchronizedScrollBarsTB: TToolButton;
    OpenDialog1: TOpenDialog;
    ToolButton5: TToolButton;
    AdvCompareFilesTB: TToolButton;
    HaltTB: TToolButton;
    PrevDiffTB: TToolButton;
    NextDiffTB: TToolButton;
    ToolButton1: TToolButton;
    Open1TB: TToolButton;
    Open2TB: TToolButton;
    ToolButton6: TToolButton;
    SaveDialog1: TSaveDialog;
    SaveDiffTB: TToolButton;
    ToolButton4: TToolButton;
    Panel3: TPanel;
    StructCompareTB: TToolButton;
    Save1TB: TToolButton;
    Save2TB: TToolButton;
    BasicStructCompareTB: TToolButton;
    HLFMTTB: TToolButton;
    SpecialStructCompareTB: TToolButton;
    FormattingTB: TToolButton;
    ShowParseTreesTB: TToolButton;
    ToolButton2: TToolButton;
    procedure StatusBar1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure ShowParseTreesTBClick(Sender: TObject);
    procedure FormattingTBClick(Sender: TObject);
    procedure Save2TBClick(Sender: TObject);
    procedure Save1TBClick(Sender: TObject);
    procedure HLFMTTBClick(Sender: TObject);
    procedure SpecialStructCompareTBClick(Sender: TObject);
    procedure BasicStructCompareTBClick(Sender: TObject);
    procedure StructCompareTBClick(Sender: TObject);
    procedure FormDeactivate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure SaveDiffTBClick(Sender: TObject);
    procedure Open2TBClick(Sender: TObject);
    procedure Open1TBClick(Sender: TObject);
    procedure NextDiffTBClick(Sender: TObject);
    procedure PrevDiffTBClick(Sender: TObject);
    procedure HaltTBClick(Sender: TObject);
    procedure AdvCompareFilesTBClick(Sender: TObject);
    procedure Panel1TopDblClick(Sender: TObject);
    procedure Panel2TopDblClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure SynchronizedScrollBarsTBClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure CompareFilesTBClick(Sender: TObject);
    procedure ResetLayoutTBClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    procedure UpdateStatusBar;
    procedure UpdateDiffBitmap;
    procedure StructCompare(mode: Integer);
    { Private declarations }
  public
    { Public declarations }
    MyEdit1: TMyEdit;
    MyEdit2: TMyEdit;
    MyPB: TMyPB;
    L: PIntList;
    lastmode: Integer;
//    SynchronizedScrollBars: Boolean;
    FDestroying,FFormCreating: Boolean;
    FDeleted,FInserted,FChanged,FMatched,FMoved,FNotCompared: Integer;
    FLeftFocused: Boolean;
//    FFormatting: Boolean;
    FDiffBitmap: TBitmap;
    FLastFunc: Integer;
//    FCompared: Boolean;
//    MySplitter1: TSplitter;
    procedure ResetLayout;
    procedure CalculateDiffs(bGetModify:Boolean = false);
    procedure DisableAll;
    procedure EnableAll;
    procedure SimpleUpdateStatusBar(c,d,i,m: Integer);
  end;

var FileDiffForm: TFileDiffForm;

implementation

{$R *.dfm}

uses crc32,LCS,tables,mainframe,grammaredit,main,treedist,hashmap,StrUtils,
  folderdiff, logframe;

/// procedura odstranujuca pridruzene objekty zo zoznamu riadkov (lines)
procedure FreeObjectsFromLines(var lines: TStringList);
var i: Integer;
    p: PLineRecord;
begin
  for i := 0 to Pred(lines.count) do begin
    if lines.Objects[i] <> nil then begin
      p := PLineRecord(lines.Objects[i]);
      if p.colorStrips <> nil then begin
        p.colorStrips.Free;
      end;
      dispose(p);
      lines.Objects[i] := nil;
    end;
  end;
end;

/// konstruktor tohto objektu, vytvarajuci obdlznik vpravo na File Difference okne, ktory zobrazuje rozdiely medzi subormi v zhustenej podobe
constructor TMyPB.Create(AOwner: TComponent);
begin
  inherited;
  Font.Pitch := fpFixed;
  Font.Name := 'Courier New';
  ControlStyle := [csCaptureMouse, csOpaque, csClickEvents, csDoubleClicks];
  Font.Size := 10;
  Canvas.Font.Assign(Font);
  ParentFont := false;
  ParentColor := false;
  Tabstop := true;
  Color := clWindow;
  FDoubleBuffered := true;
  Cursor := crHandPoint;
end;

/// destruktror odstranujuci tento objekt z pamate
destructor TMyPB.Destroy;
begin
  inherited;
end;

/// procedura obsluhujuca zmenu velkosti tohto komponentu
procedure TMyPB.Resize;
begin
  inherited;
  Repaint;
//  PostMessage(FileDiffForm.Handle,WM_PAINT,0,0);
//  PostMessage(FileDiffForm.Handle,WM_SIZE,SIZE_RESTORED,FileDiffForm.Width + FileDiffForm.Height shl 16);
//  FileDiffForm.Perform(WM_SIZE,SIZE_RESTORED,FileDiffForm.Width + FileDiffForm.Height shl 16);
end;

/// procedura obsluhujuca vykreslovanie tohto komponentu
procedure TMyPB.Paint;
var pos,pos2: Integer;
    HeightRatio: Real;
begin
  if FileDiffForm = nil then
    Exit;

  with FileDiffForm do begin
    if MyEdit1.FLines.Count = 0 then
      Exit;

    HeightRatio := self.Height/MyEdit1.FLines.Count;
  (*  self.Canvas.Brush.Color := clWindow;
    self.Canvas.FillRect(ClientRect);*)
    self.Canvas.StretchDraw(Rect(0,0,self.Width,self.Height),FDiffBitmap);

  (*  pos := MyEdit1.FWindowCharPos.y + (self.ClientHeight div MyEdit1.FCharHeight) shr 1;
    pos := self.ClientHeight*pos div MyEdit1.FLines.Count;

    pos2 := MyEdit1.FWindowCharPos.y + MyEdit1.FNumOfVisibleLines + (self.ClientHeight div MyEdit1.FCharHeight) shr 1;
    pos2 := self.ClientHeight*pos2 div MyEdit1.FLines.Count;*)
    pos := trunc(MyEdit1.FWindowCharPos.y * HeightRatio);
    pos2 := trunc((MyEdit1.FWindowCharPos.y + MyEdit1.FNumOfVisibleLines + 1) * HeightRatio);

    self.Canvas.Brush.Color := COLOR_PB_SCROLLBAR_OUTLINE;
    self.Canvas.FrameRect(Rect(0,pos,self.Width,pos2));
  end;
end;

/// procedura obsluhujuca kliknutie mysou na tento komponent a nasledny presun na dany riadok v subore
procedure TMyPB.MouseButtonDown(var Msg: TMessage);
var mpos: TPoint;
    X,Y: Integer;
    ACol,ARow: Integer;
    HeightRatio: Real;
begin
  mpos := Mouse.CursorPos;
  X := ScreenToClient(mpos).X;
  Y := ScreenToClient(mpos).Y;

  if FileDiffForm.MyEdit1.FLines.Count = 0 then
    Exit;

  HeightRatio := Height/FileDiffForm.MyEdit1.FLines.Count;

  if HeightRatio = 0 then
    FileDiffForm.MyEdit1.FVScroll.Position := 0
  else
    FileDiffForm.MyEdit1.FVScroll.Position := trunc(Y / HeightRatio);
end;

/// procedura inicializujuca File Difference okno
procedure TFileDiffForm.FormCreate(Sender: TObject);
begin
  DoubleBuffered := true;
  Panel1Top.DoubleBuffered := true;
  Panel2Top.DoubleBuffered := true;
  FFormCreating := true;
  FLeftFocused := true;
  FDestroying := false;

  VScroll1.Position := 0;
  VScroll2.Position := 0;
  HScroll1.Position := 0;
  HScroll2.Position := 0;

  SynchronizedScrollBarsTB.down := FYSynchronizedScrollBars;
//  SynchronizedScrollBars := SynchronizedScrollBarsTB.down;
  FormattingTB.down := FEnabledFormatting;
//  FFormatting := FormattingTB.down;

  FDiffBitmap := TBitmap.create;

  MyEdit1 := TMyEdit.create(Panel1);
  MyEdit1.Parent := Panel1;
  MyEdit1.AssignControls(Panel1Top,HScroll1,VScroll1);

//  MyEdit1.Align := alLeft;
//  MyEdit1.SetBounds(0,0,200,80);

(*  MySplitter1 := TSplitter.create(Panel1);
  MySplitter1.Parent := Panel1;
  MySplitter1.Beveled := true;
  MySplitter1.MinSize := 1;*)
//  MySplitter1.Align := alLeft;
//  MySplitter1.SetBounds(MyEdit1.Left + MyEdit1.Width,MyEdit1.Top,5,MyEdit1.Height);

  MyEdit2 := TMyEdit.create(Panel2);
  MyEdit2.Parent := Panel2;
  MyEdit2.AssignControls(Panel2Top,HScroll2,VScroll2);

  MyPB := TMyPB.Create(Panel3);
  MyPB.Parent := Panel3;
  MyPB.Align := alClient;
//  MyEdit2.Align := alClient;
//  MyEdit2.SetBounds(205,0,200,80);
  MyEdit1.Align := alClient;
  MyEdit2.Align := alClient;

  L := nil;
  lastmode := -1;
//  MyEdit1.LoadFromFile(FCurrentDirectory + 'test\' + 'p.pas');
//  MyEdit2.LoadFromFile(FCurrentDirectory + 'test\' + 'p3.pas');

  ResetLayout;
//  SendMessage(MyEdit1.Handle,WM_USER,0,0);
  FLastFunc := -1;
  FFormCreating := false;
end;

/// procedura, ktora zabezpeci prekreslenie a povodne rozmiestnenie a velkost vsetkych komponentov vo File Difference okne
procedure TFileDiffForm.ResetLayout;
var R: TRect;
begin
//  Panel1.SetBounds();
//  Panel2.SetBounds();
  if FDestroying then
    Exit;

  Panel2.Width := (ClientWidth-Splitter1.Width-Panel3.Width) shr 1;
  Panel1.Width := (ClientWidth-Splitter1.Width-Panel3.Width) shr 1;
  Panel3.Width := 18;

(*  Panel3.Repaint;
  PaintBox1.align := alClient;
  PaintBox1.Repaint;
  PaintBox1.Show;
  Panel3.Repaint;

  Panel1.Align := alLeft;
  Panel2.Align := alClient;
  Panel3.Align := alRight;*)
end;

/// procedura na zobrazenie jednotlivych MyEdit komponentov pri zobrazeni File Difference okna
procedure TFileDiffForm.FormShow(Sender: TObject);
var R: TRect;
begin
(*  MyRichEdit1 := TMyRichEdit.Create(self);
  MyRichEdit1.Parent := self;

  R.Left := 100;
  R.Top := 0;
  R.Right := MyRichEdit1.Width;
  R.Bottom := MyRichEdit1.Height;
  SendMessage(MyRichEdit1.handle, EM_SETRECT, 0, Integer(@R));
  MyRichEdit1.Show;*)

  MyEdit1.Show;
//  MySplitter1.Show;
  MyEdit2.Show;
end;

{ TMyEdit }

/// konstruktor vytvarajuci MyEdit objekt, ktory zobrazuje dany subor a vyznacuje v nom rozdiely
constructor TMyEdit.Create(AOwner: TComponent);
begin
  inherited;
  Font.Pitch := fpFixed;
  Font.Name := 'Courier New';
  ControlStyle := [csCaptureMouse, csOpaque, csClickEvents, csDoubleClicks];
  Font.Size := 10;
  Canvas.Font.Assign(Font);
  ParentFont := false;
  ParentColor := false;
  Tabstop := true;
  Color := clWindow;
  FDoubleBuffered := true;
  Cursor := crIBeam;

  FLines := nil;
  FHScroll := nil;
  FVScroll := nil;
  FLabel := nil;

  FBackGroundColor := COLOR_TEXTBACKGROUND;
  FGutterBackgroundColor := COLOR_GUTTER;

  FLines := TStringList.Create;
  FCompared := false;
  FStructCompared := false;
  Fselectedlineindex := -1;

  FLCSLine := '';
  FLCSRecord := nil;

  FChangedFont := true;
end;

/// funkcia nacitavajuca vstup zo suboru filepath a pripadne prekresli tento komponent po nacitani zo suboru
function TMyEdit.LoadFromFile(filepath: xString; bRedraw: Boolean = true): Boolean;
var I: Integer;
begin
  result := true;

  FCaretCharPos := Point(0,0);
  FWindowCharPos := Point(0,0);
  FMaxLineLength := 0;
  FLabel.Caption := filepath;
  FLabel.Hint := FLabel.Caption;
  FPath := filepath;

  try
    ClearLines;
    FCompared := false;
    FStructCompared := false;
    if filepath <> '' then begin
      FLines.LoadFromFile(filepath);
      FAge := fileAge(filepath);
    end;
  except
    result := false; //file not found
  end;

  Fselectedlineindex := -1;

  if bRedraw then begin
    if FileDiffForm <> nil then begin
      if not FileDiffForm.FFormCreating then
        FileDiffForm.UpdateDiffBitmap;
    end;

    Repaint;
  end;
(*  FLineRecords := nil;
  SetLength(FLineRecords, FLines.count);
  for I := 0 to Pred(FLines.count) do begin
    FLineRecords[I] := nil;
  end;*)
end;

/// funkcia ukladajuca momentalny text v tomto komponente do suboru filepath (presne to co je vidiet na obrazovke)
function TMyEdit.SaveToFile(filepath: xString): Boolean;
begin
  result := false;
  if FLines <> nil then begin
//    FreeObjectsFromLines(FLines);
//    FLines.Free;
    FLines.SaveToFile(filepath);
    FPath := filepath;
    FAge := FileAge(filepath);
    FLabel.Caption := filepath;
    FLabel.Hint := filepath;
    result := true;
  end;
end;

/// funkcia obsluhujuca posuny scrollbaru, ak je dana moznost nastavena, tak zosynchronizuje obidva scrollbary
procedure TMyEdit.Scroll(Sender: TObject);
begin
  if FYSynchronizedScrollBars then begin
    if Sender is TScrollBar then begin
      FileDiffForm.VScroll1.Position := FVScroll.Position;
      FileDiffForm.VScroll2.Position := FVScroll.Position;

      FileDiffForm.HScroll1.Position := FHScroll.Position;
      FileDiffForm.HScroll2.Position := FHScroll.Position;
    end;
  end;
  FWindowCharPos := Point(FHScroll.Position,FVScroll.Position);
  Repaint;
  FileDiffForm.MyPB.Repaint;
//  FileDiffForm.MyEdit2.Repaint;
end;

/// procedura priradujuca scrollbary k MyEdit objektom, aby sa mohli neskor synchronizovat a pod.
procedure TMyEdit.AssignControls(lbl: TPanel; hscr: TScrollBar; vscr: TScrollBar);
begin
  FLabel := lbl;
  FHScroll := hscr;
  FVScroll := vscr;
  FVScroll.OnChange := Scroll;
  FHScroll.OnChange := Scroll;
end;

/// procedura odstranujuca pridruzene objekty zo zoznamu riadkov (Flines)
procedure TMyEdit.ClearObjects;
begin
  FreeObjectsFromLines(FLines);
end;

/// procedura odstanujuca vsetok text (riadky) z tohto objektu
procedure TMyEdit.ClearLines;
begin
  ClearObjects;
  FLines.Clear;
end;

/// destruktror odstranujuci tento objekt z pamate
destructor TMyEdit.Destroy;
begin
  ClearLines;
  FLines.Free;
  if FLCSRecord <> nil then
    dispose(FLCSRecord);

  inherited;
end;

/// procedura na zmenu fontu pouziteho pre zobrazovanie riadkov v tomto komponente
procedure TMyEdit.FontChange;
begin
  FCharWidth := Canvas.TextWidth('H');
  FCharHeight := Canvas.TextHeight('I');
  if FCharWidth < 1 then begin
    FCharWidth := 9;
  end;
  if FCharHeight < 1 then begin
    FCharHeight := 12;
  end;
end;

/// procedura obsluhu zmeny velkosti tohto komponentu
procedure TMyEdit.Resize;
var R: TRect;
begin
  inherited;

  if FChangedFont then begin
    FontChange;
    FChangedFont := false;
  end;

  R := GetClientRect;
  FNumOfVisibleLines := (R.bottom - R.top) div FCharHeight - 1;
  if FLines <> nil then
    FGutterWidth := FCharWidth * (log10(FLines.Count-1) + 1) + 2
  else
    FGutterWidth := FCharWidth * ((1) + 1) + 2;
  FNumOfVisibleCharsInLine := (R.right - R.left - FGutterWidth) div FCharWidth;
  FVScroll.LargeChange := FNumOfVisibleLines;
  FHScroll.LargeChange := FNumOfVisibleCharsInLine;
  Repaint;
end;

/// funkcia na konverziu TAB znakov v texte na prislusny pocet medzier definovanych v nastaveniach (standardne 2)
function expandTabs(const S: xString): xString;
var i,j,k: Integer;
    last: Integer;
begin
  result := '';
  if S = '' then
    Exit;
  last := Length(S);  //at least one
  SetLength(result,last);
  j := 1;
  for i := 1 to Length(S) do begin
    if S[i] = SET_TABCHAR then begin
      for k := 1 to TAB_SPACES do begin
        result[j] := ' ';
        Inc(j);
        if j = last then begin
          last := last*2;
          SetLength(result,last);
        end;
      end;
    end
    else begin
      result[j] := S[i];
      Inc(j);
      if j = last then begin
        last := last*2;
        SetLength(result,last);
      end;
    end;
  end;
  SetLength(result,Pred(j));
end;

/// procedura obsluhujuca vykreslovanie tohto komponentu
procedure TMyEdit.Paint;
var i,j,k: Integer;
    R,R2: TRect;
    S,lineS: xString;
    p: PLineRecord;
    c,ci,ct: TColor;
    cs: TFontStyles;
    x: PSListNode;
    index,count: Integer;//,count,color: Integer;
    ES: TEditScript;
    b: Boolean;
begin
  inherited;
//  FileDiffForm.Caption := IntToStr(j);
//  j := j + 1;
  for i := 0 to FNumOfVisibleLines+1 do begin
      Canvas.Font.Color := TCOLOR_DEFAULT;
      Canvas.Brush.Color := FGutterBackgroundColor;
      R.TopLeft := Point(0,i*FCharHeight);
      R.BottomRight := Point(FGutterWidth-2,i*FCharHeight+FCharHeight);
      Canvas.FillRect(R);

      Canvas.Pen.Style := psSolid;
      Canvas.Pen.Color := FBackGroundColor;
      Canvas.MoveTo(FGutterWidth-2,i*FCharHeight);
      Canvas.LineTo(FGutterWidth-2,i*FCharHeight+FCharHeight);

      Canvas.Pen.Color := FGutterBackgroundColor;
      Canvas.MoveTo(FGutterWidth-1,i*FCharHeight);
      Canvas.LineTo(FGutterWidth-1,i*FCharHeight+FCharHeight);

      R2.TopLeft := Point(FGutterWidth,i*FCharHeight);
      R2.BottomRight := Point(FGutterWidth+FNumOfVisibleCharsInLine*FCharWidth+FCharWidth,i*FCharHeight+FCharHeight);
(*      Canvas.Brush.Color := FBackGroundColor;
      Canvas.FillRect(R2);*)
      if (FLines <> nil) and (i + FWindowCharPos.Y < FLines.count) then begin
        S := FLines[i+FWindowCharPos.Y];
        S := ExpandTabs(S);
        if Length(S) > FMaxLineLength then begin
          FMaxLineLength := Length(S);
        end;
        p := PLineRecord(FLines.Objects[i+FWindowCharPos.Y]);
        if (p = nil) or (not FCompared) then begin
          Canvas.Brush.Color := COLOR_NEWGUTTER;//FGutterBackgroundColor;
          Canvas.Font.Color := TCOLOR_NEWGUTTER;
          Canvas.Font.Style := TSTYLE_DEFAULT;
          if i+FWindowCharPos.Y = Fselectedlineindex then begin
            Canvas.Brush.Color := BRIGHTER_COLOR(Canvas.Brush.Color);
          end;
          Canvas.TextRect(R,FGutterWidth-2-Log10(FWindowCharPos.Y + i)*FCharWidth,i*FCharHeight,IntToStr(FWindowCharPos.Y + i));
        end
        else begin
          Canvas.Brush.Color := FGutterBackgroundColor;
          Canvas.Font.Color := TCOLOR_GUTTER;
          Canvas.Font.Style := TSTYLE_DEFAULT;
          if i+FWindowCharPos.Y = Fselectedlineindex then begin
            Canvas.Brush.Color := BRIGHTER_COLOR(Canvas.Brush.Color);
          end;
          if p.linenum >= 0 then begin
            Canvas.TextRect(R,FGutterWidth-2-Log10(p.linenum)*FCharWidth,i*FCharHeight,IntToStr(p.linenum));
          end
          else begin
            if p.diffres = ES_INSERT then begin
              Canvas.TextRect(R,FGutterWidth-2-2*FCharWidth,i*FCharHeight,'<-');
            end
            else if p.diffres = ES_DELETE then begin
              Canvas.TextRect(R,FGutterWidth-2-2*FCharWidth,i*FCharHeight,'->');
            end;
          end;
        end;
        if (p = nil) or (not FCompared) then begin
          c := FBackGroundColor;
          ct := TCOLOR_DEFAULT;
        end
        else begin
          case p.diffres of
            ES_INSERT: begin c := COLOR_INSERT; ct := TCOLOR_INSERT; end;
            ES_DELETE: begin c := COLOR_DELETE; ct := TCOLOR_DELETE; end;
            ES_CHANGE: begin c := COLOR_CHANGE; ct := TCOLOR_CHANGE; end;
            ES_NOT_COMPARED: begin c := COLOR_NOT_COMPARED; ct := TCOLOR_NOT_COMPARED; end;
            ES_MATCH: begin c := COLOR_MATCH; ct := TCOLOR_MATCH; end;
            ES_COMBINED: begin c := COLOR_COMBINED; ct := TCOLOR_COMBINED; end;
          else
            c := FBackGroundColor;
            ct := TCOLOR_DEFAULT;
          end;
        end;
        Canvas.Font.Color := ct;
        Canvas.Font.Style := TSTYLE_DEFAULT;
        if i+FWindowCharPos.Y = Fselectedlineindex then begin
          c := BRIGHTER_COLOR(c);
        end;
        Canvas.Brush.Color := c;
        if (p = nil) {or (p.colorStrips = nil)} then begin
          Canvas.TextRect(R2,FGutterWidth+1,i*FCharHeight,Copy(S,FWindowCharPos.X+1));
        end
        else begin
          b := FCompared and (i+FWindowCharPos.Y = Fselectedlineindex);
          b := b and FSelectedLineLCS and FYSynchronizedScrollBars;
          b := b and (p.diffres = ES_CHANGE);
          if b then
            S := FLCSLine;
          lineS := Copy(S,FWindowCharPos.X+1,FNumOfVisibleCharsInLine+1);
          if lineS <> '' then begin
            j := 1;
            ci := c;
            ct := TCOLOR_DEFAULT;
            cs := TSTYLE_DEFAULT;
            if ((b) or (not FCompared)) and (p.colorStrips <> nil) then begin
              p.colorStrips.getNextReset;
              while p.colorStrips.getNext(x) do begin
                if (x.key + x.data <= FWindowCharPos.X+1) then begin
                  continue;
                end;
                index := x.key - FWindowCharPos.X;
                count := x.data;
                if index < 1 then begin
                  k := 1 - index;
                  index := index + k;
                  count := count - k;
                end;

                if index > Length(lineS) then begin
                  ci := x.tag1;
                  ct := x.tag2;
                  k := x.tag3;
                  cs := [];
                  if k and 1 = 1 then
                    cs := cs + [fsBold];
                  if k and 2 = 2 then
                    cs := cs + [fsItalic];
                  if k and 4 = 4 then
                    cs := cs + [fsUnderLine];
                  if k and 8 = 8 then
                    cs := cs + [fsStrikeOut];
                  break;
                end;

                ci := c;
                ct := TCOLOR_DEFAULT;
                cs := TSTYLE_DEFAULT;
                Canvas.Brush.Color := ci;
                Canvas.Font.Color := ct;
                Canvas.Font.Style := cs;
                if index > j then begin
                  R2.Left := FGutterWidth + (j-1)*FCharWidth;
                  Canvas.TextRect(R2,FGutterWidth+1+(j-1)*FCharWidth,i*FCharHeight,Copy(lineS,j,index-j));
                end;
                j := index;

                ci := x.tag1;
                ct := x.tag2;
                k := x.tag3;
                cs := [];
                if k and 1 = 1 then
                  cs := cs + [fsBold];
                if k and 2 = 2 then
                  cs := cs + [fsItalic];
                if k and 4 = 4 then
                  cs := cs + [fsUnderLine];
                if k and 8 = 8 then
                  cs := cs + [fsStrikeOut];
                Canvas.Brush.Color := ci;
                Canvas.Font.Color := ct;
                Canvas.Font.Style := cs;
                if count > 0 then begin
                  R2.Left := FGutterWidth + (j-1)*FCharWidth;
                  Canvas.TextRect(R2,FGutterWidth+1+(j-1)*FCharWidth,i*FCharHeight,Copy(lineS,j,count));
                  j := j + count;
                end;
              end;
            end;
            if j <= Length(lineS) then begin
              ci := c;
              ct := TCOLOR_DEFAULT;
              cs := TSTYLE_DEFAULT;
              Canvas.Brush.Color := ci;
              Canvas.Font.Color := ct;
              Canvas.Font.Style := cs;
              R2.Left := FGutterWidth + (j-1)*FCharWidth;
              Canvas.TextRect(R2,FGutterWidth+1+(j-1)*FCharWidth,i*FCharHeight,Copy(lineS,j));
            end
            else begin
              if j <= FNumOfVisibleCharsInLine + 2 then begin
(*                Canvas.Brush.Color := ci;
                Canvas.Font.Color := ct;
                R2.Left := FGutterWidth + (j-1)*FCharWidth;
                Canvas.TextRect(R2,FGutterWidth+1+(j-1)*FCharWidth,i*FCharHeight,Copy(lineS,j));
*)
                ci := c;
                ct := TCOLOR_DEFAULT;
                cs := TSTYLE_DEFAULT;
                Canvas.Brush.Color := ci;
                Canvas.Font.Color := ct;
                Canvas.Font.Style := cs;
                R2.Left := FGutterWidth + (Length(lineS))*FCharWidth;
                Canvas.FillRect(R2);
//                Canvas.TextRect(R2,FGutterWidth+1+(j-1)*FCharWidth,i*FCharHeight,Copy(lineS,j));
              end;
            end;
          end
          else begin  //lineS = '' nevidime nic
            j := 1;
            ci := c;
            ct := TCOLOR_DEFAULT;
            cs := TSTYLE_DEFAULT;
            if S = '' then begin  //S = '' skutocne je to prazdna lina, inak to robi farebne konce lineov
              if ((b) or (not FCompared)) and (p.colorStrips <> nil) then begin
                p.colorStrips.getNextReset;
                while p.colorStrips.getNext(x) do begin
                  if (x.key + x.data > Length(S)) then begin
                    ci := x.tag1;
                  end;
                end;
              end;
            end;
//            ci := COLOR_TEXTBACKGROUND;
            Canvas.Brush.Color := ci;
            Canvas.Font.Color := ct;
            Canvas.Font.Style := cs;
            R2.Left := FGutterWidth + (j-1)*FCharWidth;
            Canvas.TextRect(R2,FGutterWidth+1+(j-1)*FCharWidth,i*FCharHeight,Copy(lineS,j));
          end;
        end;
      end
      else begin
        Canvas.Brush.Color := FGutterBackgroundColor;
        Canvas.FillRect(R);
        Canvas.Brush.Color := FBackGroundColor;
        Canvas.FillRect(R2);
      end;
  end;
  if FVScroll <> nil then
    FVScroll.SetParams(FVScroll.Position,0,Max(0,FLines.Count-1-FNumOfVisibleLines));
  if FHScroll <> nil then
    FHScroll.SetParams(FHScroll.Position,0,Max(0,FMaxLineLength-FNumOfVisibleCharsInLine));
end;

/// procedura obsluhujuca stlacenie tlacidla na toolbare
procedure TFileDiffForm.ResetLayoutTBClick(Sender: TObject);
begin
  ResetLayout;
end;

/// procedura zobrazujuca text v statusbar-e
procedure TFileDiffForm.UpdateStatusBar;  //!TEXT!
begin
  StatusBar1.Panels[0].Text := IntToStr(FChanged) + ' changed';
  StatusBar1.Panels[1].Text := IntToStr(FDeleted) + ' deleted';
  StatusBar1.Panels[2].Text := IntToStr(FInserted) + ' inserted';
  StatusBar1.Panels[3].Text := IntToStr(FMatched) + ' matched';
end;

/// procedura na zobrazenie momentalneho poctu zmien iba zo vstupnych parametrov c,d,i,m
procedure TFileDiffForm.SimpleUpdateStatusBar(c,d,i,m: Integer);  //!TEXT!
begin
  StatusBar1.Panels[0].Text := IntToStr(c) + ' changed';
  StatusBar1.Panels[1].Text := IntToStr(d) + ' deleted';
  StatusBar1.Panels[2].Text := IntToStr(i) + ' inserted';
  StatusBar1.Panels[3].Text := IntToStr(m) + ' matched';
end;

/// procedura zistujuca rozdiely medzi subormi, zalozena na porovnavani retazcov, ak
/// je bGetModify true, tak uvazuje aj s operaciou modify okrem standardnych insert,delete
procedure TFileDiffForm.CalculateDiffs(bGetModify:Boolean = false);  //!TEXT!
var a1: intarray;
    a2: intarray;
    I,J,k,h,t: Integer;
    D: Integer;
    len1,len2: Integer;
    es: TEditScript;
    chset: TCharSet;
    op,index,count: Integer;
    S: xString;
    X,Y: DWORD;
    p: PLineRecord;
    diff_calc_time: Integer;
    hash_calc_time: Integer;
    coloring_time: Integer;
    lastop,lastindex,lastcount: Integer;
begin
  DisableAll;
(*  S := 'ahoj';
  Y := calculateCRC32(PChar(S),Length(S));
  X := $FFFFFFFF;
  calcCRC32(Addr(S[1]),Length(S),X);
  writeln(Y);
  writeln(X);*)
  if MyEdit1.FCompared = true then begin
    MyEdit1.LoadFromFile(MyEdit1.FPath,false);
  end
  else begin
    MyEdit1.ClearObjects;
  end;
  if MyEdit2.FCompared = true then begin
    MyEdit2.LoadFromFile(MyEdit2.FPath,false);
  end
  else begin
    MyEdit2.ClearObjects;
  end;

  FInserted := 0;
  FDeleted := 0;
  FChanged := 0;
  FMatched := 0;
  FGlobalHalt := false;

  hash_calc_time := getcurrenttime;
  chset := nil;
  if DIFF_WS_FILTERING then begin
    chset := TCharSet.create('diffWS');
    chset.addString(DIFF_WS);
  end;
  len1 := MyEdit1.FLines.count;
  len2 := MyEdit2.FLines.count;
  SetLength(a1,len1);
  SetLength(a2,len2);
  StatusBar1.Panels[4].Text := 'Hashing file 1...';
  for I := 0 to Pred(len1) do begin
    Application.ProcessMessages;
    if FGlobalHalt then
      break;
    a1[I] := StringToCRC32(MyEdit1.FLines[I],chset,DIFF_CASE_SENSITIVE);
    if MyEdit1.FLines.Objects[I] = nil then begin
      new(p);
      MyEdit1.FLines.Objects[I] := TObject(p);
      p.linenum := I;
      p.diffres := ES_NOT_COMPARED;
      p.tag := -1;
      p.colorStrips := nil;
    end;
      //p := PLineRecord(Pointer(MyEdit1.FLines.Objects[I]));
(*    if MyEdit1.FLineRecords[I] = nil then begin
      new(MyEdit1.FLineRecords[I]);
      with MyEdit1.FLineRecords[I]^ do begin
        linenum := I;
        diffres := ES_NOT_COMPARED;
        tag := -1;
      end;
    end;*)
//    Form1.ListBox1.additem(IntToStr(a1[I]),nil);

//    Form1.Memo1.Text := Form1.Memo1.Text + IntToStr(a1[I]) {+ ': ' + MyEdit1.FLines[I]} + EOL;
  end;
//  Form1.Memo1.Text := Form1.Memo1.Text + '-------------------------------' + EOL + EOL + EOL + '-----------------------------------' + EOL;
  StatusBar1.Panels[4].Text := 'Hashing file 2...';
  for I := 0 to Pred(len2) do begin
    Application.ProcessMessages;
    if FGlobalHalt then
      break;
    a2[I] := StringToCRC32(MyEdit2.FLines[I],chset,DIFF_CASE_SENSITIVE);
    if MyEdit2.FLines.Objects[I] = nil then begin
      new(p);
      MyEdit2.FLines.Objects[I] := TObject(p);
      p.linenum := I;
      p.diffres := ES_NOT_COMPARED;
      p.tag := -1;
      p.colorStrips := nil;
    end;
(*    if MyEdit2.FLineRecords[I] = nil then begin
      new(MyEdit2.FLineRecords[I]);
      with MyEdit2.FLineRecords[I]^ do begin
        linenum := I;
        diffres := ES_NOT_COMPARED;
        tag := -1;
      end;
    end;*)
//    Form1.ListBox2.additem(IntToStr(a2[I]),nil);

//    Form1.Memo1.Text := Form1.Memo1.Text + IntToStr(a2[I]) {+ ': ' + MyEdit2.FLines[I]} + EOL;
  end;
  hash_calc_time := getcurrenttime - hash_calc_time;

  StatusBar1.Panels[4].Text := 'Differencing...';
  Application.ProcessMessages;
  diff_calc_time := getcurrenttime;
  if not FGlobalHalt then
    D := calcDiff(a1,len1,a2,len2,es)
  else begin
    D := 0;
    es := TEditScript.create;
  end;
  diff_calc_time := getcurrenttime - diff_calc_time;

{$IFDEF DEBUG1}
  writeln(es.toString);
{$ENDIF}
{$IFDEF DEBUG0}
  writeln('D-path: ',D);
{$ENDIF}
{$IFDEF LOG0}
  LogForm.AddLine('D-path: ' + IntToStr(D));
{$ENDIF}
(*  D := calcDist(a1,len1,a2,len2);
  writeln('Distance: ',D);*)
  coloring_time := getcurrenttime;
  I := 0; J := 0;
  lastop := ES_MATCH;
  lastindex := 0;
  lastcount := 0;
  StatusBar1.Panels[4].Text := 'Coloring...';
  es.getNextReset;
  while es.getNext(op,index,count) do begin
    UpdateStatusBar;
    Application.ProcessMessages;
    if FGlobalHalt then
      break;
    //insert/delete chaining if bGetModify = true (INSERT-DELETE => ES_CHANGE)
    if op = ES_MATCH then begin
      if bGetModify then begin
        if (lastop <> ES_MATCH) then begin
          if lastop = ES_INSERT then begin
            for k := 1 to lastcount do begin
              new(p);
              MyEdit1.FLines.InsertObject(I,'',TObject(p));
              p.linenum := -1;
              p.diffres := ES_INSERT;
              p.tag := -1;
              p.colorStrips := nil;
              p := PLineRecord(MyEdit2.FLines.Objects[J]);
              p.diffres := ES_INSERT;
              J := J + 1;
            end;
            I := I + lastcount;
            FInserted := FInserted + lastcount;
          end
          else if lastop = ES_DELETE then begin
            for k := 1 to lastcount do begin
              new(p);
              MyEdit2.FLines.InsertObject(J,'',TObject(p));
              p.linenum := -1;
              p.diffres := ES_DELETE;
              p.tag := -1;
              p.colorStrips := nil;
              p := PLineRecord(MyEdit1.FLines.Objects[I]);
              p.diffres := ES_DELETE;
              I := I + 1;
            end;
            J := J + lastcount;
            FDeleted := FDeleted + lastcount;
          end;
          lastop := ES_MATCH;
        end;
        I := I + count;
        J := J + count;
        FMatched := FMatched + count;
      end
      else begin
        I := I + count;
        J := J + count;
        FMatched := FMatched + count;
      end;
    end
    else if op = ES_INSERT then begin
      if bGetModify then begin
        if (lastop <> ES_MATCH) then begin
          t := lastcount;
          if lastop = ES_INSERT then begin
            lastcount := lastcount + count;
          end
          else if lastop = ES_DELETE then begin
            h := min(count,lastcount);
            for k := 1 to h do begin
              p := PLineRecord(MyEdit2.FLines.Objects[J]);
              p.diffres := ES_CHANGE;
              p := PLineRecord(MyEdit1.FLines.Objects[I]);
              p.diffres := ES_CHANGE;
              I := I + 1;
              J := J + 1;
            end;
            lastcount := max(count,lastcount) - h;
            FChanged := FChanged + h;
          end;
          if count > t then
            lastop := op;
        end
        else begin
          lastop := ES_INSERT;
          lastindex := index;
          lastcount := count;
        end;
      end
      else begin
        for k := 1 to count do begin
          new(p);
          MyEdit1.FLines.InsertObject(I,'',TObject(p));
          p.linenum := -1;
          p.diffres := ES_INSERT;
          p.tag := -1;
          p.colorStrips := nil;
          p := PLineRecord(MyEdit2.FLines.Objects[J]);
          p.diffres := ES_INSERT;
          J := J + 1;
        end;
        I := I + count;
        FInserted := FInserted + count;
      end;
    end
    else if op = ES_DELETE then begin
      if bGetModify then begin
        if (lastop <> ES_MATCH) then begin
          t := lastcount;
          if lastop = ES_DELETE then begin
            lastcount := lastcount + count;
          end
          else if lastop = ES_INSERT then begin
            h := min(count,lastcount);
            for k := 1 to h do begin
              p := PLineRecord(MyEdit2.FLines.Objects[J]);
              p.diffres := ES_CHANGE;
              p := PLineRecord(MyEdit1.FLines.Objects[I]);
              p.diffres := ES_CHANGE;
              I := I + 1;
              J := J + 1;
            end;
            lastcount := max(count,lastcount) - h;
            FChanged := FChanged + h;
          end;
          if count > t then
            lastop := op;
        end
        else begin
          lastop := ES_DELETE;
          lastindex := index;
          lastcount := count;
        end;
      end
      else begin
        for k := 1 to count do begin
          new(p);
          MyEdit2.FLines.InsertObject(J,'',TObject(p));
          p.linenum := -1;
          p.diffres := ES_DELETE;
          p.tag := -1;
          p.colorStrips := nil;
          p := PLineRecord(MyEdit1.FLines.Objects[I]);
          p.diffres := ES_DELETE;
          I := I + 1;
        end;
        J := J + count;
        FDeleted := FDeleted + count;
      end;
    end;
  end;

  if (bGetModify) and (lastop <> ES_MATCH) then begin
    if lastop = ES_INSERT then begin
      for k := 1 to lastcount do begin
        new(p);
        MyEdit1.FLines.InsertObject(I,'',TObject(p));
        p.linenum := -1;
        p.diffres := ES_INSERT;
        p.tag := -1;
        p.colorStrips := nil;
        p := PLineRecord(MyEdit2.FLines.Objects[J]);
        p.diffres := ES_INSERT;
        J := J + 1;
      end;
      I := I + lastcount;
      FInserted := FInserted + lastcount;
    end
    else if lastop = ES_DELETE then begin
      for k := 1 to lastcount do begin
        new(p);
        MyEdit2.FLines.InsertObject(J,'',TObject(p));
        p.linenum := -1;
        p.diffres := ES_DELETE;
        p.tag := -1;
        p.colorStrips := nil;
        p := PLineRecord(MyEdit1.FLines.Objects[I]);
        p.diffres := ES_DELETE;
        I := I + 1;
      end;
      J := J + lastcount;
      FDeleted := FDeleted + lastcount;
    end;
    lastop := ES_MATCH;
  end;

  UpdateStatusBar;

  a1 := nil;
  a2 := nil;

  if chset <> nil then
    chset.Free;

  es.Free;
  coloring_time := getcurrenttime - coloring_time;

  if FGlobalHalt then
    StatusBar1.Panels[4].Text := 'Halted!'
  else
    StatusBar1.Panels[4].Text := 'Ended.';
{$IFDEF DEBUG0}
  writeln('Hash calculation has taken: ' + IntToStr(hash_calc_time) + ' ms');
  writeln('Diff calculation has taken: ' + IntToStr(diff_calc_time) + ' ms');
  writeln('Coloring has taken: ' + IntToStr(coloring_time) + ' ms');
{$ENDIF}
{$IFDEF LOG0}
  LogForm.AddLine('Hash calculation has taken: ' + IntToStr(hash_calc_time) + ' ms');
  LogForm.AddLine('Diff calculation has taken: ' + IntToStr(diff_calc_time) + ' ms');
  LogForm.AddLine('Coloring has taken: ' + IntToStr(coloring_time) + ' ms');
{$ENDIF}

  MyEdit1.FCompared := true;
  MyEdit2.FCompared := true;
  MyEdit1.FStructCompared := false;
  MyEdit2.FStructCompared := false;

  MyEdit1.Resize;
  MyEdit2.Resize;

  UpdateDiffBitmap;
  EnableAll;
end;

/// procedura na obsluhu tlacitka na toolbare, ktore sposobi, ze sa porovnaju subory metodou porovnavania
/// retazcov s operaciami insert a delete
procedure TFileDiffForm.CompareFilesTBClick(Sender: TObject);
begin
  CalculateDiffs;
end;

/// procedura obsluhujuca zmenu velkosti okna
procedure TFileDiffForm.FormResize(Sender: TObject);
begin
  ResetLayout;
  ResetLayoutTBClick(Sender);
end;

/// procedura obsluhujuca rusenie okna
procedure TFileDiffForm.FormDestroy(Sender: TObject);
begin
  FDestroying := true;
  MyEdit1.Free;
  MyEdit2.Free;
  FDiffBitmap.Free;
  MyEdit1 := nil;
  MyEdit2 := nil;
  if L <> nil then begin
    freelist(L);
  end;
end;

/// procedura obsluhujuca dvojklik na panel nad lavym MyEdit komponentom, po dvojkliku otvori dialog pre otvorenie suboru
procedure TFileDiffForm.Panel1TopDblClick(Sender: TObject);
begin
  if FDisableCount <> 0 then
    Exit;

  OpenDialog1.Title := 'Load Input File 1';
  OpenDialog1.Filter := 'Text Files (*.*)|*.*';
  if OpenDialog1.Execute then
    MyEdit1.LoadFromFile(OpenDialog1.FileName);
end;

/// procedura obsluhujuca dvojklik na panel nad pravym MyEdit komponentom, po dvojkliku otvori dialog pre otvorenie suboru
procedure TFileDiffForm.Panel2TopDblClick(Sender: TObject);
begin
  if FDisableCount <> 0 then
    Exit;

  OpenDialog1.Title := 'Load Input File 2';
  OpenDialog1.Filter := 'Text Files (*.*)|*.*';
  if OpenDialog1.Execute then
    MyEdit2.LoadFromFile(OpenDialog1.FileName);
end;

/// procedura na obsluhu tlacitka na toolbare, ktore sposobi, ze sa porovnaju subory metodou porovnavania
/// retazcov s operaciami insert,delete,update
procedure TFileDiffForm.AdvCompareFilesTBClick(Sender: TObject);
begin
  CalculateDiffs(true);
end;

/// procedura, ktora pri spusteni nejakej potencialne dlho trvajucej operacie
/// znemozni pouzivanie inej takejto operacie, cize vacsina tlacidiel su disabled
procedure TFileDiffForm.DisableAll;
begin
  FDisableCount := FDisableCount + 1;

  if FDisableCount = 1 then begin
    //buttons
    CompareFilesTB.Enabled := false;
    AdvCompareFilesTB.Enabled := false;
    SynchronizedScrollBarsTB.Enabled := false;
    PrevDiffTB.Enabled := false;
    NextDiffTB.Enabled := false;
    Open1TB.Enabled := false;
    Open2TB.Enabled := false;
    StructCompareTB.Enabled := false;
    BasicStructCompareTB.Enabled := false;
    SpecialStructCompareTB.Enabled := false;
    HLFMTTB.Enabled := false;
    Save1TB.Enabled := false;
    Save2TB.Enabled := false;
    ResetLayoutTB.Enabled := false;
    SaveDiffTB.Enabled := false;
    FormattingTB.Enabled := false;
    ShowParseTreesTB.Enabled := false;

    GrammarEditForm.EnterGrammarButton.Enabled := false;
    GrammarEditForm.ExtraButton.Enabled := false;
    LForm.EnterGrammarButton.Enabled := false;
    RForm.EnterGrammarButton.Enabled := false;
    GrammarEditForm.OpenTB.Enabled := false;
    GrammarEditForm.SaveTB.Enabled := false;
    LForm.OpenTB.Enabled := false;
    LForm.SaveTB.Enabled := false;
    RForm.OpenTB.Enabled := false;
    RForm.SaveTB.Enabled := false;
    GrammarEditForm.NewTB.Enabled := false;
    LForm.NewTB.Enabled := false;
    RForm.NewTB.Enabled := false;

    FolderDiffForm.SynchronizedScrollBarsTB.Enabled := false;
    FolderDiffForm.RecursiveFoldersTB.Enabled := false;
    FolderDiffForm.OpenDir1TB.Enabled := false;
    FolderDiffForm.OpenDir2TB.Enabled := false;
    FolderDiffForm.DiffCompareDirsTB.Enabled := false;
    FolderDiffForm.ResetLayoutTB.Enabled := false;
    FolderDiffForm.CompareDirsTB.Enabled := false;
    //menus
    with Mainform do begin
      OpenFile11.Enabled := false;
      OpenFile21.Enabled := false;
      SaveFile11.Enabled := false;
      SaveFile21.Enabled := false;

      ShowFiles1.Enabled := false;
      CompareID1.Enabled := false;
      CompareIDU1.Enabled := false;
      EquivalenceStructuralDifference1.Enabled := false;
      QuickerStructuralDifference1.Enabled := false;
      Formatting1.Enabled := false;
      ShowParseTrees1.Enabled := false;

      Refresh2.Enabled := false;
      PrevDifference1.Enabled := false;
      NextDifference1.Enabled := false;

      SynchronizedScrollBars2.Enabled := false;
      SaveDiffsToFile1.Enabled := false;

      EnterGrammar1.Enabled := false;
      BuildParser1.Enabled := false;
      BuildParseTree1.Enabled := false;
      Open1.Enabled := false;
      Save1.Enabled := false;
      New1.Enabled := false;

      OpenDirectory11.Enabled := false;
      OpenDirectory21.Enabled := false;
      RecursiveSubfolders1.Enabled := false;
      SynchronizedScrollBars1.Enabled := false;
      Refresh1.Enabled := false;
      Compare1.Enabled := false;

      Options1.Enabled := false;
      ShowOptionsTB.Enabled := false;
      QuickStart1.Enabled := false;
    end;
    //double-clicks

    FGlobalHalt := false;
  end;

  Application.ProcessMessages;
end;

/// procedura, ktora po skonceni nejakej potencialne dlho trvajucej operacie
/// opat umozni pouzivanie inej takejto operacie, cize vsetky tlacidla su enabled
procedure TFileDiffForm.EnableAll;
begin
  FDisableCount := FDisableCount - 1;
  if FDisableCount < 0 then begin
    FDisableCount := 0;
  end;
  if FDisableCount = 0 then begin
    //buttons
    CompareFilesTB.Enabled := true;
    AdvCompareFilesTB.Enabled := true;
    SynchronizedScrollBarsTB.Enabled := true;
    PrevDiffTB.Enabled := true;
    NextDiffTB.Enabled := true;
    Open1TB.Enabled := true;
    Open2TB.Enabled := true;
    StructCompareTB.Enabled := true;
    BasicStructCompareTB.Enabled := true;
    SpecialStructCompareTB.Enabled := true;
    HLFMTTB.Enabled := true;
    Save1TB.Enabled := true;
    Save2TB.Enabled := true;
    ResetLayoutTB.Enabled := true;
    SaveDiffTB.Enabled := true;
    FormattingTB.Enabled := true;
    ShowParseTreesTB.Enabled := true;

    FolderDiffForm.SynchronizedScrollBarsTB.Enabled := true;
    FolderDiffForm.RecursiveFoldersTB.Enabled := true;
    FolderDiffForm.OpenDir1TB.Enabled := true;
    FolderDiffForm.OpenDir2TB.Enabled := true;
    FolderDiffForm.DiffCompareDirsTB.Enabled := true;
    FolderDiffForm.ResetLayoutTB.Enabled := true;
    FolderDiffForm.CompareDirsTB.Enabled := true;

    GrammarEditForm.ButtonsEnabling;
    LForm.ButtonsEnabling;
    RForm.ButtonsEnabling;
    GrammarEditForm.OpenTB.Enabled := true;
    GrammarEditForm.SaveTB.Enabled := true;
    LForm.OpenTB.Enabled := true;
    LForm.SaveTB.Enabled := true;
    RForm.OpenTB.Enabled := true;
    RForm.SaveTB.Enabled := true;
    GrammarEditForm.NewTB.Enabled := true;
    LForm.NewTB.Enabled := true;
    RForm.NewTB.Enabled := true;
    //menus
    with Mainform do begin
      OpenFile11.Enabled := true;
      OpenFile21.Enabled := true;
      SaveFile11.Enabled := true;
      SaveFile21.Enabled := true;

      ShowFiles1.Enabled := true;
      CompareID1.Enabled := true;
      CompareIDU1.Enabled := true;
      EquivalenceStructuralDifference1.Enabled := true;
      QuickerStructuralDifference1.Enabled := true;
      Formatting1.Enabled := true;
      ShowParseTrees1.Enabled := true;

      Refresh2.Enabled := true;
      PrevDifference1.Enabled := true;
      NextDifference1.Enabled := true;

      SynchronizedScrollBars2.Enabled := true;
      SaveDiffsToFile1.Enabled := true;

      EnterGrammar1.Enabled := true;
      BuildParser1.Enabled := true;
      BuildParseTree1.Enabled := true;
      Open1.Enabled := true;
      Save1.Enabled := true;
      New1.Enabled := true;

      OpenDirectory11.Enabled := true;
      OpenDirectory21.Enabled := true;
      RecursiveSubfolders1.Enabled := true;
      SynchronizedScrollBars1.Enabled := true;
      Refresh1.Enabled := true;
      Compare1.Enabled := true;

      Options1.Enabled := true;
      ShowOptionsTB.Enabled := true;
      QuickStart1.Enabled := true;
    end;
    //double-clicks

    FGlobalHalt := false;
  end;
  Application.ProcessMessages;
end;

/// procedura obsluhujuca stlacenie Stop Processing tlacidla, ktore zruzi prave prebiehajucu operaciu
procedure TFileDiffForm.HaltTBClick(Sender: TObject);
begin
  FGlobalHalt := true;
//  HaltTB.Enabled := false;
end;

/// procedura zobrazujuca rozdiely medzi dvoma vyznacenymi riadkami, ktore su oznacene ako zmenene a pri
/// pouziti textoveho porovnavania
procedure TMyEdit.ColumnDiff(lineindex: Integer);
var p,r: PLineRecord;
    ME,ME2: TMyEdit;
    ES: TEditScript;
    S1,S2: xString;
    op,index,count: Integer;
    i,i1,i2: Integer;
    x: PSListNode;
begin
  if (FCompared) and (FSelectedLineLCS) and (FYSynchronizedScrollBars) then begin
    ME := FileDiffForm.MyEdit1;
    ME2 := FileDiffForm.MyEdit2;
    if (ME.Fselectedlineindex > Pred(ME.FLines.count)) or (ME2.Fselectedlineindex > Pred(ME2.FLines.count)) then begin
      Exit;
    end;
    p := PLineRecord(ME.FLines.Objects[ME.Fselectedlineindex]);
    if (p <> nil) and (p.diffres = ES_CHANGE) then begin
      if ME.FLCSRecord <> nil then
        dispose(ME.FLCSRecord);
      new(ME.FLCSRecord);
      ME.FLCSRecord^ := p^;
      ME.FLCSLine := ME.FLines[Fselectedlineindex];

      r := PLineRecord(ME2.FLines.Objects[ME2.Fselectedlineindex]);
      if (r <> nil) and (r.diffres = ES_CHANGE) then begin
        if ME2.FLCSRecord <> nil then
          dispose(ME2.FLCSRecord);
        new(ME2.FLCSRecord);
        ME2.FLCSRecord^ := r^;
        ME2.FLCSLine := ME2.FLines[Fselectedlineindex];
      end;
      FileDiffForm.DisableAll;
      calcStringDiff(ME.FLCSLine,ME2.FLCSLine,ES);
      FileDiffForm.EnableAll;
      ES.getNextReset;
      i1 := 1; i2 := 1; i := 1;
      if ME.FLCSRecord.colorStrips <> nil then
        ME.FLCSRecord.colorStrips.Free;
      if ME2.FLCSRecord.colorStrips <> nil then
        ME2.FLCSRecord.colorStrips.Free;
      ME.FLCSRecord.colorStrips := TSortedDLinkedList.create;
      ME2.FLCSRecord.colorStrips := TSortedDLinkedList.create;
      while ES.getNext(op,index,count) do begin
        if op = ES_INSERT then begin
          ME.FLCSRecord.colorStrips.add(i,count,DARKER_COLOR(COLOR_INSERT),TCOLOR_INSERT,0);
          ME2.FLCSRecord.colorStrips.add(i,count,DARKER_COLOR(COLOR_INSERT),TCOLOR_INSERT,0);
          S1 := S1 + dupestring(' ',count);
          S2 := S2 + copy(ME2.FLCSLine,i2,count);
          i2 := i2 + count;
        end
        else if op = ES_DELETE then begin
          ME.FLCSRecord.colorStrips.add(i,count,DARKER_COLOR(COLOR_DELETE),TCOLOR_DELETE,0);
          ME2.FLCSRecord.colorStrips.add(i,count,DARKER_COLOR(COLOR_DELETE),TCOLOR_DELETE,0);
          S1 := S1 + copy(ME.FLCSLine,i1,count);
          S2 := S2 + dupestring(' ',count);
          i1 := i1 + count;
        end
        else if op = ES_CHANGE then begin
          ME.FLCSRecord.colorStrips.add(i,count,DARKER_COLOR(COLOR_CHANGE),TCOLOR_CHANGE,0);
          ME2.FLCSRecord.colorStrips.add(i,count,DARKER_COLOR(COLOR_CHANGE),TCOLOR_CHANGE,0);
          S1 := S1 + copy(ME.FLCSLine,i1,count);
          S2 := S2 + copy(ME2.FLCSLine,i2,count);
          i1 := i1 + count;
          i2 := i2 + count;
        end
        else if op = ES_MATCH then begin
          ME.FLCSRecord.colorStrips.add(i,count,COLOR_TEXTBACKGROUND,TCOLOR_MATCH,0);
          ME2.FLCSRecord.colorStrips.add(i,count,COLOR_TEXTBACKGROUND,TCOLOR_MATCH,0);
          S1 := S1 + copy(ME.FLCSLine,i1,count);
          S2 := S2 + copy(ME2.FLCSLine,i2,count);
          i1 := i1 + count;
          i2 := i2 + count;
        end;
        i := i + count;
      end;
      ME.FLCSLine := S1;
      ME2.FLCSLine := S2;

      if Length(S1) > ME.FMaxLineLength then
        ME.FMaxLineLength := Length(S1);
      if Length(S2) > ME2.FMaxLineLength then
        ME2.FMaxLineLength := Length(S2);

      p.colorStrips := ME.FLCSRecord.colorStrips;
      r.colorStrips := ME2.FLCSRecord.colorStrips;
      ES.Free;
(*      writeln(S1);
      writeln(S2);
      p.colorStrips.getNextReset;
      while p.colorStrips.getNext(x) do begin
        writeln(x.key,' ',x.data,' ',x.tag1,' ',x.tag2);
      end;*)
    end;
  end;
end;

/// procedura obsluhujuca kliknutie mysou
procedure TMyEdit.MouseButtonDown(var Msg: TMessage);
var mpos: TPoint;
    X,Y: Integer;
    row,col: Integer;
begin
  mpos := Mouse.CursorPos;
  X := ScreenToClient(mpos).X;
  Y := ScreenToClient(mpos).Y;

  row := Y div FCharHeight;

  if FYSynchronizedScrollBars then begin
    FileDiffForm.MyEdit1.Fselectedlineindex := row + FWindowCharPos.Y;
    FileDiffForm.MyEdit2.Fselectedlineindex := row + FWindowCharPos.Y;

    FileDiffForm.MyEdit1.ColumnDiff(-1);

    FileDiffForm.MyEdit1.Repaint;
    FileDiffForm.MyEdit2.Repaint;
  end
  else begin
    Fselectedlineindex := row + FWindowCharPos.Y;
    Repaint;
  end;

  if self = FileDiffForm.MyEdit2 then begin
    FileDiffForm.FLeftFocused := false;
  end
  else begin
    FileDiffForm.FLeftFocused := true;
  end;
//  FileDiffForm.Caption := IntToStr(X) + ' , ' + IntToStr(Y);
end;

/// procedura obsluhujuca dvojklik mysou
procedure TMyEdit.MouseDoubleClick(var Msg: TMessage);
var XForm,YForm: TGrammarEditForm;
    I: Integer;
    line: Integer;
    plinerec: PLineRecord;
    MyEd1,MyEd2: TMyEdit;
begin
  if FDisableCount <> 0 then
    Exit;

  if FileDiffForm.FLeftFocused then begin
    XForm := LForm;
    YForm := RForm;
    MyEd1 := self;
    MyEd2 := FileDiffForm.MyEdit2;
  end
  else begin
    XForm := RForm;
    YForm := LForm;
    MyEd1 := self;
    MyEd2 := FileDiffForm.MyEdit1;
  end;

  if not XForm.LooseChangesConfirm(self) then
    Exit;

  if (XForm.FLoadedPath <> FPath) or (XForm.RichEdit1.Modified = true) then begin
    XForm.InternalOpen(XForm,FPath);
  end;

  if FYSynchronizedScrollBars then begin
    if not YForm.LooseChangesConfirm(self) then
      Exit;
    if (YForm.FLoadedPath <> MyEd2.FPath) or (YForm.RichEdit1.Modified = true) then begin
      YForm.InternalOpen(YForm,MyEd2.FPath);
    end;
  end;

  I := 0;
  line := 0;
  
  if not FCompared then
    line := Fselectedlineindex
  else begin
    I := Fselectedlineindex;
    if I < 0 then
      I := 0
    else if I > FLines.Count - 1 then
      I := FLines.Count - 1;

    while I >= 0 do begin
      plinerec := PLineRecord(FLines.Objects[I]);
      line := plinerec.linenum;
      if line >= 0 then
        break;
      I := I - 1;
    end;
  end;

  XForm.CaretGotoLine(line);

  if FYSynchronizedScrollBars then begin
    if not MyEd2.FCompared then
      line := MyEd2.Fselectedlineindex
    else begin
      while I >= 0 do begin
        plinerec := PLineRecord(MyEd2.FLines.Objects[I]);
        line := plinerec.linenum;
        if line >= 0 then
          break;
        I := I - 1;
      end;
    end;

    YForm.CaretGotoLine(line);

    XForm.RichEdit1.SetFocus;
  end;

(*
  XForm.RichEdit1.Clear;
  for I := 0 to FLines.count - 1 do begin
    XForm.RichEdit1.Lines.Add(FLines[I]);
  end;
*)
end;

/// procedura na oznacenie zaciatku predosleho bloku zmien v porovnanych suboroch
procedure TFileDiffForm.PrevDiffTBClick(Sender: TObject);
var I,J: Integer;
    lastop: Integer;
    plinerec: PLineRecord;
    MyEd1,MyEd2: TMyEdit;
begin
  if ((not MyEdit1.FCompared) or (not MyEdit2.FCompared)) and ((not MyEdit1.FStructCompared) or (not MyEdit2.FStructCompared)) then
    Exit;
  if FileDiffForm.FLeftFocused then begin
    MyEd1 := FileDiffForm.MyEdit1;
    MyEd2 := FileDiffForm.MyEdit2;
  end
  else begin
    MyEd1 := FileDiffForm.MyEdit2;
    MyEd2 := FileDiffForm.MyEdit1;
  end;

  I := MyEd1.Fselectedlineindex;
  lastop := ES_NOT_COMPARED;
  if (MyEd1.Fselectedlineindex < 0) then begin
    I := MyEd1.FLines.Count - 1;
    if MyEd1.FLines.Count > 0 then begin
      plinerec := PLineRecord(MyEd1.FLines.Objects[I]);
      if (plinerec.diffres = ES_NOT_COMPARED) or (plinerec.diffres = ES_MATCH) or (plinerec.diffres = -1) then begin
      end
      else begin
        lastop := plinerec.diffres;
        MyEd1.Fselectedlineindex := I;
      end;
    end;
  end
  else if (MyEd1.Fselectedlineindex >= MyEd1.FLines.Count) then begin
    I := MyEd1.FLines.Count - 1;
    if MyEd1.FLines.Count > 0 then begin
      plinerec := PLineRecord(MyEd1.FLines.Objects[I]);
      if (plinerec.diffres = ES_NOT_COMPARED) or (plinerec.diffres = ES_MATCH) or (plinerec.diffres = -1) then begin
      end
      else begin
        lastop := plinerec.diffres;
        MyEd1.Fselectedlineindex := I;
      end;
    end;
  end;

  if lastop = ES_NOT_COMPARED then begin
    plinerec := PLineRecord(MyEd1.FLines.Objects[I]);
    lastop := plinerec.diffres;

    MyEd1.Fselectedlineindex := -1;
    for J := I - 1 downto 0 do begin
      plinerec := PLineRecord(MyEd1.FLines.Objects[J]);
      if (plinerec.diffres <> lastop) then begin
        if (plinerec.diffres = ES_NOT_COMPARED) or (plinerec.diffres = ES_MATCH) or (plinerec.diffres = -1) then begin
          lastop := plinerec.diffres;
        end
        else begin
          MyEd1.Fselectedlineindex := J;
          lastop := plinerec.diffres;
          break;
        end;
      end;
    end;
  end;

  if MyEd1.Fselectedlineindex <> -1 then begin
    I := MyEd1.Fselectedlineindex;
    MyEd1.Fselectedlineindex := 0;
    for J := I - 1 downto 0 do begin
      plinerec := PLineRecord(MyEd1.FLines.Objects[J]);
      if (plinerec.diffres <> lastop) then begin
        MyEd1.Fselectedlineindex := J + 1;
        break;
      end;
    end;
  end;

  if FYSynchronizedScrollBars then begin
    MyEd2.Fselectedlineindex := MyEd1.Fselectedlineindex;
  end;

  if (MyEd1.Fselectedlineindex < MyEd1.FWindowCharPos.Y) or (MyEd1.Fselectedlineindex > MyEd1.FWindowCharPos.Y + MyEd1.FNumOfVisibleLines) then begin
    I := MyEd1.Fselectedlineindex - MyEd1.FWindowCharPos.Y;
    MyEd1.FVScroll.Position := MyEd1.FVScroll.Position + I;
  end
  else begin
    if FYSynchronizedScrollBars then begin
      MyEd1.Repaint;
      MyEd2.Repaint;
    end
    else begin
      MyEd1.Repaint;
    end;
  end;
end;

/// procedura na oznacenie zaciatku nasledujuceho bloku zmien v porovnanych suboroch
procedure TFileDiffForm.NextDiffTBClick(Sender: TObject);
var I,J: Integer;
    lastop: Integer;
    plinerec: PLineRecord;
    MyEd1,MyEd2: TMyEdit;
begin
  if ((not MyEdit1.FCompared) or (not MyEdit2.FCompared)) and ((not MyEdit1.FStructCompared) or (not MyEdit2.FStructCompared)) then
    Exit;
  if FileDiffForm.FLeftFocused then begin
    MyEd1 := FileDiffForm.MyEdit1;
    MyEd2 := FileDiffForm.MyEdit2;
  end
  else begin
    MyEd1 := FileDiffForm.MyEdit2;
    MyEd2 := FileDiffForm.MyEdit1;
  end;

  I := MyEd1.Fselectedlineindex;
  lastop := ES_NOT_COMPARED;
  if (MyEd1.Fselectedlineindex < 0) then begin
    I := 0;
    if MyEd1.FLines.Count > 0 then begin
      plinerec := PLineRecord(MyEd1.FLines.Objects[0]);
      if (plinerec.diffres = ES_NOT_COMPARED) or (plinerec.diffres = ES_MATCH) or (plinerec.diffres = -1) then begin
      end
      else begin
        lastop := plinerec.diffres;
        MyEd1.Fselectedlineindex := 0;
      end;
    end;
  end
  else if (MyEd1.Fselectedlineindex >= MyEd1.FLines.Count) then begin
    I := 0;
    if MyEd1.FLines.Count > 0 then begin
      plinerec := PLineRecord(MyEd1.FLines.Objects[0]);
      if (plinerec.diffres = ES_NOT_COMPARED) or (plinerec.diffres = ES_MATCH) or (plinerec.diffres = -1) then begin
      end
      else begin
        lastop := plinerec.diffres;
        MyEd1.Fselectedlineindex := 0;
      end;
    end;
  end;
  if lastop = ES_NOT_COMPARED then begin
    plinerec := PLineRecord(MyEd1.FLines.Objects[I]);
    lastop := plinerec.diffres;

    MyEd1.Fselectedlineindex := MyEd1.FLines.Count;
    for J := I + 1 to MyEd1.FLines.Count - 1 do begin
      plinerec := PLineRecord(MyEd1.FLines.Objects[J]);
      if (plinerec.diffres <> lastop) then begin
        if (plinerec.diffres = ES_NOT_COMPARED) or (plinerec.diffres = ES_MATCH) or (plinerec.diffres = -1) then begin
          lastop := plinerec.diffres;
        end
        else begin
          MyEd1.Fselectedlineindex := J;
          break;
        end;
      end;
    end;
  end;

  if FYSynchronizedScrollBars then begin
    MyEd2.Fselectedlineindex := MyEd1.Fselectedlineindex;
  end;

  if (MyEd1.Fselectedlineindex < MyEd1.FWindowCharPos.Y) or (MyEd1.Fselectedlineindex > MyEd1.FWindowCharPos.Y + MyEd1.FNumOfVisibleLines) then begin
    I := MyEd1.Fselectedlineindex - MyEd1.FWindowCharPos.Y;
    MyEd1.FVScroll.Position := MyEd1.FVScroll.Position + I;
  end
  else begin
    if FYSynchronizedScrollBars then begin
      MyEd1.Repaint;
      MyEd2.Repaint;
    end
    else begin
      MyEd1.Repaint;
    end;
  end;
end;

/// procedura obsluhujuca stlacenie tlacidla v toolbare
procedure TFileDiffForm.Open1TBClick(Sender: TObject);
begin
  Panel1TopDblClick(Sender);
  UpdateDiffBitmap;
end;

/// procedura obsluhujuca stlacenie tlacidla v toolbare
procedure TFileDiffForm.Open2TBClick(Sender: TObject);
begin
  Panel2TopDblClick(Sender);
  UpdateDiffBitmap;
end;

/// procedura ukladajuca rozdiely v suboroch do vystupneho suboru
procedure TFileDiffForm.SaveDiffTBClick(Sender: TObject);
var lines: TStringList;
    p: PLineRecord;
    lasttype: Integer;
    i,lineno: Integer;
    ln1,ln2: PIntNode;
    size1,size2,index1,index2: Integer;
    ch,cd,ci,cm : Integer;
    i1,i2,ipos: Integer;
    S,S1,S2: xString;
    diffres: Integer;

  procedure _addline(res,posit: Integer; var str1,str2: xString);
  begin
    case res of
       ES_CHANGE: begin
                    lines.add('================================================================================');
                    lines.add('Tokens modified at ' + IntToStr(posit));
                    lines.add('================================================================================');
                    lines.add('/ '+ str1);
                    lines.add('* '+ str2);
                  end;
       ES_INSERT: begin
                    lines.add('================================================================================');
                    lines.add('Tokens inserted at ' + IntToStr(posit));
                    lines.add('================================================================================');
                    lines.add('+ '+ str2);
                  end;
       ES_DELETE: begin
                    lines.add('================================================================================');
                    lines.add('Tokens deleted at ' + IntToStr(posit));
                    lines.add('================================================================================');
                    lines.add('- '+ str1);
                  end;
    end;
    str1 := ''; str2 := '';
  end;

begin
  SaveDialog1.Title := 'Save Differences To File';
  SaveDialog1.InitialDir := FCurrentDirectory;
  SaveDialog1.Filter := 'Text Files (*.txt)|*.txt';
  SaveDialog1.DefaultExt := 'txt';
  if MyEdit1.FCompared then begin
    if SaveDialog1.Execute then begin
      lines := TStringList.create;
      lines.add('Difference Report - '+ formatdatetime(shortdateformat+', '+ShortTimeFormat, now));
      lines.add('================================================================================');
      lines.add('');
      lines.add(format('File 1: "%s"',[MyEdit1.FPath]));
      if (MyEdit1.FPath <> '') and (MyEdit1.FAge > 0) then
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,filedateToDatetime(MyEdit1.FAge)))
      else
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,now));
      lines.add(format('File 2: "%s"',[MyEdit2.FPath]));
      if (MyEdit2.FPath <> '') and (MyEdit2.FAge > 0) then
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,filedateToDatetime(MyEdit2.FAge)))
      else
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,now));
      lines.add('');

      lineno := 0;
      lasttype := -1;
      for i := 0 to Pred(MyEdit1.FLines.count) do begin
        p := PLineRecord(MyEdit1.FLines.Objects[i]);
        if (p.diffres <> ES_MATCH) and (p.diffres <> ES_NOT_COMPARED) then begin
          if p.diffres <> lasttype then begin
            if p.diffres = ES_CHANGE then begin
              lines.add('================================================================================');
              lines.add('Lines modified at ' + IntToStr(lineno));
              lines.add('================================================================================');
              lines.add('/ '+ MyEdit1.FLines[i]);
              lines.add('* '+ MyEdit2.FLines[i]);
              lineno := lineno + 1;
            end
            else if p.diffres = ES_INSERT then begin
              lines.add('================================================================================');
              lines.add('Lines inserted at '+ IntToStr(lineno));
              lines.add('================================================================================');
              lines.add('+ '+ MyEdit2.FLines[i]);
            end
            else if p.diffres = ES_DELETE then begin
              lines.add('================================================================================');
              lines.add('Lines deleted at ' + IntToStr(lineno));
              lines.add('================================================================================');
              lines.add('- '+ MyEdit1.FLines[i]);
              lineno := lineno + 1;
            end
          end
          else begin
            if p.diffres = ES_INSERT then begin
              lines.add('+ '+ MyEdit2.FLines[i]);
            end
            else if p.diffres = ES_DELETE then begin
              lines.add('- '+ MyEdit1.FLines[i]);
              lineno := lineno + 1;
            end
            else if p.diffres = ES_CHANGE then begin
              lines.add('/ '+ MyEdit1.FLines[i]);
              lines.add('* '+ MyEdit2.FLines[i]);
              lineno := lineno + 1;
            end
          end;
        end
        else begin
          lineno := lineno + 1;
        end;
        lasttype := p.diffres;
      end;
      lines.SaveToFile(SaveDialog1.FileName);

      lines.Free;
    end;
  end
  else if MyEdit1.FStructCompared then begin
    if SaveDialog1.Execute then begin
      lines := TStringList.create;
      lines.add('Structural Difference Report - ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,now));
      lines.add('================================================================================');
      lines.add('');
      lines.add(format('Grammar: "%s"',[GrammarEditForm.FLoadedPath]));
      if (GrammarEditForm.FLoadedPath <> '') and (GrammarEditForm.FAge > 0) then
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,filedateToDatetime(GrammarEditForm.FAge)))
      else
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,now));
      lines.add('');
      lines.add(format('File 1: "%s"',[MyEdit1.FPath]));
      if (MyEdit1.FPath <> '') and (MyEdit1.FAge > 0) then
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,filedateToDatetime(MyEdit1.FAge)))
      else
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,now));
      lines.add(format('File 2: "%s"',[MyEdit2.FPath]));
      if (MyEdit2.FPath <> '') and (MyEdit2.FAge > 0) then
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,filedateToDatetime(MyEdit2.FAge)))
      else
        lines.add('        Last modified on ' + formatdatetime(shortdateformat + ', ' + ShortTimeFormat,now));
      lines.add('');
      lineno := 0;

      ln1 := nil;
      ln2 := nil;
      if L <> nil then begin
        ln1 := L.head;
        if ln1 <> nil then
          ln2 := ln1.next;
      end;
      if LForm.FRoot = nil then
        size1 := 0
      else
        size1 := LForm.FTreeSize;

      if RForm.FRoot = nil then
        size2 := 0
      else
        size2 := RForm.FTreeSize;

      if (size1 > Length(ia1)) or (size2 > Length(ia2)) then begin
        LogForm.AddLine('Saving of differences failed: parse tree / preorder array inconsistent');
      end;

      //preorder both trees and save diffs to file
      index1 := 1; index2 := 1;
      diffres := ES_MATCH; ipos := 0;
      ch := 0; cd := 0; ci := 0; cm := 0;

      while (index1 <= size1) or (index2 <= size2) do begin
        if (index1 and CYCLE_THRESHOLD = 0) or (index2 and CYCLE_THRESHOLD = 0) then begin
          FileDiffForm.SimpleUpdateStatusBar(ch,cd,ci,cm);
          Application.ProcessMessages;
        end;
        if FGlobalHalt then
          break;
        begin
          //ln1,ln2 <> nil because ia1,ia2 are without nil and index1,index2 <= size1,size2
          if (index1 <= size1) and (index2 <= size2) and (ln1 <> nil) and (PToken(ln1.i) = ia1[index1]) and (PToken(ln2.i) = ia2[index2]) then begin
            if (ia1[index1].tokentype <= MAX_TERMINAL) then begin
                i1 := Length(ia1[index1].value);
                i2 := Length(ia2[index2].value);
//                S1 := ia1[index1].value;
//                S2 := ia2[index2].value;
                if (i1 = i2) and (ia1[index1].value = ia2[index2].value) then begin
                  if (diffres <> ES_MATCH) then
                    _addline(diffres,ipos,S1,S2);
                  ipos := ia1[index1].endp + 1;
                  diffres := ES_MATCH;
                  cm := cm + 1;
                end
                else begin
(*                  if i1 > i2 then begin
                    S := ia2[index2].value + dupestring(' ',i1 - i2);
                  end
                  else if i2 > i1 then begin
                    S := ia1[index1].value + dupestring(' ',i2 - i1);
                  end;
                  ch := ch + 1;
                  if i2 > i1 then begin
                    S1 := S1 + S
                  end
                  else begin
                    S1 := S1 + ia1[index1].value
                  end;
                  if i1 > i2 then begin
                    S2 := S2 + S
                  end
                  else begin
                    S2 := S2 + ia2[index2].value;
                  end;*)
                  if (diffres <> ES_CHANGE) then begin
                    if (diffres <> ES_MATCH) then
                      _addline(diffres,ipos,S1,S2);
                    ipos := ia1[index1].startp;
                    diffres := ES_CHANGE;
                  end;
                  S1 := S1 + ' ' + ia1[index1].value;
                  S2 := S2 + ' ' + ia2[index2].value;
                  ch := ch + 1;
                end;
            end;

            //move pointers in mapping and preorder arrays
            ln1 := ln1.next;
            if ln1 <> nil then
              ln1 := ln1.next;
            ln2 := ln2.next;
            if ln2 <> nil then
              ln2 := ln2.next;
            index1 := index1 + 1;
            index2 := index2 + 1;
          end
          else if (index1 > size1) or ((ln1 <> nil) and (PToken(ln1.i) = ia1[index1])) then begin
            //add to both the tokens of tree2 (in tree1 as insert / in tree2 as delete)
            if (ia2[index2].tokentype <= MAX_TERMINAL) then begin
//                S1 := S1 + dupestring(' ',Length(ia2[index2].value));
                if (diffres <> ES_INSERT) then begin
                  if (diffres <> ES_MATCH) then
                    _addline(diffres,ipos,S1,S2);
//                  ipos := ia1[index1].endp + 1;
                  diffres := ES_INSERT;
                end;
                S2 := S2 + ' ' + ia2[index2].value;
                ci := ci + 1;
            end;

            index2 := index2 + 1;
          end
          else if true or (index2 > size2) or (PToken(ln2.i) = ia2[index2]) then begin
            //add to both the tokens of tree1 (in tree2 as insert / in tree1 as delete)
            //OR
            //none of current tokens is in mapping => add to both from tree1, until ia1[index1] = ln1.i
            //OR
            //anyway
            if (ia1[index1].tokentype <= MAX_TERMINAL) then begin
//                S2 := S2 + dupestring(' ',Length(ia1[index1].value));
                if (diffres <> ES_DELETE) then begin
                  if (diffres <> ES_MATCH) then
                    _addline(diffres,ipos,S1,S2);
                  ipos := ia1[index1].startp;
                  diffres := ES_DELETE;
                end;
                S1 := S1 + ' ' + ia1[index1].value;
                cd := cd + 1;
            end;

            index1 := index1 + 1;
          end;
        end;
      end;

      _addline(diffres,ipos,S1,S2);

      //saving
      lines.SaveToFile(SaveDialog1.FileName);
    end;

    lines.Free;
  end;
end;

/// procedura vytvarajuca bitmapu rozdielov medzi subormi, ktora je potom zobrazovana v oblzniku]
/// uplne vpravo vo File Difference okna
procedure TFileDiffForm.UpdateDiffBitmap;
var i,y: integer;
    p: PLineRecord;
    c: TColor;
    HeightRatio: Real;
    lasttype: Integer;
    lasty: Integer;
begin
  FDiffBitmap.Canvas.Brush.Color := COLOR_TEXTBACKGROUND;

  FDiffBitmap.Height := Screen.Height;
  FDiffBitmap.Width := MyPB.Width;
  FDiffBitmap.Canvas.Pen.Width := 2;
  with FDiffBitmap do
    Canvas.FillRect(Rect(0,0,width,height));
  lasttype := -1;
  lasty := 0;
  y := 0;

  if (MyEdit1.FLines.Count = 0) then begin // or (MyEdit2.FLines.Count = 0) then
    MyPB.Invalidate;
    Exit;
  end;

  HeightRatio := Screen.Height/MyEdit1.FLines.Count;

  with MyEdit1 do begin
    for i := 0 to FLines.Count-1 do begin
      p := PLineRecord(MyEdit1.FLines.Objects[i]);
      if p <> nil then begin
        if p.diffres <> lasttype then begin
          case lasttype of
            ES_INSERT: c := COLOR_INSERT;
            ES_DELETE: c := COLOR_DELETE;
            ES_CHANGE: c := COLOR_CHANGE;
            ES_NOT_COMPARED: c := COLOR_NOT_COMPARED;
            ES_MATCH: c := COLOR_MATCH;
            ES_COMBINED: c := COLOR_COMBINED;
          else
            c := FBackGroundColor;
          end;
          y := trunc(i*HeightRatio);
          {if (lasttype <> ES_MATCH) or (lasttype <> ES_NOT_COMPARED) then} begin
//            FDiffBitmap.Canvas.Pen.Color := c;
            FDiffBitmap.Canvas.Brush.Color := c;
            FDiffBitmap.Canvas.FillRect(Rect(0,lasty,FDiffBitmap.Width,y)); //-1 for effect
(*            FDiffBitmap.Canvas.MoveTo(0,lasty);
            FDiffBitmap.Canvas.LineTo(FDiffBitmap.Width,lasty);
            FDiffBitmap.Canvas.MoveTo(0,y);
            FDiffBitmap.Canvas.LineTo(FDiffBitmap.Width,y);*)
          end;
        end;
        lasttype := p.diffres;
        lasty := y;
      end;
    end;
  end;
  if lasttype >= 0 then begin
    case lasttype of
      ES_INSERT: c := COLOR_INSERT;
      ES_DELETE: c := COLOR_DELETE;
      ES_CHANGE: c := COLOR_CHANGE;
      ES_NOT_COMPARED: c := COLOR_NOT_COMPARED;
      ES_MATCH: c := COLOR_MATCH;
      ES_COMBINED: c := COLOR_COMBINED;
    else
      c := MyEdit1.FBackGroundColor;
    end;
    y := trunc((MyEdit1.FLines.Count)*HeightRatio);
    {if (lasttype <> ES_MATCH) or (lasttype <> ES_NOT_COMPARED) then} begin
      FDiffBitmap.Canvas.Brush.Color := c;
      FDiffBitmap.Canvas.FillRect(Rect(0,lasty,FDiffBitmap.Width,y));
    end;
  end;
  MyPB.Invalidate;
end;

/// procedura zobrazujuca tu cast hlavneho menu, ktora suvisi s File Difference oknom
/// pri ziskani focusu
procedure TFileDiffForm.FormActivate(Sender: TObject);
begin
  MainForm.FileDiff1.Visible := true;
end;

/// procedura skryvajuca tu cast hlavneho menu, ktora suvisi s File Difference oknom
/// pri strate focusu
procedure TFileDiffForm.FormDeactivate(Sender: TObject);
begin
  MainForm.FileDiff1.Visible := false;
end;

/// procedura pocitaju strukturalny rozdiel medzi subormi, metodou mode;
/// mode 0 - porovnavanie metodou TreeScore s mapovanim cisto identickych podstromov;
/// mode 1 - porovnavanie metodou TreeScore s mapovanim podstromov, ktore maju vrcholy s rovnakym labelom;
/// mode 2 - porovnavanie metodou cistou metodou zhora-nadol;
/// mode 3 - zobrazenie vstupnych suborov;
/// mode 4 - zobrazenie parsovacich stromov pre subory;
/// mode 5 - porovnavanie metodou FMES
procedure TFileDiffForm.StructCompare(mode: Integer);
var p: PLineRecord;
    root1,root2: PToken;
    size1,size2: Integer;
    score: Integer;
    lines1,lines2,lines3,lines4: TStringList;
    U,T: TIntIntHashSet;
    hashset: TIntHashSet;
    pdata: PTokenData;
    phighlight: PTokenHighlight;
    k,d: Integer;
    fullresult, cresult: PTreeCompareResult;
    b1,b2: Boolean;
    S: xString;
begin
  //clearing existing lines
  if (MyEdit1 = nil) or (MyEdit2 = nil) then
    Exit;
  if mode = 3 then begin
    if not FEnabledFormatting then begin
      MyEdit1.LoadFromFile(MyEdit1.FPath);
      MyEdit2.LoadFromFile(MyEdit2.FPath);
      Exit;
    end;
  end;

  DisableAll;

  if MyEdit1.FCompared = true then begin
    MyEdit1.LoadFromFile(MyEdit1.FPath);
  end
  else begin
    MyEdit1.ClearObjects;
  end;
  if MyEdit2.FCompared = true then begin
    MyEdit2.LoadFromFile(MyEdit2.FPath);
  end
  else begin
    MyEdit2.ClearObjects;
  end;

  //constructing DFA/LALR, building trees if not already done so
  if GrammarEditForm.FGFL = nil then begin
    if GrammarEditForm.FLoadedPath = '' then begin
//      GrammarEditForm.InternalOpen(self,FCurrentDirectory + 'test\' + 'delphi11.grm');
      GrammarEditForm.OpenTBClick(GrammarEditForm);
    end;
    GrammarEditForm.EnterGrammarButtonClick(GrammarEditForm);
  end;
  if GrammarEditForm.FParser = nil then begin
    GrammarEditForm.BuildParserButtonClick(GrammarEditForm);
  end;
  if MyEdit1.FPath <> LForm.FLoadedPath then begin
    if LForm.FRoot <> nil then begin
      freesubtree(LForm.FRoot);
      LForm.FRoot := nil;
    end;
    LForm.FLoadedPath := '';
  end;
  if LForm.FRoot = nil then begin
    if LForm.FLoadedPath = '' then begin
      LForm.InternalOpen(self,MyEdit1.FPath);
//      LForm.InternalOpen(self,FCurrentDirectory + 'test\' + 'p.pas');
//      LForm.OpenTBClick(LForm);
    end;
    LForm.BuildParseTreeButtonClick(LForm);
  end;
  if MyEdit2.FPath <> RForm.FLoadedPath then begin
    if RForm.FRoot <> nil then begin
      freesubtree(RForm.FRoot);
      RForm.FRoot := nil;
    end;
    RForm.FLoadedPath := '';
  end;
  if RForm.FRoot = nil then begin
    if RForm.FLoadedPath = '' then begin
      RForm.InternalOpen(self,MyEdit2.FPath);
//      RForm.InternalOpen(self,FCurrentDirectory + 'test\' + 'p3.pas');
//      RForm.OpenTBClick(LForm);
    end;
    RForm.BuildParseTreeButtonClick(RForm);
  end;

  root1 := LForm.FRoot;
  root2 := RForm.FRoot;
  size1 := LForm.FTreeSize;
  size2 := RForm.FTreeSize;

  Lines1 := nil;
  Lines2 := nil;

  if (lastmode <> mode) or (GLOBAL_CHANGE) then begin
    if L <> nil then begin
      freelist(L);
    end;
    L := nil;
    GLOBAL_CHANGE := false;
  end;

  lastmode := mode;

  cresult := nil;
  fullresult := nil;

  if mode = 0 then begin
    if L = nil then
      cresult := comparetrees3(root1,size1,root2,size2,GrammarEditForm.FGFL.getRuleTable,L,false);
    if FEnabledFormatting then
      fullresult := ParallelPreOrder(root1,size1,root2,size2,L,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2,0,true,true)
    else
      fullresult := ParallelPrinting(root1,size1,root2,size2,L,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2);
  end
  else if mode = 1 then begin
    if L = nil then
      cresult := comparetrees3(root1,size1,root2,size2,GrammarEditForm.FGFL.getRuleTable,L,true);
//    ParallelPreOrder(root1,size1,root2,size2,L,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2);
//    fullresult := ParallelPrinting(root1,size1,root2,size2,L,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2);
    if FEnabledFormatting then
      fullresult := ParallelPreOrder(root1,size1,root2,size2,L,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2,0,true,true)
    else
      fullresult := ParallelPrinting(root1,size1,root2,size2,L,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2);
  end
  else if mode = 2 then begin
    if L = nil then
      cresult := comparetrees3(root1,size1,root2,size2,GrammarEditForm.FGFL.getRuleTable,L,false,true);
//    ParallelPreOrder(root1,size1,root2,size2,L,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2);
//    fullresult := ParallelPrinting(root1,size1,root2,size2,L,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2);
    if FEnabledFormatting then
      fullresult := ParallelPreOrder(root1,size1,root2,size2,L,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2,0,true,true)
    else
      fullresult := ParallelPrinting(root1,size1,root2,size2,L,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2);
  end
  else if mode = 3 then begin
//    ParallelPreOrder(root1,size1,root2,size2,nil,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2,0,false);
    if FEnabledFormatting then begin
      ParallelPreOrder(root1,size1,nil,0,nil,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2,0,false);
      ParallelPreOrder(nil,0,root2,size2,nil,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines3,Lines4,0,false);
      FreeObjectsFromLines(Lines2);
      Lines2.Free;
      FreeObjectsFromLines(Lines3);
      Lines3.Free;
      Lines2 := Lines4;
    end
    else begin
      MyEdit1.LoadFromFile(MyEdit1.FPath);
      MyEdit2.LoadFromFile(MyEdit2.FPath);
    end;
  end
  else if mode = 4 then begin
//    ParallelPreOrder(root1,size1,root2,size2,nil,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2,0,false);
//    if root1 <> nil then
      b1 := TREE_FORMATTING;
      b2 := MULTI_LINE_COMMENTS_IND;
      TREE_FORMATTING := true;
      MULTI_LINE_COMMENTS_IND := true;
      ParallelPreOrder(root1,size1,nil,0,nil,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2,0,false);
      ParallelPreOrder(nil,0,root2,size2,nil,GrammarEditForm.FGFL.getRuleTable,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines3,Lines4,0,false);
      FreeObjectsFromLines(Lines2);
      Lines2.Free;
      FreeObjectsFromLines(Lines3);
      Lines3.Free;
      Lines2 := Lines4;
      TREE_FORMATTING := b1;
      MULTI_LINE_COMMENTS_IND := b2;
//    end;
  end
  else if mode = 5 then begin
    cresult := comparetrees2(root1,size1,root2,size2);

    freesubtree(root1);
    root1 := nil;
    LForm.FRoot := nil;
//    score := comparetrees(root1,size1,root2,size2);
//    ParallelPreOrder(root1,size1,root2,size2,L,GrammarEditForm.FGFL.FIndTable,GrammarEditForm.FGFL.FHLTable,Lines1,Lines2);
  end;

{$IFDEF SCOREINMAINFORMCAPTION}
  if cresult <> nil then
    MainForm.Caption := IntToStr(cresult.score);
{$ENDIF}

  if Lines1 <> nil then begin
    FreeObjectsFromLines(MyEdit1.FLines);
    MyEdit1.FLines.Free;
    MyEdit1.FLines := Lines1;
  end;

  if Lines2 <> nil then begin
    FreeObjectsFromLines(MyEdit2.FLines);
    MyEdit2.FLines.Free;
    MyEdit2.FLines := Lines2;
  end;

  MyEdit1.FCompared := false;
  MyEdit2.FCompared := false;
  if (mode < 3) or (mode > 4) then begin
    MyEdit1.FStructCompared := true;
    MyEdit2.FStructCompared := true;
  end
  else begin
    MyEdit1.FStructCompared := false;
    MyEdit2.FStructCompared := false;
  end;

  //refreshing controls
  UpdateDiffBitMap;
  MyEdit1.FVScroll.Position := 0;
  MyEdit1.FHScroll.Position := 0;
  if not FYSynchronizedScrollBars then begin
    MyEdit2.FVScroll.Position := 0;
    MyEdit2.FHScroll.Position := 0;
  end;
  MyEdit1.Resize;
  MyEdit2.Resize;
  MyPB.Invalidate;

  k := -1;
  d := -1;

  if fullresult <> nil then begin
    FChanged := fullresult.nc;
    FDeleted := fullresult.nd;
    FInserted := fullresult.ni;
    FMatched := fullresult.nm;
    FMoved := fullresult.nmove;
//    FNotCompared := 0;
    UpdateStatusBar;
//    k := fullresult.score;
//    d := fullresult.nc + fullresult.nd + fullresult.ni + fullresult.nmove;
  end
  else if cresult <> nil then begin
    FChanged := cresult.nc;
    FDeleted := cresult.nd;
    FInserted := cresult.ni;
    FMatched := cresult.nm;
    FMoved := cresult.nmove;
//    FNotCompared := 0;
    UpdateStatusBar;
//    k := cresult.score;
//    d := cresult.nc + cresult.nd + cresult.ni + cresult.nmove;
  end;

  if cresult <> nil then begin
    k := cresult.score;
  end;
  if fullresult <> nil then begin
    d := fullresult.nc + fullresult.nd + fullresult.ni;// + fullresult.nmove;
  end;

  if cresult <> nil then
    dispose(cresult);
  if fullresult <> nil then
    dispose(fullresult);

  if FGlobalHalt then
    S := 'Halted!'
  else
    S := 'Ended.';

  if (k > -1) or (d > -1) then begin
    S := S + ' - ';
    if (k > -1) then
      S := S + 'Score: ' + IntToStr(k) + '  ';
    if (d > -1) then
      S := S + 'Diffs: ' + IntToStr(d) + '  ';
  end;

  StatusBar1.Panels[4].Text := S;
  FLastFunc := mode;
  EnableAll;
end;

/// procedura obsluhujuca tlacidlo na toolbare
procedure TFileDiffForm.StructCompareTBClick(Sender: TObject);
begin
  StructCompare(1);
end;

/// procedura obsluhujuca tlacidlo na toolbare
procedure TFileDiffForm.BasicStructCompareTBClick(Sender: TObject);
begin
  StructCompare(0);
end;

/// procedura obsluhujuca tlacidlo na toolbare
procedure TFileDiffForm.SpecialStructCompareTBClick(Sender: TObject);
var Lines1,Lines2: TStringList;
begin
  StructCompare(5);

(*
  if (LForm <> nil) and (LForm.FRoot <> nil) then begin
    Lines1 := TStringList.create;
    Lines1.Text := toStringSubTree(LForm.FRoot);
  end
  else
    Lines1 := nil;
  if (RForm <> nil) and (RForm.FRoot <> nil) then begin
    Lines2 := TStringList.create;
    Lines2.Text := toStringSubTree(RForm.FRoot);
  end
  else
    Lines2 := nil;

  if Lines1 <> nil then begin
    FreeObjectsFromLines(MyEdit1.FLines);
    MyEdit1.FLines.Free;
    MyEdit1.FLines := Lines1;
  end;

  if Lines2 <> nil then begin
    FreeObjectsFromLines(MyEdit2.FLines);
    MyEdit2.FLines.Free;
    MyEdit2.FLines := Lines2;
  end;

  UpdateDiffBitMap;
  MyEdit1.FVScroll.Position := 0;
  MyEdit1.FHScroll.Position := 0;
  if not SynchronizedScrollBars then begin
    MyEdit2.FVScroll.Position := 0;
    MyEdit2.FHScroll.Position := 0;
  end;
  MyEdit1.Resize;
  MyEdit2.Resize;
  MyPB.Invalidate;
*)
end;

/// procedura obsluhujuca tlacidlo na toolbare
procedure TFileDiffForm.HLFMTTBClick(Sender: TObject);
begin
  StructCompare(3);
end;

/// procedura ukladajuca formatovany vstup v lavej casti okna do vystupneho suboru (potencialne toho isteho, ak islo iba o naformatovanie textu)
procedure TFileDiffForm.Save1TBClick(Sender: TObject);
var myedit: TMyEdit;
begin
  if Sender = Save1TB then
    myedit := MyEdit1
  else
    myedit := MyEdit2;

  if myedit <> nil then begin
    if myedit.FLines <> nil then begin
      SaveDialog1.FileName := myedit.FPath;
      SaveDialog1.Title := 'Save Formatted Input File';
      SaveDialog1.Filter := 'Text Files (*.*)|*.*';
      if SaveDialog1.Execute then begin
        myedit.SaveToFile(SaveDialog1.FileName);
      end;
    end;
  end;
end;

/// procedura ukladajuca formatovany vstup v pravej casti okna do vystupneho suboru (potencialne toho isteho, ak islo iba o naformatovanie textu)
procedure TFileDiffForm.Save2TBClick(Sender: TObject);
begin
  Save1TBClick(Sender);
end;

/// procedura obsluhujuca stlacenie tlacidla Sychronized Scrollbars, prepinajuca synchronizaciu scrollbarov
procedure TFileDiffForm.SynchronizedScrollBarsTBClick(Sender: TObject);
begin
  FYSynchronizedScrollBars := SynchronizedScrollBarsTB.down;
  MainForm.SynchronizedScrollBars2.Checked := SynchronizedScrollBarsTB.down;
end;

/// procedura obsluhujuca stlacenie tlacidla Formatting, prepinajuca formatovanie vstupov
procedure TFileDiffForm.FormattingTBClick(Sender: TObject);
begin
  FEnabledFormatting := FormattingTB.down;
  MainForm.Formatting1.Checked := FormattingTB.down;
end;

/// procedura obsluhujuca tlacidlo na toolbare
procedure TFileDiffForm.ShowParseTreesTBClick(Sender: TObject);
begin
  StructCompare(4);
end;

/// procedura obsluhujuca zobrazovanie hover textu pri pozicii mysy nad statusbarom
procedure TFileDiffForm.StatusBar1MouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin
  StatusBar1.Hint := StatusBar1.Panels[4].Text;
end;

end.
