{Copyright or © or Copr. :

CNRS - Laboratoire Mouvement & Perception
franck.buloup@univmed.fr

This software is a computer program whose purpose is to highly simplify  
the use of ADwinPro realtime systems when you develop experimental
protocols  on life's sciences area.

This software is governed by the CeCILL  license under French law and
abiding by the rules of distribution of free software.  You can  use, 
modify and/ or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info". 

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability. 

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or 
data to be ensured and,  more generally, to use and operate it in the 
same conditions as regards security. 

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.}
unit UGrapheInspectFrame;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, ExtCtrls, TeeProcs, TeEngine, Chart, StimuliClass, CheckLst,
  StdCtrls, Series, TeeFunci, Buttons;

type
  TGrapheInspectFrame = class(TFrame)
    Chart: TChart;
    Panel1: TPanel;
    GraphOptionsButton: TButton;
    Shape1: TShape;
    Shape2: TShape;
    zoomModeRB: TRadioButton;
    CursorModeRB: TRadioButton;
    MarkersPanel: TPanel;
    M1XLabel: TLabel;
    M2XLabel: TLabel;
    SignalPanel: TPanel;
    PreviousSignalSB: TSpeedButton;
    NextSignalSB: TSpeedButton;
    channelsCB: TComboBox;
    Label1: TLabel;
    computeTFDSB: TSpeedButton;
    Label2: TLabel;
    seuilModuleCB: TComboBox;
    procedure ChartDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure ChartDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure ChartMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure zoomModeRBClick(Sender: TObject);
    procedure CursorModeRBClick(Sender: TObject);
    procedure ChartScroll(Sender: TObject);
    procedure ChartMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure GraphOptionsButtonClick(Sender: TObject);
    procedure PreviousSignalSBClick(Sender: TObject);
    procedure NextSignalSBClick(Sender: TObject);
    procedure ChartResize(Sender: TObject);
    procedure ChartMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure computeTFDSBClick(Sender: TObject);
  private
		{ Déclarations privées }
		M1X,M1Y,M2X,M2Y,BoxSize,OldX,OldY : integer;
		tempM1X,tempM1Y,tempM2X,tempM2Y : double;
    Zooming : boolean;
    TheRectZoom : TRect;
		procedure drawMark(mark:string;X,Y:integer;tempx,tempy:double);
  public
    { Déclarations publiques }
    constructor Create(AOwner : TComponent);override;
		function addSerie(name,fileName : string; Fe : double) : TStimulus;
    procedure removeSerie(AStimulus : TStimulus);
	protected
		selectedSerie : TStimulus;
  end;

implementation

uses UInspectFrame, UGrapheOptionsInspect, UInspectTFDFrame;

{$R *.dfm}

constructor TGrapheInspectFrame.Create(AOwner : TComponent);
begin
	inherited Create(AOwner);
	M1X := -1;M2X := -1;BoxSize := 5;
end;

function TGrapheInspectFrame.addSerie(name,fileName : string; Fe : double) : TStimulus;
var courbe : TStimulus;
begin
  delete(name,pos('.',name),length(name));
	courbe := TStimulus.CreateStimulus(name,0,Fe,Chart);
  courbe.ImportFromBinFile(fileName,true);
	courbe.LinePen.Width := 2;
	selectedSerie := courbe;
  //channelsCB.Items.Add(name);
  channelsCB.Items.AddObject(name,selectedSerie);
  Result := courbe;
end;

procedure TGrapheInspectFrame.removeSerie(AStimulus : TStimulus);
begin
  if AStimulus.ParentChart.SeriesCount = 1 then
  begin
    AStimulus.Free;
    Self.Free;
	end else
  begin
    channelsCB.Items.Delete(channelsCB.Items.IndexOf(AStimulus.Title));
    AStimulus.Free;
  end;
end;

procedure TGrapheInspectFrame.ChartDragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var theCheckListBox : TCheckListBox;
begin
  Accept := false;
  if Source.ClassName = 'TCheckListBox' then
  begin
    theCheckListBox := (Source as TCheckListBox);
    if not theCheckListBox.Checked[theCheckListBox.ItemIndex]
    then accept := true;
  end;
end;

procedure TGrapheInspectFrame.ChartDragDrop(Sender, Source: TObject; X,
  Y: Integer);
var theCheckListBox : TCheckListBox;
    theInspectFrame : TInspectFrame;
    theStimulus : TStimulus;
begin
  theCheckListBox := (Source as TCheckListBox);
  if theCheckListBox.Parent.Parent.ClassName = 'TInspectFrame' then
  begin
    theInspectFrame := theCheckListBox.Parent.Parent as TInspectFrame;
		theInspectFrame.addSerieToGraphe(Self,theCheckListBox.Items[theCheckListBox.itemIndex]);
		if theCheckListBox.Items.Objects[theCheckListBox.itemIndex] <> nil then
		begin
			theStimulus := theCheckListBox.Items.Objects[theCheckListBox.itemIndex] as TStimulus;
			theStimulus.Stairs := (Chart.Series[0] as TLineSeries).Stairs;
			theStimulus.LinePen.Width := (Chart.Series[0] as TLineSeries).LinePen.Width;
			theStimulus.Pointer.Visible := (Chart.Series[0] as TLineSeries).Pointer.Visible;
			theStimulus.Marks.Visible := (Chart.Series[0] as TLineSeries).Marks.Visible;
			theStimulus.Pointer.Style := (Chart.Series[0] as TLineSeries).Pointer.Style;
			theStimulus.Pointer.HorizSize := (Chart.Series[0] as TLineSeries).Pointer.HorizSize;
			theStimulus.Pointer.VertSize := (Chart.Series[0] as TLineSeries).Pointer.VertSize;
			theCheckListBox.Checked[theCheckListBox.itemIndex] := true;
			M1x := -1;
			M2x := -1;
		end;
	end;
end;

procedure TGrapheInspectFrame.drawMark(mark:string;X,Y:integer; tempx,tempy:double);
var i,YPos,XPos,MX,MY : integer;
		markColor : TColor;
		MXLabel : TLabel;
		sx1,sx2,sy1,sy2 : string;
		da,dt : double;
begin
	MX := -1;
	MY := -1;
	markColor := clBlack;
	MXLabel := nil;
	if mark = 'M1' then
	begin
		MX := M1X;
		MY := M1Y;
		markColor := clRed;
		MXLabel := M1XLabel;
	end;
	if mark = 'M2' then
	begin
		MX := M2X;
		MY := M2Y;
		markColor := clBlue;
		MXLabel := M2XLabel;
	end;

	Chart.Canvas.Pen.Color := markColor;
	Chart.Canvas.Pen.Mode := pmXor;
	Chart.Canvas.Pen.Style := psSolid;
	Chart.Canvas.Pen.Width := 3;
	if(MX>-1) then
	begin
		Chart.Canvas.MoveTo(MX,Chart.ChartRect.Top);
		Chart.Canvas.LineTo(MX,Chart.ChartRect.Bottom);
	end;

	Chart.Canvas.MoveTo(X,Chart.ChartRect.Top);
	Chart.Canvas.LineTo(X,Chart.ChartRect.Bottom);
	i := 0;
	if selectedSerie.Count >0 then
	begin
		repeat
			if abs(tempX-selectedSerie.XValues[i]) < 1/selectedSerie.Fe then
			begin
				Chart.Canvas.Pen.Width := 2;
				if(MX>-1) then
				Chart.Canvas.Rectangle(MX-BoxSize+1,MY-BoxSize+1,MX+BoxSize+1,MY+BoxSize+1);
				YPos := selectedSerie.CalcYPosValue(selectedSerie.YValues[i]);
				XPos := selectedSerie.CalcXPosValue(tempX);
				MX := XPos;
				MY := YPos;
				Chart.Canvas.Rectangle(XPos-BoxSize+1,YPos-BoxSize+1,Xpos+BoxSize+1,YPos+BoxSize+1);
				MXLabel.Caption := '(' + selectedSerie.GetVertAxis.LabelValue(selectedSerie.YValues[i]) + ','  + selectedSerie.GetHorizAxis.LabelValue(tempX) + ')';
			end;
			inc(i);
		until (abs(tempX-selectedSerie.XValues[i-1]) < 1/selectedSerie.Fe) or (i = selectedSerie.Count);
	end;
	if (MX + MXLabel.width) < Chart.Width
	then MXLabel.Left := MX
	else MXLabel.Left :=  Chart.Width - MXLabel.width;

  if mark = 'M1' then
	begin
		M1X := MX;
		M1Y := MY;
	end;
	if mark = 'M2' then
	begin
		M2X := MX;
		M2Y := MY;
	end;

	sy1 := M1XLabel.Caption;
	System.delete(sy1,1,1);
	System.delete(sy1,System.length(sy1),1);
	sx1 := sy1;
	System.delete(sy1,System.pos(',',sy1),System.length(sy1));
	System.delete(sx1,1,System.pos(',',sx1));
	sy2 := M2XLabel.Caption;
	System.delete(sy2,1,1);
	System.delete(sy2,System.length(sy2),1);
	sx2 := sy2;
	System.delete(sy2,System.pos(',',sy2),System.length(sy2));
	System.delete(sx2,1,System.pos(',',sx2));

	if(sy2='') then sy2:='0.0';
	if(sy1='') then sy1:='0.0';

  while pos(ThousandSeparator,sy1) <> 0 do
  begin
    delete(sy1,pos(ThousandSeparator,sy1),1);
  end;

  while pos(ThousandSeparator,sy2) <> 0 do
  begin
    delete(sy2,pos(ThousandSeparator,sy2),1);
  end;

	da := strtofloat(sy2)-strtofloat(sy1);

	if(sx2='') then sx2:='0.0';
	if(sx1='') then sx1:='0.0';
	dt := strtofloat(sx2)-strtofloat(sx1);

	signalPanel.Caption := selectedSerie.Title + ' - dA = ' + floattostr(da) + ' - dt = ' + floattostr(dt);
end;

procedure TGrapheInspectFrame.ChartMouseDown(Sender: TObject;
	Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
	if (PtInRect(Chart.ChartRect,Chart.ScreenToClient(Mouse.CursorPos))) then
	begin
		if cursorModeRB.Checked then
		begin
			if Button = mbLeft then
			begin
				selectedSerie.GetCursorValues(tempM1x,tempM1y);
				drawMark('M1',X,Y,tempM1x,tempM1y);
			end;
			if Button = mbRight then
			begin
				selectedSerie.GetCursorValues(tempM2x,tempM2y);
				drawMark('M2',X,Y,tempM2x,tempM2y);
			end;
		end;
    if zoomModeRB.Checked then
    begin
      if Button = mbLeft then
      begin
        TheRectZoom.Left := X;
        TheRectZoom.Top := Y;
        OLDX := X; OLDY := Y;
        zooming := true;
      end;
    end;
  end;
end;

procedure TGrapheInspectFrame.zoomModeRBClick(Sender: TObject);
begin
	Chart.AllowZoom := zoomModeRB.Checked;
	case Chart.AllowZoom of
		true : Chart.AllowPanning := pmBoth;
		false: Chart.AllowPanning := pmNone;
	end;


	Chart.Canvas.Pen.Color := clRed;
	Chart.Canvas.Pen.Mode := pmXor;
	Chart.Canvas.Pen.Style := psSolid;
	Chart.Canvas.Pen.Width := 3;
	Chart.Canvas.MoveTo(M1X,Chart.ChartRect.Top);
	Chart.Canvas.LineTo(M1X,Chart.ChartRect.Bottom);
	M1X := -1;
	Chart.Canvas.Pen.Color := clBlue;
	Chart.Canvas.Pen.Mode := pmXor;
	Chart.Canvas.Pen.Style := psSolid;
	Chart.Canvas.Pen.Width := 3;
	Chart.Canvas.MoveTo(M2X,Chart.ChartRect.Top);
	Chart.Canvas.LineTo(M2X,Chart.ChartRect.Bottom);
	M2X := -1;

	MarkersPanel.Visible := false;
end;

procedure TGrapheInspectFrame.CursorModeRBClick(Sender: TObject);
begin
	Chart.AllowZoom := not cursorModeRB.Checked;
	case Chart.AllowZoom of
		true : Chart.AllowPanning := pmBoth;
		false: Chart.AllowPanning := pmNone;
	end;
  SignalPanel.Caption := selectedSerie.Title;
	MarkersPanel.Visible := true;
end;

procedure TGrapheInspectFrame.ChartScroll(Sender: TObject);
begin
	M1X := -1;
	M2X := -1;
end;

procedure TGrapheInspectFrame.ChartMouseMove(Sender: TObject;
	Shift: TShiftState; X, Y: Integer);
begin
	if (PtInRect(Chart.ChartRect,Chart.ScreenToClient(Mouse.CursorPos))) then
	begin
		if not Chart.AllowZoom then
		begin
			if ssLeft in Shift then
			begin
				selectedSerie.GetCursorValues(tempM1x,tempM1y);
				drawMark('M1',X,Y,tempM1x,tempM1y);
			end;
			if ssRight in Shift then
			begin
				selectedSerie.GetCursorValues(tempM2x,tempM2y);
				drawMark('M2',X,Y,tempM2x,tempM2y);
			end;
		end else
    begin
      if Zooming then
        begin
          with Chart,Canvas do
          begin
            Pen.Color := clInfoBk;
            Pen.Style := psSolid;
            Pen.Mode := pmXor;
            Pen.Width := 1;
            MoveTo(TheRectZoom.Left,TheRectZoom.Top);
            LineTo(TheRectZoom.Left,OLDY);
            MoveTo(TheRectZoom.Left,TheRectZoom.Top);
            LineTo(OLDX,TheRectZoom.Top);
            MoveTo(OLDX,OLDY);
            LineTo(OLDX,TheRectZoom.Top);
            MoveTo(OLDX,OLDY);
            LineTo(TheRectZoom.Left,OLDY);

            MoveTo(TheRectZoom.Left,TheRectZoom.Top);
            LineTo(TheRectZoom.Left,Y);
            MoveTo(TheRectZoom.Left,TheRectZoom.Top);
            LineTo(X,TheRectZoom.Top);
            MoveTo(X,Y);
            LineTo(X,TheRectZoom.Top);
            MoveTo(X,Y);
            LineTo(TheRectZoom.Left,Y);
            OLDX := X; OLDY := Y;
          end;
        end;
    end;
  end;
end;

procedure TGrapheInspectFrame.GraphOptionsButtonClick(Sender: TObject);
begin
  GraphOptionsInspectForm.AChart := Chart;
	GraphOptionsInspectForm.ShowModal;
	M1x := -1;
	M2x := -1;
end;

procedure TGrapheInspectFrame.PreviousSignalSBClick(Sender: TObject);
var i : integer;
begin
	i := -1 ;
	repeat
		inc(i);
		if selectedSerie = Chart.SeriesList[i]
		then break;
	until i = Chart.SeriesList.Count - 1;
	if i = 0
	then selectedSerie := Chart.SeriesList[chart.SeriesList.count - 1] as TStimulus
	else selectedSerie := Chart.SeriesList[i-1] as TStimulus;
	DrawMark('M1',M1X,M1Y,tempM1X,tempM1Y);
	DrawMark('M2',M2X,M2Y,tempM2X,tempM2Y);
end;

procedure TGrapheInspectFrame.NextSignalSBClick(Sender: TObject);
var i : integer;
begin
	i := -1 ;
	repeat
		inc(i);
		if selectedSerie = Chart.SeriesList[i]
		then break;
	until i = Chart.SeriesList.Count - 1;
	if i = Chart.SeriesList.Count - 1
	then selectedSerie := Chart.SeriesList[0] as TStimulus
	else selectedSerie := Chart.SeriesList[i+1] as TStimulus;
	DrawMark('M1',M1X,M1Y,tempM1X,tempM1Y);
	DrawMark('M2',M2X,M2Y,tempM2X,tempM2Y);
end;

procedure TGrapheInspectFrame.ChartResize(Sender: TObject);
begin
	M1X := -1;
	M2X := -1;
end;

procedure TGrapheInspectFrame.ChartMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    TheRectZoom.Right := X;
    TheRectZoom.Bottom := Y;
    Zooming := false;
  end;
end;

procedure TGrapheInspectFrame.computeTFDSBClick(Sender: TObject);
var tfdFrame : TInspectTFDFrameForm;
    AStimulus : TStimulus;
begin
  if channelsCB.ItemIndex <> -1 then
  begin
    AStimulus := channelsCB.Items.Objects[channelsCB.ItemIndex] as TStimulus;
    tfdFrame := TInspectTFDFrameForm.Create(Self.Owner,AStimulus,strtofloat(seuilModuleCB.Text));
  end;
end;

end.
