{**********************************************}
{   TeeChart Mouse Cursor editor and routines  }
{   Copyright (c) 2007-2020 by Steema Software }
{**********************************************}
unit TeeMouseCursor;
{$I TeeDefs.inc}

interface

uses
  {$IFNDEF LINUX}
  Windows, Messages,
  {$ENDIF}
  SysUtils, Classes,

  Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,

  TeeProcs, TeCanvas;

type
  TMouseCursorEdit = class(TForm)
    Label6: TLabel;
    CBCursor: TComboFlat;
    GroupBox1: TGroupBox;
    ImageCursor: TImage;
    BLoad: TButton;
    OpenDialog1: TOpenDialog;
    procedure FormShow(Sender: TObject);
    procedure CBCursorChange(Sender: TObject);
    procedure BLoadClick(Sender: TObject);
  private
    { Private declarations }
    FOnChange    : TNotifyEvent;

    procedure ChangeCursor(ACursor:TCursor);
  public
    { Public declarations }

    TheCursor    : TCursor;
    TheComponent : TComponent;

    class function Choose(const AOwner:TComponent; ACursor:TCursor):TCursor;
    class procedure SetComboCursor(const ACombo:TComboFlat; ACursor:TCursor);

    property OnChange:TNotifyEvent read FOnChange write FOnChange;
  end;

// Adds all cursors and special "crTeeHand" cursor to ACombo.
// Sets combo ItemIndex to ACursor.
procedure TeeFillCursors(const ACombo:TComboFlat; ACursor:TCursor);
Function TeeSetCursor(ACursor:TCursor; const S:String):TCursor;

// Returns the name representation of ACursor in AName parameter.
// Returns "True" if succeed.
Function TeeCursorToIdent(ACursor:Integer; out AName:String):Boolean;

// Returns the Cursor index that corresponds to AName string parameter.
// Returns "True" if succeed.
Function TeeIdentToCursor(Const AName:String; out ACursor:Integer):Boolean;

// Fills a Picture with a Cursor image
procedure TeePreviewCursor(const ACursor:TCursor; const APicture:TPicture);

implementation

{$IFNDEF LCL}
{$R *.DFM}
{$ELSE}
{$R *.lfm}
{$ENDIF}

uses
  TeePenDlg;

procedure TMouseCursorEdit.FormShow(Sender: TObject);
begin
  TeeFillCursors(CBCursor,TheCursor);
  TeePreviewCursor(TheCursor,ImageCursor.Picture);
  TeeTranslateControl(Self, [CBCursor]);

  BLoad.Visible:=Assigned(TheComponent) and
                 (not (csDesigning in TheComponent.ComponentState));
end;

class function TMouseCursorEdit.Choose(const AOwner:TComponent; ACursor:TCursor):TCursor;
var tmp : TMouseCursorEdit;
begin
  tmp:=TMouseCursorEdit.Create(nil);

  with tmp do
  try
    TheCursor:=ACursor;
    TheComponent:=AOwner;

    PositionToCenter(tmp);

    ShowModal;

    result:=TheCursor;
  finally
    Free;
  end;
end;

{ Routines to support "crTeeHand" cursor type }
Function TeeIdentToCursor(Const AName:String; out ACursor:Integer):Boolean;
begin
  if AName=TeeMsg_TeeHand then
  begin
    ACursor:=crTeeHand;
    result:=True;
  end
  else result:=IdentToCursor(AName,ACursor);
end;

Const TeeCursorPrefix='cr';

Function TeeSetCursor(ACursor:TCursor; const S:String):TCursor;
var tmpCursor : Integer;
begin
  if TeeIdentToCursor(TeeCursorPrefix+S,tmpCursor) then
     result:=tmpCursor
  else
     result:=ACursor;
end;

Function TeeCursorToIdent(ACursor:Integer; out AName:String):Boolean;
begin
  if ACursor=crTeeHand then
  begin
    AName:=TeeMsg_TeeHand;
    result:=True;
  end
  else result:=CursorToIdent(ACursor,AName);
end;

type
  TTempCursors=class
  private
    FCombo : TComboFlat;
    procedure ProcGetCursors(const S: string);
  end;

Function DeleteCursorPrefix(Const S:String):String;
begin
  result:=S;
  if Copy(result,1,2)=TeeCursorPrefix then Delete(result,1,2);
end;

procedure TTempCursors.ProcGetCursors(const S: string);
begin
  FCombo.Items.Add(DeleteCursorPrefix(S));
end;

procedure TeeFillCursors(const ACombo:TComboFlat; ACursor:TCursor);
var tmp : TTempCursors;
begin
  With ACombo do
  begin
    Items.BeginUpdate;
    Clear;

    //  Sorted:=True; breaks new "Tree mode" chart editor
    //  Set Sorted to True at design-time in forms, not here.

    tmp:=TTempCursors.Create;
    try
      tmp.FCombo:=ACombo;
      GetCursorValues(tmp.ProcGetCursors);
      tmp.ProcGetCursors(TeeMsg_TeeHand);
    finally
      tmp.Free;
    end;

    Items.EndUpdate;

    TMouseCursorEdit.SetComboCursor(ACombo,ACursor);
  end;
end;

class procedure TMouseCursorEdit.SetComboCursor(const ACombo:TComboFlat; ACursor:TCursor);
var tmpSt : String;
begin
  with ACombo do
  if TeeCursorToIdent(ACursor,tmpSt) then
     ItemIndex:=Items.IndexOf(DeleteCursorPrefix(tmpSt))
  else
     ItemIndex:=-1;
end;

procedure TeePreviewCursor(const ACursor:TCursor; const APicture:TPicture);
var
  tmpCursor : THandle;
  tmpBitmap : TBitmap;
begin
  tmpCursor:=Screen.Cursors[ACursor];

  if tmpCursor<>0 then
  begin
    tmpBitmap:=TBitmap.Create;
    try
      {$IFDEF MSWINDOWS}
      TeeSetBitmapSize(tmpBitmap,GetSystemMetrics(SM_CXCURSOR),
                                 GetSystemMetrics(SM_CYCURSOR));

      DrawIconEx(tmpBitmap.Canvas.Handle,0,0,tmpCursor,
                 0,0,0,0,DI_NORMAL or DI_DEFAULTSIZE);
      {$ENDIF}

      APicture.Graphic:=tmpBitmap;
    finally
      tmpBitmap.Free;
    end;
  end
  else
    APicture.Graphic:=nil;
end;

procedure TMouseCursorEdit.ChangeCursor(ACursor:TCursor);
begin
  TheCursor:=ACursor;

  TeePreviewCursor(TheCursor,ImageCursor.Picture);

  if Assigned(FOnChange) then
     FOnChange(Self);
end;

procedure TMouseCursorEdit.CBCursorChange(Sender: TObject);
begin
  ChangeCursor(TeeSetCursor(TheCursor,CBCursor.CurrentItem));
end;

procedure TMouseCursorEdit.BLoadClick(Sender: TObject);
{$IFDEF MSWINDOWS}

  function CursorIndexOk:TCursor;
  var tmp : HCursor;
  begin
    tmp:=LoadCursor(0, IDC_ARROW);

    result:=TCursor(crTeeHand+1);

    while Screen.Cursors[result]<>tmp do
          Inc(result);
  end;

var tmp : TCursor;
    s   : String;
    St  : TeeString256;
begin
  if OpenDialog1.Execute then
  begin
    tmp:=CursorIndexOk;
    s:=OpenDialog1.FileName;
    Screen.Cursors[tmp]:=LoadCursorFromFile(StrPCopy(St,s));
    ChangeCursor(tmp);

    CBCursor.ItemIndex:=-1;
  end;
{$ELSE}
begin
{$ENDIF}
end;

end.
