{%MainUnit ../extctrls.pp}

{******************************************************************************
                                TCustomTrayIcon
 ******************************************************************************
                          Felipe Monteiro de Carvalho
 *****************************************************************************
  This file is part of the Lazarus Component Library (LCL)

  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************
}
{

  Delphi compatibility:

  - TCustomTrayIcon is partially compatible with Delphi implementation
}

{*******************************************************************
*  TCustomTrayIcon.Create ()
*
*  DESCRIPTION:    Creates an object from the TCustomTrayIcon class
*
*  PARAMETERS:     TheOwner  - The owner of the component (this may be nil)
*
*  RETURNS:        A pointer to the newly created object
*
*******************************************************************}
constructor TCustomTrayIcon.Create(TheOwner : TComponent);
begin
  inherited Create(TheOwner);
  FDelayedShowing := False;

  { Default property values }
  FBalloonTimeout := 3000;
  FShowIcon := True;
  FBalloonFlags := bfNone;
  FVisible := False;

  FIcon := TIcon.Create;
  FIcon.OnChange := @IconChanged;

  // Animation objects
  FIcons := TImageList.Create(Self);
  FAnimateTimer := TTimer.Create(Self);
  FAnimateTimer.Enabled := False;
  FAnimateTimer.OnTimer := @HandleOnAnimateTimer;

  FNotifier := TPopupNotifier.Create(Self);
  FNotifier.OnClose := @HandleNotifierClose;

  FTimer := TTimer.Create(Self);
  FTimer.Enabled := False;
  FTimer.OnTimer := @HandleNotifierTimeout;
end;

{*******************************************************************
*  TCustomTrayIcon.Destroy ()
*
*  DESCRIPTION:    Destroys an object derived from the TCustomTrayIcon class
*
*  PARAMETERS:     None
*
*  RETURNS:        Nothing
*
*******************************************************************}
destructor TCustomTrayIcon.Destroy;
begin
  { Avoids an unremoved icon on the tray }
  Hide;

  FTimer.Free;
  FNotifier.Free;
  FIcon.Free;

  inherited Destroy;
end;

{*******************************************************************
*  TCustomTrayIcon.Hide ()
*
*  DESCRIPTION:    Hides the Icon
*
*  PARAMETERS:     None
*
*  RETURNS:        If successfull
*
*******************************************************************}
function TCustomTrayIcon.Hide: Boolean;
begin
  if not FVisible then Exit(True);

  FVisible := False;

  if not(csDesigning in ComponentState) then
  begin
    Result := not (csLoading in ComponentState);
    if Result then
      Result := TWSCustomTrayIconClass(WidgetSetClass).Hide(Self);
    FVisible := not Result;
    FAnimateTimer.Enabled := False;
  end
  else
    Result := false;
end;

{*******************************************************************
*  TCustomTrayIcon.InternalShow()
*
*  DESCRIPTION:    Called when component is loaded, if Show() is called during
*                  loading.
*
*  PARAMETERS:     None
*
*  RETURNS:        If successfull
*
*******************************************************************}
function TCustomTrayIcon.InternalShow: Boolean;
begin
  FDelayedShowing := csLoading in ComponentState;
  if FDelayedShowing then
    exit(False);
  Result := TWSCustomTrayIconClass(WidgetSetClass).Show(Self);
  FVisible := Result;
  FAnimateTimer.Enabled := FAnimate;
end;

{*******************************************************************
*  TCustomTrayIcon.Show ()
*
*  DESCRIPTION:    Shows the Icon
*
*  PARAMETERS:     None
*
*  RETURNS:        If successfull
*
*******************************************************************}
function TCustomTrayIcon.Show: Boolean;
begin
  if FVisible then Exit(True);
  FVisible := True;
  if not(csDesigning in ComponentState) then
    Result := InternalShow
  else
    Result := False;
end;

{*******************************************************************
*  TCustomTrayIcon.SetVisible ()
*
*  DESCRIPTION:    Setter method of the Visible property
*
*******************************************************************}
procedure TCustomTrayIcon.SetVisible(Value: Boolean);
begin
  if Value then Show
  else Hide;
end;

{*******************************************************************
*  TCustomTrayIcon.HandleNotifierClose ()
*
*  DESCRIPTION:    Turns the timer off when the notifier closes
*                  to avoid keep it running unnecessarely
*
*******************************************************************}
procedure TCustomTrayIcon.HandleNotifierClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  FTimer.Enabled := False;
end;

{*******************************************************************
*  TCustomTrayIcon.HandleNotifierTimeout ()
*
*  DESCRIPTION:    Handler for the timer that verifies when the notifier
*                  should close.
*
*******************************************************************}
procedure TCustomTrayIcon.HandleNotifierTimeout(Sender: TObject);
begin
  FTimer.Enabled := False;
  
  FNotifier.Hide;
end;

procedure TCustomTrayIcon.HandleOnAnimateTimer(Sender: TObject);
var
  lBitmap: TBitmap;
begin
  lBitmap := TBitmap.Create;
  try
    FIcons.GetBitmap(FCurAnimationStep, lBitmap);
    FIcon.Assign(lBitmap);
    InternalUpdate();

    // Code to iterate throw the icons
    Inc(FCurAnimationStep);
    if FCurAnimationStep >= FIcons.Count then
      FCurAnimationStep := 0;
  finally
    lBitmap.Free;
  end;
end;

procedure TCustomTrayIcon.IconChanged(Sender: TObject);
begin
  if Handle <> 0 then
    TWSCustomTrayIconClass(WidgetSetClass).InternalUpdate(Self);
end;

class procedure TCustomTrayIcon.WSRegisterClass;
begin
  inherited WSRegisterClass;
  RegisterCustomTrayIcon;
end;

procedure TCustomTrayIcon.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (AComponent = FPopUpMenu) then
    PopupMenu := nil;
end;

procedure TCustomTrayIcon.Loaded;
begin
  inherited Loaded;
  if FDelayedShowing then
    InternalShow;
end;

{*******************************************************************
*  TCustomTrayIcon.InternalUpdate ()
*
*  DESCRIPTION:    Makes modifications to the Icon while running
*                  i.e. without hiding it and showing again
*
*                  Currently only the following parameters use this:
*
*                  - Animate
*                  - Hint
*                  - Icon
*                  - Icons
*
*                  For event parameters (PopUpMenu, OnMouseMove, etc)
*                  this isn't necessary because they are handled
*                  througth callbacks that can always call the last value.
*
*******************************************************************}
procedure TCustomTrayIcon.InternalUpdate;
begin
  if not (csLoading in ComponentState) then
    TWSCustomTrayIconClass(WidgetSetClass).InternalUpdate(Self);
end;

{*******************************************************************
*  TCustomTrayIcon.ShowBalloonHint ()
*
*  DESCRIPTION:    Shows a small message balloon near the tray icon
*
*  PARAMETERS:     None
*
*  RETURNS:        Nothing
*
*******************************************************************}
procedure TCustomTrayIcon.ShowBalloonHint;
var
  UsePopUpNotifier: Boolean;
  Pt: TPoint;
begin
  UsePopUpNotifier := not TWSCustomTrayIconClass(WidgetSetClass).ShowBalloonHint(Self);
  
  if UsePopUpNotifier then
  begin
    Pt := Self.GetPosition;
    FNotifier.Text := Self.BalloonHint;
    FNotifier.Title := Self.BalloonTitle;
    FNotifier.ShowAtPos(Pt.x, Pt.y);
    
    FTimer.Interval := Self.BalloonTimeout;
    FTimer.Enabled := True;
  end;
end;

{*******************************************************************
*  TCustomTrayIcon.GetPosition ()
*
*  DESCRIPTION:    Returns the position of the tray icon on the display.
*                  This function is utilized to show message boxes near
*                  the icon
*
*  PARAMETERS:     None
*
*  RETURNS:        Nothing
*
*******************************************************************}
function TCustomTrayIcon.GetPosition: TPoint;
begin
  Result := TWSCustomTrayIconClass(WidgetSetClass).GetPosition(Self);
end;

{*******************************************************************
*  TCustomTrayIcon.GetCanvas ()
*
*  DESCRIPTION:    Getter method of the Canvas property
*
*  PARAMETERS:     None
*
*  RETURNS:        The canvas of the underlaying Widgetset component
*
*******************************************************************}
function TCustomTrayIcon.GetCanvas: TCanvas;
begin
  //Result := Icon.Canvas;
  Result := TWSCustomTrayIconClass(WidgetSetClass).GetCanvas(Self);
end;

function TCustomTrayIcon.GetAnimateInterval: Cardinal;
begin
  Result := FAnimateTimer.Interval;
end;

procedure TCustomTrayIcon.SetAnimate(const AValue: Boolean);
begin
  if FAnimate=AValue then Exit;
  FAnimate := AValue;
  if Visible and AValue then
    FAnimateTimer.Enabled := True
  else
    FAnimateTimer.Enabled := False;
end;

procedure TCustomTrayIcon.SetAnimateInterval(const AValue: Cardinal);
begin
  if FAnimateTimer.Interval=AValue then exit;
  FAnimateTimer.Interval := AValue;
end;

procedure TCustomTrayIcon.SetHint(const AValue: string);
begin
  FHint := AValue;

  if FVisible then InternalUpdate;
end;

procedure TCustomTrayIcon.SetIcon(const AValue: TIcon);
begin
  if FIcon=AValue then Exit;
  FIcon.Assign(AValue);
  if Visible then InternalUpdate();
end;

procedure TCustomTrayIcon.SetIcons(const AValue: TCustomImageList);
begin
  if FIcons=AValue then Exit;
  FIcons.Assign(AValue);
  if Visible then InternalUpdate();
  FCurAnimationStep := 0; // Avoids index out-of-bounds errors when changing to a new imagelist
end;

procedure TCustomTrayIcon.SetPopUpMenu(const AValue: TPopupMenu);
begin
  if FPopUpMenu = AValue then
    Exit;
  if FPopupMenu <> nil then
    FPopupMenu.RemoveFreeNotification(Self);
  FPopupMenu := AValue;
  if FPopupMenu <> nil then
    FPopUpMenu.FreeNotification(Self);
end;

// included by extctrls.pp

