summaryrefslogtreecommitdiff
path: root/plugins/Clist_modern/src/modern_aniavatars.cpp
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-07-13 07:39:26 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-07-13 07:39:26 +0000
commit2ab468f0ac05d1f257fbf8aa6add9512eabbcc20 (patch)
tree7eb6482089b07994ae77d264e120f3e7772c2fd6 /plugins/Clist_modern/src/modern_aniavatars.cpp
parentccb4003b6178f4c195350ae29896fdd442412af4 (diff)
Clist_modern: changed folder structure
git-svn-id: http://svn.miranda-ng.org/main/trunk@939 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/Clist_modern/src/modern_aniavatars.cpp')
-rw-r--r--plugins/Clist_modern/src/modern_aniavatars.cpp1300
1 files changed, 1300 insertions, 0 deletions
diff --git a/plugins/Clist_modern/src/modern_aniavatars.cpp b/plugins/Clist_modern/src/modern_aniavatars.cpp
new file mode 100644
index 0000000000..88611fa1d0
--- /dev/null
+++ b/plugins/Clist_modern/src/modern_aniavatars.cpp
@@ -0,0 +1,1300 @@
+/**************************************************************************\
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2008 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+****************************************************************************
+
+Created: Mar 9, 2007
+
+Author: Artem Shpynov aka FYR: ashpynov@gmail.com
+
+****************************************************************************
+
+File contains implementation of animated avatars in contact list
+
+\**************************************************************************/
+
+#include "hdr/modern_commonheaders.h"
+
+#define IMMEDIATE_DRAW (!AniAva.bSeparateWindow)
+
+void GDIPlus_ExtractAnimatedGIF (TCHAR * szName, int width, int height, HBITMAP * pBmp, int ** pframesDelay, int * pframesCount, SIZE * sizeAvatar);
+BOOL GDIPlus_IsAnimatedGif (TCHAR * szName);
+
+/* Next is module */
+#define ANIAVAWINDOWCLASS _T("MirandaModernAniAvatar")
+#define aacheck if ( !AniAva.bModuleStarted) return
+#define aalock EnterCriticalSection(&AniAva.CS)
+#define aaunlock LeaveCriticalSection(&AniAva.CS)
+
+#define AAO_HAS_BORDER 0x01
+#define AAO_ROUND_CORNERS 0x02
+#define AAO_HAS_OVERLAY 0x04
+#define AAO_OPAQUE 0x08
+//messages
+enum {
+ AAM_FIRST = WM_USER,
+ AAM_SETAVATAR , //sync WPARAM: TCHAR * filename, LPARAM: SIZE * size, RESULT: actual size
+ AAM_SETPOSITION, //async LPARAM: pointer to set pos info - the handler will empty it, RESULT: 0
+ AAM_REDRAW, //async
+ AAM_STOP, //async stops animation, timer, hide window - prepeare for deleting
+ AAM_PAUSE, //sync keep timer and window, but do not process painting -need before graphics change
+ AAM_RESUME, //async remobe previous flag. repaints if required
+ AAM_REMOVEAVATAR, //sync WPARAM: if y more then wParam, LPARAM: shift up to lParam( remove if values is same)
+ AAM_SETPARENT, //async WPARAM: handle of new parent window
+ AAM_SELFDESTROY, //sync
+ AAM_RENDER, //sync
+ AAM_LAST,
+};
+
+typedef struct _tagAniAva_Object
+{
+ HANDLE hContact;
+ HWND hWindow;
+ BOOL bInvalidPos;
+ BOOL bToBeDeleted;
+ DWORD dwAvatarUniqId;
+ SIZE ObjectSize;
+} ANIAVA_OBJECT;
+
+typedef struct _tagAniAva_Info
+{
+ DWORD dwAvatarUniqId;
+ TCHAR * tcsFilename;
+ int nRefCount;
+ int nStripTop;
+ int nFrameCount;
+ int * pFrameDelays;
+ SIZE FrameSize;
+} ANIAVA_INFO;
+
+typedef struct _tagAniAva_WindowInfo
+{
+ HWND hWindow;
+ RECT rcPos;
+ SIZE sizeAvatar;
+ BOOL StopTimer;
+ int TimerId;
+ int nFramesCount;
+ int * delaysInterval;
+ int currentFrame;
+
+ POINT ptFromPoint;
+
+ BOOL bPlaying;
+ int overlayIconIdx;
+ BYTE bAlpha;
+ BOOL bOrderTop;
+
+ BOOL bPaused; // was request do not draw
+ BOOL bPended; // till do not draw - was painting - need to be repaint
+} ANIAVA_WINDOWINFO;
+typedef struct _tagAniAva_PosInfo
+{
+ RECT rcPos;
+ int idxOverlay;
+ BYTE bAlpha;
+} ANIAVA_POSINFO;
+typedef struct _tagAniAvaSyncCallItem
+{
+ WPARAM wParam;
+ LPARAM lParam;
+ INT_PTR nResult;
+ HANDLE hDoneEvent;
+ PSYNCCALLBACKPROC pfnProc;
+} ANIAVA_SYNCCALLITEM;
+typedef struct _tagAniAvatarImageInfo
+{
+ POINT ptImagePos;
+ int nFramesCount;
+ int * pFrameDelays;
+ SIZE szSize;
+} ANIAVATARIMAGEINFO;
+
+//main structure to handle global
+typedef struct _tagAniAva
+{
+ //protection
+ BOOL bModuleStarted;
+ CRITICAL_SECTION CS;
+ //options
+ BYTE bFlags; // 0x1 has border, 0x2 has round corners, 0x4 has overlay, 0x8 background color
+ COLORREF borderColor;
+ BYTE cornerRadius;
+ COLORREF bkgColor;
+ HIMAGELIST overlayIconImageList;
+ //animations
+ HDC hAniAvaDC;
+ HBITMAP hAniAvaBitmap;
+ HBITMAP hAniAvaOldBitmap;
+ int width;
+ int height;
+ SortedList * AniAvatarList;
+ DWORD AnimationThreadID;
+ HANDLE AnimationThreadHandle;
+ HANDLE hExitEvent;
+ //Objects
+ SortedList * Objects;
+ BOOL bSeparateWindow;
+} ANIAVA;
+
+//module static declarations
+static void __AniAva_DebugRenderStrip();
+
+static void _AniAva_DestroyAvatarWindow( HWND hwnd);
+static void _AniAva_Clear_ANIAVA_WINDOWINFO(ANIAVA_WINDOWINFO * pavwi );
+static void _AniAva_RenderAvatar(ANIAVA_WINDOWINFO * dat, HDC hdcParent = NULL, RECT * rcInParent = NULL );
+static void _AniAva_PausePainting();
+static void _AniAva_ResumePainting();
+static void _AniAva_LoadOptions();
+static void _AniAva_ReduceAvatarImages(int startY, int dY, BOOL bDestroyWindow);
+static void _AniAva_AnimationTreadProc(HANDLE hExitEvent);
+static void _AniAva_RemoveAniAvaDC(ANIAVA * pAniAva);
+static void _AniAva_RealRemoveAvatar(DWORD UniqueID);
+static int _AniAva_LoadAvatarFromImage(TCHAR * szFileName, int width, int height, ANIAVATARIMAGEINFO * pRetAII);
+static int _AniAva_SortAvatarInfo(void * first, void * last);
+static BOOL _AniAva_GetAvatarImageInfo(DWORD dwAvatarUniqId, ANIAVATARIMAGEINFO * avii);
+static HWND _AniAva_CreateAvatarWindowSync(TCHAR *szFileName);
+
+static LRESULT CALLBACK _AniAva_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+//module variables
+static ANIAVA AniAva = {0};
+
+/// IMPLEMENTATION
+
+
+int _AniAva_OnModulesUnload(WPARAM wParam,LPARAM lParam)
+{
+ SetEvent(AniAva.hExitEvent);
+ return 0;
+}
+
+
+// Init AniAva module
+int AniAva_InitModule()
+{
+ memset(&AniAva,0,sizeof(AniAva));
+ if (g_CluiData.fGDIPlusFail) return 0;
+ if ( !( db_get_b(NULL,"CList","AvatarsAnimated",(ServiceExists(MS_AV_GETAVATARBITMAP) && !g_CluiData.fGDIPlusFail))
+ && db_get_b(NULL,"CList","AvatarsShow",SETTINGS_SHOWAVATARS_DEFAULT))) return 0;
+ {
+ WNDCLASSEX wc;
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = ANIAVAWINDOWCLASS;
+ wc.lpfnWndProc = _AniAva_WndProc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(ANIAVA_WINDOWINFO*);
+ wc.hbrBackground = 0;
+ wc.style = CS_GLOBALCLASS;
+ RegisterClassEx(&wc);
+ }
+ InitializeCriticalSection(&AniAva.CS);
+ AniAva.Objects = List_Create(0,2);
+ AniAva.AniAvatarList = List_Create(0,1);
+ AniAva.AniAvatarList->sortFunc = _AniAva_SortAvatarInfo;
+ AniAva.bModuleStarted = TRUE;
+ AniAva.hExitEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
+ AniAva.AnimationThreadID = (DWORD)mir_forkthread(_AniAva_AnimationTreadProc, (void*)AniAva.hExitEvent);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, _AniAva_OnModulesUnload);
+
+ _AniAva_LoadOptions();
+
+ return 1;
+}
+
+// Unload AniAva module
+int AniAva_UnloadModule()
+{
+ aacheck 0;
+ aalock;
+ {
+ int i;
+ AniAva.bModuleStarted = FALSE;
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ if (AniAva.Objects->items[i])
+ _AniAva_DestroyAvatarWindow(((ANIAVA_OBJECT*)AniAva.Objects->items[i])->hWindow);
+
+ mir_free(AniAva.Objects->items[i]);
+ }
+ List_Destroy(AniAva.Objects);
+ mir_free(AniAva.Objects);
+
+ for (i=0; i < AniAva.AniAvatarList->realCount; i++)
+ {
+ ANIAVA_INFO * aai = (ANIAVA_INFO *)AniAva.AniAvatarList->items[i];
+ mir_free(aai->tcsFilename);
+ if (aai->pFrameDelays) free(aai->pFrameDelays);
+ mir_free(aai);
+ }
+ List_Destroy(AniAva.AniAvatarList);
+ mir_free(AniAva.AniAvatarList);
+ _AniAva_RemoveAniAvaDC(&AniAva);
+ SetEvent(AniAva.hExitEvent);
+ CloseHandle(AniAva.hExitEvent);
+ }
+ aaunlock;
+ DeleteCriticalSection(&AniAva.CS);
+ return 1;
+}
+// Update options
+int AniAva_UpdateOptions()
+{
+ BOOL bReloadAvatars = FALSE;
+ BOOL bBeEnabled = (!g_CluiData.fGDIPlusFail
+ && db_get_b(NULL,"CList","AvatarsAnimated",(ServiceExists(MS_AV_GETAVATARBITMAP) && !g_CluiData.fGDIPlusFail))
+ && db_get_b(NULL,"CList","AvatarsShow",SETTINGS_SHOWAVATARS_DEFAULT));
+ if (bBeEnabled && !AniAva.bModuleStarted)
+ {
+ AniAva_InitModule();
+ bReloadAvatars = TRUE;
+ }
+ else if ( !bBeEnabled && AniAva.bModuleStarted)
+ {
+ AniAva_UnloadModule();
+ bReloadAvatars = TRUE;
+ }
+ BOOL oldSeparate = AniAva.bSeparateWindow;
+ _AniAva_LoadOptions();
+ if ( oldSeparate != AniAva.bSeparateWindow )
+ {
+ AniAva_InvalidateAvatarPositions(NULL);
+ AniAva_RemoveInvalidatedAvatars();
+ }
+ if ( bReloadAvatars ) PostMessage(pcli->hwndContactTree,INTM_AVATARCHANGED,0,0);
+ else AniAva_RedrawAllAvatars(TRUE);
+ return 0;
+}
+// adds avatars to be displayed
+int AniAva_AddAvatar(HANDLE hContact, TCHAR * szFilename, int width, int heigth)
+{
+ int res = 0;
+ aacheck 0;
+ if ( !GDIPlus_IsAnimatedGif (szFilename))
+ return 0;
+ aalock;
+ {
+ //first try to find window for contact avatar
+ HWND hwnd = NULL;
+ int i;
+ ANIAVA_OBJECT * pavi;
+ ANIAVATARIMAGEINFO avii = {0};
+ SIZE szAva = { width, heigth };
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ pavi = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ if (pavi->hContact == hContact)
+ {
+ if (pavi->ObjectSize.cx == width && pavi->ObjectSize.cy == heigth)
+ {
+ hwnd = pavi->hWindow;
+ break;
+ }
+ else
+ {
+
+ _AniAva_DestroyAvatarWindow(pavi->hWindow);
+ pavi->hWindow = NULL;
+ _AniAva_RealRemoveAvatar(pavi->dwAvatarUniqId);
+ pavi->dwAvatarUniqId = 0;
+ break;
+ }
+ }
+ }
+ if (i == AniAva.Objects->realCount)
+ {
+ pavi = (ANIAVA_OBJECT *) mir_calloc( sizeof(ANIAVA_OBJECT));
+ pavi->hWindow = NULL;
+ pavi->hContact = hContact;
+ pavi->bInvalidPos = 0;
+ List_Insert( AniAva.Objects, pavi, AniAva.Objects->realCount);
+ }
+ //change avatar
+ pavi->bToBeDeleted = FALSE;
+ pavi->bInvalidPos = 0;
+ // now CreateAvatar
+ if (pavi->dwAvatarUniqId)
+ _AniAva_GetAvatarImageInfo(pavi->dwAvatarUniqId,&avii);
+ else
+ pavi->dwAvatarUniqId = _AniAva_LoadAvatarFromImage(szFilename, width, heigth, &avii);
+ if (hwnd)
+ SendMessage(hwnd, AAM_SETAVATAR, (WPARAM)&avii, (LPARAM) 0);
+ pavi->ObjectSize = avii.szSize;
+ res = MAKELONG(avii.szSize.cx, avii.szSize.cy);
+ }
+ aaunlock;
+ return res;
+}
+
+// call windows to set they parent in order to ensure valid zorder
+void AniAva_UpdateParent()
+{
+ aacheck;
+ aalock;
+ {
+ int i;
+ HWND parent = fnGetAncestor(pcli->hwndContactList,GA_PARENT);
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ SendMessage(pai->hWindow, AAM_SETPARENT, (WPARAM)parent,0);
+ }
+ }
+ aaunlock;
+}
+ANIAVA_OBJECT * FindAvatarByContact( HANDLE hContact )
+{
+ for ( int i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = ((ANIAVA_OBJECT *)AniAva.Objects->items[i]);
+ if (pai->hContact == hContact)
+ return pai;
+ }
+ return NULL;
+}
+
+int AniAva_RenderAvatar( HANDLE hContact, HDC hdcMem, RECT * rc )
+{
+ aacheck 0;
+ aalock;
+ ANIAVA_OBJECT * pai = FindAvatarByContact( hContact );
+ if ( pai )
+ SendMessage(pai->hWindow, AAM_RENDER, (WPARAM)hdcMem, (LPARAM) rc);
+ aaunlock;
+ return 0;
+}
+// update avatars pos
+int AniAva_SetAvatarPos(HANDLE hContact, RECT * rc, int overlayIdx, BYTE bAlpha)
+{
+ aacheck 0;
+ aalock;
+ if (AniAva.CS.LockCount>0)
+ {
+ aaunlock;
+ return 0;
+ }
+ {
+ ANIAVA_OBJECT * pai = FindAvatarByContact( hContact );
+ if ( pai )
+ {
+ ANIAVA_POSINFO * api = (ANIAVA_POSINFO *)malloc(sizeof(ANIAVA_POSINFO));
+ if ( !pai->hWindow)
+ {
+ HWND hwnd;
+ HWND parent;
+ ANIAVATARIMAGEINFO avii = {0};
+ //not found -> create window
+ char szName[150] = "AniAvaWnd_";
+ TCHAR * tszName;
+ _itoa((int)hContact,szName+10,16);
+
+ tszName = mir_a2t( szName );
+ hwnd = _AniAva_CreateAvatarWindowSync(tszName);
+ mir_free( tszName );
+ parent = fnGetAncestor(pcli->hwndContactList,GA_PARENT);
+ pai->hWindow = hwnd;
+ SendMessage(hwnd,AAM_SETPARENT,(WPARAM)parent,0);
+ if (_AniAva_GetAvatarImageInfo(pai->dwAvatarUniqId,&avii))
+ SendMessage(pai->hWindow, AAM_SETAVATAR, (WPARAM)&avii, (LPARAM) 0);
+ }
+ api->bAlpha = bAlpha;
+ api->idxOverlay = overlayIdx;
+ api->rcPos = *rc;
+ SendNotifyMessage(pai->hWindow, AAM_SETPOSITION, (WPARAM)0, (LPARAM) api);
+ // the AAM_SETPOSITION is responsible to destroy memory under api
+ pai->bInvalidPos = FALSE;
+ pai->bToBeDeleted = FALSE;
+ }
+ }
+ aaunlock;
+ return 1;
+}
+// remove avatar
+int AniAva_RemoveAvatar(HANDLE hContact)
+{
+ aacheck 0;
+ aalock;
+ {
+ int i;
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ if (pai->hContact == hContact)
+ {
+ pai->bToBeDeleted = TRUE;
+ break;
+ }
+ }
+ }
+ aaunlock;
+ return 1;
+}
+// reset positions of avatars to be drawn (still be painted at same place)
+int AniAva_InvalidateAvatarPositions(HANDLE hContact)
+{
+ int i;
+ aacheck 0;
+ aalock;
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ if (pai->hContact == hContact || !hContact)
+ {
+ pai->bInvalidPos++;
+ if (hContact) break;
+ }
+ }
+ aaunlock;
+ return 1;
+}
+// all avatars without validated position will be stop painted and probably removed
+int AniAva_RemoveInvalidatedAvatars()
+{
+ BOOL keepAvatar = FALSE;
+ aacheck 0;
+ aalock;
+
+ {
+ int i;
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ if (pai->hWindow && (pai->bInvalidPos))
+ {
+ SendMessage(pai->hWindow,AAM_STOP,0,0);
+ if (pai->bInvalidPos)//>3)
+ {
+ //keepAvatar = TRUE;
+ //pai->bToBeDeleted = TRUE;
+ pai->bInvalidPos = 0;
+ _AniAva_DestroyAvatarWindow(pai->hWindow);
+ pai->hWindow = NULL;
+ }
+ }
+ if (pai->bToBeDeleted)
+ {
+ if (pai->hWindow) _AniAva_DestroyAvatarWindow(pai->hWindow);
+ pai->hWindow = NULL;
+ if ( !keepAvatar) _AniAva_RealRemoveAvatar(pai->dwAvatarUniqId);
+ mir_free(pai);
+ List_Remove(AniAva.Objects,i);
+ i--;
+ }
+ }
+ }
+ aaunlock;
+ return 1;
+}
+
+// repaint all avatars at positions (eg on main window movement)
+int AniAva_RedrawAllAvatars(BOOL updateZOrder)
+{
+ int i;
+ aacheck 0;
+ aalock;
+ updateZOrder = 1;
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ if (updateZOrder)
+ SendMessage(pai->hWindow,AAM_REDRAW,(WPARAM)updateZOrder,0);
+ else
+ SendNotifyMessage(pai->hWindow,AAM_REDRAW,(WPARAM)updateZOrder,0);
+ }
+ aaunlock;
+ return 1;
+}
+
+//Static procedures
+static void CALLBACK _AniAva_SyncCallerUserAPCProc(DWORD_PTR dwParam)
+{
+ ANIAVA_SYNCCALLITEM* item = (ANIAVA_SYNCCALLITEM*) dwParam;
+ item->nResult = item->pfnProc(item->wParam, item->lParam);
+ SetEvent(item->hDoneEvent);
+}
+static INT_PTR _AniAva_CreateAvatarWindowSync_Worker(WPARAM tszName, LPARAM lParam)
+{
+ HWND hwnd = CreateWindowEx( WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_NOPARENTNOTIFY,ANIAVAWINDOWCLASS,(TCHAR*)tszName,WS_POPUP,
+ 0,0,1,1,pcli->hwndContactList, NULL, pcli->hInst, NULL );
+ return (INT_PTR)hwnd;
+}
+
+static HWND _AniAva_CreateAvatarWindowSync(TCHAR *szFileName)
+{
+ ANIAVA_SYNCCALLITEM item = {0};
+ int res = 0;
+ if ( !AniAva.AnimationThreadHandle) return NULL;
+ if (AniAva.AnimationThreadID == 0) return NULL;
+ item.wParam = (WPARAM) szFileName;
+ item.lParam = 0;
+ item.pfnProc = _AniAva_CreateAvatarWindowSync_Worker;
+ item.hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (GetCurrentThreadId() != AniAva.AnimationThreadID)
+ QueueUserAPC(_AniAva_SyncCallerUserAPCProc, AniAva.AnimationThreadHandle, (DWORD_PTR) &item);
+ else
+ _AniAva_SyncCallerUserAPCProc((DWORD_PTR) &item);
+ WaitForSingleObject(item.hDoneEvent, INFINITE);
+ CloseHandle(item.hDoneEvent);
+ return (HWND)item.nResult;
+}
+
+static void _AniAva_RealRemoveAvatar(DWORD UniqueID)
+{
+ int j,k;
+ for (j = 0; j < AniAva.AniAvatarList->realCount; j++)
+ {
+ ANIAVA_INFO * aai = (ANIAVA_INFO *) AniAva.AniAvatarList->items[j];
+ if (aai->dwAvatarUniqId == UniqueID)
+ {
+ aai->nRefCount--;
+ if (aai->nRefCount == 0)
+ {
+ _AniAva_PausePainting();
+ #ifdef _DEBUG
+ __AniAva_DebugRenderStrip();
+ #endif
+ mir_free(aai->tcsFilename);
+ if (aai->pFrameDelays) free(aai->pFrameDelays);
+ _AniAva_ReduceAvatarImages(aai->nStripTop,aai->FrameSize.cx*aai->nFrameCount, FALSE);
+ for (k = 0; k < AniAva.AniAvatarList->realCount; k++)
+ if (k != j) {
+ ANIAVA_INFO * taai = (ANIAVA_INFO *) AniAva.AniAvatarList->items[k];
+ if (taai->nStripTop>aai->nStripTop)
+ taai->nStripTop -= aai->FrameSize.cx*aai->nFrameCount;
+ }
+ if (AniAva.AniAvatarList->realCount>0)
+ {
+ //lets create hNewDC
+ HDC hNewDC;
+ HBITMAP hNewBmp, hNewOldBmp;
+
+ int newWidth = AniAva.width-aai->FrameSize.cx*aai->nFrameCount;
+ int newHeight = 0;
+ int i;
+ for (i=0; i < AniAva.AniAvatarList->realCount; i++)
+ if (i != j)
+ {
+ newHeight = max(newHeight,((ANIAVA_INFO *) AniAva.AniAvatarList->items[i])->FrameSize.cy);
+ }
+
+ hNewDC = CreateCompatibleDC(NULL);
+ hNewBmp = ske_CreateDIB32(newWidth,newHeight);
+ hNewOldBmp = (HBITMAP)SelectObject(hNewDC,hNewBmp);
+ // copy from old and from new strip
+ if (aai->nStripTop>0)
+ BitBlt(hNewDC,0,0,aai->nStripTop,newHeight,AniAva.hAniAvaDC,0,0, SRCCOPY);
+ if (aai->nStripTop+aai->FrameSize.cx*aai->nFrameCount < AniAva.width)
+ BitBlt(hNewDC,aai->nStripTop,0,AniAva.width-(aai->nStripTop+aai->FrameSize.cx*aai->nFrameCount),newHeight,AniAva.hAniAvaDC,aai->nStripTop+aai->FrameSize.cx*aai->nFrameCount,0, SRCCOPY);
+
+ _AniAva_RemoveAniAvaDC(&AniAva);
+ AniAva.hAniAvaDC = hNewDC;
+ AniAva.hAniAvaBitmap = hNewBmp;
+ AniAva.hAniAvaOldBitmap = hNewOldBmp;
+ AniAva.width = newWidth;
+ AniAva.height = newHeight;
+
+ }
+ else
+ {
+ _AniAva_RemoveAniAvaDC(&AniAva);
+ }
+ #ifdef _DEBUG
+ __AniAva_DebugRenderStrip();
+ #endif
+ List_Remove(AniAva.AniAvatarList, j);
+ mir_free(aai);
+ _AniAva_ResumePainting();
+ break;
+ }
+ }
+ }
+}
+static void _AniAva_RemoveAniAvaDC(ANIAVA * pAniAva)
+{
+ if (pAniAva->hAniAvaDC)
+ {
+ SelectObject(pAniAva->hAniAvaDC, pAniAva->hAniAvaOldBitmap);
+ DeleteObject(pAniAva->hAniAvaBitmap);
+ DeleteDC(pAniAva->hAniAvaDC);
+ pAniAva->hAniAvaDC = NULL;
+ pAniAva->height = 0;
+ pAniAva->width = 0;
+ pAniAva->hAniAvaBitmap = NULL;
+ }
+};
+
+static void _AniAva_DestroyAvatarWindow( HWND hwnd)
+{
+ SendMessage(hwnd,AAM_SELFDESTROY,0,0);
+}
+static int _AniAva_LoadAvatarFromImage(TCHAR * szFileName, int width, int height, ANIAVATARIMAGEINFO * pRetAII)
+{
+ ANIAVA_INFO aai = {0};
+ ANIAVA_INFO * paai = NULL;
+ BOOL fNeedInsertToList = FALSE;
+ int idx = 0;
+ aai.tcsFilename = szFileName;
+ aai.FrameSize.cx = width;
+ aai.FrameSize.cy = height;
+
+ if ( !List_GetIndex(AniAva.AniAvatarList,(void*)&aai,&idx)) idx = -1;
+ if (idx == -1) //item not present in list
+ {
+ HBITMAP hBitmap = NULL;
+ HDC hTempDC;
+ HBITMAP hOldBitmap;
+ HDC hNewDC;
+ HBITMAP hNewBmp;
+ HBITMAP hNewOldBmp;
+ int newWidth;
+ int newHeight;
+
+ paai = (ANIAVA_INFO *)mir_calloc(sizeof(ANIAVA_INFO));
+ paai->tcsFilename = mir_tstrdup(szFileName);
+ paai->dwAvatarUniqId = rand();
+ fNeedInsertToList = TRUE;
+ //get image strip
+ GDIPlus_ExtractAnimatedGIF (szFileName, width, height, &hBitmap, &(paai->pFrameDelays), &(paai->nFrameCount), &(paai->FrameSize));
+
+ //copy image to temp DC
+ hTempDC = CreateCompatibleDC(NULL);
+ hOldBitmap = (HBITMAP)SelectObject(hTempDC,hBitmap);
+
+ //lets create hNewDC
+ /*
+ newWidth = max(paai->FrameSize.cx*paai->nFrameCount,AniAva.width);
+ newHeight = AniAva.height+paai->FrameSize.cy;
+ */
+ newWidth = AniAva.width+paai->FrameSize.cx*paai->nFrameCount;
+ newHeight = max(paai->FrameSize.cy,AniAva.height);
+
+ hNewDC = CreateCompatibleDC(NULL);
+ hNewBmp = ske_CreateDIB32(newWidth,newHeight);
+ hNewOldBmp = (HBITMAP)SelectObject(hNewDC,hNewBmp);
+
+ _AniAva_PausePainting();
+ GdiFlush();
+ // copy from old and from new strip
+ BitBlt(hNewDC,0,0,AniAva.width,AniAva.height,AniAva.hAniAvaDC,0,0, SRCCOPY);
+ BitBlt(hNewDC,AniAva.width,0,paai->FrameSize.cx*paai->nFrameCount,paai->FrameSize.cy,hTempDC,0,0, SRCCOPY);
+
+ paai->nStripTop = AniAva.width;
+
+ GdiFlush();
+ //remove temp DC
+ SelectObject(hTempDC,hOldBitmap);
+ DeleteObject(hNewBmp);
+ DeleteDC(hTempDC);
+ DeleteObject(hBitmap);
+
+
+ //delete old
+ _AniAva_RemoveAniAvaDC(&AniAva);
+ //setNewDC;
+ AniAva.hAniAvaDC = hNewDC;
+ AniAva.hAniAvaBitmap = hNewBmp;
+ AniAva.hAniAvaOldBitmap = hNewOldBmp;
+ AniAva.width = newWidth;
+ AniAva.height = newHeight;
+ GdiFlush();
+ _AniAva_ResumePainting();
+ }
+ else
+ {
+ paai = (ANIAVA_INFO *)AniAva.AniAvatarList->items[idx];
+ }
+ if (paai)
+ {
+ paai->nRefCount++;
+ pRetAII->nFramesCount = paai->nFrameCount;
+ pRetAII->pFrameDelays = paai->pFrameDelays;
+ pRetAII->ptImagePos.x = paai->nStripTop;
+ pRetAII->ptImagePos.y = 0;
+ pRetAII->szSize = paai->FrameSize;
+ if (fNeedInsertToList)
+ {
+ //add to list
+ int idx = AniAva.AniAvatarList->realCount;
+ List_GetIndex(AniAva.AniAvatarList, paai,&idx);
+ List_Insert(AniAva.AniAvatarList, (void*)paai, idx);
+ }
+ return paai->dwAvatarUniqId;
+ }
+ return 0;
+}
+static BOOL _AniAva_GetAvatarImageInfo(DWORD dwAvatarUniqId, ANIAVATARIMAGEINFO * avii)
+{
+ int j;
+ BOOL res = FALSE;
+ for (j = 0; j < AniAva.AniAvatarList->realCount; j++)
+ {
+ ANIAVA_INFO * aai = (ANIAVA_INFO *) AniAva.AniAvatarList->items[j];
+ if (aai->dwAvatarUniqId == dwAvatarUniqId)
+ {
+ avii->nFramesCount = aai->nFrameCount;
+ avii->pFrameDelays = aai->pFrameDelays;
+ avii->ptImagePos.x = aai->nStripTop;
+ avii->ptImagePos.y = 0;
+ avii->szSize = aai->FrameSize;
+ res = TRUE;
+ break;
+ }
+ }
+ return res;
+}
+static void _AniAva_Clear_ANIAVA_WINDOWINFO(ANIAVA_WINDOWINFO * pavwi )
+{
+ pavwi->delaysInterval = NULL;
+ pavwi->nFramesCount = 0;
+ KillTimer(pavwi->hWindow,2);
+ pavwi->bPlaying = FALSE;
+ pavwi->TimerId = 0;
+}
+static void __AniAva_DebugRenderStrip()
+{
+ return;
+ #ifdef _DEBUG
+ {
+ HDC hDC_debug = GetDC(NULL);
+ BitBlt(hDC_debug,0,0,AniAva.width, AniAva.height,AniAva.hAniAvaDC,0,0,SRCCOPY);
+ DeleteDC(hDC_debug);
+ }
+ #endif
+}
+
+static void _AniAva_RenderAvatar(ANIAVA_WINDOWINFO * dat, HDC hdcParent /*= NULL*/, RECT * rcInParent /*= NULL */ )
+{
+ if (dat->bPaused>0) { dat->bPended = TRUE; return; }
+ else dat->bPended = FALSE;
+
+ if ( IMMEDIATE_DRAW && hdcParent == NULL ) return;
+ GdiFlush();
+#ifdef _DEBUG
+ __AniAva_DebugRenderStrip();
+#endif
+ if (dat->bPlaying && IsWindowVisible(dat->hWindow))
+ {
+ POINT ptWnd = {0};
+ SIZE szWnd = {dat->rcPos.right-dat->rcPos.left,dat->rcPos.bottom-dat->rcPos.top};
+ BLENDFUNCTION bf = {AC_SRC_OVER, 0,g_CluiData.bCurrentAlpha*dat->bAlpha/256, AC_SRC_ALPHA };
+ POINT pt_from = {0,0};
+ HDC hDC_animation = GetDC(NULL);
+ HDC copyFromDC;
+ RECT clistRect;
+ HDC tempDC = NULL;
+ HBITMAP hBmp;
+ HBITMAP hOldBmp;
+
+ /*
+ int x = bf.SourceConstantAlpha;
+ x = (49152/(383-x))-129;
+ x = min(x,255); x = max(x,0);
+ bf.SourceConstantAlpha = x;
+ */
+ if ( AniAva.bFlags == 0 ) //simple and fastest method - no borders, round corners and etc. just copy
+ {
+ pt_from.x = dat->ptFromPoint.x+dat->currentFrame*dat->sizeAvatar.cx;
+ pt_from.y = dat->ptFromPoint.y;
+ copyFromDC = AniAva.hAniAvaDC;
+ }
+ else
+ {
+ // ... need to create additional hDC_animation
+ HRGN hRgn = NULL;
+ int cornerRadius = AniAva.cornerRadius;
+ tempDC = CreateCompatibleDC( NULL );
+ hBmp = ske_CreateDIB32( szWnd.cx, szWnd.cy );
+ hOldBmp = (HBITMAP)SelectObject(tempDC,hBmp);
+ if ( AniAva.bFlags & AAO_ROUND_CORNERS )
+ {
+ if ( !cornerRadius) //auto radius
+ cornerRadius = min(szWnd.cx, szWnd.cy )/5;
+ }
+ if ( AniAva.bFlags & AAO_HAS_BORDER )
+ {
+ // if has borders - create region (round corners) and fill it, remember internal as clipping
+ HBRUSH hBrush = CreateSolidBrush( AniAva.borderColor );
+ HBRUSH hOldBrush = (HBRUSH)SelectObject( tempDC, hBrush );
+ HRGN rgnOutside = CreateRoundRectRgn( 0, 0, szWnd.cx+1, szWnd.cy+1, cornerRadius << 1, cornerRadius << 1);
+ hRgn = CreateRoundRectRgn( 1, 1, szWnd.cx, szWnd.cy, cornerRadius << 1, cornerRadius << 1);
+ CombineRgn( rgnOutside,rgnOutside,hRgn,RGN_DIFF);
+ FillRgn( tempDC, rgnOutside, hBrush);
+ ske_SetRgnOpaque( tempDC, rgnOutside, TRUE);
+ SelectObject(tempDC, hOldBrush);
+ DeleteObject(hBrush);
+ DeleteObject(rgnOutside);
+ }
+ else if ( cornerRadius > 0 )
+ {
+ // else create clipping area (round corners)
+ hRgn = CreateRoundRectRgn(0, 0, szWnd.cx+1, szWnd.cy+1, cornerRadius << 1, cornerRadius << 1);
+ }
+ else
+ {
+ hRgn = CreateRectRgn(0, 0, szWnd.cx+1, szWnd.cy+1);
+ }
+ // select clip area
+ if ( hRgn )
+ ExtSelectClipRgn(tempDC, hRgn, RGN_AND);
+
+ if ( AniAva.bFlags & AAO_OPAQUE)
+ {
+ // if back color - fill clipping area
+ HBRUSH hBrush = CreateSolidBrush( AniAva.bkgColor );
+ HBRUSH hOldBrush = (HBRUSH)SelectObject( tempDC, hBrush );
+ FillRgn( tempDC, hRgn, hBrush );
+ ske_SetRgnOpaque( tempDC, hRgn, TRUE );
+ }
+ // draw avatar
+ if ( !(AniAva.bFlags & AAO_OPAQUE))
+ BitBlt(tempDC,0, 0, szWnd.cx, szWnd.cy , AniAva.hAniAvaDC , dat->ptFromPoint.x+dat->sizeAvatar.cx*dat->currentFrame, dat->ptFromPoint.y, SRCCOPY);
+ else
+ {
+ BLENDFUNCTION abf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ ske_AlphaBlend(tempDC,0, 0, szWnd.cx, szWnd.cy , AniAva.hAniAvaDC, dat->ptFromPoint.x+dat->sizeAvatar.cx*dat->currentFrame, dat->ptFromPoint.y, szWnd.cx, szWnd.cy, abf);
+ }
+ // reset clip area
+ if ( hRgn )
+ {
+ DeleteObject(hRgn);
+ hRgn = CreateRectRgn(0, 0, szWnd.cx, szWnd.cy);
+ SelectClipRgn(tempDC, hRgn);
+ DeleteObject(hRgn);
+ }
+
+ if (( AniAva.bFlags & AAO_HAS_OVERLAY )
+ && ( dat->overlayIconIdx != -1 )
+ && ( AniAva.overlayIconImageList ))
+ {
+ // if overlay - draw overlay icon
+ // position - on avatar
+ int x = szWnd.cx-ICON_WIDTH;
+ int y = szWnd.cy-ICON_HEIGHT;
+ ske_ImageList_DrawEx(AniAva.overlayIconImageList,
+ dat->overlayIconIdx&0xFFFF,
+ tempDC, x, y, ICON_WIDTH, ICON_HEIGHT,
+ CLR_NONE, CLR_NONE, ILD_NORMAL);
+ }
+ copyFromDC = tempDC;
+ }
+ // intersect visible area
+ // update layered window
+ GetWindowRect(pcli->hwndContactTree, &clistRect);
+ if (dat->rcPos.top < 0)
+ {
+ pt_from.y += -dat->rcPos.top;
+ szWnd.cy += dat->rcPos.top;
+ }
+ if (dat->rcPos.bottom>clistRect.bottom-clistRect.top)
+ szWnd.cy -= (dat->rcPos.bottom-(clistRect.bottom-clistRect.top));
+
+ ptWnd.x = dat->rcPos.left+clistRect.left;
+ ptWnd.y = (dat->rcPos.top>0 ? dat->rcPos.top :0)+clistRect.top;
+ if (szWnd.cy>0)
+ {
+ if ( hdcParent && rcInParent && IMMEDIATE_DRAW )
+ {
+ if ( AniAva.bFlags & AAO_OPAQUE )
+ BitBlt( hdcParent, rcInParent->left, rcInParent->top, szWnd.cx, szWnd.cy, copyFromDC, pt_from.x, pt_from.y, SRCCOPY);
+ else
+ {
+ BLENDFUNCTION abf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ ske_AlphaBlend( hdcParent, rcInParent->left, rcInParent->top, szWnd.cx, szWnd.cy, copyFromDC, pt_from.x, pt_from.y, szWnd.cx, szWnd.cy, abf);
+ }
+ }
+ else if ( !g_proc_UpdateLayeredWindow(dat->hWindow, hDC_animation, &ptWnd, &szWnd, copyFromDC, &pt_from, RGB(0,0,0), &bf, ULW_ALPHA ))
+ {
+ LONG exStyle;
+ exStyle = GetWindowLongPtr(dat->hWindow,GWL_EXSTYLE);
+ exStyle |= WS_EX_LAYERED;
+ SetWindowLongPtr(dat->hWindow,GWL_EXSTYLE,exStyle);
+ if ( !IMMEDIATE_DRAW )
+ SetWindowPos( pcli->hwndContactTree, dat->hWindow, 0, 0, 0, 0, SWP_ASYNCWINDOWPOS | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING );
+ g_proc_UpdateLayeredWindow(dat->hWindow, hDC_animation, &ptWnd, &szWnd, copyFromDC, &pt_from, RGB(0,0,0), &bf, ULW_ALPHA );
+ }
+
+ g_CluiData.fAeroGlass = false;
+ CLUI_UpdateAeroGlass();
+ }
+ else
+ {
+ dat->bPlaying = FALSE;
+ }
+ ReleaseDC(NULL,hDC_animation);
+ if (tempDC)
+ {
+ SelectObject(tempDC, hOldBmp);
+ DeleteObject(hBmp);
+ DeleteDC(tempDC);
+ }
+ }
+ if ( !dat->bPlaying)
+ {
+ ShowWindow(dat->hWindow, SW_HIDE);
+ KillTimer(dat->hWindow,2); //stop animation till set pos will be called
+ }
+ GdiFlush();
+}
+static void _AniAva_PausePainting()
+{
+ int i;
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ SendMessage(pai->hWindow,AAM_PAUSE,0,0);
+ }
+}
+static void _AniAva_ResumePainting()
+{
+ int i;
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ SendNotifyMessage(pai->hWindow,AAM_RESUME,0,0);
+ }
+}
+
+static void _AniAva_ReduceAvatarImages(int startY, int dY, BOOL bDestroyWindow)
+{
+ int i;
+ for (i=0; i < AniAva.Objects->realCount; i++)
+ {
+ ANIAVA_OBJECT * pai = (ANIAVA_OBJECT *)AniAva.Objects->items[i];
+ int res = SendMessage(pai->hWindow,AAM_REMOVEAVATAR,(WPARAM)startY,(LPARAM)dY);
+ if (res == 0xDEAD && bDestroyWindow)
+ {
+ _AniAva_DestroyAvatarWindow(pai->hWindow);
+ mir_free(pai);
+ List_Remove(AniAva.Objects,i);
+ i--;
+ }
+ }
+}
+
+
+static void _AniAva_LoadOptions()
+{
+ aacheck;
+ aalock;
+ {
+ AniAva.bFlags = (db_get_b(NULL,"CList","AvatarsDrawBorders",SETTINGS_AVATARDRAWBORDER_DEFAULT)? AAO_HAS_BORDER :0) |
+ (db_get_b(NULL,"CList","AvatarsRoundCorners",SETTINGS_AVATARROUNDCORNERS_DEFAULT)? AAO_ROUND_CORNERS :0) |
+ (db_get_b(NULL,"CList","AvatarsDrawOverlay",SETTINGS_AVATARDRAWOVERLAY_DEFAULT)? AAO_HAS_OVERLAY :0) |
+ ((0) ? AAO_OPAQUE :0);
+
+ if (AniAva.bFlags & AAO_HAS_BORDER)
+ AniAva.borderColor = (COLORREF)db_get_dw(NULL,"CList","AvatarsBorderColor",SETTINGS_AVATARBORDERCOLOR_DEFAULT);;
+ if (AniAva.bFlags & AAO_ROUND_CORNERS)
+ AniAva.cornerRadius = db_get_b(NULL,"CList","AvatarsUseCustomCornerSize",SETTINGS_AVATARUSECUTOMCORNERSIZE_DEFAULT)? db_get_w(NULL,"CList","AvatarsCustomCornerSize",SETTINGS_AVATARCORNERSIZE_DEFAULT) : 0;
+ if (AniAva.bFlags & AAO_HAS_OVERLAY)
+ {
+ //check image list
+ BYTE type = db_get_b(NULL,"CList","AvatarsOverlayType",SETTINGS_AVATAROVERLAYTYPE_DEFAULT);
+ switch(type)
+ {
+ case SETTING_AVATAR_OVERLAY_TYPE_NORMAL:
+ AniAva.overlayIconImageList = hAvatarOverlays;
+ break;
+ case SETTING_AVATAR_OVERLAY_TYPE_PROTOCOL:
+ case SETTING_AVATAR_OVERLAY_TYPE_CONTACT:
+ AniAva.overlayIconImageList = g_himlCListClc;
+ break;
+ default:
+ AniAva.overlayIconImageList = NULL;
+ }
+ }
+ if (AniAva.bFlags & AAO_OPAQUE)
+ AniAva.bkgColor = 0;
+ AniAva.bSeparateWindow = db_get_b(NULL,"CList","AvatarsInSeparateWnd",SETTINGS_AVATARINSEPARATE_DEFAULT);
+
+ }
+ aaunlock;
+}
+static void _AniAva_AnimationTreadProc(HANDLE hExitEvent)
+{
+ //wait forever till hExitEvent signalled
+ DWORD rc;
+ HANDLE hThread = 0;
+ DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),&hThread,0,FALSE,DUPLICATE_SAME_ACCESS);
+ AniAva.AnimationThreadHandle = hThread;
+ SetThreadPriority(hThread,THREAD_PRIORITY_LOWEST);
+ for (;;)
+ {
+ if ( fnMsgWaitForMultipleObjectsEx )
+ rc = fnMsgWaitForMultipleObjectsEx(1,&hExitEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
+ else
+ rc = MsgWaitForMultipleObjects(1,&hExitEvent, FALSE, INFINITE, QS_ALLINPUT);
+
+ ResetEvent(hExitEvent);
+ if ( rc == WAIT_OBJECT_0 + 1 )
+ {
+ MSG msg;
+ while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if ( IsDialogMessage(msg.hwnd, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ else if ( rc == WAIT_OBJECT_0 )
+ {
+ break;
+ }
+ }
+ CloseHandle(AniAva.AnimationThreadHandle);
+ AniAva.AnimationThreadHandle = NULL;
+}
+
+static int _AniAva_SortAvatarInfo(void * first, void * last)
+{
+ int res = 0;
+ ANIAVA_INFO * aai1 = (ANIAVA_INFO *)first;
+ ANIAVA_INFO * aai2 = (ANIAVA_INFO *)last;
+ if (aai1 && aai1->tcsFilename &&
+ aai2 && aai2->tcsFilename)
+ {
+ res = _tcsicmp(aai2->tcsFilename, aai1->tcsFilename);
+ }
+ else
+ {
+ int a1 = (aai1 && aai1->tcsFilename)? 1:0;
+ int a2 = (aai2 && aai2->tcsFilename)? 1:0;
+ res = a1-a2;
+ }
+
+ if (res == 0)
+ {
+ if ( aai1->FrameSize.cx == aai2->FrameSize.cx && aai1->FrameSize.cy == aai2->FrameSize.cy )
+ return 0;
+ else
+ return 1;
+ }
+ else
+ return res;
+}
+
+void _AniAva_InvalidateParent(ANIAVA_WINDOWINFO * dat)
+{
+ if ( !IMMEDIATE_DRAW ) return;
+ HWND hwndParent = pcli->hwndContactTree;
+ RECT rcPos = dat->rcPos;
+ pcli->pfnInvalidateRect( hwndParent, &rcPos, FALSE );
+}
+
+static LRESULT CALLBACK _AniAva_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ ANIAVA_WINDOWINFO * dat = NULL;
+ if (msg == WM_TIMER || msg == WM_DESTROY || (msg>AAM_FIRST && msg < AAM_LAST))
+ dat = (ANIAVA_WINDOWINFO *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case AAM_REMOVEAVATAR:
+ if (dat->ptFromPoint.x == (int)wParam) return 0xDEAD; //need to destroy window
+ else if (dat->ptFromPoint.x>(int)wParam) dat->ptFromPoint.x -= (int)lParam;
+ return 0;
+
+ case AAM_PAUSE:
+ dat->bPaused++;
+ return 0;
+
+ case AAM_RESUME:
+ dat->bPaused--;
+ if (dat->bPaused) return 0;
+ if (dat->bPended)
+ {
+ if ( !IMMEDIATE_DRAW )
+ _AniAva_RenderAvatar(dat);
+ }
+ dat->bPended = FALSE;
+ return 0;
+
+ case AAM_STOP:
+ if (dat->bPlaying)
+ {
+ dat->bPlaying = FALSE;
+ KillTimer(hwnd,2);
+ ShowWindow(hwnd, SW_HIDE);
+ }
+ return 0;
+
+ case AAM_SETAVATAR:
+ {
+ ANIAVATARIMAGEINFO *paaii = (ANIAVATARIMAGEINFO*)wParam;
+ _AniAva_Clear_ANIAVA_WINDOWINFO(dat);
+ dat->nFramesCount = paaii->nFramesCount;
+ dat->delaysInterval = paaii->pFrameDelays;
+ dat->sizeAvatar = paaii->szSize;
+ dat->ptFromPoint = paaii->ptImagePos;
+ dat->currentFrame = 0;
+ dat->bPlaying = FALSE;
+ return MAKELONG(dat->sizeAvatar.cx,dat->sizeAvatar.cy);
+ }
+
+ case AAM_SETPOSITION:
+ {
+ ANIAVA_POSINFO * papi = (ANIAVA_POSINFO *)lParam;
+ if ( !dat->delaysInterval) return 0;
+ if ( !papi) return 0;
+ dat->rcPos = papi->rcPos;
+ dat->overlayIconIdx = papi->idxOverlay;
+ dat->bAlpha = papi->bAlpha;
+ free(papi);
+ if ( !dat->bPlaying)
+ {
+ dat->bPlaying = TRUE;
+ ShowWindow(hwnd,SW_SHOWNA);
+ dat->currentFrame = 0;
+ KillTimer(hwnd,2);
+ SetTimer(hwnd,2,dat->delaysInterval[0],NULL);
+ }
+ if ( !IMMEDIATE_DRAW )
+ _AniAva_RenderAvatar(dat);
+ return 0;
+ }
+ case AAM_SETPARENT:
+ if ( IMMEDIATE_DRAW ) return 0;
+ dat->bOrderTop = ((HWND)wParam != GetDesktopWindow());
+ SetParent(hwnd,(HWND)wParam);
+ if (dat->bOrderTop)
+ {
+ SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_ASYNCWINDOWPOS);
+ }
+ else
+ {
+ LONG exStyle;
+ exStyle = GetWindowLongPtr(pcli->hwndContactList,GWL_EXSTYLE);
+ SetWindowPos(pcli->hwndContactList,hwnd,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE/*|SWP_ASYNCWINDOWPOS*/);
+ if ( !(exStyle&WS_EX_TOPMOST))
+ SetWindowPos(pcli->hwndContactList,HWND_NOTOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE/*|SWP_ASYNCWINDOWPOS*/);
+ }
+ return 0;
+
+ case AAM_REDRAW:
+ if ( IMMEDIATE_DRAW )
+ return 0;
+ if ( wParam )
+ {
+ if (dat->bOrderTop)
+ {
+ SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_ASYNCWINDOWPOS);
+ }
+ else
+ {
+ LONG exStyle;
+ exStyle = GetWindowLongPtr(pcli->hwndContactList,GWL_EXSTYLE);
+ SetWindowPos(pcli->hwndContactList,hwnd,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE/*|SWP_ASYNCWINDOWPOS*/);
+ if ( !(exStyle&WS_EX_TOPMOST))
+ SetWindowPos(pcli->hwndContactList,HWND_NOTOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE/*|SWP_ASYNCWINDOWPOS*/);
+ }
+ }
+
+ _AniAva_RenderAvatar( dat );
+ return 0;
+
+
+ case AAM_RENDER:
+ {
+ HDC hdc = ( HDC )wParam;
+ RECT* rect = ( RECT* )lParam;
+ _AniAva_RenderAvatar( dat, hdc, rect );
+ }
+ return 0;
+
+ case AAM_SELFDESTROY:
+ return DestroyWindow(hwnd);
+
+ case WM_CREATE:
+ {
+ LONG exStyle;
+ ANIAVA_WINDOWINFO * dat = (ANIAVA_WINDOWINFO *) mir_calloc(sizeof (ANIAVA_WINDOWINFO));
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,(LONG_PTR)dat);
+ dat->hWindow = hwnd;
+ //ShowWindow(dat->hWindow,SW_SHOW);
+ //change layered mode
+ exStyle = GetWindowLongPtr(dat->hWindow,GWL_EXSTYLE);
+ exStyle |= WS_EX_LAYERED;
+ SetWindowLongPtr(dat->hWindow,GWL_EXSTYLE,exStyle);
+ exStyle = GetWindowLongPtr(dat->hWindow,GWL_STYLE);
+ exStyle &= ~WS_POPUP;
+ exStyle |= WS_CHILD;
+ SetWindowLongPtr(dat->hWindow,GWL_STYLE,exStyle);
+ break;
+ }
+ case WM_TIMER:
+ {
+ if ( !IsWindowVisible(hwnd))
+ {
+ DestroyWindow(hwnd);
+ return 0;
+ }
+ dat->currentFrame++;
+ if (dat->currentFrame >= dat->nFramesCount)
+ dat->currentFrame = 0;
+
+ if ( !IMMEDIATE_DRAW )
+ _AniAva_RenderAvatar( dat );
+ else
+ _AniAva_InvalidateParent( dat );
+
+ KillTimer(hwnd,2);
+ SetTimer(hwnd,2,dat->delaysInterval[dat->currentFrame]+1,NULL);
+ return 0;
+ }
+ case WM_DESTROY:
+ {
+ _AniAva_Clear_ANIAVA_WINDOWINFO(dat);
+ mir_free(dat);
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,(LONG_PTR)NULL);
+ break;
+ }
+
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+#undef aacheck
+#undef aalock
+#undef aaunlock
+
+/////////////////////////////////////////////////////////////////
+// some stub
+
+HWND WINAPI MyGetAncestor( HWND hWnd, UINT option )
+{
+ if ( option == GA_PARENT )
+ return GetParent( hWnd );
+
+ if ( option == GA_ROOTOWNER ) {
+ HWND result = hWnd;
+ while( true ) {
+ HWND hParent = GetParent( result );
+ if ( !hParent )
+ return result;
+
+ result = hParent;
+ }
+ }
+
+ return NULL;
+}