// =====================================================
//
//   miniLA_win - Main unit
//
//   (c) miniLA Team
//
// =====================================================
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this package; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.



unit dlgMain;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
     StdCtrls, ComCtrls, ExtCtrls, Spin, Grids, Menus, ToolWin, Buttons, Printers,
     ImgList,
     uDisplay,
     dlgColors,
     dlgTrigger,
     dlgChannel,
     dlgHWSetup,
     uCommunication,
     uLATypes,
     uUtils,
     uFileUtils,
     WOwnrScrllBar, Scroll32;

type
  TfrmMain = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Open1: TMenuItem;
    Save1: TMenuItem;
    SaveAs1: TMenuItem;
    N1: TMenuItem;
    Exit1: TMenuItem;
    Setup1: TMenuItem;
    Hardware1: TMenuItem;
    Channel1: TMenuItem;
    Trigger1: TMenuItem;
    StatusBar1: TStatusBar;
    SaveDialog1: TSaveDialog;
    OpenDialog1: TOpenDialog;
    Go1: TMenuItem;
    ImageList1: TImageList;
    TreeView1: TTreeView;
    Colors1: TMenuItem;
    OpenLayout1: TMenuItem;
    SaveLayout1: TMenuItem;
    PrintDialog1: TPrintDialog;
    PrinterSetupDialog1: TPrinterSetupDialog;
    N2: TMenuItem;
    Print1: TMenuItem;
    PrinterSetup1: TMenuItem;
    SetZoomPopupMenu: TPopupMenu;
    Setzoom1: TMenuItem;
    SetPosPopupMenu: TPopupMenu;
    SetPosition1: TMenuItem;
    DisplayPopupMenu: TPopupMenu;
    Placeleftcursor1: TMenuItem;
    Placerightcursorhere1: TMenuItem;
    Setzoom2: TMenuItem;
    Setposition2: TMenuItem;
    Showselection1: TMenuItem;
    Viewall1: TMenuItem;
    Viewfulldetail1: TMenuItem;
    N4: TMenuItem;
    General1: TMenuItem;
    N5: TMenuItem;
    SaveLayoutDialog: TSaveDialog;
    OpenDialog2: TOpenDialog;
    SigChanPopupMenu: TPopupMenu;
    Channels1: TMenuItem;
    View2: TMenuItem;
    ZoomIn1: TMenuItem;
    ZoomOut1: TMenuItem;
    ViewAll2: TMenuItem;
    ViewFullDetail2: TMenuItem;
    ZoomBetweenCursors1: TMenuItem;
    N6: TMenuItem;
    CenterToLeftCursor1: TMenuItem;
    CenterToRightCursor1: TMenuItem;
    CenterToTriggerCursor1: TMenuItem;
    Help1: TMenuItem;
    About1: TMenuItem;
    ToolBar1: TToolBar;
    btnSave: TToolButton;
    btnPrint: TToolButton;
    btnLeftCursor: TToolButton;
    btnTrigCursor: TToolButton;
    btnRightCursor: TToolButton;
    btnZoomOut: TToolButton;
    btnZoomCursor: TToolButton;
    btnZoomIn: TToolButton;
    btnStart: TToolButton;
    btnTimeBaseInc: TToolButton;
    btnTimeBaseDec: TToolButton;
    Sep02: TToolButton;
    Panel1: TPanel;
    tbZoom: TTrackBar;
    Panel2: TPanel;
    cbTimebase: TComboBox;
    pnDispVal: TPanel;
    Sep03: TToolButton;
    Sep05: TToolButton;
    ExportDialog: TSaveDialog;
    ExportBINandINIFIle1: TMenuItem;
    N3: TMenuItem;
    ToolButton1: TToolButton;
    N7: TMenuItem;
    ViewPort1: TMenuItem;
    Splitter1: TSplitter;
    Splitter2: TSplitter;
    pnLog: TPanel;
    sgSigNames: TStringGrid;
    sgSigValues: TStringGrid;
    pnSigTree: TPanel;
    SignalTreeWindow: TMenuItem;
    spTreeNames: TSplitter;
    Panel4: TPanel;
    Panel5: TPanel;
    Decoders1: TMenuItem;
    RS232Decoder: TMenuItem;
    I2CDecoder: TMenuItem;
    SPIDecoder: TMenuItem;
    btnOpen: TToolButton;
    INIExport: TMenuItem;
    VCDExport: TMenuItem;
    ImportData1: TMenuItem;
    BINImport: TMenuItem;
    ImportDialog: TOpenDialog;
    Centertonextchange1: TMenuItem;
    Centertopreviouschange1: TMenuItem;
    N8: TMenuItem;
    N9: TMenuItem;
    PWMDecoder1: TMenuItem;
    N10: TMenuItem;
    smSignalValue: TMenuItem;
    smRiseEdges: TMenuItem;
    smFallEdges: TMenuItem;
    smBothEdges: TMenuItem;
    Displayedvalue1: TMenuItem;
    Signalname1: TMenuItem;
    Base1: TMenuItem;
    smDec: TMenuItem;
    smOct: TMenuItem;
    smBin: TMenuItem;
    smHex: TMenuItem;
    Scrollbar2: TWOwnrScrllBar;
    procedure FormCreate(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure tbZoomChange(Sender: TObject);
    procedure ScrollBar2Change(Sender: TObject);
    procedure Exit1Click(Sender: TObject);
    procedure Trigger1Click(Sender: TObject);
    procedure Channel1Click(Sender: TObject);
    procedure ViewAllClick(Sender: TObject);
    procedure ZoomOutClick(Sender: TObject);
    procedure ZoomInClick(Sender: TObject);
    procedure ViewCur1Click(Sender: TObject);
    procedure ViewCur2Click(Sender: TObject);
    procedure ViewTriggerClick(Sender: TObject);
    procedure ViewFullClick(Sender: TObject);
    procedure StringGridDrawCell(Sender: TObject; Col, Row: Integer; Rect: TRect; State: TGridDrawState);
    procedure Colors1Click(Sender: TObject);
    procedure PrinterSetup1Click(Sender: TObject);
    procedure Print1Click(Sender: TObject);
    procedure StatusBar1DrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect);
    procedure Setzoom1Click(Sender: TObject);
    procedure SetPosition1Click(Sender: TObject);
    procedure PlaceCursor1Click(Sender: TObject);
    procedure PlaceCursor2Click(Sender: TObject);
    procedure Go1Click(Sender: TObject);
    procedure Hardware1Click(Sender: TObject);
    procedure General1Click(Sender: TObject);
    procedure SaveAs1Click(Sender: TObject);
    procedure Save1Click(Sender: TObject);
    procedure Open1Click(Sender: TObject);
    procedure OpenLayout1Click(Sender: TObject);
    procedure SaveLayout1Click(Sender: TObject);
    procedure About1Click(Sender: TObject);
    procedure ToolBtnClick(Sender: TObject);
    procedure INIExportClick(Sender: TObject);
    procedure ZoomCursorsClick(Sender: TObject);
    procedure ViewPort1Click(Sender: TObject);
    procedure SignalTreeWindowClick(Sender: TObject);
    procedure RS232DecoderClick(Sender: TObject);
    procedure I2CDecoderClick(Sender: TObject);
    procedure SPIDecoderClick(Sender: TObject);
    procedure VCDExportClick(Sender: TObject);
    procedure BINImportClick(Sender: TObject);
    procedure Centertonextchange1Click(Sender: TObject);
    procedure Centertopreviouschange1Click(Sender: TObject);
    procedure PWMDecoder1Click(Sender: TObject);
    procedure sgSigValuesContextPopup(Sender: TObject; MousePos: TPoint;
      var Handled: Boolean);
    procedure smDispValueClick(Sender: TObject);
    procedure smBaseClick(Sender: TObject);
  private
    { Private declarations }
    FirstProgramRun: boolean;
    procedure SetTimebaseText;
  public
    { Public declarations }
    LogPanel: TLogPanel;
    Analyzer: TAnalyzer;
    FileUtils: TFileUtils;
    destructor Destroy; override;
    procedure SaveData(Filename: string);
    procedure StateChange(Sender:TObject);
    procedure ShowData(Sender:TObject);
    procedure tbZoomUpdatePosition(pos: integer);
    procedure FormStart;
  end;

var
  frmMain: TfrmMain;


implementation

uses dlgSetZoomPos, dlgSetup, dlgAbout, dlgPortStatus, dlgDec232, dlgDecI2C,
     dlgDecDisplay, dlgDecSPI, dlgDecPWM, uINIfile;

{$R *.DFM}


procedure TfrmMain.FormCreate(Sender: TObject);
begin
  Caption := 'miniLA '+ProgVersion;
  OpenINIFile;					// open INI file for opening forms

  Analyzer:= TAnalyzer.Create;
  Analyzer.OnStateChange:=StateChange;

  LogPanel:= TLogPanel.Create(pnLog);
  LogPanel.Parent:=pnLog;
  LogPanel.Display.OnMouseMove:= ShowData;

  FileUtils := TFileUtils.Create;


  sgSigNames.Color := LogPanel.Display.BackColor;
  sgSigValues.Color := LogPanel.Display.BackColor;

  cbTimebase.ItemIndex:= 0;
  SetTimebaseText;
end;

procedure TfrmMain.FormStart;
begin
  frmChannel.btnOKClick(nil);
  PrepareDispData(frmMain.LogPanel.Display.DataSet);
  frmMain.Analyzer.Init;
  FirstProgramRun := INIFile.ReadBool('MAIN', 'FIRSTPROGRAMRUN', true);
  if FirstProgramRun then
   begin
     About1Click(self);
     FirstProgramRun := false;
   end;
end;

destructor TfrmMain.Destroy;
begin
  INIFile.WriteBool('MAIN', 'FIRSTPROGRAMRUN', FirstProgramRun);
  Analyzer.Free;
  inherited Destroy;
  CloseINIFile;
end;

procedure TfrmMain.FormResize(Sender: TObject);
begin
   if (csDestroying in ComponentState) then exit;

   LogPanel.SetHeight(LogPanel.Parent.ClientHeight-20);

   ScrollBar2.Top:=LogPanel.Parent.ClientHeight-ScrollBar2.Height-1;
   ScrollBar2.Left:=LogPanel.Left+1;
   ScrollBar2.Width:=LogPanel.Width-2;

   sgSigNames.Height := sgSigNames.Parent.ClientHeight-23-20;
   sgSigNames.DefaultColWidth := sgSigNames.Width;
   sgSigValues.Height := sgSigValues.Parent.ClientHeight-23-20;
   sgSigValues.DefaultColWidth := sgSigValues.Width;

   LogPanel.UpdateCursors;
   showdata(self);
end;

procedure TfrmMain.ScrollBar2Change(Sender: TObject);
begin
   LogPanel.SetPosition(ScrollBar2.Position);
end;

procedure TfrmMain.Exit1Click(Sender: TObject);
begin
   Close;
end;


procedure TfrmMain.ZoomInClick(Sender: TObject);
begin
  LogPanel.SetZoom(LogPanel.Display.Zoom*2);
end;


procedure TfrmMain.ZoomOutClick(Sender: TObject);
begin
   LogPanel.SetZoom(LogPanel.Display.Zoom / 2);
end;

procedure TfrmMain.tbZoomChange(Sender: TObject);
begin
   LogPanel.SetZoom(IntExp2(tbZoom.Position)*100);
end;

// updates tbZoom without firing the OnChange event
procedure TfrmMain.tbZoomUpdatePosition(pos: integer);
var
   oldNotify	:TNotifyEvent;
begin
   oldNotify := tbZoom.OnChange;
   tbZoom.OnChange := nil;
   tbZoom.Position:=pos;
   tbZoom.OnChange := oldNotify;
end;

procedure TfrmMain.ZoomCursorsClick(Sender: TObject);
begin
   LogPanel.SetViewRange(LogPanel.Cursors[0].Position,LogPanel.Cursors[1].Position);
end;


procedure TfrmMain.Channel1Click(Sender: TObject);
begin
  if frmChannel.ShowModal=mrOk then
   begin
     PrepareDispData(frmMain.LogPanel.Display.DataSet);
     LogPanel.Display.Recalc(0,0,LogPanel.Display.Width,LogPanel.Display.Height,LogPanel.Display.Bitmap.Canvas);
     LogPanel.Repaint;
   end;
end;

procedure TfrmMain.ViewAllClick(Sender: TObject);
begin
   LogPanel.SetZoom(100);
end;

procedure TfrmMain.ViewCur1Click(Sender: TObject);
begin
   if   LogPanel.Cursors[0].Visible then exit
   else LogPanel.SetCenterPosition(LogPanel.Cursors[0].Position);
end;

procedure TfrmMain.ViewCur2Click(Sender: TObject);
begin
  if LogPanel.Cursors[1].Visible then exit
  else LogPanel.SetCenterPosition(LogPanel.Cursors[1].Position)
end;

procedure TfrmMain.ViewTriggerClick(Sender: TObject);
begin
  if LogPanel.Cursors[2].Visible then exit;
  LogPanel.SetCenterPosition(LogPanel.Cursors[2].Position);
end;

procedure TfrmMain.ViewFullClick(Sender: TObject);
begin
   LogPanel.SetZoom(LogPanel.Display.MaxZoom);
end;


procedure TfrmMain.Colors1Click(Sender: TObject);
begin
  if frmColors.ShowModal=mrOk then
   begin
   end;
end;


procedure TfrmMain.PrinterSetup1Click(Sender: TObject);
begin
  PrinterSetupDialog1.Execute;
end;

procedure TfrmMain.Print1Click(Sender: TObject);
var BC,SC:TColor;
begin
  if PrintDialog1.Execute then begin
    Printer.BeginDoc;

    // draw into printer's canvas
    BC:=LogPanel.Display.BackColor;
    SC:=LogPanel.Display.ScaleColor;
    LogPanel.Display.BackColor:=clWhite;
    LogPanel.Display.ScaleColor:=clBlack;
    if Printer.PageWidth > Printer.PageHeight then
       // landscape
       LogPanel.Display.Recalc(Printer.PageWidth div 20,
                               Printer.PageWidth div 20,
                               18*Printer.PageWidth div 20,
                               17*Printer.PageHeight div 20,
                               Printer.Canvas)
    else
       // Portrait
       LogPanel.Display.Recalc(Printer.PageWidth div 14,
       			       Printer.PageWidth div 14,
                               12*Printer.PageWidth div 14,
			       9*Printer.PageWidth div 14,
                               Printer.Canvas);

    LogPanel.Display.BackColor:=BC;
    LogPanel.Display.ScaleColor:=SC;

    // redraw on screen to restore internal variables (ZoomRatio etc.)
    LogPanel.Display.Recalc(0,0,LogPanel.Display.Width,LogPanel.Display.Height,LogPanel.Display.Bitmap.Canvas);

    // debug info
    //StatusBar1.Panels[1].Text:=IntToStr(Printer.PageWidth);
    //StatusBar1.Panels[2].Text:=IntToStr(Printer.PageHeight);
    Printer.EndDoc;
  end;
end;

procedure TfrmMain.SetZoom1Click(Sender: TObject);
begin
  frmSetZoomPos.Caption:='Set zoom';
  frmSetZoomPos.SpinEdit1.MaxValue:=IntLog2(round(LogPanel.Display.MaxZoom / 100))+1;
  frmSetZoomPos.SpinEdit1.MinValue:=1;
  frmSetZoomPos.Label1.Caption:='Range from 1x to '+IntToStr(IntLog2(round(LogPanel.Display.MaxZoom / 100))+1)+'x';
  frmSetZoomPos.SpinEdit1.Value:=tbZoom.Position+1;
  if frmSetZoomPos.ShowModal=mrOk then
     LogPanel.SetZoom(IntExp2(frmSetZoomPos.SpinEdit1.Value-1)*100);
end;

procedure TfrmMain.SetPosition1Click(Sender: TObject);
var
   min, max	: integer;
begin
   min := -1*LogPanel.Cursors[2].Position;
   max := LogPanel.Display.DataHold.MaxPosition - LogPanel.Cursors[2].Position;
   frmSetZoomPos.Caption:='Set position';
   frmSetZoomPos.SpinEdit1.MaxValue:=max;
   frmSetZoomPos.SpinEdit1.MinValue:=min;
   frmSetZoomPos.SpinEdit1.Value:=LogPanel.Display.Position - LogPanel.Cursors[2].Position;
   frmSetZoomPos.Label1.Caption:='Range from '+IntToStr(min)+' to '+IntToStr(max);
   if frmSetZoomPos.ShowModal=mrOk then
    begin
      LogPanel.SetCenterPosition(frmSetZoomPos.SpinEdit1.Value+LogPanel.Cursors[2].Position);
    end;
end;

procedure TfrmMain.PlaceCursor1Click(Sender: TObject);
begin
   LogPanel.SetSnapCursorPosition(0, LogPanel.Cursors[3].Position);
   ScrollBar2.SetCursorPosition(0, LogPanel.Cursors[3].Position);
end;

procedure TfrmMain.PlaceCursor2Click(Sender: TObject);
begin
   LogPanel.SetSnapCursorPosition(1, LogPanel.Cursors[3].Position);
   ScrollBar2.SetCursorPosition(1, LogPanel.Cursors[3].Position);
end;

procedure TfrmMain.CenterToNextChange1Click(Sender: TObject);
begin
   LogPanel.CenterChange(true);
end;

procedure TfrmMain.CenterToPreviousChange1Click(Sender: TObject);
begin
   LogPanel.CenterChange(false);
end;


procedure TfrmMain.ViewPort1Click(Sender: TObject);
begin
   if not(frmPortStatus.Visible) then
      frmPortStatus.Show
   else
      frmPortStatus.Hide;
end;

procedure TfrmMain.SignalTreeWindowClick(Sender: TObject);
begin
   if pnSigTree.Visible then
    begin
      spTreeNames.Visible := false;
      pnSigTree.Visible := false;
    end
   else
    begin
      pnSigTree.Visible := true;
      spTreeNames.Visible := true;
    end;

   SignalTreeWindow.Checked := pnSigTree.Visible;
end;

procedure TfrmMain.SetTimebaseText;
var
   i		: byte;
   per		: real;
   akt_item	: byte;
begin
   akt_item := cbTimebase.ItemIndex;
   cbTimebase.Clear;					// clear all list items
   if not(Analyzer.StateAnalysis) then
    begin
      for i:=0 to 18 do
       begin
          if Analyzer.Timebase_freq[i] <> 0 then
             per := 1/Analyzer.Timebase_freq[i]
          else
             per := 0;
          cbTimebase.Items.Add(TmToStr(per,2)+'s')
       end;
      cbTimebase.Items.Add('EXT. CLK');
      cbTimebase.Enabled := true;
      btnTimebaseDec.Enabled := true;
      btnTimebaseInc.Enabled := true;
      cbTimebase.ItemIndex := akt_item;
    end
   else
    begin
      if Analyzer.Frequency <> 0 then
         per := 1/Analyzer.Frequency
      else
         per := 0;
      cbTimebase.Items.Add(TmToStr(per,2)+'s');
      cbTimebase.ItemIndex := 0;
      cbTimebase.Enabled := false;
      btnTimebaseDec.Enabled := false;
      btnTimebaseInc.Enabled := false;
    end;
end;

procedure TfrmMain.Trigger1Click(Sender: TObject);
begin
   if frmTrigger.ShowModal=mrOk then
      PrepareDispData(LogPanel.Display.DataSet);	// update signal names (might change in trigger menu)
end;


procedure TfrmMain.Hardware1Click(Sender: TObject);
begin
   Analyzer.Timer.Enabled := false;			// no IO access during HW setup
   if frmHWSetup.ShowModal = mrOK then
      SetTimebaseText;					// update timebase values (frequency might be changed)
   Analyzer.Timer.Enabled := true;			// re-enable timer
end;

procedure TfrmMain.General1Click(Sender: TObject);
begin
  if frmSetup.ShowModal=mrOk then begin
  end;
end;

procedure TfrmMain.Save1Click(Sender: TObject);
begin
  if FileUtils.FileName='' then
    SaveAs1Click(Sender)
  else
    SaveData(FileUtils.Filename);
end;

procedure TfrmMain.SaveAs1Click(Sender: TObject);
begin
  SaveDialog1.FileName:=FileUtils.FileName;
  if SaveDialog1.Execute then
    SaveData(SaveDialog1.FileName);
end;


procedure TfrmMain.SaveData(Filename: string);
var s:string;
begin
   frmMain.StatusBar1.Panels[5].Text := 'Saving data ...';
   frmMain.StatusBar1.Repaint;

   if FileUtils.WriteDataToFile(Filename, false)<0 then
     Application.MessageBox('A file save error occured. Please try again.',
       'File Save Error',mb_IconError or mb_Ok)
   else
    begin
      frmMain.Caption:='miniLA '+ProgVersion+' - '+FileUtils.FileName;
      if frmSetup.SaveLayoutWFile then
       begin
         s:=Copy(FileUtils.FileName,1,
	   Length(FileUtils.FileName)-Length(ExtractFileExt(FileUtils.FileName)));
         frmChannel.SaveLayout(s+'.aly');
       end;
    end;

   frmMain.StatusBar1.Panels[5].Text := '';
   frmMain.StatusBar1.Repaint;
end;


procedure TfrmMain.Open1Click(Sender: TObject);
var s,s2: string;
begin
  if OpenDialog1.Execute then
   begin
    frmMain.StatusBar1.Panels[5].Text := 'Loading data ...';
    frmMain.StatusBar1.Repaint;

    if FileUtils.ReadDataFromFile(OpenDialog1.FileName)<0 then
      Application.MessageBox('An error occured while loading data.','Open File Error',
	mb_IconError or mb_Ok)
    else begin
      s:=Copy(OpenDialog1.FileName,1, Length(OpenDialog1.FileName)-Length(ExtractFileExt(OpenDialog1.FileName)));
      if frmSetup.LoadLayoutWFile and FileExists(s+'.aly') then
	if frmSetup.ConfirmBefore then begin
	  s2:='Do you want to open the layout file "'+s+'.aly" too?';
	  if Application.MessageBox(PChar(s2),'Auto-open layout',mb_IconQuestion or mb_YesNo)=mrYes
	    then if frmChannel.LoadLayout(s+'.aly')<0 then
	      Application.MessageBox('An error occured while loading layout file.','Open layout',
		mb_IconError);
	end else
	  if frmChannel.LoadLayout(s+'.aly')<0 then
	    Application.MessageBox('An error occured while loading layout file.','Open layout',
	      mb_IconError);

      frmMain.Caption:='miniLA '+ProgVersion+' - '+FileUtils.FileName;

      with frmMain.LogPanel.Display do
       begin
	 PrepareDispData(DataSet);
         Recalc(0,0,Width,Height,Bitmap.Canvas);
         Repaint;
       end;

      frmMain.LogPanel.UpdateCursors;
      
    end;

    frmMain.StatusBar1.Panels[5].Text := '';
    frmMain.StatusBar1.Repaint;
   end;

end;

procedure TfrmMain.OpenLayout1Click(Sender: TObject);
begin
  if OpenDialog2.Execute then
    if frmChannel.LoadLayout(OpenDialog2.FileName)<0 then
      Application.MessageBox('An error occured while loading layout.','Open File Error',
	mb_IconError or mb_Ok)
    else begin
      PrepareDispData(frmMain.LogPanel.Display.DataSet);
      LogPanel.Display.Recalc(0,0,LogPanel.Display.Width,LogPanel.Display.Height,LogPanel.Display.Bitmap.Canvas);
      LogPanel.Repaint;
    end;
end;


procedure TfrmMain.SaveLayout1Click(Sender: TObject);
begin
  if SaveLayoutDialog.Execute then
    if frmChannel.SaveLayout(SaveLayoutDialog.FileName)<0 then
      Application.MessageBox('A file save error occured. Please try again.',
	'File Save Error',mb_IconError or mb_Ok);
end;

procedure TfrmMain.BINImportClick(Sender: TObject);
begin
  if ImportDialog.Execute then
   begin
    frmMain.StatusBar1.Panels[5].Text := 'Importing BIN file ...';
    frmMain.StatusBar1.Repaint;

    if FileUtils.ReadBINFile(ImportDialog.FileName)<0 then
      Application.MessageBox('An error occured while loading data.','Open File Error',
	mb_IconError or mb_Ok)
    else
     begin
       with frmMain.LogPanel.Display do
        begin
          Recalc(0,0,Width,Height,Bitmap.Canvas);
          Repaint;
        end;
     end;

    frmMain.LogPanel.UpdateCursors;

    frmMain.StatusBar1.Panels[5].Text := '';
    frmMain.StatusBar1.Repaint;
   end;

end;


procedure TfrmMain.INIExportClick(Sender: TObject);
var s: string;
begin
  s:=Copy(FileUtils.FileName,1, Length(FileUtils.FileName)-Length(ExtractFileExt(FileUtils.FileName)));
  ExportDialog.Filename := s;
  ExportDialog.Filter := 'Logic Analyzer Data File (*.bin)|*.bin|All files (*.*)|*.*';
  if ExportDialog.Execute then
   begin
     frmMain.StatusBar1.Panels[5].Text := 'Exporting BIN file ...';
     frmMain.StatusBar1.Repaint;

     if FileUtils.WriteDataToFile(ExportDialog.Filename, true)<0 then
	Application.MessageBox('A file save error occured. Please try again.',
	  'File Save Error',mb_IconError or mb_Ok)
     else
      begin
	s:=Copy(ExportDialog.FileName,1, Length(ExportDialog.FileName)-Length(ExtractFileExt(ExportDialog.FileName)));
	ExportDialog.Filename := s;
	ExportDialog.Filter := 'Logic Analyzer INI File (*.ini)|*.ini|All files (*.*)|*.*';
	if ExportDialog.Execute then
	 begin
	   frmMain.StatusBar1.Panels[5].Text := 'Exporting INI file ...';
	   frmMain.StatusBar1.Repaint;

	   if FileUtils.WriteINIFile(ExportDialog.Filename)<0 then
	      Application.MessageBox('A file save error occured. Please try again.',
		'File Save Error',mb_IconError or mb_Ok)
	 end;
       end;

     frmMain.StatusBar1.Panels[5].Text := '';
     frmMain.StatusBar1.Repaint;
   end;

end;

procedure TfrmMain.VCDExportClick(Sender: TObject);
var s: string;
begin
  s:=Copy(FileUtils.FileName,1, Length(FileUtils.FileName)-Length(ExtractFileExt(FileUtils.FileName)));
  ExportDialog.Filename := s;
  ExportDialog.Filter := 'VCD File (*.vcd)|*.vcd|All files (*.*)|*.*';
  if ExportDialog.Execute then
   begin
     frmMain.StatusBar1.Panels[5].Text := 'Exporting VCD file ...';
     frmMain.StatusBar1.Repaint;

     if FileUtils.WriteVCDFile(ExportDialog.Filename)<0 then
	Application.MessageBox('A file save error occured. Please try again.',
	  'File Save Error',mb_IconError or mb_Ok);

     frmMain.StatusBar1.Panels[5].Text := '';
     frmMain.StatusBar1.Repaint;
   end;

end;

procedure TfrmMain.I2CDecoderClick(Sender: TObject);
begin
   frmDecI2C.ShowModal;
end;

procedure TfrmMain.PWMDecoder1Click(Sender: TObject);
begin
   frmDecPWM.ShowModal;
end;

procedure TfrmMain.RS232DecoderClick(Sender: TObject);
begin
   frmDec232.ShowModal;
end;

procedure TfrmMain.SPIDecoderClick(Sender: TObject);
begin
   frmDecSPI.ShowModal;
end;

procedure TfrmMain.About1Click(Sender: TObject);
begin
  AboutBox.ShowModal;
end;

//******************************************************************************
//******************************************************************************
//******************************************************************************

//=============================================
procedure TfrmMain.ToolBtnClick(Sender: TObject);
//=============================================
begin
  case (Sender as TToolButton).Tag of
    1: Open1Click (Self);
    2: Save1Click (Self);
    3: Print1Click(Self);
    4: if cbTimebase.ItemIndex<26 then begin
         cbTimebase.ItemIndex:= cbTimebase.ItemIndex+1;
       end;
    5: if cbTimebase.ItemIndex> 0 then begin
         cbTimebase.ItemIndex:= cbTimebase.ItemIndex-1;
       end;
    6: ViewCur1Click(Self);
    7: ViewTriggerClick(Self);
    8: ViewCur2Click(Self);
    9: ZoomCursorsClick(Self);
   10: ZoomOutClick(Self);
   11: ZoomInClick(Self);
   12: Go1Click(Self);
   13:;
  end;
end;

//******************************************************************************
//******************************************************************************
//******************************************************************************

procedure TfrmMain.Go1Click(Sender: TObject);
begin
  if Analyzer.State=tsReady then
   begin
    btnOpen.Enabled := False;
    btnSave.Enabled := False;
    btnPrint.Enabled := False;
    btnTimeBaseInc.Enabled := False;
    btnTimeBaseDec.Enabled := False;
    btnLeftCursor.Enabled := False;
    btnTrigCursor.Enabled := False;
    btnRightCursor.Enabled := False;
    btnZoomCursor.Enabled := False;
    btnZoomOut.Enabled:= False;
    btnZoomIn.Enabled:= False;
    File1.Enabled := False;
    Setup1.Enabled := False;
    Trigger1.Enabled := False;
    Help1.Enabled := False;
    View2.Enabled := False;
    Decoders1.Enabled := False;
    cbTimebase.Enabled := False;
    tbZoom.Enabled := False;
    LogPanel.Enabled := False;
    ScrollBar2.Enabled := False;
    frmDecDisplay.Enabled := False;

    btnStart.ImageIndex := 15;
    Go1.Caption := 'Stop!';

    // set all parameters of the analyzer
    Analyzer.Timebase_sel := cbTimebase.ItemIndex;
    Analyzer.TriggerDelay := frmTrigger.TrigDelay;
    Analyzer.TriggerLength:= frmTrigger.TrigLen;
    Analyzer.TriggerMask  := frmTrigger.TrigMask;
    Analyzer.TriggerValue := frmTrigger.TrigValue;
    Analyzer.TriggerEdge  := frmTrigger.TrigEdge;
    Analyzer.TriggerSize  := frmTrigger.TrigSize;
    Analyzer.InvIntTrig   := frmTrigger.InvIntTrig;
    Analyzer.ExtTrig_en   := frmTrigger.ExtTrig_en;
    Analyzer.ExtTrig_val  := frmTrigger.ExtTrig_val;
    Analyzer.Start;
   end
  else
   begin
    Analyzer.Stop;
   end;
end;

//===========================
procedure TfrmMain.StateChange;
//===========================
var i:integer;
    s_text:string;
begin
   case Analyzer.State of
      tsNotDetected:
       begin
	 StatusBar1.Panels[0].Text:='HW Not Detected';

	 // enable all control
	 btnOpen.Enabled := True;
	 btnSave.Enabled := True;
	 btnPrint.Enabled := True;
	 btnTimeBaseInc.Enabled := True;
	 btnTimeBaseDec.Enabled := True;
	 btnLeftCursor.Enabled := True;
	 btnTrigCursor.Enabled := True;
	 btnRightCursor.Enabled := True;
	 btnZoomCursor.Enabled := True;
	 btnZoomOut.Enabled:= True;
	 btnZoomIn.Enabled:= True;
	 File1.Enabled := True;
	 Setup1.Enabled  := True;
	 Trigger1.Enabled := True;
	 cbTimebase.Enabled := True;
	 Help1.Enabled := True;
	 View2.Enabled := True;
	 Decoders1.Enabled := True;
	 tbZoom.Enabled := True;
	 LogPanel.Enabled := True;
	 frmDecDisplay.Enabled := True;

	 btnStart.ImageIndex :=14;
	 Go1.Enabled := false;
	 btnStart.Enabled := false;
       end;
      tsReady:
       begin
         if not Analyzer.StateAnalysis then
            s_text := 'TA'
         else
            s_text := 'SA';
	 StatusBar1.Panels[0].Text:= Format('FW %d.%d(%s) Ready ...',[(Analyzer.Version and $70) shr 4,Analyzer.Version and $F,s_text]);

	 if (Analyzer.Old_State = tsNotDetected) or (Analyzer.Old_State = tsReady) then
	  begin
	    Go1.Enabled := true;
	    btnStart.Enabled := true;
            SetTimebaseText;			// update timebase text (time/state analysis)
            if not Analyzer.StateAnalysis then
               frmChannel.SetEnabledChannels(32, false)
            else
	       frmChannel.SetEnabledChannels(16, false);
	  end
	 else
	  begin
	    // sampling complete, enable all control
	    btnOpen.Enabled := True;
	    btnSave.Enabled := True;
	    btnPrint.Enabled := True;
	    btnTimeBaseInc.Enabled := True;
	    btnTimeBaseDec.Enabled := True;
	    btnLeftCursor.Enabled := True;
	    btnTrigCursor.Enabled := True;
	    btnRightCursor.Enabled := True;
	    btnZoomCursor.Enabled := True;
	    btnZoomOut.Enabled:= True;
	    btnZoomIn.Enabled:= True;
	    File1.Enabled := True;
	    Setup1.Enabled  := True;
	    Trigger1.Enabled := True;
	    cbTimebase.Enabled := True;
	    Help1.Enabled := True;
	    View2.Enabled := True;
	    Decoders1.Enabled := True;
	    tbZoom.Enabled := True;
	    LogPanel.Enabled := True;
	    frmDecDisplay.Enabled := True;

	    btnStart.ImageIndex :=14;
	    Go1.Caption := 'Go!';

	    // get sampled data
	    for i:=0 to Analyzer.DataLength-1 do
	       LogPanel.Display.DataHold.DataArray[i]:=Analyzer.DataArray[i];

	    LogPanel.Timebase := Analyzer.Timebase_freq[Analyzer.Timebase_sel];
	    ScrollBar2.SetCursorPosition(2,Analyzer.TriggerPos);
	    with LogPanel do begin
	      Display.DataHold.InitData(Analyzer.Datalength, Analyzer.StateAnalysis);
	      SetCursorPosition(2,Display.DataHold.GetPosition(Analyzer.TriggerPos));
	      Display.Recalc(0,0,Display.Width,Display.Height,Display.Bitmap.Canvas);
	      Display.Repaint;
	    end;
	    LogPanel.UpdateCursors;

	    // clear results of decoder (obsolete, new data present)
	    frmDecDisplay.lbResults.Clear;
	  end;
       end;

     tsWaitForTrigger:
	StatusBar1.Panels[0].Text:='Capturing...';

     tsPosttrigger:
	StatusBar1.Panels[0].Text:='Capturing...';

     tsReading:
	StatusBar1.Panels[0].Text:='Reading...';
   end;
  StatusBar1.Refresh;
end;


//========================================
procedure TfrmMain.StatusBar1DrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect);
//========================================
var i:byte;
begin
  i:=0;
  while i<5 do begin
    if Panel=StatusBar.Panels[i] then break;
    inc(i);
  end;
  with StatusBar.Canvas do begin
    Brush.Color:=clWindow;
    Rectangle(Rect.Left,Rect.Top,Rect.Right,Rect.Bottom);
    case i of
      0: Font.Color:=clWindowText;
      1: Font.Color:=LogPanel.Cursors[2].ColorC;
      2: Font.Color:=LogPanel.Cursors[0].ColorC;
      3: Font.Color:=LogPanel.Cursors[1].ColorC;
    else Font.Color:=clWindowText;
    end;
    TextRect(Rect,Rect.Left,Rect.Top,Panel.Text);
  end;
end;




//========================================
procedure TfrmMain.StringGridDrawCell(Sender: TObject; Col, Row: Integer; Rect: TRect; State: TGridDrawState);
//========================================
var s:string;
    i:integer;
begin
  s:=(Sender as TStringGrid).Cells[Col,Row];
  i:=(Sender as TStringGrid).DefaultRowHeight-2;

  if i>16 then i:=16;

  (Sender as TStringGrid).Canvas.Brush.Color:=(Sender as TStringGrid).Color;		// draw cell with same background as stringgrid
  (Sender as TStringGrid).Canvas.FillRect(Rect);
  (Sender as TStringGrid).Canvas.Font.Height:=i;

  if LogPanel.Display.DataSet.Data[Row].Group then
     (Sender as TStringGrid).Canvas.Font.Color:=LogPanel.Display.GroupColor
  else
     (Sender as TStringGrid).Canvas.Font.Color:=LogPanel.Display.DataSet.Data[Row].Color;

  if Col=0 then
   begin
     (Sender as TStringGrid).Canvas.TextRect(Rect,
                                 	     Rect.Right-(Sender as TStringGrid).Canvas.TextWidth(s)-5,
                                 	     (Rect.Top+Rect.Bottom-(Sender as TStringGrid).Canvas.TextHeight(s)) div 2,
                                 	     s);
   end
  else
   begin
     (Sender as TStringGrid).Canvas.TextRect(Rect,
                                 	     Rect.Left+2,
                                 	     (Rect.Top+Rect.Bottom-(Sender as TStringGrid).Canvas.TextHeight(s)) div 2,
                                             s);
   end;
end;


//========================================
procedure TfrmMain.ShowData(Sender:TObject);
//========================================
var Pos  : int64;
    i    : integer;
    Mask : cardinal;
    Val	 : cardinal;
    Start: int64;
    Stop : int64;
    Sum  : int64;
begin
  Pos:= LogPanel.Cursors[3].Position;

  if LogPanel.Cursors[0].Position < LogPanel.Cursors[1].Position then
   begin
     Start:= LogPanel.Cursors[0].Position;
     Stop := LogPanel.Cursors[1].Position;
   end
  else
   begin
     Start:= LogPanel.Cursors[1].Position;
     Stop := LogPanel.Cursors[0].Position;
   end;

  // update to new signal height if necessary
  if sgSigNames.DefaultRowHeight<>LogPanel.Display.SignalHeight-1 then
   begin
     sgSigNames.DefaultRowHeight:=LogPanel.Display.SignalHeight-1;
     sgSigValues.DefaultRowHeight:=LogPanel.Display.SignalHeight-1;
   end;

   // calculate values
   for i:= 0 to LogPanel.Display.Dataset.NumberOfLines-1 do
      case LogPanel.Display.DataSet.Data[i].DispVal of
	0:
	   sgSigValues.Cells[0,i]:= ChannelPosValueToStr(pos, i);

	1:
	 begin
	   mask := Logpanel.Display.Dataset.Data[i].Mask;
	   if Logpanel.Display.Dataset.Data[i].Group = false then
	    begin
	      Sum:=0;
	      val := LogPanel.Display.DataHold.GetSampleValue(start) and Mask;
	      while (LogPanel.Display.DataHold.FindChange(Mask, stop, true)) do
	       begin
		if ((val=0) and (LogPanel.Display.DataHold.Sample<>0)) then
		   Inc(Sum);
		val := LogPanel.Display.DataHold.Sample;
	       end;
	      sgSigValues.Cells[0,i]:= IntToStr(Sum)+'r';
	    end
	   else
	    begin
	      sgSigValues.Cells[0,i]:= 'N/A';
	    end;
	 end;

	2:
	 begin
	   mask := Logpanel.Display.Dataset.Data[i].Mask;
	   if Logpanel.Display.Dataset.Data[i].Group = false then
	    begin
	      Sum:=0;
	      val := LogPanel.Display.DataHold.GetSampleValue(start) and Mask;
	      while (LogPanel.Display.DataHold.FindChange(Mask, stop, true)) do
	       begin
		if ((val<>0) and (LogPanel.Display.DataHold.Sample=0)) then
		   Inc(Sum);
		val := LogPanel.Display.DataHold.Sample;
	       end;
	      sgSigValues.Cells[0,i]:= IntToStr(Sum)+'f';
	    end
	   else
	    begin
	      sgSigValues.Cells[0,i]:= 'N/A';
	    end;
	 end;

	3:
	 begin
	   mask := Logpanel.Display.Dataset.Data[i].Mask;
	   val := LogPanel.Display.DataHold.GetSampleValue(start) and Mask;     	// seek to sample position
	   Sum:=0;
	   while (LogPanel.Display.DataHold.FindChange(Mask, stop, true)) do
	      Inc(Sum);
	   sgSigValues.Cells[0,i]:= IntToStr(Sum)+'f';
	   sgSigValues.Cells[0,i]:= IntToStr(Sum)+'e';
	 end;
  end;

end;

//=================================================================
// Signal pop-up menu
//=================================================================
procedure TfrmMain.sgSigValuesContextPopup(Sender: TObject;
  MousePos: TPoint; var Handled: Boolean);
var No:integer;
begin
  No:=(MousePos.Y-3) div LogPanel.Display.SignalHeight;
  if No<0 then No:=0;
  if No<Logpanel.Display.DataSet.NumberOfLines then
   begin
     SigChanPopupMenu.Items[0].Visible := true;			// channel name
     SigChanPopupMenu.Items[1].Visible := true;			// displayed value
     SigChanPopupMenu.Items[3].Visible := true;			// hor. separator
     if Logpanel.Display.Dataset.Data[No].Group = false then
      begin
	// single signal menu
	SigChanPopupMenu.Items[2].Visible := false;		// base selector
	SigChanPopupMenu.Items[1].Items[1].Visible := true;
	SigChanPopupMenu.Items[1].Items[2].Visible := true;
	SigChanPopupMenu.Items[0].Caption := '- '+IntToStr(No+1)+'. '+Logpanel.Display.Dataset.Data[No].Name+' -';
      end
     else
      begin
	// bus menu
	SigChanPopupMenu.Items[2].Visible := true;		// base selector
	SigChanPopupMenu.Items[1].Items[1].Visible := false;
	SigChanPopupMenu.Items[1].Items[2].Visible := false;
	SigChanPopupMenu.Items[0].Caption := '- '+Logpanel.Display.Dataset.Data[No].Name+' -';
	SigChanPopupMenu.Items[2].Items[ord(LogPanel.Display.DataSet.Data[No].Base)].Checked := true;
      end;
     SigChanPopupMenu.Items[1].Items[ord(LogPanel.Display.DataSet.Data[No].DispVal)].Checked := true;
   end
  else
   begin
     // no signal under focus
     SigChanPopupMenu.Items[0].Visible := false;
     SigChanPopupMenu.Items[1].Visible := false;
     SigChanPopupMenu.Items[2].Visible := false;
     No := -1;
   end;
  SigChanPopupMenu.Items[1].Tag := No;
  SigChanPopupMenu.Items[2].Tag := No;
end;

procedure TfrmMain.smDispValueClick(Sender: TObject);
begin
   LogPanel.Display.DataSet.Data[(Sender as TMenuItem).Parent.Tag].DispVal := (Sender as TMenuItem).Tag;
   ShowData(self);
end;

procedure TfrmMain.smBaseClick(Sender: TObject);
begin
   // note: signal number correspond to group number (for groups only)
   (frmChannel.lbGroups.Items.Objects[(Sender as TMenuItem).Parent.Tag] as TGroupList).Base := (Sender as TMenuItem).Tag;

   // update screen
   with LogPanel.Display do
    begin
      PrepareDispData(DataSet);
      Recalc(0,0,Width,Height,Bitmap.Canvas);
      Paint;
    end;
   LogPanel.UpdateCursors;
   ShowData(self);
end;


end.
