unit SpecForm;{ Form for rendering chromatograms }
{
  (C) 2000 - 2001 Jindrich Jindrich, Pavel Pisa, PiKRON Ltd.

  Originators of the CHROMuLAN project:

  Jindrich Jindrich - http://www.jindrich.com
                      http://orgchem.natur.cuni.cz/Chromulan/
                      software developer, project coordinator
  Pavel Pisa        - http://cmp.felk.cvut.cz/~pisa
                      embeded software developer
  PiKRON Ltd.       - http://www.pikron.com
                      project initiator, sponsor, instrument developer

  The CHROMuLAN project is distributed under the GNU General Public Licence.
  See file COPYING for details.

  Originators reserve the right to use and publish sources
  under different conditions too. If third party contributors
  do not accept this condition, they can delete this statement
  and only GNU license will apply.
}

{$I DEFINE.PAS}
interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls, StdCtrls, Clipbrd,
  CommInt, Menus, Buttons, {windows}

  UtlType, Fileu,
  ListType, Listu,
  Timer, BinHex,
  Msgu,
  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} {v0.38}{/v0.38 PrnUtl,} DrawMeta,
  ActnList {v0.09}, FileMenuHdl {/v0.09}{v0.11},InfoFrm{/v0.11}{uledfrm}
  {v0.14}
  ,Channelsu, AcqInfou, AnalSetupFrm
  {/v0.14}
  {v0.28}, Language{/v0.28}{v0.42}, ExeLogu{/v0.42}{v0.43}, Timersu{/v0.43}
  {v0.54}
  ,PickMethodPeakFrm
  {/v0.54}
  {v0.57}
  ,OvrManu
  {/v0.57}
  ;


type
  {v0.50}
  TRunButtonMode = (rbmRun, rbmPrepare);
  {/v0.50}
  {v0.69}
  TExtreme = (exMinX, exMaxX, exMinY, exMaxY);
  TExtremes = set of TExtreme;
const
  exAll = [exMinX..exMaxY];

type
  {/v0.69}

  ESpecForm = class(Exception);

  TSpectrumForm = class(TForm)
    PaintBox: TPaintBox;
    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;
    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;
    FilterMenuItem: TMenuItem;
    PeaksToMethodItem: TMenuItem;
    BaseLineCopyToMethod_Item: TMenuItem;
    MethodBaseLineItem: 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;
    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;
    DataMathDilute_Item: TMenuItem;
    AssignMethodPeakItem: TMenuItem;
    MethodSaveButton: TButton;
    PeaksAutodetectButton: TButton;
    PeaksBrowseButton: TButton;
    PrintReportButton: TSpeedButton;
    MethodLoadEdit: TEdit;
    Label1: TLabel;
    CalibrationFileEdit: TEdit;
    Label2: TLabel;
    SaveToBitmapFileItem: TMenuItem;
    ViewReportCheckBox: TCheckBox;
    ProgramItem: TMenuItem;
    CalcTheorPlatesItem: 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 ViewReportCheckBoxClick(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);
    procedure DataMathDilute_ItemClick(Sender: TObject);
    procedure AssignMethodPeakItemClick(Sender: TObject);
    procedure OverlayMenu_ItemClick(Sender: TObject);
    procedure SaveToBitmapFileItemClick(Sender: TObject);
    procedure ProgramItemClick(Sender: TObject);
    procedure CalcTheorPlatesItemClick(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, 0..MaxDataInAcq] 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}
    {v0.21}
    CurML: TMouseLine;
      { one of the above ones, that IsOn }
    {/v0.21}
    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 }
    FAcqDataList: TList;
      { List of TAcqData, used if more chromatograms shown in one window }
    AngleText: TAngleText;
      { for drawing peak names }
    {v0.38}{/v0.38
    FDataPrinter: TDataPrinter;}
    FSpeedButtonShift: boolean;
    FDragging: boolean; { set true if some action that uses mouse dragging
      is on (timer FActiveAcqData 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}
    {/v0.21}
    FBackgroundDrawn: boolean;
      { true just during PaintBoxPaint if DrawBackground method was already called }
    {/v0.21}
    {v0.24}
    FOwnsAcqData: boolean;
    FOwnsAcqInfo: boolean;
      { set true by default; false set just during LendData procedure }
    FDataClearing: boolean;
    {/v0.24}
    {v0.25}
    FPointCircleRadius: byte;{ulvotype}
    FChangingBaseLine: integer;
    {/v0.25}
    {v0.42}
    FIsDestroying: boolean;
    {/v0.42}
    {v0.53}
    FSpectrumFormTimerInterval: integer;{how often is Timer1Timer called}
    {/v0.53}
    {v0.54}
    FCurPeak: TULPRObj;
      { set non nil just before calling popupmenu.popup, if under mouse
        cursor is some peak }
    {/v0.54}
    {v0.50}
    FRunButtonMode: TRunButtonMode;
    {/v0.50}

    {v0.57 moved from public, made property}
    FActiveAcqData: TAcqData;
      { Currently selected FActiveAcqData. In overlay mode more TAcqData object can
        be opened and stored in FAcqDataList. FActiveAcqData points to the default one. }
    FOvrMan: TOvrMan;
    {/v0.57}

    {v0.58}
    function GetActiveAcqData: TAcqData;
    {/v0.58}
    {v0.57}
    procedure SetActiveAcqData(AAcqData: TAcqData);
    procedure DoOvrStateChanged;
    function GetOvrMan: TOvrMan;
    {/v0.57}
    {v0.50}
    procedure SetRunButtonMode(AMode: TRunButtonMode);
    {/v0.50}

    function GetFileName: string;
    procedure ForwardMessage(var Msg: TMessage);
    {v0.31 moved from public}
    procedure ClearData;
      { called after run started or data aborted (from the events triggered
        by acqinfo messages sent to the window) }
    {/v0.31}
    {v0.43}
    procedure TimerDel;
    procedure SetIsDestroying(OnOff: boolean);

    property IsDestroying: boolean read FIsDestroying write SetIsDestroying;
    procedure CMRelease(var Message: TMessage); message CM_RELEASE;
    procedure CallRelease;
    {/v0.43}
    {v0.65}
    function GetActiveChannelName: string;
    {/v0.65}
    {v0.67}
    function GetActiveChannelIndex: integer;
    {/v0.67}
  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 FActiveAcqData.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 FActiveAcqData.ULVL (i.e. store them
        to file if changes will be saved). Values get really copied to
        FActiveAcqData.ULVL only if force = true, or FActiveAcqData.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}
    procedure AcqDataDestroyed(ULA: TULAObj);
    {v0.64}
    { Display current data (just set) in form controls }
    procedure UpdateControlsFromData;
    {/v0.64}
  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) }
    {v0.57 moved to private, made property}{/v0.57
    FActiveAcqData: TAcqData;}
      { Currently selected FActiveAcqData. In overlay mode more TAcqData object can
        be opened and stored in FAcqDataList. FActiveAcqData points to the default one. }
    tmpAcqDataIndex: integer;
      { Index of FActiveAcqData in FAcqDataList used during Paint method
        (scanning FActiveAcqData list) }
    CurAcqDataIndex: integer;
      { Curently user selected tmpAcqDataIndex (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;{ulantype}

    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, PeakFinish and PeakCreate methods - manual,
        interactive with mouse defining of peaks }
    BaseLineStartPoint, BaseLineStopPoint:TPoint;

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

    procedure WndProc(var Message: TMessage); override;
    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; ADataIndex:integer);
        { Draw the points from buffer for ActivAcqData
          (and its data index - if more data in one AcqDataFile). }
    {v0.21}
    procedure DrawBackground(ACanvas: TCanvas; const ADisp: TScreenDisp);
    {/v0.21}
    procedure DrawAxis(ACanvas: TCanvas; const ADisp: TScreenDisp);
    procedure DrawBaseLine(ACanvas: TCanvas; const ADisp: TScreenDisp);
    procedure DrawHeader(ACanvas: TCanvas; const ADisp: TScreenDisp);
    { Called from PaintBoxPaint to draw the peaks. }
    procedure DrawPeaks(ACanvas: TCanvas; const ADisp: TScreenDisp);
    { Calls all of the above DrawXXXX methods, taking into account view options
      (what to draw). }
    procedure DrawSpectrum(ACanvas: TCanvas; const ADisp: TScreenDisp);

    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 FActiveAcqData file to which the new acquisition should be done }
    {/v0.14}
    {v0.24}
    procedure LendData(AAcqData: TAcqData; AAcqInfo: TAcqInfo);
      { as SetData but the form does not own the FActiveAcqData and acqinfo
        (won't get destroyed when the form is closed) }
    {/v0.24}

    procedure SetDataFile(const AFileName:string; AMode: TOpenMode);
      { Set new FActiveAcqData to be worked on. }
    procedure AddDataFile(const AFileName:string);
      { Add new FActiveAcqData 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 FActiveAcqData with given index in FAcqDataList as FActiveAcqData }

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

    {v0.57}
    procedure DataListDataClose(Index: integer);
      { as above, but does range checking }
    {/v0.57}

    procedure UpdateCaption;
    {v0.31 moved to private}{/v0.31
    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{v0.50}; var AData: TAcqData{/v0.50}):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;

    {v0.21}
    procedure WMWindowPosChanging(var Msg: TMessage);message WM_WINDOWPOSCHANGING;
    procedure WMWindowPosChanged(var Msg: TMessage);message WM_WINDOWPOSCHANGED;
    {/v0.21}
    {/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 PeakCreateStart(X,Y:integer):boolean;
      { Called upon mouse down (if DefiningPeaks). Returns false,
        if peak can not be created in at given pos }
    function PeakMoveStart(X: integer): boolean;
    function PeakSizeStart(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 PeakFinish(X, Y: integer; Shift: TShiftState);{ called upon mouse up .. }
    { function PeakStarted:boolean;{true during mouse down .. }
      procedure PeakCreate(const AStartPoint, AStopPoint:TPoint);
      procedure PeakMove(OldStartX, NewStartX:integer);
      procedure PeakSize(OldStopX, NewStopX:integer; Right: boolean);
      procedure PeakToggleSelect(X: integer; Shift: TShiftState);
      {..called from PeakFinish}
    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;}
    {v0.21}
    procedure DoAcquisitionAborted;
    {/v0.21}
    {v0.24}
    procedure DoAcqInfoDestroyed;
    {/v0.24}
    procedure RunPanelShow;
    {v0.14}{/v0.14
    procedure PortSetDefaults;}
    {v0.50}{/v0.50
    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;
    {v0.65}
    function SaveSpectrumToBMP(const AFileName: TFileName): boolean;
    {/v0.65}
    {v0.69}
    procedure UpdateViewLimitFromDataExtremes(AExtremes: TExtremes);
    {/v0.69}
    {v0.33}
    function FindAcqData(const AFileName: string; var AAcqData: TAcqData): boolean;
      { Returns true if data of given filename are in FAcqDataList and returns pointer to it,
        returns false if not found and leave the AAcqData unchanged. }
    {/v0.33}

    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}
    {v0.24}
    {property OwnsAcqData: boolean read FOwnsAcqData write FOwnsAcqData;
    property OwnsAcqInfo: boolean read FOwnsAcqInfo write FOwnsAcqInfo;}
    {/v0.24}
    {v0.50}
    property RunButtonMode: TRunButtonMode read FRunButtonMode write SetRunButtonMode;
    {/v0.50}
    {v0.57}
    property DataList: TList read FAcqDataList;
    property ActiveAcqData: TAcqData read {v0.58}GetActiveAcqData{/v0.58 FActiveAcqData} write SetActiveAcqData;
    property OvrMan: TOvrMan read GetOvrMan;
    {/v0.57}
    {v0.65}
    property ActiveChannelName: string read GetActiveChannelName;
    {/v0.65}
    {v0.67}
    property ActiveChannelIndex: integer read GetActiveChannelIndex;
    {/v0.67}
  end;

var
  SpectrumForm: TSpectrumForm;

{v0.38}
function CreateSpectrumForm(const Name: string; AMode: TOpenMode): TSpectrumForm;
{/v0.38
procedure CreateSpectrumForm(const Name: string; AMode: TOpenMode);}

{v0.24}
function SpectrumFormOpenForAcqData(AAcqData: TAcqData; AAcqInfo: TAcqInfo): TSpectrumForm;
{/v0.24}

{v0.68}
function FindSpecForm(AChannelIndex: integer; var AForm: TSpectrumForm): boolean;
{/v0.68}

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}
  {v0.43}
  GDestroying: boolean = false;
  {/v0.43}

function CountYOnScreenLine(X: TScreenX; X1: TScreenX; Y1: TScreenY;
  X2: TScreenX; Y2: TScreenY): TScreenY;
begin
  {v0.21}
  if X2 = X1 then begin
    Result := (Y1 + Y2) div 2;
  end else
  {/v0.21}
  begin
  CountYOnScreenLine := Y1 + (Y2 - Y1)* (X - X1) div (X2 - X1) ;
  end;
end;

{v0.24}
function SpectrumFormOpenForAcqData(AAcqData: TAcqData; AAcqInfo: TAcqInfo): TSpectrumForm;
var
  sf: TSpectrumForm;
begin
  sf := TSpectrumForm.Create(Application);
  if sf <> nil then
    sf.LendData(AAcqData, AAcqInfo);
  Result := sf;
end;
{/v0.24}

{v0.14}
{v0.38}
function CreateSpectrumForm(const Name: string; AMode: TOpenMode): TSpectrumForm;
{/v0.38
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
  {v0.38}                                 {ulprobju}
  Result := nil;
  {/v0.38}

  if SpectrumFormCount = MaxSpectrumFormCount then
    exit;
  {$HINTS OFF} {ulanrecs.lst ulatype ulrectyp ulsrobju uliobju}
  {sf := nil;}
  {$HINTS ON}
  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 FActiveAcqData }
    if (Name = '') then
      raise ESpecForm.Create('CreateSpecForm: Empty template file name');
    {$HINTS OFF}
    {ad := nil;}
    ai := nil;
    {$HINTS ON}

    fn := ReplaceExt(Name, ULTExt, false);
    if ExtractFileName(fn) = pvFromFileTemplate then begin
      if MainForm.ParentFileOpenDialog.Execute then begin
        fn := MainForm.ParentFileOpenDialog.FileName;
      end else begin
        raise EUserAborted.Create(GetTxt({#}'User Aborted'));
      end;
    end;
    ad := TAcqData.Create(fn, AMode);
    try
      {r := mrCancel;}
      CurULObj := ad.ULA;
      {v0.67}
      if not {v0.68}InCuLDoCmd{/v0.68 SendMessage} then
      {/v0.67}
      begin
        frm := TAnalSetupForm.Create(Application.MainForm);
        try
          r := frm.ShowModal;
          frm.Free;
        except {aaatype}
          frm.Free;
          ad.Free;
          {$HINTS OFF}
          {frm := nil;}
          ad := nil;
          {$HINTS ON}
          raise;
        end;
        if r <> mrOK then begin
          ad.Free;
          exit;{raise EUserAborted.Create('Aborted by user');}
        end;
      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 FActiveAcqData }
    if (Name = '') then
      raise ESpecForm.Create(GetTxt({#}'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;
  {v0.38}
  Result := sf;
  {/v0.38}
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);             {ulprobju}
  end;
end;
*)

procedure TSpectrumForm.FormCreate(Sender: TObject);
{v0.14}{/v0.14
var
  li: TListInfo;}
{  i: integer;}
begin
  FCreating := true;
  {v0.53}
  FSpectrumFormTimerInterval := 1000;
  {/v0.53}
  {v0.43}
  Timers.AddTimerProc(Timer1Timer, {v0.53}FSpectrumFormTimerInterval{/v0.53 1000});
  {/v0.43}{ulantype}
  {v0.24}
  FOwnsAcqData := true;
  FOwnsAcqInfo := true;
  {/v0.24}

  try
  {set def values}
  DragStartCursor := crDefault;
  AngleText := TAngleText.Create(90);
  FAcqDataList := 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;
  FActiveAcqData := 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.25} and (UserMode = umSysOp){/v0.25};
  {v0.19}
  DoAfterStop;
  {/v0.19
  Stop;}
  {v0.09}
  FXAxisBandStartX := -1;
  FYAxisBandStartY := -1;
  FBaseLineIndex := -1;

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

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

{v0.43}
const
  wndcnt: integer = 0;

procedure TSpectrumForm.WndProc(var Message: TMessage);
begin
  if GDestroying then begin
    DebLog('SpecForm GDestroying.WndProc begin');
    inc(wndcnt);
  end;
  {v0.57}
  if (Message.Msg <> 0) then begin
    if (Integer(Message.Msg) = Integer(OvrMan.UpdateMessageID)) then begin
      OvrMan.Update;
      Invalidate;
    end;
  end;      
  {/v0.57}
  inherited;
  if wndcnt > 0 then begin
    DebLog('SpecForm GDestroying.WndProc end');
    dec(wndcnt);
  end;
end;
{/v0.43}

procedure TSpectrumForm.SetIsDestroying(OnOff: boolean);
begin
  FIsDestroying := OnOff;
  GDestroying := OnOff;
end;

procedure TSpectrumForm.TimerDel;
begin
  Timers.DeleteTimerProc(Timer1Timer);
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 FActiveAcqData <> nil then begin
    ind := 0;
    cnt := FAcqDataList.Count;
    repeat
      if cnt > 1 then begin
        DataSetCurrent(ind);
      end;
      FActiveAcqData.SetViewLimit(ViewLimit);
      {FActiveAcqData.SetUserLimit(UserViewLimit);}
      FActiveAcqData.SetLimit(Limit);
      {CountBaseLinePos;}
      inc(ind);
    until (ind >= cnt);
    SetToRedraw(trAll);
    Redraw;
    {v0.09}
    DataSetCurrent(CurAcqDataIndex);
    {/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 FActiveAcqData <> 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, FActiveAcqData.ULVO.ShowAxis);
  SpectrumObjectShow(soBaseline, FActiveAcqData.ULVO.ShowBaseLine);
  SpectrumObjectShow(soData, FActiveAcqData.ULVO.ShowData);
  SpectrumObjectShow(soHeader, FActiveAcqData.ULVO.ShowHeader);
  SpectrumObjectShow(soPeaks, FActiveAcqData.ULVO.ShowPeaks);
  SpectrumObjectShow(soReport, FActiveAcqData.ULVO.ShowReport);
  SpectrumObjectShow(soXY, FActiveAcqData.ULVO.ShowXY);
  FPointCircleRadius := FActiveAcqData.ULVO.PointCircleRadius;
end;

{v0.09}
procedure TSpectrumForm.UpdateViewLimitsFromDataFiles;
var
  i:integer;
  dx{, x}: TXValue;
  dy{, y}: TYValue;
  mma, mmi, ma, mi: TUserPoint;
begin
  if FAcqDataList.Count = 0 then
    exit;
  for i := 0 to FAcqDataList.Count - 1 do begin
    DataSetCurrent(i);
    FActiveAcqData.ExpToUser(FActiveAcqData.SpectrumOpt.Extreme.Max, ma);
    FActiveAcqData.ExpToUser(FActiveAcqData.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(CurAcqDataIndex);
end;
{/v0.09}

procedure TSpectrumForm.UpdateViewLimitRecFromDataFile(force: boolean);
{v0.69}{/v0.69
var
  dy: TYValue;
  dx: TXValue;
}
{v0.25}
{  i: integer;
  cmin,cmax: TUserPoint;}
{/v0.25}
begin
  if FActiveAcqData.Mode <> omCreate then begin
    if FActiveAcqData.ULVL.FixedView or force then begin
      with UserViewLimit do begin
        Min.X := FActiveAcqData.ULVL.MinX;
        Min.Y := FActiveAcqData.ULVL.MinY;
        Max.X := FActiveAcqData.ULVL.MaxX;
        Max.Y := FActiveAcqData.ULVL.MaxY;
      end;
    end else begin
      {v0.69}
      UpdateViewLimitFromDataExtremes(exAll);
      {/v0.69
      with UserViewLimit do begin
        with FActiveAcqData.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;
        //if FActiveAcqData.IsCalFile and (FActiveAcqData.ULP.ChildCount > 0) then begin
        // p := TULPRObj(FActiveAcqData.ULP.Childs[FActiveAcqData.ULP.ChildCount-1].X2;
        //FActiveAcqData.ExpToUser(FActiveAcqData.ULP.Childs[SpectrumOpt.Extreme.Max, Max);
        //FActiveAcqData.ExpToUser(FActiveAcqData.SpectrumOpt.Extreme.Min, Min);
        //end else
        begin;
          FActiveAcqData.ExpToUser(FActiveAcqData.SpectrumOpt.Extreme.Max, Max);
          FActiveAcqData.ExpToUser(FActiveAcqData.SpectrumOpt.Extreme.Min, Min);
        end;
        dy := Max.Y - Min.Y;
        dx := Max.X - Min.X;
        Max.Y := Min.Y + (dy / 100) * 120;//increase MaxY by 20%
        Min.Y := Min.Y - (dy / 100) * 10; // decrease MinY by 10%
        Max.X := Max.X + (dx / 100) * 5;  // increase MaxX by 5%
      end;
      }
    end;
  end else begin
    if force {v0.26} or (FActiveAcqData.ULVL.MaxX <> 0){v0.26}then begin
      with UserViewLimit do begin
        Min.X := FActiveAcqData.ULVL.MinX;
        Min.Y := FActiveAcqData.ULVL.MinY;
        Max.X := FActiveAcqData.ULVL.MaxX;
        Max.Y := FActiveAcqData.ULVL.MaxY;
      end;
    end else begin
      UserViewLimit := NewUserViewLimit;
    end;
  end;

  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;
end;

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

{v0.24}
procedure TSpectrumForm.LendData(AAcqData: TAcqData; AAcqInfo: TAcqInfo);
begin
  FOwnsAcqData := false;
  FOwnsAcqInfo := false;
  SetData(AAcqData, AAcqInfo);
end;
{/v0.24}

{v0.14}
procedure TSpectrumForm.SetData(AData: TAcqData; AAcqInfo: TAcqInfo);
      { set new FActiveAcqData file to which the new acquisition should be done }
begin
  {v0.24}
  if FOwnsAcqData then begin
    if AData <> nil then
      AData.OwnerForm := Self;
  end;
  if FOwnsAcqInfo then begin
    if AAcqInfo <> nil then
      AAcqInfo.OwnerForm := Self;
  end;
  FOwnsAcqData := true;
  FOwnsAcqInfo := true;
  {/v0.24}
  FAcqInfo := AAcqInfo;
  if FAcqInfo <> nil then begin
    FAcqInfo.TheSpecForm := TForm(Self);
    {v0.53}
    FAcqInfo.TimerInterval := FSpectrumFormTimerInterval;
    {/v0.53}
  end;
  try
    DataListClear(-1);
    FActiveAcqData := AData;
    DataListAdd(FActiveAcqData);
    tmpAcqDataIndex := FAcqDataList.IndexOf(FActiveAcqData);

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

    UpdateViewLimitRecFromDataFile(false);
    UpdateViewOptionsFromDataFile;

    LockDraw;
    {v0.56}
    try
    {/v0.56}
      UpdateScreenDisp;
      UpdateViewLimit;
      UpdateCaption;
      {v0.20}
      SetToRedraw(trAll);
      {/v0.20}
    {v0.56}
    finally
      UnlockDraw;
    end;
    {/v0.56 UnlockDraw;}
    FActiveAcqData.ULF.Modified := false;
  except
    {v0.43}
    CallRelease;
    {/v0.43
    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);
    FActiveAcqData := d;
    DataListAdd(FActiveAcqData);
    tmpAcqDataIndex := FAcqDataList.IndexOf(FActiveAcqData);

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

    UpdateViewLimitRecFromDataFile(false);
    UpdateViewOptionsFromDataFile;

    LockDraw;
    UpdateScreenDisp;
    UpdateViewLimit;
    UpdateCaption;
    UnlockDraw;
    FActiveAcqData.ULF.Modified := false;
    }
  except
    on EUserAborted do
    begin
      {v0.43}
      CallRelease;
      {/v0.43
      Timer1.OnTimer := nil;
      Release;}
    end
  else
    {v0.43}
    CallRelease;
    {/v0.43
    Timer1.OnTimer := nil;
    Release;}
    raise;
  end;
end;

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

    UpdateViewLimitsFromDataFiles;

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


{v0.09}
procedure TSpectrumForm.AddData(AData: TAcqData);
begin
  try
    FActiveAcqData := AData;
    DataListAdd(FActiveAcqData);
    tmpAcqDataIndex := FAcqDataList.IndexOf(FActiveAcqData);
    Spect.SetData(FActiveAcqData);
    UpdateViewLimitsFromDataFiles;
    {v0.56}
    LockDraw;
    try
      UpdateScreenDisp;
      UpdateViewLimit;
      UpdateCaption;
    finally
      UnlockDraw;
    end;
    {/v0.56
    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 FActiveAcqData = 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 FActiveAcqData <> nil then begin
    FActiveAcqData.UserToExp(AUserViewLimit.Min, AViewLimit.Min);
    FActiveAcqData.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.FActiveAcqData[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.FActiveAcqData[CurPos] := ord(CommChar);
      if CurPos = 0 then
        Time := EndTime;
      inc(CurPos);
      if CurPos = ApexPacketSize then begin
        CurPos := 0;
        EndTime := mstime;
        if Pkt.FActiveAcqData[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
  {v0.42}
  if FIsDestroying then
    exit;
  {/v0.42}

  if IsInTimer then
    exit;
  {v0.42}{/v0.42
  if FActiveAcqData = nil then
    exit;
  if FAcqInfo = nil then begin
    if FActiveAcqData.ULA.AutodetectLater then begin
      FActiveAcqData.ULA.AutodetectLater := false;
      try
        Autodetect1Click(Self);
      except;
      end;
    end;
    exit;
  end;}
  try
    IsInTimer := true;
    {v0.42}
    {ExeLog.Log('SpecForm.Timer begin');}
    if FActiveAcqData = nil then
      exit;
    if FAcqInfo = nil then begin
      if FActiveAcqData.ULA.AutodetectLater then begin
        FActiveAcqData.ULA.AutodetectLater := false;
        try
          Autodetect1Click(Self);
        except;
        end;
      end;
      exit;
    end;
    {/v0.42}

    {v0.19}
    {v0.41}
    if FAcqInfo.OwnerForm = Self then
    {/v0.41}
      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.FActiveAcqData[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
    {v0.42}
    {ExeLog.Log('SpecForm.Timer end');}
    {/v0.42}
    IsInTimer := false;
  end;
end;

{v0.19}
procedure TSpectrumForm.DoPointsAdded;
var
  dr: TExpPoint;
  sr: TScreenPoint;
  {v0.24}
  rdr: TRecentExpPoint;{ ulantype }
  {v0.24}
begin
  {v0.24}
  if FAcqInfo <> nil then
  with FAcqInfo.RecentPoints do begin
    Position := 0;
    while Position < Size do begin
      ReadBuffer(rdr, sizeof(rdr));
      dr := rdr.Point;

      {DataSetCurrent(rdr.Index);}
      if (dr.X >= ViewLimit.Min.X) and (dr.X <= ViewLimit.Max.X) then
      begin
        if (ScrBuf[tmpAcqDataIndex, rdr.Index].Points <> nil) then begin
          FActiveAcqData.ExpToScreen(dr, ScreenDisp, sr);
          ScrBuf[tmpAcqDataIndex, rdr.Index].Points.AddPoint(sr.x,sr.y);
        end;
      end;

    end;
  end;
  {/v0.24
  if (ScrBuf[tmpAcqDataIndex].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
          FActiveAcqData.ExpToScreen(dr, ScreenDisp, sr);
          ScrBuf[tmpAcqDataIndex].Points.AddPoint(sr.x,sr.y);
        end;
      end;
    end;
  end;}

  if DrawLocked = 0 then
    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.FActiveAcqData[ApexSourceIDPos], 3) + ' ' +
      RecToHex(ai.Pkt.FActiveAcqData[ApexFootPos], 3) + ' ' +
      RecToHex(ai.pkt.FActiveAcqData[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 FActiveAcqData <> nil then with FAcqInfo do begin
    if not ai.ScanningValues then
      values := @ai.Pkt.FActiveAcqData[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;
        FActiveAcqData.AddPoint(dr);
        if ScrBuf[tmpAcqDataIndex].Points <> nil then begin
          if (dr.X >= ViewLimit.Min.X) and (dr.X <= ViewLimit.Max.X) then
          begin
            FActiveAcqData.ExpToScreen(dr, ScreenDisp, sr);
            ScrBuf[tmpAcqDataIndex].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 FActiveAcqData = 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
      FActiveAcqData.AddPoint(dr);
      if ScrBuf[tmpAcqDataIndex].Points <> nil then begin
        if (dr.X >= ViewLimit.Min.X) and (dr.X <= ViewLimit.Max.X) then
        begin
          FActiveAcqData.ExpToScreen(dr, ScreenDisp, sr);
          ScrBuf[tmpAcqDataIndex].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 FActiveAcqData <> 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;
      FActiveAcqData.AddPoint(dr);
      if ScrBuf[tmpAcqDataIndex].Points <> nil then begin
        if (dr.X >= ViewLimit.Min.X) and (dr.X <= ViewLimit.Max.X) then
        begin
          FActiveAcqData.ExpToScreen(dr, ScreenDisp, sr);
          ScrBuf[tmpAcqDataIndex].Points.AddPoint(sr.x,sr.y);
        end;
      end;
    end;
  end;
  DrawPoints(PaintBox.Canvas, ScreenDisp);
end;
}

procedure TSpectrumForm.PaintBoxPaint(Sender: TObject);
begin
  {v0.50}
  if IsDestroying then
    exit;
  {/v0.50}
  if Printing then
    exit;
  if IsInPaint then
    exit;
  if DrawLocked > 0 then
    exit;
  if FActiveAcqData = nil then
    exit;
  IsInPaint := true;
  {v0.21}
  if CurML <> nil then
    CurML.Suspend;
  {/v0.21}
  try
    {DrawPos := 0;
    LastScreenRec.X := -1;}
    {v0.21}
    FBackgroundDrawn := false;
    DrawBackground(PaintBox.Canvas, ScreenDisp);
    {/v0.21}
    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
    {v0.21}
    if CurML <> nil then
      CurML.Resume;
    {/v0.21}
    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 FActiveAcqData = nil then
    exit;
  if FActiveAcqData.ExpToScreen(AApexRec, ADisp{OutDisp^}, AScreenRec) <> 0 then
    exit;
  {$IFDEF DEBUG}
  {FActiveAcqData.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 FActiveAcqData = nil then
    exit;
  if not FActiveAcqData.ScreenToExp(TScreenPoint(AScreenPoint), OutDisp^, ap) then
    exit;
  if not FActiveAcqData.FindDataPoint(ap, AIndexPoint.Point, AIndexPoint.Index) then
    exit;
  ScreenPointToIndexPoint := true;
end;

procedure TSpectrumForm.DataSetCurrent(Index: integer);
begin
  FActiveAcqData := TAcqData(FAcqDataList.Items[Index]);
  tmpAcqDataIndex := Index;
  {v0.64}
  UpdateControlsFromData;
  {/v0.64}
  Spect.SetData(FActiveAcqData);
end;

{v0.64}
procedure TSpectrumForm.UpdateControlsFromData;
begin
  if ActiveAcqData <> nil then begin
    MethodLoadEdit.Text := ExtractFileName(ActiveAcqData.ULM.MethodTemplate);
    CalibrationFileEdit.Text := ExtractFileName(ActiveAcqData.ULM.CalibrationFileName);
  end else begin
    MethodLoadEdit.Text := '';
    CalibrationFileEdit.Text := '';
  end;
  ;
end;
{/v0.64}
{Draw methods}
procedure TSpectrumForm.DrawTemplate(ACanvas: TCanvas; const ADisp: TScreenDisp);
var
  x,y:integer;
  s: string;
  h:integer;
begin
  s := ' ' + GetTxt({#}'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;

{v0.21}
procedure TSpectrumForm.DrawBackground(ACanvas: TCanvas; const ADisp: TScreenDisp);
var
  r: TRect;
  bc: TColor;
  {v0.50}
  procedure DrawChannelName;
  var
    a: TAcqData;
    c: TChannel;
    pc: TColor;
    s: string;
    x, y, tw, th: integer;
  begin
    a := FActiveAcqData;
    if (a <> nil) {v0.66} and (a.Mode = omCreate) {/v0.66}then begin
      c := a.Channel;
      if c <> nil then begin
        pc := ACanvas.Pen.Color;
        try           {showmsg}
          ACanvas.Font.Color := c.ULN.Color;
          s := c.ULN.ChannelName;
          tw := ACanvas.TextWidth(s);
          th := ACanvas.TextHeight(s);
          x := r.Left + (r.Right - r.Left - tw) div 2;
          y := r.Top + th div 2;
          if (x < r.Right) and (y < r.Bottom) then
            ACanvas.TextOut(x, y, s);
          {v0.67}
          if {v0.68}InCuLDoCmd{/v0.68 InSendMessage}  then begin
            tw := ACanvas.TextWidth(Caption);
            x := r.Left + (r.Right - r.Left - tw) div 2;
            if x < r.Left then
              x := r.Left;
            y := y + th;
            ACanvas.TextOut(x, y, Caption);
          end;
          {/v0.67}
        finally
          ACanvas.Font.Color := pc;
        end;
      end;
    end;
  end;
  {/v0.50}
begin
  {v0.38}{/v0.38
  if CurAcqDataIndex > 0 then
    exit;}
  {v0.67}
  if not {v0.68}InCuLDoCmd{/v0.68 InSendMessage} then
  {/v0.67}
  begin
    if FBackgroundDrawn then
      exit;
  end;
  FBackgroundDrawn := true;
  with ACanvas do begin
    r.Left := ADisp.Left + 1;
    r.Top := ADisp.Top + 1;
    r.Right := ADisp.Right;
    r.Bottom := ADisp.Bottom;
    bc := Brush.Color;
    try
      Brush.Color := clWhite;
      FillRect(r);
    finally
      Brush.Color := bc;
    end;
    {v0.50}
    if FAcqInfo <> nil then
      DrawChannelName;
    {/v0.50}
  end;
end;
{/v0.21}

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

var
  ind, cnt: integer;
  ar:TExpPoint;
  sr:TScreenPoint;
  {v0.24}
  di: integer;
  {/v0.24}
  {ss:longint;}

  {v0.21}
  {/v0.21
  r: TRect;
  bc: TColor;
  }

begin
  ind := 0;
  cnt := FAcqDataList.Count;
  {v0.21}
  {/v0.21
  with ACanvas do begin
    r.Left := ADisp.Left + 1;
    r.Top := ADisp.Top + 1;
    r.Right := ADisp.Right;
    r.Bottom := ADisp.Bottom;
    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 FActiveAcqData.IsTemplate then begin
        {v0.21}
        DrawBackground(ACanvas, ADisp);
        {/v0.21}
        DrawTemplate(ACanvas, ADisp);
      end else
      {/v0.16}
      begin
        {v0.24}
        for di := 0 to FActiveAcqData.DataCount - 1 do begin
          FActiveAcqData.DataIndex := di;
        {/v0.24}
          {v0.57}
          if FActiveAcqData.ULAD[di].UDVR.Hidden then
            continue;
          {/v0.57}
          if not ScrBuf[tmpAcqDataIndex,di].WasReadyFor(ACanvas, ADisp) then begin
            { screen FActiveAcqData points buffer is not ready for this ACanvas or ADisp: }
            {v0.21}
            DrawBackground(ACanvas, ADisp);
            {/v0.21}
            if FActiveAcqData <> nil then begin
              {v1.05}
              FActiveAcqData.SeekPoint(ScrBuf[tmpAcqDataIndex,di].DrawPos);
              {/v1.05 FActiveAcqData.Seek(DrawPos);}
              {i := 0;}
              if ScrBuf[tmpAcqDataIndex,di].LastRec.X > 0 then begin
                MoveTo(ScrBuf[tmpAcqDataIndex,di].LastRec.X, ScrBuf[tmpAcqDataIndex,di].LastRec.Y);
              end;
              repeat
                if FActiveAcqData.ReadPoint(ar) then begin
                  {v1.05}
                  inc(ScrBuf[tmpAcqDataIndex,di].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[tmpAcqDataIndex,di].LastRec.X <> sr.x) or
                        (ScrBuf[tmpAcqDataIndex,di].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[tmpAcqDataIndex,di].Points <> nil then
                          ScrBuf[tmpAcqDataIndex,di].Points.AddPoint(sr.X, sr.Y);
                        ScrBuf[tmpAcqDataIndex,di].LastRec := sr;
                      end;
                    end;
                    {inc(i);}
                  end;
                end else begin
                  FActiveAcqData.Reset;
                  break;
                end;
              until false;
              {v1.05}
              ScrBuf[tmpAcqDataIndex,di].DrawPos := FActiveAcqData.GetPointCount;
              {/v1.05
              DrawPos := FActiveAcqData.GetSize;}
              {v1.05}DrawScreenPoints(ACanvas, di);{/v1.05}
            end;
          end else begin
            { screen FActiveAcqData points in buffer are ready for this ACanvas and ADisp: }
            DrawScreenPoints(ACanvas, di);
          end;
        {v0.24}
        end;
        FActiveAcqData.UsrDataIndexReselect;
        {/v0.24}
      end;{/not is template}
    end;
    inc(ind);
  until ind >= cnt;
  {v0.09}
  {v0.11}
  if cnt > 1 then
  {/v0.11}
  begin
    DataSetCurrent(CurAcqDataIndex);
  end;
  {/v0.09}
end;

procedure TSpectrumForm.DrawScreenPoints(ACanvas: TCanvas; ADataIndex:integer);
  { draw the points from buffer }
var
  i: integer;
  spi: TScreenPointInfo;
  pc: TColor;
  {v0.20}
  ly:integer;
  {/v0.20}
  {v0.24}
  {colIndex:integer;}
  spts: TScreenPoints; {drawbuf}
  {/v0.24}
  {v0.25}
  npc: TColor;
  {/v0.25}

  procedure Circle(X,Y,R: integer);
  begin
    ACanvas.Ellipse(X-R,Y-R,X+R,Y+R);
  end;

begin
  {v0.24}
  spts := ScrBuf[tmpAcqDataIndex, ADataIndex].Points;
  if (spts = nil) or (spts.Count = 0) then
    exit;
  {colIndex := ScrBuf.CurIndex mod MaxDataListColorCount;}
  {/v0.24
  if (ScrBuf[tmpAcqDataIndex, ADataIndex].Points = nil) or (ScrBuf[tmpAcqDataIndex, ADataIndex].Points.Count = 0) then
    exit;}
  {v0.25}
  {v0.58}
  npc := FActiveAcqData.ULAD[ADataIndex].ULAD.Color;
  {/v0.58
  if tmpAcqDataIndex = 0 then begin
    npc := FActiveAcqData.ULAD[ADataIndex].ULAD.Color;
    if npc = 0 then begin
      npc := FPointColors[colIndex];
    end;
  end else begin
    npc := FPointColors[colIndex];
  end;}
  {/v0.25}
  with ACanvas do begin
    pc := Pen.Color;
    try
      {v0.24}
      Pen.Color := {v0.25}npc;{/v0.25 FPointColors[colIndex];}
      for i := 0 to spts.Count - 1 do begin
        spi := TScreenPointInfo(spts.Items[i]);
      {/v0.24
      Pen.Color := FPointColors[tmpAcqDataIndex mod MaxDataListColorCount];
      for i := 0 to ScrBuf[tmpAcqDataIndex].Points.Count - 1 do begin
        spi := TScreenPointInfo(ScrBuf[tmpAcqDataIndex].Points.Items[i]);
      }

        if i = 0 then begin
          MoveTo(spi.X, spi.Y1);
          {v0.25}
          if FPointCircleRadius <> 0 then
            Circle(spi.X, spi.Y1, FPointCircleRadius);
          {/v0.25}
        end else begin
          LineTo(spi.X, spi.Y1);
          {v0.25}
          if FPointCircleRadius <> 0 then
            Circle(spi.X, spi.Y1, FPointCircleRadius);
          {/v0.25}
        end;
        ly := spi.Y1;
        if spi.MinY <> ly then begin
          LineTo(spi.X, spi.MinY);
          ly := spi.MinY;
          {v0.25}
          if FPointCircleRadius <> 0 then
            Circle(spi.X, spi.MinY, FPointCircleRadius);
          {/v0.25}
        end;
        if spi.MaxY <> ly then begin
          LineTo(spi.X, spi.MaxY);
          ly := spi.MaxY;
          {v0.25}
          if FPointCircleRadius <> 0 then
            Circle(spi.X, spi.MaxY, FPointCircleRadius);
          {/v0.25}
        end;
        if spi.Y2 <> ly then begin
          LineTo(spi.X, spi.Y2);
          {v0.25}
          if FPointCircleRadius <> 0 then
            Circle(spi.X, spi.Y2, FPointCircleRadius);
          {/v0.25}
        end;
      end;
    finally
      Pen.Color := pc;
    end;
  end;
end;

procedure TSpectrumForm.DrawAxis(ACanvas: TCanvas; const ADisp: TScreenDisp);
begin
  if FActiveAcqData = 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(FActiveAcqData, UserViewLimit, ADisp, AxisPlotting) then begin
    {v0.21}
    DrawBackGround(ACanvas, ADisp);
    {/v0.21}
    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;
  bc, co: TColor;
  ind, cnt: integer;

  radius:integer;
  dispW:integer;
begin
  {v0.21}
  DrawBackGround(ACanvas, ADisp);
  {/v0.21}

  with ACanvas do begin
    ps := Pen.Style;
    pm := Pen.Mode;
    co := Pen.Color;
    bc := Brush.Color;

    try
      Brush.Color := clBtnFace;
      Pen.Mode := pmCopy;{pmNotXor;}
      {Pen.Style := psDot;}
      cnt := FAcqDataList.Count;
      ind := 0;
      dispW := ADisp.Right - ADisp.Left;
      repeat
        if cnt > 1 then
          DataSetCurrent(ind);
        c := FActiveAcqData.ULB.ChildCount;
        for i := 0 to c - 1 do begin
          b := TULBRObj(FActiveAcqData.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[tmpAcqDataIndex 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 := FActiveAcqData.ExpToScreen(e1, ADisp, s1);
          os2 := FActiveAcqData.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);
    finally
      Pen.Style := ps;
      Pen.Mode := pm;
      Pen.Color := co;
      Brush.Color := bc;
    end;
  end;
  {v0.09}
  DataSetCurrent(CurAcqDataIndex);
  {/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 (FActiveAcqData = nil) then
    exit;
  ind := 0;
  cnt := FAcqDataList.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);

  {v0.21}
  DrawBackGround(ACanvas, ADisp);
  {/v0.21}

  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[tmpAcqDataIndex 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
            FActiveAcqData.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(CurAcqDataIndex);
    {/v0.09}
  end;
end;

procedure TSpectrumForm.DrawSpectrum(ACanvas: TCanvas; const ADisp: TScreenDisp);
begin
  {v0.21}
  {v0.38}
  {FBackgroundDrawn := false;}
  {/v0.38}
  DrawBackground(ACanvas, ADisp);
  {/v0.21}
  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}
  bc:TColor;
begin
  APlot.Reset;
  bc := ACanvas.Brush.Color;
  try
    {v0.38}
    if Printing then begin
      ACanvas.Brush.Color := clWhite;
    end else
    {/v0.38}
      ACanvas.Brush.Color := clBtnFace;{tcolor}
  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: begin
        ACanvas.TextOut(pr.X, pr.Y, pr.Info);
      end;
    end;
  end;
  finally
    ACanvas.Brush.Color := bc;
  end;
end;


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

procedure TSpectrumForm.UnlockDraw;
begin
  if DrawLocked = 0 then begin
    ShowMessage(GetTxt({#}'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
  {v0.21}
  {FBackgroundDrawn := false;}
  {/v0.21}
  if (tr and trData) <> 0 then begin
    for i := 0 to FAcqDataList.Count - 1 do begin
      {v0.24}
      ScrBuf.BufsClear;
      {/v0.24
      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.42}
  IsDestroying := true;
  {/v0.42}
  {v0.43}
  {v0.43}
  DebLog('SpecForm.FormDestroy begin');
  {/v0.43}

  TimerDel;
  {/v0.43}{ulantype}

  {v0.08}
  DefineMode := dmNone;
  {/v0.08}
  MarginML.Free;
  PeakML.Free;
  {v0.09}
  ShowPosML.Free;
  {/v0.09}
  {v0.21}
  BaseLineML.Free;
  {/v0.21}
  {v0.14}
  {v0.24}
  if FAcqInfo <> nil then begin
    if FAcqInfo.OwnerForm = Self then begin
      FAcqInfo.Free;
    end else begin
      FAcqInfo.TheSpecForm := nil;
    end;
    {v0.42}
    FAcqInfo := nil;
    {/v0.42}
  end;
  {/v0.24
  FAcqInfo.Free;}

  {/v0.14
  if MainForm.ComPort = CommPort then
    MainForm.ComPort := nil;
  ListDone(PacketFifo);
  }
{  FActiveAcqData.Free;}
  ListDone(UserLimitStack);
  dec(SpectrumFormCount);

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

  CurUserViewLimit := UserViewLimit;
    { CurUserViewLimit will be saved to INI as default for new chromatograms }
  FAcqDataList.Free;
  AngleText.Free;
  {v0.43}
  DebLog('SpecForm.FormDestroy end');
  GDestroying := false;
  {/v0.43}
end;

procedure TSpectrumForm.DataListAdd(AData: TAcqData);
begin
  {v0.50}
  AData.FormRegister(Self);
  {/v0.50
  AData.ULA.UserRegister(Self);
  AData.ULP.UserRegister(Self);
  AData.ULB.UserRegister(Self);}
  {v0.21}
  {AData.ULAD.UserRegister(Self);}
  {/v0.21}
  FAcqDataList.Add(AData);
  if CurAcqDataIndex < 0 then
    CurAcqDataIndex := 0;
  {v0.57}
  DoOvrStateChanged;
  {/v0.57}
end;

procedure TSpectrumForm.DataListClear(Index:integer);
var
  i: integer;
{  d: TAcqData;}
begin
  {v0.24}
  FDataClearing := true;
  try
    {v0.09}
    if Index = -1 then
    {/v0.09}
    begin
      for i := 0 to FAcqDataList.Count - 1 do begin
        FActiveAcqData := TAcqData(FAcqDataList.Items[i]);
        {v0.50}
        FActiveAcqData.FormUnregister(Self);
        {/v0.50
        FActiveAcqData.ULA.UserUnregister(Self);
        FActiveAcqData.ULP.UserUnregister(Self);
        FActiveAcqData.ULB.UserUnregister(Self);}
        {v0.21}
        {FActiveAcqData.ULAD.UserUnregister(Self);}
        {/v0.21}
        {v0.13}{/v0.13 UpdateViewLimitRecToDataFile(true);}

        {v0.24}
        ScrBuf.BufsClear;
        if FActiveAcqData.OwnerForm = Self then
          FActiveAcqData.Free;
        {/v0.24
        ScrBuf[i].Clear;
        FActiveAcqData.Free;}
      end;
      FActiveAcqData := nil;
      CurAcqDataIndex := -1;
    end
    {v0.09}
    else begin
      if (FAcqDataList.Count {v0.24}> 0{/v0.24 > 1}) and (Index >= 0) and (Index < FAcqDataList.Count) then begin
        FActiveAcqData := TAcqData(FAcqDataList.Items[Index]);
        {v0.50}
        FActiveAcqData.FormUnregister(Self);
        {/v0.50
        FActiveAcqData.ULA.UserUnregister(Self);
        FActiveAcqData.ULP.UserUnregister(Self);
        FActiveAcqData.ULB.UserUnregister(Self);}
        {v0.13}{/v0.13 UpdateViewLimitRecToDataFile(true);}
        {v0.24}
        ScrBuf.BufsClear;
        {/v0.24
        for i := Index to FAcqDataList.Count - 1 do
          ScrBuf[i].Clear;}
        FAcqDataList.Delete(Index);
        {v0.24}
        if FActiveAcqData.OwnerForm = Self then
          FActiveAcqData.Free;
        {/v0.24
        FActiveAcqData.Free;}
        FActiveAcqData := nil;
        {v0.24}
        if FAcqDataList.Count > 0 then
        {/v0.24}
        begin
          if CurAcqDataIndex >= FAcqDataList.Count then
            CurAcqDataIndex := FAcqDataList.Count - 1;
          DataSetCurrent(CurAcqDataIndex);
        end;
      end;
    end{/v0.09};
    {v0.57}
    DoOvrStateChanged;
    {/v0.57}
  {v0.24}
  finally
    FDataClearing := false;
  end;
  {/v0.24}
end;

procedure TSpectrumForm.AcqDataDestroyed(ULA: TULAObj);
var
  ad: TAcqData;
  i: integer;
begin
  if FDataClearing then
    exit;
  for i := 0 to FAcqDataList.Count - 1 do begin
    ad := TAcqData(FAcqDataList.Items[i]);
    if ad.ULA = ULA then begin
      if FActiveAcqData.OwnerForm <> Self then
        DataListClear(i);
      exit;
    end;
  end;
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
  {v0.21}
  FActiveAcqData.ULVL.SetFlag(rfEditModal, true);
  {/v0.21}
  FActiveAcqData.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 ((FActiveAcqData <> nil) and (FActiveAcqData.Mode <> omCreate)) {v0.24} or (FActiveAcqData.OwnerForm <> Self){v0.24} then begin
    RunStop_Item.Visible := false;
    {v0.19}
    SuspendResume_Item.Visible := false;
    {/v0.19}
    {v0.54}
    AssignMethodPeakItem.Visible := FCurPeak <> nil;
    {/v0.54}
    {v0.77}
    CalcTheorPlatesItem.Visible := FCurPeak <> nil;
    {/v0.77}
  end else begin
    {v0.54}
    {v0.54}
    AssignMethodPeakItem.Visible := false;
    {/v0.54}
    {v0.14} if (FAcqInfo <> nil) then with FAcqInfo do
    {/v0.14}
    begin
      RunStop_Item.Visible := true;
      if RunningState <> rsStopped then
        RunStop_Item.Caption := GetTxt({#}'&Stop')
      else
        RunStop_Item.Caption := GetTxt({#}'&Run');

      {v0.19}
      if RunningState in [rsRunning, rsSuspended] then begin
        SuspendResume_Item.Visible := true;
        if RunningState = rsRunning then
          SuspendResume_Item.Caption := GetTxt({#}'S&uspend')
        else
          SuspendResume_Item.Caption := GetTxt({#}'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}
  {v0.57}
  {cnt := 0;
  for i := 0 to FAcqDataList.Count - 1 do begin
    inc(cnt, TAcqData(FAcqDataList.Items[i]).DataCount);
  end;}
  OverlayMenu_Item.Visible := false;{cnt > 1;}
  {/v0.57
  if FAcqDataList.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 FAcqDataList.Count - 1 do begin
      DataSetCurrent(i);
      mi := TMenuItem.Create(Self);
      mi.Tag := i + 1;
      mi.Checked := (i = CurAcqDataIndex);

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

      mi.OnClick := OverlayFileNameClick;
      oit.Add(mi);
    end;
    DataSetCurrent(CurAcqDataIndex);
  end else begin
    OverlayMenu_Item.Visible := false;
  end;
  }
  {/v0.09}
  {v0.11}
  PeaksResponsesCalculate_Item.Visible := (FActiveAcqData <> nil) and FActiveAcqData.ULA.CalibrationStandard;
  PeaksAmountsCalculate_Item.Visible := (FActiveAcqData <> nil) {v0.12}{/v0.12 and FActiveAcqData.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;

{v0.50}
procedure TSpectrumForm.SetRunButtonMode(AMode: TRunButtonMode);
begin
  case AMode of
    rbmPrepare: begin
      RunButton.Caption := GetTxt({#}'&Prepare');
    end;
  else
    RunButton.Caption := GetTxt({#}'&Run');
  end;
  FRunButtonMode := AMode;
  {v0.67}
  UpdateCaption;
  {/v0.67}
end;

{/v0.50}
procedure TSpectrumForm.RunPanelShow;
begin
  {v0.50}                      {seqprgu}
  if FAcqInfo <> nil then begin
    if FActiveAcqData.Prg.HasBeforeStartLines then begin
      RunButtonMode := rbmPrepare;
    end;
  end;
  {/v0.50}
  RunPanel.Left := (Width div 2) - (RunPanel.Width div 2);
  RunPanel.Top := (PaintBox.Height div 2) - (RunPanel.Height div 2);
  RunPanel.Show;
  {v0.21}
{  RunButton}
  ActiveControl := RunPanel;
  {/v0.21}
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;
}

(*v0.50*)(*/v0.50
function TSpectrumForm.PortOpen: boolean;
begin
  Result := false;
  {v0.14}
  {v0.24}
  if (FAcqInfo <> nil) then
    Result := FAcqInfo.PortOpen;
  {/v0.24
  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}
  {v0.24}
  if FAcqinfo <> nil then
    FAcqInfo.PortClose;
  {/v0.24
  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 (FActiveAcqData <> nil) and (FActiveAcqData.ULAD.FActiveAcqData.Size <> 0) then begin
        if ShowMessage('Discard already acquired FActiveAcqData?', 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 FActiveAcqData <> nil then begin
    {v0.31 moved to acqinfo}{/v0.31
    FActiveAcqData.Clear;}
    {v0.21}
    CreatePeakReport;
    {/v0.21}
    SetToRedraw({v0.21 - also baselines/peaks}trAll{/v0.21 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;
}

{v0.24}
procedure TSpectrumForm.DoAcqInfoDestroyed;
begin
  {v0.43}
  DebLog('SpecForm.DoAcqInfoDestroyed begin');
  {/v0.43}
  FAcqInfo := nil;
  CallRelease;
  DebLog('SpecForm.DoAcqInfoDestroyed end');
  {/v0.43}
end;
{/v0.24}

{v0.43}
procedure TSpectrumForm.CallRelease;
begin
  DebLog('SpecForm CallRelease begin');
  IsDestroying := true;
  TimerDel;
  Release;
  DebLog('SpecForm CallRelease end');
end;
{/v0.43}

{v0.21}
procedure TSpectrumForm.DoAcquisitionAborted;
begin
  ClearData;
end;
{/v0.21}

function TSpectrumForm.GetFileName;
begin
  if FActiveAcqData <> nil then
    GetFileName := FActiveAcqData.ULF.FileName
  else
    GetFileName := '';
end;

procedure TSpectrumForm.UpdateCaption;
begin
  if (FActiveAcqData <> nil) and (FActiveAcqData.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 := GetTxt({#}'Stopped') + ' ' + FileName;
    end else if RunningState = rsReadyToRun then begin
      Caption := GetTxt({#}'Waiting for mark') + ' ' + FileName;
    end else {v0.19} if RunningState = rsSuspended then begin
      Caption := GetTxt({#}'Suspended') + ' ' + FileName;
    end else {/v0.19} begin
      Caption := GetTxt({#}'Running') + ' ' + FileName;
    end;
  end;
end;

procedure TSpectrumForm.FormResize(Sender: TObject);
begin
  {v0.21}{/v0.21
  UpdateScreenDisp;}
end;

procedure TSpectrumForm.ViewReportCheckBoxClick(Sender: TObject);
begin
  SpectrumObjectShow(soReport, ViewReportCheckBox.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 FActiveAcqData <> nil then begin
    FActiveAcqData.SaveTo(ReplaceExt(fn, UlfExt, false));
    UpdateCaption;
  end;
end;

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

  {v0.24}{/v0.24
  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}
      begin
        {v0.50}
        if DeviceMode <> dmUlan then
        {/v0.50}
        begin
          if (RunningState <> rsStopped) and (pshortstring(Msg.LParam)^ = PortName) then begin
            Msg.Result := 1;
            exit;
          end;
        end;
      end;
    end;

    cmLocalMenu: begin
      x := Left + Width div 2;
      y := Top + Height div 4;
      {v0.54}
      FCurPeak := nil;
      {/v0.54}
      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;
    }
    {v0.24}{/v0.24
    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)^.FActiveAcqData);
    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.50}
      if FAcqInfo = TAcqInfo(Msg.LParam) then
      {/v0.50}
      {v0.19}
      DoAfterStop;
      {/v0.19
      Stop;}
    end;
    {/v0.15}
    {v0.19}
    cmAcquisitionRun: begin
      {v0.50}
      if FAcqInfo = TAcqInfo(Msg.LParam) then
      {/v0.50}
      DoAfterRun;
    end;
    cmPeaksAutodetected: begin
      DoAfterAutodetect;
    end;
    cmExpPointsAdded: begin
      DoPointsAdded;
    end;
    cmAcquisitionResumed: begin
      {v0.50}
      if FAcqInfo = TAcqInfo(Msg.LParam) then
      {/v0.50}
      DoAfterResume;
    end;
    cmAcquisitionSuspended: begin
      {v0.50}
      if FAcqInfo = TAcqInfo(Msg.LParam) then
      {/v0.50}
      DoAfterSuspend;
    end;
    {/v0.19}
    {v0.21}
    cmAcquisitionAborted: begin
      {v0.50}
      if FAcqInfo = TAcqInfo(Msg.LParam) then
      {/v0.50}
      DoAcquisitionAborted;
    end;
    {/v0.21}
    {v0.24}
    cmAcqInfoDestroyed: begin
      if FAcqInfo = TAcqInfo(Msg.LParam) then
        DoAcqInfoDestroyed;
    end;
    {/v0.24}
  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 FActiveAcqData.ULI.SamplingInterval <> 0 then begin
      end;
      if FActiveAcqData.ULI.Duration <> 0 then begin
      end;
    end;
    {/v0.15}
  end;
end;

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

procedure TSpectrumForm.ULObjUpdated(var Msg: TMessage);
var o: TULObj;
{v0.50}
d: TAcqData;
{/v0.50}
begin
  o := TULObj(Msg.lParam);
  case o.RecID of
    ULPID, ULPRID: begin
      if IsDataListChild(o{v0.50},d {/v0.50}) then begin
        {v0.21}
        DoPeaksSizeChanged;
        {/v0.21}
        SetToRedraw(trPeaks);
        Redraw;
      end;
    end;
    ULBID, ULBRID: begin
      if IsDataListChild(o{v0.50},d {/v0.50}) 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;
  {v0.50}
  d: TAcqData;
  {/v0.50}
begin
{v0.20}{/v0.20
  if FDataChangeFromForm then
    exit;}
  o := TULObj(Msg.lParam);
  case o.RecID of
    ULPID, ULPRID: begin
      if IsDataListChild(o{v0.50}, d{/v0.50}) then begin
        {v0.50}
        if (o.RecID = ULPID) then begin
          d.ULP.UserUnregister(Self);
        end;
        {/v0.50}
        SetToRedraw(trPeaks);
        Redraw;
      end;
    end;
    ULBID, ULBRID: begin
      if IsDataListChild(o{v0.50}, d{/v0.50}) then begin
        {v0.50}
        if (o.RecID = ULBID) then begin
          d.ULB.UserUnregister(Self);
        end;
        {/v0.50}

        SetToRedraw(trBaseLine);
        Redraw;
      end;
    end;
    {v0.24}
    ULAID: begin
      AcqDataDestroyed(TULAObj(o));
    end;
    {/v0.24}
  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;

{v0.21}
procedure TSpectrumForm.WMWindowPosChanging(var Msg: TMessage);
begin
  inherited;
  if PWindowPos(Msg.lParam)^.HWND = Handle then begin
    {if CurML <> nil then
      CurML.Suspend;}
  end;
end;

procedure TSpectrumForm.WMWindowPosChanged(var Msg: TMessage);
begin
  inherited;
  if PWindowPos(Msg.lParam)^.HWND = Handle then begin
    if CurML <> nil then
      CurML.Suspend;
    UpdateScreenDisp;
    if CurML <> nil then
      CurML.Resume;
  end;
end;
{/v0.21}

{/Message response methods}

procedure TSpectrumForm.CopySpectrumToClipboard;
var
{  p: TPicture;}{tjpeg jpeg bitmap}
  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 tcanvas}
  {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;

{v0.65}
function TSpectrumForm.SaveSpectrumToBMP(const AFileName: TFileName): boolean;
var b: TBitmap;
begin
  Result := false;
  try
    b := TBitmap.Create;
    try
      b.Width := PaintBox.Width;
      b.Height := PaintBox.Height;
      DrawSpectrum(b.Canvas, ScreenDisp);
      b.SaveToFile(AFileName);
      Result := true;
    finally
      b.Free;
    end;
  except;
  end;
end;
{/v0.65}

{/v0.69}
procedure TSpectrumForm.UpdateViewLimitFromDataExtremes(AExtremes: TExtremes);
var
  amax, amin: TUserPoint;
  dy: TYValue;
  dx: TXValue;
begin
  with FActiveAcqData.SpectrumOpt.Extreme do begin
    if (exMaxX in AExtremes) or (exMinX in AExtremes) then begin
      if (abs(Max.X) > (MaxSingle / 100)) or (Max.X <= 0) then begin
        Max.X := DefMaxUsrXValue * 60;
        Min.X := MinAcqXValue;
      end;
    end;
    if (exMinY in AExtremes) or (exMaxY in AExtremes) then begin
      if abs(Max.Y) > (MaxSingle / 100) then begin
        Max.Y := DefMaxAcqYValue;
        Min.Y := DefMinUsrYValue;
      end;
    end;
  end;

  with UserViewLimit do begin
    //if FActiveAcqData.IsCalFile and (FActiveAcqData.ULP.ChildCount > 0) then begin
    // p := TULPRObj(FActiveAcqData.ULP.Childs[FActiveAcqData.ULP.ChildCount-1].X2;
    //FActiveAcqData.ExpToUser(FActiveAcqData.ULP.Childs[SpectrumOpt.Extreme.Max, Max);
    //FActiveAcqData.ExpToUser(FActiveAcqData.SpectrumOpt.Extreme.Min, Min);
    //end else
    begin;
      FActiveAcqData.ExpToUser(FActiveAcqData.SpectrumOpt.Extreme.Max, amax);
      FActiveAcqData.ExpToUser(FActiveAcqData.SpectrumOpt.Extreme.Min, amin);
    end;

    if exMinX in AExtremes then
      Min.X := amin.X;
    if exMaxX in AExtremes then
      Max.X := amax.X;
    if exMinY in AExtremes then
      Min.Y := {v0.73}amin.Y{/v0.73 amin.X};
    if exMaxY in AExtremes then
      Max.Y := amax.Y;
    dy := Max.Y - Min.Y;
    dx := Max.X - Min.X;
    if exMaxY in AExtremes then
      Max.Y := Min.Y + (dy / 100) * 120;//increase MaxY by 20%
    if exMinY in AExtremes then
      Min.Y := Min.Y - (dy / 100) * 10; // decrease MinY by 10%
    if exMaxX in AExtremes then
      Max.X := Max.X + (dx / 100) * 5;  // increase MaxX by 5%
  end;
end;
{/v0.69}

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}
    {v0.75}
    if ShowMessage(gettxt('Really delete all peaks?'), smYesNo, 0) = cmYes then
    {/v0.75}
    begin
      Spect.Peaks.Clear;
    end;
    {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;}
  FActiveAcqData.ULVO.ShowPeaks := true;
  //PeaksCheckBox.Checked := true;
  SetToRedraw(trPeaks);
  Redraw;
{  end;}
end;

procedure TSpectrumForm.PeaksHide;
begin
{  if PeaksMenuItem.Checked then begin
    PeaksMenuItem.Checked := false;}
  FActiveAcqData.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);
  {v0.21}
  CurML := ShowPosML;
  {/v0.21}
end;

procedure TSpectrumForm.ShowPosStop;
begin
  ShowPos_Btn.Down := false;
  SpectrumObjectShow(soXY, false);
  ShowPosML.Stop(LastMousePos.X, LastMousePos.Y);
  {v0.21}
  CurML := nil;
  {/v0.21}
end;

procedure TSpectrumForm.OverlayStart;
begin
  Overlay_Btn.Down := true;
  FOverlayMode := true;
  {v0.57}
  OvrMan.ShowPanel(nil);
  {/v0.57}
end;

procedure TSpectrumForm.OverlayStop;
begin
  Overlay_Btn.Down := false;
  FOverlayMode := false;
  {v0.57}
  OvrMan.HidePanel(nil);
  {/v0.57}
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 PeakCreateStart(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);
        {v0.21}
        CurML := MarginML;
        {/v0.21}

        started := true;
      end;
      {v0.10}
      dmShowPos: begin
        exit;
      end;
      {/v0.10}
    end;

    if not started then begin
      if IsInPeakEdge(X, Y, i) and PeakSizeStart(X) then begin
        {v0.21}{/v0.21 Screen.Cursor := crThinCrossUp;}
        started := true;
      end else if IsInBaseLineEdge(X,Y, {v0.25}FChangingBaseLine{/v0.25 i}, leftEdge)
        and BaseLineSizeStart(X, {v0.25}FChangingBaseLine{/v0.25 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
        {PeakToggleSelect(X, Shift);}
        if Spect.Peaks.Childs[i].IsFlagSet(rfSelected)
           and (not (ssShift in Shift))
           and (not (ssCtrl in Shift))
        then
          PeakEditProperties(i)
        else
          PeakToggleSelect(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
      {v0.54}
      if (Y < ScreenDisp.Top + PaintBox.Canvas.TextWidth('MMMMM'))
        and IsInPeak(X, i)
        and (Spect.Peaks.Childs[i] is TULPRObj) then
      begin
        FCurPeak := TULPRObj(Spect.Peaks.Childs[i]);
      end else begin
        FCurPeak := nil;
      end;
      {/v0.54}
      p.X := X;
      p.Y := Y;
      p := PaintBox.ClientToScreen(p);
      PopupMenu.Popup(p.X, p.Y);
    end;
  end;

  if started then begin
    {v0.56}
    if not FDragging then begin
      FDragging := true;
      LockDraw;
    end;
    {/v0.56
    FDragging := true;
    LockDraw;}
    {v0.21}
    {SetCapture(Handle);}
    {/v0.21}
  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 FActiveAcqData.ScreenToExp(sp, ScreenDisp, ep) then begin
      FActiveAcqData.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
    PeakFinish(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
          PeakFinish(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);
          {v0.21}
          CurML := nil;
          {/v0.21}
          
          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;
    {v0.21}
    {ReleaseCapture;}
    {/v0.21}
  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 FActiveAcqData = nil then
    exit;
  if not FActiveAcqData.ScreenToExp(sc1, OutDisp^, ap1) then
    exit;
  if not FActiveAcqData.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 FActiveAcqData.ExpToUser(ap1, up1) then
    exit;
  if not FActiveAcqData.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 FActiveAcqData <> nil then begin
  case so of
    soAxis: r := FActiveAcqData.ULVO.ShowAxis;
    soPeaks: r := FActiveAcqData.ULVO.ShowPeaks;
    soBaseLine: r := FActiveAcqData.ULVO.ShowBaseline;
    soXY: r := FActiveAcqData.ULVO.ShowXY;
    soHeader: r := FActiveAcqData.ULVO.ShowHeader;
    soReport: r := FActiveAcqData.ULVO.ShowReport;
    soData: r := FActiveAcqData.ULVO.ShowData;
  end;
  end;
  SpectrumObjectVisible := r;
end;

procedure TSpectrumForm.SpectrumObjectShow(so:TSpectrumObject; OnOff:boolean);
begin
  {v0.13}
  if FActiveAcqData = nil then
    exit;
  {/v0.13}
  ChangingView := true;
  try
    case so of
      soData: begin
        FActiveAcqData.ULVO.ShowData := OnOff;
      end;
      soBaseLine: begin
        {BaseLineMenuItem.Checked := OnOff;}
        //BaseLineCheckBox.Checked := OnOff;
        FActiveAcqData.ULVO.ShowBaseLine := OnOff;
        SetToRedraw(trBaseLine);
        Redraw;
      end;
      soAxis: begin
        FActiveAcqData.ULVO.ShowAxis := OnOff;
        {AxisItem.Checked := OnOff;}
        //AxisCheckBox.Checked := OnOff;
        SetToRedraw(trAxis);
        Redraw;
      end;
      soReport: begin
        FActiveAcqData.ULVO.ShowReport := OnOff;
        if OnOff then
          ShowReport
        else
          HideReport;
        {ReportMenuItem.Checked := OnOff;}
        //ReportCheckBox.Checked := OnOff;
      end;
      soPeaks: begin
        FActiveAcqData.ULVO.ShowPeaks := OnOff;
        if OnOff then
          PeaksShow
        else
          PeaksHide;
      end;
      soXY: begin
        FActiveAcqData.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
        FActiveAcqData.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);
  {v0.21}
  CurML := BaseLineML;
  {/v0.21}

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);
  {v0.21}
  CurML := BaseLineML;
  {/v0.21}

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);
    {v0.21}
    CurML := BaseLineML;
    {/v0.21}

  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);
    {v0.21}
    CurML := BaseLineML;
    {/v0.21}

  end;
  Result := true;
  {v0.21}
  Screen.Cursor := crThinCross;
  {/v0.21}
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 begin
    BaseLineML.Stop(0,0);
    {v0.21}
    CurML := nil;
    {/v0.21}
  end;
  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);
        {v0.21}
        CurML := nil;
        {/v0.21}
        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);
        {v0.21}
        CurML := nil;
        {/v0.21}

        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.PeakCreateStart(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);
  {v0.21}
  CurML := PeakML;
  {/v0.21}

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.PeakMoveStart(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);
  {v0.21}
  CurML := PeakML;
  {/v0.21}

end;

function TSpectrumForm.PeakSizeStart(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);
    {v0.21}
    CurML := PeakML;
    {/v0.21}

  end else begin
    PeakDefineMode := pdSizeLeft;
    l := OutDisp^.Left;
    if pp.p2.X >= 0 then begin
      l := pp.P2.X;
    end;
    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);
    {v0.21}
    CurML := PeakML;
    {/v0.21}
  end;
  {v0.21}
  Screen.Cursor := crThinCrossUp;
  {/v0.21}
end;

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

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

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


procedure TSpectrumForm.PeakFinish(X, Y: integer; Shift: TShiftState);
var p, start,stop:TPoint;
begin
  if PeakStartPoint.X >= 0 then begin
    PeakML.Stop(X, Y);
    {v0.21}
    CurML := nil;
    {/v0.21}
    PeakML.GetResult(p, PeakStopPoint);
    Spect.Peaks.DoChangeLock;
    try
      if PeakStartPoint.X = PeakStopPoint.X then begin
        PeakToggleSelect(PeakStartPoint.X, Shift);
      end else begin
        case PeakDefineMode of
          pdCreate: begin
            PeakCreate(PeakStartPoint, PeakStopPoint);
          end;
          pdMove: begin
            PeakML.GetObj(start, p, stop);
            PeakMove(start.X, stop.X);
          end;
          pdSizeRight: begin
            PeakSize(PeakStartPoint.X, PeakStopPoint.X, true);
          end;
          pdSizeLeft: begin
            PeakSize(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.PeakCreate(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.PeakCreate(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;
  FActiveAcqData.ULVO.ShowReport := true;
  {v0.69}
  ViewReportCheckBox.Checked := true;
  {/v0.69}
{  ReportMenuItem.Checked := true;}
end;

procedure TSpectrumForm.HideReport;
begin
  ReportPanel.Hide;
  FActiveAcqData.ULVO.ShowReport := false;
  {v0.69}
  ViewReportCheckBox.Checked := false;
  {/v0.69}
{  ReportMenuItem.Checked := false;}
end;

procedure TSpectrumForm.DoPeaksSizeChanged;
begin
  CreatePeakReport;
end;

procedure TSpectrumForm.CreatePeakReport;

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

  procedure AddPeak(p:TULPRObj);
  var
    up:TUserPoint;
    ap:TExpPoint;
  begin
    ap.X := p.X;
    ap.Y := p.Height;
    FActiveAcqData.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;{61458}
  {
    F001 - size control left
    F002 - size control right
    F003 - size control top
    F004 - size control left top
    F005 - size control top right
    F006 - size control down
    F007 - size control left down
    F008 - size control right down
    F009 - move control
    F00A - nothing
    F00B - size control top
    F00C - mouse up/down size control right edge(left/right)

  }
begin {winutl}
  ReleaseCapture;
  ReportPanel.Perform(WM_SysCommand,{v0.22}SC_DragMove{/v0.22 $F00D}, 0);{MESSAGES WINDOWS}
end;


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

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

procedure TSpectrumForm.RunButtonClick(Sender: TObject);
  (*procedure TryToRunBeforeStart;
  var
    ChannelName:TChannelName;
    Processor:TProcessor;
  begin
    {TAcqData spectrum}
    {TSeqPrg  SeqPrgu} {UliObju}
    ChannelName:=FActiveAcqData.ULI.ChannelName;
    if ChannelName='' then exit;
    Processor:=Processors.ProcessorGet(ChannelName);
    if Processor=nil then exit;

  end;*)
begin
  {v0.19}
  if FAcqInfo <> nil then begin
    {v0.51pi}
    {FActiveAcqData.Prg.HasBeforeStartLines;}
    if RunButtonMode = rbmPrepare then begin
      RunButtonMode := rbmRun;
      FActiveAcqData.Prg.RunLines(plkEvent, peBeforeStart);
      {TryToRunBeforeStart;}
    end else
    {/v0.51pi}
    begin
      FAcqInfo.Run;
      {v0.67}
      UpdateCaption;
      {/v0.67}
    end;
  end;
  {/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 FActiveAcqData.ULVO.ShowBaseLine));
end;

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

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

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

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

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

procedure TSpectrumForm.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  {v0.64}
  if (FAcqInfo <> nil) then begin
    if (FAcqInfo.OwnerForm = Self) then begin
      CanClose := FAcqInfo.CanClose
    end;
  end else begin
    if (FAcqDataList.Count > 0) then
      CanClose := TAcqData(FAcqDataList[0]).CanClose;
  end;
  {/v0.64
  if (FAcqInfo <> nil) and (FAcqInfo.OwnerForm = Self) then
    CanClose := FAcqInfo.CanClose
  else if (FAcqDataList.Count > 0) then
    CanClose := TAcqData(FAcqDataList[0]).CanClose;}
  ;
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.21}+trBaseLine{/v0.21});      {ulvoobju}
    {v0.20}
    {v0.25}
    if (FActiveAcqData <> nil) and FActiveAcqData.ULVO.AutoShowReport then
    {/v0.25}
      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 FActiveAcqData <> 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
  {v0.21}
  DrawBackGround(ACanvas, ADisp);
  {/v0.21}

  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) + ' ' + FActiveAcqData.ULA.SampleName);
    inc(y, hd + ch);
    TextOut(x,y, FActiveAcqData.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;}
  {v0.29}
  dts: string;
  dt:integer;
  al:TAlLabel;
  {/v0.29}

  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;
    FActiveAcqData.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;}{alrep}
    if LoadReport(ReportDir + repname, rep, frm) then {see testapp}
    begin
      {v0.28}
      if FLanguage <> nil then
        FLanguage.CheckForm(frm);
      {/v0.28}
      areaSum := 0;
      rep.AddNamedValue('SampleName', FActiveAcqData.ULA.SampleName);
      rep.AddNamedValue('SampleDesc', FActiveAcqData.ULA.SampleDesc);
      {v0.29}
      dts := '';
      dt := FActiveAcqData.ULA.DateTime;
      if dt <> 0 then
        dts := DateTimeToStr(FileDateToDateTime(dt));
      rep.AddNamedValue('DateTime', dts);
      al := TAlLabel(frm.FindComponent('VersionLbl'));
      if al <> nil then
        al.Caption := 'Chromulan v' + uLanVersion;{ulantype}
      {/v0.29}
      {v0.31}
      rep.AddNamedValue('FileName', ExtractFileName(FActiveAcqData.ULF.FileName));
      rep.AddNamedValue('CalStandard', ExtractFileName(FActiveAcqData.ULM.CalibrationFileName));
      rep.AddNamedValue('UserName', FActiveAcqData.ULA.UserName);
      rep.AddNamedValue('MultiplyFactor', FActiveAcqData.ULA.FindField('MultiplyFactor').AsUsrString);
      rep.AddNamedValue('DivideFactor', FActiveAcqData.ULA.FindField('DivideFactor').AsUsrString);
      {/v0.31}
      // 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(GetTxt({#}'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
      {v0.38}
      prn := AlPrinter;
      {/v0.38 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
    {v0.38}
    {/v0.38
    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
  {v0.75}
  if ShowMessage(gettxt('Really delete selected peaks?'), smYesNo, 0) = cmYes then
  {/v0.75}
  begin
    Spect.Peaks.ChildsDelete(ULPRID, rfSelected);
    DoPeaksSizeChanged;
    SetToRedraw(trPeaks);
    Redraw;
  end;
end;

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

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

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

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

procedure TSpectrumForm.MethodPeaksBrowseItemClick(Sender: TObject);
begin
  FActiveAcqData.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
  FActiveAcqData.ULM_ULB.Clear;
end;

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

procedure TSpectrumForm.BaseLineDeleteAll_ItemClick(Sender: TObject);
{var
  i: integer;
  p: TULBRObj;}
begin
  if Spect = nil then
    exit;
  {v0.75}
  if ShowMessage(gettxt('Really delete all baseline sections?'), smYesNo, 0) = cmYes then
  {/v0.75}
  begin
    Spect.BaseLine.ChildsDelete(ULBRID, 0);
    DoPeaksSizeChanged;
    SetToRedraw(trBaseLine);
    Redraw;
  end;
end;

procedure TSpectrumForm.BaseLineDeleteSelected_ItemClick(Sender: TObject);
begin
  if Spect = nil then
    exit;
  {v0.75}
  if ShowMessage(gettxt('Really delete selected  baseline sections?'), smYesNo, 0) = cmYes then
  {/v0.75}
  begin
    Spect.BaseLine.ChildsDelete(ULBRID, rfSelected);
    DoPeaksSizeChanged;
    SetToRedraw(trBaseLine);
    Redraw;
  end;
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);
  {v0.21}
  CurML := BaseLineML;
  {/v0.21}
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 < FAcqDataList.Count) then begin
      CurAcqDataIndex := i;
      DataSetCurrent(i);
    end;
  end;
end;

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

procedure TSpectrumForm.OverlayDeleteActive_ItemClick(Sender: TObject);
begin
  {v0.57}
  DataListDataClose(CurAcqDataIndex);
  {/v0.57
  if FAcqDataList.Count > 1 then begin
    if (CurAcqDataIndex >= 0) and (CurAcqDataIndex < FAcqDataList.Count) then begin
      DataListClear(CurAcqDataIndex);
      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
  {v0.64}
  MethodSaveDialog.InitialDir := MethodDir; //ulmtype
  if FActiveAcqData.ULM.MethodFileName <> '' then
    MethodSaveDialog.FileName := FActiveAcqData.ULM.MethodFileName;
  {/v0.64}
  if MethodSaveDialog.Execute then begin
    FActiveAcqData.MethodSaveToFile(MethodSaveDialog.FileName);
  end;
end;

procedure TSpectrumForm.MethodLoadFrom_ItemClick(Sender: TObject);
begin
  {v0.64}
  MethodOpenDialog.InitialDir := MethodDir;
  if FActiveAcqData.ULM.MethodTemplate <> '' then
    MethodOpenDialog.FileName := FActiveAcqData.ULM.MethodTemplate;
  {/v0.64}
  if MethodOpenDialog.Execute then begin
    FActiveAcqData.MethodLoadFromFile(MethodOpenDialog.FileName);
    {v0.64}
    UpdateControlsFromData;
    {/v0.64}
  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 := FActiveAcqData.ULM.CalibrationFileName;
  {v0.64}
  if fn = '' then begin
    fn := FActiveAcqData.ULF.FileName;
  end;
  CalibrationFileSelectDialog.InitialDir := DataDir;
  {/v0.64}
  if (fn <> '') and (FileExists(fn)) then
    CalibrationFileSelectDialog.FileName := fn;
  {v0.62}
  CalibrationFileSelectDialog.Filter := CalibrationFileDialogFilter;
  {/v0.62}
  if CalibrationFileSelectDialog.Execute then begin
    FActiveAcqData.CalibrationFileSelect(CalibrationFileSelectDialog.FileName);
    {v0.64}UpdateControlsFromData;{/v0.64}
    {v0.12}
    {v0.30 moved to TAcqData.CalibrationFileSelect}{/v0.30
    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(GetTxt({#}'Data math operation'), GetTxt({#}'Enter number to multiply X values with'), ''), r, code);
  if code = 0 then begin
    FActiveAcqData.DoMath(moMultiplyX, r);
    RedrawAll;
  end;
end;

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

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

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

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

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

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

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

{v0.21}
procedure TSpectrumForm.DataMathDilute_ItemClick(Sender: TObject);
var
  r:real;
  code: integer;
begin
  Val(InputBox(GetTxt({#}'Data math operation'),
    GetTxt({#}'Only every Nth Data point will be used, enter N'), ''), r, code);
  if code = 0 then begin
    FActiveAcqData.DoMath(moDilute, r);
    RedrawAll;
  end;
end;
{/v0.21}

{v0.33}
function TSpectrumForm.FindAcqData(const AFileName: string; var AAcqData: TAcqData): boolean;
var
  d: TAcqData;
  i: integer;
begin
  Result := false;
  for i := 0 to FAcqDataList.Count - 1 do begin
    d := TAcqData(FAcqDataList.Items[i]);
    if d.ULF.FileName = AFileName then begin
      AAcqData := d;
      Result := true;
      exit;
    end;
  end;
end;
{/v0.33}

{v0.43}
procedure TSpectrumForm.CMRelease(var Message: TMessage);
begin
  DebLog('SpecForm.CMRelease');
  inherited;
end;
{/v0.43}
{v0.54}
procedure TSpectrumForm.AssignMethodPeakItemClick(Sender: TObject);
var m: TULPRObj;
begin
  if FCurPeak = nil then
    exit;
  if {PickMethodPeakFrm.}
     PickMethodPeak(FActiveAcqData, FCurPeak, m, ShouldUpdateMethodPeakFromDataPeak) then
  begin
    Spect.PeakAssignPeakCharacterization(FCurPeak, m);
    if ShouldUpdateMethodPeakFromDataPeak then begin
      m.X :=  FCurPeak.X;
      m.X1 := FCurPeak.X1;
      m.X2 := FCurPeak.X2;
    end;
  end;
end;
{/v0.54}

{v0.58}
function TSpectrumForm.GetActiveAcqData: TAcqData;
begin
  if (DataList <> nil) and (CurAcqDataIndex >= 0) and (CurAcqDataIndex < DataList.Count) then
  begin
    Result := TAcqData(DataList.Items[CurAcqDataIndex]);
  end else begin
    Result := nil;
  end;
end;
{/v0.58}
{v0.65}
function TSpectrumForm.GetActiveChannelName: string;
begin
  Result := '';
  if FAcqInfo = nil then
    exit;
  if FActiveAcqData = nil then
    exit;
  {v0.66}
  if FActiveAcqData.Mode <> omCreate then
    exit;
  {/v0.66}
  Result := FActiveAcqData.Channel.ChannelName;
end;
{/v0.65}

{v0.67}
function TSpectrumForm.GetActiveChannelIndex: integer;
begin
  Result := -1;
  if FAcqInfo = nil then
    exit;
  if FActiveAcqData = nil then
    exit;
  if FActiveAcqData.Mode <> omCreate then
    exit;
  Result := FActiveAcqData.Channel.ChannelIndex;
end;
{/v0.67}

{v0.57}
procedure TSpectrumForm.SetActiveAcqData(AAcqData: TAcqData);
var i: integer;
begin
  for i := 0 to DataList.Count - 1 do begin
    if AAcqData = TAcqData(DataList.Items[i]) then begin
      {v0.58}
      CurAcqDataIndex := i;
      {/v0.58}
      DataSetCurrent(i);
      break;
    end;
  end;
end;

procedure TSpectrumForm.DataListDataClose(Index: integer);
begin
  if FAcqDataList.Count > 1 then begin
    if (Index >= 0) and (Index < FAcqDataList.Count) then begin
      DataListClear(Index);
      UpdateCaption;
      RedrawAll;
    end;
  end;
end;

procedure TSpectrumForm.DoOvrStateChanged;
begin
  if not (csDestroying in ComponentState) then
    OvrMan.PostUpdate;
end;

function TSpectrumForm.GetOvrMan: TOvrMan;
begin
  if FOvrMan = nil then
    FOvrMan := TOvrMan.Create(Self);
  Result := FOvrMan;
end;
{/v0.57}

procedure TSpectrumForm.OverlayMenu_ItemClick(Sender: TObject);
begin
  {v0.57}
{  OvrMan.ShowPanel(Sender);}
  {/v0.57}
end;

{v0.65}
procedure TSpectrumForm.SaveToBitmapFileItemClick(Sender: TObject);
var fn: string;
begin
  {ulobju}
  fn := '';
  if FileNameSaveSelect(fn, 'Save window to bitmap file', 'Bitmap (*.bmp)|*.bmp', '') then
    SaveSpectrumToBMP(fn);
end;
{/v0.65}

{v0.68}
function FindSpecForm(AChannelIndex: integer; var AForm: TSpectrumForm): boolean;
var
  i: integer;
  f: TForm;
  sf: TSpectrumForm absolute f;
begin
  Result := false;
  for i := 0 to Screen.FormCount - 1 do begin
    f := Screen.Forms[i];
    if f is TSpectrumForm then begin
      if sf.ActiveChannelIndex = AChannelIndex then begin
        AForm := sf;
        Result := true;
      end;
    end;
  end;
end;
{/v0.68}

{/v0.68}
procedure TSpectrumForm.ProgramItemClick(Sender: TObject);
begin
  if FActiveAcqData <> nil then
    FActiveAcqData.ProgramBrowse;
    {FActiveAcqData.Prg.Obj.Browse;}
end;

procedure TSpectrumForm.CalcTheorPlatesItemClick(Sender: TObject);
var
  s: string;
  np: extended;
  p1,p2: TExpPoint;
  u1,u2: TUserPoint;
begin
  if FCurPeak = nil then
    exit;
  if FActiveAcqData = nil then
    exit;
  np := FActiveAcqData.CalcTheorPlates(FCurPeak, p1, p2);
  FActiveAcqData.ExpToUser(p1, u1);
  FActiveAcqData.ExpToUser(p2, u2);
  s := Format('N = %.5g; t = %.3g; w(1/2) = %.3g; h(1/2) = %.3g', [np, FCurPeak.X, u2.X - u1.X, p1.Y]);
  //s := 'N = ' + Trim(RealToString(np, 10, 2)) + '; t = ' + Trim(RealToString(FCurPeak.X, 10, 2)) + '; w(1/2) = ' +
  //    Trim(RealToString(p2.X - p1.X, 10, 2)) + '; h(1/2) = ' + Trim(RealToString(p1.Y, 10, 2));{winutl}
  ShowMessage(GetTxt({#}'Theoretical plates: ' + s), smInfo, 0); {msgu}
end;
{/v0.77}
{procedure TSpectrumForm.Button1Click(Sender: TObject);
var o: TULObj;
begin
  o := AAA.DefPrg.AAPG;
  FActiveAcqData.AAPGAssign(AAA.DefPrg.AAPG);
end;}

end.
