unit SpecForm;
{$I DEFINE.PAS}
interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls, StdCtrls, Clipbrd,
  {$IFDEF WIN32}
  CommInt, ComPort, Menus, Buttons,
  {$ELSE}
  Comm,
  {$ENDIF}
  ConfType,

  TVType, MyLib, Fileu, MyType, Stru, confu,
  ListType, Listu,
  Timer, BinHex,
  ShowMsg, cmgh,
  ApexType, Spectrum, {Globals, }Axisu, Plotu,
  DrawBuf,
  UlanType, UlanGlob, {v0.11}ExtDevIntu,{/v0.11 ghlbtype,}
  UldrvTyp, ul_lcabs, devmode, filtdlg,
  ULRecTyp, ULObju,
  ULAType, ULAObju,
  ULPType, ULBType,
  ULBRType, ULBRObju,
  ULPRType, ULPRObju,
  ULVLType, ULVLObju,
  ULVOType, ULVOObju,
  ULIType, ULIObju,

  MouseLin, WinUtl, ComCtrls, ToolWin, FontUtl,

  Printers, {$IFDEF ALPRINTER} AlRep, AlPrevFr, {$ENDIF} PrnUtl, DrawMeta,
  ActnList {v0.09}, FileMenuHdl {/v0.09}{v0.11},InfoFrm{/v0.11}
  {v0.14}
  ,Channelsu, AcqInfou, AnalSetupFrm
  {/v0.14}

  ;


type
  ESpecForm = class(Exception);

  TSpectrumForm = class(TForm)
    PaintBox: TPaintBox;
    Timer1: TTimer;
    SpectrumMemo: TMemo;
    PopupMenu: TPopupMenu;
    LogMenuItem: TMenuItem;
    MarginsItem: TMenuItem;
    RunStop_Item: TMenuItem;
    ToolsPanel: TPanel;
    BaseLineCreate_Btn: TSpeedButton;
    DefinePeaksSpeedButton: TSpeedButton;
    ReportPanel: TPanel;
    ReportMemo: TMemo;
    ZoomIn_Btn: TSpeedButton;
    ZoomOut_Btn: TSpeedButton;
    ViewLabel: TLabel;
    BaseLineCheckBox: TCheckBox;
    AxisCheckBox: TCheckBox;
    PeaksCheckBox: TCheckBox;
    ReportCheckBox: TCheckBox;
    RunPanel: TPanel;
    RunButton: TButton;
    StopButton: TButton;
    N1: TMenuItem;
    HeaderItem: TMenuItem;
    View_Item: TMenuItem;
    PeaksItem: TMenuItem;
    PeaksClearMenuItem: TMenuItem;
    PeaksDeleteLastItem: TMenuItem;
    Autodetect1: TMenuItem;
    PeaksBrowseItem: TMenuItem;
    BaseLineItem: TMenuItem;
    BaseLineCreate_Item: TMenuItem;
    BaseLineBrowse_Item: TMenuItem;
    PeaksDefineItem: TMenuItem;
    PrintItem: TMenuItem;
    XYEdit: TEdit;
    PeaksDeleteSelectedItem: TMenuItem;
    N2: TMenuItem;
    MethodItem: TMenuItem;
    MethodEditItem: TMenuItem;
    MethodPeaksItem: TMenuItem;
    MethodPeaksClearItem: TMenuItem;
    MethodPeaksBrowseItem: TMenuItem;
    FilterMenuItem: TMenuItem;
    PeaksToMethodItem: TMenuItem;
    BaseLineCopyToMethod_Item: TMenuItem;
    MethodBaseLineItem: TMenuItem;
    MethodBaseLineClearItem: TMenuItem;
    MethodBaseLineBrowseItem: TMenuItem;
    BaseLineDeleteAll_Item: TMenuItem;
    BaseLineDeleteSelected_Item: TMenuItem;
    PrinterSetupDialog: TPrinterSetupDialog;
    PrintReportItem: TMenuItem;
    Window: TMenuItem;
    CopyItem: TMenuItem;
    BaseLineInsertPoint_Item: TMenuItem;
    N3: TMenuItem;
    ActionList1: TActionList;
    BaseLineInsertPoint_Action: TAction;
    BaseLineMerge_Item: TMenuItem;
    BaseLineMerge_Action: TAction;
    BaseLineGroup_Item: TMenuItem;
    BaseLineUngroup_Item: TMenuItem;
    BaseLineGroup_Action: TAction;
    BaseLineUngroup_Action: TAction;
    ShowPos_Btn: TSpeedButton;
    Overlay_Btn: TSpeedButton;
    OverlayMenu_Item: TMenuItem;
    OverlayDeleteActive_Item: TMenuItem;
    N4: TMenuItem;
    N5: TMenuItem;
    MethodSaveTo_Item: TMenuItem;
    MethodLoadFrom_Item: TMenuItem;
    MethodOpenDialog: TOpenDialog;
    MethodSaveDialog: TSaveDialog;
    PeaksResponsesCalculate_Item: TMenuItem;
    PeaksAmountsCalculate_Item: TMenuItem;
    N6: TMenuItem;
    CalibrationFileSelect_Item: TMenuItem;
    CalibrationFileSelectDialog: TOpenDialog;
    DataMath_Item: TMenuItem;
    DataMathMultiplyX_Item: TMenuItem;
    DataMathMultiplyY_Item: TMenuItem;
    DataMatAddX_Item: TMenuItem;
    DataMathAddY_Item: TMenuItem;
    DataMathCancel_Item: TMenuItem;
    DataMathMakePermanent_Item: TMenuItem;
    N7: TMenuItem;
    DataTruncate_Item: TMenuItem;
    SuspendResume_Item: TMenuItem;
    procedure FormCreate(Sender: TObject);
    {v0.14 moved to acqInfo}{/v0.14
    procedure ComPortReceive(Sender: TObject; Count: Integer);}
    procedure Timer1Timer(Sender: TObject);
    procedure PaintBoxPaint(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure LogMenuItemClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure MarginsItemClick(Sender: TObject);
    procedure PopupMenuPopup(Sender: TObject);
    procedure RunStop_ItemClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure AxisItemClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormDeactivate(Sender: TObject);
    procedure PeaksClearMenuItemClick(Sender: TObject);
    procedure PaintBoxMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PaintBoxMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure PeaksMenuItemClick(Sender: TObject);
    procedure DefineSpeedButtonClick(Sender: TObject);
    procedure ReportPanelMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure ZoomOut_BtnClick(Sender: TObject);
    procedure StopButtonClick(Sender: TObject);
    procedure RunButtonClick(Sender: TObject);
    procedure BaseLineCheckBoxMouseUp(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure AxisCheckBoxMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PeaksCheckBoxMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure ReportCheckBoxMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FilterMenuItemClick(Sender: TObject);
    procedure HeaderItemClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure PeaksDeleteLastItemClick(Sender: TObject);
    procedure Autodetect1Click(Sender: TObject);
    procedure PeaksBrowseItemClick(Sender: TObject);
    procedure BaseLineCreate_ItemClick(Sender: TObject);
    procedure BaseLineBrowse_ItemClick(Sender: TObject);
    procedure PeaksDefineItemClick(Sender: TObject);
    procedure PrintItemClick(Sender: TObject);
    procedure ViewXYItemClick(Sender: TObject);
    procedure PeaksDeleteSelectedItemClick(Sender: TObject);
    procedure ViewHeaderItemClick(Sender: TObject);
    procedure View_ItemClick(Sender: TObject);
    procedure MethodEditItemClick(Sender: TObject);
    procedure MethodPeaksClearItemClick(Sender: TObject);
    procedure MethodPeaksBrowseItemClick(Sender: TObject);
    procedure PeaksToMethodItemClick(Sender: TObject);
    procedure BaseLineCopyToMethod_ItemClick(Sender: TObject);
    procedure MethodBaseLineClearItemClick(Sender: TObject);
    procedure MethodBaseLineBrowseItemClick(Sender: TObject);
    procedure BaseLineDeleteAll_ItemClick(Sender: TObject);
    procedure BaseLineDeleteSelected_ItemClick(Sender: TObject);
    procedure DefineSpeedButtonMouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure DefineSpeedButtonMouseUp(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure PrintPreviewItemClick(Sender: TObject);
    procedure PrintReportItemClick(Sender: TObject);
    procedure WindowClick(Sender: TObject);
    procedure CopyItemClick(Sender: TObject);
    procedure BaseLineInsertPoint_ActionExecute(Sender: TObject);
    procedure BaseLineMerge_ActionExecute(Sender: TObject);
    procedure BaseLineGroup_ActionExecute(Sender: TObject);
    procedure BaseLineUngroup_ActionExecute(Sender: TObject);
    procedure OverlayDeleteActive_ItemClick(Sender: TObject);
    procedure MethodSaveTo_ItemClick(Sender: TObject);
    procedure MethodLoadFrom_ItemClick(Sender: TObject);
    procedure PeaksResponsesCalculate_ItemClick(Sender: TObject);
    procedure PeaksAmountsCalculate_ItemClick(Sender: TObject);
    procedure CalibrationFileSelect_ItemClick(Sender: TObject);
    procedure DataMathMultiplyX_ItemClick(Sender: TObject);
    procedure DataMathMultiplyY_ItemClick(Sender: TObject);
    procedure DataMatAddX_ItemClick(Sender: TObject);
    procedure DataMathAddY_ItemClick(Sender: TObject);
    procedure DataMathCancel_ItemClick(Sender: TObject);
    procedure DataMathMakePermanent_ItemClick(Sender: TObject);
    procedure DataTruncate_ItemClick(Sender: TObject);
    procedure SuspendResume_ItemClick(Sender: TObject);
  private
    { Private declarations }
    IsInTimer:boolean;
    IsInReceive:boolean;
    IsInPaint:boolean;
    {v0.14}
    FAcqInfo: TAcqInfo;
    {/v0.14
      RunningState:TRunningState;
      CommPort:TComm;
      RcvBuf:array[0..RcvBufSize - 1] of char;
      SII: PScanInputInfo;
    }
    DrawLocked: integer;
    DrawWanted: boolean;
    ScrBuf: TScrBufs;{array[0..MaxDataCountInForm - 1] of TScrBuf;}
    PeakDefineMode: TPeakDefineMode;
    BaseLineDefineMode: TPeakDefineMode;
    BaseLineSizingTwo: boolean;
      { true if by sizing one baseline section is sized also its neighbour }
    MarginML: TMouseLine;
    PeakML: TMouseLine;
    BaseLineML: TMouseLine;
    {v0.09}
    ShowPosML: TMouseLine;
    {/v0.09}
    FDefineMode: TDefineMode;
    FDefineModePermanent: boolean;
      { If define mode initiated by clicking on toolbutton with shift pressed,
        then form remains in the define mode until explicitly canceled, otherwise
        define mode set dmNone after following mousedown/mouseup sequence }
    UserLimitStack: TLst;
    ChangingView: boolean;{true during call
      to SpectrumObjectShow..; prevents
      OnClick handlers to function during
      changing Checked properties }
    DataList: TList;
      { List of TAcqData, used if more chromatograms shown in one window }
    AngleText: TAngleText;
      { for drawing peak names }
    FDataPrinter: TDataPrinter;
    FSpeedButtonShift: boolean;
    FDragging: boolean; { set true if some action that uses mouse dragging
      is on (timer data acqusition is suspended during that time) }
    {v0.20}{/v0.20
    FDataChangeFromForm: boolean;}
    FPrintingReport: boolean;
    LastMousePos: TPoint;
      { updated during PaintboxMouseMove Event }
    {v0.08}
    DragStartCursor: integer;
    {/v0.08}

    {v0.09}
    FIsInSpeedButtonClick: boolean;
    FXAxisBandStartX: integer;
      {if <> -1 then moving chrom. view along X axis by dragging screen X position}
    FYAxisBandStartY: integer;
      {if <> -1 then moving chrom. view along Y axis by dragging screen Y position}

    FBaseLineIndex: integer;
      { <> - 1 if moving or sizing baseline section, this is the index of
        the section in BaseLine object }
    FBaseLineLeftEdge: boolean;
      { Info if moveing left edge (or right edge) }
    FOverlayMode: boolean;
    FPointColors: array[0..MaxDataListColorCount-1] of TColor;
    FPeakColors: array[0..MaxDataListColorCount-1] of TColor;
    {/v0.09}
    {v0.20}
    FCreating: boolean;
      { true during FormCreate method }
    {/v0.20}

    function GetFileName: string;
    procedure ForwardMessage(var Msg: TMessage);
  protected
    procedure SetDefineMode(ADefineMode: TDefineMode);
      procedure MarginsDefineStart;
      procedure MarginsDefineStop;
      procedure BaseLineDefineStart;
      procedure BaseLineDefineStop;
      procedure PeaksDefineStart;
      procedure PeaksDefineStop;
      {v0.09}
      procedure ShowPosStart;
      procedure ShowPosStop;
      procedure OverlayStart;
      procedure OverlayStop;
      {/v0.09}

    procedure UpdatePrinterDisp(APrinter: TAlPrinter; ADefault: boolean;
      AReportType: TReportType);
    procedure UpdateViewLimitRecFromDataFile(force: boolean);
      { Called from SetDataFile, updates UserViewLimits record by values
        from Data.ULVL record (if (FixedView or force) and not Mode = omCreate) }
    {v0.09}
    procedure UpdateViewLimitsFromDataFiles;
      { find maximal limits  - all datafiles will fit to screen
        (used in overlay mode) }
    {/0.09}

    {v0.13}{/v0.13
    procedure UpdateViewLimitRecToDataFile(force:boolean);}
      { Copy current UserViewLimit values to Data.ULVL (i.e. store them
        to file if changes will be saved). Values get really copied to
        Data.ULVL only if force = true, or Data.ULVL.FixedView = true.
        Called after window size changes.}
    procedure UpdateViewOptionsFromDataFile;
    procedure CalcSpectrumDisp(ACanvas: TCanvas; AWidth: integer;
      AHeight: integer; var ADisp: TScreenDisp);
      { calculate borders for painting the spectrum inside the area
        of width AWidth, height AHeight on the ACanvas (origin 0,0) }
    {v0.08}
    procedure UpdateScreenCursor;
      { called after window activation to set correct cursor according to current
        DefineMode }
    {/v0.08}
    {v0.09}
    function XAxisBandPointIsIn(X,Y:integer):boolean;
    function XAxisBandMoveStart(X:integer):boolean;
    procedure XAxisBandMoveStop(X:integer);

    function YAxisBandPointIsIn(X,Y:integer):boolean;
    function YAxisBandMoveStart(Y:integer):boolean;
    procedure YAxisBandMoveStop(Y:integer);
    {procedure RedrawAll;}
    procedure OverlayFileNameClick(Sender: TObject);
    function GetFMH: TFileMenuHandler;
    {/v0.09}
  public
    { Public declarations }
    {v0.14}{/v0.14
    DeviceMode: TDeviceMode;
    PortName: shortstring;
    }
    {v0.14}{/v0.14 UVDetAddr: longint;}
      { ulan address of UV detector module (used if DeviceMode = dmUlan) }
    Data: TAcqData;
      { Currently selected data. In overlay mode more TAcqData object can
        be opened and stored in DataList. Data points to the default one. }
    DataIndex: integer;
      { Index of Data in DataList used during Paint method (scanning data list)}
    CurDataIndex: integer;
      { Curently user selected dataindex (to be worked with) }

    {v0.14 moved to AcqInfo}{/v0.14
    PacketFifo:TLst;
    ApexPktInfo:TApexPacketInfo;
    ExtDevDrv: PExtDevDrv;
    ZeroValue:single;  //currently set zero value (in absolute units)
                       //of the y value, used to  calculate apexdatarec.y value from
                      // apexpacket values
    ZeroTime:Longint;
    CurUlanTime: single; //gets incremented for every received experimental
                         // point by ulanglob . UlanPointTimeInterval (= 40 ms)
    LogPackets: boolean;
    }
    OrigUserViewLimit, UserViewLimit: TUserViewLimit;

    ViewLimit: TExpViewLimit;
    Limit: TExpViewLimit;
    ScreenDisp: TScreenDisp;
    PrinterDisp: TScreenDisp;
    OutDisp: PScreenDisp;
      { points to one of the above (or some other one during printing to metafile) }

    AxisPlotting: TPlotting;

    Spect:TSpectrum;
      { Spectrum used for peaks evaluation. }

    PeakStartPoint,PeakStopPoint:TPoint;
      { Used by StartPeak, FinishPeak and CreatePeak methods - manual,
        interactive with mouse defining of peaks }
    BaseLineStartPoint, BaseLineStopPoint:TPoint;

    {v0.19}
    {/v0.19
    procedure DoApexPacket(var ai:TApexPacketInfo);
    procedure DoExtDevTimer;
    }

    procedure UserViewRecToExpViewRec(var AUserViewLimit:TUserViewLimit;
      var AViewLimit:TExpViewLimit);

    {procedure ApexViewRecToUserViewRec(const AViewLimit:TExpViewLimit;
      var AUserViewLimit:TUserViewLimit); {apextype}
    function ApexRecToScreenRec(const AApexRec:TExpPoint;
      const ADisp: TScreenDisp; var AScreenRec:TScreenPoint):boolean;
    function ScreenPointToIndexPoint(const AScreenPoint:TPoint;
      var AIndexPoint:TIndexPoint):boolean;
      { ..get the datapoint (TIndexPoint = record Index:longint;
        Point:TExpPoint end;) that is closest to AScreenPoint }
    procedure SetPacketLogging(onOff:boolean);

    {Draw methods}
    {v0.16}
    procedure DrawTemplate(ACanvas: TCanvas; const ADisp: TScreenDisp);
    {/v0.16}

    procedure DrawPoints(ACanvas:TCanvas; const ADisp: TScreenDisp);
      { Draw just the points not drawn yet. }
      procedure DrawScreenPoints(ACanvas:TCanvas{; const ADisp: TScreenDisp});
        { Draw the points from buffer. }
    procedure DrawAxis(ACanvas: TCanvas; const ADisp: TScreenDisp);
    procedure DrawBaseLine(ACanvas: TCanvas; const ADisp: TScreenDisp);
    procedure DrawHeader(ACanvas: TCanvas; const ADisp: TScreenDisp);
    procedure DrawPeaks(ACanvas: TCanvas; const ADisp: TScreenDisp);
      { Called from PaintBoxPaint to draw the peaks. }
    procedure DrawSpectrum(ACanvas: TCanvas; const ADisp: TScreenDisp);
      { Calls all of the above draw methods, taking in account view options
        (what to draw) }

    procedure PlottingToCanvas(var APlot:TPlotting; ACanvas:TCanvas);
    procedure LockDraw;
    procedure UnlockDraw;
    procedure Redraw;
      { All points. }
    procedure SetToRedraw(tr:TToRedraw);
      { Define what should be surely redrawn during next paint event
       (after call to Redraw), i.e. ignore all buffers etc.}
    {v0.09}
    procedure RedrawAll;
    {/v0.09}
    {/Draw methods}

    procedure ReadFromIniFile;
    procedure WriteToIniFile;
    {v0.14}
    procedure SetData(AData: TAcqData; AAcqInfo: TAcqInfo);
      { set new data file to which the new acquisition should be done }
    {/v0.14}
    procedure SetDataFile(const AFileName:string; AMode: TOpenMode);
      { Set new data to be worked on. }
    procedure AddDataFile(const AFileName:string);
      { Add new data file (in FOverlayMode more chromatograms can be shown in
        one window }
    {v0.09}
    procedure AddData(AData: TAcqData);
      { As AddDataFile, but using already created TAcqData object
        (that can be created e.g. by TAcqData.CreateFromPoints constructor }
    {/v0.09}
    procedure DataSetCurrent(Index: integer);
      { In overlay mode - set data with given index in DataList as Data }

    procedure DataListAdd(AData: TAcqData);
      { add data to list, register SpecForm in ULA obj }
    procedure DataListClear(Index:integer);
      { unregister SpecForm in ULA obj, dispose data at index Index (only if
        count > 1; if Index = -1 do it  for all data  (can be done only
        in FormDestroy or if setting new data) }
    procedure UpdateCaption;
    procedure ClearData;

    {procedure UndrawBaseLine;}
    procedure UpdateScreenDisp;
      { Called from FormCreate, FormResize and after adding datafile.
        Makes all changes that should be done if size of the window changed. }
    procedure UpdateViewLimit;
    {v0.09}{/v0.09
    function DoSaveTo:boolean; }

      { Calls SaveDialog.Execute - SaveTo, returns false if was not saved }
    procedure SaveTo(AFileName:string);
      { Saves current chromatogram to AFileName }

    {Messages response methods}
    procedure WMAppMessage(var Msg:TMessage);message WM_APPMESSAGE;{in globals}
      procedure ULObjBeforeEdit(var Msg: TMessage);
      procedure ULObjAfterEdit(var Msg: TMessage);
      {procedure ULObjUpdated(var Msg: TMessage);}
      {v0.20}
      procedure ULObjUpdated(var Msg: TMessage);
      function IsDataListChild(o:TULObj):boolean;
      {/v0.20}
      procedure ULObjDestroyed(var Msg: TMessage);
    procedure WMCopy(var Msg: TMessage);message WM_COPY;
    procedure WMCut(var Msg: TMessage);message WM_CUT;
    procedure WMPaste(var Msg: TMessage);message WM_PASTE;

    {/Messages response methods}

    procedure PeaksShow;
    procedure PeaksHide;
    {procedure PeaksDefineAbort;}
      {procedure ClosePeaksDefineForm;}
    {0.06}
    function BaseLineInsertPointStart(X:integer):boolean;
    procedure BaseLineMerge;
      { merges selected neighbour baseline sections to one }
    procedure BaseLineGroup;
      { makes neighbouring selected baseline sections grouped - they have
        common margins (movable), gets created usually by InsertPoint command }
    procedure BaseLineUngroup;
      { makes grouped baseline sections independent }
    {/0.06}
    function BaseLineCreateStart(X,Y:integer):boolean;
      { Called upon mouse down (if DefiningBaseLine). Returns false,
        if baseline interval can not be created in at given pos }
    function BaseLineMoveStart(X: integer): boolean;
    function BaseLineSizeStart(X: integer; Index:integer; LeftEdge:boolean): boolean;
    function IsInBaseLine(X:integer; var Index:integer): boolean;
      { Is given X screen position inside any baseline interval? }
    function IsInBaseLineEdge(var X: integer; Y:integer; var Index: integer; var LeftEdge: boolean): boolean;
      { returns true if X is inside the circle drawn around the intersection of
        baseline section with the curve, adjusts X to the intersection X }
    procedure BaseLineFinish(X,Y:integer; Shift: TShiftState);
      { called upon mouse up, or upon mouse click if not dragging, finish started
        baseline define action }
    procedure BaseLineAbort;
      { abort current baseline define mode action }
    {function PeakStarted:boolean;{true during mouse down ..}
      procedure BaseLineCreate(const AStartPoint, AStopPoint:TPoint);
      procedure BaseLineMove(OldStartX, NewStartX:integer);
      procedure BaseLineSize(OldStopX, NewStopX:integer; Right: boolean; SizingTwo:boolean);
      procedure BaseLineInsertPoint(X: integer);
      procedure BaseLineToggleSelect(X, Y: integer; Shift: TShiftState);

    function StartCreatePeak(X,Y:integer):boolean;
      { Called upon mouse down (if DefiningPeaks). Returns false,
        if peak can not be created in at given pos }
    function StartMovePeak(X: integer): boolean;
    function StartSizePeak(X: integer): boolean;

    function IsPeakStart(X: integer): boolean;
    function IsPeakStop(X: integer): boolean;
    function IsInPeak(X:integer; var PeakIndex:integer): boolean;
      { Is given X screen position inside any peak? }
    function IsInPeakEdge(X,Y: integer; var PeakIndex:integer):boolean;
      { Is given screen coordinates inside edge sign of any peak? }

    procedure FinishPeak(X, Y: integer; Shift: TShiftState);{called upon mouse up ..}
    {function PeakStarted:boolean;{true during mouse down ..}
      procedure CreatePeak(const AStartPoint, AStopPoint:TPoint);
      procedure MovePeak(OldStartX, NewStartX:integer);
      procedure SizePeak(OldStopX, NewStopX:integer; Right: boolean);
      procedure ToggleSelectPeak(X: integer; Shift: TShiftState);
      {..called from FinishPeak}
    procedure PeakEditProperties(PeakIndex: integer);
      { Called if clicked with right mouse button in DefiningPeaks form mode
        inside the peak with PeakIndex. }
    procedure BaseLineEditProperties(PeakIndex: integer);


    procedure DoPeaksSizeChanged;
      { Should be called after every change of peak/baseline properties
        that efect properties of all other peaks }
    {procedure CountBaseLinePos;
    procedure SetBaseLinePos(APos:integer);}
    procedure CreatePeakReport;
      { Called from DoPeaksSizeChanged }
    procedure ShowReport;
    procedure HideReport;
    {procedure UpdateMarginRec(X,Y:integer);}
    {procedure DrawMarginRec(first:boolean);}
    procedure SetScreenSubView(X1,Y1,X2,Y2:integer);
    procedure PopScreenSubView;
    function SpectrumObjectVisible(so:TSpectrumObject):boolean;
    procedure SpectrumObjectShow(so:TSpectrumObject; OnOff:boolean);

    {Acquisition related}
    {v0.14}{/v0.14
    procedure DoInChar(CommChar:char);
      procedure DoApexChar(CommChar:char);
      procedure DoExtDevInChar(CommChar:char);
    }
    {v0.19}
    procedure DoAfterRun;
    procedure DoAfterStop;
      { called when acquistion was stopped, calls also DoAfterAutodetect }
    procedure DoAfterAutodetect;
    procedure DoPointsAdded;
    procedure DoAfterSuspend;
    procedure DoAfterResume;
    {/v0.19
    procedure Run;
    procedure Stop;}
    procedure RunPanelShow;
    {v0.14}{/v0.14
    procedure PortSetDefaults;}
    function PortOpen: boolean;
    procedure PortClose;
    procedure ResetPacketInfo;
    {/Acquisition related}

    {Printing methods}
    procedure Print(WithPreview:boolean; AReportType: TReportType);
    procedure PrintWindow(prn: TAlPrinter);
    procedure PrintReport(prn: TAlPrinter);
    function Printing: boolean;
    {/Printing methods}

    function PeakStickLen(ADispWidth: integer): integer;
    function PeakArrowLen(ADispWidth: integer): integer;
    function BaseLineCircleRadius(ADispWidth: integer): integer;
    procedure CopySpectrumToClipboard;

    property FileName:String read GetFileName;
    property DefineMode: TDefineMode read FDefineMode write SetDefineMode;
    property DefineModePermanent: boolean read FDefineModePermanent write FDefineModePermanent;
    property PrintingReport: boolean read FPrintingReport;
    {v0.09}
    property OverlayMode: boolean read FOverlayMode;
    property FMH: TFileMenuHandler read GetFMH;
    {/v0.09}
  end;

var
  SpectrumForm: TSpectrumForm;

procedure CreateSpectrumForm(const Name: string; AMode: TOpenMode);

implementation

{$R *.DFM}
uses
  Main, MargDial
{$IFDEF DEBUG}
  ,DebugFrm
{$ENDIF}{peform wm_syscommand}
;
const
  SpectrumFormCount:integer = 0;
    {current number of opened spectrum windows}
  {v0.18 to ulanglob}{/v0.18
  MaxSpectrumFormCount:integer = 4;}
    {max allowed number of spectrum windows}

function CountYOnScreenLine(X: TScreenX; X1: TScreenX; Y1: TScreenY;
  X2: TScreenX; Y2: TScreenY): TScreenY;
begin
  CountYOnScreenLine := Y1 + (Y2 - Y1)* (X - X1) div (X2 - X1) ;
end;

{v0.14}
procedure CreateSpectrumForm(const Name: string; AMode: TOpenMode);
var
  sf: TSpectrumForm;
  fn: string;
  fcreated: boolean;
  ovrmode: boolean;

  af: TForm; { ActiveMDIChild if is TSpecForm }
  ad: TAcqData;
  ai: TAcqInfo;
  r: TModalResult;
  frm: TAnalSetupForm;
begin
  if SpectrumFormCount = MaxSpectrumFormCount then
    exit;

  sf := nil;
  af := Application.MainForm.ActiveMDIChild;
  if af is TSpectrumForm then begin
    ovrmode := TSpectrumForm(af).OverlayMode
  end else begin
    ovrmode := OverlayMode;
    af := nil;
  end;

  if AMode = omCreate then begin
    { will be acquiring data }
    if (Name = '') then
      raise ESpecForm.Create('CreateSpecForm: Empty template file name');
    ad := nil;
    ai := nil;

    fn := ReplaceExt(Name, ULTExt, false);
    {v0.18}
    if ExtractFileName(fn) = pvFromFileTemplate then begin
      if MainForm.ParentFileOpenDialog.Execute then begin
        fn := MainForm.ParentFileOpenDialog.FileName;
      end else begin
        raise EUserAborted.Create('User Aborted');
      end;
    end;
    {/v0.18}
    ad := TAcqData.Create(fn, AMode);

    try
      r := mrCancel;
      CurULObj := ad.ULA;
      frm := TAnalSetupForm.Create(Application.MainForm);
      try
        r := frm.ShowModal;
        frm.Free;
      except
        frm.Free; frm := nil; ad.Free; ad := nil;
        raise;
      end;
      if r <> mrOK then begin
        ad.Free;
        exit;{raise EUserAborted.Create('Aborted by user');}
      end;

      ai := TAcqInfo.Create(ad);
      try
        sf := TSpectrumForm.Create(Application);
      except
        ad.Free; ai.Free; ad := nil; ai := nil;
        raise;
      end;
      try
        sf.SetData(ad, ai);
      except
        ad := nil; ai := nil; { released during sf.Release }
        raise;
      end;
    except
      ad.Free;
      ai.Free;
      raise;
    end;
    {v0.19}
    ai.DoRunStop;
    {v0.19}
  end else begin
    { working with already acquired data }
    if (Name = '') then
      raise ESpecForm.Create('Empty file name');
    fn := ReplaceExt(Name, ULFExt, false);
    if ovrmode then begin
      sf := TSpectrumForm(af);
      if sf = nil then begin
        sf := TSpectrumForm.Create(Application);
        fcreated := true;
      end else begin
        fcreated := false;
      end;
      if fcreated then
        sf.SetDataFile(fn, AMode)
      else
        sf.AddDataFile(fn);
    end else begin
      sf := TSpectrumForm.Create(Application);
      sf.SetDataFile(fn, AMode);
    end;

  end;
end;

(*/v0.14
procedure CreateSpectrumForm(const Name: string; AMode: TOpenMode);
var
  sf:TSpectrumForm;
  fn:string;
{  i:integer;}
  fcreated: boolean;
  ovrmode:boolean;
  af:TForm;
begin
  if SpectrumFormCount = MaxSpectrumFormCount then
    exit;
  if (Name = '') then begin
    if (omCreate = AMode) then begin
      {v0.13}
      fn := DefaultAnalysisName;

      {/v0.13
      i := StrToInt(copy(CurPortName, 4, 2));
      fn := 'NONAME' + IntToStr(i) + ULFExt;}
    end else begin
      raise ESpecForm.Create('Empty file name');
      exit;
    end;
  end else begin
    {v0.13}
    if AMode = omCreate then begin
      fn := ReplaceExt(Name, ULTExt, false);
    end else begin
      fn := ReplaceExt(Name, ULFExt, false);
    end;
    {/v0.13
    fn := ReplaceExt(Name, ULFExt, false);
    if AMode = omCreate then
      AMode := omRead;}
  end;
  {ovrmode := false;}
  af := Application.MainForm.ActiveMDIChild;
  if (af <> nil) and (af is TSpectrumForm) then
    ovrmode := TSpectrumForm(af).OverlayMode
  else
    ovrmode := OverlayMode;
  if ovrmode and (AMode <> omCreate) then begin
    sf := nil;
    if (MainForm.ActiveMDIChild <> nil) and
       (MainForm.ActiveMDIChild is TSpectrumForm) then
    begin
      sf := TSpectrumForm(MainForm.ActiveMDIChild);
    end;
    if sf = nil then begin
      sf := TSpectrumForm.Create(Application);
      fcreated := true;
    end else begin
      fcreated := false;
    end;
    if fcreated then
      sf.SetDataFile(fn, AMode)
    else
      sf.AddDataFile(fn);
  end else begin
    sf := TSpectrumForm.Create(Application);
    sf.SetDataFile(fn, AMode);
  end;
end;
*)

procedure TSpectrumForm.FormCreate(Sender: TObject);
{v0.14}{/v0.14
var
  li: TListInfo;}
{  i: integer;}
begin
  FCreating := true;
  try
  {set def values}
  DragStartCursor := crDefault;
  AngleText := TAngleText.Create(90);
  DataList := TList.Create;

  {v0.14}{/v0.14
  DeviceMode := CurDeviceMode;
  PortName := CurPortName;
  UVDetAddr := DefUVDetAddr;}

  ChangingView := false;
  UserLimitStack := nil;
  FDefineMode := dmNone;

  MarginML := TMouseLine.Create(PaintBox.Canvas);
  MarginML.Mode := mmRectangle;
  PeakML := TMouseLine.Create(PaintBox.Canvas);
  PeakML.Mode := mmVerticalColumn;
  BaseLineML := TMouseLine.Create(PaintBox.Canvas);
  BaseLineML.Mode := mmVerticalColumn;

  {v0.09}
  ShowPosML := TMouseLine.Create(PaintBox.Canvas);
  ShowPosML.Mode := mmCross;
  {/v0.09}

  {PeakML.ObjType := omVerticalColumn;
  PeakML.ObjPos :=}
  {MarginStartPoint.X := -1;}
  {ReportPanelDragStart.X := -1;}

  PeakDefineMode := pdNone;
  BaseLineDefineMode := pdNone;
  Data := nil;
  Spect := nil;
  OutDisp := @ScreenDisp;
  ScrBuf := TScrBufs.Create(PaintBox.Canvas, ScreenDisp);
  DrawLocked := 0;
  DrawWanted := false;
  PeakStartPoint.X := -1;{indicates that no peak was manually started}
  BaseLineStartPoint.X := -1;
  IsInTimer := false;
  IsInReceive := false;
  IsInPaint := false;
  {v0.14}
  FAcqInfo := nil;
  {/v0.14
  ZeroTime := mstime;
  ZeroValue := 0;
  RunningState := rsStopped;
  CommPort := nil;
  LogPackets := true;
  }
  AxisPlotting := nil;
  {v0.05}
  UserViewLimit := CurUserViewLimit;{ulanglob}
  {/v0.05
  UserViewLimit.Min.X := 0;
  UserViewLimit.Max.X := 20;
  UserViewLimit.Min.Y := -0.1;
  UserViewLimit.Max.Y := 0.1;  }

  OrigUserViewLimit := UserViewLimit;
  ViewLimit.Min.X := 0;
  ViewLimit.Max.X := 1200;
  ViewLimit.Min.Y := {v0.13}DefMinAcqYValue;{/v0.13 -2;}
  ViewLimit.Max.Y := {v0.13}DefMaxAcqYValue;{/v0.13 2;}
  Limit.Min.X := 0;
  Limit.Max.X := 1200;
  Limit.Min.Y := {v0.13}MinAcqYValue;{/v0.13 -2;}
  Limit.Max.Y := {v0.13}MaxAcqYValue;{/v0.13 2;}

  {/set def values}
  BaseLineCreate_Btn.Tag := longint(dmBaseLine);
  DefinePeaksSpeedButton.Tag := longint(dmPeaks);
  ZoomIn_Btn.Tag := longint(dmMargins);
  ShowPos_Btn.Tag := longint(dmShowPos);
  {v0.09}
  Overlay_Btn.Tag := longint(dmOverlay);
  {/v0.09}

  {v0.14}
  {/v0.14
  CommPort := TComm.Create(Self);
  PortSetDefaults;

  MainForm.ComPort := CommPort;
  }
   { just for debugging }


  {AxisItem.Checked := true;}
{  PostMessage(Handle, WM_APPMESSAGE, cmSpectrumObjectShow + soAxis,
    longint(true));
  PostMessage(Handle, WM_APPMESSAGE, cmSpectrumObjectShow + soBaseLine,
    longint(true));
}
  ReadFromIniFile;
  {v0.14 moved to acqinfo}{/v0.14
  DeviceModeCheck(DeviceMode);
  FillChar(li, sizeof(li), 0);
  case DeviceMode of
    dmUlan, dmApex: begin
      li.Capacity := 100;
      if not ListInit(ltRecords or ltAutoDestroy, @li, PacketFifo) then
        raise Exception.Create('SpecForm ListInit PacketFifo failed.');
    end;
    dmExtDev: begin
      if (not ExtDevPresent('')) or  (ExtDevInit(ExtDevDrv, '') <> 0) then begin
        raise Exception.Create('SpecForm ExtDevInit failed.');
      end;
    end;
  else
    raise Exception.Create('Invalid DeviceMode');
  end;
  }

  UpdateViewLimit;
  ResetPacketInfo;
  inc(SpectrumFormCount);
  UpdateScreenDisp;
  SetPacketLogging(false);
  LogMenuItem.Visible := DebugToolsAllowed;
  {v0.19}
  DoAfterStop;
  {/v0.19
  Stop;}
  {v0.09}
  FXAxisBandStartX := -1;
  FYAxisBandStartY := -1;
  FBaseLineIndex := -1;

  FPointColors[0] := clBlue;
  FPointColors[1] := clFuchsia; {tcolor}
  FPointColors[2] := clLime;
  FPointColors[3] := clOlive;
  FPointColors[4] := clYellow;

  FPeakColors[0] := clBlack;
  FPeakColors[1] := clFuchsia; {tcolor}
  FPeakColors[2] := clLime;
  FPeakColors[3] := clOlive;
  FPeakColors[4] := clYellow;
  {/v0.09}
  finally
    FCreating := false;
  end;
end;

{v0.09}
function TSpectrumForm.XAxisBandPointIsIn(X,Y:integer):boolean;
begin
  Result := (Y > ScreenDisp.Bottom) and (X >= ScreenDisp.Left) and (X <= ScreenDisp.Right);
end;

function TSpectrumForm.XAxisBandMoveStart(X:integer):boolean;
begin
  Result := true;
  FXAxisBandStartX := X;
  Screen.Cursor := crSizeWE;
end;

procedure TSpectrumForm.XAxisBandMoveStop(X:integer);
var dif:integer;
begin
  if FXAxisBandStartX <> -1 then begin
    dif := FXAxisBandStartX - X;
    SetScreenSubView(ScreenDisp.Left + dif, ScreenDisp.Top,
      ScreenDisp.Right + dif, ScreenDisp.Bottom);
  end;
  FXAxisBandStartX := -1;
  Screen.Cursor := crDefault;
end;

function TSpectrumForm.YAxisBandPointIsIn(X,Y:integer):boolean;
begin
  Result := (X < ScreenDisp.Left) and (Y >= ScreenDisp.Top) and (Y <= ScreenDisp.Bottom);
end;

function TSpectrumForm.YAxisBandMoveStart(Y:integer):boolean;
begin
  Result := true;
  FYAxisBandStartY := Y;
  Screen.Cursor := crSizeNS;
end;

procedure TSpectrumForm.YAxisBandMoveStop(Y:integer);
var dif:integer;
begin
  if FYAxisBandStartY <> -1 then begin
    dif := FYAxisBandStartY - Y;
    SetScreenSubView(ScreenDisp.Left, ScreenDisp.Top + dif,
      ScreenDisp.Right, ScreenDisp.Bottom + dif);
  end;
  FYAxisBandStartY := -1;
  Screen.Cursor := crDefault;
end;
{/v0.09}

procedure TSpectrumForm.UpdateViewLimit;
var
  ind, cnt: integer;
begin
  UserViewRecToExpViewRec(UserViewLimit, ViewLimit);
  if Data <> nil then begin
    ind := 0;
    cnt := DataList.Count;
    repeat
      if cnt > 1 then begin
        DataSetCurrent(ind);
      end;
      Data.SetViewLimit(ViewLimit);
      {Data.SetUserLimit(UserViewLimit);}
      Data.SetLimit(Limit);
      {CountBaseLinePos;}
      inc(ind);
    until (ind >= cnt);
    SetToRedraw(trAll);
    Redraw;
    {v0.09}
    DataSetCurrent(CurDataIndex);
    {/v0.09}
  end;
end;

procedure TSpectrumForm.UpdateScreenDisp;
var tw,th:integer;
begin
  tw := Paintbox.Canvas.TextWidth('M');
  th := PaintBox.Canvas.TextHeight('M');
  ScreenDisp.Left := 7 * tw;
  ScreenDisp.Top := 2 * th;
  {
  ScreenDisp.Height := PaintBox.Height - 4 * th;
  ScreenDisp.Width := PaintBox.Width - 8 * tw;}
  ScreenDisp.Right := ScreenDisp.Left + PaintBox.Width - 8 * tw - RectInclusive;
  ScreenDisp.Bottom := ScreenDisp.Top + PaintBox.Height - 4 * th - RectInclusive;
  PeakML.SetLimits(ScreenDisp.Left, ScreenDisp.Top, ScreenDisp.Right{Width}, ScreenDisp.Bottom{Height});
  MarginML.SetLimits(ScreenDisp.Left, ScreenDisp.Top, ScreenDisp.Right{Width}, ScreenDisp.Bottom{Height});
  {v0.09}
  ShowPosML.SetLimits(ScreenDisp.Left, ScreenDisp.Top, ScreenDisp.Right{Width}, ScreenDisp.Bottom{Height});
  {/v0.09}
  if Data <> nil then begin
    {v0.13}
    {/v0.13}
    {CountBaseLinePos;}
    SetToRedraw(trAll);
    Redraw;

  end;
end;

procedure TSpectrumForm.ReadFromIniFile;
begin
{  ConfigReadWriteValue(rwRead, SpecSection, 'DevMode', @DeviceMode, ptByte);
  ConfigReadWriteValue(rwRead, SpecSection, 'UVDetAddr', @UVDetAddr, ptLongint);}
end;

procedure TSpectrumForm.WriteToIniFile;
begin
{  ConfigReadWriteValue(rwWrite, SpecSection, 'DevMode', @DeviceMode, ptByte);
  ConfigReadWriteValue(rwWrite, SpecSection, 'UVDetAddr', @UVDetAddr, ptLongint);}
end;

procedure TSpectrumForm.UpdateViewOptionsFromDataFile;
begin
  SpectrumObjectShow(soAxis, Data.ULVO.ShowAxis);
  SpectrumObjectShow(soBaseline, Data.ULVO.ShowBaseLine);
  SpectrumObjectShow(soData, Data.ULVO.ShowData);
  SpectrumObjectShow(soHeader, Data.ULVO.ShowHeader);
  SpectrumObjectShow(soPeaks, Data.ULVO.ShowPeaks);
  SpectrumObjectShow(soReport, Data.ULVO.ShowReport);
  SpectrumObjectShow(soXY, Data.ULVO.ShowXY);
end;

{v0.09}
procedure TSpectrumForm.UpdateViewLimitsFromDataFiles;
var
  i:integer;
  dx{, x}: TXValue;
  dy{, y}: TYValue;
  mma, mmi, ma, mi: TUserPoint;
begin
  if DataList.Count = 0 then
    exit;
  for i := 0 to DataList.Count - 1 do begin
    DataSetCurrent(i);
    Data.ExpToUser(Data.SpectrumOpt.Extreme.Max, ma);
    Data.ExpToUser(Data.SpectrumOpt.Extreme.Min, mi);
    if i = 0 then begin
      mmi := mi;
      mma := ma;
    end else begin
      if mi.x < mmi.x then
        mmi.x := mi.x;
      if mi.Y < mmi.y then
        mmi.y := mi.x;
      if ma.x > mma.x then
        mma.x := ma.x;
      if ma.y > mma.y then
        mma.y := ma.y;
    end;
    dy := mma.Y - mmi.Y;
    dx := mma.X - mmi.X;
    mma.Y := mmi.Y + (dy / 100) * 120;
    mmi.Y := mmi.Y - (dy / 100) * 10;
    mma.X := mma.X + (dx / 100) * 5;
    with UserViewLimit do begin
      Min := mmi;
      Max := mma;
    end;
  end;
  DataSetCurrent(CurDataIndex);
end;
{/v0.09}

procedure TSpectrumForm.UpdateViewLimitRecFromDataFile(force: boolean);
var
  dy: TYValue;
  dx: TXValue;
begin
  if Data.Mode <> omCreate then begin
    if Data.ULVL.FixedView or force then begin
      with UserViewLimit do begin
        Min.X := Data.ULVL.MinX;
        Min.Y := Data.ULVL.MinY;
        Max.X := Data.ULVL.MaxX;
        Max.Y := Data.ULVL.MaxY;
      end;
    end else begin
      with UserViewLimit do begin
        {v0.15}
        with Data.SpectrumOpt.Extreme do begin
          if (abs(Max.X) > (MaxSingle / 100)) or (Max.X <= 0) then begin
            Max.X := DefMaxUsrXValue * 60;
            Min.X := MinAcqXValue;
          end;
          if abs(Max.Y) > (MaxSingle / 100) then begin
            Max.Y := DefMaxAcqYValue;
            Min.Y := DefMinUsrYValue;
          end;
        end;
        {/v0.15}
        Data.ExpToUser(Data.SpectrumOpt.Extreme.Max, Max);
        Data.ExpToUser(Data.SpectrumOpt.Extreme.Min, Min);
        dy := Max.Y - Min.Y;
        dx := Max.X - Min.X;
        Max.Y := Min.Y + (dy / 100) * 120;
        Min.Y := Min.Y - (dy / 100) * 10;
        Max.X := Max.X + (dx / 100) * 5;
      end;
    end;
  end else begin
    if force then begin
      with UserViewLimit do begin
        Min.X := Data.ULVL.MinX;
        Min.Y := Data.ULVL.MinY;
        Max.X := Data.ULVL.MaxX;
        Max.Y := Data.ULVL.MaxY;
      end;
    end else begin
      UserViewLimit := NewUserViewLimit;
    end;
  end;
  {v0.15}
  if abs(UserViewLimit.Max.X) > (MaxSingle / 100) then begin
    UserViewLimit.Max.X := DefMaxUsrXValue;
    UserViewLimit.Min.X := MinAcqXValue;
  end;
  if abs(UserViewLimit.Max.Y) > (MaxSingle / 100) then begin
    UserViewLimit.Max.X := DefMaxAcqYValue;
    UserViewLimit.Min.X := DefMinUsrYValue;
  end;
  {/v0.15}
end;

{v0.13}{/v0.13
procedure TSpectrumForm.UpdateViewLimitRecToDataFile(force:boolean);
begin
  if Data.ULVL.FixedView or force then begin
    with UserViewLimit do begin
      Data.ULVL.MinX := Min.X;
      Data.ULVL.MinY := Min.Y;
      Data.ULVL.MaxX := Max.X;
      Data.ULVL.MaxY := Max.Y;
    end;
  end;
end;
}

{v0.14}
procedure TSpectrumForm.SetData(AData: TAcqData; AAcqInfo: TAcqInfo);
      { set new data file to which the new acquisition should be done }
begin
  FAcqInfo := AAcqInfo;
  if FAcqInfo <> nil then
    FAcqInfo.SpecForm := TForm(Self);
  try
    DataListClear(-1);
    Data := AData;
    DataListAdd(Data);
    DataIndex := DataList.IndexOf(Data);

    Spect.Free;
    Spect := TSpectrum.Create(Data);

    UpdateViewLimitRecFromDataFile(false);
    UpdateViewOptionsFromDataFile;

    LockDraw;
    UpdateScreenDisp;
    UpdateViewLimit;
    UpdateCaption;
    {v0.20}
    SetToRedraw(trAll);
    {/v0.20}
    UnlockDraw;
    Data.ULF.Modified := false;
  except
    Timer1.OnTimer := nil;
    Release;
    raise;
  end;
end;
{/v0.14}

procedure TSpectrumForm.SetDataFile(const AFileName:string; AMode: TOpenMode);
var d: TAcqData;
begin
  try
    d := TAcqData.Create(AFileName, AMode);
    {v0.14}
    SetData(d, nil);
    {/v0.14
    DataListClear(-1);
    Data := d;
    DataListAdd(Data);
    DataIndex := DataList.IndexOf(Data);

    Spect.Free;
    Spect := TSpectrum.Create(Data);

    UpdateViewLimitRecFromDataFile(false);
    UpdateViewOptionsFromDataFile;

    LockDraw;
    UpdateScreenDisp;
    UpdateViewLimit;
    UpdateCaption;
    UnlockDraw;
    Data.ULF.Modified := false;
    }
  except
    on EUserAborted do
    begin
      Timer1.OnTimer := nil;
      Release;
    end
  else
    Timer1.OnTimer := nil;
    Release;
    raise;
  end;
end;

procedure TSpectrumForm.AddDataFile(const AFileName:string);
var d: TAcqData;
begin
  try
    d := TAcqData.Create(AFileName, omReadOnly);
    {v0.09}
    AddData(d);
    {/0.09
    Data := AData;
    DataListAdd(Data);
    DataIndex := DataList.IndexOf(Data);
    Spect.SetData(Data);

    UpdateViewLimitsFromDataFiles;

    LockDraw;
    UpdateScreenDisp;
    UpdateViewLimit;
    UpdateCaption;
    UnlockDraw;
    }
  except
  end;
end;


{v0.09}
procedure TSpectrumForm.AddData(AData: TAcqData);
begin
  try
    Data := AData;
    DataListAdd(Data);
    DataIndex := DataList.IndexOf(Data);
    Spect.SetData(Data);
    UpdateViewLimitsFromDataFiles;
    LockDraw;
    UpdateScreenDisp;
    UpdateViewLimit;
    UpdateCaption;
    UnlockDraw;
  except
  end;
end;
{/v0.09}

procedure TSpectrumForm.UserViewRecToExpViewRec(var AUserViewLimit:TUserViewLimit;
      var AViewLimit:TExpViewLimit);
var r:TUserY;
begin
  {v0.13}
  if Data = nil then
    exit;
  {/v0.13}
  if abs(AUserViewLimit.Min.X - AUserViewLimit.Max.X) < MinUserXStep then
    AUserViewLimit.Max.X := AUserViewLimit.Min.X + MinUserXStep;
  r := AUserViewLimit.Min.Y - AUserViewLimit.Max.Y;
  r := abs(r);
  if r < MinUserYStep then
    AUserViewLimit.Max.Y := AUserViewLimit.Min.Y + MinUserYStep;
  {v0.13}
  if Data <> nil then begin
    Data.UserToExp(AUserViewLimit.Min, AViewLimit.Min);
    Data.UserToExp(AUserViewLimit.Max, AViewLimit.Max);
  end else
  {/v0.13}
  begin
    AViewLimit.Min.X := AUserViewLimit.Min.X * 60;
    AViewLimit.Max.X := AUserViewLimit.Max.X * 60;
    AViewLimit.Min.Y := AUserViewLimit.Min.Y;
    AViewLimit.Max.Y := AUserViewLimit.Max.Y;
  end;
end;

{v0.14}{/v0.14

procedure TSpectrumForm.DoApexChar(CommChar:char);

  procedure HandlePacket(var ApexPktInfo:TApexPacketInfo);
  begin
    case ApexPktInfo.Pkt.Data[ApexEventIDPos] of
      eiMark: begin
        if RunningState =  rsReadyToRun then
          Run;
      end;
      eiData: begin
        if RunningState = rsRunning then
          ListRecAdd(PacketFifo, ApexPktInfo);
      end;
    else
      begin
      end;
    end;
  end;

begin
  with ApexPktInfo do begin
    if (CurPos < 4) and (CommChar <> #0) then begin
      CurPos := 0;
      EndTime := mstime;
    end else begin
      Pkt.Data[CurPos] := ord(CommChar);
      if CurPos = 0 then
        Time := EndTime;
      inc(CurPos);
      if CurPos = ApexPacketSize then begin
        CurPos := 0;
        EndTime := mstime;
        if Pkt.Data[ApexEventIDPos] <> eiData then begin
          CurPos := 0;
        end;
        HandlePacket(ApexPktInfo);
      end;
    end;
  end;
end;


procedure TSpectrumForm.DoExtDevInChar(CommChar:char);
begin
  ExtDevDoCharIn(ExtDevDrv, CommChar);
end;

procedure TSpectrumForm.DoInChar(CommChar:char);
begin
  case DeviceMode of
    dmApex: DoApexChar(CommChar);
    dmExtDev: DoExtDevInChar(CommChar);
  end;
end;

procedure TSpectrumForm.ComPortReceive(Sender: TObject; Count: Integer);
var
  CommChar:Char;
  i:Word;
  b:word;
begin
  begin
    if RunningState = rsStopped then
      exit;
    if IsInReceive then
      exit;
    if Count = 0 then
      exit;
    try
      IsInReceive := true;
      repeat
        if Count <= sizeof(RcvBuf) then begin
          b := Count;
        end else begin
          b := sizeof(RcvBuf);
        end;
        dec(Count, b);
        CommPort.Read(RcvBuf, b);
        for i := 0 to b - 1 do begin
          CommChar := RcvBuf[i];
          DoInChar(CommChar);
        end;
      until Count = 0;
    finally
      IsInReceive := false;
    end;
  end;
end;
}

procedure TSpectrumForm.Timer1Timer(Sender: TObject);
{v0.19}{/v0.19
var
  ai:TApexPacketInfo;}
{  gi:TExtDevPacketInfo;}
begin
  if IsInTimer then
    exit;
  if Data = nil then
    exit;
  if FAcqInfo = nil then
    exit;
  try
    IsInTimer := true;
    {v0.19}
    FAcqInfo.DoTimer;
    {/v0.19
    if RunningState = rsStopped then
    begin
      if DeviceMode = dmUlan then begin
        RcvRun(SII);
      end;
      exit;
    end else if RunningState = rsRunning then begin
      CheckStop;
    end;

    if not Dragging then begin
      case DeviceMode of
        dmApex: begin
          if PacketFifo <> nil then begin
            while ListRecGet(PacketFifo, ai) do begin
              DoApexPacket(ai);
            end;
          end;
        end;

        dmUlan: begin
          if PacketFifo <> nil then begin
            if SII <> nil then begin
              RcvRun(SII);
              while RcvGetPktInfo(SII, ai) do begin
                case ai.Pkt.Data[ApexEventIDPos] of
                  eiMark: begin
                    if RunningState =  rsReadyToRun then
                      Run;
                  end;
                else
                  DoApexPacket(ai);
                end;
              end;
            end;
          end;
        end;

        dmExtDev: begin
          DoExtDevTimer;
        end;
      else
        raise ESpecForm.Create('Invalid DeviceMode');
      end;
    end;}
  finally
    IsInTimer := false;
  end;
end;

{v0.19}
procedure TSpectrumForm.DoPointsAdded;
var
  dr: TExpPoint;
  sr: TScreenPoint;
begin
  {v0.20}{/v0.20
  if FDragging then
    exit;}
  if (ScrBuf[DataIndex].Points <> nil) and (FAcqInfo <> nil) then begin
    with FAcqInfo.RecentPoints do begin
      Position := 0;
      while Position < Size do begin
        ReadBuffer(dr, sizeof(dr));
        if (dr.X >= ViewLimit.Min.X) and (dr.X <= ViewLimit.Max.X) then
        begin
          Data.ExpToScreen(dr, ScreenDisp, sr);
          ScrBuf[DataIndex].Points.AddPoint(sr.x,sr.y);
        end;
      end;
    end;
  end;
  {v0.20}
  if DrawLocked = 0 then
  {/v0.20}
    DrawPoints(PaintBox.Canvas, ScreenDisp);
end;
{/v0.19
procedure TSpectrumForm.DoApexPacket(var ai:TApexPacketInfo);
var
  dr:TExpPoint;sr:TScreenPoint;
  var i:integer;
  values:PApexValues;
  acq:boolean;

  function MakeLogLine(var ai:TApexPacketInfo):string;
  var
    s:string;
    l:single;
    b:array[0..3]of byte absolute l;

  begin
    s := '';
    s := RecToHex(ai.Pkt.Data[ApexSourceIDPos], 3) + ' ' +
      RecToHex(ai.Pkt.Data[ApexFootPos], 3) + ' ' +
      RecToHex(ai.pkt.Data[ApexValuesPos], 4) + ' ';
    s := s + bin(b[0]) + ' ' + bin(b[1]) + ' ' +bin(b[2])+ ' ' +bin(b[3]);
    MakeLogLine := s;
  end;

begin
  if FAcqInfo <> nil then with FAcqInfo do
  if LogPackets then begin
    SpectrumMemo.Lines.Add(MakeLogLine(ai));
  end;
  if Data <> nil then with FAcqInfo do begin
    if not ai.ScanningValues then
      values := @ai.Pkt.Data[ApexValuesPos]
    else
      values := @ai.Pkt.Values;
    for i := 0 to ApexPacketValueCount - 1 do begin
      CurUlanTime := CurUlanTime + UlanPointTimeInterval;
      acq := true;
      if (FSampleInt <> 0) then begin
        if CurUlanTime >= FNextAcqTime then begin
          FNextAcqTime := CurUlanTime + FSampleInt;
        end else begin
          acq := false
        end;
      end;
      if acq then
      begin
        dr.X := CurUlanTime / 1000;
        dr.Y := (values^[i]) - ZeroValue;
        Data.AddPoint(dr);
        if ScrBuf[DataIndex].Points <> nil then begin
          if (dr.X >= ViewLimit.Min.X) and (dr.X <= ViewLimit.Max.X) then
          begin
            Data.ExpToScreen(dr, ScreenDisp, sr);
            ScrBuf[DataIndex].Points.AddPoint(sr.x,sr.y);
          end;
        end;
      end;
    end;
  end;
  DrawPoints(PaintBox.Canvas, ScreenDisp);
end;

procedure TSpectrumForm.DoExtDevTimer;
var
  dr: TExpPoint;
  sr: TScreenPoint;
  found:boolean;
begin
  if Data = nil then
    exit;
  found := false;
  with FAcqInfo do
  while ExtDev.ReadPoint(dr) = 0 do begin
    found  := true;
    if (FSampleInt <> 0) then begin
      if mstime >= FNextAcqTime then begin
        FNextAcqTime := mstime + FSampleInt;
      end else begin
        found := false
      end;
    end;
    if found then
    begin
      Data.AddPoint(dr);
      if ScrBuf[DataIndex].Points <> nil then begin
        if (dr.X >= ViewLimit.Min.X) and (dr.X <= ViewLimit.Max.X) then
        begin
          Data.ExpToScreen(dr, ScreenDisp, sr);
          ScrBuf[DataIndex].Points.AddPoint(sr.x,sr.y);
        end;
      end;
    end;
  end;
  if found then
    DrawPoints(PaintBox.Canvas, ScreenDisp);
end;
}

{/v0.11
procedure TSpectrumForm.DoExtDevPacket(var gi:TExtDevPacketInfo);
var
  dr:TExpPoint;
  sr:TScreenPoint;
  var i:integer;

  function MakeLogLine(var gi:TExtDevPacketInfo):string;
  var
    s:string;
    l:single;
    b:array[0..3]of byte absolute l;
  begin
    s := IntToStr(gi.Pkt[0]);
    MakeLogLine := s;
  end;

begin
  if LogPackets then begin
    SpectrumMemo.Lines.Add(MakeLogLine(gi));
  end;
  if Data <> nil then begin
    for i := 0 to ExtDevPacketValueCount - 1 do begin
      CurExtDevTime := CurExtDevTime + ExtDevPointTimeInterval;
      dr.X := CurExtDevTime / 1000;


      dr.Y := 2 * (gi.Pkt[i]) / 1024 - ZeroValue;
      Data.AddPoint(dr);
      if ScrBuf[DataIndex].Points <> nil then begin
        if (dr.X >= ViewLimit.Min.X) and (dr.X <= ViewLimit.Max.X) then
        begin
          Data.ExpToScreen(dr, ScreenDisp, sr);
          ScrBuf[DataIndex].Points.AddPoint(sr.x,sr.y);
        end;
      end;
    end;
  end;
  DrawPoints(PaintBox.Canvas, ScreenDisp);
end;
}

procedure TSpectrumForm.PaintBoxPaint(Sender: TObject);
begin
  if Printing then
    exit;
  if IsInPaint then
    exit;
  if DrawLocked > 0 then
    exit;
  if Data = nil then
    exit;
  IsInPaint := true;
  try
    {DrawPos := 0;
    LastScreenRec.X := -1;}
    if  SpectrumObjectVisible(soData) then
      DrawPoints(PaintBox.Canvas, ScreenDisp);
    if SpectrumObjectVisible(soAxis) then
      DrawAxis(PaintBox.Canvas, ScreenDisp);
    if SpectrumObjectVisible(soPeaks) then
      DrawPeaks(PaintBox.Canvas, ScreenDisp);
    if SpectrumObjectVisible(soBaseLine) then
      DrawBaseLine(PaintBox.Canvas, ScreenDisp);
    if SpectrumObjectVisible(soHeader) then
      DrawHeader(PaintBox.Canvas, ScreenDisp);
  finally
    DrawWanted := false;
    IsInPaint := false;
  end;
end;

function TSpectrumForm.ApexRecToScreenRec(const AApexRec:TExpPoint;
  const ADisp: TScreenDisp;
  var AScreenRec:TScreenPoint):boolean;
{$IFDEF DEBUG}
{var ap:TExpPoint;}
{$ENDIF}
begin
  ApexRecToScreenRec := false;
  if Data = nil then
    exit;
  if Data.ExpToScreen(AApexRec, ADisp{OutDisp^}, AScreenRec) <> 0 then
    exit;
  {$IFDEF DEBUG}
  {Data.ScreenToApex(AScreenRec, ap);
  if AApexRec.X <> ap.X then begin
    DebugFormLog('SpecForm.ApexRecToScreenRec dif: X=' +
      IntToStr(AApexRec.X) + 'x' + IntToStr(ap.X));
  end;}
  {$ENDIF}
  ApexRecToScreenRec := true;
end;

function TSpectrumForm.ScreenPointToIndexPoint(const AScreenPoint:TPoint;
      var AIndexPoint:TIndexPoint):boolean;
var ap:TExpPoint; {index:longint;}
begin
  ScreenPointToIndexPoint := false;
  if Data = nil then
    exit;
  if not Data.ScreenToExp(TScreenPoint(AScreenPoint), OutDisp^, ap) then
    exit;
  if not Data.FindDataPoint(ap, AIndexPoint.Point, AIndexPoint.Index) then
    exit;
  ScreenPointToIndexPoint := true;
end;

procedure TSpectrumForm.DataSetCurrent(Index: integer);
begin
  Data := TAcqData(DataList.Items[Index]);
  DataIndex := Index;
  Spect.SetData(Data);
end;

{Draw methods}
procedure TSpectrumForm.DrawTemplate(ACanvas: TCanvas; const ADisp: TScreenDisp);
var
  x,y:integer;
  s: string;
  h:integer;
begin
  s := ' Template ';
  h := ACanvas.Font.Height;
  ACanvas.Font.Height := 24;
  x := (ADisp.Right - ADisp.Left  - ACanvas.TextWidth(s)) div 2 + ADisp.Left;
  y := (ADisp.Bottom - ADisp.Top - ACanvas.TextHeight(s)) div 2 + ADisp.Top;
  ACanvas.TextOut(x, y, s);
  ACanvas.Font.Height := h;
end;

procedure TSpectrumForm.DrawPoints(ACanvas: TCanvas; const ADisp: TScreenDisp);

var
  ind, cnt: integer;
  ar:TExpPoint;
  sr:TScreenPoint;
  {ss:longint;}

  r: TRect;
  bc: TColor;

begin
  ind := 0;
  cnt := DataList.Count;
  with ACanvas do begin
    r.Left := ADisp.Left + 1;
    r.Top := ADisp.Top + 1;
    r.Right := ADisp.Right;{ADisp.Left + 1 + ADisp.Width;}
    r.Bottom := ADisp.Bottom;{ADisp.Top + ADisp.Height;}
    bc := Brush.Color;
    Brush.Color := clWhite;
    FillRect(r);
    Brush.Color := bc;
  end;

  repeat
    if cnt > 1 then begin
      DataSetCurrent(ind);
    end;
    with ACanvas do begin
      {v0.16}
      if Data.IsTemplate then begin
        DrawTemplate(ACanvas, ADisp);
      end else
      {/v0.16}
      begin
        if not ScrBuf[DataIndex].WasReadyFor(ACanvas, ADisp) then begin
          if Data <> nil then begin
            {v1.05}
            Data.SeekPoint(ScrBuf[DataIndex].DrawPos);
            {/v1.05 Data.Seek(DrawPos);}
            {i := 0;}
            if ScrBuf[DataIndex].LastRec.X > 0 then begin
              MoveTo(ScrBuf[DataIndex].LastRec.X, ScrBuf[DataIndex].LastRec.Y);
            end;
            repeat
              if Data.ReadPoint(ar) then begin
                {v1.05}
                inc(ScrBuf[DataIndex].DrawPos);
                {/v1.05
                inc(DrawPos, sizeof(ar));}

                if  ar.x > ViewLimit.Max.X then
                  break;
                if (ar.x >= ViewLimit.Min.X) then begin
                  if ApexRecToScreenRec(ar, ADisp, sr) then begin
                    if (ScrBuf[DataIndex].LastRec.X <> sr.x) or
                      (ScrBuf[DataIndex].LastRec.y <> sr.y) then
                    begin
                      {v1.05}{/v1.05
                      if i = 0 then begin
                        Canvas.MoveTo(sr.X, sr.Y)
                      end else begin
                        Canvas.LineTo(sr.X, sr.Y);
                      end;}
                      if ScrBuf[DataIndex].Points <> nil then
                        ScrBuf[DataIndex].Points.AddPoint(sr.X, sr.Y);
                      ScrBuf[DataIndex].LastRec := sr;
                    end;
                  end;
                  {inc(i);}
                end;
              end else begin
                Data.Reset;
                break;
              end;
            until false;
            {v1.05}
            ScrBuf[DataIndex].DrawPos := Data.GetPointCount;
            {/v1.05
            DrawPos := Data.GetSize;}
            {v1.05}DrawScreenPoints(ACanvas{, ADisp});{/v1.05}
          end;
        end else begin
          DrawScreenPoints(ACanvas{, ADisp});
        end;
      end;{/not is template}
    end;

    inc(ind);
  until ind >= cnt;
  {v0.09}
  {v0.11}
  if cnt > 1 then
  {/v0.11}
  begin
    DataSetCurrent(CurDataIndex);
  end;
  {/v0.09}
end;

procedure TSpectrumForm.DrawScreenPoints(ACanvas: TCanvas{; const ADisp: TScreenDisp});
  { draw the points from buffer }
var
  i: integer;
  spi: TScreenPointInfo;
  pc: TColor;
  {v0.20}
  ly:integer;
  {/v0.20}
begin
  if (ScrBuf[DataIndex].Points = nil) or (ScrBuf[DataIndex].Points.Count = 0) then
    exit;
  with ACanvas do begin
    {
    r.Left := OutDisp^.Left;
    r.Top := OutDisp^.Top;
    r.Right := OutDisp^.Left + OutDisp^.Width;
    r.Bottom := OutDisp^.Top + OutDisp^.Height;
    bc := Canvas.Brush.Color;
    Canvas.Brush.Color := clWhite;
    PaintBox.Canvas.FillRect(r);
    Canvas.Brush.Color := bc; textout logbrush logpen
    }
    pc := Pen.Color;
    Pen.Color := {v0.09} FPointColors[DataIndex mod MaxDataListColorCount];{/v0.09 clBlue;}
    for i := 0 to ScrBuf[DataIndex].Points.Count - 1 do begin
      spi := TScreenPointInfo(ScrBuf[DataIndex].Points.Items[i]);
      if i = 0 then begin
        MoveTo(spi.X, spi.Y1);
      end else begin
        LineTo(spi.X, spi.Y1);
      end;
      {v0.20}
      ly := spi.Y1;
      if spi.MinY <> ly then begin
        LineTo(spi.X, spi.MinY);
        ly := spi.MinY;
      end;
      if spi.MaxY <> ly then begin
        LineTo(spi.X, spi.MaxY);
        ly := spi.MaxY;
      end;
      if spi.Y2 <> ly then begin
        LineTo(spi.X, spi.Y2);
      end;
      {/v0.20
      if spi.Y1 <> spi.Y2 then
        LineTo(spi.X, spi.Y2);}
    end;
    Pen.Color := pc;
  end;
end;

procedure TSpectrumForm.DrawAxis(ACanvas: TCanvas; const ADisp: TScreenDisp);
begin
  if Data = nil then
    exit;
  if AxisPlotting = nil then begin
    AxisPlotting := TPlotting.Create(ACanvas, 100);{plotu}
    if AxisPlotting = nil then
      exit;
  end else
    AxisPlotting.Canvas := ACanvas;
  if CreateAxisPlotting(Data, UserViewLimit, ADisp, AxisPlotting) then begin
    PlottingToCanvas(AxisPlotting, ACanvas);
  end;
end;

procedure TSpectrumForm.DrawBaseLine(ACanvas: TCanvas; const ADisp: TScreenDisp);
var
  b: TULBRObj;
  c, i: integer;
  e1, e2: TExpPoint;
  s1, s2: TScreenPoint;
  os1, os2: integer;
  lineOn: boolean;

  ps: TPenStyle;
  pm: TPenMode;
  co: TColor;
  ind, cnt: integer;

  radius:integer;
  dispW:integer;
begin
  with ACanvas do begin
    ps := Pen.Style;
    pm := Pen.Mode;
    co := Pen.Color;

    Pen.Mode := pmCopy;{pmNotXor;}
    {Pen.Style := psDot;}
    cnt := DataList.Count;
    ind := 0;
    dispW := ADisp.Right - ADisp.Left;
    repeat
      if cnt > 1 then
        DataSetCurrent(ind);
      c := Data.ULB.ChildCount;
      for i := 0 to c - 1 do begin
        b := TULBRObj(Data.ULB.Childs[i]);
        if b.RecID <> ULBRID then
          continue;
        if b.IsFlagSet(rfSelected) then begin
          Pen.Color := clRed;
          if Printing then
            radius := BaseLineCircleRadius(DispW)
          else
            radius := 4 * BaseLineCircleRadius(DispW) div 3;
        end else begin
          Pen.Color := {v0.09} FPeakColors[DataIndex mod MaxDataListColorCount];{/v0.09 clBlack}
          radius := BaseLineCircleRadius(DispW);
        end;
        e1.x := b.X1;
        e1.y := b.Y1;
        e2.x := b.X2;
        e2.y := b.Y2;
        os1 := Data.ExpToScreen(e1, ADisp, s1);
        os2 := Data.ExpToScreen(e2, ADisp, s2);
        lineOn := false;
        if (os1 = 0) then begin
          Pen.Style := psSolid;
          Ellipse(s1.x - radius, s1.y - radius,
            s1.x + radius, s1.y + radius);
          lineOn := true;
        end else begin
          if ((os1 and opLeft) <> 0) and
             ((os2 = 0) or ((os2 and opRight) <> 0)) then
          begin
            s1.Y := CountYOnScreenLine(ADisp.Left, s1.X, s1.Y, s2.X, s2.Y);
            s1.X := ADisp.Left;
            lineOn := true;
          end;
        end;

        if (os2 = 0) then begin
          Pen.Style := psSolid;
          Ellipse(s2.x - radius, s2.y - radius,
            s2.x + radius, s2.y + radius);
          lineOn := true;
        end else begin
          if lineOn then begin
            s2.Y := CountYOnScreenLine(ADisp.Right{ADisp.Left + ADisp.Width}, s1.X, s1.Y,
              s2.X, s2.Y);
            s2.X := ADisp.Right;{ADisp.Left + ADisp.Width};
          end;
        end;

        if lineOn then begin
          Pen.Style := psDot;
          MoveTo(s1.x, s1.y);
          LineTo(s2.x, s2.y);
        end;
      end;
      inc(ind);
    until (ind >= cnt);
    Pen.Style := ps;
    Pen.Mode := pm;
    Pen.Color := co;
  end;
  {v0.09}
  DataSetCurrent(CurDataIndex);
  {/v0.09}
end;

procedure TSpectrumForm.DrawPeaks(ACanvas: TCanvas; const ADisp: TScreenDisp);
var
  i: integer;
  p: TULPRObj;{PPeak;}
  sp1, sp2, sp3: TScreenPoint;
  sb1, sb2: TScreenPoint;
  p1, p2, p3, b1, b2: TExpPoint;
  u: TUserPoint;
  ind, cnt:integer;
  bc, pc: TColor;
  pm: TPenMode;
  pw: integer;
  curColor: TColor;
  curW: integer;
  {bs: TBrushStyle; r: TRect;}
  dispW:integer;
  psl: integer; {peakStickLen}
  pal: integer; {pealArrowLen}
  s: string;

  {v0.09}
  procedure DrawLeftEdge(X: TScreenX; StartY, StopY: TScreenY);
  begin
    with ACanvas do begin
      if StartY <= StopY then begin
        Pen.Width := curW;
        MoveTo(X, StartY - psl);
        LineTo(X, StopY + psl);
        Pen.Width := pw;
        LineTo(X + pal, StopY + psl);
        LineTo(X + pal div 2, StopY + psl - psl div 4);
        MoveTo(X + pal, StopY + psl);
        LineTo(X + pal div 2, StopY + psl + psl div 4 + 1);
      end else begin
        Pen.Width := curW;
        MoveTo(X, StartY + psl);
        LineTo(X, StopY - psl);
        Pen.Width := pw;
        LineTo(X + pal, StopY - psl);
        LineTo(X + pal div 2, StopY - psl - psl div 4);
        MoveTo(X + pal, StopY - psl);
        LineTo(X + pal div 2, StopY - psl + psl div 4 + 1);
      end;
    end;
  end;

  procedure DrawRightEdge(X: TScreenX; StartY, StopY: TScreenY);
  begin
    with ACanvas do begin
      if StartY <= StopY then begin
        Pen.Width := curW;
        MoveTo(X, StartY - psl);
        LineTo(X, StopY + psl);
        Pen.Width := pw;
        LineTo(X - pal, StopY + psl);
        LineTo(X - pal div 2, StopY + psl - psl div 4);
        MoveTo(X - pal, StopY + psl);
        LineTo(X - pal div 2, StopY + psl + psl div 4 + 1);
      end else begin
        Pen.Width := curW;
        MoveTo(X, StartY + psl);
        LineTo(X, StopY - psl);
        Pen.Width := pw;
        LineTo(X - pal, StopY - psl);
        LineTo(X - pal div 2, StopY - psl - psl div 4);
        MoveTo(X - pal, StopY - psl);
        LineTo(X - pal div 2, StopY - psl + psl div 4 + 1);
      end;
    end;
  end;
  {/v0.09}

begin
  if (Data = nil) then
    exit;
  ind := 0;
  cnt := DataList.Count;

  bc := ACanvas.Brush.Color;
  pm := ACanvas.Pen.Mode;
  pc := ACanvas.Pen.Color;
  pw := ACanvas.Pen.Width;
  dispW := ADisp.Right - ADisp.Left;
  psl := PeakStickLen(dispW);
  pal := PeakArrowLen(dispW);

  try
    with ACanvas do begin
      Brush.Color := clWhite;
      Pen.Mode := pmCopy;
      repeat
        if cnt > 1 then
          DataSetCurrent(ind);
        for i := 0 to Spect.Peaks.ChildCount - 1 do begin
          p := TULPRObj(Spect.Peaks.Childs[i]);
          if p.RecID <> ULPRID then
            continue;
          p1.x := p.X1;
          p1.y := p.Y1;
          p2.x := p.X2;
          p2.y := p.Y2;
          p3.x := p.X;
          p3.y := p.Height;
          b1.x := p.X1;
          b1.y := p.BaseY1;
          b2.x := p.X2;
          b2.y := p.BaseY2;

          if p.IsFlagSet(rfSelected) then begin
            curColor := clRed;
            if Printing then
              curW := 1
            else
              curW := 2;
          end else begin
            curColor := {v0.09} FPeakColors[DataIndex mod MaxDataListColorCount];{/v0.09 clBlack;}
            curW := 1;
          end;
          Pen.Color := curColor;

          if ApexRecToScreenRec(p1, ADisp, sp1) then begin
            if not ApexRecToScreenRec(b1, ADisp, sb1) then begin
            end;
            {v0.09}
            DrawLeftEdge(sp1.X, sp1.Y, sb1.Y)
            {/v0.09
              Pen.Width := curW;
              MoveTo(sp1.X, sp1.Y - psl);
              LineTo(sp1.X, sb1.Y + psl);
              Pen.Width := pw;
              LineTo(sp1.X + pal, sb1.Y + psl);
              LineTo(sp1.X + pal div 2, sb1.Y + psl - psl div 4);
              MoveTo(sp1.X + pal, sb1.Y + psl);
              LineTo(sp1.X + pal div 2, sb1.Y + psl + psl div 4 + 1);
            }
          end;

          if ApexRecToScreenRec(p2, ADisp, sp2) then begin
            if not ApexRecToScreenRec(b2, ADisp, sb2) then begin
            end;
            {v0.09}
            DrawRightEdge(sp2.X, sp2.Y, sb2.Y)
            {/v0.09
            Pen.Width := curW;
            MoveTo(sp2.X, sp2.Y - psl);
            LineTo(sp2.X, sb2.Y + psl);
            Pen.Width := pw;
            LineTo(sp2.X - pal, sb2.Y + psl);
            LineTo(sp2.X - pal div 2, sb2.Y + psl - psl div 4);
            MoveTo(sp2.X - pal, sb2.Y + psl);
            LineTo(sp2.X - pal div 2, sb2.Y + psl + psl div 4 + 1);}
          end;

          if ApexRecToScreenRec(p3, ADisp, sp3) then begin
            Data.ExpToUser(p3, u);
            s := p.PeakName + ' ' + FloatToStrF(u.X, ffFixed, 4, 2);
              {FloatToRoundStr(u.X, 3, 0); }
            AngleText.TextOut(Printing, ACanvas, curColor,
              sp3.X - TextHeight(s) div 2, ADisp.Top + TextWidth('M') div 4 + TextWidth(s), s);
          end;

        end;
        inc(ind);
      until (ind >= cnt);
    end;{/with ACanvas}
  finally
    ACanvas.Brush.Color := bc;
    ACanvas.Pen.Mode := pm;
    ACanvas.Pen.Color := pc;
    ACanvas.Pen.Width := pw;
    {v0.09}
    DataSetCurrent(CurDataIndex);
    {/v0.09}
  end;
end;

procedure TSpectrumForm.DrawSpectrum(ACanvas: TCanvas; const ADisp: TScreenDisp);
begin
  DrawPoints(ACanvas, ADisp);
  if SpectrumObjectVisible(soAxis) then
    DrawAxis(ACanvas, ADisp);
  if SpectrumObjectVisible(soPeaks) then
    DrawPeaks(ACanvas, ADisp);
  if SpectrumObjectVisible(soBaseLine) then
    DrawBaseLine(ACanvas, ADisp);
  if not PrintingReport then begin
    if SpectrumObjectVisible(soHeader) then
      DrawHeader(ACanvas, ADisp);
  end;
end;


procedure TSpectrumForm.PlottingToCanvas(var APlot:TPlotting; ACanvas:TCanvas);
var pr:TPlotRecord;{plotu}
begin
  APlot.Reset;
  while APlot.Read(pr) do begin
    case pr.pc of
      pcMoveTo: ACanvas.MoveTo(pr.X, pr.Y);
      pcLineTo: ACanvas.LineTo(pr.X, pr.Y);
      pcTextOut: ACanvas.TextOut(pr.X, pr.Y, GetString(PString(pr.Info)));
    end;
  end;
end;


procedure TSpectrumForm.LockDraw;
begin
  inc(DrawLocked);
end;

procedure TSpectrumForm.UnlockDraw;
begin
  if DrawLocked = 0 then begin
    ShowMessage('Uneven UnlockDraw',smError,0);
    exit;
  end;
  dec(DrawLocked);
  if (DrawLocked = 0) and DrawWanted then
    Redraw;
end;

procedure TSpectrumForm.SetToRedraw(tr:TToRedraw);
var i: integer;
begin
  if (tr and trData) <> 0 then begin
    for i := 0 to DataList.Count - 1 do begin
      ScrBuf[i].Clear;
      {
      ScrBuf[i].DrawPos := 0;
      ScrBuf[i].LastRec.X := -1;
      ScrBuf[i].Points.Free;
      ScrBuf[i].Points := nil;
      }{drawbuf}
    end;
  end;
end;

procedure TSpectrumForm.Redraw;
begin
  DrawWanted := true;
  if DrawLocked > 0 then
    exit;
  Invalidate;
end;

{/Draw methods}



procedure TSpectrumForm.FormDestroy(Sender: TObject);
begin
  {if (PeaksDefineForm <> nil) and (PeaksDefineForm.ConnectedForm = Self) then
  begin
  end;}
  {v0.08}
  DefineMode := dmNone;
  {/v0.08}
  MarginML.Free;
  PeakML.Free;
  {v0.09}
  ShowPosML.Free;
  {/v0.09}
  {v0.14}
  FAcqInfo.Free;
  {/v0.14
  if MainForm.ComPort = CommPort then
    MainForm.ComPort := nil;
  ListDone(PacketFifo);
  }
{  Data.Free;}
  ListDone(UserLimitStack);
  dec(SpectrumFormCount);

  Spect.Free;
  DataListClear(-1);
  ScrBuf.Free;

  CurUserViewLimit := UserViewLimit;
    { CurUserViewLimit will be saved to INI as default for new chromatograms }
  DataList.Free;
  AngleText.Free;
end;

procedure TSpectrumForm.DataListAdd(AData: TAcqData);
begin
  AData.ULA.UserRegister(Self);
  AData.ULP.UserRegister(Self);
  AData.ULB.UserRegister(Self);
  DataList.Add(AData);
  if CurDataIndex < 0 then
    CurDataIndex := 0;
end;

procedure TSpectrumForm.DataListClear(Index:integer);
var
  i: integer;
{  d: TAcqData;}
begin
  {v0.09}
  if Index = -1 then
  {/v0.09}
  begin
    for i := 0 to DataList.Count - 1 do begin
      Data := TAcqData(DataList.Items[i]);
      Data.ULA.UserUnregister(Self);
      Data.ULP.UserUnregister(Self);
      Data.ULB.UserUnregister(Self);
      {v0.13}{/v0.13 UpdateViewLimitRecToDataFile(true);}
      ScrBuf[i].Clear;{Points.Free;}
      Data.Free;
    end;
    Data := nil;
    CurDataIndex := -1;
  end
  {v0.09}
  else begin
    if (DataList.Count > 1) and (Index >= 0) and (Index < DataList.Count) then begin
      Data := TAcqData(DataList.Items[Index]);
      Data.ULA.UserUnregister(Self);
      Data.ULP.UserUnregister(Self);
      Data.ULB.UserUnregister(Self);
      {v0.13}{/v0.13 UpdateViewLimitRecToDataFile(true);}
      for i := Index to DataList.Count - 1 do
        ScrBuf[i].Clear;{Points.Free;}
      DataList.Delete(Index);
      Data.Free;
      Data := nil;
      if CurDataIndex >= DataList.Count then
        CurDataIndex := DataList.Count - 1;
      DataSetCurrent(CurDataIndex);
    end;
  end{/v0.09};
end;

procedure TSpectrumForm.SetPacketLogging(onOff:boolean);
begin
  if OnOff then begin
    SpectrumMemo.Visible := true;
  end else begin
    SpectrumMemo.Visible := false;
  end;
  if FAcqInfo <> nil then
    FAcqInfo.LogPackets := OnOff;
  SetToRedraw(trAll);
  Redraw;
end;

procedure TSpectrumForm.LogMenuItemClick(Sender: TObject);
begin
  LogMenuItem.Checked := not LogMenuItem.Checked;
  SetPacketLogging(LogMenuItem.Checked);
end;

procedure TSpectrumForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Action := caFree;
  {ClosePeaksDefineForm;}
  {if ReportFrm <> nil then
    PostMessage(ReportFrm.Handle, WM_APPMESSAGE,
      cmSpectrumFormClosed, longint(Self));}
  {v0.19}
  DoAfterStop;
  {/v0.19
  Stop;}
  WriteToIniFile;
  MainForm.Chromatogram_Btn.Enabled := false;
end;

procedure TSpectrumForm.MarginsItemClick(Sender: TObject);
begin
  Data.ULVL.Edit;
  {
  if ExecuteMarginsDialog(UserViewLimit) = mrOK then begin
    UpdateViewLimit;
    SetToRedraw(trAll);
    Redraw;
  end;
  }
end;

procedure TSpectrumForm.PopupMenuPopup(Sender: TObject);
{v0.09}
var
  oit, mi: TMenuItem;
  i, cnt: integer;
{/0.09}
begin
  if (Data <> nil) and (Data.Mode <> omCreate) then begin
    RunStop_Item.Visible := false;
    {v0.19}
    SuspendResume_Item.Visible := false;
    {/v0.19}
  end else begin
    {v0.14} if (FAcqInfo <> nil) then with FAcqInfo do
    {/v0.14}
    begin
      RunStop_Item.Visible := true;
      if RunningState <> rsStopped then
        RunStop_Item.Caption := '&Stop'
      else
        RunStop_Item.Caption := '&Run';

      {v0.19}
      if RunningState in [rsRunning, rsSuspended] then begin
        SuspendResume_Item.Visible := true;
        if RunningState = rsRunning then
          SuspendResume_Item.Caption := 'S&uspend'
        else
          SuspendResume_Item.Caption := 'R&esume';
      end else begin
        SuspendResume_Item.Visible := false;
      end;
      {/v0.19}
    end;
  end;
  case DefineMode of
    dmBaseLine: begin
      BaseLineInsertPoint_Item.Visible := true;
    end;
  else
    BaseLineInsertPoint_Item.Visible := false;
  end;
  {v0.09}
  if DataList.Count > 1 then begin
    OverlayMenu_Item.Visible := true;
    oit := OverlayMenu_Item;
    cnt := oit.Count - 1;
    while (cnt >= 0) and (oit.Items[cnt].Caption <> '-') do begin
      oit.Items[cnt].Free;
      cnt := oit.Count - 1;
    end;
    for i := 0 to DataList.Count - 1 do begin
      DataSetCurrent(i);
      mi := TMenuItem.Create(Self);
      mi.Tag := i + 1;
      mi.Checked := (i = CurDataIndex);

      mi.Caption := Data.ULF.FileName;
      if mi.Caption = '' then
        mi.Caption := 'TMP' + IntToStr(integer(Data.ULF));

      mi.OnClick := OverlayFileNameClick;
      oit.Add(mi);
    end;
    DataSetCurrent(CurDataIndex);
  end else begin
    OverlayMenu_Item.Visible := false;
  end;
  {/v0.09}
  {v0.11}
  PeaksResponsesCalculate_Item.Visible := (Data <> nil) and Data.ULA.CalibrationStandard;
  PeaksAmountsCalculate_Item.Visible := (Data <> nil) {v0.12}{/v0.12 and Data.ULM.CalcAmounts};
  {/v0.11}
end;

procedure TSpectrumForm.RunStop_ItemClick(Sender: TObject);
begin
  {v0.19}
  if FAcqInfo <> nil then
    FAcqInfo.DoRunStop;
  {/v0.19
  if FAcqInfo <> nil then with FAcqInfo do
  begin
    if RunningState <> rsStopped then
      Stop
    else
      Run;
  end;}
end;

procedure TSpectrumForm.RunPanelShow;
begin
  RunPanel.Left := (Width div 2) - (RunPanel.Width div 2);
  RunPanel.Top := (PaintBox.Height div 2) - (RunPanel.Height div 2);
  RunPanel.Show;
end;
{v0.14}{/v0.14
procedure TSpectrumForm.PortSetDefaults;
var
  op:TCommOptions;
begin
  // $IFDEF WIN32
  with CommPort do begin
    AfterOpenState := aoSpecified;
    BaudRate := br9600;
    DataBits := da8;
    DeviceName := 'COM2';
    FlowControl := fcDefault;
    MonitorEvents := [evRxChar];
    Options := [];
    Parity := paNone;
    ReadBufSize := 4096;
    ReadTimeout := 1000;
    StopBits := sb10;
    WriteBufSize := 2048;
    WriteTimeout := 1000;
    AfterOpenState := aoSpecified;
    op := [];
    Options := op;
    DTROnOpen := TCommEscapeState(1);
    RTSOnOpen := TCommEscapeState(0);
    XOnOnOpen := TCommEscapeState(2);
    BreakOnOpen := TCommEscapeState(2);
   end;
// $ELSE
//with CommPort do begin
//    BaudRate := tbr9600;
//    DataBits := tdbEight;
//    Parity := tpSpace;
//    ReadBufferSize := 2048;
//    Stopbits := tsbOne;
//    TxLowCount := 1024;
//    WriteBufferSize := 1024;
//    Events := [tceRxChar];
//  end;
//$ENDIF
end;
}

function TSpectrumForm.PortOpen: boolean;
begin
  Result := false;
  {v0.14}
  if (FAcqInfo = nil) then
    Result := FAcqInfo.PortOpen;
  {/v0.14
  case DeviceMode of
    dmUlan: begin
      Result := RcvInit(SII, UVDetAddr);
    end;
  else
    //$IFDEF WIN32
    CommPort.OnRxChar := ComPortReceive;
    CommPort.DeviceName := PortName;
    ReadWriteComConfig(rwRead, CommPort);
    case DeviceMode of
      dmExtDev: begin
        CommPort.DTROnOpen := esOn;
        CommPort.RTSOnOpen := esOff;
      end;
    end;
    if CommPort = MainForm.ComPort then
      MainForm.ComPort := nil;
    CommPort.Open;
    //$ELSE
    //CommPort.OnReceive := ComPortReceive;
    //CommPort.Port := TPort(ComPortNumber);
    //$ENDIF
    Result := true;
  end;
  }
end;

procedure TSpectrumForm.PortClose;
begin
  {v0.14}
  if FAcqInfo = nil then
    FAcqInfo.PortClose;
  {/v0.14
  begin
    case DeviceMode of
      dmUlan: begin
        RcvDone(SII);
      end;
    else
      //$IFDEF WIN32
      CommPort.Close;
      CommPort.OnRxChar := nil;
      ReadWriteComConfig(rwWrite, CommPort);
      if MainForm.ComPort = nil then
        MainForm.ComPort := CommPort;
      //$ELSE
      //CommPort.Port := tptNone;
      //CommPort.OnReceive := nil;
      //CommPort := nil;
      //$ENDIF
    end;
  end;
  }
end;

procedure TSpectrumForm.ResetPacketInfo;
begin
  {v0.14}
  if FAcqInfo <> nil then FAcqInfo.ResetPacketInfo;
  {/v0.14
    FillChar(ApexPktInfo, sizeof(ApexPktInfo), 0);
  }
end;

{v0.19}
procedure TSpectrumForm.DoAfterRun;
begin
  if FAcqInfo = nil then
    exit;
  with FAcqInfo do begin
    if RunningState = rsReadyToRun then begin
      RunPanelShow
    end else {if RunningState := rsRunning then} begin
      RunPanel.Hide;
      ClearData;
    end;
  end;
  UpdateCaption;
end;
{/v0.19
procedure TSpectrumForm.Run;
var pn: shortstring;
begin
  if (FAcqInfo <> nil) then with FAcqInfo do
  begin
    if RunningState = rsStopped then begin
      pn := PortName;
      if SendMessage(MainForm.Handle, WM_APPMESSAGE, cmQueryRunning,
        longint(@pn)) = 1 then
      begin
        ShowMsg.ShowMessage('Acquisition is already running', smError,0);
        exit;
      end;
      if (Data <> nil) and (Data.ULAD.Data.Size <> 0) then begin
        if ShowMessage('Discard already acquired data?', smNoYes, 0) <> cmYes then
          exit;
      end;
      ResetPacketInfo;
      if not PortOpen then
        exit;
      RunningState := rsReadyToRun;
      RunPanelShow;
    end else begin
      ClearData;
      ResetPacketInfo;
      ZeroTime := mstime;
      CurUlanTime := 0;
      ApexPktInfo.EndTime := ZeroTime;
      case DeviceMode of
        dmUlan: RcvSetProp(SII, rpZeroTime, @ZeroTime);
        dmExtDev: ExtDev.Start;
      end;
      FAcqInfo.Start;
      RunningState := rsRunning;
      RunPanel.Hide;
    end;
  end;
  UpdateCaption;
end;
}

procedure TSpectrumForm.ClearData;
begin
  if Data <> nil then begin
    Data.Clear;
    SetToRedraw(trData);
    Redraw;
  end;
end;
{v0.19}
procedure TSpectrumForm.DoAfterStop;
begin
  if csDestroying in ComponentState then
    exit;
  RunPanel.Hide;
  DoAfterAutodetect;
  UpdateCaption;
end;

procedure TSpectrumForm.DoAfterResume;
begin
  UpdateCaption;
end;

procedure TSpectrumForm.DoAfterSuspend;
begin
  UpdateCaption;
end;

{/v0.19
procedure TSpectrumForm.Stop;
begin
  if FAcqInfo <> nil then with FAcqInfo do
  if RunningState <> rsStopped then begin
    RunningState := rsStopped;
    PortClose;
    RunPanel.Hide;
    case DeviceMode of
      dmExtDev: ExtDev.Stop;
    end;
    try
      Autodetect1Click(Self);
    except
    end;
  end;
  UpdateCaption;
end;
}
function TSpectrumForm.GetFileName;
begin
  if Data <> nil then
    GetFileName := Data.ULF.FileName
  else
    GetFileName := '';
end;

procedure TSpectrumForm.UpdateCaption;
begin
  if (Data <> nil) and (Data.Mode <> omCreate) then begin
    Caption := FileName;
  end else begin
    {v0.14}if FAcqInfo <> nil then with FAcqInfo do{/v0.14}
    if RunningState = rsStopped then begin
      Caption := 'Stopped ' + FileName;
    end else if RunningState = rsReadyToRun then begin
      Caption := 'Waiting for mark ' + FileName;
    end else {v0.19} if RunningState = rsSuspended then begin
      Caption := 'Suspended ' + FileName;
    end else {/v0.19} begin
      Caption := 'Running ' + FileName;
    end;
  end;
end;

procedure TSpectrumForm.FormResize(Sender: TObject);
begin
  UpdateScreenDisp;
end;

procedure TSpectrumForm.AxisItemClick(Sender: TObject);
begin
{  SpectrumObjectShow(soAxis, not AxisItem.Checked);}
end;


{v0.09}{/v0.09
function TSpectrumForm.DoSaveTo:boolean;
var fn, ext: string;
begin
  if SaveDialog.Execute then begin
    fn := SaveDialog.FileName;
    ext := GetExtFromFileDialogFilter(SaveDialog.Filter, SaveDialog.FilterIndex);
    fn := ReplaceExt(fn, ext, false);
    SaveTo(fn);
    DoSaveTo := true;
  end else begin
    DoSaveTo := false;
  end;
end;
}
procedure TSpectrumForm.SaveTo(AFileName:string);
{v0.09}
{var fn, ext: string;}
{/v0.09}
var fn:string;
begin
  fn := AFileName;
  {v0.09}
  {ext := GetExtFromFileDialogFilter(SaveDialog.Filter, SaveDialog.FilterIndex);
  fn := ReplaceExt(fn, ext, false);}
  if Data <> nil then begin
    Data.SaveTo(ReplaceExt(fn, UlfExt, false));
    UpdateCaption;
  end;
end;

{Messages response methods}
procedure TSpectrumForm.WMAppMessage(var Msg:TMessage);
var x,y:integer;

  procedure ClipCopy;
  begin
    if ActiveControl = ReportMemo then begin
      ReportMemo.CopyToClipboard;
    end;
  end;

  procedure ClipPaste;
  begin
    if ActiveControl = ReportMemo then begin
      ReportMemo.PasteFromClipboard;
    end;
  end;

  procedure ClipCut;
  begin
    if ActiveControl = ReportMemo then begin
      ReportMemo.CutToClipboard;
    end;
  end;
type
  pshortstring = ^shortstring;

begin
  case Msg.WParam of
    cmQueryRunning:  begin
      {v0.14}if FAcqInfo <> nil then with FAcqInfo do{/v0.14}
      if (RunningState <> rsStopped) and (pshortstring(Msg.LParam)^ = PortName) then begin
        Msg.Result := 1;
        exit;
      end;
    end;

    cmLocalMenu: begin
      x := Left + Width div 2;
      y := Top + Height div 4;
      PopupMenu.Popup(x, y);
    end;

    cmDefineModeSet: DefineMode := TDefineMode(Msg.LParam);
    cmPermamentDefineModeSet: begin
      DefineMode := TDefineMode(Msg.LParam);
      if DefineMode <> dmNone then begin
        DefineModePermanent := true;
      end;
      FSpeedButtonShift := false;
    end;
    {cmPeaksDefineStart: PeaksDefineStart;
    cmPeaksDefineStop: PeaksDefineStop;
    cmMarginsDefineStart: MarginsDefineStart;
    cmMarginsDefineStop: MarginsDefineStop;
    cmBaseLineDefineStart: BaseLineDefineStart;
    cmBaseLineDefineStop: BaseLineDefineStop;
    }
    cmClipCopy : ClipCopy;
    cmClipCut: ClipCut;
    cmClipPaste :ClipPaste;
    cmSpectrumObjectShow..cmSpectrumObjectShow + SpectrumObjectCount: begin
      SpectrumObjectShow(Msg.WParam - cmSpectrumObjectShow,
        boolean(Msg.LParam));
    end;
    {
    cmExtDevDataChanged : begin
      if pointer(Msg.LParam) = Channel then
        SetPoints(PChannel(Channel)^.Data);
    end;
    cmChannelStarted, cmChannelPaused,
    cmChannelResumed, cmChannelStopped: begin
    end;
    cmChannelOptionsChanged, cmSolventRecoverySet:begin
      if (pointer(Msg.LParam) = Channel) then
        UpdateRecoveryLevelShape;
    end;
{      UpdateChannelCommands(Msg.WParam, word(Msg.LParam));}
    {ulantype}
    cmULObjBeforeEdit: ULObjBeforeEdit(Msg);
    cmULObjAfterEdit, cmULObjAfterBrowseEdit: ULObjAfterEdit(Msg);
    {v0.20}
    cmULObjUpdated: ULObjUpdated(Msg);
    cmULObjDestroyed: ULObjDestroyed(Msg);
    {/v0.20}
    {cmULObjUpdated: ULObjUpdated(Msg);
    cmULObjDestroyed: ULObjDestroyed(Msg);}
    {v0.15}
    cmAcquisitionStop: begin
      {v0.19}
      DoAfterStop;
      {/v0.19
      Stop;}
    end;
    {/v0.15}
    {v0.19}
    cmAcquisitionRun: begin
      DoAfterRun;
    end;
    cmPeaksAutodetected: begin
      DoAfterAutodetect;
    end;
    cmExpPointsAdded: begin
      DoPointsAdded;
    end;
    cmAcquisitionResumed: begin
      DoAfterResume;
    end;
    cmAcquisitionSuspended: begin
      DoAfterSuspend;
    end;
    {/v0.19}
  end;
  inherited;
end;

procedure TSpectrumForm.ULObjBeforeEdit(var Msg: TMessage);
var o: TULObj;
begin
  o := TULObj(Msg.lParam);
  if o.RecID = ULVLID then begin
    {v0.13}{/v0.13 UpdateViewLimitRecToDataFile(true);}
    CurUserViewLimit := UserViewLimit;
  end;
end;

procedure TSpectrumForm.ULObjAfterEdit(var Msg: TMessage);
var o: TULObj;
begin
  o := TULObj(Msg.lParam);
  case o.RecID of
    ULVLID: begin
      UpdateViewLimitRecFromDataFile(true);
      UpdateViewLimit;
    end;
    ULPID, ULPRID: begin
      DoPeaksSizeChanged;
      SetToRedraw(trPeaks);
      Redraw;
    end;
    ULBID, ULBRID: begin
      DoPeaksSizeChanged;
      SetToRedraw(trBaseLine);
      Redraw;
    end;
    ULVOID: begin
      UpdateViewOptionsFromDataFile;
    end;
    ULAID: begin
      SetToRedraw(trHeader);
      Redraw;
    end;
    {v0.15}
    ULIID: begin
      if Data.ULI.SamplingInterval <> 0 then begin
      end;
      if Data.ULI.Duration <> 0 then begin
      end;
    end;
    {/v0.15}
  end;
end;

{v0.20}
function TSpectrumForm.IsDataListChild(o:TULObj):boolean;
var
  d: TAcqData;
  i: integer;
begin
  Result := false;
  for i := 0 to DataList.Count - 1 do begin
    d := TAcqData(DataList[i]);
    if d.ULF.OwnsObj(o) then begin
      Result := true;
      exit;
    end;
  end;
end;

procedure TSpectrumForm.ULObjUpdated(var Msg: TMessage);
var o: TULObj;
begin
  o := TULObj(Msg.lParam);
  case o.RecID of
    ULPID, ULPRID: begin
      if IsDataListChild(o) then begin
        SetToRedraw(trPeaks);
        Redraw;
      end;
    end;
    ULBID, ULBRID: begin
      if IsDataListChild(o) then begin
        SetToRedraw(trBaseLine);
        Redraw;
      end;
    end;
  end;
end;
{/v0.20
procedure TSpectrumForm.ULObjUpdated(var Msg: TMessage);
var o: TULObj;
begin
  if FDataChangeFromForm then
    exit;
  o := TULObj(Msg.lParam);
  case o.RecID of
    ULPID, ULBID: begin
      SetToRedraw(trPeaks);
      Redraw;
    end;
  end;
end;
}

procedure TSpectrumForm.ULObjDestroyed(var Msg: TMessage);
var o: TULObj;
begin
{v0.20}{/v0.20
  if FDataChangeFromForm then
    exit;}
  o := TULObj(Msg.lParam);
  case o.RecID of
    ULPID, ULPRID: begin
      if IsDataListChild(o) then begin
        SetToRedraw(trPeaks);
        Redraw;
      end;
    end;
    ULBID, ULBRID: begin
      if IsDataListChild(o) then begin
        SetToRedraw(trBaseLine);
        Redraw;
      end;
    end;
  end;
end;



procedure TSpectrumForm.WMCopy(var Msg: TMessage);
begin
  ForwardMessage(Msg);
  inherited;
end;

procedure TSpectrumForm.WMCut(var Msg: TMessage);
begin
  ForwardMessage(Msg);
  inherited;
end;

procedure TSpectrumForm.WMPaste(var Msg: TMessage);
begin
  ForwardMessage(Msg);
  inherited;
end;

procedure TSpectrumForm.ForwardMessage(var Msg: TMessage);
begin
end;

{/Message response methods}

procedure TSpectrumForm.CopySpectrumToClipboard;
var
{  p: TPicture;}
  mf{,mf2}: TDrawMetaFile;
  i:word;

  {$IFDEF DEBUG}
  procedure CheckClipboard;
  const maxfnlen = 255;
  var fn: array[0..MaxFnLen-1] of char;
  begin
    Clipboard.Open;
    try
      i := 0;
      DebLog('Clipboard formats:');
      repeat
        i := EnumClipboardFormats(i);
        if i <> 0 then begin
           if GetClipboardFormatName(i, fn, maxfnlen) = 0 then begin
             DebLog('Predefined: ' + IntToStr(i));
           end else begin
             DebLog(StrPas(fn));
           end;
        end;
      until i = 0;
    finally
      Clipboard.Close;{windows}
    end;
    DebLog('Clipboard formats end.');

  end;
  {$ENDIF}
begin
  mf := TDrawMetaFile.Create(PaintBox.Width, PaintBox.Height);
  DrawSpectrum(mf.Canvas, ScreenDisp);
  mf.Close;
  {p := TPicture.Create; windows}
  {p.RegisterClipboardFormat(CF_METAFILEPICT, TMetafile);
  RegisterClipboardFormat(
  p.LoadFromClipboardFormat Assign(TMetaFile(mf));}
  {$IFDEF DEBUG}
  CheckClipboard;
  {$ENDIF}
  Clipboard.Assign(mf);
  mf.Free;
  {$IFDEF DEBUG}
  CheckClipboard;
  {$ENDIF}
  {p.Free;}
end;

procedure TSpectrumForm.FormActivate(Sender: TObject);
begin
  MainForm.Chromatogram_Btn.Enabled := true;
  UpdateScreenCursor;
end;

procedure TSpectrumForm.FormDeactivate(Sender: TObject);
begin
  MainForm.Chromatogram_Btn.Enabled := false;
  Screen.Cursor := crDefault;
end;

procedure TSpectrumForm.PeaksClearMenuItemClick(Sender: TObject);
begin
  if (Spect <> nil) then begin
    {v0.20}{/v0.20 FDataChangeFromForm := true;
    try}
    Spect.Peaks.Clear;
    {v0.20}{/v0.20 finally
      FDataChangeFromForm := false;
    end;
    SetToRedraw(trPeaks);
    Redraw;}
  end;
end;

procedure TSpectrumForm.PeaksShow;
begin
{  if not PeaksMenuItem.Checked then begin}
{    PeaksMenuItem.Checked := true;}
  Data.ULVO.ShowPeaks := true;
  PeaksCheckBox.Checked := true;
  SetToRedraw(trPeaks);
  Redraw;
{  end;}
end;

procedure TSpectrumForm.PeaksHide;
begin
{  if PeaksMenuItem.Checked then begin
    PeaksMenuItem.Checked := false;}
  Data.ULVO.ShowPeaks := false;
  PeaksCheckBox.Checked := false;
  if DefineMode = dmPeaks then
    DefineMode := dmNone;{PeaksDefineStop;}
  SetToRedraw(trPeaks);
  Redraw;
{  end;}
end;

procedure TSpectrumForm.PeaksDefineStart;
begin
  DefinePeaksSpeedButton.Down := true;
  PeaksDefineItem.Checked := true;
  PeakDefineMode := pdReady;
  PeaksShow;
{  Screen.Cursor := crThinCrossUp; called from UpdateCursor}
end;

procedure TSpectrumForm.PeaksDefineStop;
begin
  DefinePeaksSpeedButton.Down := false;
  PeaksDefineItem.Checked := false;
  PeakDefineMode := pdNone;
  Screen.Cursor := crDefault;
end;

{v0.09}
procedure TSpectrumForm.ShowPosStart;
begin
  ShowPos_Btn.Down := true;
  SpectrumObjectShow(soXY, true);
  ShowPosML.Start(LastMousePos.X, LastMousePos.Y);
end;

procedure TSpectrumForm.ShowPosStop;
begin
  ShowPos_Btn.Down := false;
  SpectrumObjectShow(soXY, false);
  ShowPosML.Stop(LastMousePos.X, LastMousePos.Y);
end;

procedure TSpectrumForm.OverlayStart;
begin
  Overlay_Btn.Down := true;
  FOverlayMode := true;
end;

procedure TSpectrumForm.OverlayStop;
begin
  Overlay_Btn.Down := false;
  FOverlayMode := false;
end;
{/v0.09}

procedure TSpectrumForm.SetDefineMode(ADefineMode: TDefineMode);
begin
  PeaksDefineStop;
  BaseLineDefineStop;
  MarginsDefineStop;
  ShowPosStop;
  OverlayStop;

  case ADefineMode of
    dmPeaks: PeaksDefineStart;
    dmBaseLine: BaseLineDefineStart;
    dmMargins: MarginsDefineStart;
    dmShowPos: ShowPosStart;
    dmOverlay: OverlayStart;
  end;
  if ADefineMode = dmNone then
    DefineModePermanent := false;
  FDefineMode := ADefineMode;
  UpdateScreenCursor;
end;

{v0.08}
procedure TSpectrumForm.UpdateScreenCursor;
begin
  case FDefineMode of
    dmPeaks: Screen.Cursor := crThinCrossUp;
    dmBaseLine: Screen.Cursor := crThinCross;
    dmMargins: Screen.Cursor := crZoomIn;
    {v0.09}
    dmShowPos: Screen.Cursor := crThinCross;
    dmOverlay: Screen.Cursor := crDefault;{for now}
    {/v0.09}
  else
    Screen.Cursor := crDefault;
  end;
end;
{/0.08}

procedure TSpectrumForm.PaintBoxMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  p: TPoint;
  i: integer;
  editedProp: boolean;
  leftEdge:boolean;

  started:boolean;{some mouse action?}
begin
  started := false;
  DragStartCursor := Screen.Cursor;
  if Button = mbLeft then begin

    case FDefineMode of

      dmPeaks: begin
        if
          {v0.09}
          (IsInPeak(X, i) and (not PeakOverlapAllowed)) or
          {/v0.09IsInPeak(X, i) or }
          (not StartCreatePeak(X, Y)) then
         exit;
        {Screen.Cursor := crThinCross;{crHSplit;}
        started := true;
      end;


      dmBaseLine: begin
        if IsInBaseLine(X, i) and (not PeakOverlapAllowed) then begin
          {v0.07}
          if not BaseLineInsertPointStart(X) then
            exit;
          {/v0.07}
          exit;
        end;
        if (not BaseLineCreateStart(X, Y)) then begin
          exit;
        end;
        started := true;
        {
        if IsInBaseLine(X, i) then begin
          if ssShift in Shift then begin
            if not BaseLineMoveStart(X) then
              exit;
          end else begin
            if not BaseLineSizeStart(X) then
              exit;
          end;
        end else begin
          if not BaseLineCreateStart(X, Y) then
            exit;
        end;
        }
      end;

      dmMargins: begin
        {end else if DefiningMargins then begin}
        MarginML.Start(X, Y);
        started := true;
      end;
      {v0.10}
      dmShowPos: begin
        exit;
      end;
      {/v0.10}
    end;

    if not started then begin
      if IsInPeakEdge(X, Y, i) and StartSizePeak(X) then begin
        Screen.Cursor := crThinCrossUp;
        started := true;
      end else if IsInBaseLineEdge(X,Y, i, leftEdge) and BaseLineSizeStart(X, i, leftEdge) then begin
        Screen.Cursor := crThinCross{crHSplit};
        started := true;
      end else if XAxisBandPointIsIn(X, Y) and XAxisBandMoveStart(X) then begin
        started := true;
      end else if YAxisBandPointIsIn(X, Y) and YAxisBandMoveStart(Y) then begin
        started := true;
      end else if (Y < ScreenDisp.Top + PaintBox.Canvas.TextWidth('MMMMM'))
        and IsInPeak(X, i) then
      begin
        {ToggleSelectPeak(X, Shift);}
        if Spect.Peaks.Childs[i].IsFlagSet(rfSelected)
           and (not (ssShift in Shift))
           and (not (ssCtrl in Shift))
        then
          PeakEditProperties(i)
        else
          ToggleSelectPeak(X, Shift);
      end;
    end;

  end else if Button = mbRight then begin

    editedProp := false;
    {
    case FDefineMode of
      dmPeaks: begin
        if IsInPeak(X, i) then begin
          PeakEditProperties(i);
          editedProp := true;
        end;
      end;
      dmBaseLine: begin
        if IsInBaseLine(X, i) then begin
          BaseLineEditProperties(i);
          editedProp := true;
        end;
      end;
    end;
    }
    if not editedProp then begin
      p.X := X;
      p.Y := Y;
      p := PaintBox.ClientToScreen(p);
      PopupMenu.Popup(p.X, p.Y);
    end;

  end;
  if started then begin
    FDragging := true;
    {v0.20}
    LockDraw;
    {/v0.20}
  end;
end;

procedure TSpectrumForm.PaintBoxMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);

  procedure UpdateXY;
  var
    sp: TScreenPoint;
    ep: TExpPoint;
    u: TUserPoint;{ulantype}
  begin
    sp.X := X;
    sp.Y := Y;
    if Data.ScreenToExp(sp, ScreenDisp, ep) then begin
      Data.ExpToUser(ep, u);
      XYEdit.Text := FloatToStrF(u.X, ffFixed, 4, 2) + ';' + FloatToStrF(u.Y, ffFixed, 6, 4);
    end;
  end;

begin
   {if BaseLineDragStart >= 0 then begin
     Screen.Cursor := crVSplit;
     UndrawBaseLine;
     BaseLinePos := Y;
     DrawBaseLine;
   end else }
   case FDefineMode of

     dmMargins: begin
       if MarginML.IsOn then
         MarginML.Update(X, Y);
     end;

     dmPeaks: begin
       if PeakML.IsOn then
         PeakML.Update(X,Y);
     end;

     dmBaseLine: begin
       if BaseLineML.IsOn then
         BaseLineML.Update(X,Y);
     end;
     dmShowPos: begin
       if ShowPosML.IsOn then
         ShowPosML.Update(X,Y);
     end;
   else
     if BaseLineML.IsOn then
       BaseLineML.Update(X,Y)
     else if PeakML.IsOn then
       PeakML.Update(X,Y)
     else if MarginML.IsOn then
       MarginML.Update(X, Y);
   end;
   if SpectrumObjectVisible(soXY) then begin
     UpdateXY;
   end;
  LastMousePos.X := X;
  LastMousePos.Y := Y;
end;

procedure TSpectrumForm.PaintBoxMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  sp, ep: TPoint;

begin
  Screen.Cursor := DragStartCursor;
  if PeakML.IsOn then begin
    FinishPeak(X, Y, Shift);
    {
    case FDefineMode of
      dmBaseLine: Screen.Cursor := crThinCross;
      dmPeaks: Screen.Cursor := crThinCrossUp;
      dmMargins: Screen.Cursor := crZoomIn;
    else
      Screen.Cursor := crDefault;
    end;}
  end else if BaseLineML.IsOn then begin
    BaseLineFinish(X, Y, Shift);
    {
    case FDefineMode of
      dmBaseLine: Screen.Cursor := crThinCross;
      dmPeaks: Screen.Cursor := crThinCrossUp;
      dmMargins: Screen.Cursor := crZoomIn;
    else
      Screen.Cursor := crDefault;
    end;
    }
  end else if (FXAxisBandStartX <> -1) then begin
    XAxisBandMoveStop(X);
  end else if (FYAxisBandStartY <> -1) then begin
    YAxisBandMoveStop(Y);
  end else begin
    case FDefineMode of
      (*
      dmPeaks: begin
        if PeakML.IsOn then begin
          FinishPeak(X, Y, Shift);
          Screen.Cursor := crDefault;
        end;
      end;

      dmBaseLine: begin
        if BaseLineML.IsOn then begin
          BaseLineFinish(X, Y);
          Screen.Cursor := crDefault;
        end;
      end;
      *)

      dmMargins: begin
        if MarginML.IsOn then begin
          MarginML.Stop(X, Y);
          MarginML.GetResult(sp, ep);
          SetScreenSubView(sp.X, sp.Y, ep.X, ep.Y);
        end;
        if not DefineModePermanent then begin
          DefineMode := dmNone;
        end;
      end;

    end;
  end;
  {v0.20}
  if FDragging then begin
    UnlockDraw;
    FDragging := false;
  end;
  {/v0.20
  Dragging := false;}
end;

procedure TSpectrumForm.SetScreenSubView(X1,Y1,X2,Y2:integer);
var
  sc1,{sc,}
  sc2:TScreenPoint;ap1,ap2:TExpPoint;
  up1,up2:TUserPoint;
  ou:TUserViewLimit;
  li:TListInfo;
begin
  sc1.X := X1;
  sc1.Y := Y1;
  sc2.X := X2;
  sc2.Y := Y2;
  ou:= UserViewLimit;
  if Data = nil then
    exit;
  if not Data.ScreenToExp(sc1, OutDisp^, ap1) then
    exit;
  if not Data.ScreenToExp(sc2, OutDisp^, ap2) then
    exit;
  if abs(ap1.x - ap2.x) <= 0.001 then
    exit;
  if abs(ap1.y - ap2.y) <= 0.0001 then
    exit;
  if not Data.ExpToUser(ap1, up1) then
    exit;
  if not Data.ExpToUser(ap2, up2) then
    exit;
  {$IFDEF DEBUG}
  DebLog('SetSubView: ' + FloatToStrF(up1.x, ffFixed, 6, 3)
    + ',' + FloatToStrF(up1.y, ffFixed, 7, 5) + ' -> ' +
    FloatToStrF(up2.x, ffFixed, 6, 3) + ',' + FloatToStrF(up2.y, ffFixed, 7, 5));
  {$ENDIF}
  if up1.X < up2.X then begin
    UserViewLimit.Min.X := up1.X;
    UserViewLimit.Max.X := up2.X;
  end else if up1.X > up2.X then begin
    UserViewLimit.Min.X := up2.X;
    UserViewLimit.Max.X := up1.X;
  end else
    exit;
  if up1.Y < up2.Y then begin
    UserViewLimit.Min.Y := up1.Y;
    UserViewLimit.Max.Y := up2.Y;
  end else if up1.Y > up2.Y then begin
    UserViewLimit.Min.Y := up2.Y;
    UserViewLimit.Max.Y := up1.Y;
  end else
    exit;
  if UserLimitStack = nil then begin
    FillChar(li, sizeof(li), 0);
    li.RecordSize := sizeof(TUserViewLimit);
    li.Capacity := 100;
    ListInit(ltRecords or ltAutoDestroy, @li, UserLimitStack);
  end;
  if UserLimitStack <> nil then
    ListRecAdd(UserLimitStack, ou);
  UpdateViewLimit;
end;

procedure TSpectrumForm.PopScreenSubView;
begin
  if UserLimitStack = nil then
    exit;
  if ListRecPop(UserLimitStack, UserViewLimit) then
    UpdateViewLimit;
end;

function TSpectrumForm.SpectrumObjectVisible(so:TSpectrumObject):boolean;
var r:boolean;
begin
  r := false;
  if Data <> nil then begin
  case so of
    soAxis: r := Data.ULVO.ShowAxis;
    soPeaks: r := Data.ULVO.ShowPeaks;
    soBaseLine: r := Data.ULVO.ShowBaseline;
    soXY: r := Data.ULVO.ShowXY;
    soHeader: r := Data.ULVO.ShowHeader;
    soReport: r := Data.ULVO.ShowReport;
    soData: r := Data.ULVO.ShowData;
  end;
  end;
  SpectrumObjectVisible := r;
end;

procedure TSpectrumForm.SpectrumObjectShow(so:TSpectrumObject; OnOff:boolean);
begin
  {v0.13}
  if Data = nil then
    exit;
  {/v0.13}
  ChangingView := true;
  try
    case so of
      soData: begin
        Data.ULVO.ShowData := OnOff;
      end;
      soBaseLine: begin
        {BaseLineMenuItem.Checked := OnOff;}
        BaseLineCheckBox.Checked := OnOff;
        Data.ULVO.ShowBaseLine := OnOff;
        SetToRedraw(trBaseLine);
        Redraw;
      end;
      soAxis: begin
        Data.ULVO.ShowAxis := OnOff;
        {AxisItem.Checked := OnOff;}
        AxisCheckBox.Checked := OnOff;
        SetToRedraw(trAxis);
        Redraw;
      end;
      soReport: begin
        Data.ULVO.ShowReport := OnOff;
        if OnOff then
          ShowReport
        else
          HideReport;
        {ReportMenuItem.Checked := OnOff;}
        ReportCheckBox.Checked := OnOff;
      end;
      soPeaks: begin
        Data.ULVO.ShowPeaks := OnOff;
        if OnOff then
          PeaksShow
        else
          PeaksHide;
      end;
      soXY: begin
        Data.ULVO.ShowXY := OnOff;
        if OnOff then begin
          {ViewXYItem.Checked := true;}
          XYEdit.Visible := true;
        end else begin
          {ViewXYItem.Checked := false;}
          XYEdit.Visible := false;
        end;
      end;
      soHeader: begin
        Data.ULVO.ShowHeader := OnOff;
        if OnOff then begin
          {ViewHeaderItem.Checked := true;}
        end else begin
          {ViewHeaderItem.Checked := false;}
        end;
        SetToRedraw(trHeader);
        Redraw;

      end;
    end;
  finally
    ChangingView := false;
  end;
end;

function TSpectrumForm.BaseLineCreateStart(X,Y:integer): boolean;
var
  pp, cp, np: TScreenPeak;
  l, r{w}: integer;
  ind:integer;
begin
  Result := true;
  Spect.GetScreenBaseLineRec(X, OutDisp^, ind, pp, cp, np);
  if (cp.p1.X >= 0) and (not PeakOverlapAllowed) then begin
    Result := false;
    exit;
  end;

  BaseLineStartPoint.X := X;
  BaseLineStartPoint.Y := Y;
  BaseLineDefineMode := pdCreate;
  l := X;
  {w := OutDisp^.Width - (l - OutDisp^.Left);}
  r := OutDisp^.Right;
  if np.p1.X >= 0 then begin
    r := np.p1.X - 1;
    {w := w - (OutDisp^.Width - (np.p1.X - OutDisp^.Left));}
  end;
  BaseLineML.SetLimits(l, OutDisp^.Top, r{w}, OutDisp^.Bottom{Height});
  BaseLineML.Mode := mmVerticalColumn;
  BaseLineML.Start(X, Y);
end;

function TSpectrumForm.BaseLineMoveStart(X: integer): boolean;
var
  pp,cp,np: TScreenPeak;
  l,r{w}:integer;
  start, size:TPoint;
  ind:integer;
begin
  Result := true;
  BaseLineStartPoint.X := X;
  BaseLineDefineMode := pdMove;

  Spect.GetScreenBaseLineRec(X, OutDisp^, ind, pp, cp, np);
  if cp.P1.X < 0 then begin
    Result := false;
    exit;
  end;
  l := OutDisp^.Left;
  if pp.p2.X >= 0 then
    l := pp.p2.X;
  {w := OutDisp^.Width - (l - OutDisp^.Left);}
  r := OutDisp^.Right;
  if np.p1.X >= 0 then begin
    {w := w - (OutDisp^.Width + OutDisp^.Left - np.p1.X);}
    r := np.p1.X - 1;
  end;
  BaseLineML.SetLimits(l, OutDisp^.Top, r{w}, OutDisp^.Bottom{OutDisp^.Height});

  BaseLineML.Mode := mmGraphObject;
  start.X := cp.p1.X;
  start.Y := OutDisp^.Top;
  size.X := (cp.p2.X - cp.p1.X);
  size.Y := OutDisp^.Bottom - OutDisp^.Top + RectInclusive{OutDisp^.Height};
  BaseLineML.SetObj(otVerticalColumn, start, size);
  BaseLineML.Start(X, OutDisp^.Top);
end;

function TSpectrumForm.BaseLineSizeStart(X: integer{v0.09}; Index:integer; LeftEdge:boolean{v0.09}): boolean;
var
  pp, cp, np: TScreenPeak;{previous peek, current peak, nextpeak}
  l,r: integer;
  dispw: integer;
  bcr: integer;
  br: TULBRObj;
  {v0.09}{/v0.09 index:integer;{/v0.09}
begin
  Result := false;
  {v0.09}
  if Index < 0 then begin
    exit;
  end;
  Spect.GetScreenBaseLineNeighbours(Index, OutDisp^, pp, cp, np);
  if ((cp.P1.X < 0) and (cp.P2.X < 0)) then begin
    exit;
  end;
  FBaseLineIndex := Index;
  FBaseLineLeftEdge := LeftEdge;
  {/0.09
  Spect.GetScreenBaseLineRecs(X, OutDisp^, ind, pp, cp, np);
  if ((cp.P1.X < 0) and (cp.P2.X < 0)) or (ind < 0) then begin
    Result := false;
    exit;
  end;
  }
  BaseLineStartPoint.X := X;
  BaseLineSizingTwo := false;
  dispW := OutDisp^.Right - OutDisp^.Left;
  bcr := BaseLineCircleRadius(dispW);

  br := TULBRObj(Spect.BaseLine.Childs[Index]);
  if {v0.09} not LeftEdge {/v0.09 (X - cp.P1.X) > (cp.P2.X - X)} then begin
    BaseLineDefineMode := pdSizeRight;
    l := cp.P1.X;
    if l < OutDisp.Left then
      l := OutDisp.Left;

    r := OutDisp^.Right;
    if np.p1.X >= 0 then begin
      if (br.GroupedRight or BaseSplittingAllowed) and (abs(cp.p2.x - np.p1.X) < bcr) then
      begin
        BaseLineSizingTwo := true;
        r := np.p2.X - 1
      end else begin
        r := np.p1.X - 1;
      end;
    end;
    BaseLineML.SetLimits(l, OutDisp^.Top, r{w}, OutDisp^.Bottom{OutDisp^.Height});
    BaseLineML.Mode := mmVertical;{mmVerticalColumn;}
    BaseLineML.Start(X{l}, cp.p1.Y);
  end else begin
    BaseLineDefineMode := pdSizeLeft;
    l := OutDisp^.Left;
    if pp.p2.X >= OutDisp^.Left then begin
      if (br.GroupedLeft or BaseSplittingAllowed) and (abs(cp.p1.x - pp.p2.X) < BaseLineCircleRadius(dispW))then
      begin
        BaseLineSizingTwo := true;
        if pp.p1.X >= OutDisp^.Left then
          l := pp.p1.X + 1
        else
          l := 0;
      end else begin
        l := pp.P2.X;
      end;
    end;
    {w := OutDisp^.Width - (l - OutDisp^.Left) -
         (OutDisp^.Left + OutDisp^.Width - cp.p2.X);}
    r := cp.P2.X;
    BaseLineML.SetLimits(l, OutDisp^.Top, r{w}, OutDisp^.Bottom);
    BaseLineML.Mode := mmVertical;{mmVerticalColumn;}
    BaseLineML.Start(X{cp.p2.X}, cp.p2.Y);
  end;
  Result := true;
end;

function TSpectrumForm.IsInBaseLine(X: integer; var Index: integer): boolean;
begin
  IsInBaseLine := (Spect <> nil) and
    (Spect.IsScreenXInBaseLine(X, OutDisp^, Index));
end;

function TSpectrumForm.IsInBaseLineEdge(var X: integer; Y: integer; var Index: integer; var LeftEdge:boolean): boolean;
begin
  IsInBaseLineEdge := Spect.IsScreenPointInBaseLineEdge(X,Y, OutDisp^, Index, LeftEdge);
end;


procedure TSpectrumForm.BaseLineAbort;
begin
      {
      DoPeaksSizeChanged;
      SetToRedraw(trBaseLine);
      Redraw;
      }
  if BaseLineML.IsOn then
    BaseLineML.Stop(0,0);
  if not DefineModePermanent then begin
    if DefineMode = dmBaseLine then
      DefineMode := dmNone;
  end else begin
    BaseLineDefineMode:= pdReady;
    BaseLineStartPoint.X := -1;
  end;
end;

procedure TSpectrumForm.BaseLineFinish(X,Y:integer; Shift: TShiftState);
var
  p, start,stop:TPoint;
{  i: integer;}
begin
  try
    if BaseLineStartPoint.X >= 0 then begin
      if abs(BaseLineStartPoint.X - X) <= 2 then begin
        BaseLineML.Stop(X, Y);
        BaseLineML.GetResult(p, BaseLineStopPoint);
        case BaseLineDefineMode of
          pdInsertPoint: begin
            BaseLineInsertPoint(X);
          end;
        else
          BaseLineToggleSelect(X, Y, Shift);
          exit;
        end;
      end else begin
        BaseLineML.Stop(X, Y);
        BaseLineML.GetResult(p, BaseLineStopPoint);
        case BaseLineDefineMode of
          pdInsertPoint: begin
            BaseLineInsertPoint(X);
          end;
          pdCreate: begin
            BaseLineCreate(BaseLineStartPoint, BaseLineStopPoint);
          end;
          pdMove: begin
            BaseLineML.GetObj(start, p, stop);
            BaseLineMove(start.X, stop.X);
          end;
          pdSizeRight: begin
            BaseLineSize(BaseLineStartPoint.X, BaseLineStopPoint.X, true, BaseLineSizingTwo);
          end;
          pdSizeLeft: begin
            BaseLineSize(BaseLineStartPoint.X, BaseLineStopPoint.X, false, BaseLineSizingTwo);
          end;
        end;
      end;
      DoPeaksSizeChanged;
      SetToRedraw(trBaseLine);
      Redraw;
    end;

  finally
    if not DefineModePermanent then begin
      if DefineMode = dmBaseLine then
        DefineMode := dmNone;
    end else begin
      BaseLineDefineMode:= pdReady;
      BaseLineStartPoint.X := -1;
    end;
  end;
end;

procedure TSpectrumForm.BaseLineToggleSelect(X,Y: integer; Shift: TShiftState);
begin
  Spect.ToggleSelectScreenBaseLine(X, Y, OutDisp^, Shift);
  SetToRedraw(trBaseLine);
  Redraw;
end;

procedure TSpectrumForm.BaseLineMove(OldStartX, NewStartX:integer);
var p:integer;
begin
  Spect.MoveScreenBaseLine(OldStartX, NewStartX, OutDisp^, p);
end;

procedure TSpectrumForm.BaseLineSize(OldStopX, NewStopX:integer; Right: boolean; SizingTwo:boolean);
var
  p:integer;
begin {spectrum}
  Spect.SizeScreenBaseLine(OldStopX, NewStopX, OutDisp^, p, right, SizingTwo);
end;

procedure TSpectrumForm.BaseLineInsertPoint(X: integer);
begin
  Spect.BaseLineInsertPointFromScreen(OutDisp^, X);{spectrum}
end;

procedure TSpectrumForm.BaseLineCreate(const AStartPoint, AStopPoint:TPoint);
var ip1,ip2:TIndexPoint;
begin
  if AStartPoint.X >= AStopPoint.X then
    exit;
  if abs(AStartPoint.X - AStopPoint.X) < MousePointArea then
    exit;
  if ScreenPointToIndexPoint(AStartPoint, ip1) and
     ScreenPointToIndexPoint(AStopPoint, ip2)
  then begin
    Spect.BaseLineCreate(ip1, ip2);
  end;
end;

function TSpectrumForm.StartCreatePeak(X,Y:integer): boolean;
var
  pp,cp,np: TScreenPeak;
  l, r{w}: integer;
begin
  Result := true;
  Spect.GetScreenPeaks(X, OutDisp^, pp, cp, np);
  if cp.p1.X >= 0 then begin
    Result := false;
    exit;
  end;

  PeakStartPoint.X := X;
  PeakStartPoint.Y := Y;
  PeakDefineMode := pdCreate;

  l := X;
  {w := OutDisp^.Width - (l - OutDisp^.Left);}
  r := OutDisp^.Right;
  if np.p1.X >= 0 then begin
    {w := w - (OutDisp^.Width - (np.p1.X - OutDisp^.Left));}
    r := np.p1.X - 1;
  end;
  PeakML.SetLimits(l, OutDisp^.Top, r{w}, OutDisp^.Bottom{OutDisp^.Height});
  PeakML.Mode := mmVerticalColumn;
  PeakML.Start(X, Y);
end;

function TSpectrumForm.IsPeakStart(X: integer): boolean;
var p:integer;
begin
  IsPeakStart := (Spect <> nil) and
    (Spect.IsScreenPeakStart(X, OutDisp^, p));
end;

function TSpectrumForm.IsInPeak(X: integer; var PeakIndex: integer): boolean;
begin
  IsInPeak := (Spect <> nil) and
    (Spect.IsScreenXInPeak(X, OutDisp^, PeakIndex));
end;

function TSpectrumForm.IsInPeakEdge(X,Y: integer; var PeakIndex:integer):boolean;
      { Is given screen coordinates inside edge sign of any peak? }
begin
  IsInPeakEdge := Spect.IsScreenPointInPeakEdge(X, Y, OutDisp^, PeakIndex);
end;


function TSpectrumForm.IsPeakStop(X: integer): boolean;
var p:integer;
begin
  IsPeakStop := (Spect <> nil) and
    (Spect.IsScreenPeakStop(X, OutDisp^, p));
end;

function TSpectrumForm.StartMovePeak(X: integer): boolean;
var
  pp,cp,np: TScreenPeak;
  l,r{w}:integer;
  start, size:TPoint;
begin
  Result := true;
  PeakStartPoint.X := X;
  PeakDefineMode := pdMove;

  Spect.GetScreenPeaks(X, OutDisp^, pp, cp, np);
  if cp.P1.X < 0 then begin
    Result := false;
    exit;
  end;
  l := OutDisp^.Left;
  if pp.p2.X >= 0 then
    l := pp.p2.X;
  {w := OutDisp^.Width - (l - OutDisp^.Left);}
  r := OutDisp^.Right;
  if np.p1.X >= 0 then begin
    {w := w - (OutDisp^.Width + OutDisp^.Left - np.p1.X);}
    r := np.P1.X - 1;
  end;
  PeakML.SetLimits(l, OutDisp^.Top, r{w}, OutDisp^.Bottom{OutDisp^.Height});

  PeakML.Mode := mmGraphObject;
  start.X := cp.p1.X;
  start.Y := OutDisp^.Top;
  size.X := (cp.p2.X - cp.p1.X);
  size.Y := OutDisp^.Bottom - OutDisp^.Top + 1;{OutDisp^.Height;}
  PeakML.SetObj(otVerticalColumn, start, size);
  PeakML.Start(X, OutDisp^.Top);
end;

function TSpectrumForm.StartSizePeak(X:integer): boolean;
var
  pp,cp,np: TScreenPeak;{previous peek, current peak, nextpeak}
  l,r{w}:integer;
begin
  Result := true;
  PeakStartPoint.X := X;
{  PeakDefineMode := pdSize;}
  Spect.GetScreenPeaks(X, OutDisp^, pp, cp, np);
  if (cp.P1.X < 0) and (cp.P2.X < 0) then begin
    Result := false;
    exit;
  end;
  if (X - cp.P1.X) > (cp.P2.X - X) then begin
    PeakDefineMode := pdSizeRight;
    l := cp.P1.X;
    {w := OutDisp^.Width - (l - OutDisp^.Left);}
    r := OutDisp^.Right;
    if np.p1.X >= 0 then begin
      {w := w - (OutDisp^.Width + OutDisp^.Left - np.p1.X);}
      r := np.p1.X - 1;
    end;
    PeakML.SetLimits(l, OutDisp^.Top, r{w}, OutDisp^.Bottom);
    PeakML.Mode := mmVertical;{mmVerticalColumn;}
    PeakML.Start(X{cp.p1.X}, cp.p1.Y);
  end else begin
    PeakDefineMode := pdSizeLeft;
    l := OutDisp^.Left;
    if pp.p2.X >= 0 then begin
      l := pp.P2.X;
    end;
    {w := OutDisp^.Width - (l - OutDisp^.Left) -
         (OutDisp^.Left + OutDisp^.Width - cp.p2.X);}
    r := cp.p2.X;

    PeakML.SetLimits(l, OutDisp^.Top, r, OutDisp^.Bottom);
    PeakML.Mode := mmVertical;{mmVerticalColumn;}
    PeakML.Start(X{cp.p2.X}, cp.p2.Y);
  end;
end;

procedure TSpectrumForm.MovePeak(OldStartX, NewStartX:integer);
var p:integer;
begin
  Spect.MoveScreenPeak(OldStartX,NewStartX, OutDisp^, p);
end;

procedure TSpectrumForm.SizePeak(OldStopX, NewStopX:integer; Right: boolean);
var
  p:integer;
begin
  Spect.SizeScreenPeak(OldStopX,NewStopX, OutDisp^, p, right);
end;

procedure TSpectrumForm.ToggleSelectPeak(X: integer; Shift: TShiftState);
begin
  Spect.ToggleSelectScreenPeak(X, OutDisp^, Shift);
  SetToRedraw(trPeaks);
  Redraw;
end;


procedure TSpectrumForm.FinishPeak(X, Y: integer; Shift: TShiftState);
var p, start,stop:TPoint;
begin
  if PeakStartPoint.X >= 0 then begin
    PeakML.Stop(X, Y);
    PeakML.GetResult(p, PeakStopPoint);
    Spect.Peaks.DoChangeLock;
    try
      if PeakStartPoint.X = PeakStopPoint.X then begin
        ToggleSelectPeak(PeakStartPoint.X, Shift);
      end else begin
        case PeakDefineMode of
          pdCreate: begin
            CreatePeak(PeakStartPoint, PeakStopPoint);
          end;
          pdMove: begin
            PeakML.GetObj(start, p, stop);
            MovePeak(start.X, stop.X);
          end;
          pdSizeRight: begin
            SizePeak(PeakStartPoint.X, PeakStopPoint.X, true);
          end;
          pdSizeLeft: begin
            SizePeak(PeakStartPoint.X, PeakStopPoint.X, false);
          end;
        end;
        DoPeaksSizeChanged;
        {CreatePeakReport;}
      end;
    finally
      Spect.Peaks.DoChangeUnlock;
    end;
    SetToRedraw(trPeaks);
    Redraw;
  end;
  if not DefineModePermanent then begin
    if DefineMode = dmPeaks then
      DefineMode := dmNone;
  end else begin
    PeakDefineMode:= pdReady;
    PeakStartPoint.X := -1;
  end;
end;

procedure TSpectrumForm.CreatePeak(const AStartPoint, AStopPoint:TPoint);
var ip1,ip2:TIndexPoint;
begin
  if AStartPoint.X >= AStopPoint.X then
    exit;
  if abs(AStartPoint.X - AStopPoint.X) < MousePointArea then
    exit;
  if ScreenPointToIndexPoint(AStartPoint, ip1) and
     ScreenPointToIndexPoint(AStopPoint, ip2)
  then begin
    Spect.CreatePeak(ip1, ip2);
  end;
end;

procedure TSpectrumForm.PeakEditProperties(PeakIndex: integer);
begin
  Spect.Peaks.Childs[PeakIndex].Edit;
end;

procedure TSpectrumForm.BaseLineEditProperties(PeakIndex: integer);
begin
  Spect.BaseLine.Childs[PeakIndex].Edit;
end;

procedure TSpectrumForm.ShowReport;
begin
  {v0.20}
  if FCreating then
    exit;
  {/v0.20}
  ReportPanel.Show;
  CreatePeakReport;
  Data.ULVO.ShowReport := true;
{  ReportMenuItem.Checked := true;}
end;

procedure TSpectrumForm.HideReport;
begin
  ReportPanel.Hide;
  Data.ULVO.ShowReport := false;
{  ReportMenuItem.Checked := false;}
end;

procedure TSpectrumForm.DoPeaksSizeChanged;
begin
  CreatePeakReport;
end;

procedure TSpectrumForm.CreatePeakReport;

  procedure StartReport;
  begin
    ReportMemo.Lines.Add('File: ' + GetFileNamePart(fpBaseName, Data.ULF.FileName));
    ReportMemo.Lines.Add('Time[min.] Area[%]')
  end;

  procedure AddPeak(p:TULPRObj);
  var
    up:TUserPoint;
    ap:TExpPoint;
  begin
    ap.X := p.X;
    ap.Y := p.Height;
    Data.ExpToUser(ap, up);
    ReportMemo.Lines.Add(RealToString(up.X, 10, 2) + RealToString(p.Ratio * 100, 7, 2));
  end;

  procedure FinishReport;
  begin
  end;

var
  i:integer;

begin
  {v0.20}
  if Spect = nil then
    exit;
  {/v0.20
  FDataChangeFromForm :=true;
  try}
    Spect.RecalculatePeaks;
  {v0.20}{/v0.20 finally
    FDataChangeFromForm := false;
  end;}

  if not ReportPanel.Visible then
    exit;

  ReportMemo.Lines.Clear;
  StartReport;
  for i := 0 to Spect.Peaks.ChildCount - 1 do
    AddPeak(TULPRObj(Spect.Peaks.Childs[i]));
  FinishReport;
end;

procedure TSpectrumForm.PeaksMenuItemClick(Sender: TObject);
begin
{  if PeaksMenuItem.Checked then
    PeaksHide
  else
    PeaksShow;}
end;

procedure TSpectrumForm.DefineSpeedButtonClick(Sender: TObject);
var
  dm: TDefineMode;
  cm: integer;
begin
  {v0.09}
  if FIsInSpeedButtonClick then
    exit;
  {/v0.09}
  try
    FIsInSpeedButtonClick := true;
    dm := TDefineMode(TSpeedButton(Sender).Tag);
    if FSpeedButtonShift then
      cm := cmPermamentDefineModeSet
    else
      cm := cmDefineModeSet;
    if DefineMode = dm then begin
      dm := dmNone;
    end else begin
      {PostMessage(Handle, WM_APPMESSAGE, cmDefineModeSet, longint(dm));}
    end;
    PostMessage(Handle, WM_APPMESSAGE, cm, longint(dm));
  finally
    FIsInSpeedButtonClick := false;
  end;
end;

procedure TSpectrumForm.MarginsDefineStart;
begin
  ZoomIn_Btn.Down := true;
{  Screen.Cursor := crZoomIn; called from UpdateScreenCursor }
end;

procedure TSpectrumForm.MarginsDefineStop;
begin
  ZoomIn_Btn.Down := false;
  Screen.Cursor := crDefault;
end;

procedure TSpectrumForm.BaseLineDefineStart;
begin
  BaseLineCreate_Btn.Down := true;
  BaseLineCreate_Item.Checked := true;
  SpectrumObjectShow(soBaseline, true);
  {v0.07}
{  Screen.Cursor := crThinCross; called from UpdateScreenCursor}
  {/v0.07}
{  BaseLineMenuItem.Checked := true;
  BaseLineCheckBox.Checked := true;
  SetToRedraw(trBaseLine);
  Redraw;}
end;

procedure TSpectrumForm.BaseLineDefineStop;
begin
  BaseLineCreate_Btn.Down := false;
  BaseLineCreate_Item.Checked := false;
  {v0.07}
  Screen.Cursor := crDefault;
  {/v0.07}
  {v0.11}
  BaseLineDefineMode := pdNone;
  {/v0.11}
end;

procedure TSpectrumForm.ReportPanelMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
const
  SC_DragMove = $F012;
begin
  ReleaseCapture;
  ReportPanel.Perform(WM_SysCommand, SC_DragMove, 0);
end;


procedure TSpectrumForm.ZoomOut_BtnClick(Sender: TObject);
begin
  PopScreenSubView;
end;

procedure TSpectrumForm.StopButtonClick(Sender: TObject);
begin
  {v0.19}
  if FAcqInfo <> nil then
    FAcqInfo.Stop;
  {/v0.19
  Stop;}
end;

procedure TSpectrumForm.RunButtonClick(Sender: TObject);
begin
  {v0.19}
  if FAcqInfo <> nil then
    FAcqInfo.Run;
  {/v0.19
  Run;}
end;

procedure TSpectrumForm.BaseLineCheckBoxMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  PostMessage(Handle, WM_APPMESSAGE, cmSpectrumObjectShow + soBaseLine,
    longint(not Data.ULVO.ShowBaseLine));
end;

procedure TSpectrumForm.AxisCheckBoxMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  PostMessage(Handle, WM_APPMESSAGE, cmSpectrumObjectShow + soAxis,
    longint(not Data.ULVO.ShowAxis));
end;

procedure TSpectrumForm.PeaksCheckBoxMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  PostMessage(Handle, WM_APPMESSAGE, cmSpectrumObjectShow + soPeaks,
    longint(not Data.ULVO.ShowPeaks));
end;

procedure TSpectrumForm.ReportCheckBoxMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  PostMessage(Handle, WM_APPMESSAGE, cmSpectrumObjectShow + soReport,
    longint(not Data.ULVO.ShowReport));
end;

procedure TSpectrumForm.FilterMenuItemClick(Sender: TObject);
begin
  if Data.ULMF.Edit = mrOK then
    Data.FilterUpdate;
{
  GetFilterPars(fp);
  FilterParsForm.ParamsToControls(fp);
  if FilterParsForm.ShowModal = mrOK then begin
    FilterParsForm.ControlsToParams(fp);
    Data.SetFilterPars(fp);
    SetToRedraw(trData);
    Redraw;
  end;
  }
end;

procedure TSpectrumForm.HeaderItemClick(Sender: TObject);
begin
  Data.ULA.Edit;
end;

procedure TSpectrumForm.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  {v0.19}
  if FAcqInfo <> nil then
    CanClose := FAcqInfo.CanClose;
  {/v0.19
  if not VerifyAbort then
    exit;
  if (FAcqInfo <> nil) then with FAcqInfo do
  if RunningState <> rsStopped then begin
    if ShowMessage('Abort acquisition?', smNoYes, 0) <> cmYes then begin
      CanClose := false;
      exit;
    end;
    Stop;
  end;
  if (Data <> nil) and (Data.ULF <> nil) then begin
    if (pos('NONAME', Data.ULF.FileName) <> 0) then
    begin

      if Data.ULAD.Data.Size > 0 then begin
        if not FMH.SaveAs then begin
          if ShowMessage('Discard data?', smNoYes, 0) <> cmYes then begin
            CanClose := false;
          end else begin
            Data.AcquiredDataDiscard;
          end;
        end;
      end;

    end else begin
      if Data.ULF.Modified then begin
        if Data.ULF.ReadOnly then begin
          case ShowMessage(Data.ULF.FileName + ' was opened Read Only but modified. ' +
            'Save to other file?', smYesNoCancel, 0) of
            cmCancel: CanClose := false;
            cmYes: begin
              if not FMH.SaveAs then begin
                CanClose := false;
              end else begin
                Data.ULF.Modified := false;
              end;
            end;
            cmNo: Data.ULF.Modified := false;
          end;
        end else begin
          case ShowMessage(Data.ULF.FileName, smFileModifiedSave,0) of
            cmCancel: CanClose := false;
            cmNo: Data.ULF.Modified := false;
          end;
        end;
      end;
    end
  end;}
end;

procedure TSpectrumForm.PeaksDeleteLastItemClick(Sender: TObject);
var c: TComponent;
begin
  if Spect.Peaks.ChildCount > 0 then begin
    c := Spect.Peaks.Components[Spect.Peaks.ComponentCount-1];
    c.Free;
    DoPeaksSizeChanged;
    SetToRedraw(trPeaks);
    Redraw;
  end;
end;

{v0.19}
procedure TSpectrumForm.DoAfterAutodetect;
begin
  {v0.20}
  LockDraw;
  try
  {/v0.20}
    SpectrumObjectShow(soPeaks, true);
    SpectrumObjectShow(soBaseLine, true);
    SetToRedraw(trPeaks);
    {v0.20}
    ShowReport;
    {/v0.20}
    Redraw;
  {v0.20}
  finally
    UnlockDraw;
  end;
  {/v0.20}
end;
{/v0.19}

procedure TSpectrumForm.Autodetect1Click(Sender: TObject);
begin
  {v0.19}
  LockDraw;
  try
    Spect.Autodetect;
    DoAfterAutodetect;
  finally
    UnlockDraw;
  end;
  {/v0.19
  InfoFormShow('Autodetecting peaks...');
  LockDraw;
  try
    Spect.Autodetect;
    SpectrumObjectShow(soPeaks, true);
    SpectrumObjectShow(soBaseLine, true);
    SetToRedraw(trPeaks);
    Redraw;
  finally
    InfoFormHide;
    UnlockDraw;
  end;   }
end;

procedure TSpectrumForm.PeaksBrowseItemClick(Sender: TObject);
begin
  Spect.Peaks.Browse;
end;

procedure TSpectrumForm.BaseLineCreate_ItemClick(Sender: TObject);
begin
  if DefineMode <> dmBaseLine then
    DefineMode := dmBaseLine
  else
    DefineMode := dmNone;
end;

procedure TSpectrumForm.BaseLineBrowse_ItemClick(Sender: TObject);
begin
  Spect.BaseLine.Browse;
end;

procedure TSpectrumForm.PeaksDefineItemClick(Sender: TObject);
begin
  if DefineMode = dmPeaks then
    DefineMode := dmNone
  else
    DefineMode := dmPeaks;
end;

procedure TSpectrumForm.UpdatePrinterDisp(APrinter: TAlPrinter;
  ADefault:boolean; AReportType: TReportType);
var tw, th: integer; {prnutl}
begin
  if ADefault then begin
    case AReportType of
      rtWindow: APrinter.Orientation := poLandscape;
      rtReport: APrinter.Orientation := poPortrait;
    end;
  end else begin
    {$IFDEF ALPRINTER}
    APrinter.DoAfterPrinterSetupDialog;
    {$ENDIF}
  end;
  tw := APrinter.Canvas.TextWidth('M');
  th := APrinter.Canvas.TextHeight(' ');
  PrinterDisp.Top := 4 * th;
  PrinterDisp.Left := 20 * tw;
  {PrinterDisp.Width := APrinter.PageWidth - 30 * tw;
   PrinterDisp.Height := APrinter.PageHeight - 12 * th;}
  PrinterDisp.Right := PrinterDisp.Left + APrinter.PageWidth - 30 * tw - RectInclusive;
  PrinterDisp.Bottom := PrinterDisp.Top + APrinter.PageHeight - 12 * th - RectInclusive;
  if Data <> nil then begin
    SetToRedraw(trAll);
  end;
end;

procedure TSpectrumForm.CalcSpectrumDisp(ACanvas: TCanvas; AWidth: integer;
  AHeight: integer; var ADisp: TScreenDisp);
var
  tw, th: integer;
begin
  tw := ACanvas.TextWidth('M');
  th := ACanvas.TextHeight(' ');
  ADisp.Top := 4 * th;
  ADisp.Left := 20 * tw;
  ADisp.Right := ADisp.Left + AWidth - 30 * tw - RectInclusive;
  ADisp.Bottom := ADisp.Top + AHeight - 12 * th - RectInclusive;
end;

procedure TSpectrumForm.DrawHeader(ACanvas: TCanvas; const ADisp: TScreenDisp);
var
  cw,ch, hd, wd:integer;
  x,y:integer;
{  s: string;}
begin
  with ACanvas do begin
    cw := TextWidth('M');
    ch := TextHeight('M');
    wd := cw;
    hd := ch div 4;
    x := ADisp.Left + hd;
    y := ADisp.Top + wd;
    MoveTo(x, y);
    TextOut(x,y, DateTimeToStr(Now) + ' ' + Data.ULA.SampleName);
    inc(y, hd + ch);
    TextOut(x,y, Data.ULA.SampleDesc);
  end;
end;

function TSpectrumForm.Printing: boolean;
begin
  Printing := {(FDataPrinter <> nil)}OutDisp <> @ScreenDisp;
end;


procedure TSpectrumForm.PrintWindow(prn: TAlPrinter);
begin
  with prn do begin
    BeginDoc;{messages}
    DrawSpectrum(prn.Canvas, PrinterDisp);
    EndDoc;
    if prn.Previewing then
      PreviewPages(prn.Pages);
  end;
end;


procedure TSpectrumForm.PrintReport(prn: TAlPrinter);
var
  rep: TAlReport;
  frm: tForm;
  areaSum : extended;
{  peakCount: integer;}

  im: TAlImage;
  mf: TDrawMetaFile;
  imdisp: TScreenDisp;

  i: integer;

  p: TULPRObj;
const
  repname = 'report.dfm';

  procedure AddPeak(p:TULPRObj);
  var
    up:TUserPoint;
    ap:TExpPoint;
  begin
    ap.X := p.X;
    ap.Y := p.Height;
    Data.ExpToUser(ap, up);
    {ReportMemo.Lines.Add( + RealToString(p.Ratio * 100, 7, 2));}

    rep.AddNamedValue('PeakNr',  IntToStr(i+1));
    rep.AddNamedValue('PeakName', p.PeakName);
    rep.AddNamedValue('Time', RealToString(up.X, 10, 2));
    rep.AddNamedValue('Area', RealToString(p.AreaSize, 10, 4));
    rep.AddNamedValue('Percent', RealToString(p.Ratio * 100, 7, 2));
    {v0.11}
    rep.AddNamedValue('Response', RealToString(p.Response, 10, 4));
    rep.AddNamedValue('Amount', RealToString(p.Amount, 10, 4));
    {/v0.11}
    areaSum := areaSum + p.AreaSize;
  end;


begin
  CreatePeakReport;
  FPrintingReport := true;
  try
    {Spect.RecalculatePeaks;}
    if LoadReport(ReportDir + repname, rep, frm) then {see testapp}
    begin
      areaSum := 0;
      rep.AddNamedValue('SampleName', Data.ULA.SampleName);
      rep.AddNamedValue('SampleDesc', Data.ULA.SampleDesc);
      // details
      for i := 0 to Spect.Peaks.ChildCount - 1 do begin
        p := TULPRObj(Spect.Peaks.Childs[i]);
        AddPeak(p);
      end;
      rep.AddNamedValue('PeakCount', IntToStr(Spect.Peaks.ChildCount));
      rep.AddNamedValue('AreaSum', RealToString(areaSum, 10, 4));

      with TFrmPreview.Create(Application) do
      begin
        im := TAlImage(frm.FindComponent('ReportImage'));
        if im <> nil then begin
          mf := TDrawMetaFile.Create(im.Width, im.Height);
          CalcSpectrumDisp(mf.Canvas, im.Width, im.Height, imdisp);
          DrawSpectrum(mf.Canvas, imdisp);
          mf.Close;
          im.Picture.Assign(TMetaFile(mf));
          mf.Free;
        end;
        rep.MakePreviewPages(Previewer.Pages);
        FirstPage;
      end;
      // cleanup
      frm.Release;
    end else begin
      MessageDlg (Format('could not load report %s',[repname]),
                   mtError,[mbOK],0);
    end;
  finally
    FPrintingReport := false;
  end;
end;

procedure TSpectrumForm.Print(WithPreview:boolean; AReportType: TReportType);
var prn: TAlPrinter;
begin
  OutDisp := @PrinterDisp;
  try
    AlPrinter.Previewing := WithPreview;
    UpdatePrinterDisp(AlPrinter, true, AReportType);
      { set default values to Printer, update OutDisp parameters }
    if PrinterSetupDialog.Execute then begin
      FDataPrinter := TDataPrinter.Create;
      prn := FDataPrinter.Prn;
      UpdatePrinterDisp(prn, false, AReportType);
        { just update OutDisp parameters according to the values set in
          PrinterSetupDialog }
      case AReportType of
        rtWindow: PrintWindow(prn);
        rtReport: PrintReport(prn);
      end;
    end;
  finally
    FDataPrinter.Free;
    FDataPrinter := nil;
    OutDisp := @ScreenDisp;
    SetToRedraw(trAll);
    Redraw;
  end;
end;

procedure TSpectrumForm.PrintItemClick(Sender: TObject);
begin
{  Print(false);}
end;

procedure TSpectrumForm.ViewXYItemClick(Sender: TObject);
begin
{  SpectrumObjectShow(soXY, not ViewXYItem.Checked);}
end;

procedure TSpectrumForm.PeaksDeleteSelectedItemClick(Sender: TObject);
{var
  i: integer;
  p: TULPRObj;}
begin
  Spect.Peaks.ChildsDelete(ULPRID, rfSelected);
  DoPeaksSizeChanged;
  SetToRedraw(trPeaks);
  Redraw;
end;

procedure TSpectrumForm.ViewHeaderItemClick(Sender: TObject);
begin
{  SpectrumObjectShow(soHeader, not ViewHeaderItem.Checked);}
end;

procedure TSpectrumForm.View_ItemClick(Sender: TObject);
begin
  Data.ULVO.Edit;
end;

procedure TSpectrumForm.MethodEditItemClick(Sender: TObject);
begin
  Data.ULM.Edit;
end;

procedure TSpectrumForm.MethodPeaksClearItemClick(Sender: TObject);
begin
  Data.ULM_ULP.Clear;
end;

procedure TSpectrumForm.MethodPeaksBrowseItemClick(Sender: TObject);
begin
  Data.ULM_ULP.Browse;
end;

procedure TSpectrumForm.PeaksToMethodItemClick(Sender: TObject);
begin
  Spect.CopySelectedPeaksToMethod;
end;

function TSpectrumForm.PeakStickLen(ADispWidth: integer): integer;
begin
  if Printing then begin
    PeakStickLen := ADispWidth div 90{round(MMPeakStickLen / MMPerInch * FDataPrinter.PPIY)}
  end else begin
    PeakStickLen := PointsPeakStickLen;
  end;
end;

function TSpectrumForm.PeakArrowLen(ADispWidth: integer): integer;
begin
  if Printing then begin
    PeakArrowLen := ADispWidth div 90{round(MMPeakArrowLen /MMPerInch * FDataPrinter.PPIX)}
  end else begin
    PeakArrowLen := PointsPeakArrowLen;
  end;
end;

function TSpectrumForm.BaseLineCircleRadius(ADispWidth: integer): integer;
begin
  if Printing then begin
    BaseLineCircleRadius := ADispWidth div 180 {round(MMBaseLineCircleRadius /MMPerInch * FDataPrinter.PPIX)}
  end else begin
    BaseLineCircleRadius := PointsBaseLineCircleRadius;
  end;
end;

procedure TSpectrumForm.BaseLineCopyToMethod_ItemClick(Sender: TObject);
begin
  Spect.CopySelectedBaseLineToMethod;
end;

procedure TSpectrumForm.MethodBaseLineClearItemClick(Sender: TObject);
begin
  Data.ULM_ULB.Clear;
end;

procedure TSpectrumForm.MethodBaseLineBrowseItemClick(Sender: TObject);
begin
  Data.ULM_ULB.Browse;
end;

procedure TSpectrumForm.BaseLineDeleteAll_ItemClick(Sender: TObject);
{var
  i: integer;
  p: TULBRObj;}
begin
  if Spect = nil then
    exit;
  Spect.BaseLine.ChildsDelete(ULBRID, 0);
  DoPeaksSizeChanged;
  SetToRedraw(trBaseLine);
  Redraw;
end;

procedure TSpectrumForm.BaseLineDeleteSelected_ItemClick(Sender: TObject);
begin
  if Spect = nil then
    exit;
  Spect.BaseLine.ChildsDelete(ULBRID, rfSelected);
  DoPeaksSizeChanged;
  SetToRedraw(trBaseLine);
  Redraw;
end;

procedure TSpectrumForm.DefineSpeedButtonMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  FSpeedButtonShift := ssShift in Shift;
end;

procedure TSpectrumForm.DefineSpeedButtonMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  FSpeedButtonShift := ssShift in Shift;
end;

procedure TSpectrumForm.PrintPreviewItemClick(Sender: TObject);
begin
{  Print(true);}
end;

procedure TSpectrumForm.PrintReportItemClick(Sender: TObject);
begin
  Print(true, rtReport);
end;

procedure TSpectrumForm.WindowClick(Sender: TObject);
begin
  Print(true, rtWindow);
end;

procedure TSpectrumForm.CopyItemClick(Sender: TObject);
begin
  {SendMessage(Handle, WM_COPY, 0, 0);}
  CopySpectrumToClipboard;
end;

function TSpectrumForm.BaseLineInsertPointStart(X:integer):boolean;
var
  pp, cp, np: TScreenPeak;{previous peek, current peak, nextpeak}
  l, r: integer;
{  dispw: integer;}
{  bcr: integer;}
  ind: integer;
begin
  Result := false;
{  dispW := OutDisp^.Right - OutDisp^.Left;}
{  bcr := BaseLineCircleRadius(dispW);}

  Spect.GetScreenBaseLineRec(X, OutDisp^, ind, pp, cp, np);
  if (cp.P2.X < X) or (cp.P1.X > X) then begin
    Result := false;
    exit;
  end;
  BaseLineDefineMode := pdInsertPoint;
  BaseLineStartPoint.X := X;

  if cp.P1.X < OutDisp^.Left then
    l := Outdisp^.Left
  else
    l := cp.P1.X + 1;

  if cp.P2.X > OutDisp^.Right then
    r := Outdisp^.Right
  else
    r := cp.P2.X - 1;
  BaseLineML.SetLimits(l, OutDisp^.Top, r, OutDisp^.Bottom);
  BaseLineML.Mode := mmVertical;
  BaseLineML.Start(X, cp.p1.Y);
end;

procedure TSpectrumForm.BaseLineInsertPoint_ActionExecute(Sender: TObject);
var i:integer;
begin
  if IsInBaseLine(LastMousePos.X, i) then begin
    if BaseLineDefineMode = pdInsertPoint then begin
      BaseLineAbort;
    end else begin
      BaseLineInsertPointStart(LastMousePos.X);
    end;
  end;
end;

procedure TSpectrumForm.BaseLineMerge_ActionExecute(Sender: TObject);
begin
  BaseLineMerge;
end;

procedure TSpectrumForm.BaseLineMerge;
begin
  Spect.BaseLineMerge;
  DoPeaksSizeChanged;
  SetToRedraw(trBaseLine);
  Redraw;
end;

procedure TSpectrumForm.BaseLineGroup;
begin
  Spect.BaseLineGroup;
  DoPeaksSizeChanged;
  SetToRedraw(trBaseLine);
  Redraw;
end;

procedure TSpectrumForm.BaseLineUnGroup;
begin
  Spect.BaseLineUnGroup;
  DoPeaksSizeChanged;
  SetToRedraw(trBaseLine);
  Redraw;
end;


procedure TSpectrumForm.BaseLineGroup_ActionExecute(Sender: TObject);
begin
  BaseLineGroup;
end;

procedure TSpectrumForm.BaseLineUngroup_ActionExecute(Sender: TObject);
begin
  BaseLineUngroup;
end;

procedure TSpectrumForm.OverlayFileNameClick(Sender: TObject);
var i: integer;
begin
  if Sender is TMenuItem then with Sender as TMenuItem do begin
    i := Tag - 1;
    if (i >= 0) and (i < DataList.Count) then begin
      CurDataIndex := i;
      DataSetCurrent(i);
    end;
  end;
end;

procedure TSpectrumForm.RedrawAll;
begin
  SetToRedraw(trAll);
  Redraw;
end;

procedure TSpectrumForm.OverlayDeleteActive_ItemClick(Sender: TObject);
begin
  if DataList.Count > 1 then begin
    if (CurDataIndex >= 0) and (CurDataIndex < DataList.Count) then begin
      DataListClear(CurDataIndex);
      UpdateCaption;
      RedrawAll;
    end;
  end;
end;

{v0.11}

function TSpectrumForm.GetFMH: TFileMenuHandler;
begin
 with Application.MainForm as TMainForm do
   GetFMH := FMH;
end;

procedure TSpectrumForm.MethodSaveTo_ItemClick(Sender: TObject);
begin
  if MethodSaveDialog.Execute then begin
    Data.MethodSaveToFile(MethodSaveDialog.FileName);
  end;
end;

procedure TSpectrumForm.MethodLoadFrom_ItemClick(Sender: TObject);
begin
  if MethodOpenDialog.Execute then begin
    Data.MethodLoadFromFile(MethodOpenDialog.FileName);
  end;
end;

procedure TSpectrumForm.PeaksResponsesCalculate_ItemClick(Sender: TObject);
begin
  if Spect = nil then
    exit;
  Spect.PeaksResponsesCalculate;
end;

procedure TSpectrumForm.PeaksAmountsCalculate_ItemClick(Sender: TObject);
begin
  if Spect = nil then
    exit;
  Spect.PeaksAmountsCalculate;
end;

{/v0.11}

procedure TSpectrumForm.CalibrationFileSelect_ItemClick(Sender: TObject);
var fn:string;
begin
  fn := Data.ULM.CalibrationFileName;
  if fn <> '' then
    CalibrationFileSelectDialog.FileName := fn;
  if CalibrationFileSelectDialog.Execute then begin
    Data.CalibrationFileSelect(CalibrationFileSelectDialog.FileName);
    {v0.12}
    if Spect = nil then
      exit;
    Spect.PeaksAmountsCalculateCalibrationFile;
    {/v0.12}
  end;
end;

procedure TSpectrumForm.DataMathMultiplyX_ItemClick(Sender: TObject);
var
  r:real;
  code: integer;
begin
  Val(InputBox('Data math operation', 'Enter number to multiply X values with', ''), r, code);
  if code = 0 then begin
    Data.DoMath(moMultiplyX, r);
    RedrawAll;
  end;
end;

procedure TSpectrumForm.DataMathMultiplyY_ItemClick(Sender: TObject);
var
  r:real;
  code: integer;
begin
  Val(InputBox('Data math operation', 'Enter number to multiply Y values with', ''), r, code);
  if code = 0 then begin
    Data.DoMath(moMultiplyY, r);
    RedrawAll;
  end;
end;

procedure TSpectrumForm.DataMatAddX_ItemClick(Sender: TObject);
var
  r:real;
  code: integer;
begin
  Val(InputBox('Data math operation', 'Enter number to add to X values', ''), r, code);
  if code = 0 then begin
    Data.DoMath(moAddX, r);
    RedrawAll;
  end;
end;

procedure TSpectrumForm.DataMathAddY_ItemClick(Sender: TObject);
var
  r:real;
  code: integer;
begin
  Val(InputBox('Data math operation', 'Enter number to add to Y values', ''), r, code);
  if code = 0 then begin
    Data.DoMath(moAddY, r);
    RedrawAll;
  end;
end;

procedure TSpectrumForm.DataMathCancel_ItemClick(Sender: TObject);
begin
  Data.DoMath(moNone, 0);
  RedrawAll;
end;

procedure TSpectrumForm.DataMathMakePermanent_ItemClick(Sender: TObject);
begin
  Data.MakeMathPermanent;
end;

{v0.15}
procedure TSpectrumForm.DataTruncate_ItemClick(Sender: TObject);
var
  s:string;
  t:single;
  code:integer;
begin
  s := InputBox('Truncate analysis', 'Discard data after time [min]:', '');
  val(s, t, code);
  if code <> 0 then
    exit;
  Data.Truncate(t *  60);
  RedrawAll;
end;
{/v0.15}

procedure TSpectrumForm.SuspendResume_ItemClick(Sender: TObject);
begin
  {v0.19}
  if FAcqInfo <> nil then
    FAcqInfo.DoSuspendResume;
  {/v0.19}
end;

end.
