{******************************************************************}
{ NtfyDlg.pas                                                      }
{                                                                  }
{ Author    : A.Nasir Senturk                                      }
{ Home Page : http://www.shenturk.com                              }
{ Email     : shenturk@gmail.com                                   }
{                                                                  }
{ Date      : 12.01.2007                                           }
{                                                                  }
{ Sizden iki şey rica edicem:                                      }
{ 1. Lutfen bu baslik kismini kaldirmayiniz.                       }
{ 2. Mumkunse bagis yapiniz.                                       }
{ *****************************************************************}


unit NtfyDlg;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, GdipApi, GdipObj, DirectDraw, TextUtil, ExtCtrls, ConstDef;

type
  TNotifyStyle = (nfInfo, nfWarning, nfError, nfNotChange);

type
  TNotifierForm = class(TForm)
    BackgrndLbl: TLabel;
    CloseBtn: TButton;
    HeaderTextLbl: TLabel;
    StatusTextLbl: TLabel;
    Countdown: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure CountdownTimer(Sender: TObject);
    procedure CloseBtnClick(Sender: TObject);
  private
    { Private declarations }
    OpacityMin,
    OpacityMax,
    Opacity: Byte;
    MainBuffer: TGPBitmap;
    DrawCanvas: TGPGraphics;

    ColorizedImage,
    OverlayImage,
    CloseImage: TGPBitmap;
    BackColor: Cardinal;
    BackOpacity: Byte;

    BackPen: TGPPen;
    PenColor: Cardinal;
    BackBrush: TGPSolidBrush;
    BrushColor: Cardinal;

    Updating: Boolean;
    TimeLeft: Integer;

    procedure AllocateHandle;
    procedure ReleaseHandle;
    procedure AllocateDrawItems;
    procedure ReleaseDrawItems;
    procedure SetFormStyleEx;
  protected

  public
    { Public declarations }
    ScaleWidth,
    ScaleHeight: Single;
    TextData: WideString;
    NotifyStyle: TNotifyStyle;
    procedure UpdateLayered;
    procedure UpdateMainWindow;
    procedure PaintBackground;
    procedure PaintButtons;
    procedure PaintStatusText;
    procedure PaintHeaderText;
    procedure HideForm;
    procedure ShowForm;
    procedure HideFormEffect(const Min: Integer);
    procedure ShowFormEffect(const Max: Integer);
    procedure Hibernate;
    procedure Wakeup;
    procedure FadeInEffect(const Step, Wait, Max: Integer);
    procedure FadeOutEffect(const Step, Wait, Min: Integer);
    procedure SetStatusText(const Text: WideString);
    procedure EnableCountdownTimer(Enable: Boolean);
    procedure InitNotifierForm;
    procedure DoneNotifierForm;
    procedure LoadOptions;
    procedure SaveOptions;
    procedure ResetCountdown;
    procedure SetColorsDrawItems;
  end;

implementation

uses Main, ShelApix, OptnsDlg;

{$R *.dfm}

{ TNotifierForm }

procedure TNotifierForm.AllocateHandle;
begin
  MainBuffer := TGPBitmap.Create(Width, Height, PixelFormat32bppARGB);
  DrawCanvas := TGPGraphics.Create(MainBuffer);
end;

procedure TNotifierForm.FadeInEffect(const Step, Wait, Max: Integer);
begin
  if not EyMainForm.EnableFadeEffect then Exit;
  while Opacity < Max do
  begin
    Application.ProcessMessages;
    if Opacity + Step >= Max then
    begin
      Opacity := Max;
      UpdateMainWindow;
      Break;
    end;
    Opacity := Opacity + Step;
    UpdateMainWindow;
    Sleep(Wait);
  end;
end;

procedure TNotifierForm.FadeOutEffect(const Step, Wait, Min: Integer);
begin
  if not EyMainForm.EnableFadeEffect then Exit;
  while Opacity > Min do
  begin
    Application.ProcessMessages;
    if Opacity - Step <= Min then
    begin
      Opacity := Min;
      UpdateMainWindow;
      Break;
    end;
    Opacity := Opacity - Step;
    UpdateMainWindow;
    Sleep(Wait);
  end;
end;

procedure TNotifierForm.HideForm;
begin
  HideFormEffect(OpacityMin);
  Self.Hide;
end;

procedure TNotifierForm.PaintBackground;

  procedure PaintColorizedOverlay;
  begin
    {
    DrawCanvas.DrawImage(OverlayImage,
      BackgrndLbl.Left + 6,
      BackgrndLbl.Top + 3,
      OverlayImage.GetWidth, OverlayImage.GetHeight);
    }
    DrawCanvas.DrawImage(OverlayImage,
      BackgrndLbl.Left + (7 * ScaleWidth) - ScaleWidth,
      BackgrndLbl.Top + (4 * ScaleHeight) - ScaleHeight + 0.15,
      OverlayImage.GetWidth * ScaleWidth,
      OverlayImage.GetHeight * ScaleHeight);
  end;

  procedure PaintColorized(Color: Cardinal; Alpha: Byte);
  const
    CMatrix: ColorMatrix = (
      (1.0, 0.0, 0.0, 0.0, 1.0),
      (0.0, 1.0, 0.0, 0.0, 0.0),
      (0.0, 0.0, 1.0, 0.0, 0.0),
      (0.0, 0.0, 0.0, 1.0, 0.0),
      (0.0, 0.0, 0.0, 0.0, 1.0)
    );
  var
    Attr: TGPImageAttributes;
    Matrix: ColorMatrix;
  begin

    Matrix := CMatrix;

    Matrix[3, 3] := (Alpha / 255);
    Matrix[0, 0] := 2 * (GetBValue(Color) / 255);
    Matrix[1, 1] := 2 * (GetGValue(Color) / 255);
    Matrix[2, 2] := 2 * (GetRValue(Color) / 255);

    Attr := TGPImageAttributes.Create;
    try
      Attr.SetColorMatrix(Matrix, ColorMatrixFlagsDefault, ColorAdjustTypeBitmap);

      DrawCanvas.DrawImage(ColorizedImage,
        MakeRect(BackgrndLbl.Left, BackgrndLbl.Top,
        ColorizedImage.GetWidth * ScaleWidth,
        ColorizedImage.GetHeight * ScaleHeight),  // dest rect
        0, 0, ColorizedImage.GetWidth, ColorizedImage.GetHeight,       // source rect
        UnitPixel,
        Attr);

      PaintColorizedOverlay;

    finally
      Attr.Free;
    end;

  end;

begin
  if BackgrndLbl.Visible then
  begin
    case NotifyStyle of
      nfWarning   : BackColor := aclGreen;
      nfError     : BackColor := $00640000;
      nfNotChange : BackColor := $000080C0;//$00FF8000;//$00000064;//
    else
      BackColor := $00006400;
    end;
    PaintColorized(BackColor, BackOpacity);
  end;
end;

procedure TNotifierForm.PaintHeaderText;
var
  WideText: WideString;
begin
  if HeaderTextLbl.Visible then
  begin
    case NotifyStyle of
      nfWarning   : WideText := 'Uyarı!';
      nfError     : WideText := 'Başarısız!';
      nfNotChange : WideText := 'Değişiklik Yok!';
    else
      WideText := 'Güncellendi!';
    end;

    PaintLabelTo(DrawCanvas, HeaderTextLbl, WideText, StringAlignmentNear,
      $FFFFFFFF);
  end;
end;

procedure TNotifierForm.ReleaseHandle;
begin
  if Assigned(MainBuffer) then FreeAndNil(MainBuffer);
  if Assigned(DrawCanvas) then FreeAndNil(DrawCanvas);
end;

procedure TNotifierForm.ShowForm;
begin
  UpdateLayered;
  Self.Show;
  Self.BringToFront;
  ShowFormEffect(OpacityMax);
  SetWindowPos(Self.Handle, HWND_TOP or HWND_TOPMOST, 0, 0, 0, 0,
    SWP_NOMOVE or SWP_NOSIZE or SWP_FRAMECHANGED);
end;

procedure TNotifierForm.UpdateLayered;
begin

  Updating := True;
  try
    ReleaseHandle;
    AllocateHandle;
    PaintBackground;
    PaintButtons;
    PaintHeaderText;
    PaintStatusText;
    UpdateMainWindow;
  finally
    Updating := False;
  end;
end;

procedure TNotifierForm.UpdateMainWindow;
var
  ScrDC, MemDC: HDC;
  BitmapHandle, PrevBitmap: HBITMAP;
  BlendFunc: _BLENDFUNCTION;
  Size: TSize;
  P, S: TPoint;
begin

  ScrDC := CreateCompatibleDC(0);
  MemDC := CreateCompatibleDC(ScrDC);

  MainBuffer.GetHBITMAP(0, BitmapHandle);
  PrevBitmap := SelectObject(MemDC, BitmapHandle);
  Size.cx := Width;
  Size.cy := Height;
  P := Point(Left, Top);
  S := Point(0, 0);

  with BlendFunc do
  begin
    BlendOp := AC_SRC_OVER;
    BlendFlags := 0;
    SourceConstantAlpha := Opacity;
    AlphaFormat := AC_SRC_ALPHA;
  end;

  UpdateLayeredWindow(Handle, ScrDC, @P, @Size, MemDC, @S, 0,
    @BlendFunc, ULW_ALPHA);

  SelectObject(MemDC, PrevBitmap);
  DeleteObject(BitmapHandle);

  DeleteDC(MemDC);
  DeleteDC(ScrDC);

end;

procedure TNotifierForm.FormCreate(Sender: TObject);
begin

  BrushColor := $80000000;
  PenColor := $8F808080;
  
  NotifyStyle := nfInfo;

  ScaleWidth := 1.0;//0.85;//
  ScaleHeight := 1.0;//0.80;//

  SetFormStyleEx;

  CloseImage := TGPBitmap.Create(UIPath + CloseLeaveImage);

  ColorizedImage := TGPBitmap.Create(UIPath + Background4Image);
  OverlayImage := TGPBitmap.Create(UIPath + Overlay4Image);

  LoadOptions;

  OpacityMin := DefOpacityMin;
  OpacityMax := DefOpacityMax;

  if EyMainForm.EnableFadeEffect then Opacity := OpacityMin
  else Opacity := OpacityMax;

  Self.Left := Screen.WorkAreaWidth - Self.Width;
  Self.Top := Screen.WorkAreaHeight - Self.Height;
  Self.Width := Round(ColorizedImage.GetWidth * ScaleWidth);
  Self.Height := Round(ColorizedImage.GetHeight * ScaleHeight);

  CloseBtn.Left := Self.Width - Integer(CloseImage.GetWidth) - 10;

  AllocateDrawItems;

  UpdateLayered;

end;

procedure TNotifierForm.FormDestroy(Sender: TObject);
begin
  ReleaseDrawItems;
  SaveOptions;
  CloseImage.Free;
  OverlayImage.Free;
  ColorizedImage.Free;
  ReleaseHandle;
end;

procedure TNotifierForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
{
var
  R: TRect;}
begin

  EnableCountdownTimer(False);
  CloseBtnClick(CloseBtn);
  {
  if Button = mbLeft then
  begin
    ReleaseCapture;
    SendMessage( Handle, WM_SYSCOMMAND, SC_DRAGMOVE, 0 );
    GetWindowRect(Handle, R);
    Left := R.Left;
    Top := R.Top;
    UpdateLayered;
  end;
  }
end;

procedure TNotifierForm.PaintStatusText;
begin
  if StatusTextLbl.Visible then
  begin
    case NotifyStyle of
      nfWarning:
        begin
          BackColor := $80000080;
        end;
      nfError:
        begin
          BackColor := $80800000;
        end;
      nfNotChange:
        begin
          BackColor := $40000000;
        end;
    else
      BackColor := $80008000;
    end;

    SetColorsDrawItems;
    {
    StatusTextLbl.Caption := TextData;
    PaintLabelTo(DrawCanvas, StatusTextLbl, StatusTextLbl.Caption,
      StringAlignmentCenter, $00FFFFFF, True, TextRenderingHintAntiAliasGridFit);
    }
    DrawCanvas.FillRectangle(BackBrush, MakeRect(StatusTextLbl.BoundsRect));
    DrawCanvas.DrawRectangle(BackPen, MakeRect(StatusTextLbl.BoundsRect));
    PaintLabelTo(DrawCanvas, StatusTextLbl, TextData,
      StringAlignmentCenter, $FFFFFFFF, True, TextRenderingHintAntiAliasGridFit);
  end;
end;

procedure TNotifierForm.SetStatusText(const Text: WideString);
begin
  StatusTextLbl.Caption := Text;
  UpdateLayered;
end;

procedure TNotifierForm.CountdownTimer(Sender: TObject);
begin
  if TimeLeft > 0 then
    Dec(TimeLeft)
  else begin
    EnableCountdownTimer(False);
    CloseBtnClick(CloseBtn);
  end;
end;

procedure TNotifierForm.InitNotifierForm;
begin
  NotifyStyle := nfInfo;
  TextData := '';
  LoadOptions;
  Self.Left := Screen.WorkAreaWidth - Self.Width;
  Self.Top := Screen.WorkAreaHeight - Self.Height;
  TimeLeft := 15;
  EnableCountdownTimer(True);
end;

procedure TNotifierForm.DoneNotifierForm;
begin
  SaveOptions;
  EnableCountdownTimer(False);
end;

procedure TNotifierForm.EnableCountdownTimer(Enable: Boolean);
begin
  Countdown.Enabled := Enable;
end;

procedure TNotifierForm.HideFormEffect(const Min: Integer);
begin
  if EyMainForm.EnableFadeEffect then
    FadeOutEffect(DefOpacityStep, DefOpacityWait, Min)
  else begin
    Opacity := Min;
    UpdateMainWindow;
  end;
end;

procedure TNotifierForm.ShowFormEffect(const Max: Integer);
begin
  if EyMainForm.EnableFadeEffect then
    FadeInEffect(DefOpacityStep, DefOpacityWait, Max)
  else begin
    Opacity := Max;
    UpdateMainWindow;
  end;
end;

procedure TNotifierForm.Hibernate;
begin
  OpacityMax := DefOpacityMax div 2;
  HideFormEffect(OpacityMax);
end;

procedure TNotifierForm.Wakeup;
begin
  OpacityMax := DefOpacityMax;
  ShowFormEffect(OpacityMax);
end;

procedure TNotifierForm.PaintButtons;
begin
  if CloseBtn.Visible then
  begin
    DrawImageTo(DrawCanvas, CloseBtn.Left, CloseBtn.Top,
      CloseImage.GetWidth, CloseImage.GetHeight, CloseImage);
  end;
end;

procedure TNotifierForm.LoadOptions;
begin
  {
  BackColor := IniFile.ReadInteger(sLoginDialog, sBackColor, Integer(aclGoldenrod));//Integer($FF004080));
  BackOpacity := IniFile.ReadInteger(sLoginDialog, sBackOpacity, Integer($A0));
  }
  BackColor := $0000C080;
  BackOpacity := $FF;
end;

procedure TNotifierForm.SaveOptions;
begin
  //IniFile.WriteInteger(sLoginDialog, sLeft, Left);
  //IniFile.WriteInteger(sLoginDialog, sTop, Top);
end;

procedure TNotifierForm.CloseBtnClick(Sender: TObject);
begin
  DoneNotifierForm;
  HideForm;
end;

procedure TNotifierForm.ResetCountdown;
begin
  TimeLeft := 10;
end;

procedure TNotifierForm.AllocateDrawItems;
begin
  BackBrush := TGPSolidBrush.Create(BrushColor); //$80800000
  BackPen := TGPPen.Create(PenColor);
end;

procedure TNotifierForm.ReleaseDrawItems;
begin
  if Assigned(BackBrush) then FreeAndNil(BackBrush);
  if Assigned(BackPen) then FreeAndNil(BackPen);
end;

procedure TNotifierForm.SetFormStyleEx;
var
  StyleEx: Cardinal;
begin
  StyleEx := GetWindowLong(Handle, GWL_EXSTYLE);
  if StyleEx and WS_EX_LAYERED = 0 then
    SetWindowLong(Handle, GWL_EXSTYLE, StyleEx or WS_EX_LAYERED);
end;

procedure TNotifierForm.SetColorsDrawItems;
begin
  BackPen.SetColor(PenColor);
  BackBrush.SetColor(BackColor);
end;

end.

