{*************************************************************************}
{                                                                         }
{ written by TMS Software                                                 }
{           copyright © 2013                                              }
{           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 FMX.TMSHotSpotImageEditor;

interface

{$I TMSDEFS.INC}

uses
  SysUtils, Classes, UITypes, FMX.Controls, Types, FMX.Types, FMX.Forms, FMX.Objects,
  FMX.TMSHotSpotImage, FMX.TMSBitmapContainer, FMX.Layouts, FMX.TMSSpeedButton, FMX.ListBox,
  FMX.Dialogs, Math, FMX.Menus, FMX.Edit, FMX.TabControl, FMX.Ani, FMX.Colors, FMX.Platform,
  FMX.TMSBitmap, FMX.TMSBaseControl, FMX.StdCtrls, UIConsts, FMX.Styles
  {$IFDEF DELPHIXE5_LVL}
  , FMX.Graphics
  {$ENDIF}
  ;

const
  BordRectSz = 2;

type

  tagRGBTRIPLE = record
    rgbtBlue: Byte;
    rgbtGreen: Byte;
    rgbtRed: Byte;
  end;

  TRGBTriple = tagRGBTRIPLE;

  TStatus = (stNormal, stDrawing, stDrawPoly, stMovePoint, stMoveLine, stMove,
             stScaleEllipse, stWand);

  TBool2DArray = array of array of boolean;
  TRGBTripleRow = array[0..30000] of trgbtriple;
  pRGBTripleRow = ^TRGBTripleRow;

  TTMSFMXHotSpotImageEditorForm = class(TForm)
    lbNPoints: TLabel;
    lbnX: TLabel;
    lbnY: TLabel;
    lbPozX: TLabel;
    lbPozY: TLabel;
    ToolBar1: TToolBar;
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    ScrollBox1: TScrollBox;
    pnBackground: TPanel;
    pnContainer: TPanel;
    Splitter1: TSplitter;
    btNormal: TTMSFMXSpeedButton;
    TMSFMXBitmapContainer1: TTMSFMXBitmapContainer;
    btRect: TTMSFMXSpeedButton;
    btZoomRST: TTMSFMXSpeedButton;
    btZoomOut: TTMSFMXSpeedButton;
    btZoomIn: TTMSFMXSpeedButton;
    TMSFMXSpeedButton6: TTMSFMXSpeedButton;
    btDelLine: TTMSFMXSpeedButton;
    btDelPoint: TTMSFMXSpeedButton;
    btAddPoint: TTMSFMXSpeedButton;
    btDelete: TTMSFMXSpeedButton;
    btWand: TTMSFMXSpeedButton;
    btPoly: TTMSFMXSpeedButton;
    btEllipse: TTMSFMXSpeedButton;
    PB: TPaintBox;
    pnLoadBack: TPanel;
    btBackImage: TButton;
    OPD: TOpenDialog;
    trTol: TTrackBar;
    lbWTolerance: TLabel;
    lbWDensity: TLabel;
    trDensity: TTrackBar;
    pmHotSpot: TPopupMenu;
    miDelete: TMenuItem;
    miSaveHSImage: TMenuItem;
    SPD: TSaveDialog;
    Label3: TLabel;
    seZoomRatio: TSpinBox;
    pnProperties: TPanel;
    pnlTitle: TLabel;
    TabControl1: TTabControl;
    TabItem1: TTabItem;
    btLoadClick: TButton;
    btClearClick: TButton;
    imgClick: TImageControl;
    cmb_ClickImgIndex: TComboBox;
    Label10: TLabel;
    Label11: TLabel;
    bt_ClearClickColor: TButton;
    ColorPanel1: TColorPanel;
    TabItem2: TTabItem;
    btLoadHover: TButton;
    btClearHover: TButton;
    btClearHoverColor: TButton;
    cmb_HoverImgIndex: TComboBox;
    ColorPanel2: TColorPanel;
    ImgHover: TImageControl;
    Label12: TLabel;
    Label13: TLabel;
    TabItem3: TTabItem;
    Btn_Selected: TButton;
    btClearSelected: TButton;
    btClearSelectedColor: TButton;
    cmb_SelectImgIndex: TComboBox;
    ColorPanel3: TColorPanel;
    Img_Selected: TImageControl;
    Label14: TLabel;
    Label15: TLabel;
    TabItem4: TTabItem;
    Btn_Blink: TButton;
    btClearBlink: TButton;
    bt_ClearBlinkColor: TButton;
    cmb_BlinkImgIndex: TComboBox;
    ColorPanel4: TColorPanel;
    Img_Blink: TImageControl;
    Label16: TLabel;
    Label17: TLabel;
    Button3: TButton;
    ColorPanel5: TColorPanel;
    Label1: TLabel;
    Button4: TButton;
    ColorPanel6: TColorPanel;
    Label2: TLabel;
    Button5: TButton;
    ColorPanel7: TColorPanel;
    Label4: TLabel;
    Button6: TButton;
    ColorPanel8: TColorPanel;
    Label18: TLabel;
    Panel2: TPanel;
    Label5: TLabel;
    edtName: TEdit;
    Label6: TLabel;
    edtID: TEdit;
    Label7: TLabel;
    spAngle: TSpinBox;
    chk_Blink: TCheckBox;
    Label8: TLabel;
    Label9: TLabel;
    se_OffsetX: TSpinBox;
    se_OffsetY: TSpinBox;
    ckClip: TCheckBox;
    ckSelectable: TCheckBox;
    procedure PBMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure PBMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
    procedure PBMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure FormCreate(Sender: TObject);
    procedure miDeleteClick(Sender: TObject);
    procedure btLoadClickClick(Sender: TObject);
    procedure btLoadHoverClick(Sender: TObject);
    procedure btRectClick(Sender: TObject);
    procedure btOkClick(Sender: TObject);
    procedure miSaveHSImageClick(Sender: TObject);
    procedure btBackImageClick(Sender: TObject);
    procedure btEllipseClick(Sender: TObject);
    procedure btDeleteClick(Sender: TObject);
    procedure edtNameMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Single);
    procedure edtIDMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Single);
    procedure btPolyClick(Sender: TObject);
    procedure spAngleChange(Sender: TObject);
    procedure btNormalClick(Sender: TObject);
    procedure btWandClick(Sender: TObject);
    procedure trTolChange(Sender: TObject);
    procedure btZoomInClick(Sender: TObject);
    procedure btZoomOutClick(Sender: TObject);
    procedure btZoomRSTClick(Sender: TObject);
    procedure Btn_SelectedClick(Sender: TObject);
    procedure Btn_BlinkClick(Sender: TObject);
    procedure se_OffsetXChange(Sender: TObject);
    procedure se_OffsetYChange(Sender: TObject);
    procedure cmb_ClickImgIndexChange(Sender: TObject);
    procedure cmb_HoverImgIndexChange(Sender: TObject);
    procedure cmb_SelectImgIndexChange(Sender: TObject);
    procedure cmb_BlinkImgIndexChange(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure btClearClickClick(Sender: TObject);
    procedure btClearHoverClick(Sender: TObject);
    procedure btClearSelectedClick(Sender: TObject);
    procedure btClearBlinkClick(Sender: TObject);
    procedure bt_ClearClickColorClick(Sender: TObject);
    procedure bt_ClearBlinkColorClick(Sender: TObject);
    procedure PBPaint(Sender: TObject; Canvas: TCanvas);
    procedure FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
      Shift: TShiftState);
    procedure edtIDKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
      Shift: TShiftState);
    procedure ckClipChange(Sender: TObject);
    procedure chk_BlinkChange(Sender: TObject);
    procedure ckSelectableChange(Sender: TObject);
    procedure trDensityChange(Sender: TObject);
    procedure btClearHoverColorClick(Sender: TObject);
    procedure btClearSelectedColorClick(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
  private
    function getHoverBorderItem: integer;
    procedure SetHoverBorderItem(const Value: integer);
    procedure DrawHotSpotImage(HotSpot: TTMSFMXHotSpot; Canvas: TCanvas);  // draw image using ImageIndex
  private
    FX, FY: Single;
    FBorderPoly: TBorderPoly;
    FHoverPos: THoverPosition;
    FHoverBorderItem,FHoverItem, FSelectedItem: Integer;
    PStart, PCurrent: TPointF;
    FStatus: TStatus;
    CurrentDelta:Single;
    procedure disablePolyTools;
    procedure enablePolyTools;
    procedure disableWandTools;
    procedure enableWandTools;
    procedure DoMagicWand(X, Y: Single);
    function RestrictX(X: Single): Single;
    function RestrictY(Y: Single): Single;
    procedure SetSelectedItem(const Value: Integer);
    Function  GetSelectedItem:integer;
    function SavePoly:Boolean;
    procedure lProperties(doEnabled: Boolean);
    procedure SwapHotSpots;
    function GetImageName(cmb: TComboBox): String;
    procedure SetImageName(cmb: TComboBox; ImageName: String);
    procedure LoadAllImageComboboxes;
    property HoverBorderItem:integer read getHoverBorderItem write SetHoverBorderItem;
  public
    procedure LoadSettings(inifile:TFileName);
    procedure SaveSettings(inifile:TFileName);
    property SelectedItem: Integer read GetSelectedItem write SetSelectedItem;
  end;

  [ComponentPlatformsAttribute(TMSPlatforms)]
  TTMSFMXHotSpotImageEditorDialog = class(TComponent)
  private
    FHotSpotImage: TTMSFMXHotSpotImage;
  protected
    procedure Notification(AComponent: TComponent; AOperation: TOperation); override;
  public
    function Execute: boolean;
  published
    property HotSpotImage: TTMSFMXHotSpotImage read FHotSpotImage write FHotSpotImage;
  end;

var
  TMSFMXHotSpotImageEditorForm: TTMSFMXHotSpotImageEditorForm;
  FHotSpots_org: TTMSFMXHotSpots;
  FHotSpots: TTMSFMXHotSpots;
  BitmapContainer: TTMSFMXBitmapContainer;
  HotSpotsChanged:boolean;
  FPicture: TBitmap;
  tmPoints: TPoints;
  FPoint1,FPoint2: Integer;
  svMove: TPoint;

implementation

uses
  iniFiles;

{$R *.fmx}

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.DisablePolyTools;
begin
  btAddPoint.Enabled := False;
  btDelPoint.Enabled := False;
  btDelLine.Enabled := False;
  lbNPoints.Visible := False;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.EnablePolyTools;
begin
  btAddPoint.Enabled := True;
  btDelPoint.Enabled := True;
  btDelLine.Enabled := True;
  lbNPoints.Visible := True;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.DisableWandTools;
begin
  trTol.Visible := False;
  lbWTolerance.Visible := False;
  lbWDensity.Visible := False;
  trDensity.Visible := False;
  lbNPoints.Visible := False;
  btZoomIn.Enabled:=true;
  btZoomOut.Enabled:=true;
  btZoomRST.Enabled:=true;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.enableWandTools;
begin
  trTol.Visible := True;
  lbWTolerance.Visible := True;
  lbWDensity.Visible := True;
  trDensity.Visible := True;
  lbNPoints.Visible := True;
  btZoomIn.Enabled:=false;
  btZoomOut.Enabled:=false;
  btZoomRST.Enabled:=false;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.lProperties(doEnabled: Boolean);
var
  i: Integer;
begin
  pnProperties.Enabled := doEnabled;

  for i:=0 to pnProperties.ChildrenCount - 1 do
  begin
    if pnProperties.Children[I] is TControl then
      (pnProperties.Children[I] as TControl).Enabled:= doEnabled
  end;
end;

//------------------------------------------------------------------------------

function TTMSFMXHotSpotImageEditorForm.SavePoly: boolean;
var
  tt:TTMSFMXHotSpot;
begin
  if length(tmPoints)<3 then
  begin
    tmPoints := nil;
    Result := False;
    Exit;
  end;
  FStatus := stNormal;
  tt := FHotSpots.Add;
  tt.Clipped := True;
  tt.ShapeType := stPolygon;
  tt.PolyPoints := tmPoints;
  if btPoly.IsChecked then
  begin
    btNormal.IsChecked := True;
    btPoly.IsChecked := False;
  end;
  tmPoints := nil;
  PB.Repaint;
  Result := True;
  HotSpotsChanged:=true;
end;

//------------------------------------------------------------------------------

procedure TTMSFMXHotSpotImageEditorForm.LoadAllImageComboboxes;
var
  i: Integer;
begin
  cmb_ClickImgIndex.Clear;
  cmb_HoverImgIndex.Clear;
  cmb_SelectImgIndex.Clear;
  cmb_BlinkImgIndex.Clear;
  if Assigned(BitmapContainer) then
  begin
    cmb_ClickImgIndex.Items.Add('');
    cmb_HoverImgIndex.Items.Add('');
    cmb_SelectImgIndex.Items.Add('');
    cmb_BlinkImgIndex.Items.Add('');
    for i := 0 to BitmapContainer.Items.Count - 1 do
    begin
      cmb_ClickImgIndex.Items.Add(BitmapContainer.Items[I].Name);
      cmb_HoverImgIndex.Items.Add(BitmapContainer.Items[I].Name);
      cmb_SelectImgIndex.Items.Add(BitmapContainer.Items[I].Name);
      cmb_BlinkImgIndex.Items.Add(BitmapContainer.Items[I].Name);
    end;
  end;
end;

//------------------------------------------------------------------------------

procedure TTMSFMXHotSpotImageEditorForm.FormCreate(Sender: TObject);
begin
  FStatus := stNormal;
  HoverBorderItem := -1;
  FHoverItem := -1;
  FSelectedItem := -1;
  CurrentDelta := 0;
  PB.Width := FPicture.Width;
  PB.Height := FPicture.Height;
  LoadAllImageComboboxes;
  disablePolyTools;
  disableWandTools;
  lProperties(False);
end;

procedure TTMSFMXHotSpotImageEditorForm.FormKeyDown(Sender: TObject;
  var Key: Word; var KeyChar: Char; Shift: TShiftState);
var
  temp: Integer;
begin
  if (Key = Ord('I')) and (ssCtrl in Shift) and not (ssShift in Shift) then
  begin
    btZoomInClick(Self);
    Exit;
  end;
  if (Key = Ord('I')) and (ssCtrl in Shift) and (ssShift in Shift) then
  begin
    btZoomOutClick(Self);
    Exit;
  end;


  if (FStatus = stDrawPoly) and ((Key = vkReturn) or (Key = vkEscape)) then
  begin
    if SavePoly then
      SelectedItem := FHotSpots.Count - 1;
  end;



  if (SelectedItem = -1) or (Key <> vkDelete) then
    Exit;

  if edtId.IsFocused or edtName.IsFocused or spAngle.isFocused or se_OffsetX.IsFocused or se_OffsetY.IsFocused then
    Exit;

  temp := SelectedItem;
  SelectedItem := -1;
  FHotSpots.Delete(temp);
  HoverBorderItem := -1;
  FHoverItem := -1;
  FHoverPos := hpNone;
  PB.Cursor := crDefault;
  PB.Repaint;
  HotSpotsChanged:=true;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.PBMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
var
  P: TPointF;
  i: Integer;
begin
  pnBackground.SetFocus;//so that the editboxes lose focus when mb is pressed
  case Button of
  TMouseButton.mbLeft:
    begin
      //Create ellipse or rectangle
      if btRect.IsChecked or btEllipse.IsChecked then
      begin
        if FStatus = stDrawPoly then SavePoly;

          PStart.x := x;
          PStart.y := y;

        PCurrent := PointF(X, Y);
        FStatus := stDrawing;
      end;

      if FStatus <> stDrawPoly then
        tmPoints := nil;

      if FStatus = stWand then
      begin
        PStart.x := X;
        PStart.y := Y;
        DoMagicWand(X, Y);
        PB.Repaint;
      end;
      //Create polygon
      if btPoly.IsChecked then
      begin
        PStart := PointF(X, Y);
        PCurrent := PointF(X, Y);
        if tmPoints <> nil then
        begin
          //if the user tries to close the polygon then finish drawing
          if (abs(tmPoints[0].x - tmPoints[High(tmPoints)].x) < CornerSize)and
             (abs(tmPoints[0].y - tmPoints[High(tmPoints)].y) < CornerSize) then
          begin
            tmPoints := Copy(tmPoints,0,High(tmPoints));
            if SavePoly then
              SelectedItem := FHotSpots.Count - 1;
            Exit;
          end;
          SetLength(tmPoints,Length(tmPoints) + 1);
          tmPoints[High(tmPoints)].x := X;
          tmPoints[High(tmPoints)].y := Y;
        end
        else
        begin
          FStatus := stDrawPoly;
          SetLength(tmPoints,2);
          Toolbar1.Enabled := False;
          tmPoints[0].x := X;
          tmPoints[0].y := Y;
          tmPoints[1].x := X;
          tmPoints[1].y := Y;
        end;
      end;

      //Editing mode
      if btNormal.IsChecked then
      begin
        //If the mouse is on the border
        if HoverBorderItem <> -1 then
        begin
          if FHotSpots[HoverBorderItem].ShapeType = stPolygon then
          begin
            case FBorderPoly of
            bLine:
              begin
                PCurrent := PointF(X, Y);
                FStatus := stMoveLine;
              end;
            bPoint:
              begin
                FStatus := stMovePoint;
              end;
            end;
            SelectedItem := HoverBorderItem;
            PB.Repaint;
          end
          else
          begin
            if FHotSpots[HoverBorderItem].ShapeType = stEllipse then
            begin
              FStatus := stScaleEllipse;
              PStart := PointF(X, Y);
              PCurrent := PointF(X, Y);
              SelectedItem:=HoverBorderItem;
              PB.Repaint;
            end;
          end;
        end
        else
          if FHoverItem <> -1 then
          begin
            // if SelectedItem=FHoverItem then//if it's already selected then move it
            // begin
            PCurrent.x := x;
            PCurrent.y := y;
            PStart.x := x;
            PStart.y := y;
            FStatus := stMove;
            svMove.x := FHotSpots[FHoverItem].x;
            svMove.y := FHotSpots[FHoverItem].y;
            SelectedItem := FHoverItem;
            // end else SelectedItem:= FHoverItem;//else select it
          end
          else
            SelectedItem := -1;
        end;

        //If Add point function is selected
        if (btAddPoint.IsChecked) and (HoverBorderItem <> -1) and (FBorderPoly = bLine) then
        begin
          tmPoints := FHotSpots[HoverBorderItem].PolyPoints;
          SetLength(tmPoints,length(tmPoints) + 1);

          //if the point is between the last and the first
          if ((FPoint1=0)and(FPoint2=(high(tmPoints)-1))) or
             ((FPoint2=0)and(FPoint1=(high(tmPoints)-1))) then
          begin
            //Add the point at the end
            tmPoints[High(tmPoints)].x := x;
            tmPoints[High(tmPoints)].y := y;
            FHotSpots[HoverBorderItem].PolyPoints:=tmPoints;
          end
          else
          begin
            //else move all the points one position up
            for i := High(tmPoints) downto Max(FPoint1,FPoint2) + 1 do
            begin
              tmPoints[i] := tmPoints[i - 1];
            end;
            //add the point
            i := Max(FPoint1,FPoint2);
            tmPoints[i].x := x;
            tmPoints[i].y := y;
            //finally update the hotspot
            FHotSpots[HoverBorderItem].PolyPoints := tmPoints;
          end;
          //automatically select the normal button
          btNormal.IsChecked := True;
          PB.Repaint;
          HotSpotsChanged:=true;
        end;

        //If Del line function is selected
        if (btDelLine.IsChecked) and (HoverBorderItem <> -1) and (FBorderPoly = bLine) then
        begin
           //if it has less than 5 points delete the whole hotspot
          if Length(FHotSpots[HoverBorderItem].PolyPoints) <= 4 then
          begin
            btDeleteClick(Self);
            Exit;
          end;
          tmPoints := FHotSpots[HoverBorderItem].PolyPoints;

          //if the point is between the last and the first
          if ((FPoint1 = 0) and (FPoint2 = (High(tmPoints) - 1))) or
             ((FPoint2 = 0) and (FPoint1 = (High(tmPoints) - 1))) then
          begin
            //move the points down 1 position, but without the last point
            for i := Min(FPoint1,FPoint2) to High(tmPoints) - 2 do
              tmPoints[i] := tmPoints[i+1];
          end
          else
          begin
            //else move down the points two positions
            for i := Min(FPoint1,FPoint2) to High(tmPoints) - 2 do
              tmPoints[i] := tmPoints[i+2];
          end;
          //adjust the length of the array of points
          Setlength(tmPoints,length(tmPoints) - 2);
          //finally update the hotspot
          FHotSpots[HoverBorderItem].PolyPoints := tmPoints;
          // btNormal.Down := True; // disabled for wand generated hotspots which have too many points
          //                        // to make easyer editing
          PB.Repaint;
          HotSpotsChanged:=true;
        end;
        // If Del point function is selected
        if (btDelPoint.IsChecked) and (HoverBorderItem <> -1) and (FBorderPoly = bPoint) then
        begin
          if Length(FHotSpots[HoverBorderItem].PolyPoints) <= 3 then
          begin
            btDeleteClick(Self);
            Exit;
          end;
          tmPoints := FHotSpots[HoverBorderItem].PolyPoints;
          for i := FPoint1 to High(tmPoints) - 1 do
            tmPoints[i] := tmPoints[i + 1];
          Setlength(tmPoints,length(tmPoints) - 1);
          FHotSpots[HoverBorderItem].PolyPoints := tmPoints;
          // btNormal.Down := True; //disabled for wand generated hotspots which have too many points
          //                        //to make easyer editing
          PB.Repaint;
          HotSpotsChanged:=true;
        end;
      end;
  TMouseButton.mbRight:
    begin
      if FStatus = stDrawPoly then
      begin
        if SavePoly then SelectedItem := FHotSpots.Count - 1;
        Exit;
      end;
      // To simulate mouse movement if the user repeatedly clicks the right mouse button
      PBMouseMove(Sender,Shift,X, Y);

      if (HoverBorderItem <> -1) or (FHoverItem <> -1) then
      begin
        if FHoverItem <> -1 then
          SelectedItem := FHoverItem
        else
          SelectedItem := HoverBorderItem;
        P := PB.LocalToAbsolute(PointF(X, Y));
        pmHotSpot.Popup(Screen.MousePos.X, Screen.MousePos.Y);
      end;
    end;
  end;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.PBMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
var
  i: Integer;
begin
  lbPozx.Text := floattostr(x);
  lbPozy.Text := floattostr(y);
  case FStatus of
  stNormal:
    begin
      PB.Cursor := crDefault;
      if btPoly.IsChecked or btRect.IsChecked or btEllipse.IsChecked then
        Exit;
      for i := 0 to FHotSpots.Count - 1 do
      begin
        if FHotSpots[i].ShapeType = stPolygon then
        begin
          FBorderPoly := FHotSpots[i].getBorderPolyPos(Round(x),Round(y),FPoint1,FPoint2);
          case FBorderPoly of
          bNone:
            begin
              FHoverItem := -1;
              HoverBorderItem := -1;
              Continue;
            end;
          bPoint,bLine:
            begin
              HoverBorderItem := i;
              FHoverItem := -1;
              PB.Cursor := crHandPoint;
              Exit;
            end;
          bInside:
            begin
              FHoverItem := i;
              HoverBorderItem := -1;
              PB.Cursor := crHandPoint;
              Exit;
            end;
          end;
        end
        else
        begin
            FHoverPos := FHotSpots[i].GetHoverPos(Round(X), Round(Y));
            if not (FHoverPos in [hpNone, hpInside]) then
            begin
              HoverBorderItem := i;
              Break;
            end else HoverBorderItem := -1;
            if FHoverPos = hpInside then
            begin
              FHoverItem := i;
              Break;
            end
            else
              FHoverItem := -1;
          end;
        end;

        case FHoverPos of
        hpNone:PB.Cursor := crDefault;
        hpInside:PB.Cursor := crHandPoint;
        hpBorder:PB.Cursor := crSizeAll;
        end;
      end;
  stDrawing:
    with PB.Canvas do
    begin
      X := RestrictX(X);
      Y := RestrictY(Y);
      FX := X;
      FY := Y;

      PB.Repaint;

      PCurrent.X := X;
      PCurrent.Y := Y;
    end;
  stDrawPoly:
    with PB.Canvas do
    begin
      X := RestrictX(X);
      Y := RestrictY(Y);

      FX := X;
      FY := Y;

      PB.Repaint;

      //TODO
      PCurrent.X := X;
      PCurrent.Y := Y;
      tmPoints[High(tmPoints)].x := x;
      tmPoints[High(tmPoints)].y := y;
    end;
  stMove:
    begin
      X := RestrictX(X);
      Y := RestrictY(Y);
      //making it snap to the original position
      if (Abs(PStart.x-x) >= 2) or (Abs(PStart.y-y) >= 2) then
      begin
        PCurrent := SubtractPoints(PointF(x,y),PCurrent);
        FHotspots[SelectedItem].X1 := FHotspots[SelectedItem].X1 + Round(PCurrent.x);
        FHotspots[SelectedItem].Y1 := FHotspots[SelectedItem].Y1 + Round(PCurrent.y);
        PCurrent.x := x;
        PCurrent.y := y;
        end else
        begin
          FHotspots[SelectedItem].Y1 := svmove.y;
          FHotspots[SelectedItem].X1 := svmove.x;
        end;
        PB.Repaint;
        HotSpotsChanged:=true;
      end;
  stMovePoint:
    begin
      X := RestrictX(X);
      Y := RestrictY(Y);
      FHotspots[HoverBorderItem].PolyPoint[FPoint1]:=(RPoint(x,y));
      PB.Repaint;
      HotSpotsChanged:=true;
    end;
  stMoveLine:
    begin
      X := RestrictX(X);
      Y := RestrictY(Y);
      PCurrent := SubtractPoints(PointF(x,y),PCurrent);
      FHotspots[HoverBorderItem].PolyPoint[FPoint1] := AddPoints(FHotspots[HoverBorderItem].PolyPoint[FPoint1],PCurrent);
      FHotspots[HoverBorderItem].PolyPoint[FPoint2] := AddPoints(FHotspots[HoverBorderItem].PolyPoint[FPoint2],PCurrent);
      PCurrent.x := x;
      PCurrent.y := y;
      PB.Repaint;
      HotSpotsChanged:=true;
    end;
  stScaleEllipse:
    with PB.Canvas do
    begin
      FHotSpots[HoverBorderItem].scaleEllipse(Point(Round(x),Round(y)),Point(Round(PCurrent.X), Round(PCurrent.Y)),Point(Round(PStart.X), Round(PStart.Y)));
      PCurrent := PointF(x,y);
      PB.Repaint;
      HotSpotsChanged:=true;
    end;
  end;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.PBMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
  tt: TTMSFMXHotSpot;

begin
  case FStatus of
  //finish the hotspot(rect or ellipse)
  stDrawing:
    begin
      FStatus := stNormal;
      tt := FHotSpots.Add;
      tt.Clipped := True;

      tt.SetRect(Round(PStart.X), Round(PStart.Y), Round(RestrictX(X)), Round(RestrictY(Y)));
      if tt.Width < 10 then
        tt.Width := 10;
      if tt.Height < 10 then
        tt.Height := 10;
      if btRect.IsChecked then
        tt.ShapeType := stPolygon;
      if btEllipse.IsChecked then
        tt.ShapeType := stEllipse;
      btNormal.IsChecked := True;
      SelectedItem := FHotSpots.Count - 1;
      PB.Repaint;
      HotSpotsChanged := true;
    end;
  stMovePoint,stMoveLine:
    begin
      FStatus := stNormal;
      SelectedItem := HoverBorderItem;
      HoverBorderItem := -1;
      PB.Repaint;
      HotSpotsChanged:=true;
    end;
  stMove:
    begin
      FStatus := stNormal;
      PB.Repaint;
      HotSpotsChanged := true;
    end;
  stScaleEllipse:
    begin
      SelectedItem := HoverBorderItem;
      FStatus := stNormal;
      HoverBorderItem := -1;
      PB.Repaint;
      HotSpotsChanged:=true;
    end;
  end;
end;

procedure TTMSFMXHotSpotImageEditorForm.PBPaint(Sender: TObject;
  Canvas: TCanvas);
var
  i,j: Integer;
  r:TRectF;
begin
  r.Left:=0;
  r.top:=0;
  r.Bottom:=FPicture.Height+CurrentDelta;
  r.Right:=FPicture.Width+CurrentDelta;
  try
    Canvas.DrawBitmap(FPicture, RectF(0, 0, FPicture.Width, FPicture.Height), r, 1);

    for i := 0 to FHotSpots.Count-1 do
    begin
      {$IFDEF DELPHIXE6_LVL}
      Canvas.Stroke.Kind := TBrushKind.Solid;
      {$ELSE}
      Canvas.Stroke.Kind := TBrushKind.bkSolid;
      {$ENDIF}
      if i = HoverBorderItem then
      begin
        if FHotspots[i].ShapeType <> stPolygon then
        begin
          Canvas.Stroke.Color := claRed;
          {$IFDEF DELPHIXE6_LVL}
          Canvas.StrokeDash := TStrokeDash.Solid
          {$ELSE}
          Canvas.StrokeDash := TStrokeDash.sdSolid
          {$ENDIF}
        end
        else
        begin
          Canvas.Stroke.Color := claBlack;
          {$IFDEF DELPHIXE6_LVL}
          if FStatus <> stNormal then
            Canvas.StrokeDash := TStrokeDash.Dot
          else
            Canvas.StrokeDash := TStrokeDash.Solid
          {$ELSE}
          if FStatus <> stNormal then
            Canvas.StrokeDash := TStrokeDash.sdDot
          else
            Canvas.StrokeDash := TStrokeDash.sdSolid
          {$ENDIF}
        end;
      end
       else
        if i = FSelectedItem then
          Canvas.Stroke.Color := claBlue
        else
          Canvas.Stroke.Color := claBlack;

      FHotspots[i].DrawShape(Canvas);
      DrawHotSpotImage(FHotspots[i], Canvas);

      if i = FSelectedItem then with FHotSpots[i] do
      begin
        if (PolyPoints <> nil) and ((FStatus = stNormal) or (FStatus = stMove)) then
        begin
          if ShapeType = stEllipse then
          begin
            for j := 0 to high(PolyPoints) do with PolyPoints[j] do
              if (j mod 3) = 0 then
                Canvas.DrawRect(RectF(x - BordRectSz,y-BordRectSz,x+BordRectSz,y+BordRectSz), 0, 0, AllCorners, 1);
          end
          else
          begin
            for j := 0 to high(PolyPoints) do
              with PolyPoints[j] do
                Canvas.DrawRect(RectF(x - BordRectSz,y-BordRectSz,x+BordRectSz,y+BordRectSz), 0, 0, AllCorners, 1);
          end;
        end;
      end;
    end;

    if (FStatus = stWand) then
      lbNPoints.Text := 'Poly Points: ' + IntToStr(length(tmPoints))
    else
      if (SelectedItem <> -1) then
          lbNPoints.Text := 'Poly Points: '+IntToStr(length(FHotspots[SelectedItem].PolyPoints));

    if (FStatus = stWand) and (tmPoints <> nil) then
    begin
      Canvas.Stroke.Color := claBlack;
      Canvas.StrokeThickness := 1;
      Canvas.DrawPolygon(tmPoints, 1);
    end;

    if FStatus = stDrawing then
    begin
      Canvas.StrokeThickNess := 1;
      Canvas.Stroke.Color := claBlack;
      {$IFDEF DELPHIXE6_LVL}
      Canvas.StrokeDash := TStrokeDash.Dot;
      {$ELSE}
      Canvas.StrokeDash := TStrokeDash.sdDot;
      {$ENDIF}
      Canvas.DrawRect(RectF(PStart.X, PStart.Y, FX, FY), 0, 0, AllCorners, 1);
    end;
  finally
    if (FStatus = stDrawPoly) and (tmPoints <> nil) then
    with PB.Canvas do
    begin
      Canvas.Stroke.Color := claBlack;
      Canvas.StrokeThickness := 1;
      {$IFDEF DELPHIXE6_LVL}
      Canvas.StrokeDash := TStrokeDash.Dot;
      {$ELSE}
      Canvas.StrokeDash := TStrokeDash.sdDot;
      {$ENDIF}
      Canvas.DrawPolygon(tmPoints, 1);
      Canvas.DrawLine(PStart, PCurrent, 1);
    end;
  end;
end;

//------------------------------------------------------------------------------
function TTMSFMXHotSpotImageEditorForm.RestrictX(X: Single): Single;
begin
  Result := Max(Min(PB.Width, X), 0);
end;

//------------------------------------------------------------------------------
function TTMSFMXHotSpotImageEditorForm.RestrictY(Y: Single): Single;
begin
  Result := Max(Min(PB.Height, Y), 0);
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.DoMagicWand(X, Y: Single);
var
  bmp: TBitmap;
begin
  bmp := TBitmap.Create(Round(PB.Width), Round(PB.Height));
  try
    bmp.Canvas.BeginScene;
    PB.PaintTo(bmp.canvas, RectF(0, 0, bmp.Width, bmp.Height));
    bmp.Canvas.EndScene;
    tmPoints := GetMagicWandPolygon(bmp, X, Y, trTol.Value/trTol.Max, trDensity.Value, trDensity.Max);
  finally
    bmp.Free;
  end;
end;

procedure TTMSFMXHotSpotImageEditorForm.DrawHotSpotImage(HotSpot: TTMSFMXHotSpot; Canvas: TCanvas);
var
  X, Y: Single;
  s: String;
  bmp: TBitmap;
begin
  if not Assigned(HotSpot) or not Assigned(BitmapContainer) then
    Exit;

  X := HotSpot.X + HotSpot.OffsetX;
  Y := HotSpot.Y + HotSpot.OffsetY;
  s := HotSpot.HoverImageName;
  if (s = '') then
    s := HotSpot.ClickImageName;
  if (s = '') then
    s := HotSpot.SelectedImageName;
  if (s = '') then
    s := HotSpot.BlinkImageName;
  if s <> '' then
  begin
    bmp := BitmapContainer.FindBitmap(s);
    if Assigned(bmp) then
      Canvas.DrawBitmap(bmp, RectF(0, 0, bmp.Width, bmp.Height), RectF(X, Y, X + bmp.Width, Y + bmp.Height), 1);
  end;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.miDeleteClick(Sender: TObject);
var
  temp:integer;
begin
  temp := SelectedItem;
  SelectedItem := -1;
  FHotSpots.Delete(temp);
  HoverBorderItem := -1;
  FHoverItem:= -1;
  FHoverPos := hpNone;
  PB.Cursor := crDefault;
  PB.Repaint;
  HotSpotsChanged:=true;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btLoadClickClick(Sender: TObject);
begin
  OPD.Title := 'Clicked picture';
  if FSelectedItem=-1 then
    Exit;
  if OPD.Execute then
    ImgClick.Bitmap.LoadFromFile(OPD.FileName);
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btClearClickClick(Sender: TObject);
begin
  if (FSelectedItem = -1) then
    Exit;
  ImgClick.Bitmap.Clear(0);
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btLoadHoverClick(Sender: TObject);
begin
  OPD.Title := 'Hover picture';
  if FSelectedItem = -1 then
    Exit;
  if OPD.Execute then
    ImgHover.Bitmap.LoadFromFile(OPD.FileName);
end;

//------------------------------------------------------------------------------

procedure TTMSFMXHotSpotImageEditorForm.btClearHoverClick(Sender: TObject);
begin
  if FSelectedItem = -1 then
    Exit;
  ImgHover.Bitmap.Clear(0);
end;

//------------------------------------------------------------------------------

function TTMSFMXHotSpotImageEditorForm.GetImageName(cmb: TComboBox): String;
begin
  Result := '';
  if Assigned(BitmapContainer) then
  begin
    if Assigned(cmb.Selected) then
      Result := cmb.Selected.Text;
  end;
end;

//------------------------------------------------------------------------------

procedure TTMSFMXHotSpotImageEditorForm.SetImageName(cmb: TComboBox; ImageName: String);
begin
  if Assigned(BitmapContainer) and Assigned(cmb) then
  begin
    if (ImageName = '') then
      cmb.ItemIndex := 0
    else
      cmb.ItemIndex := cmb.Items.IndexOf(ImageName);
  end;
end;

procedure TTMSFMXHotSpotImageEditorForm.SetSelectedItem(const Value: Integer);
begin
  if FSelectedItem <> Value then
  begin
    // save the properties for the old selected hotspot
    if FSelectedItem <> -1 then
    with FHotSpots[FSelectedItem] do
    begin
      HoverImage.Assign(imgHover.Bitmap);
      ClickImage.Assign(imgClick.Bitmap);
      SelectedImage.Assign(Img_Selected.Bitmap);
      BlinkImage.Assign(Img_Blink.Bitmap);
      ClickFill.Color := ColorPanel1.Color;
      HoverFill.Color := ColorPanel2.Color;
      SelectedFill.Color := ColorPanel3.Color;
      BlinkFill.Color := ColorPanel4.Color;

      ClickStroke.Color := ColorPanel5.Color;
      HoverStroke.Color := ColorPanel6.Color;
      SelectedStroke.Color := ColorPanel7.Color;
      BlinkStroke.Color := ColorPanel8.Color;

      HoverImageName := GetImageName(cmb_HoverImgIndex);
      ClickImageName := GetImageName(cmb_ClickImgIndex);
      SelectedImageName := GetImageName(cmb_SelectImgIndex);
      BlinkImageName := GetImageName(cmb_BlinkImgIndex);
      OffsetX := Round(se_OffsetX.Value);
      OffsetY := Round(se_OffsetY.Value);
      HotSpotsChanged:=true;
      // Added the name and id
      try
        Id := StrToInt(edtId.Text);
      except
        on EConvertError do
          ID := 0;
      end;
      Name := edtName.Text;
      if ShapeType = stPolygon then
        SavePoly;
    end;

    // load the images (and the name&ID) of the newly selected hotspot
    FSelectedItem := Value;
    PB.Repaint;
    lProperties(FSelectedItem <> -1);

    if FSelectedItem <> -1 then
    with FHotSpots[FSelectedItem] do
    begin
      if ShapeType = stPolygon then
        EnablePolyTools
      else
        DisablePolyTools;

      if HoverImage.IsEmpty then
        ImgHover.Bitmap.Clear(0)
      else
        ImgHover.Bitmap.Assign(HoverImage);

      if ClickImage.IsEmpty then
        ImgClick.Bitmap.Clear(0)
      else
        ImgClick.Bitmap.Assign(ClickImage);

      if SelectedImage.IsEmpty then
        Img_Selected.Bitmap.Clear(0)
      else
        Img_Selected.Bitmap.Assign(SelectedImage);

      if BlinkImage.IsEmpty then
        Img_Blink.Bitmap.Clear(0)
      else
        Img_Blink.Bitmap.Assign(BlinkImage);

      ColorPanel1.Color := ClickFill.Color;
      ColorPanel2.Color := HoverFill.Color;
      ColorPanel3.Color := SelectedFill.Color;
      ColorPanel4.Color := BlinkFill.Color;

      ColorPanel5.Color := ClickStroke.Color;
      ColorPanel6.Color := HoverStroke.Color;
      ColorPanel7.Color := SelectedStroke.Color;
      ColorPanel8.Color := BlinkStroke.Color;

      SetImageName(cmb_HoverImgIndex, HoverImageName);
      SetImageName(cmb_ClickImgIndex, ClickImageName);
      SetImageName(cmb_SelectImgIndex, SelectedImageName);
      SetImageName(cmb_BlinkImgIndex, BlinkImageName);

      se_OffsetX.Value := OffsetX;
      se_OffsetY.Value := OffsetY;

      edtName.Text := Name;
      edtID.Text := IntToStr(Id);
      spAngle.Value := Angle;
      ckClip.IsChecked := Clipped;
      chk_Blink.IsChecked := Blink;
      ckSelectable.IsChecked := Selectable;
      ToolBar1.Enabled:=true;
      btDelete.Enabled := True;
    end
    else
    begin
      DisablePolyTools;
      edtId.Text := '';
      edtName.Text := '';
      spAngle.Value := 0;
      imgHover.Bitmap.Clear(0);
      imgClick.Bitmap.Clear(0);
      img_Selected.Bitmap.Clear(0);
      img_Blink.Bitmap.Clear(0);
      SetImageName(cmb_HoverImgIndex, '');
      SetImageName(cmb_ClickImgIndex, '');
      SetImageName(cmb_SelectImgIndex, '');
      SetImageName(cmb_BlinkImgIndex, '');

      se_OffsetX.Value := 4;
      se_OffsetY.Value := 4;

      btDelete.Enabled := False;
    end;
  end;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btRectClick(Sender: TObject);
begin
  if FStatus = stWand then
  begin
    DisableWandTools;
    SavePoly;
    tmPoints := nil;
  end;
  if FStatus = stDrawPoly then
  begin
    SavePoly;
  end;
  FStatus := stNormal;
  SelectedItem := -1;
  PB.Repaint;
end;


//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btOkClick(Sender: TObject);
begin
  btZoomRSTClick(Sender);
 // simulate the unselection of the item in order to save the hint
  if FStatus = stWand then
    SavePoly;
  SelectedItem := -1;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.miSaveHSImageClick(Sender: TObject);
var
  Source: TBitmap;
begin
  if not Assigned(FPicture) then
    Exit;

  if SPD.Execute then
  begin
    Source := TBitmap.Create(0, 0);
    with FHotSpots[FSelectedItem] do
    try
      Source.Width := X2-X1;
      Source.Height := Y2-Y1;
      Source.Canvas.BeginScene;
      Source.Canvas.DrawBitmap(FPicture, RectF(X1, Y1, X2, Y2), RectF(0, 0, Source.Width, Source.Height), 1);
      Source.Canvas.EndScene;
    finally
      Source.SaveToFile(SPD.FileName);
      Source.Free;
    end;
  end;
end;

procedure TTMSFMXHotSpotImageEditorForm.btBackImageClick(Sender: TObject);
var
 sft:TShiftState;
begin
  OPD.Title := 'Background picture';
  if OPD.Execute then
  begin
    sft:=[];
    PBMouseDown(PB,TMouseButton.mbLeft,sft,-1,-1);
    PBMouseUP(PB,TMouseButton.mbLeft,sft,-1,-1);
    FPicture.LoadFromFile(OPD.FileName);
    //Dragos
    CurrentDelta:=0;  // Remouve this if you don't Reset Zoom factour
    PB.Width:=FPicture.Width + CurrentDelta;
    PB.Height:=FPicture.Height + CurrentDelta;
    if FHotSpots_org.Count>0 then
    begin
       if MessageDlg('Clear HotSpots ?',  TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], 0)= mrYes  then
    Begin
      FHotSpots_org.Clear;
      Fhotspots.Clear;
    End;
    end
      else
      begin
        FHotSpots_org.Clear;
        Fhotspots.Clear;
      end;
    PB.Repaint;
  end;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btEllipseClick(Sender: TObject);
begin
  if FStatus = stWand then
  begin
    DisableWandTools;
    SavePoly;
    tmPoints := nil;
  end;
  SelectedItem := -1;
  if FStatus = stDrawPoly then
  begin
    savePoly;
  end;
  FStatus := stNormal;
  PB.Repaint;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btDeleteClick(Sender: TObject);
var
  temp: Integer;
begin
  if (SelectedItem = -1) then
    Exit;
  temp := SelectedItem;
  SelectedItem := -1;
  FHotSpots.Delete(temp);
  HoverBorderItem := -1;
  FHoverItem := -1;
  FHoverPos := hpNone;
  PB.Cursor := crDefault;
  btNormal.IsChecked := True;
  PB.Repaint;
  HotSpotsChanged:=true;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.edtIDKeyDown(Sender: TObject;
  var Key: Word; var KeyChar: Char; Shift: TShiftState);
begin
  if ((KeyChar > '9') or (KeyChar < '0')) and (KeyChar > ' ') then
    KeyChar := #0;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.edtNameMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
  edtName.SelectAll;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.edtIDMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  edtID.SelectAll;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btPolyClick(Sender: TObject);
begin
  if FStatus = stWand then
  begin
    DisableWandTools;
    SavePoly;
    FStatus := stNormal;
    tmPoints := nil;
  end;
  SelectedItem := -1;
  PB.Repaint;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.spAngleChange(Sender: TObject);
begin
  if SelectedItem = -1 then
    Exit;
  FHotSpots[SelectedItem].Angle := Round(spAngle.Value);
  PB.Repaint;
  HotSpotsChanged:=true;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btNormalClick(Sender: TObject);
begin
  if FStatus = stWand then
  begin
    DisableWandTools;
    if SavePoly then
      SelectedItem := FHotSpots.Count - 1;
    tmPoints := nil;
  end;
  if FStatus = stDrawPoly then
  begin
    if SavePoly then
      SelectedItem := FHotSpots.Count - 1;
  end;
  FStatus := stNormal;
  PB.Repaint;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.ckClipChange(Sender: TObject);
begin
  if SelectedItem <> -1 then
    FHotSpots[SelectedItem].Clipped := ckClip.IsChecked;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btWandClick(Sender: TObject);
begin
  PB.Cursor := crDefault;
  PStart.x := -1;
  PStart.y := -1;
  if FStatus = stDrawPoly then
    SavePoly;
  SelectedItem := -1;
  tmPoints := nil;
  FStatus := stWand;
  EnableWandTools;
  PB.Repaint;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.trDensityChange(Sender: TObject);
begin
  if (PStart.x<0) or (PStart.y<0) then
    Exit;
  DoMagicWand(PStart.x,PStart.y);
  PB.Repaint;
end;

procedure TTMSFMXHotSpotImageEditorForm.trTolChange(Sender: TObject);
begin
  if (PStart.x<0) or (PStart.y<0) then
    Exit;
  DoMagicWand(PStart.x,PStart.y);
  PB.Repaint;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.LoadSettings(inifile: TFileName);
var
  ini: TIniFile;
begin
  ini := TIniFile.Create(inifile);
  try
    Top := ini.ReadInteger('Size','Top',Top);
    Left := ini.ReadInteger('Size','Left',Left);
    Height := ini.ReadInteger('Size','Height',Height);
    Width := ini.ReadInteger('Size','Width',Width);
    pnBackground.Width := ini.ReadFloat('Splitter','Position',pnBackground.Width);
    trDensity.Value := ini.ReadFloat('Wand','Accuracy',trDensity.Value);
    trTol.Value := ini.ReadFloat('Wand','Tolerance',trTol.Value);
    seZoomRatio.Value := ini.ReadFloat('Zoom','Ratio',seZoomRatio.Value);
  finally
    ini.free;
  end;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.SaveSettings(inifile: TFileName);
var
  ini:TIniFile;
begin
  ini:=TIniFile.Create(inifile);
  try
    ini.WriteInteger('Size','Top',Top);
    ini.WriteInteger('Size','Left',Left);
    ini.WriteInteger('Size','Height',Height);
    ini.WriteInteger('Size','Width',Width);
    ini.WriteFloat('Splitter','Position',pnBackground.Width);
    ini.WriteFloat('Wand','Accuracy',trDensity.Value);
    ini.WriteFloat('Wand','Tolerance',trTol.Value);
    ini.WriteFloat('Zoom','Ratio',seZoomRatio.Value);
  finally
    ini.free;
  end;
end;

//------------------------------------------------------------------------------
procedure TTMSFMXHotSpotImageEditorForm.btZoomInClick(Sender: TObject);
begin
  if HotSpotsChanged then SwapHotSpots;
  FHotSpots.Assign(FHotSpots_org);
  CurrentDelta := CurrentDelta + seZoomRatio.Value;
  FHotSpots.ReScale(Round(CurrentDelta));
  PB.Width := FPicture.Width + CurrentDelta;
  PB.Height := FPicture.Height + CurrentDelta;
  PB.Repaint;
end;

procedure TTMSFMXHotSpotImageEditorForm.btZoomOutClick(Sender: TObject);
var
  i: Single;
begin
  i := CurrentDelta - seZoomRatio.Value;
  if FPicture.Width+i <= 0 then
    Exit;
  if FPicture.Height+i <= 0 then
    Exit;
  if HotSpotsChanged then
    SwapHotSpots;
  FHotSpots.Assign(FHotSpots_org);
  CurrentDelta := i;
  FHotSpots.ReScale(Round(CurrentDelta));
  PB.Width := FPicture.Width + CurrentDelta;
  PB.Height := FPicture.Height + CurrentDelta;
  PB.Repaint;
end;

procedure TTMSFMXHotSpotImageEditorForm.SpeedButton1Click(Sender: TObject);
begin
  fhotspots.Clear;
  HotSpotsChanged := true;
  PB.Repaint;
end;

procedure TTMSFMXHotSpotImageEditorForm.SwapHotSpots;
begin
  FHotSpots_org.Assign(FHotSpots);
  FHotSpots_org.ReScale(FPicture.Width,FPicture.Height);
  HotSpotsChanged := false;
end;

procedure TTMSFMXHotSpotImageEditorForm.btZoomRSTClick(Sender: TObject);
begin
  if HotSpotsChanged then
    SwapHotSpots;
  FHotSpots.Assign(FHotSpots_org);
  CurrentDelta := 0;
  PB.Width := FPicture.Width;
  PB.Height := FPicture.Height;
end;

function TTMSFMXHotSpotImageEditorForm.GetSelectedItem: integer;
begin
  if FSelectedItem >= FHotSpots.Count then
    FSelectedItem := -1;
  Result := FSelectedItem;
end;

function TTMSFMXHotSpotImageEditorForm.getHoverBorderItem: integer;
begin
  if FHoverBorderItem>=FHotSpots.Count then
      FHoverBorderItem:=-1;
  result:=FHoverBorderItem;
end;

procedure TTMSFMXHotSpotImageEditorForm.SetHoverBorderItem(const Value: integer);
begin
  if value<FHotSpots.Count then FHoverBorderItem:=value
                           else FHoverBorderItem:=-1;
end;

procedure TTMSFMXHotSpotImageEditorForm.Btn_SelectedClick(Sender: TObject);
begin
  OPD.Title := 'Selected picture';
  if FSelectedItem =-1 then
    Exit;
  if OPD.Execute then
    Img_Selected.Bitmap.LoadFromFile(OPD.FileName);
end;

procedure TTMSFMXHotSpotImageEditorForm.Btn_BlinkClick(Sender: TObject);
begin
  OPD.Title := 'Blink picture';
  if FSelectedItem=-1 then
    Exit;
  if OPD.Execute then
    Img_Blink.Bitmap.LoadFromFile(OPD.FileName);
end;

procedure TTMSFMXHotSpotImageEditorForm.chk_BlinkChange(Sender: TObject);
begin
  if SelectedItem <> -1 then
    FHotSpots[SelectedItem].Blink := chk_Blink.IsChecked;
end;

procedure TTMSFMXHotSpotImageEditorForm.se_OffsetXChange(Sender: TObject);
begin
  if SelectedItem = -1 then
    Exit;
  FHotSpots[SelectedItem].OffsetX := Round(se_OffsetX.Value);
  PB.Repaint;
  HotSpotsChanged:=true;
end;

procedure TTMSFMXHotSpotImageEditorForm.se_OffsetYChange(Sender: TObject);
begin
  if SelectedItem = -1 then
    Exit;
  FHotSpots[SelectedItem].OffsetY := Round(se_OffsetY.Value);
  PB.Repaint;
  HotSpotsChanged:=true;
end;


procedure TTMSFMXHotSpotImageEditorForm.cmb_ClickImgIndexChange(Sender: TObject);
begin
  if SelectedItem = -1 then
    Exit;
  FHotSpots[SelectedItem].ClickImageName := GetImageName(cmb_ClickImgIndex);
  PB.Repaint;
  HotSpotsChanged:=true;
end;

procedure TTMSFMXHotSpotImageEditorForm.cmb_HoverImgIndexChange(Sender: TObject);
begin
  if SelectedItem = -1 then
    Exit;
  FHotSpots[SelectedItem].HoverImageName := GetImageName(cmb_HoverImgIndex);
  PB.Repaint;
  HotSpotsChanged:=true;
end;

procedure TTMSFMXHotSpotImageEditorForm.cmb_SelectImgIndexChange(Sender: TObject);
begin
  if SelectedItem = -1 then
    Exit;
  FHotSpots[SelectedItem].SelectedImageName := GetImageName(cmb_SelectImgIndex);
  PB.Repaint;
  HotSpotsChanged:=true;
end;

procedure TTMSFMXHotSpotImageEditorForm.cmb_BlinkImgIndexChange(Sender: TObject);
begin
  if SelectedItem = -1 then
    Exit;
  FHotSpots[SelectedItem].BlinkImageName := GetImageName(cmb_BlinkImgIndex);
  PB.Repaint;
  HotSpotsChanged:=true;
end;

procedure TTMSFMXHotSpotImageEditorForm.btClearSelectedClick(Sender: TObject);
begin
  if FSelectedItem =-1 then
    Exit;
  Img_Selected.Bitmap.Clear(0);
end;

procedure TTMSFMXHotSpotImageEditorForm.btClearSelectedColorClick(
  Sender: TObject);
begin
  ColorPanel3.Color := claNull;
end;

procedure TTMSFMXHotSpotImageEditorForm.btClearHoverColorClick(
  Sender: TObject);
begin
  ColorPanel2.Color := claNull;
end;

procedure TTMSFMXHotSpotImageEditorForm.btClearBlinkClick(Sender: TObject);
begin
  if FSelectedItem=-1 then
    Exit;
  Img_Blink.Bitmap.Clear(0);
end;

procedure TTMSFMXHotSpotImageEditorForm.bt_ClearClickColorClick(Sender: TObject);
begin
  ColorPanel1.Color := claNull;
end;

procedure TTMSFMXHotSpotImageEditorForm.Button2Click(Sender: TObject);
begin
  SelectedItem := -1;
end;

procedure TTMSFMXHotSpotImageEditorForm.Button3Click(Sender: TObject);
begin
  ColorPanel5.Color := claNull;
end;

procedure TTMSFMXHotSpotImageEditorForm.Button4Click(Sender: TObject);
begin
  ColorPanel6.Color := claNull;
end;

procedure TTMSFMXHotSpotImageEditorForm.Button5Click(Sender: TObject);
begin
  ColorPanel7.Color := claNull;
end;

procedure TTMSFMXHotSpotImageEditorForm.Button6Click(Sender: TObject);
begin
  ColorPanel8.Color := claNull;
end;

procedure TTMSFMXHotSpotImageEditorForm.bt_ClearBlinkColorClick(Sender: TObject);
begin
  ColorPanel4.Color := claNull;
end;

procedure TTMSFMXHotSpotImageEditorForm.ckSelectableChange(Sender: TObject);
begin
  if SelectedItem <> -1 then
    FHotSpots[SelectedItem].Selectable := ckSelectable.IsChecked;
end;

function TTMSFMXHotSpotImageEditorDialog.Execute: boolean;
var
  frmEditor: TTMSFMXHotSpotImageEditorForm;
begin
  if not Assigned(HotSpotImage) then
    raise Exception.Create('No HotSpotImage assigned');

  Result := false;
  FHotSpots_Org.Assign(HotSpotImage.HotSpots);
  FPicture.Assign(HotSpotImage.Bitmap);
  FHotSpots.Assign(HotSpotImage.HotSpots);
  frmEditor := TTMSFMXHotSpotImageEditorForm.Create(nil);
  try
    if frmEditor.ShowModal = mrOK then
    begin
      HotSpotImage.HotSpots.Assign(FHotSpots);
      HotSpotImage.Bitmap.Assign(FPicture);
      Result := true;
    end;
  finally
    frmEditor.Free;
  end;
end;

procedure TTMSFMXHotSpotImageEditorDialog.Notification(AComponent: TComponent;
  AOperation: TOperation);
begin
  inherited;
  if (AOperation = opRemove) and (AComponent = FHotSpotImage) then
    FHotSpotImage := nil;
end;


initialization
  FHotSpots := TTMSFMXHotSpots.Create(nil);
  FHotSpots_org := TTMSFMXHotSpots.Create(nil);
  FPicture := TBitmap.Create(0, 0);

finalization
  FHotSpots.Free;
  FHotSpots_org.Free;
  FPicture.Free;

end.
