{***************************************************************************}
{ TMS Advanced Charts for IntraWeb                                          }
{ for Delphi & C++Builder                                                   }
{                                                                           }
{ written by TMS Software                                                   }
{            copyright © 2010                                               }
{            Email : info@tmssoftware.com                                   }
{            Web : http://www.tmssoftware.com                               }
{                                                                           }
{ The source code is given as is. The author is not responsible             }
{ for any possible damage done due to the use of this code.                 }
{ The component can be freely used in any application. The complete         }
{ source code remains property of the author and may not be distributed,    }
{ published, given or sold in any form as such. No parts of the source      }
{ code can be included in any other component or application without        }
{ written authorization of the author.                                      }
{***************************************************************************}
unit IWAdvChart;

{$I TMSDEFS.INC}

interface

uses
  AdvChartView, AdvChartViewGDIP, AdvChart, Math, Dialogs, IWApplication,
  {$IFDEF Linux}
  QControls, QGraphics, QIWPicCntnr,
  {$ELSE}
  Controls, Graphics, Windows,
  {$ENDIF}
  {$IFDEF DELPHI6_LVL}
  Types,
  {$ENDIF}
  Classes, 
  IWControl, IWTypes, SysUtils, 
  IWExtCtrls, JPEG, IWHTMLTag
  {$IFDEF TMSIW6}
  , IWRenderContext, IWColor
  {$ENDIF}
  {$IFDEF TMSIW9}
  , IWXMLTag
  {$ENDIF}
  ;

const
  MAJ_VER = 2; // Major version nr.
  MIN_VER = 9; // Minor version nr.
  REL_VER = 0; // Release nr.
  BLD_VER = 0; // Build nr.
  DATE_VER = 'AUG, 2010'; // Month version

type
  {$IFDEF TMSIW11}
  TIWComponent40Context = TIWCompContext;
  TIWBaseHTMLComponentContext = TIWCompContext;
  TIWBaseComponentContext = TIWCompContext;
  {$ENDIF}

  {$IFNDEF TMSIW11}
  TIWBaseComponentContext = TIWBaseHTMLComponentContext;
  {$ENDIF}

  TIWChartSelectSerieIndexEvent = procedure(Sender: TObject; Serie: integer; PointIndex: integer) of object;

  TIWChartAnnotationMouseEvent = procedure(Sender: TObject; Serie, Annotation, Point: integer) of object;

  TIWChartClickEvent = procedure(Sender: TObject; X, Y: Integer) of object;

  TTIWAdvChart = class(TIWControl)
  private
    FUpdateCount: integer;
    FVersion: string;
    FChanged: boolean;
    FChart: TAdvGDIPChart;
    FImageQuality: integer;
    FOnSelectSerieIndex: TIWChartSelectSerieIndexEvent;
    FOnAnnotationClick: TIWChartAnnotationMouseEvent;
    FChartClick: TIWChartClickEvent;
    {$IFDEF TMSIW9}
    FAsyncChartClick: TIWChartClickEvent;
    FOnAsyncSelectSerieIndex: TIWChartSelectSerieIndexEvent;
    FOnAsyncAnnotationClick: TIWChartAnnotationMouseEvent;
    {$ENDIF}
    procedure SetChart(const Value: TAdvGDIPChart);
    function GetVersion: string;
    function GetChart: TAdvGDIPChart;
    function OutputImage: string;    
  protected
    procedure FillPoints;
    {$IFDEF TMSIW6}
    function ImageHTML(const ASrc,BSrc: string; AContext: TIWBaseComponentContext): TIWHTMLTag;
    {$ELSE}
    function ImageHTML(const ASrc,BSrc: string): TIWHTMLTag;
    {$ENDIF}
    procedure SetValue(const value:string); override;
    procedure DoMouseEvents(X, Y: Integer);
    {$IFDEF TMSIW9}
    procedure DoAsyncChartClick(AParams: TStringList);
    {$ENDIF}
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure ChartChanged(Sender: TObject);
    {$IFDEF TMSIW6}
    procedure IWPaint; override;
    function RenderHTML(AContext: TIWBaseComponentContext): TIWHTMLTag; override;
    {$ELSE}
    procedure Paint; override;
    function RenderHTML: TIWHTMLTag; override;
    {$ENDIF}
    {$IFDEF TMSIW9}
    function RenderAsync(AContext: TIWBaseHTMLComponentContext): TIWXMLTag; override;
    {$ENDIF}
    function GetVersionNr: Integer; virtual;
    function GetVersionString: string; virtual;
    procedure BeginUpdate;
    procedure EndUpdate;
  published
    property Chart: TAdvGDIPChart read GetChart write SetChart;
    property Version: string read GetVersion;
    property OnSelectSerieIndex: TIWChartSelectSerieIndexEvent read FOnSelectSerieIndex write FOnSelectSerieIndex;
    property OnAnnotationClick: TIWChartAnnotationMouseEvent read FOnAnnotationClick write FOnAnnotationClick;
    property OnChartClick: TIWChartClickEvent read FChartClick write FChartClick;
    {$IFDEF TMSIW9}
    property OnAsyncSelectSerieIndex: TIWChartSelectSerieIndexEvent read FOnAsyncSelectSerieIndex write FOnAsyncSelectSerieIndex;
    property OnAsyncAnnotationClick: TIWChartAnnotationMouseEvent read FOnAsyncAnnotationClick write FOnAsyncAnnotationClick;
    property OnAsyncChartClick: TIWChartClickEvent read FAsyncChartClick write FAsyncChartClick;
    {$ENDIF}
  end;

  {$IFDEF TMSIW6}
  function GetCacheDir(Control: TIWCustomControl): string;
  function GetControlHTML(Control: TIWCustomControl; AContext: TIWBaseComponentContext): string;
  {$ELSE}
  function GetCacheDir(Control: TIWControl): string;
  function GetControlHTML(Control: TIWControl): string;
  {$ENDIF}
  

implementation
uses
  IWGlobal, IWAppForm, IWForm, IWServerControllerBase
    {$IFDEF TMSIW11}
  , IWStrings, IWSystem
  {$ELSE}
  , SWStrings, SWSystem
  {$ENDIF}
  ;

{$IFDEF TMSIW6}
function GetControlHTML(Control: TIWCustomControl;
  AContext: TIWBaseComponentContext): string;
{$ELSE}
function GetControlHTML(Control: TIWControl): string;
{$ENDIF}
var
  it: TIWHTMLTag;
begin
  // !!!!!!!!!!!!!! IMPORTANT NOTICE !!!!!!!!!!!!!!!!!!
  //
  // If you encounter a compilation problem here,
  // make sure that you followed the installation steps
  // in INSTALL.TXT, in particular, that you're using the
  // correct TMSDEFS.INC file
  //
  // !!!!!!!!!!!!!! IMPORTANT NOTICE !!!!!!!!!!!!!!!!!!

  {$IFDEF TMSIW6}
  it := Control.RenderHTML(AContext);
  {$ELSE}
  it := Control.RenderHTML;
  {$ENDIF}

  {$IFDEF TMSIW51}
  Result := it.Render;
  {$ELSE}
  it.Contents.Render(Result);
  {$ENDIF}
  it.Free;
end;

{$IFDEF TMSIW6}
function GetCacheDir(Control:TIWCustomControl): string;
{$ELSE}
function GetCacheDir(Control:TIWControl): string;
{$ENDIF}
begin
  {$IFDEF TMSIW51}

  //7.1 code for user cache dir
  {$IFDEF TMSIW71}
  Result := GServerController.UserCacheURL;
  {$ENDIF}
  //pre 7.1 code for user cache dir
  {$IFNDEF TMSIW71}
  Result := GServerController.CacheURL;
  {$ENDIF}

  {$ELSE}
  Result := Control.WebApplication.URLBase + '/cache/';
  {$ENDIF}
end;
  
{ TTIWAdvChart }

{$IFDEF TMSIW9}
function TTIWAdvChart.RenderAsync(
  AContext: TIWBaseHTMLComponentContext): TIWXMLTag;
var
  ImgName, js: string;
begin
  Result := TIWXMLTag.CreateTag('control');
  try
    Result.AddStringParam('id', HTMLName);
    Result.AddStringParam('type', 'TTIWAdvChart');
    RenderAsyncCommonProperties( AContext, Result );

    ImgName := OutputImage;

    js :=
      '<![CDATA['
      + 'document.getElementById("' + HTMLName + '_IMG").src = "' + GetCacheDir(Self) + ExtractFileName(ImgName) + '"'#13
      +']]>';

    GGetWebApplicationThreadVar.CallBackResponse.AddJavaScriptToExecute(js);

  except
    FreeAndNil(Result);
  end;
end;
{$ENDIF}

function TTIWAdvChart.GetChart: TAdvGDIPChart;
begin
  result := FChart;
end;

function TTIWAdvChart.GetVersion: string;
begin
  Result := FVersion;
end;

function TTIWAdvChart.GetVersionNr: Integer;
begin
  Result := MakeLong(MakeWord(BLD_VER, REL_VER), MakeWord(MIN_VER, MAJ_VER));
end;

function TTIWAdvChart.GetVersionString: string;
var
  vn: Integer;
begin
  vn := GetVersionNr;
  Result := IntToStr(Hi(Hiword(vn))) + '.' + IntToStr(Lo(Hiword(vn))) + '.' + IntToStr(Hi(Loword(vn))) + '.' + IntToStr(Lo(Loword(vn))) + ' ' + DATE_VER;
end;

constructor TTIWAdvChart.Create(AOwner: TComponent);
var
  FDesignTime: boolean;
begin
  inherited;
  Width := 400;
  Height := 300;
  FChart := TAdvGDIPChart.Create(Self);

  FDesignTime := (csDesigning in ComponentState) and not
      ((csReading in Owner.ComponentState) or (csLoading in Owner.ComponentState));

  if FDesignTime then
  begin
    FChart.Series.Add;
    FChart.Range.RangeTo := 10;
  end;

  FImageQuality := 100;
  FUpdateCount := 0;
  FVersion := GetVersionString;  
end;

destructor TTIWAdvChart.Destroy;
begin
  FChart.free;
  inherited;
end;

procedure TTIWAdvChart.DoMouseEvents(X, Y: Integer);
var
  I, K: integer;
  sp: TChartSeriePoint;
begin
  if not Assigned(Chart) then
    Exit;

  with Chart do
    for I := 0 to Series.Count - 1 do
      with Series[I] do
        for K := 0 to Annotations.Count - 1 do
          with Annotations[K] do
            if PtInRect(AnnotationRect, Point(X, Y)) then
            begin
              if Assigned(OnAnnotationClick) then
                OnAnnotationClick(Self, I, K, PointIndex);

              if Assigned(OnAsyncAnnotationClick) then
                OnAsyncAnnotationClick(Self, I, K, PointIndex);
              Exit;
            end;


    sp := Chart.XYtoSeriePoint(X, Y);
    if (sp.serie <> -1) and (sp.Point <> -1) then
    begin
      Chart.Series[sp.Serie].SelectedIndex := sp.Point;
      if Assigned(FOnSelectSerieIndex) then
        FOnSelectSerieIndex(Self, sp.serie, sp.Point);

      if Assigned(FOnAsyncSelectSerieIndex) then
        FOnAsyncSelectSerieIndex(Self, sp.serie, sp.Point);

      ChartChanged(Self);
      Exit;
    end;

  if Assigned(OnChartClick) then
    OnChartClick(Self, X, Y);

  if Assigned(OnAsyncChartClick) then
    OnAsyncChartClick(Self, X, Y);
end;

procedure TTIWAdvChart.EndUpdate;
begin
  Dec(FUpdateCount);
  if FUpdateCount = 0 then
    ChartChanged(Self);
end;

{$IFDEF TMSIW9}
procedure TTIWAdvChart.DoAsyncChartClick(AParams: TStringList);
begin
     //
end;
{$ENDIF}

procedure TTIWAdvChart.FillPoints;
var
  i: integer;
  r: integer;
  d: Double;
  K: Integer;
begin
  r := FChart.Range.RangeTo - FChart.Range.RangeFrom;
  for I := 0 to FChart.Series.Count - 1 do
  begin
    with FChart.Series[I] do
    begin
      d := (I + 1) * 100 / (r / 2);    
      if Odd(I) then
      begin
        for K := 0 to r div 2 do
          AddSinglePoint((I * 100) + (d * K));

        for K := r div 2 to r do
          AddSinglePoint((I * 100) + ((d * (r / 2)) - d * K));
      end
      else
      begin
        for K := 0 to r div 2 do
          AddSinglePoint((I * 100) + ((d * (r / 2)) - d * K));

        for K := r div 2 to r do
          AddSinglePoint((I * 100) + (d * K));
      end;
      AutoRange := arCommon;
    end;
  end;
end;

{$IFDEF TMSIW6}
procedure TTIWAdvChart.IWPaint;
{$ELSE}
procedure TTIWAdvChart.Paint;
{$ENDIF}
begin
  if not (Assigned(FChart)) then
  begin
    inherited;
    Exit;
  end;

  inc(FUpdateCount);

  if FChanged then
  begin
    FChart.BeginUpdate;
    FillPoints;
    FChart.ResetUpdate;
  end;

  FChanged := false;

  // design time painting
  FChart.Draw(Canvas, Bounds(0, 0, Width, Height),1,1, true);
  
  dec(FUpdateCount);
end;

{$IFDEF TMSIW6}
function TTIWAdvChart.ImageHTML(const ASrc, BSrc: string; AContext: TIWBaseComponentContext): TIWHTMLTag;
{$ELSE}
function TTIWAdvChart.ImageHTML(const ASrc, BSrc: string): TIWHTMLTag;
{$ENDIF}
var
  sl: TStringList;
begin
  Result := TIWHTMLTag.CreateTag('DIV');
  sl := TStringList.Create;
  with sl do
  begin
    Add('<Script Language="JavaScript">');
    Add('function '+HTMLName+'DoMouseUp(e)');
    Add('{');
    Add('var isIE = navigator.appName.indexOf("Microsoft") != -1;');
    Add('var x;');
    Add('var y;');
    Add('var cx;');
    Add('var cy;');
    Add('if (isIE)');
    Add('{');
    Add('x = event.clientX;');
    Add('y = event.clientY;');
    Add('}');
    Add('else');
    Add('{');
    Add('x = e.clientX;');
    Add('y = e.clientY;');
    Add('}');
    Add('x = x - '+inttostr(Left));
    Add('y = y - '+inttostr(Top));

    Add('if (document.compatMode && document.compatMode != "BackCompat")');
    Add('{');
    Add('  x = x + document.documentElement.scrollLeft;');
    Add('  y = y + document.documentElement.scrollTop;');
    Add('}');
    Add('else');
    Add('{');
    Add('  x = x + document.body.scrollLeft;');
    Add('  y = y + document.body.scrollTop;');
    Add('}');
        
    Add(' FindElem(''' + HTMLName + '_INPUT'').value = ""+x+";"+y');

    if Assigned(OnChartClick) or Assigned(OnSelectSerieIndex) or Assigned(OnAnnotationClick) then
      Add('return SubmitClick('#39+HTMLName+#39', 0, '#39'false'#39');'#13)
    else
      Add('processAjaxEvent(''onChartClick'', '
        + HTMLControlImplementation.IWCLName
        + ',''' + HTMLName + '.' + 'DoAsyncChartClick' + ''','
        + 'true' + ', null, '
        + 'true' + ');');

    Add('}');
    Add('</script>');
  end;
  Result.Contents.AddText(sl.Text);
  sl.Free;
  with Result.Contents.AddTag('IMG') do
  begin
    AddStringParam('SRC', ASrc);
    AddStringParam('NAME', HTMLName);
    AddStringParam('ID', HTMLName+'_IMG');
    AddIntegerParam('WIDTH', Width);
    AddIntegerParam('HEIGHT', Height);
    AddStringParam('OnMouseUp',  HTMLName+'DoMouseUp(event)');
  end;
  with Result.Contents.AddTag('INPUT') do
  begin
    AddStringParam('TYPE','hidden');
    AddStringParam('NAME',HTMLName+'');
    AddStringParam('ID',HTMLName+'_INPUT');
  end;
end;

function TTIWAdvChart.OutputImage: string;
var
  ImgName, str: string;
begin
  ImgName := '';

  if Assigned(FChart) then
  begin
    str := 'png';
    {$IFDEF TMSIW71}
      ImgName := TIWServerControllerBase.NewCacheFile(str,true);
    {$ELSE}
      ImgName := TIWServerControllerBase.NewCacheFile(str);
    {$ENDIF}

    FChart.SaveToImage(ImgName, Width, Height,itpng, 100);
  end;

  result := ImgName;
end;

procedure TTIWAdvChart.BeginUpdate;
begin
  Inc(FUpdateCount);
end;

procedure TTIWAdvChart.ChartChanged(Sender: TObject);
begin
  if FUpdateCount = 0 then
  begin
    FChanged := true;
    Invalidate;
  end;
end;

{$IFDEF TMSIW6}
function TTIWAdvChart.RenderHTML(AContext: TIWBaseComponentContext): TIWHTMLTag;
{$ELSE}
function TTIWAdvChart.RenderHTML: TIWHTMLTag;
{$ENDIF}
var
  ImgName,ImgHot: string;
  htmlres: string;
begin
  htmlres := '';
  ImgName := '';
  ImgHot := '';

  ImgName := OutputImage;

  //Register async call
  AContext.WebApplication.RegisterCallBack(HTMLName + '.DoAsyncChartClick', DoAsyncChartClick);  

  Result := ImageHTML(GetCacheDir(Self) + ExtractFilename(ImgName),
  GetCacheDir(Self) + ExtractFilename(ImgHot)
  {$IFDEF TMSIW6}
    ,AContext);
  {$ELSE}
    );
  {$ENDIF}
end;

procedure TTIWAdvChart.SetChart(const Value: TAdvGDIPChart);
begin
  if FChart <> value then
  begin
    FChart.Assign(Value);
    ChartChanged(self);
  end;
end;

procedure TTIWAdvChart.SetValue(const value: string);
var
  vp, x, y: Integer;
  varvalue : string;
begin
  varvalue := value;

  if Length(value) > 0 then
  begin
    vp := Pos(';', varvalue);

    if vp > 0 then
    begin
      x := strtoint(Copy(varvalue, 0, Pos(';', varvalue) - 1));
      y := strtoint(Copy(varvalue, Pos(';', varvalue) + 1 , Length(varvalue) - Pos(';', varvalue)));
      DoMouseEvents(x, y);
      inherited SetValue(varvalue);
    end;
  end
end;

end.
