| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 | unit TB2Anim;{  Toolbar2000  Copyright (C) 1998-2005 by Jordan Russell  All rights reserved.  The contents of this file are subject to the "Toolbar2000 License"; you may  not use or distribute this file except in compliance with the  "Toolbar2000 License". A copy of the "Toolbar2000 License" may be found in  TB2k-LICENSE.txt or at:    http://www.jrsoftware.org/files/tb2k/TB2k-LICENSE.txt  Alternatively, the contents of this file may be used under the terms of the  GNU General Public License (the "GPL"), in which case the provisions of the  GPL are applicable instead of those in the "Toolbar2000 License". A copy of  the GPL may be found in GPL-LICENSE.txt or at:    http://www.jrsoftware.org/files/tb2k/GPL-LICENSE.txt  If you wish to allow use of your version of this file only under the terms of  the GPL and not to allow others to use your version of this file under the  "Toolbar2000 License", indicate your decision by deleting the provisions  above and replace them with the notice and other provisions required by the  GPL. If you do not delete the provisions above, a recipient may use your  version of this file under either the "Toolbar2000 License" or the GPL.  $jrsoftware: tb2k/Source/TB2Anim.pas,v 1.11 2005/02/16 08:15:58 jr Exp $}interface{$I TB2Ver.inc}{$Q-}uses  Windows, Messages, SysUtils, Classes;const  WM_TB2K_STEPANIMATION = WM_USER + $555;  WM_TB2K_ANIMATIONENDED = WM_USER + $556;type  TTBAnimationDirection = set of (tbadLeft, tbadRight, tbadDown, tbadUp);procedure TBStartAnimation(const AWnd: HWND; const ABlend: Boolean;  const ADirection: TTBAnimationDirection);procedure TBStepAnimation(const Msg: TMessage);procedure TBEndAnimation(const Wnd: HWND);implementation{ Notes to self:  - It originally had the NOMIRRORBITMAP flag on the BitBlt calls, because    Windows 2000's AnimateWindow function has it. But it had to be removed    because on Windows 98 with the Standard VGA or VMware video driver, it    caused no bits to be blitted, even though Windows 98 is supposed to    support NOMIRRORBITMAP according to the documentation. I don't think it's    necessary anyway.}const  DCX_USESTYLE = $10000;  WS_EX_LAYERED = $80000;  NOMIRRORBITMAP = $80000000;  ULW_ALPHA = 2;type  PAnimateThreadFuncData = ^TAnimateThreadFuncData;  TAnimateThreadFuncData = record    SequenceID: Integer;    Wnd: HWND;    Time: Integer;    Blending: Boolean;    CurStep: Integer;    DC, BmpDC: HDC;    Bmp: HBITMAP;    ScreenClientRect: TRect;    Size: TPoint;    LastPos: TPoint;    Direction: TTBAnimationDirection;    AnimateThreadAbort: BOOL;    AnimationEnded: BOOL;    StepMessagePending: BOOL;  end;function AnimateThreadFunc(Parameter: Pointer): Integer;var  StartTime, FrameStartTime, NextFrameStartTime: DWORD;  StartStep, ElapsedTime, I: Integer;  P: TPoint;begin  Result := 0;  StartTime := GetTickCount;  FrameStartTime := StartTime;  with PAnimateThreadFuncData(Parameter)^ do begin    StartStep := CurStep;    while not AnimateThreadAbort do begin      ElapsedTime := FrameStartTime - StartTime;      if (ElapsedTime < 0) or (ElapsedTime >= Time) then        Break;      I := StartStep + ((255 * ElapsedTime) div Time);      if (I < 0) or (I >= 255) then        Break;      GetCursorPos(P);      if (P.X <> LastPos.X) or (P.Y <> LastPos.Y) then begin        if PtInRect(ScreenClientRect, P) then          Break;        LastPos := P;      end;      if I > CurStep then begin        CurStep := I;        if InterlockedExchange(Integer(StepMessagePending), 1) = 0 then          SendNotifyMessage(Wnd, WM_TB2K_STEPANIMATION, 0, SequenceID);      end;      { Wait until the timer has ticked at least 10 msec }      NextFrameStartTime := GetTickCount;      while NextFrameStartTime - FrameStartTime < 10 do begin        { We don't know the rate of the system timer, so check every 5 msec          to see if it's incremented }        Sleep(5);        NextFrameStartTime := GetTickCount;      end;      FrameStartTime := NextFrameStartTime;    end;    AnimationEnded := True;    SendNotifyMessage(Wnd, WM_TB2K_STEPANIMATION, 0, SequenceID);  end;end;threadvar  AnimateThreadHandle: THandle;  AnimateData: TAnimateThreadFuncData;  AnimationSequenceID: Integer;procedure FinalizeAnimation;begin  with PAnimateThreadFuncData(@AnimateData)^ do begin    if Blending then      SetWindowLong(Wnd, GWL_EXSTYLE,        GetWindowLong(Wnd, GWL_EXSTYLE) and not WS_EX_LAYERED)    else      SetWindowRgn(Wnd, 0, False);    BitBlt(DC, 0, 0, Size.X, Size.Y, BmpDC, 0, 0, SRCCOPY);    DeleteDC(BmpDC);    DeleteObject(Bmp);    ReleaseDC(Wnd, DC);    SendNotifyMessage(Wnd, WM_TB2K_ANIMATIONENDED, 0, 0);  end;end;procedure TBEndAnimation(const Wnd: HWND);begin  if (AnimateThreadHandle <> 0) and     ((Wnd = 0) or (AnimateData.Wnd = Wnd)) then begin    AnimateData.AnimateThreadAbort := True;    WaitForSingleObject(AnimateThreadHandle, INFINITE);    CloseHandle(AnimateThreadHandle);    AnimateThreadHandle := 0;    FinalizeAnimation;  end;end;procedure TBStartAnimation(const AWnd: HWND; const ABlend: Boolean;  const ADirection: TTBAnimationDirection);var  ZeroPt: TPoint;  R: TRect;  ThreadID: TThreadID;  Blend: TBlendFunction;  Rgn: HRGN;begin  TBEndAnimation(0);  ZeroPt.X := 0;  ZeroPt.Y := 0;  Inc(AnimationSequenceID);  FillChar(AnimateData, SizeOf(AnimateData), 0);  { Note: The pointer cast avoids GetTls calls for every field access }  with PAnimateThreadFuncData(@AnimateData)^ do begin    SequenceID := AnimationSequenceID;    Wnd := AWnd;    Blending := ABlend;    Direction := ADirection;    GetCursorPos(LastPos);    GetClientRect(Wnd, ScreenClientRect);    MapWindowPoints(Wnd, 0, ScreenClientRect, 2);    GetWindowRect(Wnd, R);    DC := GetDCEx(Wnd, 0, DCX_WINDOW or DCX_CACHE {or DCX_USESTYLE ?});    Size.X := R.Right - R.Left;    Size.Y := R.Bottom - R.Top;    Bmp := CreateCompatibleBitmap(DC, Size.X, Size.Y {or $01000000 ?});    BmpDC := CreateCompatibleDC(DC);    // AnimateWindow calls SetLayout, but I'm not sure that we need to.    //if Assigned(SetLayoutProc) then    //  SetLayoutProc(BmpDC, 0);    SelectObject(BmpDC, Bmp);    //SetBoundsRect(BmpDC, nil, DCB_RESET or DCB_ENABLE);    SendMessage(Wnd, WM_PRINT, WPARAM(BmpDC), PRF_NONCLIENT or PRF_CLIENT or      PRF_ERASEBKGND or PRF_CHILDREN);    //GetBoundsRect    if Blending then begin      SetWindowLong(Wnd, GWL_EXSTYLE, GetWindowLong(Wnd, GWL_EXSTYLE) or WS_EX_LAYERED);      Time := 175;  { actually more like ~147 because CurStep starts at 40 }      CurStep := 40;      Longint(Blend) := 0;      Blend.BlendOp := AC_SRC_OVER;      Blend.SourceConstantAlpha := CurStep;      UpdateLayeredWindow(Wnd, 0, @R.TopLeft, @Size, BmpDC, @ZeroPt, 0,        @Blend, ULW_ALPHA);    end    else begin      Time := 150;      CurStep := 0;      Rgn := CreateRectRgn(0, 0, 0, 0);      if not BOOL(SetWindowRgn(Wnd, Rgn, False)) then        DeleteObject(Rgn);  { just in case }    end;    { These are the same flags AnimateWindow uses. SWP_ASYNCWINDOWPOS is      needed or else it doesn't "save bits" properly.      Note: SWP_ASYNCWINDOWPOS seems to have no effect on Windows 95 & NT 4.0,      so bits behind the window are not saved & restored correctly. }    SetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or      SWP_NOZORDER or SWP_NOACTIVATE or SWP_SHOWWINDOW or SWP_NOREDRAW or      SWP_NOOWNERZORDER or SWP_ASYNCWINDOWPOS);  end;  AnimateThreadHandle := BeginThread(nil, 0, AnimateThreadFunc, @AnimateData,    CREATE_SUSPENDED, ThreadID);  if AnimateThreadHandle = 0 then begin    { just in case... }    FinalizeAnimation;    Exit;  end;  ResumeThread(AnimateThreadHandle);end;procedure TBStepAnimation(const Msg: TMessage);var  Rgn: HRGN;  Blend: TBlendFunction;  X, Y: Integer;begin  if Msg.LParam <> AnimationSequenceID then    { ignore messages dangling in the queue from aborted animation sequences }    Exit;  with PAnimateThreadFuncData(@AnimateData)^ do begin    if not AnimationEnded then begin      if Blending then begin        Longint(Blend) := 0;        Blend.BlendOp := AC_SRC_OVER;        Blend.SourceConstantAlpha := AnimateData.CurStep;        UpdateLayeredWindow(Wnd, 0, nil, nil, 0, nil, 0, @Blend, ULW_ALPHA);      end      else begin        if tbadDown in Direction then          Y := MulDiv(Size.Y, AnimateData.CurStep, 255) - Size.Y        else if tbadUp in Direction then          Y := Size.Y - MulDiv(Size.Y, AnimateData.CurStep, 255)        else          Y := 0;        if tbadRight in Direction then          X := MulDiv(Size.X, AnimateData.CurStep, 255) - Size.X        else if tbadLeft in Direction then          X := Size.X - MulDiv(Size.X, AnimateData.CurStep, 255)        else          X := 0;        Rgn := CreateRectRgn(X, Y, X + Size.X, Y + Size.Y);        if not BOOL(SetWindowRgn(Wnd, Rgn, False)) then          DeleteObject(Rgn);  { just in case }        BitBlt(DC, X, Y, Size.X, Size.Y, BmpDC, 0, 0, SRCCOPY);      end;    end    else      TBEndAnimation(Wnd);    StepMessagePending := False;  end;end;initializationfinalization  TBEndAnimation(0);end.
 |