unit Mdiform;
{ =======================================================================
  WinEDR Master MDI form (c) John Dempster, 1996-2010 All Rights Reserved
  =======================================================================
  V1.0
  V1.2 15/6/98 Variance/Power spectrum now usable Print and Copy Data work
  V1.2a 23/6/98 ASCII data file import option added
  V1.2b 25/6/98 Binary file import added
                ASCII text import now imports data correctly
                Clipboard buffer limit reduced to 31000
  V1.3 20/7/98 Variance/Spectral Analysis module
                 DC and AC Channel selection now works correctly.
                 Set Block dialog box added allowing Test/Background and
                 rejected status to be set for a block of records.
                 Sum of 2 Lorentzian fit now works correctly
  V1.3a 21/7/98  Bug in Copy Data which caused  GPI fixed
  V2.0 24/6/99   32 bit version, name changed to WinEDR
       4/9/99    12/16 bit A/D resolution added
  V2.0.1 7/2/99 ... TProgressBar out of range fixed
                    Background subtraction now works in variance/mean plots
  V2.0.2 21/2/00 ... Copy Image and Print outputs improved
  V2.0.3 25/2/00 ... Individual exponential components now displayed (dweltime.pas)
  V2.0.4 14/3/00 ... Power spectrum of a range of records now works correctly
  V2.0.5 12/4/00 ... Export of WCP from Detect & Export now fixed
  V2.0.6 27/4/00 ... Support for NI boards up to 267 NIDAQ V6.5.1 added
  V2.0.7 29/6/00 ... Multi-channel A/D now works with PCI-1200
  V2.1.0 18/2/01 ... Support for Digidata 132X added
                     Can now read Axopatch gain telegraph
  V2.1.1 12/3/01 ... Axoutils32.dll now loaded from c:\axon\libs (not \windows\system)
                     (WinWCP now compatible with AxoScope)
  V2.1.2 27/3/01 ... Support for CED Power 1401's added
                     Warning now given when READ-ONLY files opened
                     DigFilt.pas bug fixed (channel cross-talk in 1st 256 samples).
  V2.1.3 ??
  V2.2.3 14/8/01 ... Dwell times summary table added (SingleChanAnal)
                     Single open times histogram now works (SingleChanAnal)
                     SealTest modified to prevent FP errors when test pulse is small
  V2.2.4 11/10/01 .. Error which prevented selection of No Lab Interface (16 bit) option
                     now fixed (SESLabIO)
                 ... All points in State amplitude histogram fixed (List Index Out or range error)
  V2.2.5 24/10/01 ... .EDE single-channel event record file now compatible
                      with earlier versions
                      Record To Disk (not working since V2.2.2) now fixed
                      (change to dd1320.pas in components)
    11/12/01 ... Stimulator now turned off when record form closed or de-activated (rec.pas)
  V2.2.6 8/1/02  ... now supports D/A output on PCI-6035E correctly
                     Changes to zero level in single-channel module now reflected in display module
                     9/1/02 ... (V2.2.6) ExportToASCII added
  V2.3.0 25/2/02 ... Updated Detect Events module added
  V2.3.1 9/4/02 .... RH.ADCVoltageRange now correctly takes account of
                     CDRFH.ADCAmplifierGain (detsigs.pas, eventdetector.pas)
         14/4/02 ... MatchTemplate detection criterion now aligned with peak signal
                     Export Gaps to EDR added to Detect Events  module
  V2.3.3 29.5.02 ... Support for N.I boards without D/A added
                     Selection of event detection channel in
                     multi-channel files now works correctly.
         6.6.02 .... Holding voltage increment now works (rec.pas)
  V2.3.4 30.7.02 ... Changes to detected event list now saved to file when form closed (eventdetector.pas)
         31.7.02 ... Occasional misplacement of inserted events fixed (eventdetector.pas)
  V2.3.6 17.12.02 ... Now tested with ITC-18 and ITC-18
                      Event detect module updated
  V2.3.7 31.01.03 ... Now works with ITC-16 and ITC-18 (old and new drivers)
         13.02.03 ... File name in command string can now be opened when WinEDR started
  V2.3.8 27.02.03 ... Page View can now compress digitised signals within line
                      allowing lines of any duration
  V2.3.9 8.04.03 .... Support for CED 1401s modified to have 10V A/D and D/As added
                      Failure to complete SealTest sweeo when A/D input at upper limit
                      now fixed.
                      Support for PCI-6014E added
  V2.4 29.05.03 ..... Amplifier gain now updated as soon as CED 1902 gain changed (ced1902u.pas)
  V2.4.1 12.06.03 ... Bug: Incorrect display of detection criteria trace when more than
                      one channel fixed (eventdetector.pas)
                      Display now updated automatically when channel changed (pageview.pas)
  V2.4.2 25.06.03 ... View Records display updated
         3.7.03 ..... ADCVoltageRange now picked up correctly (sealtest.pas)
         8.4.03 ..... NumEvents now restricted to limits of Events array
  V2.4.3 25/8/3 ..... Both \1401 and \1401\utils now checked for 1401 commands
                      (Sealtext.pas) Auto-display values now different for each channel
                      (ampmodule.pas) CED 1902 queried after each command sent
                      (fixes failure to set AC coupling with 29xx s/n 1902s)
                      Old Detect Signals module removed from program
  V2.4.4 7/9/3 .....  Floating point error in sealtest.pas fixed
  V2.4.5 21/1/4 .... Support for DaqCard 6024E added
  V2.4.5 19/2/4 .... Bug in UpdateChannelScaling which used
                     SESLabIO.ADCMaxValue instead of MaxADCValue fixed
  V2.4.6 3/3/4 ..... Whole record printout in PageView now works correctly
  11.3.04 ... Seal Test Floating point errors now trapped, avoiding message box.
  V2.4.8. 14/4/4 ... VP500 support added
                     Data files now have index numbers appended when new file created
  V2.4.9  09/08/04   Support for Multiclamp 700 A&B added
                     External Trigger now support Active High and Low polarity
  V2.5.0  13/09/04   Record module, Update Gain button added allowing user to force amp. gain update.
  V2.5.1  14/09/04   Capacitance calculation fixed (no memory violation errors)
  V2.5.2  19/12/04   Amplitude histogram ranges now set to min-max of data
                     Seal test no longer needs DAC 1 -> Ext. Trigger connection
                     VP500 Junction Potential can now be adjusted with arrow buttons
  V2.5.3  23/01/05   Baseline of detected events can now be plotted (Detect Events)
                     Single-channel current stability plots block duration can now be specified
                     No. of channels can be specified in single-channel current open probability  
  V2.5.4  09/03/05   Stimulus pulses now work again (CED 1401 & National Instruments)
  V2.5.5  15/04/05   Amplifier gain reading now works again with Digidata 1320 interface
                     National Instruments M Series boards now supported
  V2.5.6. 10/05/05   Channel calibration factor no longer changes when
                     Setup/Recording opened with a file
                     previously recorded using a different amplifier
  V2.5.7  16/05/05   A/D sampling now works again with Digidata 1200 (rec.pas)
  V2.5.8  18/07/05   Support for NPI SEC 05LX switch clamp added
                     Import/export to Strathclyde Chart added
                     Import from WAV files added
                     Import from ASCII files improved
  V2.6.1  01/08/05   NPI SEC 05LX gain now works correctly
  V2.6.3
  V2.6.4  19/10/05   Now works with CED 1401-plus again
  V2.6.5  16.01.06   Support for Digidata 1200 under Windows NT/2000/XP added
                     Baseline trends can now be subtracted from detected event waveforms
  V2.6.6  12/04/06   Support for Dagan 3900A added
  V2.6.7  09/06/06   Single-channel current stability plot added
                     Bug which caused loss of a/d samples when display resized
                      fixed
  V2.6.8 07.06.06 ... Bug which limited export event range to 1-1 fixed (eventdetector.pas)
  V2.6.9 13.08.06 ... Count-matched (selected n larged events) averaging option added (eventdetector.pas)
  V2.7.0 03.09.06 ... Suppport for Cairn Optopatch mode telegraph added
  V2.7.1 11.09.06
  V2.7.4 14.11.06
  V2.7.5 22.11.06 ... Decay time measurement % decay in event detection module can
                      be set by user.
                      Excluded margins in single-channel amplitude measurement now a
                      applied consistently throughout single-channel module. Manual
                      cursor-determined amplitudes and durations can now be exported
                      to file. Exported ABF files can now be filtered using CLAMPFIT V9
  V2.7.6 27.11.06 ... Linear fit added to Detected Events average
                      Event re-analysis now required after external event list loaded
                      Event list file now updated after events are delected or added
  V2.7.7 03.12.06 ... Digidata 1200 installation for Windows 2000 now works
  V2.7.8 12.12.06 ... Export to IGOR IBW files now works
                      Single-channel cursors measurements now plottable in
                      stability plots and amplitude histogram.
                      St. Dev. criterion added to Set Block function
  V2.7.9 02.02.07 ... Peak detection within detected event waveform analysis
                      can now be restricted to a-a cursor defined analysis area
                      eventdetector.pas
                      Imported data from WCP files now scaled correctly (adcdatafile.pas)
                      Channels with 0 points in CFS files now ignored (adcdatafile.pas)
                      Display grid now turned on/off more reliably
  V2.8.0 08.03.07 ... (EventDetector.pas) T.(x%) decay can now be relative to peak, mid-point of rise, or a0 cursor
                      Zero level can be at start of record or event
                      Decaying exponential fits added
                      Scaling factors of detected event exported to WCP files now correct
  V2.8.1 06.04.07 ... (sealtest.pas) Amplifier gain now set and stored correctly
                      when Amplifier= None or Manual
                      Now works with USB 6514 (digital I/O not supported errors ignored)
                      Incorrect sample at end of seal test with ITC16/18 fixed
  V2.8.2 17.04.07 ... ITC16/18 ncorrect sample at end of seal test with ITC16/18 fixed
                      for new drivers as well as old
  V2.8.3 05.06.07 ... New stimulus waveform generator
  V2.8.5 21.08.07 ... No. of points averaged to compute baseline
                      in EventDetector can now be changed
  V2.8.6 29.08.07 ... Data file clusters no longer pre-allocated before recording (Rec.pas)
                      Maximum number of ADC/DAC points now 1048576 with NIDAQ-MX interfaces
                      No. of increments in waveform protocols no longer changed when
                      single steps editted
  V2.8.7 04.09.07 ... Digidata 1320 Series no longer calibrates interface on startup
  V2.8.8 05.09.07 ... EDR files with spaces in them can now be opened by double clicking
                      on the data file icon. "Unable to create log file" warning no longer
                      occurs when more than one copy of WinEDR running.
  V2.8.9 07.10.07 ... Within-burst open probability now computed correctly (SingleCHanAnal.pas)
                      Detection criterion signal now limited to min/max A/D values
                      preventing wrap-round on overflow (EventDetector.pas)
  V2.9.0 11.03.08 ... Support for CED Power 1401 Mk2 added
                      Amplifier support for Axoclamp 2 and Dagan TEV200A added
                      Real-time event frequency graph added
  V2.9.1 28.04.08 ... Channel scaling factors now correct when importing
                      from Axon ABF V1.8 files.
  V2.9.2 01.05.08 ... Memory limit violation errors in event detection module
                      when display window duration increased fixed
  V2.9.3 10.08.08 ... Signal display windows improved.
                      Y axis ticks now now 1,2, or 5 step units
                      Individual channels can hidden. Y axis size of each channel can
                      be adjusted.
                      Bug in CFS import which caused repetition of data fixed
  V2.9.4 16.07.08 ... Gain telegraph channels in setup box now updated correctly
                      with selected amplifier (V2.9.3 problem)
                      Holding voltage now updated correctly when switching between
                      seal test and recording window with Digidata 132X series interfaces
  V2.9.5 25.07.08 ... Bug which caused incorrect storage of A/D resolution in EDR
                      file during recording fixed.
  V2.9.6 05.08.08 ... Support for Digidata 1440 added
  V2.9.7 19.08.08 ... Error which caused incorrect A/D sampling interval being reported
                  (possibly introduced WinEDR V2.9.4-6) with NIDAQ-MX
                  when sampling intervals shorter than board limits selected fixed.
  V2.9.8 20.08.08 ... Min. sampling/update interval for 6221/6229 boards now 1E-6s.
                      Event Detector: Buffer overflow error when exporting large events fixed
                      Exported event record limited to 65536 sample points per channel
  V2.9.9 12.09.08 CED 1401 (10V added) interface option added
                   NPI Turbo-Tec03X gain scaling now correct
  V3.0.0 18.09.08  Out of Memory error when files exported now fixed
  V3.0.1 26.09.08 Digidata 1440 interface now detected correctly
  V3.0.2 16.12.08 Cell resistance measurement plot added to recording window
                  Size of user-defined waveform stimuli no longer limited to 1024 points
                  and can now be loaded from a file.
                  Event detector list size increased to 1000000
  V3.0.3 09.02.09 File ident line now changeable in Record and Edit Markers / Line box
                  not view windows. Log file cannot be edited within WinEDR
                  Notes can be added to in log window.
                  Size of EDR.INI increased to accommodate large file paths
  V3.0.4 23.03.09 Min. display window duration reduced to 50ms
  V3.0.5 14.04.09 Event export bug where first record was duplicated in all exported records fixed
  V3.0.7 13.07.09 Compensation for Rs & Cslow cancellation added to capacity measurement
                  Greal & Gimag can be inverted, Tested with Scitec 420 lock-in amplifier
                  Mixing of multiple channels in first block of low pass filter fixed
                  Backup of raw data file when filtering no longer produces file error warning.
  V3.0.8 29.07.09 Dagan PCOne gain telegraph now read correctly over full gain range
  V3.0.9 03.09.09 CFS files can now exported.
                  CFS files with 4 byte int, 4 & 8 byte real data can be imported
  V3.1.0 14.09.09 Capacity measurement: Stored GReal and GImag data now inverted when inversion options selected
                  Notch filter now works correctly at centre frequencies which are
                  a small fraction of sampling frequency.
  V3.1.1 12.10.10 16 A/D channels now supported
                  No. bins, bin widths and ranges can be set for histograms
                  Support for Axoclamp 2 HS1, HS10 and HS0.1 headstages added
   V3.1.2 27.01.10 Access violation when event detector opened with data files containing
                   more than one channel fixed.
   V3.1.3 15.02.10 Access violations when Subt. Baseline Trend selected fixed. (eventdetector.pas)
                   Detected events now aligned at mid-point of rising phase
                   Size of rising edge detection window can be set by user
   V3.1.4 08.03.10 Average frequency plot now available as Frequency (avg) variable
                   in addition to instantaneous Frequency (Inst)
                   Average event frequency now correctly calculated over
                   specified range of counting intervals.
                   Real time resistance parameters now preserved in INI file

  =======================================================================}

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, Menus, Global, FileIo, Shared, Setup,
  PrintRec, Rec, SealTest, ViewSig,
  About, Log, defset, ced1902u, simmepc, SingleChanAnal,
  simnoise, simchan, NoiseAnal, digfilt, SESLabIO, ComCtrls,
  ADCDataFile, math, strutils, recplotunit  ;

const
    AllForms = True ;
    AllExceptRecordFrm = False ;  
type
  TMain = class(TForm)
    MainMenu: TMainMenu;
    File1: TMenuItem;
    Edit: TMenuItem;
    View: TMenuItem;
    Help1: TMenuItem;
    mnNew: TMenuItem;
    mnOpen: TMenuItem;
    mnPrint: TMenuItem;
    mnPrintSetup: TMenuItem;
    Exit1: TMenuItem;
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;
    mnZoomIn: TMenuItem;
    mnZoomOutAll: TMenuItem;
    Setup: TMenuItem;
    mnCED1902: TMenuItem;
    Recording1: TMenuItem;
    Analysis: TMenuItem;
    mnClose: TMenuItem;
    PrinterSetupDialog1: TPrinterSetupDialog;
    mnStimulusGenerator: TMenuItem;
    CopyData: TMenuItem;
    CopyImage: TMenuItem;
    Record1: TMenuItem;
    mnRecord: TMenuItem;
    mnSealTest: TMenuItem;
    Simulations1: TMenuItem;
    mnMEPCSim: TMenuItem;
    mnImport: TMenuItem;
    mnExport: TMenuItem;
    N2: TMenuItem;
    Contents: TMenuItem;
    SearchforHelpon1: TMenuItem;
    HowtouseHelp1: TMenuItem;
    About: TMenuItem;
    InspectLogFile: TMenuItem;
    Windows: TMenuItem;
    mnDefaultSettings: TMenuItem;
    mnAppend: TMenuItem;
    mnViewSig: TMenuItem;
    mNDummy: TMenuItem;
    mnNoiseAnal: TMenuItem;
    mnSimNoise: TMenuItem;
    mnSimChan: TMenuItem;
    mnSingleChanAnal: TMenuItem;
    N1: TMenuItem;
    mnDigitalFilter: TMenuItem;
    mnRestoreOriginal: TMenuItem;
    mnRecentFileSeparator: TMenuItem;
    mnRecentFile0: TMenuItem;
    mnRecentFile1: TMenuItem;
    mnRecentFile2: TMenuItem;
    mnRecentFile3: TMenuItem;
    mnDisplayGrid: TMenuItem;
    SESLabIO: TSESLabIO;
    mnInvert: TMenuItem;
    mnDetectEvent2: TMenuItem;
    StatusBar: TStatusBar;
    mnShowHints: TMenuItem;
    mnPageView: TMenuItem;
    N3: TMenuItem;
    mnEditMarkers: TMenuItem;
    ImportFile: TADCDataFile;
    mnECGFrm: TMenuItem;
    mnVP500: TMenuItem;
    mnZoomInCh0: TMenuItem;
    mnZoomInCh1: TMenuItem;
    mnZoomInCh2: TMenuItem;
    mnZoomInCh3: TMenuItem;
    mnZoomInCh4: TMenuItem;
    mnZoomInCh5: TMenuItem;
    mnZoomInCh6: TMenuItem;
    mnZoomInCh7: TMenuItem;
    mnZoomInCh8: TMenuItem;
    mnZoomInCh9: TMenuItem;
    mnZoomOut: TMenuItem;
    mnZoomOutCh0: TMenuItem;
    mnZoomOutCh1: TMenuItem;
    mnZoomOutCh2: TMenuItem;
    mnZoomOutCh3: TMenuItem;
    mnZoomOutCh4: TMenuItem;
    mnZoomOutCh5: TMenuItem;
    mnZoomOutCh6: TMenuItem;
    mnZoomOutCh7: TMenuItem;
    mnZoomOutCh8: TMenuItem;
    mnZoomOutCh9: TMenuItem;
    mnFileProperties: TMenuItem;
    procedure mnOpenClick(Sender: TObject);
    procedure mnZoomInClick(Sender: TObject);
    procedure Exit1Click(Sender: TObject);
    procedure Recording1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure mnZoomOutAllClick(Sender: TObject);
    procedure mnCloseClick(Sender: TObject);
    procedure mnPrintClick(Sender: TObject);
    procedure mnPrintSetupClick(Sender: TObject);
    procedure mnRecordClick(Sender: TObject);
    procedure mnNewClick(Sender: TObject);
    procedure mnStimulusGeneratorClick(Sender: TObject);
    procedure CopyDataClick(Sender: TObject);
    procedure mnSealTestClick(Sender: TObject);
    procedure AboutClick(Sender: TObject);
    procedure InspectLogFileClick(Sender: TObject);
    procedure mnImportClick(Sender: TObject);
    procedure CopyImageClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure mnDefaultSettingsClick(Sender: TObject);
    procedure mnCED1902Click(Sender: TObject);
    procedure ContentsClick(Sender: TObject);
    procedure SearchforHelpon1Click(Sender: TObject);
    procedure HowtouseHelp1Click(Sender: TObject);
    procedure mnAppendClick(Sender: TObject);
    procedure mnExportClick(Sender: TObject);
    procedure mnViewSigClick(Sender: TObject);
    procedure mnMEPCSimClick(Sender: TObject);
    procedure mnNoiseAnalClick(Sender: TObject);
    procedure mnSimNoiseClick(Sender: TObject);
    procedure mnSimChanClick(Sender: TObject);
    procedure mnSingleChanAnalClick(Sender: TObject);
    procedure mnDigitalFilterClick(Sender: TObject);
    procedure mnRestoreOriginalClick(Sender: TObject);
    procedure mnRecentFile0Click(Sender: TObject);
    procedure mnDisplayGridClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure mnInvertClick(Sender: TObject);
    procedure mnDetectEvent2Click(Sender: TObject);
    procedure EditClick(Sender: TObject);
    procedure mnShowHintsClick(Sender: TObject);
    procedure mnPageViewClick(Sender: TObject);
    procedure File1Click(Sender: TObject);
    procedure mnEditMarkersClick(Sender: TObject);
    procedure mnECGFrmClick(Sender: TObject);
    procedure mnVP500Click(Sender: TObject);
    procedure mnZoomOutClick(Sender: TObject);
    procedure mnZoomInCh0Click(Sender: TObject);
    procedure mnZoomOutCh0Click(Sender: TObject);
    procedure ViewClick(Sender: TObject);
    procedure mnFilePropertiesClick(Sender: TObject);
    procedure SetupClick(Sender: TObject);
  private
    { Private declarations }

    function ImportFromDataFile : Boolean ;
    function CreateIndexedFileName( FileName : String ) : String ;
    procedure AppendEDRFile( FileName : string ) ;
    procedure UpdateRecentFilesList ;


  public
    { Public declarations }
    //SetupChanged : Boolean ;
    Version : string ;

    // Most recent cell parameters estimates from seal test
    Cm : Single ;
    Gm : Single ;
    Ga : Single;
    RSeal : Single ;

    procedure SetCopyMenu( EnableCopyData, EnableCopyImage : boolean ) ;
    procedure UpdateWindows ;
    procedure SetMenus ;
    procedure ClearTicksInViewMenu ;
    procedure CopyAndPrintMenus( CopyEnabled,PrintEnabled : Boolean ) ;
    procedure CloseFormsAndDataFile( CloseAllForms : Boolean ) ;
    procedure LoadDataFiles( FileName : string ) ;
    function CreateNewDataFile( var FHeader : TCDRFileHeader ) : Boolean ;
    procedure SetRecentFileItem( MenuItem : TMenuItem ; FileName : string ) ;
    procedure UpdateViewSig ;
    procedure UpdateMDIWindows ;
    procedure UpdateChannelScalingFactors ;
    procedure UpdateAllDisplays ;
    function FormExists( FormName : String ) : Boolean ;
    end;

var
  Main: TMain;


implementation

uses export, AmpModule, Invert, EventDetector, PageView, EditMarkers,
  Import, ImportASCIIUnit, maths , ECG, ImportRawUnit, VP500Panel,
  FilePropsUnit, Wavgen;

{$R *.DFM}

procedure TMain.FormShow(Sender: TObject);
{ ---------------------------------------------------------------
  Initialize global program settings and load last data file used
  ---------------------------------------------------------------}
var
   i,ch : Integer ;
   FileName : String ;
begin
     Version := 'V3.1.4' ;
     Caption := 'WinEDR - Strathclyde Electrophysiology Data Recorder ' + Version ;

      { Get directory which contains EDR program }
      Settings.ProgDirectory := ExtractFilePath(ParamStr(0)) ;
      Application.HelpFile := Settings.ProgDirectory + 'WinEDR.HLP';

      { Create channel names list }
      ChannelNames := TStringList.Create ;

      { Create default set of record types }
      RecordTypes := TStringList.Create ;
      RecordTypes.Add( 'ALL' ) ;
      RecordTypes.Add( 'EVOK' ) ;
      RecordTypes.Add( 'MINI' ) ;
      RecordTypes.Add( 'FAIL' ) ;
      RecordTypes.Add( 'TEST' ) ;
      RecordTypes.Add( 'LEAK' ) ;

     { Default values for channels }

     CdrFH.NumChannels := 1 ;
     CdrFH.NumSamplesInFile := 0 ;
     CdrFH.NumBytesInHeader := NumBytesInHeader ;
     CdrFH.ADCVoltageRange := 5.0 ;
     CdrFH.dt := 0.001 ;
     CdrFH.Version := 6.1 ;
     for ch := 0 to EDRChannelLimit do begin
         RecChannel[ch].TimeZero := 1. ;
         RecChannel[ch].ADCScale := 1. ;
         RecChannel[ch].CursorIndex := 128 ;
         RecChannel[ch].ZeroIndex := 0 ;
         RecChannel[ch].Cursor0 := 0 ;
         RecChannel[ch].Cursor1 := 256 ;
         { Zero levels fixed at hardware zero }
         RecChannel[ch].ADCZero := 0 ;
         RecChannel[ch].ADCZeroAt := -1 ;
         RecChannel[ch].ADCCalibrationFactor := 0.001 ;
         RecChannel[ch].ADCAmplifierGain := 1. ;
         RecChannel[ch].ADCCalibrationBar := 0.0 ;
         RecChannel[ch].ADCUnits := 'mV' ;
         RecChannel[ch].ADCName := format('Ch.%d',[ch]);
         RecChannel[ch].ChannelOffset := ch ;
         RecChannel[ch].color := clBlue ;
         RecChannel[ch].xMin := 0. ;
         RecChannel[ch].xMax := CdrFH.NumSamples-1 ;
         RecChannel[ch].yMin := -32768 ;
         RecChannel[ch].yMax := 32767 ;
         RecChannel[ch].InUse := True ;
         Channel[ch] := RecChannel[ch] ;
         end ;

     CdrFH.WCPFileName := '' ;

     { Initialise to no laboratory interface }
     Settings.LaboratoryInterface := NoInterface12 ;
     Settings.ADCSamplingInterval := 0.001 ;
     Settings.ADCVoltageRangeIndex := 0 ;
     Settings.NumChannels := 1 ;

     Settings.TriggerMode := 'F' ;
     Settings.ExternalTriggerActiveHigh := False ;
     Settings.EventDetector.Channel := 0 ;
     Settings.EventDetector.yThreshold := 0. ;
     Settings.EventDetector.tThreshold := CdrFH.dt*10.0 ;
     Settings.EventDetector.BaselineAverage := 100 ;
     Settings.EventDetector.PreTriggerFraction := 0.1 ;
     Settings.EventDetector.AnalysisWindow := 512 ;
     Settings.EventDetector.RecordSize := 512 ;
     Settings.EventDetector.DeadTime := CdrFH.dt*Settings.EventDetector.RecordSize ;
     Settings.EventDetector.PositivePeaks := True ;
     Settings.EventDetector.BaselineAtStart := True ;
     Settings.EventDetector.SubtractBaseline := False ;
     Settings.EventDetector.NumBaselinePoints := 20 ;
     Settings.EventDetector.NumBaselineGap := 0 ;
     Settings.EventDetector.TDecayPercent := 90.0 ;
     Settings.EventDetector.TDecayFrom := 0 ;
     Settings.EventDetector.RisingEdgeWindow := 20 ;
     Settings.EventDetector.AvgFrequencyInterval := 1.0 ;

     Settings.DwellTimes.ChanNum := 0 ;
     Settings.DwellTimes.RecordSize := 512 ;
     Settings.DwellTimes.Threshold := 0.5 ;

     Settings.NumTriggerSweeps := 1 ;
     Settings.RecordDuration := 10. ;
     Settings.DisplayDuration := 10.0 ;
     Settings.DisplayGrid := True ;
     mnDisplayGrid.Checked := Settings.DisplayGrid ;
     Settings.NumHorizontalGridLines := 10 ;
     Settings.NumVerticalGridLines := 10 ;
     
     Settings.FixedZeroLevels := False ;

     Settings.CutOffFrequency := 0. ;
     Settings.NewCalculation := False ;

     { Minimum interval for updating D/A converters when
       generating command voltage waveforms }
     Settings.MinDACInterval := 0.001 ;

     Settings.TUnits := 's' ;
     Settings.TScale := 1.0 ;
     Settings.TUnScale := 1.0 ;
     Settings.TBarValue := 0.0 ;

     { Name of command voltage protocol file in current use }
     Settings.StimFileName := '' ;

     { Divide factor that the patch/voltage clamp applies to its
       command voltage input. The D/A output voltage is thus scaled up
       by this factor }
     Settings.StimDirectory := Settings.ProgDirectory + 'Stim\' ;

     for i := 0 to High(Settings.VCommand) do begin
         Settings.VCommand[i].DivideFactor := 1. ;
         Settings.VCommand[i].HoldingVoltage := 0. ;
         Settings.VCommand[i].HoldingVoltageIncrement := 0. ;
         Settings.VCommand[i].HoldingVoltageAlt := 0. ;
         end ;

     Settings.DigitalOutputs := 0 ;
     Settings.UpdateOutputs := True ;
     Settings.DACSelected := 0 ;

     { Default settings for seal test pulse }
     Settings.SealTest.Use := 1 ;
     Settings.SealTest.PulseHeight1 := 0.01 ;
     Settings.SealTest.HoldingVoltage1 := 0. ;
     Settings.SealTest.PulseHeight2 := 0.01 ;
     Settings.SealTest.HoldingVoltage2 := 0. ;
     Settings.SealTest.PulseHeight3 := 0.0 ;
     Settings.SealTest.HoldingVoltage3 := 0. ;

     Settings.SealTest.PulseWidth:= 0.03 ;
     Settings.SealTest.CurrentChannel := 0 ;
     Settings.SealTest.VoltageChannel := 1 ;
     Settings.SealTest.AutoScale := True ;
     Settings.SealTest.DACNum := 0 ;
     Settings.SealTest.SmoothingFactor := 0.1 ;

     { Set flag indicating this is the first sweep, to force an autoscale }
     Settings.SealTest.FirstSweep := True ;

     Settings.Colors.Cursors := clWhite ;
     Settings.Colors.Grid := clAqua ;

     Settings.Plot.TopMargin := 50.0 ;
     Settings.Plot.LeftMargin := 50.0 ;
     Settings.Plot.BottomMargin := 50.0 ;
     Settings.Plot.RightMargin := 25.0 ;
     Settings.Plot.FontName := 'Arial' ;
     Settings.Plot.FontSize := 12 ;
     Settings.Plot.LineThickness := 2 ;
     Settings.Plot.MarkerSize := 5 ;
     Settings.Plot.ShowLines := True ;
     Settings.Plot.ShowMarkers := True ;
     Settings.Plot.MetafileWidth := 600 ;
     Settings.Plot.MetafileHeight := 500 ;

     { Bitmap size for images copied to clipboard }
     Settings.BitmapWidth := 600 ;
     Settings.BitmapHeight := 500 ;

     Settings.DataDirectory := '' ;

     { Settings for record hard copy plots }
     Settings.ShowLabels := True ;
     Settings.ShowZeroLevels := True ;

     { Default settings for fluorescence ratio analysis }
     Settings.Fluorescence.InUse := False ;
     Settings.Fluorescence.MinChannels := 3 ;
     Settings.Fluorescence.RMax := 1.0 ;
     Settings.Fluorescence.RMin := 0.0 ;
     Settings.Fluorescence.KEff := 1.0 ;
     Settings.Fluorescence.FThreshold := 0.0 ;
     Settings.Fluorescence.NumerChan := 0 ;
     Settings.Fluorescence.DenomChan := 1 ;
     Settings.Fluorescence.RatioChan := 2 ;
     Settings.Fluorescence.ConcChan := 3 ;
     Settings.Fluorescence.RatioDisplayMax := 10.0 ;
     Settings.Fluorescence.ConcDisplayMax := 1.0 ;

     { Default settings for cell capacity analysis }
     Settings.Capacity.InUse := False ;
     Settings.Capacity.MinChannels := 7 ;
     Settings.Capacity.ImChan := 0 ;
     Settings.Capacity.VmChan := 1 ;
     Settings.Capacity.GrealChan := 2 ;
     Settings.Capacity.GimagChan := 3 ;
     Settings.Capacity.GmChan := 4 ;
     Settings.Capacity.GsChan := 5 ;
     Settings.Capacity.CmChan := 6 ;
     Settings.Capacity.CmDisplayMax := 200.0 ;
     Settings.Capacity.GmDisplayMax := 10.0 ;
     Settings.Capacity.GsDisplayMax := 500.0 ;
     Settings.Capacity.Frequency := 1000.0 ;
     Settings.Capacity.VRev := 0.0 ;
     Settings.Capacity.InvertGReal := False ;
     Settings.Capacity.InvertGImag := False ;
     Settings.Capacity.GChannelsUseGainTelegraph := False ;
     Settings.Capacity.SineWaveRMS := 0.01 ;
     Settings.Capacity.CapacityCompensationInUse := False ;
     Settings.Capacity.RSeriesComp := 0.0 ;
     Settings.Capacity.CellCapacityComp := 0.0 ;

     // Default settings for realtime event analysis
     Settings.RTEventAnalysis.InUse := False ;
     Settings.RTEventAnalysis.Channel := 0 ;
     Settings.RTEventAnalysis.DetectionThreshold := 10.0 ;
     Settings.RTEventAnalysis.RunningMeanTime := 1.0 ;
     Settings.RTEventAnalysis.DeadTime := 0.1 ;

     // Default settings for real-time resistance analysis
     Settings.RTResistance.InUse := False ;
     Settings.RTResistance.ImChannel := 0 ;
     Settings.RTResistance.VmChannel := 1 ;
     Settings.RTResistance.Amplitude := 0.01 ;
     Settings.RTResistance.Duration := 0.02 ;
     Settings.RTResistance.Interval := 1.0 ;
     Settings.RTResistance.Plot := 2 ;


     Settings.PageViewLinesPerPage := 8 ;
     Settings.PageViewLineDuration := 1.0 ;

    Cm := 0.0 ;
    Gm := 0.0 ;
    Ga := 0.0 ;
    RSeal := 0.0 ;

     MarkerList := TStringList.Create ;

     { Set the file names and handles for all header blocks to null }
     CdrFH.FileHandle := -1 ;
     CdrFH.FileName := '' ;

     SetMenus ;

     // Load initialization file to get name of last data file used }
     LoadInitializationFile( Settings.ProgDirectory + 'EDR.ini' ) ;
     { Name of last used data file returned in fH.FileName }

     // Initialise lab. interface
     SESLabIO.LabInterfaceType := Settings.LaboratoryInterface ;

     // Set A/D input mode
     SESLabIO.ADCInputMode := Settings.ADCInputMode ;

     SESLabIO.ADCVoltageRange := CDRFH.ADCVoltageRange ;

     WindowMenu := Windows ;

     { Add names of recently accessed data files to Files menu }
     mnRecentFileSeparator.Visible := False ;
     SetRecentFileItem( mnRecentFile0, Settings.RecentFiles[0] ) ;
     SetRecentFileItem( mnRecentFile1, Settings.RecentFiles[1] ) ;
     SetRecentFileItem( mnRecentFile2, Settings.RecentFiles[2] ) ;
     SetRecentFileItem( mnRecentFile3, Settings.RecentFiles[3] ) ;

     { Open log file (contains log of program activity) }
     OpenLogFile ;
     WriteToLogFile( 'EDR Started' ) ;

     // Set channel display limits to whole ADC data range
     for ch := 0 to EDRChannelLimit do begin
         Channel[ch].xMin := 0. ;
         Channel[ch].xMax := CdrFH.NumSamples-1 ;
         Channel[ch].Cursor1 := CdrFH.NumSamples div 2 ;
         Channel[ch].yMin := SESLabIO.ADCMinValue ;
         Channel[ch].yMax := SESLabIO.ADCMaxValue ;
         end ;

     { Open a data file if one has been supplied in parameter string }
     FileName :=  '' ;
     for i := 1 to ParamCount do begin
         if i > 1 then FileName := FileName + ' ' ;
         FileName := FileName + ParamStr(i) ;
         end ;

     if ANSIContainsText( ExtractFileExt(FileName),'.edr') then begin
        if FileExists(FileName) then begin
           if (FileGetAttr(FileName) AND faReadOnly) = 0 then LoadDataFiles(FileName)
           else ShowMessage( FileName + ' is READ-ONLY. Unable to open!') ;

           end ;
        end ;

     end;


procedure TMain.SetRecentFileItem(
          MenuItem : TMenuItem ;    { Menu item to be updated }
          FileName : string         { Name of data file to be added }
          ) ;
{ ------------------------------------------------------
  Add the name of a recently used data file to File menu
  ------------------------------------------------------ }
begin
     if FileName <> '' then begin
        mnRecentFileSeparator.Visible := True ;
        MenuItem.Caption := FileName ;
        MenuItem.Visible := True ;
        end
     Else MenuItem.Visible := False ;
     end ;


procedure TMain.mnOpenClick(Sender: TObject);
{ -Menu item --------------------
  Open an existing EDR data file
  -------------------------------}
begin

     { Close existing data file }
     CloseFormsAndDataFile( AllForms ) ;

     { Get the name of a data file from user }
     OpenDialog.options := [ofPathMustExist] ;
     OpenDialog.DefaultExt := DataFileExtension ;
     if Settings.DataDirectory <> '' then
        OpenDialog.InitialDir := Settings.DataDirectory ;
     OpenDialog.Filter := format( ' EDR Files (*%s)|*%s',
                                  [DataFileExtension,DataFileExtension]) ;
     OpenDialog.Title := 'Open File ' ;

     if OpenDialog.execute then begin
        CdrFH.FileName := OpenDialog.FileName ;
        LoadDataFiles( CdrFH.FileName ) ;

        { Save data directory }
        Settings.DataDirectory := ExtractFilePath( CdrFH.FileName ) ;

        // Update list of recently used files
        UpdateRecentFilesList ;

        end ;

     SetMenus ;
     end;


procedure TMain.LoadDataFiles( FileName : string ) ;
{ -------------------------------------------
  Load EDR data file and any associated files
  -------------------------------------------}
var
   ch : Integer ;
   OK : Boolean ;
begin

     OK := False ;
     if FileExists( FileName ) then OK := True ;
     if OK then begin
        if (FileGetAttr(FileName) AND faReadOnly) <> 0 then begin
           OK := False ;
           MessageDlg( FileName + ' is READ-ONLY. Unable to open!',mtWarning, [mbOK], 0 ) ;
           end ;
        end ;

     if OK then begin
        { Open data file }
        CdrFH.Filename := ChangeFileExt(FileName,DataFileExtension) ;
        CdrFH.FileHandle := FileOpen( CdrFH.FileName, fmOpenReadWrite ) ;
        Main.Caption := 'WinEDR ' + Version + ' ' + FileName ;
        if CdrFH.Filehandle >= 0 then begin
            { Load the raw file details }
            GetCDRHeader( CdrFH ) ;
            { Make sure all channels are visible }
            for ch := 0 to CdrFH.NumChannels-1 do Channel[ch].InUse := True ;
            WriteToLogFile( CdrFH.FileName + ' opened' ) ;
            // Update open windows with new file info.
            UpdateMDIWindows ;
            end
        else begin
             MessageDlg( format(' File Error =%d',[CdrFH.Filehandle]),
                         mtWarning, [mbOK], 0 ) ;
             OK := False ;
             end ;
        end ;

     // Close windows if file could not be opened
     if not OK then CloseFormsAndDataFile( AllForms ) ;

     SetMenus ;
     end ;


function TMain.CreateNewDataFile(
         var FHeader : TCDRFileHeader
         ) : Boolean ;
{ --------------------------
  Create a new EDR data file
  --------------------------
  10/7/01 ... Associated .REC and .WCE files now deleted when new file created }
var
   ch : Integer ;
begin
     { Close any existing file }
     if FHeader.FileHandle >= 0 then begin
        FileClose( FHeader.FileHandle ) ;
        FHeader.FileHandle := -1 ;
        end ;

     { Create a new file }
     FHeader.FileHandle := FileCreate( FHeader.FileName ) ;
     if FHeader.FileHandle >= 0 then begin

        { No data points }
        FHeader.NumSamplesInFile := 0 ;
        { No saved file }
        FHeader.WCPFileName := '' ;
        { No back up file }
        FHeader.BackedUp := False ;
        { Turn all channels on }
        for ch := 0 to FHeader.NumChannels-1 do Channel[ch].InUse := True ;
        // Clear markers
        MarkerList.Clear ;

        { Save initial values to header block }
        SaveCDRHeader( FHeader ) ;
        // Delete event file associated with a previous data file of same name
        if FileExists(ChangeFileExt(FHeader.FileName,EventFileExtension)) then
           DeleteFile(PChar(ChangeFileExt(FHeader.FileName,EventFileExtension))) ;
        // Delete variance record file associated with a previous data file of same name
        if FileExists(ChangeFileExt(FHeader.FileName,'.rec')) then
           DeleteFile(PChar(ChangeFileExt(FHeader.FileName,'.rec'))) ;

        Result := True ;
        end
     else begin
        ShowMessage( 'Error: Could not create ' + FHeader.FileName ) ;
        Result := False ;
        end ;

     end ;


procedure TMain.mnZoomInClick(Sender: TObject);
{ ---------------------
  Display zoom in menu
  --------------------- }
var
    i,ch : Integer ;
begin

     for i := 0 to mnZoomIn.Count-1 do mnZoomIn.Items[i].Visible := False ;
     if MDIChildCount <= 0 then Exit ;

     if ActiveMDIChild.Name = 'RecordFrm' then begin
        // Use Recording channels if recording form is active
        for ch := 0 to Settings.NumChannels-1 do begin
            mnZoomIn.Items[ch].Caption := format( 'Ch.%d (%s)',
                                         [ch,RecChannel[ch].ADCName]) ;
            mnZoomIn.Items[ch].Tag := ch ;
            mnZoomIn.Items[ch].Visible := True ;
            end ;
         end
     else begin
        // Otherwise use file channels
        for ch := 0 to CDRFH.NumChannels-1 do begin
            mnZoomIn.Items[ch].Caption := format( 'Ch.%d (%s)',
                                         [ch,Channel[ch].ADCName]) ;
            mnZoomIn.Items[ch].Tag := ch ;
            mnZoomIn.Items[ch].Visible := True ;
            end ;
        end ;

     end;


procedure TMain.Exit1Click(Sender: TObject);
{ --------------------------------
  Save state and terminate program
  -------------------------------- }
begin
     close ;
     end;


procedure TMain.Recording1Click(Sender: TObject);
{ - Menu Item -------------------------------
  Record parameters set-up dialog (setup.pas)
  -------------------------------------------}
begin
     if FormExists('SetupDlg') then begin
        if SetupDlg.WindowState = wsMinimized then SetupDlg.WindowState := wsNormal ;
        SetupDlg.BringToFront ;
        SetupDlg.SetFocus ;
        end
     else begin
        SetupDlg := TSetupDlg.Create(Self) ;
        SetupDlg.Left := 10 ;
        SetupDlg.Top := 10 ;
        end ;

     end;


procedure TMain.FormDestroy(Sender: TObject);
begin
     RecordTypes.Free ;
     ChannelNames.Free ;
     end;


procedure TMain.mnZoomOutAllClick(Sender: TObject);
{ - Menu Item ---------------------------------
  Set display channels to minimum magnification
  ---------------------------------------------}
var
   i,ch : Integer ;
begin

     if MDIChildCount <= 0 then Exit ;

     for ch := 0 to EDRChannelLimit do begin
         Channel[ch].yMin := -Channel[ch].ADCMaxValue -1 ;
         Channel[ch].yMax := Channel[ch].ADCMaxValue ;
         RecChannel[ch].yMin := -RecChannel[ch].ADCMaxValue -1 ;
         RecChannel[ch].yMax := RecChannel[ch].ADCMaxValue ;
         end ;

     for i := 0 to MDIChildCount-1 do begin
          if MDIChildren[I].Name = 'SimMEPCFrm' then
             TSimMEPCFrm(MDIChildren[I]).ZoomOut
          else if MDIChildren[I].Name = 'SimNoiseFrm' then
             TSimNoiseFrm(MDIChildren[I]).ZoomOut
          else if MDIChildren[I].Name = 'SimChanFrm' then
             TSimChanFrm(MDIChildren[I]).ZoomOut
          else if MDIChildren[I].Name = 'NoiseAnal' then
             TNoiseAnalFrm(MDIChildren[I]).ZoomOutAll
          else if MDIChildren[I].Name = 'SingleChanAnalFrm' then
             TSingleChanAnalFrm(MDIChildren[I]).ZoomOutAll
          else if MDIChildren[I].Name = 'RecordFrm' then
             TRecordFrm(MDIChildren[I]).ZoomOutAll
          else if MDIChildren[I].Name = 'ViewSigFrm' then
             TViewSigFrm(MDIChildren[I]).ZoomOutAll
          else if MDIChildren[I].Name = 'SealTestFrm' then
             TSealTestFrm(MDIChildren[I]).ZoomOut
          else if MDIChildren[I].Name = 'EventDetFrm' then
             TEventDetFrm(MDIChildren[I]).ZoomOutAll ;
          end ;
    end;


procedure TMain.mnCloseClick(Sender: TObject);
{ -------------------------
  Close all forms and files
  -------------------------}
begin
     { Close all child windows and data files }
     CloseFormsAndDataFile( AllForms ) ;
     CdrFH.FileName := '' ;
     CdrFH.FileHandle := -1 ;

     end ;


procedure TMain.CloseFormsAndDataFile( CloseAllForms : Boolean ) ;
{ -------------------------
  Close all forms and files
  -------------------------}
var
   i : Integer ;
begin

     { Close all child windows }
     for i := 0 to MDIChildCount-1 do begin
         if CloseAllForms or
            ((MDICHildren[i].Name <> 'RecordFrm') and
             (MDICHildren[i].Name <> 'RecPlotFrm')) then MDICHildren[i].Close
         end ;

     { Close files }
     if  CdrFH.FileHandle >= 0 then begin

         FileClose(CdrFH.FileHandle) ;
         CdrFH.FileHandle := -1 ;


         end ;

     { Allow form close messages to be processed before continuing }
     Application.ProcessMessages ;

     SetMenus ;

     Caption := 'WinEDR - Strathclyde Electrophysiology Data Recorder ' + Version ;

     end ;


procedure TMain.UpdateRecentFilesList ;
// ----------------------------------
// Update list of recently used files
// ----------------------------------
var
    i : Integer ;
begin

     // If same file has been opened again, don't update
     if Settings.RecentFiles[0] = CDRFH.FileName then Exit ;

     // Shift list along
     for i := High(Settings.RecentFiles) downto 1 do
         Settings.RecentFiles[i] := Settings.RecentFiles[i-1] ;
     Settings.RecentFiles[0] := CDRFH.FileName ;

     { Update list in Files menu }
     mnRecentFileSeparator.Visible := False ;
     SetRecentFileItem( mnRecentFile0, Settings.RecentFiles[0] ) ;
     SetRecentFileItem( mnRecentFile1, Settings.RecentFiles[1] ) ;
     SetRecentFileItem( mnRecentFile2, Settings.RecentFiles[2] ) ;
     SetRecentFileItem( mnRecentFile3, Settings.RecentFiles[3] ) ;

     end ;


procedure TMain.mnPrintClick(Sender: TObject);
{ - Menu Item ----------------------------------------------------
  Print the record or graph displayed in the currently active form
  ----------------------------------------------------------------}
begin
     if ActiveMDIChild.Name = 'ViewSigFrm' then
        TViewSigFrm(ActiveMDIChild).PrintDisplay ;
     if ActiveMDIChild.Name = 'NoiseAnalFrm' then
        TNoiseAnalFrm(ActiveMDIChild).PrintDisplay ;
     if ActiveMDIChild.Name = 'SingleChanAnalFrm' then
        TSingleChanAnalFrm(ActiveMDIChild).PrintDisplay ;
     if ActiveMDIChild.Name = 'EventDetFrm' then
        TEventDetFrm(ActiveMDIChild).PrintDisplay ;
     if ActiveMDIChild.Name = 'PageViewFrm' then
        TPageViewFrm(ActiveMDIChild).PrintDisplay ;
     if ActiveMDIChild.Name = 'ECGFrm' then
        TPageViewFrm(ActiveMDIChild).PrintDisplay ;

     end;


procedure TMain.mnPrintSetupClick(Sender: TObject);
{ - Menu Item ------------------
  Standard printer setup dialog
  ------------------------------}
begin
     PrinterSetupDialog1.Execute ;
     end;


procedure TMain.mnRecordClick(Sender: TObject);
{ - Menu Item --------------
  Open record to disk module
  --------------------------}
begin
     if FormExists('RecordFrm') then begin
        if RecordFrm.WindowState = wsMinimized then RecordFrm.WindowState := wsNormal ;
        RecordFrm.BringToFront ;
        RecordFrm.SetFocus ;
        end
     else begin
        RecordFrm := TRecordFrm.Create(Self) ;
        RecordFrm.Left := 15 ;
        RecordFrm.Top := 15 ;
        RecordFrm.Height := ClientHeight - RecordFrm.Top - 30 ;
        RecordFrm.Width := Min( RecordFrm.Width,
                                 ClientWidth - RecordFrm.Left - 30 ) ;
        end ;

     end;


procedure TMain.mnNewClick(Sender: TObject);
{ - Menu Item --------
  Create new data file
  --------------------}
var
   OK : Boolean ;
begin

     { Close any existing data file }
     CloseFormsAndDataFile( AllExceptRecordFrm ) ;

     { Present user with standard Save File dialog box }
     SaveDialog.options := [ofOverwritePrompt,ofHideReadOnly,ofPathMustExist] ;
     SaveDialog.DefaultExt := DataFileExtension ;
     SaveDialog.FileName := CreateIndexedFileName( Settings.RecentFiles[0] ) ;
     SaveDialog.Filter := format( ' EDR Files (*%s)|*%s',
                                  [DataFileExtension,DataFileExtension]) ;
     SaveDialog.Title := 'New Data File' ;

     if Settings.DataDirectory <> '' then
        SaveDialog.InitialDir := Settings.DataDirectory ;

     { Create new data file }
     if SaveDialog.execute then begin

        { Save data directory }
        Settings.DataDirectory := ExtractFilePath( SaveDialog.FileName ) ;
        CdrFH.FileName := SaveDialog.FileName ;
        CdrFH.FileName := ChangeFileExt( CdrFH.FileName, DataFileExtension ) ;
        OK := CreateNewDataFile( CdrFH ) ;
        if OK then begin
           { Close again to permit re-opening by LoadDataFiles }
           CloseFormsAndDataFile( AllExceptRecordFrm ) ; ;
           WriteToLogFile( 'New file ' + CdrFH.FileName ) ;

           { Re-open data file }
           LoadDataFiles( CdrFH.FileName ) ;

           // Update recording window
           if FormExists('RecordFrm') then RecordFrm.NewFile ;

           // Update list of recently used files
           UpdateRecentFilesList ;

           end ;
        end ;
     end ;


procedure TMain.mnStimulusGeneratorClick(Sender: TObject);
//  ---------------------------------
// Open stimulus settings setup box
// ---------------------------------
var
  CreateForm : Boolean ;
  i : Integer ;
begin

    CreateForm := True ;
    for i := 0 to MDIChildCount-1 do
     if MDIChildren[i].Name = 'WavGenFrm' then begin
        WavGenFrm.SetFocus ;
        CreateForm := False ;
        end ;

    if CreateForm then begin
       WavGenFrm := TWavGenFrm.Create(Self) ;
       WavGenFrm.Left := 10 ;
       WavGenFrm.Top := 10 ;
       end ;
    end ;


procedure TMain.SetMenus ;
{ ---------------------------
  Activate usable menu items
  --------------------------}
begin
     View.Enabled := True ;
     Analysis.Enabled := False ;
     mnPrint.Enabled := False ;
     Edit.Enabled := True ;
     Setup.Enabled := True ;
     Record1.Enabled := True ;
     mnClose.Enabled := False ;
     mnAppend.Enabled := False ;
     mnFileProperties.Enabled := False ;
     mnExport.Enabled := False ;
     Simulations1.Enabled := False ;

     { Enable setup, recording and simulation ... if a data file is open }
     if CdrFH.FileHandle >= 0 then begin
           { Allow these functions when a data file is open }
           mnClose.Enabled := True ;
           mnFileProperties.Enabled := True ;
           Simulations1.Enabled := True ;
           Edit.Enabled := True ;
           mnAppend.Enabled := True ;

           { Enable these functions when file contains data records }
           if CdrFH.NumSamplesInFile > 0 then begin
              Analysis.Enabled := True ;
              mnPrint.Enabled := True ;
              mnExport.Enabled := True ;
              Edit.Enabled := True ;
              end ;

           end ;

     { If a back up is available, enabled restore menu option }
     mnRestoreOriginal.Enabled := CdrFH.BackedUp ;
     end ;


procedure TMain.SetCopyMenu( EnableCopyData, EnableCopyImage : boolean ) ;
{ --------------------------------------------------
  Enable/disable copy data and copy image menu items
  --------------------------------------------------}
begin
     CopyData.enabled := EnableCopyData ;
     CopyImage.enabled := EnableCopyImage ;
     end ;


procedure TMain.mnSealTestClick(Sender: TObject);
{ ----------------------------
  Menu - Open seal test module
  ----------------------------}
begin
     if FormExists('SealTestFrm') then begin
        if SealTestFrm.WindowState = wsMinimized then SealTestFrm.WindowState := wsNormal ;
        SealTestFrm.BringToFront ;
        SealTestFrm.SetFocus ;
        end
     else begin
        SealTestFrm := TSealTestFrm.Create(Self) ;
        SealTestFrm.Left := 15 ;
        SealTestFrm.Top := 15 ;
        end ;
     end;


procedure TMain.AboutClick(Sender: TObject);
begin
     AboutDlg := TAboutDlg.Create(Self) ;
     AboutDlg.ShowModal ;
     end;


procedure TMain.UpdateWindows ;
begin
    { ViewWCPChild.NewFile := True ;}
     end ;


procedure TMain.InspectLogFileClick(Sender: TObject);
begin
     LogFrm := TLogFrm.Create(Self) ;
     end;


procedure TMain.mnImportClick(Sender: TObject);
{ - Menu Item --------------------------------
  Import records from a foreign data file type
  --------------------------------------------}
begin

     CloseFormsAndDataFile( AllForms ) ;

     if ImportFromDataFile then begin ;
        { Open new data file }
        LoadDataFiles( CdrFH.FileName ) ;
        // Update list of recently used files
        UpdateRecentFilesList ;
        // Update windows
        UpdateMDIWindows ;
        SetMenus ;
        end ;

     end;


procedure TMain.CopyDataClick(Sender: TObject);
{ -----------------------------
  Copy data values to clipboard
  -----------------------------}
begin
     if ActiveMDIChild.Name = 'ViewSigFrm' then
        TViewSigFrm(ActiveMDIChild).scDisplay.CopyDataToClipBoard ;
     if ActiveMDIChild.Name = 'NoiseAnalFrm' then
        TNoiseAnalFrm(ActiveMDIChild).CopyDataToClipboard ;
     if ActiveMDIChild.Name = 'SingleChanAnalFrm' then
        TSingleChanAnalFrm(ActiveMDIChild).CopyDataToClipboard ;
     if ActiveMDIChild.Name = 'EventDetFrm' then
        TEventDetFrm(ActiveMDIChild).CopyDataToClipboard ;
     if ActiveMDIChild.Name = 'ECGFrm' then
        TECGFrm(ActiveMDIChild).CopyDataToClipboard ;
     if ActiveMDIChild.Name = 'RecPlotFrm' then
        TRecPlotFrm(ActiveMDIChild).CopyDataToClipboard ;

     end ;


procedure TMain.CopyImageClick(Sender: TObject);
{ ---------------------------------
  Copy displayed image to clipboard
  ---------------------------------}
begin
     if ActiveMDIChild.Name = 'ViewSigFrm' then
        TViewSigFrm(ActiveMDIChild).CopyImageToClipboard ;
     if ActiveMDIChild.Name = 'NoiseAnalFrm' then
        TNoiseAnalFrm(ActiveMDIChild).CopyImageToClipboard ;
     if ActiveMDIChild.Name = 'SingleChanAnalFrm' then
        TSingleChanAnalFrm(ActiveMDIChild).CopyImageToClipboard ;
     if ActiveMDIChild.Name = 'EventDetFrm' then
        TEventDetFrm(ActiveMDIChild).CopyImageToClipboard ;
     if ActiveMDIChild.Name = 'PageViewFrm' then
        TPageViewFrm(ActiveMDIChild).CopyImageToClipboard ;
     if ActiveMDIChild.Name = 'ECGFrm' then
        TECGFrm(ActiveMDIChild).CopyImageToClipboard ;


     end ;


procedure TMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
     if MessageDlg( 'Exit Program! Are you Sure? ', mtConfirmation,
        [mbYes,mbNo], 0 ) = mrYes then CanClose := True
                                  else CanClose := False ;
     end;

procedure TMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin

        CloseFormsAndDataFile( AllForms ) ;

        { Close log file }
        CloseLogFile ;
        { Save initialization file }
        SaveInitializationFile( Settings.ProgDirectory + 'EDR.ini' ) ;

        // Dispose of marker list
        MarkerList.Free ;

        end;

{ **** MENU ITEMS **************************************************** }


procedure TMain.ClearTicksInViewMenu ;
begin
     {mnViewWCP.checked := false ;}
     end ;


procedure TMain.mnDefaultSettingsClick(Sender: TObject);
// --------------------------------
// Display default settings window
// --------------------------------
begin
     if FormExists('DefSetFrm') then begin
        if DefSetFrm.WindowState = wsMinimized then DefSetFrm.WindowState := wsNormal ;
        DefSetFrm.BringToFront ;
        DefSetFrm.SetFocus ;
        end
     else begin
        DefSetFrm := TDefSetFrm.Create(Self) ;
        DefSetFrm.Left := 10 ;
        DefSetFrm.Top := 10 ;
        end ;

     end;


procedure TMain.mnCED1902Click(Sender: TObject);
// ----------------------------------------
// Display CED 1902 amplifier control panel
// ----------------------------------------
begin
     if FormExists('CED1902Frm') then begin
        if CED1902Frm.WindowState = wsMinimized then CED1902Frm.WindowState := wsNormal ;
        CED1902Frm.BringToFront ;
        CED1902Frm.SetFocus ;
        end
     else begin
        CED1902Frm := TCED1902Frm.Create(Self) ;
        CED1902Frm.Left := 10 ;
        CED1902Frm.Top := 10 ;
        end ;

     end;


procedure TMain.ContentsClick(Sender: TObject);
begin
     application.helpcommand( HELP_CONTENTS, 0 ) ;
     end;


procedure TMain.SearchforHelpon1Click(Sender: TObject);
begin
     application.helpcommand( HELP_PARTIALKEY, 0 ) ;

     end;


procedure TMain.HowtouseHelp1Click(Sender: TObject);
begin
     application.helpcommand( HELP_HELPONHELP, 0 ) ;
     end;


procedure TMain.mnAppendClick(Sender: TObject);
{ - Menu Item -------------------------------------------
  Append records from another EDR data file (convert.pas)
  -------------------------------------------------------}
begin

     { Get the name of a data file from user }
     OpenDialog.options := [ofPathMustExist] ;
     OpenDialog.DefaultExt := DataFileExtension ;
     if Settings.DataDirectory <> '' then
        OpenDialog.InitialDir := Settings.DataDirectory ;
     OpenDialog.Filter := format( ' EDR Files (*%s)|*%s',
                                  [DataFileExtension,DataFileExtension]) ;
     OpenDialog.Title := 'Append File ' ;

     if not OpenDialog.execute then Exit ;

     if OpenDialog.FileName = CdrFH.FileName then begin
        MessageDlg( 'APPEND: Can''t append file to itself',mtWarning, [mbOK], 0 ) ;
        Exit ;
        end ;

     AppendEDRFile( OpenDialog.FileName ) ;

     SetMenus ;
     end;


procedure TMain.mnExportClick(Sender: TObject);
{ - Menu Item --------------------------------
  Export records to a foreign data file type
  --------------------------------------------}
begin
     ExportFrm.ShowModal ;
     end ;


procedure TMain.mnViewSigClick(Sender: TObject);
{ --------------------------
  Open signal display window
  -------------------------- }
begin
     if FormExists('ViewSigFrm') then begin
        if ViewSigFrm.WindowState = wsMinimized then ViewSigFrm.WindowState := wsNormal ;
        ViewSigFrm.BringToFront ;
        ViewSigFrm.SetFocus ;
        end
     else begin
        ViewSigFrm := TViewSigFrm.Create(Self) ;
        ViewSigFrm.Left := 15 ;
        ViewSigFrm.Top := 15 ;
        ViewSigFrm.Height := ClientHeight - ViewSigFrm.Top - 10 ;
        ViewSigFrm.Width := Min( ViewSigFrm.Width,
                                 ClientWidth - ViewSigFrm.Left - 30 ) ;
        end ;
     end;


procedure TMain.mnMEPCSimClick(Sender: TObject);
{ ---------------------------
  Open MEPC simulation window
  --------------------------- }
begin
     if FormExists('simmepcfrm') then begin
        if simmepcfrm.WindowState = wsMinimized then simmepcfrm.WindowState := wsNormal ;
        simmepcfrm.BringToFront ;
        simmepcfrm.SetFocus ;
        end
     else begin
        simmepcfrm := Tsimmepcfrm.Create(Self) ;
        simmepcfrm.Left := 20 ;
        simmepcfrm.Top := 20 ;
        end ;
     end;


procedure TMain.mnNoiseAnalClick(Sender: TObject);
{ -----------------------------------
  Open variance/power spectrum window
  ----------------------------------- }
begin
     if FormExists('NoiseAnalFrm') then begin
        if NoiseAnalFrm.WindowState = wsMinimized then NoiseAnalFrm.WindowState := wsNormal ;
        NoiseAnalFrm.BringToFront ;
        NoiseAnalFrm.SetFocus ;
        end
     else begin
        NoiseAnalFrm := TNoiseAnalFrm.Create(Self) ;
        NoiseAnalFrm.Left := 20 ;
        NoiseAnalFrm.Top := 20 ;
        end ;
     end;


procedure TMain.mnSimNoiseClick(Sender: TObject);
{ ----------------------------
  Ion channel noise simulation
  ----------------------------}
begin
     if FormExists('SimNoiseFrm') then begin
        if SimNoiseFrm.WindowState = wsMinimized then SimNoiseFrm.WindowState := wsNormal ;
        SimNoiseFrm.BringToFront ;
        SimNoiseFrm.SetFocus ;
        end
     else begin
        SimNoiseFrm := TSimNoiseFrm.Create(Self) ;
        SimNoiseFrm.Left := 20 ;
        SimNoiseFrm.Top := 20 ;
        end ;
     end;


procedure TMain.CopyAndPrintMenus( CopyEnabled,PrintEnabled : Boolean ) ;
{ -------------------------------------------
  Enable/disable copying and printingf menus
  -------------------------------------------}
begin
     CopyData.Enabled := CopyEnabled ;
     CopyImage.Enabled := CopyEnabled  ;
     mnPrint.Enabled := PrintEnabled ;
     end ;


procedure TMain.mnSimChanClick(Sender: TObject);
{ ----------------------------------
  Single-channel currents simulation
  ----------------------------------}
begin
     if FormExists('SimChanFrm') then begin
        if SimChanFrm.WindowState = wsMinimized then SimChanFrm.WindowState := wsNormal ;
        SimChanFrm.BringToFront ;
        SimChanFrm.SetFocus ;
        end
     else begin
        SimChanFrm := TSimChanFrm.Create(Self) ;
        SimChanFrm.Left := 20 ;
        SimChanFrm.Top := 20 ;
        end ;
     end;


procedure TMain.mnSingleChanAnalClick(Sender: TObject);
{ -----------------------------------
  Open single-channel analysis window
  ----------------------------------- }
begin
     if FormExists('singlechananalfrm') then begin
        if singlechananalfrm.WindowState = wsMinimized then singlechananalfrm.WindowState := wsNormal ;
        singlechananalfrm.BringToFront ;
        singlechananalfrm.SetFocus ;
        end
     else begin
        singlechananalfrm := Tsinglechananalfrm.Create(Self) ;
        singlechananalfrm.Left := 20 ;
        singlechananalfrm.Top := 20 ;
        end ;
     end;


procedure TMain.mnDigitalFilterClick(Sender: TObject);
{ - Menu Item --------------------------------
  Open digital filter dialog form (digfilt.pas)
  --------------------------------------------}
begin
     DigFilterDlg.ShowModal ;
     if DigFilterDlg.ModalResult = mrOK then UpdateViewSig
     end;


procedure TMain.mnRestoreOriginalClick(Sender: TObject);
{ - Menu Item --------------------------
  Restore orginal data from back up file
  -------------------------------------- }
begin
     Screen.Cursor := crHourGlass ;
     RestoreFromBackupFile ;
     Screen.Cursor := crDefault ;
     UpdateViewSig ;
     end ;


procedure TMain.UpdateViewSig ;
var
   i : Integer ;
begin
     { Update Signal display window if it is open }
     for i := 0 to MDIChildCount-1 do
         if MDIChildren[I].Name = 'ViewSigFrm' then TViewSigFrm(MDIChildren[I]).NewData ;
     end;


procedure TMain.UpdateMDIWindows ;
var
   i : Integer ;
   ViewSigExists : Boolean ;
begin

     SetMenus ;

     ViewSigExists := False ;
     for i := 0 to MDIChildCount-1 do begin
         if MDIChildren[I].Name = 'ViewSigFrm' then begin
            TViewSigFrm(MDIChildren[I]).DisplayFromFile ;
            ViewSigExists := True ;
            end ;
         end ;

     { If no replay form ... create one }
     if (CdrfH.NumSamplesInFile > 0) and (not ViewSigExists) then
        ViewSigFrm := TViewSigFrm.Create(Self) ;

     end;


procedure TMain.mnRecentFile0Click(Sender: TObject);
{ - Menu Item -------------------
  Load a recently used data file
  ------------------------------- }
var
   FileName : string ;
begin

     { Close existing data file }
     CloseFormsAndDataFile( AllForms ) ;
     
     { Get file name. Note, the .Tag property of the menu item was set
       at design time to point to the appropriate RecentFiles array item }
     FileName := Settings.RecentFiles[TMenuItem(Sender).Tag] ;
     if (FileName <> '') and FileExists(FileName) then begin

       { OldNumSamples := RawFH.NumSamples ;}

        LoadDataFiles(FileName) ;

        { Set display magnification to minimum }
{        if CdrFH.NumRecords > 0 then begin
           if OldNumSamples <> RawFH.NumSamples then mnZoomOut.Click ;
           mnShowRaw.Click ;
           end ;}
        SetMenus ;
        end ;

     end;



procedure TMain.mnDisplayGridClick(Sender: TObject);
{ -----------------------------------------------------
  Enable/display grid overlay on oscilloscope displays
  ----------------------------------------------------- }
var
   i : Integer ;
begin
     Settings.DisplayGrid := not Settings.DisplayGrid ;
     mnDisplayGrid.Checked := Settings.DisplayGrid ;
     for i := 0 to MDIChildCount-1 do begin
          if MDIChildren[I].Name = 'ViewSigFrm' then
             TViewSigFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'SimNoiseFrm' then
             TSimNoiseFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'SimChanFrm' then
             TSimChanFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'NoiseAnalFrm' then
             TNoiseAnalFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'SingleChanAnalFrm' then
             TSingleChanAnalFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'RecordFrm' then
             TRecordFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'ViewSigFrm' then
             TViewSigFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'SealTestFrm' then
             TSealTestFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'PageViewFrm' then
             TPageViewFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'EventDetFrm' then
             TEventDetFrm(MDIChildren[I]).ChangeDisplayGrid ;
          end ;

     end;


procedure TMain.UpdateAllDisplays ;
{ -----------------------------------------------
  Update all open forms when data file is changed
  ----------------------------------------------- }
var
   i : Integer ;
begin

     for i := 0 to MDIChildCount-1 do begin
          if MDIChildren[I].Name = 'ViewSigFrm' then
             TViewSigFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'SimNoiseFrm' then
             TSimNoiseFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'SimChanFrm' then
             TSimChanFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'NoiseAnalFrm' then
             TNoiseAnalFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'SingleChanAnalFrm' then
             TSingleChanAnalFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'RecordFrm' then begin
             TRecordFrm(MDIChildren[I]).RestartADC ;
             TRecordFrm(MDIChildren[I]).ChangeDisplayGrid
             end
          else if MDIChildren[I].Name = 'ViewSigFrm' then
             TViewSigFrm(MDIChildren[I]).ChangeDisplayGrid
          else if MDIChildren[I].Name = 'SealTestFrm' then
             TSealTestFrm(MDIChildren[I]).ChangeDisplayGrid ;
          end ;
     end;

procedure TMain.UpdateChannelScalingFactors ;
// ------------------------------
// Update channel scaling factors
// ------------------------------
var
   ch : Integer ;
begin
     for ch := 0 to CDRFH.NumChannels-1 do begin

         // Ensure that calibration factor is non-zero
         if Channel[ch].ADCCalibrationFactor = 0.0 then
            Channel[ch].ADCCalibrationFactor := 0.001 ;

         // Ensure that amplifier gain is non-zero
         if Channel[ch].ADCAmplifierGain = 0.0 then
            Channel[ch].ADCAmplifierGain := 1.0 ;

         // Calculate bits->units scaling factor
         Channel[ch].ADCScale := Abs(CDRFH.ADCVoltageRange) /
                                (Channel[ch].ADCCalibrationFactor*
                                 Channel[ch].ADCAmplifierGain
                                 *(Channel[ch].ADCMaxValue+1) ) ;
         end ;
     end ;


procedure TMain.mnInvertClick(Sender: TObject);
 // - Menu Item --------------------------------
 // Open Invert channel dialog box (invert.pas)
 // --------------------------------------------
begin
     InvertDlg.ShowModal ;
     if InvertDlg.ModalResult = mrOK then UpdateViewSig
     end;

procedure TMain.mnDetectEvent2Click(Sender: TObject);
{ ---------------------------
  Open event detection window
  --------------------------- }
begin
     if FormExists('EventDetFrm') then begin
        if EventDetFrm.WindowState = wsMinimized then EventDetFrm.WindowState := wsNormal ;
        EventDetFrm.BringToFront ;
        EventDetFrm.SetFocus ;
        end
     else begin
        EventDetFrm := TEventDetFrm.Create(Self) ;
        EventDetFrm.Left := 20 ;
        EventDetFrm.Top := 20 ;
        end ;
     end;


procedure TMain.EditClick(Sender: TObject);
// --------------------------------------
// Enable/disable Copy items on Edit menu
// --------------------------------------
begin

     CopyData.Enabled := False ;
     CopyImage.Enabled := False ;

     if MDIChildCount <= 0 then Exit ;

     if (ActiveMDIChild.Name = 'ViewSigFrm') then begin
        CopyData.Enabled := True ;
        CopyImage.Enabled := True ;
        end
     else if (ActiveMDIChild.Name = 'NoiseAnalFrm') then begin
        CopyData.Enabled := True ;
        CopyImage.Enabled := True ;
        end
     else if (ActiveMDIChild.Name = 'SingleChanAnalFrm') then begin
        CopyData.Enabled := True ;
        CopyImage.Enabled := True ;
        end
     else if (ActiveMDIChild.Name = 'EventDetFrm') then begin
        CopyData.Enabled := EventDetFrm.IsClipboardDataAvailable ;
        CopyImage.Enabled := EventDetFrm.IsClipboardDataAvailable ;
        end
     else if (ActiveMDIChild.Name = 'ECGFrm') then begin
        CopyData.Enabled := ECGFrm.IsClipboardDataAvailable ;
        CopyImage.Enabled := ECGFrm.IsClipboardDataAvailable ;
        end
     else if (ActiveMDIChild.Name = 'PageViewFrm') then begin
        CopyData.Enabled := False ;
        CopyImage.Enabled := True ;
        end
     else if (ActiveMDIChild.Name = 'RecPlotFrm') then begin
        CopyData.Enabled := True ;
        CopyImage.Enabled := False ;
        end ;

     if CDRFH.FileHandle >= 0 then mnEditMarkers.Enabled := True
                              else mnEditMarkers.Enabled := False ;

     end;


procedure TMain.mnShowHintsClick(Sender: TObject);
{ --------------------
  Enable/Disable hints
  --------------------}
begin
     Application.ShowHint := not Application.ShowHint ;
     mnShowHints.Checked := Application.ShowHint ;
     end;

procedure TMain.mnPageViewClick(Sender: TObject);
// ------------------------------------------
// Open Multi-line page signal display window
// ------------------------------------------
begin
     if FormExists('PageViewFrm') then begin
        if PageViewFrm.WindowState = wsMinimized then PageViewFrm.WindowState := wsNormal ;
        PageViewFrm.BringToFront ;
        PageViewFrm.SetFocus ;
        end
     else begin
        PageViewFrm := TPageViewFrm.Create(Self) ;
        PageViewFrm.Left := 15 ;
        PageViewFrm.Top := 15 ;
        end ;

     end;


procedure TMain.File1Click(Sender: TObject);
// -----------------------------------
// Enable/disable available menu items
// -----------------------------------
var
    i : Integer ;
begin

     if MDIChildCount > 0 then begin

        if FormExists( 'RecordFrm' ) then begin
              for i := 0 to File1.Count-1 do begin
                 if RecordFrm.Recording then File1.Items[i].Enabled := False
                                        else File1.Items[i].Enabled := True ;
                 end ;
           end ;

        if ActiveMDIChild.Name = 'EventDetFrm' then begin
           mnPrint.Enabled := False ;
           if TEventDetFrm(ActiveMDIChild).IsClipboardDataAvailable then
              mnPrint.Enabled := True ;
           end
        else if ActiveMDIChild.Name = 'ECGFrm' then begin
           mnPrint.Enabled := False ;
           if TECGFrm(ActiveMDIChild).IsClipboardDataAvailable then
              mnPrint.Enabled := True ;
           end
        else if ActiveMDIChild.Name = 'PageViewFrm' then begin
           mnPrint.Enabled := True ;
           end ;

        end ;

     end;


procedure TMain.mnEditMarkersClick(Sender: TObject);
// --------------------------------------
//  Display edit chart markers dialog box
// --------------------------------------
begin
     // Display dialog box
     EditMarkersFrm.Left :=Main.Left + 15 ;
     EditMarkersFrm.Top :=Main.Top +  55 ;
     EditMarkersFrm.showmodal ;

     // Save settings
     SaveCDRHeader( CDRFH ) ;

     // Update display
     if FormExists('ViewSigFrm') then ViewSigFrm.DisplayFromFile ;
     if FormExists('PageViewFrm') then PageViewFrm.NewFile ;

     end ;


function TMain.ImportFromDataFile : Boolean ;
{ -------------------------------------------
  Import data from another data file format
  -------------------------------------------}
const
   NumScansPerBuf = 512 ;
Type
     TFilter = record
              Ext : string ;
              FType : TADCDataFileType ;
              end ;
var
   Filters : Array[1..17] of TFilter ;
   FileName : String ;
   FileType : TADCDataFileType ;
   i : Integer ;
   Buf : Array[0..NumScansPerBuf*(EDRChannelLimit+1)-1] of SmallInt ;
   NumScans : Integer ;
   NumScansTotal : Integer ;
   NumScansToRead : Integer ;
   NumRead : Integer ;
   NumWrite : Integer ;
   iRec,ch : Integer ;
begin

     Result := False ;

     Filters[1].Ext := 'Axon Files (*.DAT,*.ABF)|*.DAT;*.ABF|' ;
     Filters[1].FType := ftUnknown ;
     Filters[2].Ext := 'CED Files (*.DAT,*.CFS)|*.DAT;*.CFS|' ;
     Filters[2].FType := ftCFS ;
     Filters[3].Ext := 'PAT Files (*.SCD)|*.SCD|' ;
     Filters[3].FType := ftSCD ;
     Filters[4].Ext := 'CDR Files (*.CDR)|*.CDR|' ;
     Filters[4].FType := ftCDR ;
     Filters[5].Ext := 'WCP Files (*.WCP)|*.WCP|' ;
     Filters[5].FType := ftWCP ;
     Filters[6].Ext := 'WinCDR Files (*.WCD)|*.WCD|' ;
     Filters[6].FType := ftWCD ;
     Filters[7].Ext := 'SPAN Files (*.SPA)|*.SPA|' ;
     Filters[7].FType := ftSPA ;
     Filters[8].Ext := 'SCAN Files (*.SCA)|*.SCA|' ;
     Filters[8].FType := ftSCA ;
     Filters[9].Ext := 'Physionet WFDB Files (*.HEA)|*.HEA|' ;
     Filters[9].FType := ftWFDB ;
     Filters[10].Ext := 'IGOR Binary Files (*.IBW)|*.IBW|' ;
     Filters[10].FType := ftIBW ;
     Filters[11].Ext := 'PoNeMah Files (*.PRO)|*.PRO|' ;
     Filters[11].FType := ftPNM ;
     Filters[12].Ext := 'Chart Files (*.CHT)|*.cht|' ;
     Filters[12].FType := ftCHT ;
     Filters[13].Ext := 'WAV Files (*.WAV, *.DAT)|*.wav;*.dat|' ;
     Filters[13].FType := ftCHT ;
     Filters[14].Ext := 'ASCII Text (*.TXT,*.ASC)|*.TXT;*.ASC|' ;
     Filters[14].FType := ftASC ;
     Filters[15].Ext := 'Raw Binary (*.dat,*.raw)|*.dat;*.raw|' ;
     Filters[15].FType := ftRaw ;
     Filters[16].Ext := 'HEKA Pulse (*.asc)|*.asc|' ;
     Filters[16].FType := ftHEK ;
     Filters[17].Ext := 'All Files (*.*)|*.*|' ;
     Filters[17].FType := ftUnknown ;

     OpenDialog.Filter := '' ;
     for i := 1 to High(Filters) do
         OpenDialog.Filter := OpenDialog.Filter + Filters[i].Ext ;
     OpenDialog.options := [ofPathMustExist] ;
     OpenDialog.DefaultExt := 'DAT' ;

     OpenDialog.Title := 'Import File ' ;
     if Settings.DataDirectory <> '' then OpenDialog.InitialDir := Settings.DataDirectory ;

     if not OpenDialog.execute then Exit ;

     Settings.DataDirectory := ExtractFilePath( OpenDialog.FileName ) ;
     FileType := Filters[OpenDialog.FilterIndex].FType ;
     FileName := OpenDialog.FileName ;

     { Close existing data file }
     if CdrFH.FileHandle >= 0 then begin
        FileClose( CdrFH.FileHandle ) ;
        CdrFH.FileHandle := -1 ;
        end ;

     { Create name of EDR file to hold imported file }
     CdrFH.FileName := ChangeFileExt( FileName, DataFileExtension ) ;
     { Make sure an existing data file is not overwritten, unintentionally }
     if not FileOverwriteCheck( CdrFH.FileName ) then Exit ;

     // Report progress
     Main.StatusBar.SimpleText := format(
     ' IMPORT: Importing data from %s ',[FileName] ) ;

     if FileType = ftASC then begin
        // ASCII format data files
        ImportASCIIFrm.ImportFile := ImportFile ;
        ImportASCIIFrm.FileName := FileName ;
        if ImportASCIIFrm.ShowModal <> mrOK Then Exit ;
        if not Main.ImportFile.OpenDataFile( FileName, FileType ) then Exit ;
        end
     else if FileType = ftRaw then begin
        // Raw binary data files
        ImportRawFrm.ImportFile := ImportFile ;
        if ImportRawFrm.ShowModal <> mrOK Then Exit ;
        if not Main.ImportFile.OpenDataFile( FileName, FileType ) then Exit ;
        end
     else  if FileType = ftHEK then begin
        if not Main.ImportFile.OpenDataFile( FileName, FileType ) then Exit ;
        end
     else begin
        // All other data files
        FileType := ImportFile.FindFileType( FileName ) ;
        if not Main.ImportFile.OpenDataFile( FileName, FileType ) then Exit ;
        end ;

     { Create EDR data file to hold import}
     if not CreateNewDataFile( CdrFH ) then Exit ;

     CdrFH.NumChannels := Main.ImportFile.NumChannelsPerScan ;
     //NumSamplesPerBuf := NumScansPerBuf*Main.ImportFile.NumChannelsPerScan ;

     // Reset display magnification
     for ch := 0 to EDRChannelLimit do begin
         Channel[ch].ADCMaxValue := Main.ImportFile.MaxADCValue ;
         Channel[ch].yMin := -Channel[ch].ADCMaxValue -1 ;
         Channel[ch].yMax := Channel[ch].ADCMaxValue ;
         end ;

     CdrFH.IdentLine := Main.ImportFile.IdentLine ;

     { Copy records }
     NumScansTotal := 0 ;
     for iRec := 1 to Main.ImportFile.NumRecords do begin

         Main.ImportFile.RecordNum := iRec ;
         CdrFH.ADCVoltageRange := Main.ImportFile.ChannelADCVoltageRange[0] ;
         for ch := 0 to Main.ImportFile.NumChannelsPerScan-1 do begin
             Channel[ch].ChannelOffset := Main.ImportFile.ChannelOffset[ch] ;
             Channel[ch].ADCName := Main.ImportFile.ChannelName[ch] ;
             Channel[ch].ADCUnits := Main.ImportFile.ChannelUnits[ch] ;
             Channel[ch].ADCSCale := Main.ImportFile.ChannelScale[ch] ;
             Channel[ch].ADCCalibrationFactor := Main.ImportFile.ChannelCalibrationFactor[ch] ;
             Channel[ch].ADCAmplifierGain := Main.ImportFile.ChannelGain[ch] ;
             end ;

         { Copy sampling interval and A/D range }
         CdrFH.dt := Main.ImportFile.ScanInterval ;
         CdrFH.ADCVoltageRange := Main.ImportFile.ChannelADCVoltageRange[0] ;

         NumScans := 0 ;
         NumScansToRead := Main.ImportFile.NumScansPerRecord ;
         while NumScansToRead > 0 do begin

             // Read A/D sample from source file
             for i := 0 to High(Buf) do Buf[i] := 0 ;
             NumRead := Main.ImportFile.LoadADCBuffer( NumScans, NumScansPerBuf, Buf ) ;
             if NumRead <= 0 then Break ;

             // Correct calibration factor for any differences between
             for ch := 0 to Main.ImportFile.NumChannelsPerScan-1 do begin
                 Channel[ch].ADCCalibrationFactor := Main.ImportFile.ChannelCalibrationFactor[ch]*
                                                     (Main.ImportFile.ChannelADCVoltageRange[0]/
                                                      Main.ImportFile.ChannelADCVoltageRange[ch])  ;
                 end ;

             { Write A/D samples to EDR file }
             NumWrite := Min(NumRead,NumScansToRead) ;
             WriteCDRBuffer( CdrFH,NumScansTotal,Buf,NumWrite ) ;

             NumScans := NumScans + NumRead ;
             NumScansToRead := NumScansToRead - NumRead ;
             NumScansTotal := NumScansTotal + NumWrite ;

             // Report progress
             Main.StatusBar.SimpleText := format(
             ' IMPORT: Importing %d/%d (%d channel) scans from record %d/%d from %s ',
             [NumScans,
              Main.ImportFile.NumScansPerRecord,
              Main.ImportFile.NumChannelsPerScan,
              iRec,Main.ImportFile.NumRecords,FileName]) ;

             end ;

         end ;

     CDRfH.NumSamplesInFile := NumScansTotal*CdrFH.NumChannels ;
     for ch := 0 to CdrFH.NumChannels-1 do
         Channel[ch].ADCScale := CalibFactorToADCScale(
                                 CdrFH.ADCVoltageRange,
                                 Channel[ch] ) ;

     // Final progress
     Main.StatusBar.SimpleText := format(
     'IMPORT: %d records (%dx%d channel scans) imported from %s to %s',
     [Main.ImportFile.NumRecords,
      Main.ImportFile.NumScansPerRecord,
      Main.ImportFile.NumChannelsPerScan,
      FileName,
      CDRFH.FileName]) ;
     WriteToLogFile( Main.StatusBar.SimpleText ) ;

     { Save file header }
     SaveCDRHeader( CdrFH ) ;

     { Close source file }
     Main.ImportFile.CloseDataFile ;

     { Close dest. file }
     if CdrFH.FileHandle >= 0 then begin
        FileClose( CdrFH.FileHandle ) ;
        CdrFH.FileHandle := -1 ;
        end ;

     Result := True ;

     end ;


procedure TMain.AppendEDRFile( FileName : string ) ;
{ ------------------------------------------------
  Append an EDR data file on to end of current one
  ------------------------------------------------}
const
   NumScansPerBuf = 512 ;
var
   Buf : Array[0..NumScansPerBuf*(EDRChannelLimit+1)-1] of SmallInt ;
   NumScans : Integer ;
   NumScansTotal : Integer ;
   NumScansOriginal : Integer ;
   NumScansToRead : Integer ;
   NumRead : Integer ;
   NumWrite : Integer ;
   iRec : Integer ;
begin

     { Open data file to be imported }
     if not Main.ImportFile.OpenDataFile( FileName, ftEDR ) then Exit ;

     if (CdrFH.NumChannels <> Main.ImportFile.NumChannelsPerScan) then begin
        MessageDlg( 'APPEND: Unable to append files with different numbers of channels!',
                    mtWarning, [mbOK], 0 ) ;
        Exit ;
        end ;

     if Channel[0].ADCMaxValue <> Main.ImportFile.MaxADCValue then begin
        MessageDlg( 'APPEND: Unable to append files with different A/D resolutions!',
                    mtWarning, [mbOK], 0 ) ;
        Exit ;
        end ;

     if CDRFH.dt <> Main.ImportFile.ScanInterval then begin
        MessageDlg( 'APPEND: Unable to append files with different sampling intervals!',
                    mtWarning, [mbOK], 0 ) ;
        Exit ;
        end ;

     { Copy records }
     NumScansOriginal := CDRFH.NumSamplesInFile div CDRFH.NumChannels ;
     NumScansTotal := NumScansOriginal ;
     for iRec := 1 to Main.ImportFile.NumRecords do begin

         Main.ImportFile.RecordNum := iRec ;

         NumScans := 0 ;
         NumScansToRead := Main.ImportFile.NumScansPerRecord ;
         while NumScansToRead > 0 do begin

             // Read A/D sample from source file
             NumRead := Main.ImportFile.LoadADCBuffer( NumScans, NumScansPerBuf, Buf ) ;

             { Write A/D samples to EDR file }
             NumWrite := Min(NumRead,NumScansToRead) ;
             WriteCDRBuffer( CdrFH,NumScansTotal,Buf,NumWrite ) ;

             NumScans := NumScans + NumRead ;
             NumScansToRead := NumScansToRead - NumRead ;
             NumScansTotal := NumScansTotal + NumWrite ;

             // Report progress
             Main.StatusBar.SimpleText := format(
             ' APPEND: Add %d/%d scans (%d ch.) from record %d/%d from %s ',
             [ NumScans,
               Main.ImportFile.NumScansPerRecord,
               Main.ImportFile.NumChannelsPerScan,
               iRec,
               Main.ImportFile.NumRecords,
               FileName]) ;

             end ;

         end ;

     // Final progress
     Main.StatusBar.SimpleText := format(
     'APPEND: %d records (%dx%d channel scans) imported from %s',
     [Main.ImportFile.NumRecords,
      NumScansTotal - NumScansOriginal,
      CdrFH.NumChannels,
      FileName]) ;
     WriteToLogFile( Main.StatusBar.SimpleText ) ;

     CDRfH.NumSamplesInFile := NumScansTotal*CdrFH.NumChannels ;

     { Save file header }
     SaveCDRHeader( CdrFH ) ;

     WriteToLogFile( FileName + ' appended to ' + CdrFH.FileName ) ;

     { Close source file }
     Main.ImportFile.CloseDataFile ;

     // Update windows
     UpdateMDIWindows ;

     end ;

procedure TMain.mnECGFrmClick(Sender: TObject);
// ------------------------
//  Open ECG analysis window
// ------------------------
begin
     ECGFrm := TECGFrm.Create(Self) ;
     end;


procedure TMain.mnVP500Click(Sender: TObject);
// -------------------------------
//  Display VP500 control window
// ------------------------------- }
begin
     if FormExists('VP500PanelFrm') then begin
        if VP500PanelFrm.WindowState = wsMinimized then VP500PanelFrm.WindowState := wsNormal ;
        VP500PanelFrm.BringToFront ;
        VP500PanelFrm.SetFocus ;
        end
     else begin
        VP500PanelFrm := TVP500PanelFrm.Create(Self) ;
        VP500PanelFrm.Left := 10 ;
        VP500PanelFrm.Top := 10 ;
        end ;
     end ;


function TMain.CreateIndexedFileName(
         FileName : String ) : String ;
// ---------------------------------------------------
// Append an (incremented) index number to end of file
// ---------------------------------------------------
var
     i : Integer ;
     ExtensionStart : Integer ;
     IndexNumberStart : Integer ;
     IndexNum : Integer ;
     NewFileName : String ;
     FileStem : String ;
begin

     if FileName = '' then FileName := 'File' ;

     // Ensure file has EDR extension
     ChangeFileExt( FileName, '.edr' ) ;

     // Find '_nnn' index number (if it exists)
     i := Length(FileName) ;
     ExtensionStart := Length(FileName)+1 ;
     IndexNumberStart := -1 ;
     While (i > 0) do begin
         if FileName[i] = '.' then ExtensionStart := i ;
         if FileName[i] = '_' then begin
            IndexNumberStart := i ;
            Break ;
            end ;
         Dec(i) ;
         end ;

     if ((ExtensionStart - IndexNumberStart) > 4)
        or (ExtensionStart < 5) then IndexNumberStart := -1 ;


     // Find next available (lowest) index number for this file name

     FileStem :=  '' ;
     if IndexNumberStart > 0 then begin
        for i := 1 to IndexNumberStart-1 do
            FileStem :=  FileStem + FileName[i] ;
        end
     else begin
        for i := 1 to ExtensionStart-1 do
            FileStem :=  FileStem + FileName[i] ;
        end ;

     IndexNum := 0 ;
     repeat
          Inc(IndexNum) ;
          NewFileName := FileStem + format('_%.3d',[IndexNum]) ;
          for i := ExtensionStart to Length(FileName) do
              NewFileName := NewFileName + FileName[i] ;
          until not FileExists(NewFileName) ;

     // Return name
     Result := NewFileName ;

     end ;


procedure TMain.mnZoomOutClick(Sender: TObject);
// ----------------
// Display zoom out
// ----------------
var
    i,ch : Integer ;
begin

     for i := 0 to mnZoomOut.Count-1 do mnZoomOut.Items[i].Visible := False ;
     if MDIChildCount <= 0 then Exit ;

     if ActiveMDIChild.Name = 'RecordFrm' then begin
        // Use Recording channels if recording form is active
        for ch := 0 to Settings.NumChannels-1 do begin
            mnZoomOut.Items[ch].Caption := format( 'Ch.%d (%s)',
                                         [ch,RecChannel[ch].ADCName]) ;
            mnZoomOut.Items[ch].Tag := ch ;
            mnZoomOut.Items[ch].Visible := True ;
            end ;
         end
     else begin
        // Otherwise use file channels
        for ch := 0 to CDRFH.NumChannels-1 do begin
            mnZoomOut.Items[ch].Caption := format( 'Ch.%d (%s)',
                                         [ch,Channel[ch].ADCName]) ;
            mnZoomOut.Items[ch].Tag := ch ;
            mnZoomOut.Items[ch].Visible := True ;
            end ;
        end ;
     end ;


procedure TMain.mnZoomInCh0Click(Sender: TObject);
// --------------------------------------
// Vertically magnify selected channel X2
// --------------------------------------
var
    i : Integer ;
begin

     for i := 0 to MDIChildCount-1 do begin
          if MDIChildren[I].Name = 'NoiseAnalFrm' then
             TNoiseAnalFrm(MDIChildren[I]).ZoomIn(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'SingleChanAnalFrm' then
             TSingleChanAnalFrm(MDIChildren[I]).ZoomIn(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'RecordFrm' then
             TRecordFrm(MDIChildren[I]).ZoomIn(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'ViewSigFrm' then
             TViewSigFrm(MDIChildren[I]).ZoomIn(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'PageViewFrm' then
             TPageViewFrm(MDIChildren[I]).ZoomIn(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'EventDetFrm' then
             TEventDetFrm(MDIChildren[I]).ZoomIn(TMenuItem(Sender).Tag) ;
          end ;
    end;


procedure TMain.mnZoomOutCh0Click(Sender: TObject);
// --------------------------------------
// Vertically reduce selected channel X2
// --------------------------------------
var
    i : Integer ;
begin

     for i := 0 to MDIChildCount-1 do begin
          if MDIChildren[I].Name = 'NoiseAnal' then
             TNoiseAnalFrm(MDIChildren[I]).ZoomOut(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'SingleChanAnalFrm' then
             TSingleChanAnalFrm(MDIChildren[I]).ZoomOut(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'RecordFrm' then
             TRecordFrm(MDIChildren[I]).ZoomOut(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'ViewSigFrm' then
             TViewSigFrm(MDIChildren[I]).ZoomOut(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'PageViewFrm' then
             TPageViewFrm(MDIChildren[I]).ZoomOut(TMenuItem(Sender).Tag)
          else if MDIChildren[I].Name = 'EventDetFrm' then
             TEventDetFrm(MDIChildren[I]).ZoomOut(TMenuItem(Sender).Tag) ;
          end ;

    end;

procedure TMain.ViewClick(Sender: TObject);
// ------------------------------
// Enable/disable View menu items
// ------------------------------
begin

     mnDisplayGrid.Checked := Settings.DisplayGrid ;

     if (CDRFH.NumSamplesInFile > 0) and (CDRFH.FileHandle >=0) then begin
        mnViewSig.Enabled := True ;
        mnPageView.Enabled := True ;
        end
     else begin
        mnViewSig.Enabled := False ;
        mnPageView.Enabled := False ;
        end ;

     end;

procedure TMain.mnFilePropertiesClick(Sender: TObject);
// - Menu Item -------------------------------
//  File properties dialog (fileprops.pas)
//  -------------------------------------------}
begin
     if FormExists('FilePropsDlg') then begin
        if FilePropsDlg.WindowState = wsMinimized then FilePropsDlg.WindowState := wsNormal ;
        FilePropsDlg.BringToFront ;
        FilePropsDlg.SetFocus ;
        end
     else begin
        FilePropsDlg := TFilePropsDlg.Create(Self) ;
        FilePropsDlg.Left := 10 ;
        FilePropsDlg.Top := 10 ;
        end ;
     end ;


function TMain.FormExists( FormName : String ) : Boolean ;
// --------------------------
// Return TRUE if form exists
// --------------------------
var
    i : Integer ;
begin
     Result := False ;
     for i := 0 to MDIChildCount-1 do
         if ANSIContainsText(MDIChildren[i].Name,FormName) then Result := True ;
     end ;


procedure TMain.SetupClick(Sender: TObject);
var
    Amp : Integer ;
begin

     mnVP500.Enabled := False ;
     for Amp := 1 to 2 do
       if Amplifier.AmplifierType[Amp] = amVP500 then mnVP500.Enabled := True ;

     mnCED1902.Enabled := False ;
     for Amp := 1 to 2 do
       if Amplifier.AmplifierType[Amp] = amCED1902 then mnCED1902.Enabled := True ;
     end;

end.


