unit TagViewer;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, pFIBDatabase, StdCtrls, ExtCtrls, TeeProcs, TeEngine, Chart,
  DbChart, ComCtrls, ActnList, ToolWin, ActnMan, ActnCtrls,
  XPStyleActnCtrls, FIBDatabase, DB, FIBDataSet, pFIBDataSet,
  tvGroup, janXMLParser2, ImgList, Menus;


type
  TTagViewerForm = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Splitter1: TSplitter;
    tree: TTreeView;
    ActionManager1: TActionManager;
    ActionToolBar1: TActionToolBar;
    ActionToolBar2: TActionToolBar;
    acGroupAdd: TAction;
    acGroupDel: TAction;
    acGroupEdit: TAction;
    acTrendGroupNew: TAction;
    Splitter2: TSplitter;
    listNav: TListView;
    acUpdate: TAction;
    Panel5: TPanel;
    listTrend: TListView;
    Label1: TLabel;
    Panel6: TPanel;
    Chart: TDBChart;
    ds: TpFIBDataSet;
    pFIBTransaction1: TpFIBTransaction;
    Splitter3: TSplitter;
    Panel3: TPanel;
    ImageList1: TImageList;
    acTrendTagNew: TAction;
    acTrendClear: TAction;
    acPrint: TAction;
    acSizeDec: TAction;
    acSizeInc: TAction;
    acBack: TAction;
    acForward: TAction;
    acPeriod: TAction;
    acOnLine: TAction;
    acMemPeriod: TAction;
    acRstPeriod: TAction;
    acOnlyTime: TAction;
    acGoto: TAction;
    Timer: TTimer;
    lbDtBeg: TLabel;
    Label3: TLabel;
    lbDtEnd: TLabel;
    Label2: TLabel;
    lbDelta: TLabel;
    acZoomReset: TAction;
    PrinterSetupDialog: TPrinterSetupDialog;
    ImageList2: TImageList;
    acClone: TAction;
    PopupMenu1: TPopupMenu;
    PopupMenu2: TPopupMenu;
    PopupMenu3: TPopupMenu;
    N20: TMenuItem;
    N21: TMenuItem;
    N22: TMenuItem;
    N23: TMenuItem;
    N24: TMenuItem;
    N25: TMenuItem;
    N26: TMenuItem;
    N27: TMenuItem;
    N28: TMenuItem;
    N29: TMenuItem;
    N30: TMenuItem;
    N31: TMenuItem;
    N32: TMenuItem;
    N33: TMenuItem;
    N34: TMenuItem;
    N35: TMenuItem;
    N36: TMenuItem;
    N37: TMenuItem;
    N38: TMenuItem;
    procedure FormDestroy(Sender: TObject);
    procedure acGroupAddEditExecute(Sender: TObject);
    procedure treeChange(Sender: TObject; Node: TTreeNode);
    procedure acTrendGroupNewExecute(Sender: TObject);
    procedure acUpdateExecute(Sender: TObject);
    procedure acBackForwardExecute(Sender: TObject);
    procedure ChartScroll(Sender: TObject);
    procedure ChartMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure ChartZoom(Sender: TObject);
    procedure acPeriodExecute(Sender: TObject);
    procedure TimerTimer(Sender: TObject);
    procedure acOnLineExecute(Sender: TObject);
    procedure acOnlyTimeExecute(Sender: TObject);
    procedure acSizeDecExecute(Sender: TObject);
    procedure acSizeIncExecute(Sender: TObject);
    procedure acGotoExecute(Sender: TObject);
    procedure listTrendAdvancedCustomDrawSubItem(Sender: TCustomListView;
      Item: TListItem; SubItem: Integer; State: TCustomDrawState;
      Stage: TCustomDrawStage; var DefaultDraw: Boolean);
    procedure listTrendClick(Sender: TObject);
    procedure acZoomResetExecute(Sender: TObject);
    procedure acMemPeriodExecute(Sender: TObject);
    procedure acRstPeriodExecute(Sender: TObject);
    procedure treeDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure treeDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure acGroupDelExecute(Sender: TObject);
    procedure acTrendClearExecute(Sender: TObject);
    procedure acTrendTagNewExecute(Sender: TObject);
    procedure acPrintExecute(Sender: TObject);
    procedure listTrendColumnClick(Sender: TObject; Column: TListColumn);
    procedure acCloneExecute(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FScrollFlag: boolean;

    OptimizedSelect: boolean;

    procedure listTrendClear;
    procedure SetTrendLine;
    procedure AddTagToTrend(ATag: TtvTag);
    { Private declarations }
  public
    CurGroup: TtvGroup;
    MemDtBeg, MemDtEnd: TDatetime;

    db: TpFIBDatabase;
    fname: string;
    Readonly: boolean;

    procedure Init;
    procedure Load;
    procedure Save;
  end;

var
  TagViewerForm: TTagViewerForm;

implementation
uses
  tvGroupProp, tvTagList, tvPeriod, Series, tvGoto, tvClone, hyperstr;

{$R *.dfm}

{ TTagViewerForm }

////////////////////////////////////////////////////////////////////////////////
// DESTROY
////////////////////////////////////////////////////////////////////////////////
procedure TTagViewerForm.FormDestroy(Sender: TObject);
var
  i: integer;
begin
  for i:=0 to tree.Items.Count-1 do
    if tree.Items[i].Data <> nil then
      TtvGroup(tree.Items[i].Data).Free;
end;

////////////////////////////////////////////////////////////////////////////////
// INIT
////////////////////////////////////////////////////////////////////////////////
procedure TTagViewerForm.Init;
begin
  // установка DB
  ds.Database := db;
  ds.Transaction.DefaultDatabase := db;

  OptimizedSelect := false;
  if db.QueryValue(
        'select count(*) from RDB$PROCEDURES where RDB$PROCEDURE_NAME=''GETTRENDEXA''', 0)>0
  then
    OptimizedSelect := true;

  Load;

  if Readonly then begin
    acGroupAdd.Enabled := false;
    acGroupDel.Enabled := false;
    acGroupEdit.Enabled := false;
    acClone.Enabled := false;
    tree.DragMode := dmManual;
    ActionToolBar1.Visible := false;
  end;
end;

////////////////////////////////////////////////////////////////////////////////
// LOAD
////////////////////////////////////////////////////////////////////////////////
procedure TTagViewerForm.Load;

    procedure _load_group(xnode: TjanXMLNode2; treenode: TTreeNode);
    var
      i,k,j: integer;
      g: TtvGroup;
    begin
      with xnode do begin
        g := TtvGroup.Create;
        g.Name := text;
        g.UnitName := forceChildByName('UnitName').text;
        g.Descr := forceChildByName('Descr').text;

        g.UseColor := attribute['UseColor'] = '1';
        Val( attribute['Color'], g.Color, i);
        Val( attribute['DefaultDelta'], g.DefaultDelta, i);
        Val( attribute['DefaultYmin'], g.DefaultYmin, i);
        Val( attribute['DefaultYmax'], g.DefaultYmax, i);

        treenode := tree.Items.AddChildObject(treenode, g.Name, g);

        with forceChildByName('Tags') do
          for i:=0 to nodes.Count-1 do
            with TjanXMLNode2(nodes[i]) do begin
              k := length(g.Tags);
              SetLength(g.Tags, k+1);
              g.Tags[k] := TtvTag.Create;
              g.Tags[k].Name := text;
              g.Tags[k].Descr := forceChildByName('Descr').text;;
              g.Tags[k].LegName := forceChildByName('LegName').text;;
              g.Tags[k].Transform := forceChildByName('Transform').text;;

              g.Tags[k].UseColor := attribute['UseColor'] = '1';
              Val( attribute['Color'], g.Tags[k].Color, j);
              Val( attribute['Width'], g.Tags[k].Width, j);
              g.Tags[k].Stairs := attribute['Stairs'] = '1';
              g.Tags[k].Pointers := attribute['Pointers'] = '1';

            end;

        with forceChildByName('Groups') do
          for i:=0 to nodes.Count-1 do
            _load_group( TjanXMLNode2(nodes[i]), treenode);

      end;
    end;

var
  i: integer;
  dom: TjanXMLParser2;
//  node: TjanXMLNode2;
begin
  dom := TjanXMLParser2.create;
  try
    dom.LoadXML(fname);

    tree.Items.Clear;
    tree.Items.Add(nil, 'Графики');

    for i:=0 to dom.nodes.Count-1 do begin
      _load_group( TjanXMLNode2(dom.nodes[i]), tree.Items[0]);
    end;
    
  finally
    dom.Free;
  end;

  tree.AlphaSort;
end;

////////////////////////////////////////////////////////////////////////////////
// SAVE
////////////////////////////////////////////////////////////////////////////////
procedure TTagViewerForm.Save;

    procedure _save_group(xnode: TjanXMLNode2; treenode: TTreeNode);
    var
      i: integer;
      g: TtvGroup;
      node: TjanXMLNode2;
      s: string;
    begin
      g := TtvGroup(treenode.Data);
      with xnode.addChildByName('g') do begin
        text := g.Name;
        addChildByName('UnitName').text := g.UnitName;
        addChildByName('Descr').text := g.Descr;

        
        if g.UseColor then attribute['UseColor'] := '1';

        str( g.Color, s);
        attribute['Color'] := s;

        str( g.DefaultDelta, s);
        attribute['DefaultDelta'] := s;

        str( g.DefaultYmin, s);
        attribute['DefaultYmin'] := s;

        str( g.DefaultYmax, s);
        attribute['DefaultYmax'] := s;

        
        with addChildByName('Tags') do
          for i:=0 to length(g.Tags)-1 do
            with addChildByName('t') do begin
              text := g.tags[i].Name;
              addChildByName('Descr').text := g.tags[i].Descr;
              addChildByName('LegName').text := g.tags[i].LegName;
              addChildByName('Transform').text := g.tags[i].Transform;

              str( g.tags[i].Width, s);
              attribute['Width'] := s;

              if g.tags[i].UseColor then attribute['UseColor'] := '1';

              str( g.tags[i].Color, s);
              attribute['Color'] := s;

              if g.tags[i].Stairs then attribute['Stairs'] := '1';
              if g.tags[i].Pointers then attribute['Pointers'] := '1';
            end;

        node := addChildByName('Groups');
        with node do
          for i:=0 to treenode.Count-1 do
            if treenode.Item[i].Data<>nil then
              if TObject(treenode.Item[i].Data).ClassType = TtvGroup then
                _save_group(node, treenode.Item[i]);

      end;
    end;

var
  i: integer;
  dom: TjanXMLParser2;
begin
  dom := TjanXMLParser2.create;
  try

    dom.name := 'root';
    for i:=0 to tree.Items[0].Count-1 do
      _save_group(dom, tree.Items[0].Item[i]);

    dom.SaveXML(fname);
  finally
    dom.Free;
  end;

end;


////////////////////////////////////////////////////////////////////////////////
// ADD / EDIT GROUP
////////////////////////////////////////////////////////////////////////////////
procedure TTagViewerForm.acGroupAddEditExecute(Sender: TObject);
var
  newflag: boolean;
  w: TtvGroupPropForm;
begin
  if tvTagListForm=nil then begin
    tvTagListForm := TtvTagListForm.Create(self);
    tvTagListForm.db := db;
    tvTagListForm.Init;
  end;

  newflag := Sender = acGroupAdd;

  if tree.Selected=nil then tree.Selected := tree.Items[0];

  if not newflag then
    if tree.Selected.Data = nil then exit;

  w := TtvGroupPropForm.Create(self);
  try

    if newflag then
      w.M := nil
    else
      w.M := TtvGroup( tree.Selected.data );

    if w.ShowModal=mrOk then begin
      if newflag then begin
        tree.Selected := tree.Items.AddChildObject(tree.Selected, w.M.Name, w.M);
      end;

      tree.Selected.Text := w.M.Name;
      Save;
    end;
  finally
    w.Free;
  end;

  treeChange(nil,tree.Selected);
end;

procedure TTagViewerForm.treeChange(Sender: TObject; Node: TTreeNode);
var
  i: integer;
begin
  listNav.Items.Clear;
  if node.Data=nil then exit;

  with TtvGroup(node.Data) do begin
    for i:=0 to length(Tags)-1 do
      with listNav.Items.Add do begin
        Caption := Tags[i].GetLegname;
        Data := @Tags[i];
      end;
  end;
end;


procedure TTagViewerForm.listTrendClear;
var
  i: integer;
begin
  for i:=0 to listTrend.Items.Count-1 do
    TtvTrendTag(listTrend.Items[i].Data).Free;

  listTrend.Items.Clear;
end;



procedure TTagViewerForm.AddTagToTrend(ATag: TtvTag);
var
  M: TtvTrendTag;
begin
    M := TtvTrendTag.Create(db, chart);
    with ATag do begin
      M.Name := Name;

      M.Descr := GetLegname;
      M.Transform := Transform;
      M.Width := Width;
      M.UseColor := UseColor;
      M.Color := Color;
      M.Stairs := Stairs;
      M.Pointers := Pointers;
    end;
    M.Init(OptimizedSelect);

    with listTrend.Items.Add do begin
      Data := M;
      Caption := M.Descr;
      SubItems.Add('');
      if M.IdTag <> -1 then
        SubItems.Add('')
      else
        SubItems.Add('нет!');
      Checked := true;
      StateIndex := 1;
    end;
end;    


////////////////////////////////////////////////////////////////////////////////
// Отображение группы на тренде
////////////////////////////////////////////////////////////////////////////////
procedure TTagViewerForm.acTrendGroupNewExecute(Sender: TObject);
var
  i: integer;
begin
  if not db.Connected then exit;

  if tree.Selected = nil then exit;

  tree.Selected.Expanded := not tree.Selected.Expanded;

  if tree.Selected.Data = nil then exit;
  CurGroup := TtvGroup(tree.Selected.Data);

  if CurGroup.UseColor then
    Chart.Color := CurGroup.Color
  else
    Chart.Color := clWindow;

  Chart.Title.Visible := true;
  Chart.Title.Text.Text := CurGroup.Name;

  Chart.Foot.Visible := trim(CurGroup.Descr)<>'';
  Chart.Foot.Text.Text := CurGroup.Descr;

  Chart.LeftAxis.Title.Caption := CurGroup.UnitName;


  if length(CurGroup.Tags)=0 then exit;
  listTrendClear;

  if sender<>nil then
    for i:=0 to length(CurGroup.Tags)-1 do
      AddTagToTrend(CurGroup.Tags[i]);

  for i:=0 to listTrend.Items.Count-1 do
    with TtvTrendTag(listTrend.Items[i].Data) do
      if srs.SeriesColor = clWhite then
        srs.SeriesColor := clBlack;


//  if (CurGroup.dtBeg=0) then begin
//    CurGroup.dtBeg := StrToDateTime('22.07.2005 13:45:00');
//    CurGroup.dtEnd := StrToDateTime('22.07.2005 14:00:00');
//  end;

  if (CurGroup.dtBeg=0) then begin
    CurGroup.dtEnd := now;
    CurGroup.dtBeg := CurGroup.dtEnd - CurGroup.DefaultDelta;
  end;

  if (CurGroup.Ymin=0) and (CurGroup.Ymax=0) then begin
    CurGroup.Ymax := CurGroup.DefaultYmin;
    CurGroup.Ymin := CurGroup.DefaultYmax;
  end;

  acUpdateExecute(nil);
end;

// UPDATE
procedure TTagViewerForm.acUpdateExecute(Sender: TObject);
var
  i: integer;
  delta: TDatetime;
begin
  if CurGroup=nil then exit;

  Chart.UndoZoom;
  Chart.LeftAxis.SetMinMax(CurGroup.Ymin, CurGroup.Ymax);

  if CurGroup=nil then exit;
  for i:=0 to listTrend.Items.Count-1 do
    with TtvTrendTag(listTrend.Items[i].Data) do begin
      Open( CurGroup.dtBeg, CurGroup.dtEnd );
    end;

  Chart.BottomAxis.SetMinMax(CurGroup.dtBeg, CurGroup.dtEnd);

  lbDtBeg.Caption := FormatDateTime('dd-mm-yyyy hh:nn:ss', CurGroup.dtBeg);
  lbDtEnd.Caption := FormatDateTime('dd-mm-yyyy hh:nn:ss', CurGroup.dtEnd);

  delta := CurGroup.dtEnd - CurGroup.dtBeg;
  lbDelta.Caption := inttostr(trunc(delta)) + ' дней, ' +  FormatDateTime('hh:nn:ss', delta);

  SetTrendLine;
end;

//     BACK <==    FORWARD ==>
procedure TTagViewerForm.acBackForwardExecute(Sender: TObject);
var
  dtDelta: TDatetime;
begin
  if CurGroup=nil then exit;
  dtDelta := CurGroup.dtEnd - CurGroup.dtBeg;
  if Sender = acBack then begin
    CurGroup.dtEnd := CurGroup.dtEnd - dtDelta;
    CurGroup.dtBeg := CurGroup.dtBeg - dtDelta;
  end else begin
    CurGroup.dtEnd := CurGroup.dtEnd + dtDelta;
    CurGroup.dtBeg := CurGroup.dtBeg + dtDelta;
  end;

  acUpdateExecute(nil);
end;

// CHART SCROLL
procedure TTagViewerForm.ChartScroll(Sender: TObject);
begin
  FScrollFlag := true;
end;

// CHART MOUSE UP
procedure TTagViewerForm.ChartMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if CurGroup=nil then exit;

  if FScrollFlag then begin
    CurGroup.dtBeg := Chart.BottomAxis.Minimum;
    CurGroup.dtEnd := Chart.BottomAxis.Maximum;

    CurGroup.Ymax := Chart.LeftAxis.Maximum;
    CurGroup.Ymin := Chart.LeftAxis.Minimum;

    acUpdateExecute(nil);
    FScrollFlag := false;
  end;
end;

// CHART ZOOM
procedure TTagViewerForm.ChartZoom(Sender: TObject);
begin
  if CurGroup=nil then exit;
  CurGroup.Ymax := Chart.LeftAxis.Maximum;
  CurGroup.Ymin := Chart.LeftAxis.Minimum;
  CurGroup.dtBeg := Chart.BottomAxis.Minimum;
  CurGroup.dtEnd := Chart.BottomAxis.Maximum;
end;

procedure TTagViewerForm.acPeriodExecute(Sender: TObject);
var
  w: TtvPeriodForm;
begin
  if CurGroup=nil then exit;
  w := TtvPeriodForm.create(self);
  try
    w.dtBeg := CurGroup.dtBeg;
    w.dtEnd := CurGroup.dtEnd;
    if w.ShowModal = mrOk then begin
      CurGroup.dtBeg := w.dtBeg;
      CurGroup.dtEnd := w.dtEnd;
      acUpdateExecute(nil);
    end;
  finally
    w.Free;
  end;

end;

procedure TTagViewerForm.TimerTimer(Sender: TObject);
var
  dtDelta: TDatetime;
begin
  if CurGroup=nil then exit;
  dtDelta := now - CurGroup.dtEnd;
  CurGroup.dtEnd := CurGroup.dtEnd + dtDelta;
  CurGroup.dtBeg := CurGroup.dtBeg + dtDelta;
  acUpdateExecute(nil);
end;

procedure TTagViewerForm.acOnLineExecute(Sender: TObject);
begin
  Timer.Enabled := not Timer.Enabled;
  if Timer.Enabled then
    acOnLine.ImageIndex := 20
  else
    acOnLine.ImageIndex := 12;
end;

procedure TTagViewerForm.acOnlyTimeExecute(Sender: TObject);
begin
  acOnlyTime.Checked := not acOnlyTime.Checked;
  if acOnlyTime.Checked then
    Chart.BottomAxis.DateTimeFormat := 'hh:mm:ss'
  else
    Chart.BottomAxis.DateTimeFormat := 'dd-mm-yy hh:mm';
end;

procedure TTagViewerForm.acSizeDecExecute(Sender: TObject);
begin
  Chart.ZoomPercent(80.0);
end;

procedure TTagViewerForm.acSizeIncExecute(Sender: TObject);
begin
  Chart.ZoomPercent(120.0);
end;

// ПЕРЕХОД К ПЕРИОДУ
procedure TTagViewerForm.acGotoExecute(Sender: TObject);
var
  w: TtvGotoForm;
  dt, delta: TDatetime;
begin
  if CurGroup=nil then exit;
  w := TtvGotoForm.Create(self);
  try
    delta := CurGroup.dtEnd - CurGroup.dtBeg;
    dt := CurGroup.dtEnd - ((delta) / 2);
    w.dtBegDate.DateTime := dt;
    w.dtBegTime.DateTime := dt;
    if w.ShowModal=mrOk then begin
      dt := w.dtBegDate.DateTime;
      ReplaceTime(dt, w.dtBegTime.DateTime);
      CurGroup.dtEnd := dt + ((delta) / 2);
      CurGroup.dtBeg := CurGroup.dtEnd - delta;
      acUpdateExecute(nil);
    end;
  except
    w.Free;
  end;

end;

procedure TTagViewerForm.SetTrendLine;
var
  i: integer;
begin
  if CurGroup=nil then exit;

  for i:=0 to listTrend.Items.Count-1 do
    with TtvTrendTag(listTrend.Items[i].Data) do begin
      listTrend.Items[i].Subitems[0] := floattostr( srs.YValues.TempValue );
    end;

end;

procedure TTagViewerForm.listTrendAdvancedCustomDrawSubItem(
  Sender: TCustomListView; Item: TListItem; SubItem: Integer;
  State: TCustomDrawState; Stage: TCustomDrawStage;
  var DefaultDraw: Boolean);
begin
  if SubItem=2 then begin
    listTrend.Canvas.Brush.Color := TtvTrendTag(Item.Data).srs.SeriesColor;
  end;
end;

procedure TTagViewerForm.listTrendClick(Sender: TObject);
begin
  if listTrend.Selected = nil then exit;

    with TtvTrendTag(listTrend.Selected.Data) do
      if srs.Tag = 0 then begin
        srs.LinePen.Style := psClear;
        srs.Pointer.Visible := true;
        listTrend.Selected.StateIndex := 0;
        srs.Tag := 1;
      end else
      if srs.Tag = 1 then
      begin
        srs.Pointer.Visible := false;
        srs.Tag := 2;
        listTrend.Selected.StateIndex := -1;
      end else
      if srs.Tag = 2 then
      begin
        srs.LinePen.Style := psSolid;
        srs.Pointer.Visible := Pointers;
        listTrend.Selected.StateIndex := 1;
        srs.Tag := 0;
      end;

{
  for i:=0 to listTrend.Items.Count-1 do
    with TtvTrendTag(listTrend.Items[i].Data) do
      if srs.LinePen.Style = psSolid then begin
//        srs.LinePen.Style := psSolid;
        srs.Pointer.Visible := Pointers;
      end else begin
//        srs.LinePen.Style := psClear;
        srs.Pointer.Visible := listTrend.Items[i].Selected;
      end;
}
end;


procedure TTagViewerForm.acZoomResetExecute(Sender: TObject);
var
  dt: TDatetime;
begin
  if CurGroup=nil then exit;
  CurGroup.Ymax := CurGroup.DefaultYmax;
  CurGroup.Ymin := CurGroup.DefaultYmin;

  dt := (CurGroup.dtEnd + CurGroup.dtBeg) / 2;

  CurGroup.dtEnd := dt + CurGroup.DefaultDelta / 2 ;
  CurGroup.dtBeg := CurGroup.dtEnd - CurGroup.DefaultDelta;

  acUpdateExecute(nil);
end;

procedure TTagViewerForm.acMemPeriodExecute(Sender: TObject);
begin
  if CurGroup=nil then exit;

  MemDtBeg := CurGroup.dtBeg;
  MemDtEnd := CurGroup.dtEnd;
  acRstPeriod.Hint := DateTimeToStr(MemDtBeg) + '  -  ' + DateTimeToStr(MemDtEnd);
  acRstPeriod.Enabled := true;
end;

procedure TTagViewerForm.acRstPeriodExecute(Sender: TObject);
begin
  if CurGroup=nil then exit;

  if (MemDtBeg=0) or (MemDtEnd=0) then exit;
  CurGroup.dtBeg := MemDtBeg;
  CurGroup.dtEnd := MemDtEnd;
  acUpdateExecute(nil);
end;


procedure TTagViewerForm.treeDragDrop(Sender, Source: TObject; X,
  Y: Integer);
begin
  Tree.Selected.MoveTo(Tree.DropTarget, naAddChild);
  Save;
end;

procedure TTagViewerForm.treeDragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
begin
//
end;

procedure TTagViewerForm.acGroupDelExecute(Sender: TObject);
var
  i: integer;
begin
  if tree.Selected=nil then exit;
  if tree.Selected.Data=nil then exit;

  for i:=0 to tree.Selected.Count-1 do
    TtvGroup(tree.Selected.Item[i].Data).Free;

  tree.Selected.Delete;
end;

procedure TTagViewerForm.acTrendClearExecute(Sender: TObject);
begin
  listTrendClear;
end;

procedure TTagViewerForm.acTrendTagNewExecute(Sender: TObject);
begin
  if tree.Selected = nil then exit;
  if tree.Selected.Data = nil then exit;
  if listNav.Selected = nil then exit;

  if CurGroup=nil then acTrendGroupNewExecute(nil);


  AddTagToTrend( TtvGroup(tree.Selected.Data).Tags[listNav.Selected.Index] );

  with TtvTrendTag(listTrend.Items[listTrend.Items.Count-1].Data) do
    if srs.SeriesColor = clWhite then
      srs.SeriesColor :=  random($1000000);

  acUpdateExecute(nil);

end;

procedure TTagViewerForm.acPrintExecute(Sender: TObject);
begin
  PrinterSetupDialog.Execute;

  With Chart do try
    Chart.Legend.Visible := true;
    Print;
  finally
    Chart.Legend.Visible := false;
  end;
end;

procedure TTagViewerForm.listTrendColumnClick(Sender: TObject;
  Column: TListColumn);
begin

    with TtvTrendTag(listTrend.items[0].Data) do
      begin
        listTrend.items[0].StateIndex := listTrend.items[0].StateIndex + 1;
        Column.Caption := inttostr(listTrend.items[0].StateIndex);
      end
end;


////////////////////////////////////////////////////////////////////////////////
// Клонирование группы
////////////////////////////////////////////////////////////////////////////////
procedure TTagViewerForm.acCloneExecute(Sender: TObject);
var
  sFind1: string;
  sRepl1: string;
  sFind2: string;
  sRepl2: string;
  sFind3: string;
  sRepl3: string;

  function repl_str(s: string): string;
  begin
    ReplaceS(s, sFind1, sRepl1);
    ReplaceS(s, sFind2, sRepl2);
    ReplaceS(s, sFind3, sRepl3);
    result := s;
  end;


  procedure copy_group_props(t1,t2: TTreenode);
  var
    j: integer;
    m1,m2: TtvGroup;
  begin
    m1 := t1.Data;
    m2 := TtvGroup.Create;
    t2.Data := m2;

    m2.Name := repl_str(m1.Name);
    m2.UnitName := m1.UnitName;
    m2.Descr := m1.Descr;
    m2.UseColor := m1.UseColor;
    m2.Color := m1.Color;
    m2.DefaultDelta := m1.DefaultDelta;
    m2.DefaultYmin := m1.DefaultYmin;
    m2.DefaultYmax := m1.DefaultYmax;

    SetLength(m2.Tags, length(m1.Tags) );
    for j:=0 to length(m1.Tags)-1 do begin
      m2.Tags[j] := TtvTag.Create;
      m2.Tags[j].Name := repl_str(m1.Tags[j].Name);
      m2.Tags[j].Descr := repl_str(m1.Tags[j].Descr);
      m2.Tags[j].LegName := repl_str(m1.Tags[j].LegName);
      m2.Tags[j].Transform := m1.Tags[j].Transform;
      m2.Tags[j].Width := m1.Tags[j].Width;
      m2.Tags[j].UseColor := m1.Tags[j].UseColor;
      m2.Tags[j].Stairs := m1.Tags[j].Stairs;
      m2.Tags[j].Pointers := m1.Tags[j].Pointers;
    end;

    t2.Text := m2.Name;
  end;


  procedure replicate_node(t1,t2: TTreeNode);
  var
    i: integer;
    t: TTreeNode;
  begin
    copy_group_props(t1, t2);
    for i:=0 to t1.Count-1 do begin
      t := tree.Items.AddChild(t2, 'clone');
      replicate_node(t1.Item[i], t);
    end;
  end;



var
  i: integer;
  t1,t2: TTreenode;
begin
  if tree.Selected = nil then exit;
  if tree.Selected.Level=0 then exit;

  with TtvCloneForm.Create(self) do try
    i := ShowModal;
    sFind1 := edFind1.Text;
    sRepl1 := edRepl1.Text;
    sFind2 := edFind2.Text;
    sRepl2 := edRepl2.Text;
    sFind3 := edFind3.Text;
    sRepl3 := edRepl3.Text;
  finally
    free;
  end;
  if i<>mrOk then exit;

  t1 := tree.Selected;
  t2 := tree.Items.AddChild(tree.Selected.Parent, 'clone');
  replicate_node(t1,t2);

  Save;
end;

procedure TTagViewerForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  listTrendClear;
  Release;
end;

end.
