diff options
Diffstat (limited to 'plugins/AVS/src')
| -rw-r--r-- | plugins/AVS/src/acc.cpp | 908 | ||||
| -rw-r--r-- | plugins/AVS/src/acc.h | 44 | ||||
| -rw-r--r-- | plugins/AVS/src/commonheaders.h | 119 | ||||
| -rw-r--r-- | plugins/AVS/src/image_utils.cpp | 753 | ||||
| -rw-r--r-- | plugins/AVS/src/image_utils.h | 50 | ||||
| -rw-r--r-- | plugins/AVS/src/main.cpp | 2652 | ||||
| -rw-r--r-- | plugins/AVS/src/mir_thread.cpp | 50 | ||||
| -rw-r--r-- | plugins/AVS/src/mir_thread.h | 49 | ||||
| -rw-r--r-- | plugins/AVS/src/options.cpp | 1147 | ||||
| -rw-r--r-- | plugins/AVS/src/poll.cpp | 319 | ||||
| -rw-r--r-- | plugins/AVS/src/poll.h | 36 | ||||
| -rw-r--r-- | plugins/AVS/src/resource.h | 60 | ||||
| -rw-r--r-- | plugins/AVS/src/version.h | 5 | 
13 files changed, 6192 insertions, 0 deletions
diff --git a/plugins/AVS/src/acc.cpp b/plugins/AVS/src/acc.cpp new file mode 100644 index 0000000000..507245fca8 --- /dev/null +++ b/plugins/AVS/src/acc.cpp @@ -0,0 +1,908 @@ +/*
 +
 +Miranda IM: the free IM client for Microsoft* Windows*
 +
 +Copyright 2000-2004 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.
 +*/
 +
 +#include "commonheaders.h"
 +
 +extern FI_INTERFACE *fei;
 +
 +int GetImageFormat(TCHAR *filename);
 +INT_PTR DrawAvatarPicture(WPARAM wParam, LPARAM lParam);
 +INT_PTR GetAvatarBitmap(WPARAM wParam, LPARAM lParam);
 +INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam);
 +void InternalDrawAvatar(AVATARDRAWREQUEST *r, HBITMAP hbm, LONG bmWidth, LONG bmHeight, DWORD dwFlags);
 +
 +
 +#define DM_AVATARCHANGED (WM_USER + 20)
 +#define DM_MYAVATARCHANGED (WM_USER + 21)
 +
 +#define GIF_DISPOSAL_UNSPECIFIED	0
 +#define GIF_DISPOSAL_LEAVE			1
 +#define GIF_DISPOSAL_BACKGROUND		2
 +#define GIF_DISPOSAL_PREVIOUS		3
 +
 +typedef struct 
 +{
 +	HANDLE hContact;
 +	char proto[64];
 +	HANDLE hHook;
 +	HANDLE hHookMy;
 +	HFONT hFont;   // font
 +	COLORREF borderColor;
 +	COLORREF bkgColor;
 +	COLORREF avatarBorderColor;
 +	int avatarRoundCornerRadius;
 +	TCHAR noAvatarText[128];
 +	BOOL respectHidden;
 +	BOOL showingFlash;
 +	BOOL resizeIfSmaller;
 +	BOOL fAero;
 +	BOOL showingAnimatedGif;
 +
 +	struct {
 +		HBITMAP *hbms;
 +		int *times;
 +
 +		FIMULTIBITMAP *multi;
 +		FIBITMAP *dib;
 +		int frameCount;
 +		int logicalWidth;
 +		int logicalHeight;
 +		BOOL loop;
 +		RGBQUAD background;
 +		BOOL started;
 +
 +		struct {
 +			int num;
 +			int top;
 +			int left;
 +			int width;
 +			int height;
 +			int disposal_method;
 +		} frame;
 +	} ag;
 +
 +} ACCData;
 +
 +
 +void ResizeFlash(HWND hwnd, ACCData* data)
 +{
 +	if ((data->hContact != NULL || data->proto[0] != '\0')
 +		&& ServiceExists(MS_FAVATAR_RESIZE))
 +	{
 +		RECT rc;
 +		GetClientRect(hwnd, &rc);
 +
 +		if (data->borderColor != -1 || data->avatarBorderColor != -1)
 +		{
 +			rc.left ++;
 +			rc.right -= 2;
 +			rc.top ++;
 +			rc.bottom -= 2;
 +		}
 +
 +		FLASHAVATAR fa = {0}; 
 +		fa.hContact = data->hContact;
 +		fa.cProto = data->proto;
 +		fa.hParentWindow = hwnd;
 +		fa.id = 1675;
 +		CallService(MS_FAVATAR_RESIZE, (WPARAM)&fa, (LPARAM)&rc);
 +		CallService(MS_FAVATAR_SETPOS, (WPARAM)&fa, (LPARAM)&rc);
 +	}
 +}
 +
 +void SetBkgFlash(HWND hwnd, ACCData* data)
 +{
 +	if ((data->hContact != NULL || data->proto[0] != '\0')
 +		&& ServiceExists(MS_FAVATAR_SETBKCOLOR))
 +	{
 +		FLASHAVATAR fa = {0}; 
 +		fa.hContact = data->hContact;
 +		fa.cProto = data->proto;
 +		fa.hParentWindow = hwnd;
 +		fa.id = 1675;
 +
 +		if (data->bkgColor != -1)
 +			CallService(MS_FAVATAR_SETBKCOLOR, (WPARAM)&fa, (LPARAM)data->bkgColor);
 +		else
 +			CallService(MS_FAVATAR_SETBKCOLOR, (WPARAM)&fa, (LPARAM)RGB(255,255,255));
 +	}
 +}
 +
 +void DestroyFlash(HWND hwnd, ACCData* data)
 +{
 +	if (!data->showingFlash)
 +		return;
 +
 +	if ((data->hContact != NULL || data->proto[0] != '\0')
 +		&& ServiceExists(MS_FAVATAR_DESTROY))
 +	{
 +		FLASHAVATAR fa = {0}; 
 +		fa.hContact = data->hContact;
 +		fa.cProto = data->proto;
 +		fa.hParentWindow = hwnd;
 +		fa.id = 1675;
 +		CallService(MS_FAVATAR_DESTROY, (WPARAM)&fa, 0);
 +	}
 +
 +	data->showingFlash = FALSE;
 +}
 +
 +void StartFlash(HWND hwnd, ACCData* data)
 +{
 +	if (!ServiceExists(MS_FAVATAR_MAKE))
 +		return;
 +
 +	int format;
 +	if (data->hContact != NULL)
 +	{
 +		format = DBGetContactSettingWord(data->hContact, "ContactPhoto", "Format", 0);
 +	}
 +	else if (data->proto[0] != '\0')
 +	{
 +		protoPicCacheEntry *ace = NULL;
 +		for(int i = 0; i < g_MyAvatars.getCount(); i++) 
 +		{
 +			if (!lstrcmpA(data->proto, g_MyAvatars[i].szProtoname))
 +			{
 +				ace = &g_MyAvatars[i];
 +				break;
 +			}
 +		}
 +
 +		if (ace != NULL && ace->szFilename != NULL)
 +			format = GetImageFormat(ace->szFilename);
 +		else 
 +			format = 0;
 +	}
 +	else
 +		return;
 +
 +	if (format != PA_FORMAT_XML && format != PA_FORMAT_SWF)
 +		return;
 +
 +	FLASHAVATAR fa = {0}; 
 +	fa.hContact = data->hContact;
 +	fa.cProto = data->proto;
 +	fa.hParentWindow = hwnd;
 +	fa.id = 1675;
 +	CallService(MS_FAVATAR_MAKE, (WPARAM)&fa, 0);
 +
 +	if (fa.hWindow == NULL) 
 +		return;
 +
 +	data->showingFlash = TRUE;
 +	ResizeFlash(hwnd, data);
 +	SetBkgFlash(hwnd, data);
 +}
 +
 +BOOL AnimatedGifGetData(ACCData* data)
 +{
 +	FIBITMAP *page = fei->FI_LockPage(data->ag.multi, 0);
 +	if (page == NULL)
 +		return FALSE;
 +	
 +	// Get info
 +	FITAG *tag = NULL;
 +	if (!fei->FI_GetMetadata(FIMD_ANIMATION, page, "LogicalWidth", &tag))
 +		goto ERR;
 +	data->ag.logicalWidth = *(WORD *)fei->FI_GetTagValue(tag);
 +	
 +	if (!fei->FI_GetMetadata(FIMD_ANIMATION, page, "LogicalHeight", &tag))
 +		goto ERR;
 +	data->ag.logicalHeight = *(WORD *)fei->FI_GetTagValue(tag);
 +	
 +	if (!fei->FI_GetMetadata(FIMD_ANIMATION, page, "Loop", &tag))
 +		goto ERR;
 +	data->ag.loop = (*(LONG *)fei->FI_GetTagValue(tag) > 0);
 +	
 +	if (fei->FI_HasBackgroundColor(page))
 +		fei->FI_GetBackgroundColor(page, &data->ag.background);
 +
 +	fei->FI_UnlockPage(data->ag.multi, page, FALSE);
 +	return TRUE;
 +
 +ERR:
 +	fei->FI_UnlockPage(data->ag.multi, page, FALSE);
 +	return FALSE;
 +}
 +
 +void AnimatedGifDispodeFrame(ACCData* data)
 +{
 +	if (data->ag.frame.disposal_method == GIF_DISPOSAL_PREVIOUS) 
 +	{
 +		// TODO
 +	} 
 +	else if (data->ag.frame.disposal_method == GIF_DISPOSAL_BACKGROUND) 
 +	{
 +		for (int y = 0; y < data->ag.frame.height; y++) 
 +		{
 +			RGBQUAD *scanline = (RGBQUAD *) fei->FI_GetScanLine(data->ag.dib, 
 +				data->ag.logicalHeight - (y + data->ag.frame.top) - 1) + data->ag.frame.left;
 +			for (int x = 0; x < data->ag.frame.width; x++)
 +				*scanline++ = data->ag.background;
 +		}
 +	}
 +}
 +
 +void AnimatedGifMountFrame(ACCData* data, int page)
 +{
 +	data->ag.frame.num = page;
 +
 +	if (data->ag.hbms[page] != NULL)
 +	{
 +		data->ag.frame.disposal_method = GIF_DISPOSAL_LEAVE;
 +		return;
 +	}
 +
 +	FIBITMAP *dib = fei->FI_LockPage(data->ag.multi, data->ag.frame.num);
 +	if (dib == NULL)
 +		return;
 +
 +	FITAG *tag = NULL;
 +	if (fei->FI_GetMetadata(FIMD_ANIMATION, dib, "FrameLeft", &tag))
 +		data->ag.frame.left = *(WORD *)fei->FI_GetTagValue(tag);
 +	else
 +		data->ag.frame.left = 0;
 +
 +	if (fei->FI_GetMetadata(FIMD_ANIMATION, dib, "FrameTop", &tag))
 +		data->ag.frame.top = *(WORD *)fei->FI_GetTagValue(tag);
 +	else
 +		data->ag.frame.top = 0;
 +
 +	if (fei->FI_GetMetadata(FIMD_ANIMATION, dib, "FrameTime", &tag))
 +		data->ag.times[page] = *(LONG *)fei->FI_GetTagValue(tag);
 +	else
 +		data->ag.times[page] = 0;
 +
 +	if (fei->FI_GetMetadata(FIMD_ANIMATION, dib, "DisposalMethod", &tag))
 +		data->ag.frame.disposal_method = *(BYTE *)fei->FI_GetTagValue(tag);
 +	else
 +		data->ag.frame.disposal_method = 0;
 +
 +	data->ag.frame.width  = fei->FI_GetWidth(dib);
 +	data->ag.frame.height = fei->FI_GetHeight(dib);
 +
 +
 +	//decode page
 +	int palSize = fei->FI_GetColorsUsed(dib);
 +	RGBQUAD *pal = fei->FI_GetPalette(dib);
 +	bool have_transparent = false;
 +	int transparent_color = -1;
 +	if ( fei->FI_IsTransparent(dib)) {
 +		int count = fei->FI_GetTransparencyCount(dib);
 +		BYTE *table = fei->FI_GetTransparencyTable(dib);
 +		for ( int i = 0; i < count; i++ ) {
 +			if ( table[i] == 0 ) {
 +				have_transparent = true;
 +				transparent_color = i;
 +				break;
 +			}
 +		}
 +	}
 +
 +	//copy page data into logical buffer, with full alpha opaqueness
 +	for ( int y = 0; y < data->ag.frame.height; y++ ) {
 +		RGBQUAD *scanline = (RGBQUAD *)fei->FI_GetScanLine(data->ag.dib, data->ag.logicalHeight - (y + data->ag.frame.top) - 1) + data->ag.frame.left;
 +		BYTE *pageline = fei->FI_GetScanLine(dib, data->ag.frame.height - y - 1);
 +		for ( int x = 0; x < data->ag.frame.width; x++ ) {
 +			if ( !have_transparent || *pageline != transparent_color ) {
 +				*scanline = pal[*pageline];
 +				scanline->rgbReserved = 255;
 +			}
 +			scanline++;
 +			pageline++;
 +		}
 +	}
 +
 +	data->ag.hbms[page] = fei->FI_CreateHBITMAPFromDIB(data->ag.dib);
 +
 +	fei->FI_UnlockPage(data->ag.multi, dib, FALSE);
 +}
 +
 +void AnimatedGifDeleteTmpValues(ACCData* data)
 +{
 +	if (data->ag.multi != NULL)
 +	{
 +		fei->FI_CloseMultiBitmap(data->ag.multi, 0);
 +		data->ag.multi = NULL;
 +	}
 +
 +	if (data->ag.dib != NULL)
 +	{
 +		fei->FI_Unload(data->ag.dib);
 +		data->ag.dib = NULL;
 +	}
 +}
 +
 +void DestroyAnimatedGif(HWND hwnd, ACCData* data)
 +{
 +	if (!data->showingAnimatedGif)
 +		return;
 +
 +	AnimatedGifDeleteTmpValues(data);
 +
 +	if (data->ag.hbms != NULL)
 +	{
 +		for (int i = 0; i < data->ag.frameCount; i++)
 +			if (data->ag.hbms[i] != NULL)
 +				DeleteObject(data->ag.hbms[i]);
 +
 +		free(data->ag.hbms);
 +		data->ag.hbms = NULL;
 +	}
 +
 +	if (data->ag.times != NULL)
 +	{
 +		free(data->ag.times);
 +		data->ag.times = NULL;
 +	}
 +
 +	data->showingAnimatedGif = FALSE;
 +}
 +
 +void StartAnimatedGif(HWND hwnd, ACCData* data)
 +{
 +	if (fei == NULL)
 +		return;
 +
 +	int x, y;
 +	AVATARCACHEENTRY *ace = NULL;
 +
 +	if (data->hContact != NULL)
 +		ace = (AVATARCACHEENTRY *) GetAvatarBitmap((WPARAM) data->hContact, 0);	
 +	else
 +		ace = (AVATARCACHEENTRY *) GetMyAvatar(0, (LPARAM) data->proto);
 +
 +	if (ace == NULL)
 +		return;
 +
 +	int format = GetImageFormat(ace->szFilename);
 +	if (format != PA_FORMAT_GIF)
 +		return;
 +
 +	FREE_IMAGE_FORMAT fif = fei->FI_GetFileTypeT(ace->szFilename, 0);
 +	if(fif == FIF_UNKNOWN)
 +		fif = fei->FI_GetFIFFromFilenameT(ace->szFilename);
 +
 +	data->ag.multi = fei->FI_OpenMultiBitmapT(fif, ace->szFilename, FALSE, TRUE, FALSE, GIF_LOAD256);
 +	if (data->ag.multi == NULL)
 +		return;
 +
 +	data->ag.frameCount = fei->FI_GetPageCount(data->ag.multi);
 +	if (data->ag.frameCount <= 1)
 +		goto ERR;
 +
 +	if (!AnimatedGifGetData(data))
 +		goto ERR;
 +
 +	//allocate entire logical area
 +	data->ag.dib = fei->FI_Allocate(data->ag.logicalWidth, data->ag.logicalHeight, 32, 0, 0, 0);
 +	if (data->ag.dib == NULL)
 +		goto ERR;
 +
 +	//fill with background color to start
 +	for (y = 0; y < data->ag.logicalHeight; y++) 
 +	{
 +		RGBQUAD *scanline = (RGBQUAD *) fei->FI_GetScanLine(data->ag.dib, y);
 +		for (x = 0; x < data->ag.logicalWidth; x++)
 +			*scanline++ = data->ag.background;
 +	}
 +
 +	data->ag.hbms = (HBITMAP *) malloc(sizeof(HBITMAP) * data->ag.frameCount);
 +	memset(data->ag.hbms, 0, sizeof(HBITMAP) * data->ag.frameCount);
 +
 +	data->ag.times = (int *) malloc(sizeof(int) * data->ag.frameCount);
 +	memset(data->ag.times, 0, sizeof(int) * data->ag.frameCount);
 +
 +	AnimatedGifMountFrame(data, 0);
 +
 +	data->showingAnimatedGif = TRUE;
 +
 +	return;
 +ERR:
 +	fei->FI_CloseMultiBitmap(data->ag.multi, 0);
 +	data->ag.multi = NULL;
 +}
 +
 +void DestroyAnimation(HWND hwnd, ACCData* data)
 +{
 +	DestroyFlash(hwnd, data);
 +	DestroyAnimatedGif(hwnd, data);
 +}
 +
 +void StartAnimation(HWND hwnd, ACCData* data)
 +{
 +	StartFlash(hwnd, data);
 +
 +	if (!data->showingFlash)
 +		StartAnimatedGif(hwnd, data);
 +}
 +
 +BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
 +{
 +	BOOL ret;
 +
 +	POINT pt;
 +
 +	pt.x = lpRect->left;
 +	pt.y = lpRect->top;
 +
 +	ret = ScreenToClient(hWnd, &pt);
 +
 +	if (!ret) return ret;
 +
 +	lpRect->left = pt.x;
 +	lpRect->top = pt.y;
 +
 +
 +	pt.x = lpRect->right;
 +	pt.y = lpRect->bottom;
 +
 +	ret = ScreenToClient(hWnd, &pt);
 +
 +	lpRect->right = pt.x;
 +	lpRect->bottom = pt.y;
 +
 +	return ret;
 +}
 +
 +static void Invalidate(HWND hwnd)
 +{
 +	ACCData* data =  (ACCData *) GetWindowLongPtr(hwnd, 0);
 +	if (data->bkgColor == -1)
 +	{
 +		HWND parent = GetParent(hwnd);
 +		RECT rc;
 +		GetWindowRect(hwnd, &rc);
 +		ScreenToClient(parent, &rc); 
 +		InvalidateRect(parent, &rc, TRUE);
 +	}
 +	InvalidateRect(hwnd, NULL, TRUE);
 +}
 +
 +static void NotifyAvatarChange(HWND hwnd)
 +{
 +	PSHNOTIFY pshn = {0};
 +	pshn.hdr.idFrom = GetDlgCtrlID(hwnd);
 +	pshn.hdr.hwndFrom = hwnd;
 +	pshn.hdr.code = NM_AVATAR_CHANGED;
 +	pshn.lParam = 0;
 +	SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) &pshn);
 +}
 +
 +static void DrawText(HDC hdc, HFONT hFont, const RECT &rc, const TCHAR *text)
 +{
 +	HGDIOBJ oldFont = SelectObject(hdc, hFont);
 +
 +	// Get text rectangle
 +	RECT tr = rc;
 +	tr.top += 10;
 +	tr.bottom -= 10;
 +	tr.left += 10;
 +	tr.right -= 10;
 +
 +	// Calc text size
 +	RECT tr_ret = tr;
 +	DrawText(hdc, text, -1, &tr_ret, 
 +			DT_WORDBREAK | DT_NOPREFIX | DT_CENTER | DT_CALCRECT);
 +
 +	// Calc needed size
 +	tr.top += ((tr.bottom - tr.top) - (tr_ret.bottom - tr_ret.top)) / 2;
 +	tr.bottom = tr.top + (tr_ret.bottom - tr_ret.top);
 +	DrawText(hdc, text, -1, &tr, 
 +			DT_WORDBREAK | DT_NOPREFIX | DT_CENTER);
 +
 +	SelectObject(hdc, oldFont);
 +}
 +
 +static LRESULT CALLBACK ACCWndProc(HWND hwnd, UINT msg,  WPARAM wParam, LPARAM lParam) {
 +	ACCData* data =  (ACCData *) GetWindowLongPtr(hwnd, 0);
 +	switch(msg) 
 +	{
 +		case WM_NCCREATE:
 +		{
 +			SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_OWNERDRAW);
 +			SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_TRANSPARENT);
 +
 +			data = (ACCData*) mir_alloc(sizeof(ACCData));
 +			if (data == NULL) 
 +				return FALSE;
 +			SetWindowLongPtr(hwnd, 0, (LONG_PTR)data);
 +
 +			ZeroMemory(data, sizeof(ACCData));
 +			data->hHook = HookEventMessage(ME_AV_AVATARCHANGED, hwnd, DM_AVATARCHANGED);
 +			data->hHookMy = HookEventMessage(ME_AV_MYAVATARCHANGED, hwnd, DM_MYAVATARCHANGED);
 +			data->hFont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
 +			data->borderColor = -1;
 +			data->bkgColor = -1;
 +			data->avatarBorderColor = -1;
 +			data->respectHidden = TRUE;
 +			data->showingFlash = FALSE;
 +			data->resizeIfSmaller = TRUE;
 +			data->showingAnimatedGif = FALSE;
 +			data->fAero = FALSE;
 +
 +			return TRUE;
 +		}
 +		case WM_NCDESTROY:
 +		{
 +			DestroyAnimation(hwnd, data);
 +			if (data) 
 +			{
 +				UnhookEvent(data->hHook);
 +				UnhookEvent(data->hHookMy);
 +				mir_free(data);
 +			}
 +			SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL);
 +			break;
 +		}
 +		case WM_SETFONT:
 +		{
 +			data->hFont = (HFONT)wParam;
 +			Invalidate(hwnd);
 +			break;
 +		}
 +		case AVATAR_SETCONTACT:
 +		{
 +			DestroyAnimation(hwnd, data);
 +
 +			data->hContact = (HANDLE) lParam;
 +			lstrcpynA(data->proto, (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)data->hContact, 0), sizeof(data->proto));
 +
 +			StartAnimation(hwnd, data);
 +
 +			NotifyAvatarChange(hwnd);
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +		case AVATAR_SETPROTOCOL:
 +		{
 +			DestroyAnimation(hwnd, data);
 +
 +			data->hContact = NULL;
 +			if (lParam == NULL)
 +				data->proto[0] = '\0';
 +			else
 +				lstrcpynA(data->proto, (char *) lParam, sizeof(data->proto));
 +
 +			StartAnimation(hwnd, data);
 +
 +			NotifyAvatarChange(hwnd);
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +		case AVATAR_SETBKGCOLOR:
 +		{
 +			data->bkgColor = (COLORREF) lParam;
 +			if (data->showingFlash)
 +				SetBkgFlash(hwnd, data);
 +			NotifyAvatarChange(hwnd);
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +		case AVATAR_SETBORDERCOLOR:
 +		{
 +			data->borderColor = (COLORREF) lParam;
 +			if (data->showingFlash)
 +				ResizeFlash(hwnd, data);
 +			NotifyAvatarChange(hwnd);
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +		case AVATAR_SETAVATARBORDERCOLOR:
 +		{
 +			data->avatarBorderColor = (COLORREF) lParam;
 +			if (data->showingFlash)
 +				ResizeFlash(hwnd, data);
 +			NotifyAvatarChange(hwnd);
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +		case AVATAR_SETAVATARROUNDCORNERRADIUS:
 +		{
 +			data->avatarRoundCornerRadius = (int) lParam;
 +			NotifyAvatarChange(hwnd);
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +		case AVATAR_SETNOAVATARTEXT:
 +		{
 +			lstrcpyn(data->noAvatarText, TranslateTS((TCHAR*) lParam), SIZEOF(data->noAvatarText));
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +		case AVATAR_RESPECTHIDDEN:
 +		{
 +			data->respectHidden = (BOOL) lParam;
 +			NotifyAvatarChange(hwnd);
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +		case AVATAR_SETRESIZEIFSMALLER:
 +		{
 +			data->resizeIfSmaller = (BOOL) lParam;
 +			NotifyAvatarChange(hwnd);
 +			Invalidate(hwnd);
 +			return TRUE;
 +		}
 +
 +		case AVATAR_SETAEROCOMPATDRAWING:
 +			data->fAero = lParam;
 +			return(TRUE);
 +
 +		case AVATAR_GETUSEDSPACE:
 +		{
 +			int *width = (int *)wParam;
 +			int *height = (int *)lParam;
 +
 +			RECT rc;
 +			GetClientRect(hwnd, &rc);
 +
 +			// Get avatar
 +			if (data->showingFlash && ServiceExists(MS_FAVATAR_GETINFO))
 +			{
 +				FLASHAVATAR fa = {0}; 
 +				fa.hContact = data->hContact;
 +				fa.cProto = data->proto;
 +				fa.hParentWindow = hwnd;
 +				fa.id = 1675;
 +				CallService(MS_FAVATAR_GETINFO, (WPARAM)&fa, 0);
 +				if (fa.hWindow != NULL)
 +				{
 +					*width = rc.right - rc.left;
 +					*height = rc.bottom - rc.top;
 +					return TRUE;
 +				}
 +			}
 +
 +			avatarCacheEntry *ace;
 +			if (data->hContact == NULL)
 +				ace = (avatarCacheEntry *) CallService(MS_AV_GETMYAVATAR, 0, (LPARAM) data->proto);
 +			else
 +				ace = (avatarCacheEntry *) CallService(MS_AV_GETAVATARBITMAP, (WPARAM) data->hContact, 0);
 +
 +			if (ace == NULL || ace->bmHeight == 0 || ace->bmWidth == 0 
 +				|| (data->respectHidden && (ace->dwFlags & AVS_HIDEONCLIST)))
 +			{
 +				*width = 0;
 +				*height = 0;
 +				return TRUE;
 +			}
 +
 +			// Get its size
 +			int targetWidth = rc.right - rc.left;
 +			int targetHeight = rc.bottom - rc.top;
 +
 +			if (!data->resizeIfSmaller && ace->bmHeight <= targetHeight && ace->bmWidth <= targetWidth)
 +			{
 +				*height = ace->bmHeight;
 +				*width = ace->bmWidth;
 +			} 
 +			else if (ace->bmHeight > ace->bmWidth)
 +			{
 +				float dScale = targetHeight / (float)ace->bmHeight;
 +				*height = targetHeight;
 +				*width = (int) (ace->bmWidth * dScale);
 +			}
 +			else 
 +			{
 +				float dScale = targetWidth / (float)ace->bmWidth;
 +				*height = (int) (ace->bmHeight * dScale);
 +				*width = targetWidth;
 +			}
 +
 +			return TRUE;
 +		}
 +		case DM_AVATARCHANGED:
 +		{
 +			if (data->hContact == (HANDLE) wParam)
 +			{
 +				DestroyAnimation(hwnd, data);
 +				StartAnimation(hwnd, data);
 +
 +				NotifyAvatarChange(hwnd);
 +				Invalidate(hwnd);
 +			}
 +			break;
 +		}
 +		case DM_MYAVATARCHANGED:
 +		{
 +			if (data->hContact == NULL && strcmp(data->proto, (char*) wParam) == 0)
 +			{
 +				DestroyAnimation(hwnd, data);
 +				StartAnimation(hwnd, data);
 +
 +				NotifyAvatarChange(hwnd);
 +				Invalidate(hwnd);
 +			}
 +			break;
 +		}
 +		case WM_NCPAINT:
 +		case WM_PAINT:
 +		{
 +			PAINTSTRUCT ps;
 +			HDC hdc = BeginPaint(hwnd, &ps);
 +			if (hdc == NULL) 
 +				break;
 +
 +			int oldBkMode = SetBkMode(hdc, TRANSPARENT);
 +			SetStretchBltMode(hdc, HALFTONE);
 +
 +			RECT rc;
 +			GetClientRect(hwnd, &rc);
 +
 +			// Draw background
 +			if (data->bkgColor != -1)
 +			{
 +				HBRUSH hbrush = CreateSolidBrush(data->bkgColor);
 +				FillRect(hdc, &rc, hbrush);
 +				DeleteObject(hbrush);
 +			}
 +
 +			if (data->hContact == NULL && data->proto[0] == '\0'
 +				&& DBGetContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1))
 +			{
 +				DrawText(hdc, data->hFont, rc, TranslateT("Protocols have different avatars"));
 +			}
 +
 +			// Has a flash avatar
 +			else if (data->showingFlash)
 +			{
 +				// Don't draw
 +
 +				// Draw control border if needed
 +				if (data->borderColor == -1 && data->avatarBorderColor != -1)
 +				{
 +					HBRUSH hbrush = CreateSolidBrush(data->avatarBorderColor);
 +					FrameRect(hdc, &rc, hbrush);
 +					DeleteObject(hbrush);
 +				}
 +			}
 +
 +			// Has an animated gif
 +			// Has a "normal" image
 +			else
 +			{
 +				// Draw avatar
 +				AVATARDRAWREQUEST avdrq = {0};
 +				avdrq.cbSize = sizeof(avdrq);
 +				avdrq.rcDraw = rc;
 +				avdrq.hContact = data->hContact;
 +				avdrq.szProto = data->proto;
 +				avdrq.hTargetDC = hdc;
 +				avdrq.dwFlags = AVDRQ_HIDEBORDERONTRANSPARENCY
 +					| (data->respectHidden ? AVDRQ_RESPECTHIDDEN : 0) 
 +					| (data->hContact != NULL ? 0 : AVDRQ_OWNPIC)
 +					| (data->avatarBorderColor == -1 ? 0 : AVDRQ_DRAWBORDER)
 +					| (data->avatarRoundCornerRadius <= 0 ? 0 : AVDRQ_ROUNDEDCORNER)
 +					| (data->fAero ? AVDRQ_AERO : 0)
 +					| (data->resizeIfSmaller ? 0 : AVDRQ_DONTRESIZEIFSMALLER);
 +				avdrq.clrBorder = data->avatarBorderColor;
 +				avdrq.radius = data->avatarRoundCornerRadius;
 +
 +				INT_PTR ret;
 +				if (data->showingAnimatedGif)
 +				{
 +					InternalDrawAvatar(&avdrq, data->ag.hbms[data->ag.frame.num], data->ag.logicalWidth, data->ag.logicalHeight, 0);
 +					ret = 1;
 +
 +					if (!data->ag.started)
 +					{
 +						SetTimer(hwnd, 0, data->ag.times[data->ag.frame.num], NULL);
 +						data->ag.started = TRUE;
 +					}
 +				}
 +				else
 +					ret = DrawAvatarPicture(0, (LPARAM)&avdrq);
 +				
 +				if (ret == 0) 
 +					DrawText(hdc, data->hFont, rc, data->noAvatarText);
 +			}
 +
 +			// Draw control border
 +			if (data->borderColor != -1)
 +			{
 +				HBRUSH hbrush = CreateSolidBrush(data->borderColor);
 +				FrameRect(hdc, &rc, hbrush);
 +				DeleteObject(hbrush);
 +			}
 +
 +			SetBkMode(hdc, oldBkMode);
 +
 +			EndPaint(hwnd, &ps);
 +			return TRUE;
 +		}
 +		case WM_ERASEBKGND:
 +		{
 +			HDC hdc = (HDC) wParam;
 +			RECT rc;
 +			GetClientRect(hwnd, &rc);
 +
 +			// Draw background
 +			if (data->bkgColor != -1)
 +			{
 +				HBRUSH hbrush = CreateSolidBrush(data->bkgColor);
 +				FillRect(hdc, &rc, hbrush);
 +				DeleteObject(hbrush);
 +			}
 +
 +			// Draw control border
 +			if (data->borderColor != -1)
 +			{
 +				HBRUSH hbrush = CreateSolidBrush(data->borderColor);
 +				FrameRect(hdc, &rc, hbrush);
 +				DeleteObject(hbrush);
 +			}
 +
 +			return TRUE;
 +		}
 +		case WM_SIZE:
 +		{
 +			if (data->showingFlash)
 +				ResizeFlash(hwnd, data);
 +			InvalidateRect(hwnd, NULL, TRUE);
 +			break;
 +		}
 +		case WM_TIMER:
 +		{
 +			if (wParam != 0)
 +				break;
 +			KillTimer(hwnd, 0);
 +
 +			if (!data->showingAnimatedGif)
 +				break;
 +
 +			AnimatedGifDispodeFrame(data);
 +
 +			int frame = data->ag.frame.num + 1;
 +			if (frame >= data->ag.frameCount)
 +			{
 +				// Don't need fi data no more
 +				AnimatedGifDeleteTmpValues(data);
 +				frame = 0;
 +			}
 +			AnimatedGifMountFrame(data, frame);
 +
 +			data->ag.started = FALSE;
 +			InvalidateRect(hwnd, NULL, FALSE);
 +
 +			break;
 +		}
 +	}
 +	return DefWindowProc(hwnd, msg, wParam, lParam);
 +}
 +
 +
 +int LoadACC() 
 +{
 +	WNDCLASSEX wc = {0};
 +	wc.cbSize         = sizeof(wc);
 +	wc.lpszClassName  = AVATAR_CONTROL_CLASS;
 +	wc.lpfnWndProc    = ACCWndProc;
 +	wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
 +	wc.cbWndExtra     = sizeof(ACCData*);
 +	wc.hbrBackground  = 0;
 +	wc.style          = CS_GLOBALCLASS;
 +	RegisterClassEx(&wc);
 +	return 0;
 +}
 diff --git a/plugins/AVS/src/acc.h b/plugins/AVS/src/acc.h new file mode 100644 index 0000000000..11b235adc0 --- /dev/null +++ b/plugins/AVS/src/acc.h @@ -0,0 +1,44 @@ +/* 
 +Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
 +
 +This is free software; you can redistribute it and/or
 +modify it under the terms of the GNU Library General Public
 +License as published by the Free Software Foundation; either
 +version 2 of the License, or (at your option) any later version.
 +
 +This 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
 +Library General Public License for more details.
 +
 +You should have received a copy of the GNU Library General Public
 +License along with this file; see the file license.txt.  If
 +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 +Boston, MA 02111-1307, USA.  
 +*/
 +
 +
 +#ifndef __ACC_H__
 +# define __ACC_H__
 +
 +class A2T
 +{
 +	TCHAR* buf;
 +
 +public:
 +	A2T( const char* s ) : buf( mir_a2t( s )) {}
 +	A2T( const char* s, int cp ) : buf( mir_a2t_cp( s, cp )) {}
 +	~A2T() { mir_free(buf); }
 +
 +	__forceinline operator TCHAR*() const
 +	{	return buf;
 +	}
 +};
 +
 +INT_PTR avSetAvatar( HANDLE hContact, TCHAR* tszPath );
 +INT_PTR avSetMyAvatar( char* szProto, TCHAR* tszPath );
 +
 +int LoadACC();
 +
 +
 +#endif // __ACC_H__
 diff --git a/plugins/AVS/src/commonheaders.h b/plugins/AVS/src/commonheaders.h new file mode 100644 index 0000000000..5b9d4ee113 --- /dev/null +++ b/plugins/AVS/src/commonheaders.h @@ -0,0 +1,119 @@ +/*
 +
 +Miranda IM: the free IM client for Microsoft* Windows*
 +
 +Copyright 2000-2004 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.
 +*/
 +
 +#define MIRANDA_VER  0x0A00
 +#define _WIN32_WINNT 0x0501
 +
 +#include "m_stdhdr.h"
 +
 +#include <windows.h>
 +#include <gdiplus.h>
 +#include <commctrl.h>
 +#include <stdio.h>
 +#include <time.h>
 +#include <stddef.h>
 +#include <process.h>
 +#include <io.h>
 +#include <string.h>
 +#include <direct.h>
 +#include <math.h>
 +#include <win2k.h>
 +
 +#include <newpluginapi.h>
 +#include <m_system_cpp.h>
 +#include <m_clist.h>
 +#include <m_clc.h>
 +#include <m_clui.h>
 +#include <m_plugins.h>
 +#include <m_system.h>
 +#include <m_database.h>
 +#include <m_langpack.h>
 +#include <m_button.h>
 +#include <m_options.h>
 +#include <m_protosvc.h>
 +#include <m_utils.h>
 +#include <m_skin.h>
 +#include <m_contacts.h>
 +#include <m_file.h>
 +#include <m_addcontact.h>
 +#include <m_png.h>
 +#include <m_userinfo.h>
 +#include "m_folders.h"
 +
 +#include <m_avatars.h>
 +#include <m_popup.h>
 +#include <m_imgsrvc.h>
 +#include "m_metacontacts.h"
 +#include "m_avatarhistory.h"
 +
 +#include "resource.h"
 +#include "m_updater.h"
 +#include "m_flash.h"
 +#include "image_utils.h"
 +#include "mir_thread.h"
 +#include "poll.h"
 +#include "m_acc.h"
 +#include "acc.h"
 +
 +
 +// shared vars
 +//extern HINSTANCE g_hInst;
 +
 +/* most free()'s are invalid when the code is executed from a dll, so this changes
 + all the bad free()'s to good ones, however it's still incorrect code. The reasons for not
 + changing them include:
 +
 +  * DBFreeVariant has a CallService() lookup
 +  * free() is executed in some large loops to do with clist creation of group data
 +  * easy search and replace
 +
 +*/
 +
 +// The same fields as avatarCacheEntry + proto name
 +struct protoPicCacheEntry : public avatarCacheEntry
 +{
 +	__inline void* operator new( size_t size ) { return mir_alloc( size ); }
 +	__inline void operator delete( void* p ) { mir_free( p ); }
 +
 +    protoPicCacheEntry() { memset(this, 0, sizeof(*this)); };
 +	~protoPicCacheEntry();
 +
 +	void clear();
 +
 +	char*  szProtoname;
 +	TCHAR* tszAccName;
 +};
 +
 +extern OBJLIST<protoPicCacheEntry> g_ProtoPictures, g_MyAvatars;
 +
 +
 +int SetAvatarAttribute(HANDLE hContact, DWORD attrib, int mode);
 +
 +#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]))
 +
 +
 +#define GAIR_FAILED 1000
 +
 +#define AVS_IGNORENOTIFY 0x1000
 +
 +#define AVS_DEFAULT "Global avatar"
 diff --git a/plugins/AVS/src/image_utils.cpp b/plugins/AVS/src/image_utils.cpp new file mode 100644 index 0000000000..35cb33259a --- /dev/null +++ b/plugins/AVS/src/image_utils.cpp @@ -0,0 +1,753 @@ +#include "commonheaders.h"
 +#include "image_utils.h"
 +
 +#include <ocidl.h>
 +#include <olectl.h>
 +
 +/*
 +Theese are the ones needed
 +#include <win2k.h>
 +#include <newpluginapi.h>
 +#include <m_langpack.h>
 +#include <m_utils.h>
 +#include <m_png.h>
 +#include <m_protocols.h>
 +*/
 +
 +extern int _DebugTrace(const char *fmt, ...);
 +extern int _DebugTrace(HANDLE hContact, const char *fmt, ...);
 +
 +
 +#define GET_PIXEL(__P__, __X__, __Y__) ( __P__ + width * 4 * (__Y__) + 4 * (__X__))
 +
 +
 +extern FI_INTERFACE *fei;
 +
 +// Make a bitmap all transparent, but only if it is a 32bpp
 +void MakeBmpTransparent(HBITMAP hBitmap)
 +{
 +	BITMAP bmp;
 +	DWORD dwLen;
 +	BYTE *p;
 +
 +	GetObject(hBitmap, sizeof(bmp), &bmp);
 +
 +	if (bmp.bmBitsPixel != 32)
 +		return;
 +
 +	dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
 +	p = (BYTE *)malloc(dwLen);
 +	if (p == NULL)
 +		return;
 +
 +	memset(p, 0, dwLen);
 +	SetBitmapBits(hBitmap, dwLen, p);
 +
 +	free(p);
 +}
 +
 +// Resize /////////////////////////////////////////////////////////////////////////////////////////
 +
 +
 +// Returns a copy of the bitmap with the size especified
 +// wParam = ResizeBitmap *
 +// lParam = NULL
 +INT_PTR BmpFilterResizeBitmap(WPARAM wParam,LPARAM lParam)
 +{
 +	// Call freeiamge service (is here only for backward compatibility)
 +	return CallService(MS_IMG_RESIZE, wParam, lParam);
 +}
 +
 +HBITMAP CopyBitmapTo32(HBITMAP hBitmap)
 +{
 +	BITMAPINFO RGB32BitsBITMAPINFO;
 +	BYTE * ptPixels;
 +	HBITMAP hDirectBitmap;
 +
 +	BITMAP bmp;
 +	DWORD dwLen;
 +	BYTE *p;
 +
 +	GetObject(hBitmap, sizeof(bmp), &bmp);
 +
 +	dwLen = bmp.bmWidth * bmp.bmHeight * 4;
 +	p = (BYTE *)malloc(dwLen);
 +	if (p == NULL)
 +		return NULL;
 +
 +	// Create bitmap
 +	ZeroMemory(&RGB32BitsBITMAPINFO, sizeof(BITMAPINFO));
 +	RGB32BitsBITMAPINFO.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 +	RGB32BitsBITMAPINFO.bmiHeader.biWidth = bmp.bmWidth;
 +	RGB32BitsBITMAPINFO.bmiHeader.biHeight = bmp.bmHeight;
 +	RGB32BitsBITMAPINFO.bmiHeader.biPlanes = 1;
 +	RGB32BitsBITMAPINFO.bmiHeader.biBitCount = 32;
 +
 +	hDirectBitmap = CreateDIBSection(NULL,
 +									(BITMAPINFO *)&RGB32BitsBITMAPINFO,
 +									DIB_RGB_COLORS,
 +									(void **)&ptPixels,
 +									NULL, 0);
 +
 +	// Copy data
 +	if (bmp.bmBitsPixel != 32)
 +	{
 +		HDC hdcOrig, hdcDest;
 +		HBITMAP oldOrig, oldDest;
 +
 +		hdcOrig = CreateCompatibleDC(NULL);
 +		oldOrig = (HBITMAP) SelectObject(hdcOrig, hBitmap);
 +
 +		hdcDest = CreateCompatibleDC(NULL);
 +		oldDest = (HBITMAP) SelectObject(hdcDest, hDirectBitmap);
 +
 +		BitBlt(hdcDest, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcOrig, 0, 0, SRCCOPY);
 +
 +		SelectObject(hdcDest, oldDest);
 +		DeleteObject(hdcDest);
 +		SelectObject(hdcOrig, oldOrig);
 +		DeleteObject(hdcOrig);
 +
 +		// Set alpha
 +		fei->FI_CorrectBitmap32Alpha(hDirectBitmap, FALSE);
 +	}
 +	else
 +	{
 +		GetBitmapBits(hBitmap, dwLen, p);
 +		SetBitmapBits(hDirectBitmap, dwLen, p);
 +	}
 +
 +	free(p);
 +
 +	return hDirectBitmap;
 +}
 +
 +HBITMAP CreateBitmap32(int cx, int cy)
 +{
 +	BITMAPINFO RGB32BitsBITMAPINFO;
 +	UINT * ptPixels;
 +	HBITMAP DirectBitmap;
 +
 +	ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO));
 +	RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
 +	RGB32BitsBITMAPINFO.bmiHeader.biWidth=cx;//bm.bmWidth;
 +	RGB32BitsBITMAPINFO.bmiHeader.biHeight=cy;//bm.bmHeight;
 +	RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1;
 +	RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32;
 +
 +	DirectBitmap = CreateDIBSection(NULL,
 +									(BITMAPINFO *)&RGB32BitsBITMAPINFO,
 +									DIB_RGB_COLORS,
 +									(void **)&ptPixels,
 +									NULL, 0);
 +	return DirectBitmap;
 +}
 +
 +// Set the color of points that are transparent
 +void SetTranspBkgColor(HBITMAP hBitmap, COLORREF color)
 +{
 +	BITMAP bmp;
 +	DWORD dwLen;
 +	BYTE *p;
 +	int x, y;
 +
 +	GetObject(hBitmap, sizeof(bmp), &bmp);
 +
 +	if (bmp.bmBitsPixel != 32)
 +		return;
 +
 +	dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
 +	p = (BYTE *)malloc(dwLen);
 +	if (p == NULL)
 +		return;
 +	memset(p, 0, dwLen);
 +
 +	GetBitmapBits(hBitmap, dwLen, p);
 +
 +	bool changed = false;
 +	for (y = 0; y < bmp.bmHeight; ++y) {
 +		BYTE *px = p + bmp.bmWidth * 4 * y;
 +
 +		for (x = 0; x < bmp.bmWidth; ++x)
 +		{
 +			if (px[3] == 0)
 +			{
 +				px[0] = GetBValue(color);
 +				px[1] = GetGValue(color);
 +				px[2] = GetRValue(color);
 +				changed = true;
 +			}
 +			px += 4;
 +		}
 +	}
 +
 +	if (changed)
 +		SetBitmapBits(hBitmap, dwLen, p);
 +
 +	free(p);
 +}
 +
 +
 +#define HIMETRIC_INCH   2540    // HIMETRIC units per inch
 +
 +void SetHIMETRICtoDP(HDC hdc, SIZE* sz)
 +{
 +	POINT pt;
 +	int nMapMode = GetMapMode(hdc);
 +	if ( nMapMode < MM_ISOTROPIC && nMapMode != MM_TEXT )
 +	{
 +		// when using a constrained map mode, map against physical inch
 +		SetMapMode(hdc,MM_HIMETRIC);
 +		pt.x = sz->cx;
 +		pt.y = sz->cy;
 +		LPtoDP(hdc,&pt,1);
 +		sz->cx = pt.x;
 +		sz->cy = pt.y;
 +		SetMapMode(hdc, nMapMode);
 +	}
 +	else
 +	{
 +		// map against logical inch for non-constrained mapping modes
 +		int cxPerInch, cyPerInch;
 +		cxPerInch = GetDeviceCaps(hdc,LOGPIXELSX);
 +		cyPerInch = GetDeviceCaps(hdc,LOGPIXELSY);
 +		sz->cx = MulDiv(sz->cx, cxPerInch, HIMETRIC_INCH);
 +		sz->cy = MulDiv(sz->cy, cyPerInch, HIMETRIC_INCH);
 +	}
 +
 +	pt.x = sz->cx;
 +	pt.y = sz->cy;
 +	DPtoLP(hdc,&pt,1);
 +	sz->cx = pt.x;
 +	sz->cy = pt.y;
 +}
 +
 +INT_PTR BmpFilterLoadBitmap32(WPARAM wParam,LPARAM lParam)
 +{
 +	FIBITMAP *dib32 = NULL;
 +
 +	if(fei == NULL)
 +		return 0;
 +
 +	FIBITMAP *dib = (FIBITMAP *)CallService(MS_IMG_LOAD, lParam, IMGL_RETURNDIB|IMGL_TCHAR);
 +
 +	if(dib == NULL)
 +		return 0;
 +
 +	if(fei->FI_GetBPP(dib) != 32) {
 +		dib32 = fei->FI_ConvertTo32Bits(dib);
 +		fei->FI_Unload(dib);
 +	}
 +	else
 +		dib32 = dib;
 +
 +	if(dib32) {
 +		if(fei->FI_IsTransparent(dib32)) {
 +			if(wParam) {
 +				DWORD *dwTrans = (DWORD *)wParam;
 +				*dwTrans = 1;
 +			}
 +		}
 +		if(fei->FI_GetWidth(dib32) > 128 || fei->FI_GetHeight(dib32) > 128) {
 +			FIBITMAP *dib_new = fei->FI_MakeThumbnail(dib32, 128, FALSE);
 +			fei->FI_Unload(dib32);
 +			if(dib_new == NULL)
 +				return 0;
 +			dib32 = dib_new;
 +		}
 +
 +		HBITMAP bitmap = fei->FI_CreateHBITMAPFromDIB(dib32);
 +
 +		fei->FI_Unload(dib32);
 +		fei->FI_CorrectBitmap32Alpha(bitmap, FALSE);
 +		return (INT_PTR)bitmap;
 +	}
 +	return 0;
 +}
 +
 +static HWND hwndClui = 0;
 +
 +//
 +// Save ///////////////////////////////////////////////////////////////////////////////////////////
 +// PNG and BMP will be saved as 32bit images, jpg as 24bit with default quality (75)
 +// returns 1 on success, 0 on failure
 +
 +int BmpFilterSaveBitmap(HBITMAP hBmp, char *szFile, int flags)
 +{
 +	IMGSRVC_INFO i = {0};
 +	i.cbSize = sizeof(IMGSRVC_INFO);
 +	i.szName = szFile;
 +	i.hbm = hBmp;
 +	i.dwMask = IMGI_HBITMAP;
 +	i.fif = FIF_UNKNOWN;
 +
 +	return !CallService(MS_IMG_SAVE, (WPARAM) &i, MAKELONG(0, flags));
 +}
 +
 +
 +int BmpFilterSaveBitmapW(HBITMAP hBmp, wchar_t *wszFile, int flags)
 +{
 +	IMGSRVC_INFO i = {0};
 +	i.cbSize = sizeof(IMGSRVC_INFO);
 +	i.wszName = wszFile;
 +	i.hbm = hBmp;
 +	i.dwMask = IMGI_HBITMAP;
 +	i.fif = FIF_UNKNOWN;
 +
 +	return !CallService(MS_IMG_SAVE, (WPARAM) &i, MAKELONG(IMGL_WCHAR, flags));
 +}
 +
 +// Save an HBITMAP to an image
 +// wParam = HBITMAP
 +// lParam = filename
 +INT_PTR BmpFilterSaveBitmap(WPARAM wParam,LPARAM lParam)
 +{
 +	if ( fei == NULL )
 +		return -1;
 +
 +	const char *szFile = (const char*)lParam;
 +	char szFilename[MAX_PATH];
 +	if ( !CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)szFile, (LPARAM)szFilename))
 +		mir_snprintf(szFilename, SIZEOF(szFilename), "%s", szFile);
 +
 +	int filenameLen = lstrlenA( szFilename );
 +	if ( filenameLen > 4 )
 +		return BmpFilterSaveBitmap(( HBITMAP )wParam, szFilename, 0);
 +
 +	return -1;
 +}
 +
 +INT_PTR BmpFilterSaveBitmapW(WPARAM wParam,LPARAM lParam)
 +{
 +	if ( fei == NULL )
 +		return -1;
 +
 +	const wchar_t *wszFile = (const wchar_t *)lParam;
 +	wchar_t wszFilename[MAX_PATH];
 +	if ( !CallService(MS_UTILS_PATHTOABSOLUTEW, (WPARAM)wszFile, (LPARAM)wszFilename))
 +		mir_sntprintf(wszFilename, SIZEOF(wszFilename), _T("%s"), wszFile);
 +
 +	int filenameLen = lstrlenW( wszFilename );
 +	if ( filenameLen > 4 )
 +		return BmpFilterSaveBitmapW(( HBITMAP )wParam, wszFilename, 0 );
 +
 +	return -1;
 +}
 +
 +// Returns != 0 if can save that type of image, = 0 if cant
 +// wParam = 0
 +// lParam = PA_FORMAT_*   // image format
 +// kept for compatibilty - with freeimage we can save all common formats
 +
 +INT_PTR BmpFilterCanSaveBitmap(WPARAM wParam,LPARAM lParam)
 +{
 +	return 1;
 +}
 +
 +
 +// Other utilities ////////////////////////////////////////////////////////////////////////////////
 +
 +
 +static BOOL ColorsAreTheSame(int colorDiff, BYTE *px1, BYTE *px2)
 +{
 +	return abs(px1[0] - px2[0]) <= colorDiff
 +			&& abs(px1[1] - px2[1]) <= colorDiff
 +			&& abs(px1[2] - px2[2])  <= colorDiff;
 +}
 +
 +
 +void AddToStack(int *stack, int *topPos, int x, int y)
 +{
 +	int i;
 +
 +	// Already is in stack?
 +	for(i = 0 ; i < *topPos ; i += 2)
 +	{
 +		if (stack[i] == x && stack[i+1] == y)
 +			return;
 +	}
 +
 +	stack[*topPos] = x;
 +	(*topPos)++;
 +
 +	stack[*topPos] = y;
 +	(*topPos)++;
 +}
 +
 +
 +BOOL GetColorForPoint(int colorDiff, BYTE *p, int width, int height,
 +					  int x0, int y0, int x1, int y1, int x2, int y2, BOOL *foundBkg, BYTE colors[][3])
 +{
 +	BYTE *px1, *px2, *px3;
 +
 +	px1 = GET_PIXEL(p, x0,y0);
 +	px2 = GET_PIXEL(p, x1,y1);
 +	px3 = GET_PIXEL(p, x2,y2);
 +
 +	// If any of the corners have transparency, forget about it
 +	// Not using != 255 because some MSN bmps have 254 in some positions
 +	if (px1[3] < 253 || px2[3] < 253 || px3[3] < 253)
 +		return FALSE;
 +
 +	// See if is the same color
 +	if (ColorsAreTheSame(colorDiff, px1, px2) && ColorsAreTheSame(colorDiff, px3, px2))
 +	{
 +		*foundBkg = TRUE;
 +		memmove(colors, px1, 3);
 +	}
 +	else
 +	{
 +		*foundBkg = FALSE;
 +	}
 +
 +	return TRUE;
 +}
 +
 +
 +DWORD GetImgHash(HBITMAP hBitmap)
 +{
 +	BITMAP bmp;
 +	DWORD dwLen;
 +	WORD *p;
 +
 +	GetObject(hBitmap, sizeof(bmp), &bmp);
 +
 +	dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
 +	p = (WORD *)malloc(dwLen);
 +	if (p == NULL)
 +		return 0;
 +	memset(p, 0, dwLen);
 +
 +	GetBitmapBits(hBitmap, dwLen, p);
 +
 +	DWORD ret = 0;
 +	for (DWORD i = 0 ; i < dwLen/2 ; i++)
 +		ret += p[i];
 +
 +	free(p);
 +
 +	return ret;
 +}
 +
 +/*
 + * Changes the handle to a grayscale image
 + */
 +HBITMAP MakeGrayscale(HANDLE hContact, HBITMAP hBitmap)
 +{
 +	if(hBitmap) {
 +		FIBITMAP *dib = fei->FI_CreateDIBFromHBITMAP(hBitmap);
 +
 +		if(dib) {
 +			FIBITMAP *dib_new = fei->FI_ConvertToGreyscale(dib);
 +			fei->FI_Unload(dib);
 +			if(dib_new) {
 +				DeleteObject(hBitmap);
 +				HBITMAP hbm_new = fei->FI_CreateHBITMAPFromDIB(dib_new);
 +				fei->FI_Unload(dib_new);
 +				return hbm_new;
 +			}
 +		}
 +	}
 +	return hBitmap;
 +}
 +
 +/*
 + * See if finds a transparent background in image, and set its transparency
 + * Return TRUE if found a transparent background
 + */
 +BOOL MakeTransparentBkg(HANDLE hContact, HBITMAP *hBitmap)
 +{
 +	BYTE *p = NULL;
 +	DWORD dwLen;
 +	int width, height, i, j;
 +	BITMAP bmp;
 +	BYTE colors[8][3];
 +	BOOL foundBkg[8];
 +	BYTE *px1;
 +	int count, maxCount, selectedColor;
 +	HBITMAP hBmpTmp;
 +	int colorDiff;
 +
 +	GetObject(*hBitmap, sizeof(bmp), &bmp);
 +	width = bmp.bmWidth;
 +	height = bmp.bmHeight;
 +	colorDiff = DBGetContactSettingWord(hContact, "ContactPhoto", "TranspBkgColorDiff",
 +					DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgColorDiff", 10));
 +
 +	// Min 5x5 to easy things in loop
 +	if (width <= 4 || height <= 4)
 +		return FALSE;
 +
 +	dwLen = width * height * 4;
 +	p = (BYTE *)malloc(dwLen);
 +	if (p == NULL)
 +	{
 +		return FALSE;
 +	}
 +
 +	if (bmp.bmBitsPixel == 32)
 +	{
 +		hBmpTmp = *hBitmap;
 +	}
 +	else
 +	{
 +		// Convert to 32 bpp
 +		hBmpTmp = CopyBitmapTo32(*hBitmap);
 +	}
 +
 +	GetBitmapBits(hBmpTmp, dwLen, p);
 +
 +	// **** Get corner colors
 +
 +	// Top left
 +	if (!GetColorForPoint(colorDiff, p, width, height,
 +						  0, 0, 0, 1, 1, 0, &foundBkg[0], &colors[0]))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Top center
 +	if (!GetColorForPoint(colorDiff, p, width, height,
 +						  width/2, 0, width/2-1, 0, width/2+1, 0, &foundBkg[1], &colors[1]))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Top Right
 +	if (!GetColorForPoint(colorDiff, p, width, height,
 +						  width-1, 0, width-1, 1, width-2, 0, &foundBkg[2], &colors[2]))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Center left
 +	if (!GetColorForPoint(colorDiff, p, width, height,
 +						  0, height/2, 0, height/2-1, 0, height/2+1, &foundBkg[3], &colors[3]))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Center left
 +	if (!GetColorForPoint(colorDiff, p, width, height,
 +						  width-1, height/2, width-1, height/2-1, width-1, height/2+1, &foundBkg[4], &colors[4]))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Bottom left
 +	if (!GetColorForPoint(colorDiff, p, width, height,
 +						  0, height-1, 0, height-2, 1, height-1, &foundBkg[5], &colors[5]))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Bottom center
 +	if (!GetColorForPoint(colorDiff, p, width, height,
 +						  width/2, height-1, width/2-1, height-1, width/2+1, height-1, &foundBkg[6], &colors[6]))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Bottom Right
 +	if (!GetColorForPoint(colorDiff, p, width, height,
 +						  width-1, height-1, width-1, height-2, width-2, height-1, &foundBkg[7], &colors[7]))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// **** X corners have to have the same color
 +
 +	count = 0;
 +	for (i = 0 ; i < 8 ; i++)
 +	{
 +		if (foundBkg[i])
 +			count++;
 +	}
 +
 +	if (count < DBGetContactSettingWord(hContact, "ContactPhoto", "TranspBkgNumPoints",
 +						DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgNumPoints", 5)))
 +	{
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Ok, X corners at least have a color, lets compare then
 +	maxCount = 0;
 +	for (i = 0 ; i < 8 ; i++)
 +	{
 +		if (foundBkg[i])
 +		{
 +			count = 0;
 +
 +			for (j = 0 ; j < 8 ; j++)
 +			{
 +				if (foundBkg[j] && ColorsAreTheSame(colorDiff, (BYTE *) &colors[i], (BYTE *) &colors[j]))
 +					count++;
 +			}
 +
 +			if (count > maxCount)
 +			{
 +				maxCount = count;
 +				selectedColor = i;
 +			}
 +		}
 +	}
 +
 +	if (maxCount < DBGetContactSettingWord(hContact, "ContactPhoto", "TranspBkgNumPoints",
 +						DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgNumPoints", 5)))
 +	{
 +		// Not enought corners with the same color
 +		if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp);
 +		free(p);
 +		return FALSE;
 +	}
 +
 +	// Get bkg color as mean of colors
 +	{
 +		int bkgColor[3];
 +
 +		bkgColor[0] = 0;
 +		bkgColor[1] = 0;
 +		bkgColor[2] = 0;
 +		for (i = 0 ; i < 8 ; i++)
 +		{
 +			if (foundBkg[i] && ColorsAreTheSame(colorDiff, (BYTE *) &colors[i], (BYTE *) &colors[selectedColor]))
 +			{
 +				bkgColor[0] += colors[i][0];
 +				bkgColor[1] += colors[i][1];
 +				bkgColor[2] += colors[i][2];
 +			}
 +		}
 +		bkgColor[0] /= maxCount;
 +		bkgColor[1] /= maxCount;
 +		bkgColor[2] /= maxCount;
 +
 +		colors[selectedColor][0] = bkgColor[0];
 +		colors[selectedColor][1] = bkgColor[1];
 +		colors[selectedColor][2] = bkgColor[2];
 +	}
 +
 +	// **** Set alpha for the background color, from the borders
 +
 +	if (hBmpTmp != *hBitmap)
 +	{
 +		DeleteObject(*hBitmap);
 +
 +		*hBitmap = hBmpTmp;
 +
 +		GetObject(*hBitmap, sizeof(bmp), &bmp);
 +		GetBitmapBits(*hBitmap, dwLen, p);
 +	}
 +
 +	{
 +		// Set alpha from borders
 +		int x, y;
 +		int topPos = 0;
 +		int curPos = 0;
 +		int *stack = (int *)malloc(width * height * 2 * sizeof(int));
 +		bool transpProportional = (DBGetContactSettingByte(NULL, AVS_MODULE, "MakeTransparencyProportionalToColorDiff", 0) != 0);
 +
 +		if (stack == NULL)
 +		{
 +			free(p);
 +			return FALSE;
 +		}
 +
 +		// Put four corners
 +		AddToStack(stack, &topPos, 0, 0);
 +		AddToStack(stack, &topPos, width/2, 0);
 +		AddToStack(stack, &topPos, width-1, 0);
 +		AddToStack(stack, &topPos, 0, height/2);
 +		AddToStack(stack, &topPos, width-1, height/2);
 +		AddToStack(stack, &topPos, 0, height-1);
 +		AddToStack(stack, &topPos, width/2, height-1);
 +		AddToStack(stack, &topPos, width-1, height-1);
 +
 +		while(curPos < topPos)
 +		{
 +			// Get pos
 +			x = stack[curPos]; curPos++;
 +			y = stack[curPos]; curPos++;
 +
 +			// Get pixel
 +			px1 = GET_PIXEL(p, x, y);
 +
 +			// It won't change the transparency if one exists
 +			// (This avoid an endless loop too)
 +			// Not using == 255 because some MSN bmps have 254 in some positions
 +			if (px1[3] >= 253)
 +			{
 +				if (ColorsAreTheSame(colorDiff, px1, (BYTE *) &colors[selectedColor]))
 +				{
 +					if (transpProportional)
 +					{
 +						px1[3] = min(252,
 +								(abs(px1[0] - colors[selectedColor][0])
 +								+ abs(px1[1] - colors[selectedColor][1])
 +								+ abs(px1[2] - colors[selectedColor][2])) / 3);
 +					}
 +					else
 +					{
 +						px1[3] = 0;
 +					}
 +
 +					// Add 4 neighbours
 +
 +					if (x + 1 < width)
 +						AddToStack(stack, &topPos, x + 1, y);
 +
 +					if (x - 1 >= 0)
 +						AddToStack(stack, &topPos, x - 1, y);
 +
 +					if (y + 1 < height)
 +						AddToStack(stack, &topPos, x, y + 1);
 +
 +					if (y - 1 >= 0)
 +						AddToStack(stack, &topPos, x, y - 1);
 +				}
 +			}
 +		}
 +
 +		free(stack);
 +	}
 +
 +	dwLen = SetBitmapBits(*hBitmap, dwLen, p);
 +	free(p);
 +
 +	return TRUE;
 +}
 +
 +/////////////////////////////////////////////////////////////////////////////////////////
 +// Other utils
 +
 +int SaveAvatar( const char* protocol, const TCHAR* tszFileName )
 +{
 +	int result = CallProtoService(protocol, PS_SETMYAVATART, 0, ( LPARAM )tszFileName);
 +	if ( result == CALLSERVICE_NOTFOUND ) {
 +		if ( tszFileName != NULL ) {
 +			char szFileName[ MAX_PATH ];
 +			WideCharToMultiByte( CP_ACP, 0, tszFileName, -1, szFileName, SIZEOF(szFileName), 0, 0 );
 +			result = CallProtoService(protocol, PS_SETMYAVATAR, 0, ( LPARAM )szFileName);
 +		}
 +		else result = CallProtoService(protocol, PS_SETMYAVATAR, 0, 0);
 +	}
 +	
 +	return result;
 +}
 diff --git a/plugins/AVS/src/image_utils.h b/plugins/AVS/src/image_utils.h new file mode 100644 index 0000000000..eadb2a3cfe --- /dev/null +++ b/plugins/AVS/src/image_utils.h @@ -0,0 +1,50 @@ +#ifndef __IMAGE_UTILS_H__
 +# define __IMAGE_UTILS_H__
 +
 +#define _WIN32_WINNT 0x0501
 +#include <windows.h>
 +
 +#include <m_avatars.h>
 +
 +
 +// Load an image
 +// wParam = NULL
 +// lParam = filename
 +INT_PTR BmpFilterLoadBitmap32(WPARAM wParam,LPARAM lParam);
 +
 +// Save an HBITMAP to an image
 +// wParam = HBITMAP
 +// lParam = full path of filename
 +INT_PTR BmpFilterSaveBitmap(WPARAM wParam,LPARAM lParam);
 +INT_PTR BmpFilterSaveBitmapW(WPARAM wParam,LPARAM lParam);
 +#define BmpFilterSaveBitmapT BmpFilterSaveBitmapW
 +
 +// Returns != 0 if can save that type of image, = 0 if cant
 +// wParam = 0
 +// lParam = PA_FORMAT_*   // image format
 +INT_PTR BmpFilterCanSaveBitmap(WPARAM wParam,LPARAM lParam);
 +
 +// Returns a copy of the bitmap with the size especified or the original bitmap if nothing has to be changed
 +// wParam = ResizeBitmap *
 +// lParam = NULL
 +INT_PTR BmpFilterResizeBitmap(WPARAM wParam,LPARAM lParam);
 +
 +
 +int BmpFilterSaveBitmap(HBITMAP hBmp, char *szFile, int flags);
 +int BmpFilterSaveBitmapW(HBITMAP hBmp, wchar_t *wszFile, int flags);
 +#define BmpFilterSaveBitmapT BmpFilterSaveBitmapW
 +
 +HBITMAP CopyBitmapTo32(HBITMAP hBitmap);
 +
 +BOOL PreMultiply(HBITMAP hBitmap);
 +BOOL MakeTransparentBkg(HANDLE hContact, HBITMAP *hBitmap);
 +HBITMAP MakeGrayscale(HANDLE hContact, HBITMAP hBitmap);
 +DWORD GetImgHash(HBITMAP hBitmap);
 +
 +int AVS_pathIsAbsolute(const TCHAR *path);
 +size_t AVS_pathToRelative(const TCHAR *sPrc, TCHAR *pOut);
 +size_t AVS_pathToAbsolute(const TCHAR *pSrc, TCHAR *pOut);
 +
 +int SaveAvatar( const char* protocol, const TCHAR* tszFileName );
 +
 +#endif // __IMAGE_UTILS_H__
 diff --git a/plugins/AVS/src/main.cpp b/plugins/AVS/src/main.cpp new file mode 100644 index 0000000000..eb690a3b9b --- /dev/null +++ b/plugins/AVS/src/main.cpp @@ -0,0 +1,2652 @@ +/*
 +
 +Miranda IM: the free IM client for Microsoft* Windows*
 +
 +Copyright 2000-2004 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.
 +*/
 +
 +#include "commonheaders.h"
 +#include "version.h"
 +
 +HINSTANCE g_hInst = 0;
 +int hLangpack;
 +
 +static TCHAR   g_szDataPath[MAX_PATH];		// user datae path (read at startup only)
 +static BOOL    g_MetaAvail = FALSE;
 +BOOL           g_AvatarHistoryAvail = FALSE;
 +static long    hwndSetMyAvatar = 0;
 +
 +static HANDLE  hMyAvatarsFolder = 0;
 +static HANDLE  hGlobalAvatarFolder = 0;
 +static HANDLE  hLoaderEvent = 0;
 +static HANDLE  hLoaderThread = 0;
 +
 +static HANDLE  hOptInit = 0;
 +static HANDLE  hModulesLoaded = 0;
 +static HANDLE  hPresutdown = 0;
 +static HANDLE  hOkToExit = 0;
 +static HANDLE  hAccChanged = 0;
 +
 +HANDLE hProtoAckHook = 0, hContactSettingChanged = 0, hEventChanged = 0, hEventContactAvatarChanged = 0,
 +		hMyAvatarChanged = 0, hEventDeleted = 0, hUserInfoInitHook = 0;
 +HICON	g_hIcon = 0;
 +
 +BOOL (WINAPI *AvsAlphaBlend)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION) = NULL;
 +
 +static struct CacheNode *g_Cache = 0;
 +static CRITICAL_SECTION cachecs, alloccs;
 +
 +static int ComparePicture( const protoPicCacheEntry* p1, const protoPicCacheEntry* p2 )
 +{
 +	if ((lstrcmpA(p1->szProtoname, "Global avatar") == 0) || strstr(p1->szProtoname, "Global avatar"))
 +		return -1;
 +	if ((lstrcmpA(p2->szProtoname, "Global avatar") == 0) || strstr(p1->szProtoname, "Global avatar"))
 +		return 1;
 +	return lstrcmpA( p1->szProtoname, p2->szProtoname );
 +}
 +
 +OBJLIST<protoPicCacheEntry>
 +	g_ProtoPictures( 10, ComparePicture ),
 +	g_MyAvatars( 10, ComparePicture );
 +
 +char* g_szMetaName = NULL;
 +
 +#ifndef SHVIEW_THUMBNAIL
 +#define SHVIEW_THUMBNAIL 0x702D
 +#endif
 +
 +// Stores the id of the dialog
 +
 +int			ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist = FALSE, int pa_format = 0);
 +static int	ShutdownProc(WPARAM wParam, LPARAM lParam);
 +static int	OkToExitProc(WPARAM wParam, LPARAM lParam);
 +static int	OnDetailsInit(WPARAM wParam, LPARAM lParam);
 +static int	GetFileHash(TCHAR* filename);
 +static DWORD GetFileSize(TCHAR *szFilename);
 +
 +void ProcessAvatarInfo(HANDLE hContact, int type, PROTO_AVATAR_INFORMATIONT *pai, const char *szProto);
 +int FetchAvatarFor(HANDLE hContact, char *szProto = NULL);
 +static INT_PTR ReportMyAvatarChanged(WPARAM wParam, LPARAM lParam);
 +
 +BOOL Proto_IsAvatarsEnabled(const char *proto);
 +BOOL Proto_IsAvatarFormatSupported(const char *proto, int format);
 +void Proto_GetAvatarMaxSize(const char *proto, int *width, int *height);
 +int Proto_AvatarImageProportion(const char *proto);
 +BOOL Proto_NeedDelaysForAvatars(const char *proto);
 +int Proto_GetAvatarMaxFileSize(const char *proto);
 +int Proto_GetDelayAfterFail(const char *proto);
 +
 +FI_INTERFACE *fei = 0;
 +
 +PLUGININFOEX pluginInfoEx = {
 +	sizeof(PLUGININFOEX),
 +	"Avatar service",
 +	__VERSION_DWORD,
 +	"Load and manage contact pictures for other plugins.",
 +	"Nightwish, Pescuma",
 +	"",
 +	"Copyright 2000-2012 Miranda-IM project",
 +	"http://www.miranda-im.org",
 +	UNICODE_AWARE,
 +// {E00F1643-263C-4599-B84B-053E5C511D29}
 +	{ 0xe00f1643, 0x263c, 0x4599, { 0xb8, 0x4b, 0x5, 0x3e, 0x5c, 0x51, 0x1d, 0x29 } }
 +};
 +
 +extern INT_PTR CALLBACK DlgProcOptionsAvatars(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
 +extern INT_PTR CALLBACK DlgProcOptionsProtos(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
 +extern INT_PTR CALLBACK DlgProcOptionsOwn(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
 +extern INT_PTR CALLBACK DlgProcAvatarOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
 +extern INT_PTR CALLBACK DlgProcAvatarUserInfo(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
 +extern INT_PTR CALLBACK DlgProcAvatarProtoInfo(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
 +
 +
 +static int SetProtoMyAvatar(char *protocol, HBITMAP hBmp, TCHAR *originalFilename, int format, BOOL square, BOOL grow);
 +
 +// See if a protocol service exists
 +int ProtoServiceExists(const char *szModule,const char *szService)
 +{
 +	char str[MAXMODULELABELLENGTH * 2];
 +	strcpy(str,szModule);
 +	strcat(str,szService);
 +	return ServiceExists(str);
 +}
 +
 +
 +/*
 + * output a notification message.
 + * may accept a hContact to include the contacts nickname in the notification message...
 + * the actual message is using printf() rules for formatting and passing the arguments...
 + *
 + * can display the message either as systray notification (baloon popup) or using the
 + * popup plugin.
 + */
 +
 +#ifdef _DEBUG
 +
 +int _DebugTrace(const char *fmt, ...)
 +{
 +	char	debug[2048];
 +	int		ibsize = 2047;
 +	va_list va;
 +	va_start(va, fmt);
 +
 +	mir_snprintf(debug, SIZEOF(debug) - 10, " ***** AVS [%08d] [ID:%04x]: ", GetTickCount(), GetCurrentThreadId());
 +	OutputDebugStringA(debug);
 +	_vsnprintf(debug, ibsize, fmt, va);
 +	OutputDebugStringA(debug);
 +	OutputDebugStringA(" ***** \n");
 +
 +	return 0;
 +}
 +
 +int _DebugTrace(HANDLE hContact, const char *fmt, ...)
 +{
 +	char text[1024];
 +	size_t len;
 +	va_list va;
 +
 +	char *name = NULL;
 +	char *proto = NULL;
 +	if (hContact != NULL)
 +	{
 +		name = (char*) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0);
 +		proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +	}
 +
 +	mir_snprintf(text, SIZEOF(text) - 10, " ***** AVS [%08d] [ID:%04x]: [%08d - %s - %s] ",
 +		GetTickCount(), GetCurrentThreadId(), hContact, proto == NULL ? "" : proto, name == NULL ? "" : name);
 +	len = strlen(text);
 +
 +	va_start(va, fmt);
 +	mir_vsnprintf(&text[len], SIZEOF(text) - len, fmt, va);
 +	va_end(va);
 +
 +	OutputDebugStringA(text);
 +	OutputDebugStringA(" ***** \n");
 +
 +	return 0;
 +}
 +
 +#endif
 +
 +/*
 + * path utilities (make avatar paths relative to *PROFILE* directory, not miranda directory.
 + * taken and modified from core services
 + */
 +
 +int AVS_pathIsAbsolute(const TCHAR *path)
 +{
 +	if (!path || !(lstrlen(path) > 2))
 +		return 0;
 +	if ((path[1]==':'&&path[2]=='\\')||(path[0]=='\\'&&path[1]=='\\')) return 1;
 +	return 0;
 +}
 +
 +size_t AVS_pathToRelative(const TCHAR *pSrc, TCHAR *pOut)
 +{
 +	if (!pSrc || !*pSrc || _tcslen(pSrc) > MAX_PATH) return 0;
 +	if (!AVS_pathIsAbsolute( pSrc ))
 +		lstrcpyn(pOut, pSrc, MAX_PATH);
 +	else {
 +		TCHAR szTmp[MAX_PATH];
 +		mir_sntprintf(szTmp, SIZEOF(szTmp), _T("%s"), pSrc);
 +		_tcslwr(szTmp);
 +		if (_tcsstr(szTmp, g_szDataPath))
 +			lstrcpyn(pOut, pSrc + _tcslen(g_szDataPath) + 1, MAX_PATH);
 +		else
 +			lstrcpyn(pOut, pSrc, MAX_PATH);
 +	}
 +	return _tcslen(pOut);
 +}
 +
 +size_t AVS_pathToAbsolute(const TCHAR *pSrc, TCHAR *pOut)
 +{
 +	if (!pSrc || !lstrlen(pSrc) || lstrlen(pSrc) > MAX_PATH)
 +		return 0;
 +
 +	if (AVS_pathIsAbsolute(pSrc) || !_istalnum(pSrc[0]))
 +		lstrcpyn(pOut, pSrc, MAX_PATH);
 +	else
 +		mir_sntprintf(pOut, MAX_PATH, _T("%s\\%s"), g_szDataPath, pSrc, MAX_PATH);
 +	return lstrlen(pOut);
 +}
 +
 +static void NotifyMetaAware(HANDLE hContact, struct CacheNode *node = NULL, AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)-1)
 +{
 +	if (ace == (AVATARCACHEENTRY *)-1)
 +		ace = &node->ace;
 +
 +	NotifyEventHooks(hEventChanged, (WPARAM)hContact, (LPARAM)ace);
 +
 +	if (g_MetaAvail && (node->dwFlags & MC_ISSUBCONTACT) && DBGetContactSettingByte(NULL, g_szMetaName, "Enabled", 0)) {
 +		HANDLE hMasterContact = (HANDLE)DBGetContactSettingDword(hContact, g_szMetaName, "Handle", 0);
 +
 +		if (hMasterContact && (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hMasterContact, 0) == hContact &&
 +			!DBGetContactSettingByte(hMasterContact, "ContactPhoto", "Locked", 0))
 +			NotifyEventHooks(hEventChanged, (WPARAM)hMasterContact, (LPARAM)ace);
 +	}
 +	if (node->dwFlags & AVH_MUSTNOTIFY) {
 +		// Fire the event for avatar history
 +		node->dwFlags &= ~AVH_MUSTNOTIFY;
 +		if (node->ace.szFilename[0] != '\0') {
 +			CONTACTAVATARCHANGEDNOTIFICATION cacn = {0};
 +			cacn.cbSize = sizeof(CONTACTAVATARCHANGEDNOTIFICATION);
 +			cacn.hContact = hContact;
 +			cacn.format = node->pa_format;
 +			_tcsncpy(cacn.filename, node->ace.szFilename, MAX_PATH);
 +			cacn.filename[MAX_PATH - 1] = 0;
 +
 +			// Get hash
 +			char *szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +			if (szProto != NULL) {
 +				DBVARIANT dbv = {0};
 +				if (!DBGetContactSetting(hContact, szProto, "AvatarHash", &dbv)) {
 +					if (dbv.type == DBVT_TCHAR) {
 +						_tcsncpy(cacn.hash, dbv.ptszVal, SIZEOF(cacn.hash));
 +					} else if (dbv.type == DBVT_BLOB) {
 +						// Lets use base64 encode
 +						char *tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 +						int i;
 +						for(i = 0; i < dbv.cpbVal / 3 && i*4+3 < sizeof(cacn.hash)-1; i++) {
 +							BYTE a = dbv.pbVal[i*3];
 +							BYTE b = i*3 + 1 < dbv.cpbVal ? dbv.pbVal[i*3 + 1] : 0;
 +							BYTE c = i*3 + 2 < dbv.cpbVal ? dbv.pbVal[i*3 + 2] : 0;
 +
 +							cacn.hash[i*4] = tab[(a & 0xFC) >> 2];
 +							cacn.hash[i*4+1] = tab[((a & 0x3) << 4) + ((b & 0xF0) >> 4)];
 +							cacn.hash[i*4+2] = tab[((b & 0xF) << 2) + ((c & 0xC0) >> 6)];
 +							cacn.hash[i*4+3] = tab[c & 0x3F];
 +						}
 +						if (dbv.cpbVal % 3 != 0 && i*4+3 < sizeof(cacn.hash)-1) {
 +							BYTE a = dbv.pbVal[i*3];
 +							BYTE b = i*3 + 1 < dbv.cpbVal ? dbv.pbVal[i*3 + 1] : 0;
 +
 +							cacn.hash[i*4] = tab[(a & 0xFC) >> 2];
 +							cacn.hash[i*4+1] = tab[((a & 0x3) << 4) + ((b & 0xF0) >> 4)];
 +							if (i + 1 < dbv.cpbVal)
 +								cacn.hash[i*4+2] = tab[((b & 0xF) << 4)];
 +							else
 +								cacn.hash[i*4+2] = '=';
 +							cacn.hash[i*4+3] = '=';
 +						}
 +					}
 +					DBFreeVariant(&dbv);
 +				}
 +			}
 +
 +			// Default value
 +			if (cacn.hash[0] == '\0')
 +				mir_sntprintf(cacn.hash, SIZEOF(cacn.hash), _T("AVS-HASH-%x"), GetFileHash(cacn.filename));
 +
 +			NotifyEventHooks(hEventContactAvatarChanged, (WPARAM)cacn.hContact, (LPARAM)&cacn);
 +		}
 +		else
 +			NotifyEventHooks(hEventContactAvatarChanged, (WPARAM)hContact, NULL);
 +	}
 +}
 +
 +static int g_maxBlock = 0, g_curBlock = 0;
 +static struct CacheNode **g_cacheBlocks = NULL;
 +
 +/*
 + * allocate a cache block and add it to the list of blocks
 + * does not link the new block with the old block(s) - caller needs to do this
 + */
 +
 +static struct CacheNode *AllocCacheBlock()
 +{
 +	struct CacheNode *allocedBlock = NULL;
 +
 +	allocedBlock = (struct CacheNode *)malloc(CACHE_BLOCKSIZE * sizeof(struct CacheNode));
 +	ZeroMemory((void *)allocedBlock, sizeof(struct CacheNode) * CACHE_BLOCKSIZE);
 +
 +	for(int i = 0; i < CACHE_BLOCKSIZE - 1; i++)
 +	{
 +		//InitializeCriticalSection(&allocedBlock[i].cs);
 +		allocedBlock[i].pNextNode = &allocedBlock[i + 1];				// pre-link the alloced block
 +	}
 +	//InitializeCriticalSection(&allocedBlock[CACHE_BLOCKSIZE - 1].cs);
 +
 +	if (g_Cache == NULL)													// first time only...
 +		g_Cache = allocedBlock;
 +
 +	// add it to the list of blocks
 +
 +	if (g_curBlock == g_maxBlock) {
 +		g_maxBlock += 10;
 +		g_cacheBlocks = (struct CacheNode **)realloc(g_cacheBlocks, g_maxBlock * sizeof(struct CacheNode *));
 +	}
 +	g_cacheBlocks[g_curBlock++] = allocedBlock;
 +
 +	return(allocedBlock);
 +}
 +
 +int SetAvatarAttribute(HANDLE hContact, DWORD attrib, int mode)
 +{
 +	struct CacheNode *cacheNode = g_Cache;
 +
 +	while(cacheNode) {
 +		if (cacheNode->ace.hContact == hContact) {
 +			DWORD dwFlags = cacheNode->ace.dwFlags;
 +
 +			cacheNode->ace.dwFlags = mode ? cacheNode->ace.dwFlags | attrib : cacheNode->ace.dwFlags & ~attrib;
 +			if (cacheNode->ace.dwFlags != dwFlags)
 +				NotifyMetaAware(hContact, cacheNode);
 +			break;
 +		}
 +		cacheNode = cacheNode->pNextNode;
 +	}
 +	return 0;
 +}
 +
 +/*
 + * convert the avatar image path to a relative one...
 + * given: contact handle, path to image
 + */
 +void MakePathRelative(HANDLE hContact, TCHAR *path)
 +{
 +	TCHAR szFinalPath[MAX_PATH];
 +	szFinalPath[0] = '\0';
 +
 +	size_t result = AVS_pathToRelative(path, szFinalPath);
 +	if (result && lstrlen(szFinalPath) > 0) {
 +		DBWriteContactSettingTString(hContact, "ContactPhoto", "RFile", szFinalPath);
 +		if (!DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0))
 +			DBWriteContactSettingTString(hContact, "ContactPhoto", "Backup", szFinalPath);
 +	}
 +}
 +
 +/*
 + * convert the avatar image path to a relative one...
 + * given: contact handle
 + */
 +
 +static void MakePathRelative(HANDLE hContact)
 +{
 +	DBVARIANT dbv = {0};
 +
 +	if (!DBGetContactSetting(hContact, "ContactPhoto", "File", &dbv)) {
 +		if (dbv.type == DBVT_TCHAR) {
 +			MakePathRelative(hContact, dbv.ptszVal);
 +		}
 +		DBFreeVariant(&dbv);
 +	}
 +}
 +
 +static void ResetTranspSettings(HANDLE hContact)
 +{
 +	DBDeleteContactSetting(hContact, "ContactPhoto", "MakeTransparentBkg");
 +	DBDeleteContactSetting(hContact, "ContactPhoto", "TranspBkgNumPoints");
 +	DBDeleteContactSetting(hContact, "ContactPhoto", "TranspBkgColorDiff");
 +}
 +
 +static TCHAR *getJGMailID(char *szProto)
 +{
 +	static TCHAR szJID[MAX_PATH+1];
 +	DBVARIANT dbva={0}, dbvb={0};
 +
 +	szJID[0] = '\0';
 +	if (DBGetContactSettingTString(NULL, szProto, "LoginName", &dbva))
 +		return szJID;
 +	if (DBGetContactSettingTString(NULL, szProto, "LoginServer", &dbvb)) {
 +		DBFreeVariant(&dbva);
 +		return szJID;
 +	}
 +
 +	mir_sntprintf(szJID, SIZEOF(szJID), _T("%s@%s"), dbva.ptszVal, dbvb.ptszVal);
 +	DBFreeVariant(&dbva);
 +	DBFreeVariant(&dbvb);
 +	return szJID;
 +}
 +
 +// create the avatar in cache
 +// returns 0 if not created (no avatar), iIndex otherwise, -2 if has to request avatar, -3 if avatar too big
 +int CreateAvatarInCache(HANDLE hContact, avatarCacheEntry *ace, char *szProto)
 +{
 +	DBVARIANT dbv = {0};
 +	char *szExt = NULL;
 +	TCHAR tszFilename[MAX_PATH];
 +	HANDLE hFile = INVALID_HANDLE_VALUE;
 +	DWORD dwFileSizeHigh = 0, dwFileSize = 0, sizeLimit = 0;
 +
 +	tszFilename[0] = 0;
 +
 +	ace->hbmPic = 0;
 +	ace->dwFlags = 0;
 +	ace->bmHeight = 0;
 +	ace->bmWidth = 0;
 +	ace->lpDIBSection = NULL;
 +	ace->szFilename[0] = 0;
 +
 +	if (szProto == NULL) {
 +		char *proto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +		if (proto == NULL || !DBGetContactSettingByte(NULL, AVS_MODULE, proto, 1)) {
 +			return -1;
 +		}
 +
 +		if (DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0)
 +			&& !DBGetContactSettingTString(hContact, "ContactPhoto", "Backup", &dbv)) {
 +				AVS_pathToAbsolute(dbv.ptszVal, tszFilename);
 +				DBFreeVariant(&dbv);
 +		}
 +		else if (!DBGetContactSettingTString(hContact, "ContactPhoto", "RFile", &dbv)) {
 +			AVS_pathToAbsolute(dbv.ptszVal, tszFilename);
 +			DBFreeVariant(&dbv);
 +		}
 +		else if (!DBGetContactSettingTString(hContact, "ContactPhoto", "File", &dbv)) {
 +			AVS_pathToAbsolute(dbv.ptszVal, tszFilename);
 +			DBFreeVariant(&dbv);
 +		}
 +		else {
 +			return -2;
 +		}
 +	}
 +	else {
 +		if (hContact == 0) {				// create a protocol picture in the proto picture cache
 +			if (!DBGetContactSettingTString(NULL, PPICT_MODULE, szProto, &dbv)) {
 +				AVS_pathToAbsolute(dbv.ptszVal, tszFilename);
 +				DBFreeVariant(&dbv);
 +			}
 +			else {
 +				if (lstrcmpA(szProto, AVS_DEFAULT)) {
 +					if (!DBGetContactSettingTString(NULL, PPICT_MODULE, AVS_DEFAULT, &dbv)) {
 +						AVS_pathToAbsolute(dbv.ptszVal, tszFilename);
 +						DBFreeVariant(&dbv);
 +					}
 +
 +					if (!strstr(szProto, "Global avatar for")) {
 +						PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)szProto);
 +						if (pdescr == NULL)
 +							return -1;
 +						char key[MAX_PATH];
 +						mir_snprintf(key, SIZEOF(key), "Global avatar for %s accounts", pdescr->szProtoName);
 +						if (!DBGetContactSettingTString(NULL, PPICT_MODULE, key, &dbv)) {
 +							AVS_pathToAbsolute(dbv.ptszVal, tszFilename);
 +							DBFreeVariant(&dbv);
 +						}
 +					}
 +				}
 +			}
 +		}
 +		else if (hContact == (HANDLE)-1) {	// create own picture - note, own avatars are not on demand, they are loaded once at
 +			// startup and everytime they are changed.
 +			if (szProto[0] == '\0') {
 +				// Global avatar
 +				if ( DBGetContactSettingTString(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv))
 +					return -10;
 +
 +				AVS_pathToAbsolute(dbv.ptszVal, tszFilename);
 +				DBFreeVariant(&dbv);
 +			}
 +			else if (ProtoServiceExists(szProto, PS_GETMYAVATART)) {
 +				if (CallProtoService(szProto, PS_GETMYAVATART, (WPARAM)tszFilename, (LPARAM)MAX_PATH))
 +					tszFilename[0] = '\0';
 +			}
 +			else if (ProtoServiceExists(szProto, PS_GETMYAVATAR)) {
 +				char szFileName[ MAX_PATH ];
 +				if (CallProtoService(szProto, PS_GETMYAVATAR, (WPARAM)szFileName, (LPARAM)MAX_PATH))
 +					tszFilename[0] = '\0';
 +				else
 +					MultiByteToWideChar( CP_ACP, 0, szFileName, -1, tszFilename, SIZEOF( tszFilename ));
 +			}
 +			else if (!DBGetContactSettingTString(NULL, szProto, "AvatarFile", &dbv)) {
 +				AVS_pathToAbsolute(dbv.ptszVal, tszFilename);
 +				DBFreeVariant(&dbv);
 +			}
 +			else return -1;
 +		}
 +	}
 +
 +	if ( lstrlen(tszFilename) < 4 )
 +		return -1;
 +
 +	TCHAR* tmpPath = Utils_ReplaceVarsT(tszFilename);
 +	mir_sntprintf(tszFilename, SIZEOF(tszFilename), _T("%s"), tmpPath);
 +	mir_free(tmpPath);
 +
 +	if ((hFile = CreateFile(tszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
 +		return -2;
 +
 +	CloseHandle(hFile);
 +	WPARAM isTransparentImage = 0;
 +
 +	ace->hbmPic = (HBITMAP) BmpFilterLoadBitmap32((WPARAM)&isTransparentImage, (LPARAM)tszFilename);
 +	ace->dwFlags = 0;
 +	ace->bmHeight = 0;
 +	ace->bmWidth = 0;
 +	ace->lpDIBSection = NULL;
 +	_tcsncpy(ace->szFilename, tszFilename, MAX_PATH);
 +	ace->szFilename[MAX_PATH - 1] = 0;
 +	if (ace->hbmPic != 0) {
 +		BITMAP bminfo;
 +
 +		GetObject(ace->hbmPic, sizeof(bminfo), &bminfo);
 +
 +		ace->cbSize = sizeof(avatarCacheEntry);
 +		ace->dwFlags = AVS_BITMAP_VALID;
 +		if (hContact != NULL && DBGetContactSettingByte(hContact, "ContactPhoto", "Hidden", 0))
 +			ace->dwFlags |= AVS_HIDEONCLIST;
 +		ace->hContact = hContact;
 +		ace->bmHeight = bminfo.bmHeight;
 +		ace->bmWidth = bminfo.bmWidth;
 +
 +		BOOL noTransparency = DBGetContactSettingByte(0, AVS_MODULE, "RemoveAllTransparency", 0);
 +
 +		// Calc image hash
 +		if (hContact != 0 && hContact != (HANDLE)-1)
 +		{
 +			// Have to reset settings? -> do it if image changed
 +			DWORD imgHash = GetImgHash(ace->hbmPic);
 +
 +			if (imgHash != DBGetContactSettingDword(hContact, "ContactPhoto", "ImageHash", 0))
 +			{
 +				ResetTranspSettings(hContact);
 +				DBWriteContactSettingDword(hContact, "ContactPhoto", "ImageHash", imgHash);
 +			}
 +
 +			// Make transparent?
 +			if (!noTransparency && !isTransparentImage
 +				&& DBGetContactSettingByte(hContact, "ContactPhoto", "MakeTransparentBkg",
 +				DBGetContactSettingByte(0, AVS_MODULE, "MakeTransparentBkg", 0)))
 +			{
 +				if (MakeTransparentBkg(hContact, &ace->hbmPic))
 +				{
 +					ace->dwFlags |= AVS_CUSTOMTRANSPBKG | AVS_HASTRANSPARENCY;
 +					GetObject(ace->hbmPic, sizeof(bminfo), &bminfo);
 +					isTransparentImage = TRUE;
 +				}
 +			}
 +		}
 +		else if (hContact == (HANDLE)-1) // My avatars
 +		{
 +			if (!noTransparency && !isTransparentImage
 +				&& DBGetContactSettingByte(0, AVS_MODULE, "MakeTransparentBkg", 0)
 +				&& DBGetContactSettingByte(0, AVS_MODULE, "MakeMyAvatarsTransparent", 0))
 +			{
 +				if (MakeTransparentBkg(0, &ace->hbmPic))
 +				{
 +					ace->dwFlags |= AVS_CUSTOMTRANSPBKG | AVS_HASTRANSPARENCY;
 +					GetObject(ace->hbmPic, sizeof(bminfo), &bminfo);
 +					isTransparentImage = TRUE;
 +				}
 +			}
 +		}
 +
 +		if (DBGetContactSettingByte(0, AVS_MODULE, "MakeGrayscale", 0))
 +		{
 +			ace->hbmPic = MakeGrayscale(hContact, ace->hbmPic);
 +		}
 +
 +		if (noTransparency)
 +		{
 +			fei->FI_CorrectBitmap32Alpha(ace->hbmPic, TRUE);
 +			isTransparentImage = FALSE;
 +		}
 +
 +		if (bminfo.bmBitsPixel == 32 && isTransparentImage)
 +		{
 +			if (fei->FI_Premultiply(ace->hbmPic))
 +			{
 +				ace->dwFlags |= AVS_HASTRANSPARENCY;
 +			}
 +			ace->dwFlags |= AVS_PREMULTIPLIED;
 +		}
 +
 +		if (szProto)
 +		{
 +			protoPicCacheEntry *pAce = (protoPicCacheEntry *)ace;
 +			if (hContact == 0)
 +				pAce->dwFlags |= AVS_PROTOPIC;
 +			else if (hContact == (HANDLE)-1)
 +				pAce->dwFlags |= AVS_OWNAVATAR;
 +		}
 +
 +		return 1;
 +	}
 +	return -1;
 +}
 +
 +/*
 + * link a new cache block with the already existing chain of blocks
 + */
 +
 +static struct CacheNode *AddToList(struct CacheNode *node) {
 +	struct CacheNode *pCurrent = g_Cache;
 +
 +	while(pCurrent->pNextNode != 0)
 +		pCurrent = pCurrent->pNextNode;
 +
 +	pCurrent->pNextNode = node;
 +	return pCurrent;
 +}
 +
 +struct CacheNode *FindAvatarInCache(HANDLE hContact, BOOL add, BOOL findAny = FALSE)
 +{
 +	struct CacheNode *cacheNode = g_Cache, *foundNode = NULL;
 +
 +	char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +	if (szProto == NULL || !DBGetContactSettingByte(NULL, AVS_MODULE, szProto, 1))
 +		return NULL;
 +
 +	EnterCriticalSection(&cachecs);
 +
 +	while(cacheNode)
 +	{
 +		if (cacheNode->ace.hContact == hContact)
 +		{
 +			cacheNode->ace.t_lastAccess = time(NULL);
 +			foundNode = cacheNode->loaded || findAny ? cacheNode : NULL;
 +			LeaveCriticalSection(&cachecs);
 +			return foundNode;
 +		}
 +		if (foundNode == NULL && cacheNode->ace.hContact == 0)
 +			foundNode = cacheNode;				// found an empty and usable node
 +
 +		cacheNode = cacheNode->pNextNode;
 +	}
 +
 +	// not found
 +
 +	if (add)
 +	{
 +		if (foundNode == NULL) {					// no free entry found, create a new and append it to the list
 +			EnterCriticalSection(&alloccs);     // protect memory block allocation
 +			struct CacheNode *newNode = AllocCacheBlock();
 +			AddToList(newNode);
 +			foundNode = newNode;
 +			LeaveCriticalSection(&alloccs);
 +		}
 +
 +		foundNode->ace.hContact = hContact;
 +		if (g_MetaAvail)
 +			foundNode->dwFlags |= (DBGetContactSettingByte(hContact, g_szMetaName, "IsSubcontact", 0) ? MC_ISSUBCONTACT : 0);
 +		foundNode->loaded = FALSE;
 +		foundNode->mustLoad = 1;                                 // pic loader will watch this and load images
 +		LeaveCriticalSection(&cachecs);
 +		SetEvent(hLoaderEvent);                                     // wake him up
 +		return NULL;
 +	}
 +	else
 +	{
 +		foundNode = NULL;
 +	}
 +	LeaveCriticalSection(&cachecs);
 +	return foundNode;
 +}
 +
 +#define POLYNOMIAL (0x488781ED) /* This is the CRC Poly */
 +#define TOPBIT (1 << (WIDTH - 1)) /* MSB */
 +#define WIDTH 32
 +
 +static int GetFileHash(TCHAR* filename)
 +{
 +	HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
 +	if (hFile == INVALID_HANDLE_VALUE)
 +		return 0;
 +
 +	int remainder = 0;
 +	char data[1024];
 +	DWORD dwRead;
 +	do
 +	{
 +		// Read file chunk
 +		dwRead = 0;
 +		ReadFile(hFile, data, 1024, &dwRead, NULL);
 +
 +		/* loop through each byte of data */
 +		for (int byte = 0; byte < (int) dwRead; ++byte) {
 +			/* store the next byte into the remainder */
 +			remainder ^= (data[byte] << (WIDTH - 8));
 +			/* calculate for all 8 bits in the byte */
 +			for (int bit = 8; bit > 0; --bit) {
 +				/* check if MSB of remainder is a one */
 +				if (remainder & TOPBIT)
 +					remainder = (remainder << 1) ^ POLYNOMIAL;
 +				else
 +					remainder = (remainder << 1);
 +			}
 +		}
 +	} while(dwRead == 1024);
 +
 +	CloseHandle(hFile);
 +
 +	return remainder;
 +}
 +
 +static int ProtocolAck(WPARAM wParam, LPARAM lParam)
 +{
 +	ACKDATA *ack = (ACKDATA *) lParam;
 +
 +	if (ack != NULL && ack->type == ACKTYPE_AVATAR && ack->hContact != 0
 +		// Ignore metacontacts
 +		&& (!g_MetaAvail || strcmp(ack->szModule, g_szMetaName)))
 +	{
 +		if (ack->result == ACKRESULT_SUCCESS)
 +		{
 +			if (ack->hProcess == NULL)
 +				ProcessAvatarInfo(ack->hContact, GAIR_NOAVATAR, NULL, ack->szModule);
 +			else
 +				ProcessAvatarInfo(ack->hContact, GAIR_SUCCESS, (PROTO_AVATAR_INFORMATIONT *) ack->hProcess, ack->szModule);
 +		}
 +		else if (ack->result == ACKRESULT_FAILED)
 +		{
 +			ProcessAvatarInfo(ack->hContact, GAIR_FAILED, (PROTO_AVATAR_INFORMATIONT *) ack->hProcess, ack->szModule);
 +		}
 +		else if (ack->result == ACKRESULT_STATUS)
 +		{
 +			char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ack->hContact, 0);
 +			if (szProto == NULL || Proto_NeedDelaysForAvatars(szProto))
 +			{
 +				// Queue
 +				DBWriteContactSettingByte(ack->hContact, "ContactPhoto", "NeedUpdate", 1);
 +				QueueAdd(ack->hContact);
 +			}
 +			else
 +			{
 +				// Fetch it now
 +				FetchAvatarFor(ack->hContact, szProto);
 +			}
 +		}
 +	}
 +	return 0;
 +}
 +
 +INT_PTR ProtectAvatar(WPARAM wParam, LPARAM lParam)
 +{
 +	HANDLE hContact = (HANDLE)wParam;
 +	BYTE was_locked = DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0);
 +
 +	if (fei == NULL || was_locked == (BYTE)lParam)      // no need for redundant lockings...
 +		return 0;
 +
 +	if (hContact) {
 +		if (!was_locked)
 +			MakePathRelative(hContact);
 +		DBWriteContactSettingByte(hContact, "ContactPhoto", "Locked", lParam ? 1 : 0);
 +		if (lParam == 0)
 +			MakePathRelative(hContact);
 +		ChangeAvatar(hContact, TRUE);
 +	}
 +	return 0;
 +}
 +
 +/*
 + * for subclassing the open file dialog...
 + */
 +struct OpenFileSubclassData {
 +	BYTE *locking_request;
 +	BYTE setView;
 +};
 +
 +static BOOL CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 +{
 +	switch(msg) {
 +	case WM_INITDIALOG:
 +		{
 +			OPENFILENAME *ofn = (OPENFILENAME *)lParam;
 +
 +			OpenFileSubclassData *data = (OpenFileSubclassData *) malloc(sizeof(OpenFileSubclassData));
 +			SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)data);
 +			data->locking_request = (BYTE *)ofn->lCustData;
 +			data->setView = TRUE;
 +
 +			TranslateDialogDefault(hwnd);
 +			CheckDlgButton(hwnd, IDC_PROTECTAVATAR, *(data->locking_request));
 +			break;
 +		}
 +
 +	case WM_COMMAND:
 +		if (LOWORD(wParam) == IDC_PROTECTAVATAR)
 +		{
 +			OpenFileSubclassData *data= (OpenFileSubclassData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
 +			*(data->locking_request) = IsDlgButtonChecked(hwnd, IDC_PROTECTAVATAR) ? TRUE : FALSE;
 +		}
 +		break;
 +
 +	case WM_NOTIFY:
 +		{
 +			OpenFileSubclassData *data= (OpenFileSubclassData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
 +			if (data->setView)
 +			{
 +				HWND hwndParent = GetParent(hwnd);
 +				HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ;
 +				if (hwndLv != NULL)
 +				{
 +					SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0);
 +					data->setView = FALSE;
 +				}
 +			}
 +		}
 +		break;
 +
 +	case WM_NCDESTROY:
 +		OpenFileSubclassData *data= (OpenFileSubclassData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
 +		free((OpenFileSubclassData *)data);
 +		SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)0);
 +		break;
 +	}
 +
 +	return FALSE;
 +}
 +
 +/*
 + * set an avatar (service function)
 + * if lParam == NULL, a open file dialog will be opened, otherwise, lParam is taken as a FULL
 + * image filename (will be checked for existance, though)
 + */
 +
 +INT_PTR avSetAvatar(HANDLE hContact, TCHAR* tszPath)
 +{
 +	BYTE is_locked = 0;
 +	TCHAR FileName[MAX_PATH], szBackupName[MAX_PATH];
 +	TCHAR *szFinalName = NULL;
 +	HANDLE hFile = 0;
 +	BYTE locking_request;
 +
 +	if (hContact == NULL || fei == NULL)
 +		return 0;
 +
 +	is_locked = DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0);
 +
 +	if ( tszPath == NULL ) {
 +		OPENFILENAME ofn = {0};
 +		TCHAR filter[256];
 +
 +		filter[0] = '\0';
 +		CallService(MS_UTILS_GETBITMAPFILTERSTRINGST, SIZEOF(filter), ( LPARAM )filter);
 +
 +		if (IsWinVer2000Plus())
 +			ofn.lStructSize = sizeof(ofn);
 +		else
 +			ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
 +		ofn.hwndOwner = 0;
 +		ofn.lpstrFile = FileName;
 +		ofn.lpstrFilter = filter;
 +		ofn.nMaxFile = MAX_PATH;
 +		ofn.nMaxFileTitle = MAX_PATH;
 +		ofn.Flags = OFN_FILEMUSTEXIST | OFN_ENABLETEMPLATE | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
 +		ofn.lpstrInitialDir = _T(".");
 +		*FileName = '\0';
 +		ofn.lpstrDefExt = _T("");
 +		ofn.hInstance = g_hInst;
 +		ofn.lpTemplateName = MAKEINTRESOURCE(IDD_OPENSUBCLASS);
 +		ofn.lpfnHook = (LPOFNHOOKPROC)OpenFileSubclass;
 +		locking_request = is_locked;
 +		ofn.lCustData = (LPARAM)&locking_request;
 +		if (GetOpenFileName(&ofn)) {
 +			szFinalName = FileName;
 +			is_locked = locking_request ? 1 : is_locked;
 +		}
 +		else
 +			return 0;
 +	}
 +	else
 +		szFinalName = tszPath;
 +
 +	/*
 +	* filename is now set, check it and perform all needed action
 +	*/
 +
 +	if ((hFile = CreateFile(szFinalName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
 +		return 0;
 +
 +	// file exists...
 +
 +	CloseHandle(hFile);
 +
 +	AVS_pathToRelative(szFinalName, szBackupName);
 +	DBWriteContactSettingTString(hContact, "ContactPhoto", "Backup", szBackupName);
 +
 +	DBWriteContactSettingByte(hContact, "ContactPhoto", "Locked", is_locked);
 +	DBWriteContactSettingTString(hContact, "ContactPhoto", "File", szFinalName);
 +	MakePathRelative(hContact, szFinalName);
 +	// Fix cache
 +	ChangeAvatar(hContact, TRUE);
 +
 +	return 0;
 +}
 +
 +INT_PTR SetAvatar(WPARAM wParam, LPARAM lParam)
 +{	return avSetAvatar(( HANDLE )wParam, A2T(( const char* )lParam ));
 +}
 +
 +INT_PTR SetAvatarW(WPARAM wParam, LPARAM lParam)
 +{	return avSetAvatar(( HANDLE )wParam, ( TCHAR* )lParam );
 +}
 +
 +/*
 + * see if is possible to set the avatar for the expecified protocol
 + */
 +static INT_PTR CanSetMyAvatar(WPARAM wParam, LPARAM lParam)
 +{
 +	char *protocol = (char *) wParam;
 +	if (protocol == NULL || fei == NULL)
 +		return 0;
 +
 +	return ProtoServiceExists(protocol, PS_SETMYAVATAR);
 +}
 +
 +struct SetMyAvatarHookData {
 +	char *protocol;
 +	BOOL square;
 +	BOOL grow;
 +
 +	BOOL thumbnail;
 +};
 +
 +
 +/*
 + * Callback to set thumbnaill view to open dialog
 + */
 +static UINT_PTR CALLBACK SetMyAvatarHookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 +{
 +	switch(msg) {
 +	case WM_INITDIALOG:
 +		{
 +			InterlockedExchange(&hwndSetMyAvatar, (LONG) hwnd);
 +
 +			SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)lParam);
 +			OPENFILENAME *ofn = (OPENFILENAME *)lParam;
 +			SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData;
 +			data->thumbnail = TRUE;
 +
 +			SetWindowText(GetDlgItem(hwnd, IDC_MAKE_SQUARE), TranslateT("Make the avatar square"));
 +			SetWindowText(GetDlgItem(hwnd, IDC_GROW), TranslateT("Grow avatar to fit max allowed protocol size"));
 +
 +			CheckDlgButton(hwnd, IDC_MAKE_SQUARE, data->square ? BST_CHECKED : BST_UNCHECKED);
 +			CheckDlgButton(hwnd, IDC_GROW, data->grow ? BST_CHECKED : BST_UNCHECKED);
 +
 +			if (data->protocol != NULL && (Proto_AvatarImageProportion(data->protocol) & PIP_SQUARE))
 +				EnableWindow(GetDlgItem(hwnd, IDC_MAKE_SQUARE), FALSE);
 +		}
 +		break;
 +
 +	case WM_NOTIFY:
 +		{
 +			OPENFILENAME *ofn = (OPENFILENAME *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
 +			SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData;
 +			if (data->thumbnail)
 +			{
 +				HWND hwndParent = GetParent(hwnd);
 +				HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ;
 +				if (hwndLv != NULL)
 +				{
 +					SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0);
 +					data->thumbnail = FALSE;
 +				}
 +			}
 +			break;
 +		}
 +	case WM_DESTROY:
 +		{
 +			OPENFILENAME *ofn = (OPENFILENAME *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
 +			SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData;
 +			data->square = IsDlgButtonChecked(hwnd, IDC_MAKE_SQUARE);
 +			data->grow = IsDlgButtonChecked(hwnd, IDC_GROW);
 +
 +			InterlockedExchange(&hwndSetMyAvatar, 0);
 +			break;
 +		}
 +	}
 +
 +	return 0;
 +}
 +
 +const TCHAR *GetFormatExtension(int format)
 +{
 +	if (format == PA_FORMAT_PNG)
 +		return _T(".png");
 +	if (format == PA_FORMAT_JPEG)
 +		return _T(".jpg");
 +	if (format == PA_FORMAT_ICON)
 +		return _T(".ico");
 +	if (format == PA_FORMAT_BMP)
 +		return _T(".bmp");
 +	if (format == PA_FORMAT_GIF)
 +		return _T(".gif");
 +	if (format == PA_FORMAT_SWF)
 +		return _T(".swf");
 +	if (format == PA_FORMAT_XML)
 +		return _T(".xml");
 +
 +	return NULL;
 +}
 +
 +int GetImageFormat(TCHAR *filename)
 +{
 +	size_t len = lstrlen(filename);
 +
 +	if (len < 5)
 +		return PA_FORMAT_UNKNOWN;
 +
 +	if (_tcsicmp(_T(".png"), &filename[len-4]) == 0)
 +		return PA_FORMAT_PNG;
 +
 +	if (_tcsicmp(_T(".jpg"), &filename[len-4]) == 0 || _tcsicmp(_T(".jpeg"), &filename[len-4]) == 0)
 +		return PA_FORMAT_JPEG;
 +
 +	if (_tcsicmp(_T(".ico"), &filename[len-4]) == 0)
 +		return PA_FORMAT_ICON;
 +
 +	if (_tcsicmp(_T(".bmp"), &filename[len-4]) == 0 || _tcsicmp(_T(".rle"), &filename[len-4]) == 0)
 +		return PA_FORMAT_BMP;
 +
 +	if (_tcsicmp(_T(".gif"), &filename[len-4]) == 0)
 +		return PA_FORMAT_GIF;
 +
 +	if (_tcsicmp(_T(".swf"), &filename[len-4]) == 0)
 +		return PA_FORMAT_SWF;
 +
 +	if (_tcsicmp(_T(".xml"), &filename[len-4]) == 0)
 +		return PA_FORMAT_XML;
 +
 +	return PA_FORMAT_UNKNOWN;
 +}
 +
 +static void FilterGetStrings(TCHAR *filter, int bytesLeft, BOOL xml, BOOL swf)
 +{
 +	TCHAR *pfilter;
 +	int wParam = bytesLeft;
 +
 +	lstrcpyn(filter, TranslateT("All Files"), bytesLeft); bytesLeft-=lstrlen(filter);
 +	_tcsncat(filter, _T(" (*.bmp;*.jpg;*.gif;*.png"), bytesLeft);
 +	if (swf) _tcscat(filter, _T(";*.swf"));
 +	if (xml) _tcscat(filter, _T(";*.xml"));
 +	_tcscat(filter, _T(")"));
 +	pfilter=filter+lstrlen(filter)+1; bytesLeft=wParam-(pfilter-filter);
 +	lstrcpyn(pfilter, _T("*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG"), bytesLeft);
 +	if (swf) _tcscat(pfilter, _T(";*.SWF"));
 +	if (xml) _tcscat(pfilter, _T(";*.XML"));
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +
 +	lstrcpyn(pfilter, TranslateT("Windows Bitmaps"), bytesLeft); bytesLeft-=lstrlen(pfilter);
 +	_tcsncat(pfilter, _T(" (*.bmp;*.rle)"), bytesLeft);
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +	lstrcpyn(pfilter, _T("*.BMP;*.RLE"), bytesLeft);
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +
 +	lstrcpyn(pfilter,TranslateT("JPEG Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter);
 +	_tcsncat(pfilter, _T(" (*.jpg;*.jpeg)"), bytesLeft);
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +	lstrcpyn(pfilter, _T("*.JPG;*.JPEG"), bytesLeft);
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +
 +	lstrcpyn(pfilter,TranslateT("GIF Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter);
 +	_tcsncat(pfilter, _T(" (*.gif)"), bytesLeft);
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +	lstrcpyn(pfilter, _T("*.GIF"), bytesLeft);
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +
 +	lstrcpyn(pfilter,TranslateT("PNG Bitmaps"), bytesLeft); bytesLeft-=lstrlen(pfilter);
 +	_tcsncat(pfilter, _T(" (*.png)"), bytesLeft);
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +	lstrcpyn(pfilter, _T("*.PNG"), bytesLeft);
 +	pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +
 +	if (swf)
 +	{
 +		lstrcpyn(pfilter,TranslateT("Flash Animations"), bytesLeft); bytesLeft-=lstrlen(pfilter);
 +		_tcsncat(pfilter, _T(" (*.swf)"), bytesLeft);
 +		pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +		lstrcpyn(pfilter, _T("*.SWF"), bytesLeft);
 +		pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +	}
 +
 +	if (xml)
 +	{
 +		lstrcpyn(pfilter, TranslateT("XML Files"), bytesLeft); bytesLeft-=lstrlen(pfilter);
 +		_tcsncat(pfilter, _T(" (*.xml)"), bytesLeft);
 +		pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +		lstrcpyn(pfilter, _T("*.XML"), bytesLeft);
 +		pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter);
 +	}
 +
 +	if (bytesLeft) *pfilter='\0';
 +}
 +
 +static void DeleteGlobalUserAvatar()
 +{
 +	DBVARIANT dbv = {0};
 +	if (DBGetContactSettingTString(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv))
 +		return;
 +
 +	TCHAR szFilename[MAX_PATH];
 +	AVS_pathToAbsolute(dbv.ptszVal, szFilename);
 +	DBFreeVariant(&dbv);
 +
 +	DeleteFile(szFilename);
 +	DBDeleteContactSetting(NULL, AVS_MODULE, "GlobalUserAvatarFile");
 +}
 +
 +static void SetIgnoreNotify(char *protocol, BOOL ignore)
 +{
 +	for(int i = 0; i < g_MyAvatars.getCount(); i++) {
 +		if (protocol == NULL || !lstrcmpA(g_MyAvatars[i].szProtoname, protocol)) {
 +			if (ignore)
 +				g_MyAvatars[i].dwFlags |= AVS_IGNORENOTIFY;
 +			else
 +				g_MyAvatars[i].dwFlags &= ~AVS_IGNORENOTIFY;
 +		}
 +	}
 +}
 +
 +static int InternalRemoveMyAvatar(char *protocol)
 +{
 +	SetIgnoreNotify(protocol, TRUE);
 +
 +	// Remove avatar
 +	int ret = 0;
 +	if (protocol != NULL)
 +	{
 +		if (ProtoServiceExists(protocol, PS_SETMYAVATAR))
 +			ret = SaveAvatar(protocol, NULL);
 +		else
 +			ret = -3;
 +
 +		if (ret == 0)
 +		{
 +			// Has global avatar?
 +			DBVARIANT dbv = {0};
 +			if (!DBGetContactSettingTString(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv))
 +			{
 +				DBFreeVariant(&dbv);
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
 +				DeleteGlobalUserAvatar();
 +			}
 +		}
 +	}
 +	else
 +	{
 +		PROTOACCOUNT **accs;
 +		int i,count;
 +
 +		ProtoEnumAccounts( &count, &accs );
 +		for (i = 0; i < count; i++)
 +		{
 +			if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR))
 +				continue;
 +
 +			if (!Proto_IsAvatarsEnabled( accs[i]->szModuleName ))
 +				continue;
 +
 +			// Found a protocol
 +			int retTmp = SaveAvatar( accs[i]->szModuleName, NULL);
 +			if (retTmp != 0)
 +				ret = retTmp;
 +		}
 +
 +		DeleteGlobalUserAvatar();
 +
 +		if (ret)
 +			DBWriteContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
 +		else
 +			DBWriteContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 0);
 +	}
 +
 +	SetIgnoreNotify(protocol, FALSE);
 +
 +	ReportMyAvatarChanged((WPARAM)(( protocol == NULL ) ? "" : protocol ), 0);
 +	return ret;
 +}
 +
 +static int InternalSetMyAvatar(char *protocol, TCHAR *szFinalName, SetMyAvatarHookData &data, BOOL allAcceptXML, BOOL allAcceptSWF)
 +{
 +	HANDLE hFile = 0;
 +
 +	int format = GetImageFormat(szFinalName);
 +	if (format == PA_FORMAT_UNKNOWN || (hFile = CreateFile(szFinalName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
 +		return -3;
 +
 +	CloseHandle(hFile);
 +
 +	// file exists...
 +
 +	HBITMAP hBmp = NULL;
 +
 +	if (format == PA_FORMAT_SWF)
 +	{
 +		if (!allAcceptSWF)
 +			return -4;
 +	}
 +	else if (format == PA_FORMAT_XML)
 +	{
 +		if (!allAcceptXML)
 +			return -4;
 +	}
 +	else
 +	{
 +		// Try to open if is not a flash or XML
 +		hBmp = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) szFinalName, IMGL_TCHAR);
 +		if (hBmp == NULL)
 +			return -4;
 +	}
 +
 +	SetIgnoreNotify(protocol, TRUE);
 +
 +	int ret = 0;
 +	if (protocol != NULL)
 +	{
 +		ret = SetProtoMyAvatar(protocol, hBmp, szFinalName, format, data.square, data.grow);
 +
 +		if (ret == 0)
 +		{
 +			DeleteGlobalUserAvatar();
 +			DBWriteContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
 +		}
 +	}
 +	else
 +	{
 +		PROTOACCOUNT **accs;
 +		int i,count;
 +
 +		ProtoEnumAccounts( &count, &accs );
 +		for (i = 0; i < count; i++)
 +		{
 +			if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR))
 +				continue;
 +
 +			if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName ))
 +				continue;
 +
 +			int retTmp = SetProtoMyAvatar( accs[i]->szModuleName, hBmp, szFinalName, format, data.square, data.grow);
 +			if (retTmp != 0)
 +				ret = retTmp;
 +		}
 +
 +		DeleteGlobalUserAvatar();
 +
 +		if (ret)
 +		{
 +			DBWriteContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
 +		}
 +		else
 +		{
 +			// Copy avatar file to store as global one
 +			TCHAR globalFile[1024];
 +			BOOL saved = TRUE;
 +			if (FoldersGetCustomPathT(hGlobalAvatarFolder, globalFile, SIZEOF(globalFile), _T("")))
 +			{
 +				mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\%s"), g_szDataPath, _T("GlobalAvatar"));
 +				CreateDirectory(globalFile, NULL);
 +			}
 +
 +			TCHAR *ext = _tcsrchr(szFinalName, _T('.')); // Can't be NULL here
 +			if (format == PA_FORMAT_XML || format == PA_FORMAT_SWF)
 +			{
 +				mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar%s"), globalFile, ext);
 +				CopyFile(szFinalName, globalFile, FALSE);
 +			}
 +			else
 +			{
 +				// Resize (to avoid too big avatars)
 +				ResizeBitmap rb = {0};
 +				rb.size = sizeof(ResizeBitmap);
 +				rb.hBmp = hBmp;
 +				rb.max_height = 300;
 +				rb.max_width = 300;
 +				rb.fit = (data.grow ? 0 : RESIZEBITMAP_FLAG_DONT_GROW)
 +						| (data.square ? RESIZEBITMAP_MAKE_SQUARE : RESIZEBITMAP_KEEP_PROPORTIONS);
 +
 +				HBITMAP hBmpTmp = (HBITMAP) BmpFilterResizeBitmap((WPARAM)&rb, 0);
 +
 +				// Check if need to resize
 +				if (hBmpTmp == hBmp || hBmpTmp == NULL)
 +				{
 +					// Use original image
 +					mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar%s"), globalFile, ext);
 +					CopyFile(szFinalName, globalFile, FALSE);
 +				}
 +				else
 +				{
 +					// Save as PNG
 +					mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar.png"), globalFile);
 +					if (BmpFilterSaveBitmap((WPARAM) hBmpTmp, (LPARAM) globalFile))
 +						saved = FALSE;
 +
 +					DeleteObject(hBmpTmp);
 +				}
 +			}
 +
 +			if (saved)
 +			{
 +				TCHAR relFile[1024];
 +				if (AVS_pathToRelative(globalFile, relFile))
 +					DBWriteContactSettingTString(NULL, AVS_MODULE, "GlobalUserAvatarFile", relFile);
 +				else
 +					DBWriteContactSettingTString(NULL, AVS_MODULE, "GlobalUserAvatarFile", globalFile);
 +
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 0);
 +			}
 +			else
 +			{
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1);
 +			}
 +		}
 +	}
 +
 +	DeleteObject(hBmp);
 +
 +	SetIgnoreNotify(protocol, FALSE);
 +
 +	ReportMyAvatarChanged((WPARAM)(( protocol == NULL ) ? "" : protocol ), 0);
 +	return ret;
 +}
 +
 +/*
 + * set an avatar for a protocol (service function)
 + * if lParam == NULL, a open file dialog will be opened, otherwise, lParam is taken as a FULL
 + * image filename (will be checked for existance, though)
 + */
 +
 +INT_PTR avSetMyAvatar( char* protocol, TCHAR* tszPath )
 +{
 +	TCHAR FileName[MAX_PATH];
 +	TCHAR *szFinalName = NULL;
 +	BOOL allAcceptXML;
 +	BOOL allAcceptSWF;
 +
 +	// Protocol allow seting of avatar?
 +	if (protocol != NULL && !CanSetMyAvatar((WPARAM) protocol, 0))
 +		return -1;
 +
 +	if (tszPath == NULL && hwndSetMyAvatar != 0)
 +	{
 +		SetForegroundWindow((HWND) hwndSetMyAvatar);
 +		SetFocus((HWND) hwndSetMyAvatar);
 + 		ShowWindow((HWND) hwndSetMyAvatar, SW_SHOW);
 +		return -2;
 +	}
 +
 +	SetMyAvatarHookData data = { 0 };
 +
 +	// Check for XML and SWF
 +	if (protocol == NULL)
 +	{
 +		allAcceptXML = TRUE;
 +		allAcceptSWF = TRUE;
 +
 +		PROTOACCOUNT **accs;
 +		int i,count;
 +
 +		ProtoEnumAccounts( &count, &accs );
 +		for (i = 0; i < count; i++)
 +		{
 +			if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR))
 +				continue;
 +
 +			if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName ))
 +				continue;
 +
 +			allAcceptXML = allAcceptXML && Proto_IsAvatarFormatSupported( accs[i]->szModuleName, PA_FORMAT_XML);
 +			allAcceptSWF = allAcceptSWF && Proto_IsAvatarFormatSupported( accs[i]->szModuleName, PA_FORMAT_SWF);
 +		}
 +
 +		data.square = DBGetContactSettingByte(0, AVS_MODULE, "SetAllwaysMakeSquare", 0);
 +	}
 +	else
 +	{
 +		allAcceptXML = Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_XML);
 +		allAcceptSWF = Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_SWF);
 +
 +		data.protocol = protocol;
 +		data.square = (Proto_AvatarImageProportion(protocol) & PIP_SQUARE)
 +						|| DBGetContactSettingByte(0, AVS_MODULE, "SetAllwaysMakeSquare", 0);
 +	}
 +
 +	if (tszPath == NULL) {
 +		OPENFILENAME ofn = {0};
 +		TCHAR filter[512];
 +		TCHAR inipath[1024];
 +
 +		data.protocol = protocol;
 +
 +		filter[0] = '\0';
 +		FilterGetStrings(filter, SIZEOF(filter), allAcceptXML, allAcceptSWF);
 +
 +		FoldersGetCustomPathT(hMyAvatarsFolder, inipath, SIZEOF(inipath), _T("."));
 +
 +		if (IsWinVer2000Plus())
 +			ofn.lStructSize = sizeof(ofn);
 +		else
 +			ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
 +		ofn.hwndOwner = 0;
 +		ofn.lpstrFile = FileName;
 +		ofn.lpstrFilter = filter;
 +		ofn.nMaxFile = MAX_PATH;
 +		ofn.nMaxFileTitle = MAX_PATH;
 +		ofn.Flags = OFN_FILEMUSTEXIST | OFN_ENABLETEMPLATE | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
 +		ofn.lpstrInitialDir = inipath;
 +		ofn.lpTemplateName = MAKEINTRESOURCE(IDD_SET_OWN_SUBCLASS);
 +		ofn.lpfnHook = SetMyAvatarHookProc;
 +		ofn.lCustData = (LPARAM) &data;
 +
 +		*FileName = '\0';
 +		ofn.lpstrDefExt = _T("");
 +		ofn.hInstance = g_hInst;
 +
 +		TCHAR title[256];
 +		if (protocol == NULL)
 +			mir_sntprintf(title, SIZEOF(title), TranslateT("Set My Avatar"));
 +		else
 +		{
 +			TCHAR* prototmp = mir_a2t(protocol);
 +			mir_sntprintf(title, SIZEOF(title), TranslateT("Set My Avatar for %s"), prototmp);
 +			mir_free(prototmp);
 +		}
 +		ofn.lpstrTitle = title;
 +
 +		if (GetOpenFileName(&ofn))
 +			szFinalName = FileName;
 +		else
 +			return 1;
 +	}
 +	else
 +		szFinalName = (TCHAR *)tszPath;
 +
 +	/*
 +	* filename is now set, check it and perform all needed action
 +	*/
 +
 +	if (szFinalName[0] == '\0')
 +		return InternalRemoveMyAvatar(protocol);
 +
 +	return InternalSetMyAvatar(protocol, szFinalName, data, allAcceptXML, allAcceptSWF);
 +}
 +
 +static INT_PTR SetMyAvatar( WPARAM wParam, LPARAM lParam )
 +{	return avSetMyAvatar(( char* )wParam, A2T(( const char* )lParam ));
 +}
 +
 +static INT_PTR SetMyAvatarW( WPARAM wParam, LPARAM lParam )
 +{	return avSetMyAvatar(( char* )wParam, ( TCHAR* )lParam );
 +}
 +
 +///////////////////////////////////////////////////////////////////////////////////////////////////////
 +
 +static DWORD GetFileSize(TCHAR *szFilename)
 +{
 +	HANDLE hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 +	if (hFile == INVALID_HANDLE_VALUE)
 +		return 0;
 +
 +	DWORD low = GetFileSize(hFile, NULL);
 +
 +	CloseHandle(hFile);
 +
 +	if (low == INVALID_FILE_SIZE)
 +		return 0;
 +
 +	return low;
 +}
 +
 +struct SaveProtocolData {
 +	DWORD max_size;
 +	TCHAR image_file_name[MAX_PATH];
 +	BOOL saved;
 +	BOOL need_smaller_size;
 +	int width;
 +	int height;
 +	TCHAR temp_file[MAX_PATH];
 +	HBITMAP hBmpProto;
 +};
 +
 +static void SaveImage(SaveProtocolData &d, char *protocol, int format)
 +{
 +	if (Proto_IsAvatarFormatSupported(protocol, format))
 +	{
 +		mir_sntprintf(d.image_file_name, SIZEOF(d.image_file_name), _T("%s%s"), d.temp_file, GetFormatExtension(format));
 +		if (!BmpFilterSaveBitmapT(d.hBmpProto, d.image_file_name, format == PA_FORMAT_JPEG ? JPEG_QUALITYSUPERB : 0))
 +		{
 +			if (d.max_size != 0 && GetFileSize(d.image_file_name) > d.max_size)
 +			{
 +				DeleteFile(d.image_file_name);
 +
 +				if (format == PA_FORMAT_JPEG)
 +				{
 +					// Try with lower quality
 +					if (!BmpFilterSaveBitmapT(d.hBmpProto, d.image_file_name, JPEG_QUALITYGOOD))
 +					{
 +						if (GetFileSize(d.image_file_name) > d.max_size)
 +						{
 +							DeleteFile(d.image_file_name);
 +							d.need_smaller_size = TRUE;
 +						}
 +						else
 +							d.saved = TRUE;
 +					}
 +				}
 +				else
 +					d.need_smaller_size = TRUE;
 +			}
 +			else
 +				d.saved = TRUE;
 +		}
 +	}
 +}
 +
 +static int SetProtoMyAvatar(char *protocol, HBITMAP hBmp, TCHAR *originalFilename, int originalFormat,
 +							BOOL square, BOOL grow)
 +{
 +	if (!ProtoServiceExists(protocol, PS_SETMYAVATAR))
 +		return -1;
 +
 +	// If is swf or xml, just set it
 +
 +	if (originalFormat == PA_FORMAT_SWF)
 +	{
 +		if (!Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_SWF))
 +			return -1;
 +
 +		return SaveAvatar(protocol, originalFilename);
 +	}
 +
 +	if (originalFormat == PA_FORMAT_XML)
 +	{
 +		if (!Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_XML))
 +			return -1;
 +
 +		return SaveAvatar(protocol, originalFilename);
 +	}
 +
 +	// Get protocol info
 +	SaveProtocolData d = {0};
 +
 +	d.max_size = (DWORD) Proto_GetAvatarMaxFileSize(protocol);
 +
 +	Proto_GetAvatarMaxSize(protocol, &d.width, &d.height);
 +	int orig_width = d.width;
 +	int orig_height = d.height;
 +
 +	if (Proto_AvatarImageProportion(protocol) & PIP_SQUARE)
 +		square = TRUE;
 +
 +
 +	// Try to save until a valid image is found or we give up
 +	int num_tries = 0;
 +	do {
 +		// Lets do it
 +		ResizeBitmap rb;
 +		rb.size = sizeof(ResizeBitmap);
 +		rb.hBmp = hBmp;
 +		rb.max_height = d.height;
 +		rb.max_width = d.width;
 +		rb.fit = (grow ? 0 : RESIZEBITMAP_FLAG_DONT_GROW)
 +				| (square ? RESIZEBITMAP_MAKE_SQUARE : RESIZEBITMAP_KEEP_PROPORTIONS);
 +
 +		d.hBmpProto = (HBITMAP) BmpFilterResizeBitmap((WPARAM)&rb, 0);
 +
 +		if (d.hBmpProto == NULL)
 +		{
 +			if (d.temp_file[0] != '\0')
 +				DeleteFile(d.temp_file);
 +			return -1;
 +		}
 +
 +		// Check if can use original image
 +		if (d.hBmpProto == hBmp
 +			&& Proto_IsAvatarFormatSupported(protocol, originalFormat)
 +			&& (d.max_size == 0 || GetFileSize(originalFilename) < d.max_size))
 +		{
 +			if (d.temp_file[0] != '\0')
 +				DeleteFile(d.temp_file);
 +
 +			// Use original image
 +			return SaveAvatar(protocol, originalFilename);
 +		}
 +
 +		// Create a temporary file (if was not created already)
 +		if (d.temp_file[0] == '\0')
 +		{
 +			d.temp_file[0] = '\0';
 +			if (GetTempPath(MAX_PATH, d.temp_file) == 0
 +				|| GetTempFileName(d.temp_file, _T("mir_av_"), 0, d.temp_file) == 0)
 +			{
 +				DeleteObject(d.hBmpProto);
 +				return -1;
 +			}
 +		}
 +
 +		// Which format?
 +
 +		// First try to use original format
 +		if (originalFormat != PA_FORMAT_BMP)
 +			SaveImage(d, protocol, originalFormat);
 +
 +		if (!d.saved && originalFormat != PA_FORMAT_PNG)
 +			SaveImage(d, protocol, PA_FORMAT_PNG);
 +
 +		if (!d.saved && originalFormat != PA_FORMAT_JPEG)
 +			SaveImage(d, protocol, PA_FORMAT_JPEG);
 +
 +		if (!d.saved && originalFormat != PA_FORMAT_GIF)
 +			SaveImage(d, protocol, PA_FORMAT_GIF);
 +
 +		if (!d.saved)
 +			SaveImage(d, protocol, PA_FORMAT_BMP);
 +
 +		num_tries++;
 +		if (!d.saved && d.need_smaller_size && num_tries < 4)
 +		{
 +			// Cleanup
 +			if (d.hBmpProto != hBmp)
 +				DeleteObject(d.hBmpProto);
 +
 +			// use a smaller size
 +			d.width = orig_width * (4 - num_tries) / 4;
 +			d.height = orig_height * (4 - num_tries) / 4;
 +		}
 +
 +	} while(!d.saved && d.need_smaller_size && num_tries < 4);
 +
 +	int ret;
 +
 +	if (d.saved)
 +	{
 +		// Call proto service
 +		ret = SaveAvatar(protocol, d.image_file_name);
 +		DeleteFile(d.image_file_name);
 +	}
 +	else
 +	{
 +		ret = -1;
 +	}
 +
 +	if (d.temp_file[0] != '\0')
 +		DeleteFile(d.temp_file);
 +
 +	if (d.hBmpProto != hBmp)
 +		DeleteObject(d.hBmpProto);
 +
 +	return ret;
 +}
 +
 +static INT_PTR ContactOptions(WPARAM wParam, LPARAM lParam)
 +{
 +	if (wParam)
 +		CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_AVATAROPTIONS), 0, DlgProcAvatarOptions, (LPARAM)wParam);
 +	return 0;
 +}
 +
 +INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam)
 +{
 +	int i;
 +	char *szProto = (char *)lParam;
 +
 +	if (wParam || g_shutDown || fei == NULL)
 +		return 0;
 +
 +	if (lParam == 0 || IsBadReadPtr(szProto, 4))
 +		return 0;
 +
 +	for(i = 0; i < g_MyAvatars.getCount(); i++) {
 +		if (!lstrcmpA(szProto, g_MyAvatars[i].szProtoname) && g_MyAvatars[i].hbmPic != 0)
 +			return (INT_PTR)&g_MyAvatars[i];
 +	}
 +	return 0;
 +}
 +
 +static protoPicCacheEntry *GetProtoDefaultAvatar(HANDLE hContact)
 +{
 +	char *szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +	if (szProto) {
 +		for(int i = 0; i < g_ProtoPictures.getCount(); i++) {
 +			protoPicCacheEntry& p = g_ProtoPictures[i];
 +			if ( !lstrcmpA(p.szProtoname, szProto) && p.hbmPic != NULL)
 +				return &g_ProtoPictures[i];
 +		}
 +	}
 +	return NULL;
 +}
 +
 +HANDLE GetContactThatHaveTheAvatar(HANDLE hContact, int locked = -1)
 +{
 +	if (g_MetaAvail && DBGetContactSettingByte(NULL, g_szMetaName, "Enabled", 0)) {
 +		if (DBGetContactSettingDword(hContact, g_szMetaName, "NumContacts", 0) >= 1) {
 +			if (locked == -1)
 +				locked = DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0);
 +
 +			if (!locked)
 +				hContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0);
 +		}
 +	}
 +	return hContact;
 +}
 +
 +INT_PTR GetAvatarBitmap(WPARAM wParam, LPARAM lParam)
 +{
 +	if (wParam == 0 || g_shutDown || fei == NULL)
 +		return 0;
 +
 +	HANDLE hContact = (HANDLE) wParam;
 +	hContact = GetContactThatHaveTheAvatar(hContact);
 +
 +	// Get the node
 +	struct CacheNode *node = FindAvatarInCache(hContact, TRUE);
 +	if (node == NULL || !node->loaded)
 +		return (INT_PTR) GetProtoDefaultAvatar(hContact);
 +	else
 +		return (INT_PTR) &node->ace;
 +}
 +
 +// Just delete an avatar from cache
 +// An cache entry is never deleted. What is deleted is the image handle inside it
 +// This is done this way to keep track of which avatars avs have to keep track
 +void DeleteAvatarFromCache(HANDLE hContact, BOOL forever)
 +{
 +	hContact = GetContactThatHaveTheAvatar(hContact);
 +
 +	struct CacheNode *node = FindAvatarInCache(hContact, FALSE);
 +	if (node == NULL) {
 +		struct CacheNode temp_node = {0};
 +		if (g_MetaAvail)
 +			temp_node.dwFlags |= (DBGetContactSettingByte(hContact, g_szMetaName, "IsSubcontact", 0) ? MC_ISSUBCONTACT : 0);
 +		NotifyMetaAware(hContact, &temp_node, (AVATARCACHEENTRY *)GetProtoDefaultAvatar(hContact));
 +		return;
 +	}
 +	node->mustLoad = -1;                        // mark for deletion
 +	if (forever)
 +		node->dwFlags |= AVS_DELETENODEFOREVER;
 +	SetEvent(hLoaderEvent);
 +}
 +
 +int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist, int pa_format)
 +{
 +	if (g_shutDown)
 +		return 0;
 +
 +	hContact = GetContactThatHaveTheAvatar(hContact);
 +
 +	// Get the node
 +	struct CacheNode *node = FindAvatarInCache(hContact, g_AvatarHistoryAvail && fNotifyHist, TRUE);
 +	if (node == NULL)
 +		return 0;
 +
 +	if (fNotifyHist)
 +		node->dwFlags |= AVH_MUSTNOTIFY;
 +
 +	node->mustLoad = fLoad ? 1 : -1;
 +	node->pa_format = pa_format;
 +	SetEvent(hLoaderEvent);
 +	return 0;
 +}
 +
 +/*
 + * this thread scans the cache and handles nodes which have mustLoad set to > 0 (must be loaded/reloaded) or
 + * nodes where mustLoad is < 0 (must be deleted).
 + * its waken up by the event and tries to lock the cache only when absolutely necessary.
 + */
 +
 +static void PicLoader(LPVOID param)
 +{
 +	DWORD dwDelay = DBGetContactSettingDword(NULL, AVS_MODULE, "picloader_sleeptime", 80);
 +
 +	if (dwDelay < 30)
 +		dwDelay = 30;
 +	else if (dwDelay > 100)
 +		dwDelay = 100;
 +
 +	while(!g_shutDown) {
 +		struct CacheNode *node = g_Cache;
 +
 +		while(!g_shutDown && node) {
 +			if (node->mustLoad > 0 && node->ace.hContact) {
 +				node->mustLoad = 0;
 +				AVATARCACHEENTRY ace_temp;
 +
 +				if (DBGetContactSettingByte(node->ace.hContact, "ContactPhoto", "NeedUpdate", 0))
 +					QueueAdd(node->ace.hContact);
 +
 +				CopyMemory(&ace_temp, &node->ace, sizeof(AVATARCACHEENTRY));
 +				ace_temp.hbmPic = 0;
 +
 +				int result = CreateAvatarInCache(node->ace.hContact, &ace_temp, NULL);
 +
 +				if (result == -2)
 +				{
 +					char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)node->ace.hContact, 0);
 +					if (szProto == NULL || Proto_NeedDelaysForAvatars(szProto))
 +					{
 +						QueueAdd(node->ace.hContact);
 +					}
 +					else
 +					{
 +						if (FetchAvatarFor(node->ace.hContact, szProto) == GAIR_SUCCESS)
 +							// Try yo create again
 +							result = CreateAvatarInCache(node->ace.hContact, &ace_temp, NULL);
 +					}
 +				}
 +
 +				if ((result == 1 && ace_temp.hbmPic != 0)) // Loaded
 +				{
 +					HBITMAP oldPic = node->ace.hbmPic;
 +
 +					EnterCriticalSection(&cachecs);
 +					CopyMemory(&node->ace, &ace_temp, sizeof(AVATARCACHEENTRY));
 +					node->loaded = TRUE;
 +					LeaveCriticalSection(&cachecs);
 +					if (oldPic)
 +						DeleteObject(oldPic);
 +					NotifyMetaAware(node->ace.hContact, node);
 +				}
 +				else if (result == 0 || result == -3) // Has no avatar
 +				{
 +					HBITMAP oldPic = node->ace.hbmPic;
 +
 +					EnterCriticalSection(&cachecs);
 +					CopyMemory(&node->ace, &ace_temp, sizeof(AVATARCACHEENTRY));
 +					node->loaded = FALSE;
 +					node->mustLoad = 0;
 +					LeaveCriticalSection(&cachecs);
 +					if (oldPic)
 +						DeleteObject(oldPic);
 +					NotifyMetaAware(node->ace.hContact, node);
 +				}
 +
 +				mir_sleep(dwDelay);
 +			}
 +			else if (node->mustLoad < 0 && node->ace.hContact) {         // delete this picture
 +				HANDLE hContact = node->ace.hContact;
 +				EnterCriticalSection(&cachecs);
 +				node->mustLoad = 0;
 +				node->loaded = 0;
 +				if (node->ace.hbmPic)
 +					DeleteObject(node->ace.hbmPic);
 +				ZeroMemory(&node->ace, sizeof(AVATARCACHEENTRY));
 +				if (node->dwFlags & AVS_DELETENODEFOREVER) {
 +					node->dwFlags &= ~AVS_DELETENODEFOREVER;
 +				} else {
 +					node->ace.hContact = hContact;
 +				}
 +				LeaveCriticalSection(&cachecs);
 +				NotifyMetaAware(hContact, node, (AVATARCACHEENTRY *)GetProtoDefaultAvatar(hContact));
 +			}
 +			// protect this by changes from the cache block allocator as it can cause inconsistencies while working
 +			// on allocating a new block.
 +			EnterCriticalSection(&alloccs);
 +			node = node->pNextNode;
 +			LeaveCriticalSection(&alloccs);
 +		}
 +		WaitForSingleObject(hLoaderEvent, INFINITE);
 +		//_DebugTrace(0, "pic loader awake...");
 +		ResetEvent(hLoaderEvent);
 +	}
 +}
 +
 +static int MetaChanged(WPARAM wParam, LPARAM lParam)
 +{
 +	if (wParam == 0 || g_shutDown)
 +		return 0;
 +
 +	AVATARCACHEENTRY *ace;
 +
 +	HANDLE hContact = (HANDLE) wParam;
 +	HANDLE hSubContact = GetContactThatHaveTheAvatar(hContact);
 +
 +	// Get the node
 +	struct CacheNode *node = FindAvatarInCache(hSubContact, TRUE);
 +	if (node == NULL || !node->loaded) {
 +		ace = (AVATARCACHEENTRY *)GetProtoDefaultAvatar(hSubContact);
 +		QueueAdd(hSubContact);
 +	}
 +	else
 +		ace = &node->ace;
 +
 +	NotifyEventHooks(hEventChanged, (WPARAM)hContact, (LPARAM)ace);
 +	return 0;
 +}
 +
 +static LIST<void> arServices( 10 );
 +
 +static int DestroyServicesAndEvents()
 +{
 +	UnhookEvent(hContactSettingChanged);
 +	UnhookEvent(hProtoAckHook);
 +	UnhookEvent(hUserInfoInitHook);
 +	UnhookEvent(hOptInit);
 +	UnhookEvent(hModulesLoaded);
 +	UnhookEvent(hPresutdown);
 +	UnhookEvent(hOkToExit);
 +	UnhookEvent(hAccChanged);
 +
 +	for ( int i=0; i < arServices.getCount(); i++ )
 +		DestroyServiceFunction( arServices[i] );
 +
 +	arServices.destroy();
 +
 +	DestroyHookableEvent(hEventChanged);
 +	DestroyHookableEvent(hEventContactAvatarChanged);
 +	DestroyHookableEvent(hMyAvatarChanged);
 +	hEventChanged = 0;
 +	hEventContactAvatarChanged = 0;
 +	hMyAvatarChanged = 0;
 +	UnhookEvent(hEventDeleted);
 +	return 0;
 +}
 +
 +static void LoadDefaultInfo()
 +{
 +	protoPicCacheEntry* pce = new protoPicCacheEntry;
 +	if (CreateAvatarInCache(0, pce, AVS_DEFAULT) != 1)
 +		DBDeleteContactSetting(0, PPICT_MODULE, AVS_DEFAULT);
 +
 +	pce->szProtoname = mir_strdup(AVS_DEFAULT);
 +	pce->tszAccName = mir_tstrdup(TranslateT(AVS_DEFAULT));
 +	g_ProtoPictures.insert(pce);
 +}
 +
 +static void LoadProtoInfo( PROTOCOLDESCRIPTOR* proto )
 +{
 +	if ( proto->type == PROTOTYPE_PROTOCOL && proto->cbSize == sizeof( *proto ))
 +	{
 +		char protoName[MAX_PATH];
 +		mir_snprintf(protoName, SIZEOF(protoName), "Global avatar for %s accounts", proto->szName);
 +		TCHAR protoNameTmp[MAX_PATH];
 +		TCHAR *tszName = mir_a2t(proto->szName);
 +		mir_sntprintf(protoNameTmp, SIZEOF(protoNameTmp), TranslateT("Global avatar for %s accounts"), tszName);
 +		protoPicCacheEntry* pce = new protoPicCacheEntry;
 +		if (CreateAvatarInCache(0, pce, protoName) != 1)
 +			DBDeleteContactSetting(0, PPICT_MODULE, protoName);
 +
 +		pce->szProtoname = mir_strdup(protoName);
 +		pce->tszAccName = mir_tstrdup(protoNameTmp);
 +		g_ProtoPictures.insert(pce);
 +		mir_free(tszName);
 +	}
 +}
 +
 +static void LoadAccountInfo( PROTOACCOUNT* acc )
 +{
 +	protoPicCacheEntry* pce = new protoPicCacheEntry;
 +	if ( CreateAvatarInCache(0, pce, acc->szModuleName ) != 1 )
 +		DBDeleteContactSetting(0, PPICT_MODULE, acc->szModuleName);
 +
 +	pce->szProtoname = mir_strdup( acc->szModuleName );
 +	pce->tszAccName = mir_tstrdup( acc->tszAccountName );
 +	g_ProtoPictures.insert( pce );
 +
 +	pce = new protoPicCacheEntry;
 +	CreateAvatarInCache((HANDLE)-1, pce, acc->szModuleName );
 +	pce->szProtoname = mir_strdup( acc->szModuleName );
 +	pce->tszAccName = mir_tstrdup( acc->tszAccountName );
 +	g_MyAvatars.insert( pce );
 +}
 +
 +static int OnAccChanged(WPARAM wParam, LPARAM lParam)
 +{
 +	PROTOACCOUNT* pa = ( PROTOACCOUNT* )lParam;
 +
 +	switch( wParam ) {
 +	case PRAC_ADDED:
 +		LoadAccountInfo( pa );
 +		break;
 +
 +	case PRAC_REMOVED:
 +		{
 +			int idx;
 +			protoPicCacheEntry tmp;
 +			tmp.szProtoname = mir_strdup(pa->szModuleName);
 +			if (( idx = g_ProtoPictures.getIndex( &tmp )) != -1 )
 +				g_ProtoPictures.remove( idx );
 +			if (( idx = g_MyAvatars.getIndex( &tmp )) != -1 )
 +				g_MyAvatars.remove( idx );
 +		}
 +		break;
 +	}
 +
 +	return 0;
 +}
 +
 +static int ModulesLoaded(WPARAM wParam, LPARAM lParam)
 +{
 +	int i;
 +	DBVARIANT dbv = {0};
 +	TCHAR szEventName[100];
 +	int   result = 0;
 +
 +	InitPolls();
 +
 +	mir_sntprintf(szEventName, 100, _T("avs_loaderthread_%d"), GetCurrentThreadId());
 +	hLoaderEvent = CreateEvent(NULL, TRUE, FALSE, szEventName);
 +	hLoaderThread = (HANDLE) mir_forkthread(PicLoader, 0);
 +	SetThreadPriority(hLoaderThread, THREAD_PRIORITY_IDLE);
 +
 +	// Folders plugin support
 +	if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
 +	{
 +		hMyAvatarsFolder = (HANDLE) FoldersRegisterCustomPathT("Avatars", "My Avatars",
 +			MIRANDA_USERDATAT _T("\\Avatars"));
 +
 +		hGlobalAvatarFolder = (HANDLE) FoldersRegisterCustomPathT("Avatars", "My Global Avatar Cache",
 +			MIRANDA_USERDATAT _T("\\Avatars"));
 +	}
 +
 +	g_AvatarHistoryAvail = ServiceExists(MS_AVATARHISTORY_ENABLED);
 +
 +	g_MetaAvail = ServiceExists(MS_MC_GETPROTOCOLNAME) ? TRUE : FALSE;
 +	if (g_MetaAvail) {
 +		g_szMetaName = (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
 +		if (g_szMetaName == NULL)
 +			g_MetaAvail = FALSE;
 +	}
 +
 +	PROTOACCOUNT **accs = NULL;
 +	int accCount;
 +	ProtoEnumAccounts( &accCount, &accs );
 +
 +	if ( fei != NULL )
 +	{
 +		LoadDefaultInfo();
 +		PROTOCOLDESCRIPTOR** proto;
 +		int protoCount;
 +		CallService(MS_PROTO_ENUMPROTOS, ( WPARAM )&protoCount, ( LPARAM )&proto);
 +		for ( i=0; i < protoCount; i++ )
 +			LoadProtoInfo( proto[i] );
 +		for(i = 0; i < accCount; i++)
 +			LoadAccountInfo( accs[i] );
 +	}
 +
 +	// Load global avatar
 +	protoPicCacheEntry* pce = new protoPicCacheEntry;
 +	CreateAvatarInCache((HANDLE)-1, pce, "");
 +	pce->szProtoname = mir_strdup("");
 +	g_MyAvatars.insert( pce );
 +
 +	hAccChanged = HookEvent(ME_PROTO_ACCLISTCHANGED, OnAccChanged);
 +	hPresutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownProc);
 +	hOkToExit = HookEvent(ME_SYSTEM_OKTOEXIT, OkToExitProc);
 +	hUserInfoInitHook = HookEvent(ME_USERINFO_INITIALISE, OnDetailsInit);
 +
 +	return 0;
 +}
 +
 +static void ReloadMyAvatar(LPVOID lpParam)
 +{
 +	char *szProto = (char *)lpParam;
 +
 +	mir_sleep(500);
 +	for(int i = 0; !g_shutDown && i < g_MyAvatars.getCount(); i++) {
 +		char *myAvatarProto = g_MyAvatars[i].szProtoname;
 +
 +		if (szProto[0] == 0) {
 +			// Notify to all possibles
 +			if (lstrcmpA(myAvatarProto, szProto)) {
 +				if (!ProtoServiceExists( myAvatarProto, PS_SETMYAVATAR))
 +					continue;
 +				if (!Proto_IsAvatarsEnabled( myAvatarProto ))
 +					continue;
 +			}
 +
 +		} else if (lstrcmpA(myAvatarProto, szProto)) {
 +			continue;
 +		}
 +
 +		if (g_MyAvatars[i].hbmPic)
 +			DeleteObject(g_MyAvatars[i].hbmPic);
 +
 +		if (CreateAvatarInCache((HANDLE)-1, &g_MyAvatars[i], myAvatarProto) != -1)
 +			NotifyEventHooks(hMyAvatarChanged, (WPARAM)myAvatarProto, (LPARAM)&g_MyAvatars[i]);
 +		else
 +			NotifyEventHooks(hMyAvatarChanged, (WPARAM)myAvatarProto, 0);
 +	}
 +
 +	free(lpParam);
 +}
 +
 +static INT_PTR ReportMyAvatarChanged(WPARAM wParam, LPARAM lParam)
 +{
 +	if (wParam == NULL)
 +		return -1;
 +
 +	char *proto = (char *) wParam;
 +
 +	for(int i = 0; i < g_MyAvatars.getCount(); i++) {
 +		if (g_MyAvatars[i].dwFlags & AVS_IGNORENOTIFY)
 +			continue;
 +
 +		if ( !lstrcmpA(g_MyAvatars[i].szProtoname, proto)) {
 +			LPVOID lpParam = (void *)malloc(lstrlenA(g_MyAvatars[i].szProtoname) + 2);
 +			strcpy((char *)lpParam, g_MyAvatars[i].szProtoname);
 +			mir_forkthread(ReloadMyAvatar, lpParam);
 +			return 0;
 +		}
 +	}
 +
 +	return -2;
 +}
 +
 +static int ContactSettingChanged(WPARAM wParam, LPARAM lParam)
 +{
 +	DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam;
 +
 +	if (cws == NULL || g_shutDown)
 +		return 0;
 +
 +	if (wParam == 0) {
 +		if (!strcmp(cws->szSetting, "AvatarFile")
 +			|| !strcmp(cws->szSetting, "PictObject")
 +			|| !strcmp(cws->szSetting, "AvatarHash")
 +			|| !strcmp(cws->szSetting, "AvatarSaved"))
 +		{
 +			ReportMyAvatarChanged((WPARAM) cws->szModule, 0);
 +		}
 +		return 0;
 +	}
 +	else if (g_MetaAvail && !strcmp(cws->szModule, g_szMetaName)) {
 +		if (lstrlenA(cws->szSetting) > 6 && !strncmp(cws->szSetting, "Status", 5))
 +			MetaChanged(wParam, 0);
 +	}
 +	return 0;
 +}
 +
 +static int ContactDeleted(WPARAM wParam, LPARAM lParam)
 +{
 +	DeleteAvatarFromCache((HANDLE)wParam, TRUE);
 +
 +	return 0;
 +}
 +
 +static int OptInit(WPARAM wParam, LPARAM lParam)
 +{
 +	OPTIONSDIALOGPAGE odp = { 0 };
 +	odp.cbSize = sizeof(odp);
 +	odp.hInstance = g_hInst;
 +	odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY;
 +	odp.pszGroup = LPGEN("Customize");
 +	odp.pszTitle = LPGEN("Avatars");
 +
 +	odp.pszTab = LPGEN("Protocols");
 +	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_PICTS);
 +	odp.pfnDlgProc = DlgProcOptionsProtos;
 +	Options_AddPage(wParam, &odp);
 +
 +	odp.pszTab = LPGEN("Contact Avatars");
 +	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_AVATARS);
 +	odp.pfnDlgProc = DlgProcOptionsAvatars;
 +	Options_AddPage(wParam, &odp);
 +
 +	odp.pszTab = LPGEN("Own Avatars");
 +	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_OWN);
 +	odp.pfnDlgProc = DlgProcOptionsOwn;
 +	Options_AddPage(wParam, &odp);
 +
 +	return 0;
 +}
 +
 +static int OkToExitProc(WPARAM wParam, LPARAM lParam)
 +{
 +	EnterCriticalSection(&cachecs);
 +	g_shutDown = TRUE;
 +
 +	DestroyServicesAndEvents();
 +
 +	LeaveCriticalSection(&cachecs);
 +
 +	SetEvent(hLoaderEvent);
 +	FreePolls();
 +	return 0;
 +}
 +
 +static int ShutdownProc(WPARAM wParam, LPARAM lParam)
 +{
 +	DeleteCriticalSection(&cachecs);
 +	DeleteCriticalSection(&alloccs);
 +	return 0;
 +}
 +
 +void InternalDrawAvatar(AVATARDRAWREQUEST *r, HBITMAP hbm, LONG bmWidth, LONG bmHeight, DWORD dwFlags)
 +{
 +	float dScale = 0;
 +	int newHeight, newWidth;
 +	HDC hdcAvatar;
 +	HBITMAP hbmMem;
 +	DWORD topoffset = 0, leftoffset = 0;
 +	HRGN rgn = 0, oldRgn = 0;
 +	int targetWidth = r->rcDraw.right - r->rcDraw.left;
 +	int targetHeight = r->rcDraw.bottom - r->rcDraw.top;
 +	BLENDFUNCTION bf = {0};
 +
 +	hdcAvatar = CreateCompatibleDC(r->hTargetDC);
 +	hbmMem = (HBITMAP)SelectObject(hdcAvatar, hbm);
 +
 +	if ((r->dwFlags & AVDRQ_DONTRESIZEIFSMALLER) && bmHeight <= targetHeight && bmWidth <= targetWidth) {
 +		newHeight = bmHeight;
 +		newWidth = bmWidth;
 +	}
 +	else if (bmHeight >= bmWidth) {
 +		dScale = targetHeight / (float)bmHeight;
 +		newHeight = targetHeight;
 +		newWidth = (int) (bmWidth * dScale);
 +	}
 +	else {
 +		dScale = targetWidth / (float)bmWidth;
 +		newWidth = targetWidth;
 +		newHeight = (int) (bmHeight * dScale);
 +	}
 +
 +	topoffset = targetHeight > newHeight ? (targetHeight - newHeight) / 2 : 0;
 +	leftoffset = targetWidth > newWidth ? (targetWidth - newWidth) / 2 : 0;
 +
 +	// create the region for the avatar border - use the same region for clipping, if needed.
 +
 +	oldRgn = CreateRectRgn(0,0,1,1);
 +
 +	if (GetClipRgn(r->hTargetDC, oldRgn) != 1)
 +	{
 +		DeleteObject(oldRgn);
 +		oldRgn = NULL;
 +	}
 +
 +	if (r->dwFlags & AVDRQ_ROUNDEDCORNER)
 +		rgn = CreateRoundRectRgn(r->rcDraw.left + leftoffset, r->rcDraw.top + topoffset, r->rcDraw.left + leftoffset + newWidth + 1, r->rcDraw.top + topoffset + newHeight + 1, 2 * r->radius, 2 * r->radius);
 +	else
 +		rgn = CreateRectRgn(r->rcDraw.left + leftoffset, r->rcDraw.top + topoffset, r->rcDraw.left + leftoffset + newWidth, r->rcDraw.top + topoffset + newHeight);
 +
 +	ExtSelectClipRgn(r->hTargetDC, rgn, RGN_AND);
 +
 +	bf.SourceConstantAlpha = r->alpha > 0 ? r->alpha : 255;
 +	bf.AlphaFormat = dwFlags & AVS_PREMULTIPLIED ? AC_SRC_ALPHA : 0;
 +
 +	if (!(r->dwFlags & AVDRQ_AERO))
 +		SetStretchBltMode(r->hTargetDC, HALFTONE);
 +	//else
 +	//	FillRect(r->hTargetDC, &r->rcDraw, (HBRUSH)GetStockObject(BLACK_BRUSH));
 +
 +	if (r->dwFlags & AVDRQ_FORCEFASTALPHA && !(r->dwFlags & AVDRQ_AERO) && AvsAlphaBlend) {
 +		AvsAlphaBlend(
 +			r->hTargetDC, r->rcDraw.left + leftoffset, r->rcDraw.top + topoffset, newWidth, newHeight,
 +			hdcAvatar, 0, 0, bmWidth, bmHeight, bf);
 +	} else {
 +		if ((bf.SourceConstantAlpha == 255 && bf.AlphaFormat == 0 && !(r->dwFlags & AVDRQ_FORCEALPHA) && !(r->dwFlags & AVDRQ_AERO)) || !AvsAlphaBlend) {
 +			StretchBlt(r->hTargetDC, r->rcDraw.left + leftoffset, r->rcDraw.top + topoffset, newWidth, newHeight, hdcAvatar, 0, 0, bmWidth, bmHeight, SRCCOPY);
 +		} else {
 +			/*
 +			* get around SUCKY AlphaBlend() rescaling quality...
 +			*/
 +			FIBITMAP *fb = fei->FI_CreateDIBFromHBITMAP(hbm);
 +			FIBITMAP *fbResized = fei->FI_Rescale(fb, newWidth, newHeight, FILTER_BICUBIC);
 +			HBITMAP hbmResized = fei->FI_CreateHBITMAPFromDIB(fbResized);
 +			fei->FI_Unload(fb);
 +			fei->FI_Unload(fbResized);
 +
 +			HBITMAP hbmTempOld;
 +			HDC hdcTemp = CreateCompatibleDC(r->hTargetDC);
 +			hbmTempOld = (HBITMAP)SelectObject(hdcTemp, hbmResized);
 +
 +			AvsAlphaBlend(
 +				r->hTargetDC, r->rcDraw.left + leftoffset, r->rcDraw.top + topoffset, newWidth, newHeight,
 +				hdcTemp, 0, 0, newWidth, newHeight, bf);
 +
 +			SelectObject(hdcTemp, hbmTempOld);
 +			DeleteObject(hbmResized);
 +			DeleteDC(hdcTemp);
 +		}
 +
 +		if ((r->dwFlags & AVDRQ_DRAWBORDER) && !((r->dwFlags & AVDRQ_HIDEBORDERONTRANSPARENCY) && (dwFlags & AVS_HASTRANSPARENCY))) {
 +			HBRUSH br = CreateSolidBrush(r->clrBorder);
 +			HBRUSH brOld = (HBRUSH)SelectObject(r->hTargetDC, br);
 +			FrameRgn(r->hTargetDC, rgn, br, 1, 1);
 +			SelectObject(r->hTargetDC, brOld);
 +			DeleteObject(br);
 +		}
 +
 +		SelectClipRgn(r->hTargetDC, oldRgn);
 +		DeleteObject(rgn);
 +		if (oldRgn) DeleteObject(oldRgn);
 +
 +		SelectObject(hdcAvatar, hbmMem);
 +		DeleteDC(hdcAvatar);
 +	}
 +}
 +
 +INT_PTR DrawAvatarPicture(WPARAM wParam, LPARAM lParam)
 +{
 +	AVATARDRAWREQUEST *r = (AVATARDRAWREQUEST *)lParam;
 +	AVATARCACHEENTRY *ace = NULL;
 +
 +	if (fei == NULL || r == NULL || IsBadReadPtr((void *)r, sizeof(AVATARDRAWREQUEST)))
 +		return 0;
 +
 +	if (r->cbSize != sizeof(AVATARDRAWREQUEST))
 +		return 0;
 +
 +	if (r->dwFlags & AVDRQ_PROTOPICT) {
 +		if (r->szProto == NULL)
 +			return 0;
 +
 +		for(int i = 0; i < g_ProtoPictures.getCount(); i++) {
 +			protoPicCacheEntry& p = g_ProtoPictures[i];
 +			if ( !lstrcmpA(p.szProtoname, r->szProto) && lstrlenA(r->szProto) == lstrlenA(p.szProtoname) && p.hbmPic != 0) {
 +				ace = (AVATARCACHEENTRY *)&g_ProtoPictures[i];
 +				break;
 +			}
 +		}
 +	}
 +	else if (r->dwFlags & AVDRQ_OWNPIC) {
 +		if (r->szProto == NULL)
 +			return 0;
 +
 +		if (r->szProto[0] == '\0' && DBGetContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1))
 +			return -1;
 +
 +		ace = (AVATARCACHEENTRY *)GetMyAvatar(0, (LPARAM)r->szProto);
 +	}
 +	else
 +		ace = (AVATARCACHEENTRY *)GetAvatarBitmap((WPARAM)r->hContact, 0);
 +
 +	if (ace && (!(r->dwFlags & AVDRQ_RESPECTHIDDEN) || !(ace->dwFlags & AVS_HIDEONCLIST))) {
 +		ace->t_lastAccess = time(NULL);
 +
 +		if (ace->bmHeight == 0 || ace->bmWidth == 0 || ace->hbmPic == 0)
 +			return 0;
 +
 +		InternalDrawAvatar(r, ace->hbmPic, ace->bmWidth, ace->bmHeight, ace->dwFlags);
 +		return 1;
 +	}
 +
 +	return 0;
 +}
 +
 +static int OnDetailsInit(WPARAM wParam, LPARAM lParam)
 +{
 +	HANDLE hContact = (HANDLE) lParam;
 +	if (hContact == NULL)
 +	{
 +		// User dialog
 +		OPTIONSDIALOGPAGE odp = {0};
 +		odp.cbSize = sizeof(odp);
 +		odp.hIcon = g_hIcon;
 +		odp.hInstance = g_hInst;
 +		odp.pfnDlgProc = DlgProcAvatarProtoInfo;
 +		odp.pszTemplate = MAKEINTRESOURCEA(IDD_PROTO_AVATARS);
 +		odp.pszTitle = LPGEN("Avatar");
 +		UserInfo_AddPage(wParam, &odp);
 +	}
 +	else
 +	{
 +		char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +		if (szProto == NULL || DBGetContactSettingByte(NULL, AVS_MODULE, szProto, 1))
 +		{
 +			// Contact dialog
 +			OPTIONSDIALOGPAGE odp = {0};
 +			odp.cbSize = sizeof(odp);
 +			odp.hIcon = g_hIcon;
 +			odp.hInstance = g_hInst;
 +			odp.pfnDlgProc = DlgProcAvatarUserInfo;
 +			odp.position = -2000000000;
 +			odp.pszTemplate = MAKEINTRESOURCEA(IDD_USER_AVATAR);
 +			odp.pszTitle = LPGEN("Avatar");
 +			UserInfo_AddPage(wParam, &odp);
 +		}
 +	}
 +	return 0;
 +}
 +
 +static int LoadAvatarModule()
 +{
 +	mir_getLP(&pluginInfoEx);
 +
 +	init_mir_thread();
 +
 +	InitializeCriticalSection(&cachecs);
 +	InitializeCriticalSection(&alloccs);
 +
 +	hOptInit = HookEvent(ME_OPT_INITIALISE, OptInit);
 +	hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
 +	hContactSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, ContactSettingChanged);
 +	hEventDeleted = HookEvent(ME_DB_CONTACT_DELETED, ContactDeleted);
 +	hProtoAckHook = HookEvent(ME_PROTO_ACK, ProtocolAck);
 +
 +	arServices.insert( CreateServiceFunction( MS_AV_GETAVATARBITMAP, GetAvatarBitmap ));
 +	arServices.insert( CreateServiceFunction( MS_AV_PROTECTAVATAR, ProtectAvatar ));
 +	arServices.insert( CreateServiceFunction( MS_AV_SETAVATAR, SetAvatar ));
 +	arServices.insert( CreateServiceFunction( MS_AV_SETMYAVATAR, SetMyAvatar ));
 +	arServices.insert( CreateServiceFunction( MS_AV_CANSETMYAVATAR, CanSetMyAvatar ));
 +	arServices.insert( CreateServiceFunction( MS_AV_CONTACTOPTIONS, ContactOptions ));
 +	arServices.insert( CreateServiceFunction( MS_AV_DRAWAVATAR, DrawAvatarPicture ));
 +	arServices.insert( CreateServiceFunction( MS_AV_GETMYAVATAR, GetMyAvatar ));
 +	arServices.insert( CreateServiceFunction( MS_AV_REPORTMYAVATARCHANGED, ReportMyAvatarChanged ));
 +	arServices.insert( CreateServiceFunction( MS_AV_LOADBITMAP32, BmpFilterLoadBitmap32 ));
 +	arServices.insert( CreateServiceFunction( MS_AV_SAVEBITMAP, BmpFilterSaveBitmap ));
 +	arServices.insert( CreateServiceFunction( MS_AV_CANSAVEBITMAP, BmpFilterCanSaveBitmap ));
 +	arServices.insert( CreateServiceFunction( MS_AV_RESIZEBITMAP, BmpFilterResizeBitmap ));
 +	arServices.insert( CreateServiceFunction( MS_AV_SETAVATARW, SetAvatarW ));
 +	arServices.insert( CreateServiceFunction( MS_AV_SETMYAVATARW, SetMyAvatarW ));
 +
 +	hEventChanged = CreateHookableEvent(ME_AV_AVATARCHANGED);
 +	hEventContactAvatarChanged = CreateHookableEvent(ME_AV_CONTACTAVATARCHANGED);
 +	hMyAvatarChanged = CreateHookableEvent(ME_AV_MYAVATARCHANGED);
 +
 +	AllocCacheBlock();
 +
 +	HMODULE hDll;
 +	if (hDll = GetModuleHandle(_T("gdi32")))
 +		AvsAlphaBlend = (BOOL (WINAPI *)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION)) GetProcAddress(hDll, "GdiAlphaBlend");
 +	if (AvsAlphaBlend == NULL && (hDll = LoadLibrary(_T("msimg32.dll"))))
 +		AvsAlphaBlend = (BOOL (WINAPI *)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION)) GetProcAddress(hDll, "AlphaBlend");
 +
 +	TCHAR* tmpPath = Utils_ReplaceVarsT(_T("%miranda_userdata%"));
 +	lstrcpyn(g_szDataPath, tmpPath, SIZEOF(g_szDataPath)-1);
 +	mir_free(tmpPath);
 +	g_szDataPath[MAX_PATH - 1] = 0;
 +	_tcslwr(g_szDataPath);
 +
 +	return 0;
 +}
 +
 +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD dwReason, LPVOID reserved)
 +{
 +	g_hInst = hInstDLL;
 +	return TRUE;
 +}
 +
 +extern "C" __declspec(dllexport) PLUGININFOEX * MirandaPluginInfoEx(DWORD mirandaVersion)
 +{
 +	return &pluginInfoEx;
 +}
 +
 +static const MUUID interfaces[] = { { 0xece29554, 0x1cf0, 0x41da, { 0x85, 0x82, 0xfb, 0xe8, 0x45, 0x5c, 0x6b, 0xec } }, MIID_LAST};
 +extern "C" __declspec(dllexport) const MUUID * MirandaPluginInterfaces(void)
 +{
 +	return interfaces;
 +}
 +
 +extern "C" int __declspec(dllexport) Load(void)
 +{
 +	INT_PTR result = CALLSERVICE_NOTFOUND;
 +
 +	mir_getLP( &pluginInfoEx );
 +
 +	if (ServiceExists(MS_IMG_GETINTERFACE))
 +		result = CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM)&fei);
 +
 +	if (fei == NULL || result != S_OK) {
 +		MessageBox(0, TranslateT("Fatal error, image services not found. Avatar services will be disabled."), TranslateT("Avatar Service"), MB_OK);
 +		return 1;
 +	}
 +	LoadACC();
 +	return LoadAvatarModule();
 +}
 +
 +extern "C" int __declspec(dllexport) Unload(void)
 +{
 +	struct CacheNode *pNode = g_Cache;
 +
 +	while(pNode) {
 +		//DeleteCriticalSection(&pNode->cs);
 +		if (pNode->ace.hbmPic != 0)
 +			DeleteObject(pNode->ace.hbmPic);
 +		pNode = pNode->pNextNode;
 +	}
 +
 +	for(int i = 0; i < g_curBlock; i++)
 +		free(g_cacheBlocks[i]);
 +	free(g_cacheBlocks);
 +
 +	g_ProtoPictures.destroy();
 +	g_MyAvatars.destroy();
 +
 +	CloseHandle(hLoaderEvent);
 +	DeleteCriticalSection(&alloccs);
 +	DeleteCriticalSection(&cachecs);
 +	return 0;
 +}
 +
 +///////////////////////////////////////////////////////////////////////////////////////////////////
 +
 +protoPicCacheEntry::~protoPicCacheEntry()
 +{
 +	if ( hbmPic != 0 )
 +		DeleteObject( hbmPic );
 +	mir_free( szProtoname );
 +	mir_free( tszAccName );
 +}
 +
 +void protoPicCacheEntry::clear()
 +{
 +	if (hbmPic != 0)
 +		DeleteObject(hbmPic);
 +
 +	memset(this, 0, sizeof(avatarCacheEntry));
 +}
 +
 +/*
 +wParam=(int *)max width of avatar - will be set (-1 for no max)
 +lParam=(int *)max height of avatar - will be set (-1 for no max)
 +return=0 for sucess
 +*/
 +#define PS_GETMYAVATARMAXSIZE "/GetMyAvatarMaxSize"
 +
 +/*
 +wParam=0
 +lParam=0
 +return=One of PIP_SQUARE, PIP_FREEPROPORTIONS
 +*/
 +#define PIP_FREEPROPORTIONS	0
 +#define PIP_SQUARE			1
 +#define PS_GETMYAVATARIMAGEPROPORTION "/GetMyAvatarImageProportion"
 +
 +/*
 +wParam = 0
 +lParam = PA_FORMAT_*   // avatar format
 +return = 1 (supported) or 0 (not supported)
 +*/
 +#define PS_ISAVATARFORMATSUPPORTED "/IsAvatarFormatSupported"
 +
 +
 +
 +BOOL Proto_IsAvatarsEnabled(const char *proto)
 +{
 +	if (ProtoServiceExists(proto, PS_GETAVATARCAPS))
 +		return CallProtoService(proto, PS_GETAVATARCAPS, AF_ENABLED, 0);
 +
 +	return TRUE;
 +}
 +
 +BOOL Proto_IsAvatarFormatSupported(const char *proto, int format)
 +{
 +	if (ProtoServiceExists(proto, PS_GETAVATARCAPS))
 +		return CallProtoService(proto, PS_GETAVATARCAPS, AF_FORMATSUPPORTED, format);
 +
 +	if (ProtoServiceExists(proto, PS_ISAVATARFORMATSUPPORTED))
 +		return CallProtoService(proto, PS_ISAVATARFORMATSUPPORTED, 0, format);
 +
 +	if (format >= PA_FORMAT_SWF)
 +		return FALSE;
 +
 +	return TRUE;
 +}
 +
 +int Proto_AvatarImageProportion(const char *proto)
 +{
 +	if (ProtoServiceExists(proto, PS_GETAVATARCAPS))
 +		return CallProtoService(proto, PS_GETAVATARCAPS, AF_PROPORTION, 0);
 +
 +	if (ProtoServiceExists(proto, PS_GETMYAVATARIMAGEPROPORTION))
 +		return CallProtoService(proto, PS_GETMYAVATARIMAGEPROPORTION, 0, 0);
 +
 +	return 0;
 +}
 +
 +void Proto_GetAvatarMaxSize(const char *proto, int *width, int *height)
 +{
 +	if (ProtoServiceExists(proto, PS_GETAVATARCAPS))
 +	{
 +		POINT maxSize;
 +		CallProtoService(proto, PS_GETAVATARCAPS, AF_MAXSIZE, (LPARAM) &maxSize);
 +		*width = maxSize.y;
 +		*height = maxSize.x;
 +	}
 +	else if (ProtoServiceExists(proto, PS_GETMYAVATARMAXSIZE))
 +	{
 +		CallProtoService(proto, PS_GETMYAVATARMAXSIZE, (WPARAM) width, (LPARAM) height);
 +	}
 +	else
 +	{
 +		*width = 300;
 +		*height = 300;
 +	}
 +
 +	if (*width < 0)
 +		*width = 0;
 +	else if (*width > 300)
 +		*width = 300;
 +
 +	if (*height < 0)
 +		*height = 0;
 +	else if (*height > 300)
 +		*height = 300;
 +}
 +
 +BOOL Proto_NeedDelaysForAvatars(const char *proto)
 +{
 +	if (ProtoServiceExists(proto, PS_GETAVATARCAPS))
 +	{
 +		int ret = CallProtoService(proto, PS_GETAVATARCAPS, AF_DONTNEEDDELAYS, 0);
 +		if (ret > 0)
 +			return FALSE;
 +		else
 +			return TRUE;
 +	}
 +
 +	return TRUE;
 +}
 +
 +int Proto_GetAvatarMaxFileSize(const char *proto)
 +{
 +	if (ProtoServiceExists(proto, PS_GETAVATARCAPS))
 +		return CallProtoService(proto, PS_GETAVATARCAPS, AF_MAXFILESIZE, 0);
 +
 +	return 0;
 +}
 +
 +int Proto_GetDelayAfterFail(const char *proto)
 +{
 +	if (ProtoServiceExists(proto, PS_GETAVATARCAPS))
 +		return CallProtoService(proto, PS_GETAVATARCAPS, AF_DELAYAFTERFAIL, 0);
 +
 +	return 0;
 +}
 +
 +BOOL Proto_IsFetchingAlwaysAllowed(const char *proto)
 +{
 +	if (ProtoServiceExists(proto, PS_GETAVATARCAPS))
 +		return CallProtoService(proto, PS_GETAVATARCAPS, AF_FETCHALWAYS, 0);
 +
 +	return FALSE;
 +}
 diff --git a/plugins/AVS/src/mir_thread.cpp b/plugins/AVS/src/mir_thread.cpp new file mode 100644 index 0000000000..d12a0c9731 --- /dev/null +++ b/plugins/AVS/src/mir_thread.cpp @@ -0,0 +1,50 @@ +/*
 +Copyright (C) 2005 Ricardo Pescuma Domenecci
 +
 +This is free software; you can redistribute it and/or
 +modify it under the terms of the GNU Library General Public
 +License as published by the Free Software Foundation; either
 +version 2 of the License, or (at your option) any later version.
 +
 +This 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
 +Library General Public License for more details.
 +
 +You should have received a copy of the GNU Library General Public
 +License along with this file; see the file license.txt.  If
 +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 +Boston, MA 02111-1307, USA.  
 +*/
 +
 +
 +#include "commonheaders.h"
 +
 +
 +BOOL g_shutDown = FALSE;
 +static HANDLE hShutdownEvent = NULL;
 +static HANDLE hOkToExit = NULL;
 +
 +
 +static int OkToExitProc(WPARAM wParam, LPARAM lParam)
 +{
 +	g_shutDown = TRUE;
 +	SetEvent(hShutdownEvent);
 +	CloseHandle(hShutdownEvent);
 +	UnhookEvent(hOkToExit);
 +	return 0;
 +}
 +
 +
 +void init_mir_thread() 
 +{
 +	hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 +	hOkToExit = HookEvent(ME_SYSTEM_OKTOEXIT, OkToExitProc);
 +}
 +
 +
 +void mir_sleep(int time) 
 +{
 +	if (!g_shutDown)
 +		WaitForSingleObject(hShutdownEvent, time);
 +}
 diff --git a/plugins/AVS/src/mir_thread.h b/plugins/AVS/src/mir_thread.h new file mode 100644 index 0000000000..0f4f20fe8e --- /dev/null +++ b/plugins/AVS/src/mir_thread.h @@ -0,0 +1,49 @@ +/* 
 +Copyright (C) 2005 Ricardo Pescuma Domenecci
 +
 +This is free software; you can redistribute it and/or
 +modify it under the terms of the GNU Library General Public
 +License as published by the Free Software Foundation; either
 +version 2 of the License, or (at your option) any later version.
 +
 +This 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
 +Library General Public License for more details.
 +
 +You should have received a copy of the GNU Library General Public
 +License along with this file; see the file license.txt.  If
 +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 +Boston, MA 02111-1307, USA.  
 +*/
 +
 +
 +#ifndef __MIR_THREAD_H__
 +# define __MIR_THREAD_H__
 +
 +#include <windows.h>
 +
 +#ifdef __cplusplus
 +extern "C" 
 +{
 +#endif
 +
 +void init_mir_thread();
 +
 +
 +// This variable need to be constantly checked against and the thread function must exit
 +// when this is true
 +extern BOOL g_shutDown;
 +
 +void mir_sleep(int time);
 +
 +
 +
 +
 +
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif // __MIR_THREAD_H__
 diff --git a/plugins/AVS/src/options.cpp b/plugins/AVS/src/options.cpp new file mode 100644 index 0000000000..58502a9655 --- /dev/null +++ b/plugins/AVS/src/options.cpp @@ -0,0 +1,1147 @@ +/*
 +
 +Miranda IM: the free IM client for Microsoft* Windows*
 +
 +Copyright 2000-2004 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.
 +*/
 +
 +#include "commonheaders.h"
 +
 +#define DM_SETAVATARNAME (WM_USER + 10)
 +#define DM_REALODAVATAR (WM_USER + 11)
 +#define DM_AVATARCHANGED (WM_USER + 12)
 +#define DM_PROTOCOLCHANGED (WM_USER + 13)
 +
 +extern int _DebugPopup(HANDLE hContact, const char *fmt, ...);
 +extern INT_PTR SetAvatar(WPARAM wParam, LPARAM lParam);
 +extern OBJLIST<protoPicCacheEntry> g_ProtoPictures;
 +extern HANDLE hEventChanged;
 +extern HINSTANCE g_hInst;
 +extern HICON g_hIcon;
 +
 +extern int CreateAvatarInCache(HANDLE hContact, struct avatarCacheEntry *ace, char *szProto);
 +extern INT_PTR ProtectAvatar(WPARAM wParam, LPARAM lParam);
 +extern int SetAvatarAttribute(HANDLE hContact, DWORD attrib, int mode);
 +extern int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist = FALSE, int pa_format = 0);
 +extern void DeleteAvatarFromCache(HANDLE, BOOL);
 +extern HBITMAP LoadPNG(struct avatarCacheEntry *ace, char *szFilename);
 +extern HANDLE GetContactThatHaveTheAvatar(HANDLE hContact, int locked = -1);
 +
 +extern int ProtoServiceExists(const char *szModule,const char *szService);
 +extern BOOL Proto_IsAvatarsEnabled(const char *proto);
 +extern BOOL ScreenToClient(HWND hWnd, LPRECT lpRect);
 +
 +static BOOL dialoginit = TRUE;
 +
 +struct WindowData {
 +	HANDLE hContact;
 +	HANDLE hHook;
 +};
 +
 +static void RemoveProtoPic(const char *szProto)
 +{
 +	DBDeleteContactSetting(NULL, PPICT_MODULE, szProto);
 +
 +	if ( szProto == NULL )
 +		return;
 +
 +	if ( !lstrcmpA(AVS_DEFAULT, szProto )) {
 +		for (int i = 0; i < g_ProtoPictures.getCount(); i++) {
 +			protoPicCacheEntry& p = g_ProtoPictures[i];
 +			if (p.szProtoname == NULL)
 +				continue;
 +
 +			p.clear();
 +			CreateAvatarInCache(0, &p, ( char* )p.szProtoname);
 +			NotifyEventHooks(hEventChanged, 0, (LPARAM)&p);
 +		}
 +		return;
 +	}
 +	
 +	if (strstr(szProto, "Global avatar for")) {
 +		char szProtoname[MAX_PATH] = {0};
 +		lstrcpynA(szProtoname, szProto, lstrlenA(szProto)- lstrlenA("accounts"));
 +		lstrcpyA(szProtoname, strrchr(szProtoname, ' ') + 1);
 +		for (int i = 0; i < g_ProtoPictures.getCount(); i++) {
 +			protoPicCacheEntry& p = g_ProtoPictures[i];
 +
 +			PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)p.szProtoname);
 +			if (pdescr == NULL && lstrcmpA(p.szProtoname, szProto))
 +				continue;
 +			
 +			if (!lstrcmpA(p.szProtoname, szProto) || !lstrcmpA(pdescr->szProtoName, szProtoname)) {
 +				if (p.szProtoname == NULL)
 +					continue;
 +
 +				p.clear();
 +				CreateAvatarInCache(0, &p, ( char* )p.szProtoname);
 +				NotifyEventHooks( hEventChanged, 0, (LPARAM)&p );
 +			}
 +		}
 +		return;
 +	}
 +
 +	for (int i = 0; i < g_ProtoPictures.getCount(); i++) {
 +		protoPicCacheEntry& p = g_ProtoPictures[i];
 +		if ( !lstrcmpA( p.szProtoname, szProto )) {
 +			p.clear();
 +			NotifyEventHooks( hEventChanged, 0, (LPARAM)&p );
 +		}
 +	}
 +}
 +
 +static void SetProtoPic(char *szProto)
 +{
 +	TCHAR FileName[MAX_PATH];
 +	OPENFILENAME ofn={0};
 +	TCHAR filter[256];
 +
 +	filter[0] = '\0';
 +	CallService(MS_UTILS_GETBITMAPFILTERSTRINGST, SIZEOF(filter), ( LPARAM )filter);
 +
 +	ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
 +	ofn.lpstrFilter = filter;
 +	ofn.hwndOwner=0;
 +	ofn.lpstrFile = FileName;
 +	ofn.nMaxFile = MAX_PATH;
 +	ofn.nMaxFileTitle = MAX_PATH;
 +	ofn.Flags=OFN_HIDEREADONLY;
 +	ofn.lpstrInitialDir = _T(".");
 +	*FileName = '\0';
 +	ofn.lpstrDefExt = _T("");
 +	if ( GetOpenFileName( &ofn )) {
 +		HANDLE hFile;
 +
 +		if ((hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
 +			return;
 +
 +		CloseHandle(hFile);
 +
 +		TCHAR szNewPath[MAX_PATH];
 +		AVS_pathToRelative(FileName, szNewPath);
 +		DBWriteContactSettingTString(NULL, PPICT_MODULE, szProto, szNewPath);
 +
 +		if (!lstrcmpA(AVS_DEFAULT, szProto)) {
 +			for ( int i = 0; i < g_ProtoPictures.getCount(); i++ ) {
 +				protoPicCacheEntry& p = g_ProtoPictures[i];
 +				if (lstrlenA(p.szProtoname) != 0) {
 +					if (p.hbmPic == 0) {
 +						CreateAvatarInCache(0, &p, ( char* )szProto);
 +						NotifyEventHooks(hEventChanged, 0, (LPARAM)&p);
 +					}
 +				}
 +			}
 +		}
 +		else if (strstr(szProto, "Global avatar for")) {
 +			char szProtoname[MAX_PATH] = {0};
 +			lstrcpynA(szProtoname, szProto, lstrlenA(szProto)- lstrlenA("accounts"));
 +			lstrcpyA(szProtoname, strrchr(szProtoname, ' ') + 1);
 +			for (int i = 0; i < g_ProtoPictures.getCount(); i++) {
 +				PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)g_ProtoPictures[i].szProtoname);
 +				if (pdescr == NULL && lstrcmpA(g_ProtoPictures[i].szProtoname, szProto))
 +					continue;
 +
 +				if (!lstrcmpA(g_ProtoPictures[i].szProtoname, szProto) || !lstrcmpA(pdescr->szProtoName, szProtoname)) {
 +					protoPicCacheEntry& p = g_ProtoPictures[i];
 +					if (lstrlenA(p.szProtoname) != 0) {
 +						if (p.hbmPic == 0) {
 +							CreateAvatarInCache(0, &p, ( char* )szProto);
 +							NotifyEventHooks(hEventChanged, 0, (LPARAM)&p);
 +						}
 +					}
 +				}
 +			}
 +		}
 +		else {
 +			for (int i = 0; i < g_ProtoPictures.getCount(); i++) {
 +				protoPicCacheEntry& p = g_ProtoPictures[i];
 +				if ( lstrlenA(p.szProtoname) == 0)
 +					break;
 +
 +				if (!strcmp(p.szProtoname, szProto) && lstrlenA(p.szProtoname) == lstrlenA(szProto)) {
 +					if (p.hbmPic != 0) 
 +						DeleteObject(p.hbmPic);
 +					ZeroMemory(&p, sizeof(avatarCacheEntry));
 +					CreateAvatarInCache(0, &p, ( char* )szProto);
 +					NotifyEventHooks(hEventChanged, 0, (LPARAM)&p );
 +					break;
 +				}
 +			}
 +		}
 +	}
 +}
 +
 +static char* g_selectedProto;
 +
 +INT_PTR CALLBACK DlgProcOptionsAvatars(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 +{
 +	switch (msg) {
 +	case WM_INITDIALOG:
 +		TranslateDialogDefault(hwndDlg);
 +
 +		CheckDlgButton(hwndDlg, IDC_SHOWWARNINGS, DBGetContactSettingByte(0, AVS_MODULE, "warnings", 0));
 +		CheckDlgButton(hwndDlg, IDC_MAKE_GRAYSCALE, DBGetContactSettingByte(0, AVS_MODULE, "MakeGrayscale", 0));
 +		CheckDlgButton(hwndDlg, IDC_MAKE_TRANSPARENT_BKG, DBGetContactSettingByte(0, AVS_MODULE, "MakeTransparentBkg", 0));
 +		CheckDlgButton(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL, DBGetContactSettingByte(0, AVS_MODULE, "MakeTransparencyProportionalToColorDiff", 0));
 +
 +		SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETRANGE, 0, MAKELONG(8, 2));
 +		SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, (LPARAM)DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgNumPoints", 5));
 +
 +		SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
 +		SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, (LPARAM)DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgColorDiff", 10));
 +		{
 +			BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), enabled);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), enabled);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), enabled);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), enabled);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), enabled);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), enabled);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), enabled);
 +		}
 +
 +		return TRUE;
 +
 +	case WM_COMMAND:
 +		if ((LOWORD(wParam) == IDC_BKG_NUM_POINTS || LOWORD(wParam) == IDC_BKG_COLOR_DIFFERENCE)
 +			&& (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus()))
 +			return FALSE;
 +		SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
 +		break;
 +
 +	case WM_NOTIFY:
 +		switch (((LPNMHDR) lParam)->idFrom) {
 +		case IDC_MAKE_TRANSPARENT_BKG:
 +			{
 +				BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), transp_enabled);
 +				break;
 +			}
 +		case 0:
 +			switch (((LPNMHDR) lParam)->code) {
 +			case PSN_APPLY:
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "warnings", IsDlgButtonChecked(hwndDlg, IDC_SHOWWARNINGS) ? 1 : 0);
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "MakeGrayscale", IsDlgButtonChecked(hwndDlg, IDC_MAKE_GRAYSCALE) ? 1 : 0);
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "MakeTransparentBkg", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG) ? 1 : 0);
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "MakeTransparencyProportionalToColorDiff", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL) ? 1 : 0);
 +				DBWriteContactSettingWord(NULL, AVS_MODULE, "TranspBkgNumPoints", (WORD) SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0));
 +				DBWriteContactSettingWord(NULL, AVS_MODULE, "TranspBkgColorDiff", (WORD) SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0));
 +		}	}
 +		break;
 +	}
 +	return FALSE;
 +}
 +
 +INT_PTR CALLBACK DlgProcOptionsOwn(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 +{
 +	switch (msg) {
 +	case WM_INITDIALOG:
 +		TranslateDialogDefault(hwndDlg);
 +
 +		CheckDlgButton(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP, DBGetContactSettingByte(0, AVS_MODULE, "MakeMyAvatarsTransparent", 0));
 +		CheckDlgButton(hwndDlg, IDC_SET_MAKE_SQUARE, DBGetContactSettingByte(0, AVS_MODULE, "SetAllwaysMakeSquare", 0));
 +
 +		return TRUE;
 +
 +	case WM_COMMAND:
 +		SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
 +		break;
 +
 +	case WM_NOTIFY:
 +		switch (((LPNMHDR) lParam)->idFrom) {
 +		case 0:
 +			switch (((LPNMHDR) lParam)->code) {
 +			case PSN_APPLY:
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "MakeMyAvatarsTransparent", IsDlgButtonChecked(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP) ? 1 : 0);
 +				DBWriteContactSettingByte(NULL, AVS_MODULE, "SetAllwaysMakeSquare", IsDlgButtonChecked(hwndDlg, IDC_SET_MAKE_SQUARE) ? 1 : 0);
 +		}	}
 +		break;
 +	}
 +	return FALSE;
 +}
 +
 +static char* GetProtoFromList(HWND hwndDlg, int iItem)
 +{
 +	LVITEM item;
 +	item.mask = LVIF_PARAM;
 +	item.iItem = iItem;
 +	if ( !ListView_GetItem( GetDlgItem(hwndDlg, IDC_PROTOCOLS), &item ))
 +		return NULL;
 +
 +	protoPicCacheEntry* pce = ( protoPicCacheEntry* )item.lParam;
 +	return ( pce == NULL ) ? NULL : pce->szProtoname;
 +}
 +
 +INT_PTR CALLBACK DlgProcOptionsProtos(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 +{
 +	HWND hwndList = GetDlgItem(hwndDlg, IDC_PROTOCOLS);
 +	HWND hwndChoosePic = GetDlgItem(hwndDlg, IDC_SETPROTOPIC);
 +	HWND hwndRemovePic = GetDlgItem(hwndDlg, IDC_REMOVEPROTOPIC);
 +
 +	switch (msg) {
 +	case WM_INITDIALOG:
 +		{
 +			LVITEM item = {0};
 +			LVCOLUMN lvc = {0};
 +			UINT64 newItem = 0;
 +
 +			dialoginit = TRUE;
 +			TranslateDialogDefault(hwndDlg);
 +			ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES);
 +			lvc.mask = LVCF_FMT;
 +			lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT;
 +			ListView_InsertColumn(hwndList, 0, &lvc);
 +
 +			item.mask = LVIF_TEXT | LVIF_PARAM;
 +			item.iItem = 1000;
 +			for (int i = 0; i < g_ProtoPictures.getCount(); i++ ) {
 +				item.lParam = ( LPARAM )&g_ProtoPictures[i];
 +				item.pszText = g_ProtoPictures[i].tszAccName;
 +				newItem = ListView_InsertItem(hwndList, &item);
 +				if (newItem >= 0)
 +					ListView_SetCheckState(hwndList, newItem, 
 +						DBGetContactSettingByte(NULL, AVS_MODULE, g_ProtoPictures[i].szProtoname, 1) ? TRUE : FALSE);
 +			}
 +			ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
 +			ListView_Arrange(hwndList, LVA_ALIGNLEFT | LVA_ALIGNTOP);
 +			EnableWindow(hwndChoosePic, FALSE);
 +			EnableWindow(hwndRemovePic, FALSE);
 +
 +			dialoginit = FALSE;
 +		}
 +		return TRUE;
 +
 +	case WM_COMMAND:
 +		switch(LOWORD(wParam)) {
 +		case IDC_SETPROTOPIC:
 +		case IDC_REMOVEPROTOPIC:
 +			{
 +				int iItem = ListView_GetSelectionMark(hwndList);
 +				char* szProto = GetProtoFromList(hwndDlg, iItem);
 +				if ( szProto ) {
 +					if (LOWORD(wParam) == IDC_SETPROTOPIC)
 +						SetProtoPic( szProto );
 +					else
 +						RemoveProtoPic( szProto );
 +
 +					NMHDR nm = { hwndList, IDC_PROTOCOLS, NM_CLICK };
 +					SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nm);
 +				}
 +				break;
 +			}
 +		}
 +		break;
 +	
 +	case WM_DRAWITEM:
 +		{
 +			LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT) lParam;
 +
 +			if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) {
 +				AVATARDRAWREQUEST avdrq = {0};
 +				avdrq.cbSize = sizeof(avdrq);
 +				avdrq.hTargetDC = dis->hDC;
 +				avdrq.dwFlags |= AVDRQ_PROTOPICT;
 +				avdrq.szProto = g_selectedProto;
 +				GetClientRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), &avdrq.rcDraw);
 +				CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq);
 +			}
 +			return TRUE;
 +		}
 +
 +	case WM_NOTIFY:
 +		if (dialoginit)
 +			break;
 +
 +		switch (((LPNMHDR) lParam)->idFrom) {
 +		case IDC_PROTOCOLS:
 +			switch (((LPNMHDR) lParam)->code) {
 +			case LVN_KEYDOWN:
 +				{
 +					NMLVKEYDOWN* ptkd = (NMLVKEYDOWN*)lParam;
 +					if (ptkd&&ptkd->wVKey==VK_SPACE&&ListView_GetSelectedCount(ptkd->hdr.hwndFrom)==1)
 +						SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
 +				}
 +				break;
 +			case LVN_ITEMCHANGED:
 +				{
 +					NMLISTVIEW *nmlv = (NMLISTVIEW *)lParam;						
 +					if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_PROTOCOLS)) && ((nmlv->uNewState ^ nmlv->uOldState) & LVIS_STATEIMAGEMASK))
 +						SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
 +				}
 +				break;
 +			case NM_CLICK:
 +				{
 +					EnableWindow(hwndChoosePic, TRUE);
 +					EnableWindow(hwndRemovePic, TRUE);
 +
 +					int iItem = ListView_GetSelectionMark(hwndList);
 +					g_selectedProto = GetProtoFromList(hwndDlg, iItem);
 +					if ( g_selectedProto ) {
 +						DBVARIANT dbv = {0};
 +						if (!DBGetContactSettingTString(NULL, PPICT_MODULE, g_selectedProto, &dbv)) 
 +						{
 +							if (!AVS_pathIsAbsolute(dbv.ptszVal))
 +							{
 +								TCHAR szFinalPath[MAX_PATH];
 +								mir_sntprintf(szFinalPath, SIZEOF(szFinalPath), _T("%%miranda_path%%\\%s"), dbv.ptszVal);
 +								SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, szFinalPath);
 +							}
 +							else SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, dbv.ptszVal);
 +
 +							InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), NULL, TRUE);
 +							DBFreeVariant(&dbv);
 +						}
 +						else {
 +							SetWindowText(GetDlgItem(hwndDlg, IDC_PROTOAVATARNAME), _T(""));
 +							InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), NULL, TRUE);
 +						}
 +					}
 +					break;
 +				}
 +			}
 +			break;
 +		case 0:
 +			switch (((LPNMHDR) lParam)->code) {
 +			case PSN_APPLY:
 +				{
 +					for (int i = 0; i < ListView_GetItemCount(hwndList); i++) {
 +						char *szProto = GetProtoFromList(hwndDlg, i);
 +
 +						BOOL oldVal = DBGetContactSettingByte(NULL, AVS_MODULE, szProto, 1);
 +						BOOL newVal = ListView_GetCheckState(hwndList, i);
 +
 +						if (oldVal && !newVal)
 +						{
 +							HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
 +							while (hContact != NULL) 
 +							{
 +								char* szContactProto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
 +								if (szContactProto != NULL && !strcmp(szContactProto, szProto)) 
 +									DeleteAvatarFromCache(hContact, TRUE);
 +
 +								hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
 +							}
 +						}
 +
 +						if (newVal)
 +							DBWriteContactSettingByte(NULL, AVS_MODULE, szProto, 1);
 +						else
 +							DBWriteContactSettingByte(NULL, AVS_MODULE, szProto, 0);
 +					}
 +				}
 +			}
 +		}
 +		break;
 +	}
 +	return FALSE;
 +}
 +
 +void LoadTransparentData(HWND hwndDlg, HANDLE hContact)
 +{
 +	CheckDlgButton(hwndDlg, IDC_MAKETRANSPBKG, DBGetContactSettingByte(hContact, "ContactPhoto", "MakeTransparentBkg", DBGetContactSettingByte(0, AVS_MODULE, "MakeTransparentBkg", 0)));
 +	SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, (LPARAM)DBGetContactSettingWord(hContact, "ContactPhoto", "TranspBkgNumPoints", DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgNumPoints", 5)));
 +	SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, (LPARAM)DBGetContactSettingWord(hContact, "ContactPhoto", "TranspBkgColorDiff", DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgColorDiff", 10)));
 +
 +	BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
 +	EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled);
 +	EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled);
 +	EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled);
 +	EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled);
 +	EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled);
 +	EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled);
 +}
 +
 +void SaveTransparentData(HWND hwndDlg, HANDLE hContact)
 +{
 +	BOOL transp = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
 +	if (DBGetContactSettingByte(0, AVS_MODULE, "MakeTransparentBkg", 0) == transp)
 +		DBDeleteContactSetting(hContact, "ContactPhoto", "MakeTransparentBkg");
 +	else
 +		DBWriteContactSettingByte(hContact, "ContactPhoto", "MakeTransparentBkg", transp);
 +
 +	WORD tmp = (WORD) SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0);
 +	if (DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgNumPoints", 5) == tmp)
 +		DBDeleteContactSetting(hContact, "ContactPhoto", "TranspBkgNumPoints");
 +	else
 +		DBWriteContactSettingWord(hContact, "ContactPhoto", "TranspBkgNumPoints", tmp);
 +
 +	tmp = (WORD) SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0);
 +	if (DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgColorDiff", 10) == tmp)
 +		DBDeleteContactSetting(hContact, "ContactPhoto", "TranspBkgColorDiff");
 +	else
 +		DBWriteContactSettingWord(hContact, "ContactPhoto", "TranspBkgColorDiff", tmp);
 +}
 +
 +void SaveTransparentData(HWND hwndDlg, HANDLE hContact, BOOL locked)
 +{
 +	SaveTransparentData(hwndDlg, hContact);
 +
 +	HANDLE tmp = GetContactThatHaveTheAvatar(hContact, locked);
 +	if (tmp != hContact)
 +		SaveTransparentData(hwndDlg, tmp);
 +}
 +
 +INT_PTR CALLBACK DlgProcAvatarOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 +{
 +	HANDLE hContact;
 +	struct WindowData *dat = (struct WindowData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
 +
 +	if (dat)
 +		hContact = dat->hContact;
 +
 +	switch(msg) {
 +	case WM_INITDIALOG:
 +		{
 +			TCHAR szTitle[512];
 +			TCHAR *szNick = NULL;
 +			struct WindowData *dat = (struct WindowData *)malloc(sizeof(struct WindowData));
 +
 +			if (dat)
 +				dat->hContact = (HANDLE)lParam;
 +
 +			SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
 +			hContact = (HANDLE)lParam;
 +			TranslateDialogDefault(hwndDlg);
 +			if (hContact) {
 +				szNick = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR);
 +				mir_sntprintf(szTitle, 500, TranslateT("Set avatar options for %s"), szNick);
 +				SetWindowText(hwndDlg, szTitle);
 +			}
 +			SendMessage(hwndDlg, DM_SETAVATARNAME, 0, 0);
 +			ShowWindow(hwndDlg, SW_SHOWNORMAL);
 +			InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), NULL, FALSE);
 +			CheckDlgButton(hwndDlg, IDC_PROTECTAVATAR, DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0) ? TRUE : FALSE);
 +			CheckDlgButton(hwndDlg, IDC_HIDEAVATAR, DBGetContactSettingByte(hContact, "ContactPhoto", "Hidden", 0) ? TRUE : FALSE);
 +
 +			SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETBUDDY, (WPARAM)GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), 0);
 +			SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETRANGE, 0, MAKELONG(8, 2));
 +
 +			SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETBUDDY, (WPARAM)GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), 0);
 +			SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
 +
 +			LoadTransparentData(hwndDlg, GetContactThatHaveTheAvatar(hContact));
 +			dat->hHook = HookEventMessage(ME_AV_AVATARCHANGED, hwndDlg, DM_AVATARCHANGED);
 +			SendMessage(hwndDlg, WM_SETICON, IMAGE_ICON, (LPARAM)g_hIcon);
 +		}
 +		return TRUE;
 +
 +	case WM_COMMAND:
 +		switch(LOWORD(wParam)) {
 +		case ID_USE_DEFAULTS:
 +			hContact = GetContactThatHaveTheAvatar(hContact);
 +
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "MakeTransparentBkg");
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "TranspBkgNumPoints");
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "TranspBkgColorDiff");
 +
 +			LoadTransparentData(hwndDlg, hContact);
 +
 +			SendMessage(hwndDlg, DM_REALODAVATAR, 0, 0);
 +			break;
 +
 +		case IDOK:
 +			{
 +				BOOL locked = IsDlgButtonChecked(hwndDlg, IDC_PROTECTAVATAR);
 +				int hidden = IsDlgButtonChecked(hwndDlg, IDC_HIDEAVATAR) ? 1 : 0;
 +				SetAvatarAttribute(hContact, AVS_HIDEONCLIST, hidden);
 +				if (hidden != DBGetContactSettingByte(hContact, "ContactPhoto", "Hidden", 0))
 +					DBWriteContactSettingByte(hContact, "ContactPhoto", "Hidden", hidden);
 +
 +				if (!locked && DBGetContactSettingByte(hContact, "ContactPhoto", "NeedUpdate", 0))
 +					QueueAdd(hContact);
 +
 +				// Continue to the cancel handle
 +			}
 +
 +		case IDCANCEL:
 +			DestroyWindow(hwndDlg);
 +			break;
 +
 +		case IDC_PROTECTAVATAR:
 +			{
 +				BOOL locked = IsDlgButtonChecked(hwndDlg, IDC_PROTECTAVATAR);
 +				ProtectAvatar((WPARAM)hContact, locked ? 1 : 0);
 +			}
 +			break;
 +
 +		case IDC_CHANGE:
 +			SetAvatar((WPARAM)hContact, 0);
 +			SendMessage(hwndDlg, DM_SETAVATARNAME, 0, 0);
 +			CheckDlgButton(hwndDlg, IDC_PROTECTAVATAR, DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0) ? TRUE : FALSE);
 +			break;
 +
 +		case IDC_BKG_NUM_POINTS:
 +		case IDC_BKG_COLOR_DIFFERENCE:
 +			if (HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus())
 +				break;
 +
 +		case IDC_MAKETRANSPBKG:
 +			{
 +				BOOL enable = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), enable);
 +
 +				SendMessage(hwndDlg, DM_REALODAVATAR, 0, 0);
 +			}
 +			break;
 +
 +		case IDC_RESET:
 +			{
 +				char *szProto = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +				DBVARIANT dbv = {0};
 +
 +				ProtectAvatar((WPARAM)hContact, 0);
 +				if (MessageBox(0, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
 +					if (!DBGetContactSettingTString(hContact, "ContactPhoto", "File", &dbv)) {
 +						DeleteFile(dbv.ptszVal);
 +						DBFreeVariant(&dbv);
 +					}
 +				}
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Locked");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Backup");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "RFile");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "File");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Format");
 +				DBDeleteContactSetting(hContact, szProto, "AvatarHash");
 +				DBDeleteContactSetting(hContact, szProto, "AvatarSaved");
 +				DeleteAvatarFromCache(hContact, FALSE);
 +
 +				QueueAdd(hContact);
 +
 +				DestroyWindow(hwndDlg);
 +			}
 +			break;
 +
 +		case IDC_DELETE:
 +			{
 +				DBVARIANT dbv = {0};
 +				ProtectAvatar((WPARAM)hContact, 0);
 +				if (MessageBox(0, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
 +					if (!DBGetContactSettingTString(hContact, "ContactPhoto", "File", &dbv)) {
 +						DeleteFile(dbv.ptszVal);
 +						DBFreeVariant(&dbv);
 +					}
 +				}
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Locked");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Backup");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "RFile");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "File");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Format");
 +				DeleteAvatarFromCache(hContact, FALSE);
 +				SendMessage(hwndDlg, DM_SETAVATARNAME, 0, 0);
 +				InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), NULL, TRUE);
 +				break;
 +			}
 +		}
 +		break;
 +
 +	case WM_DRAWITEM:
 +		{
 +			LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT) lParam;
 +
 +			if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) {
 +				AVATARDRAWREQUEST avdrq = {0};
 +				GetClientRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), &avdrq.rcDraw);
 +
 +				FillRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNFACE));
 +
 +				avdrq.hContact = hContact;
 +				avdrq.cbSize = sizeof(avdrq);
 +				avdrq.hTargetDC = dis->hDC;
 +				avdrq.dwFlags |= AVDRQ_DRAWBORDER;
 +				avdrq.clrBorder = GetSysColor(COLOR_BTNTEXT);
 +				avdrq.radius = 6;
 +				if (!CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq)) 
 +				{
 +					// Get text rectangle
 +					RECT rc = avdrq.rcDraw;
 +					rc.top += 10;
 +					rc.bottom -= 10;
 +					rc.left += 10;
 +					rc.right -= 10;
 +
 +					// Calc text size
 +					RECT rc_ret = rc;
 +					DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc_ret, 
 +						DT_WORDBREAK | DT_NOPREFIX | DT_CENTER | DT_CALCRECT);
 +
 +					// Calc needed size
 +					rc.top += ((rc.bottom - rc.top) - (rc_ret.bottom - rc_ret.top)) / 2;
 +					rc.bottom = rc.top + (rc_ret.bottom - rc_ret.top);
 +					DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc, 
 +						DT_WORDBREAK | DT_NOPREFIX | DT_CENTER);
 +				}
 +
 +				FrameRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNSHADOW));
 +			}
 +			return TRUE;
 +		}
 +	case DM_SETAVATARNAME:
 +		{
 +			TCHAR szFinalName[MAX_PATH];
 +			DBVARIANT dbv = {0};
 +			BYTE is_locked = DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0);
 +
 +			szFinalName[0] = 0;
 +
 +			if (is_locked && !DBGetContactSettingTString(hContact, "ContactPhoto", "Backup", &dbv)) {
 +				AVS_pathToAbsolute(dbv.ptszVal, szFinalName);
 +				DBFreeVariant(&dbv);
 +			}
 +			else if (!DBGetContactSettingTString(hContact, "ContactPhoto", "RFile", &dbv)) {
 +				AVS_pathToAbsolute(dbv.ptszVal, szFinalName);
 +				DBFreeVariant(&dbv);
 +			}
 +			else if (!DBGetContactSettingTString(hContact, "ContactPhoto", "File", &dbv)) {
 +				AVS_pathToAbsolute(dbv.ptszVal, szFinalName);
 +				DBFreeVariant(&dbv);
 +			}
 +			szFinalName[MAX_PATH - 1] = 0;
 +			SetDlgItemText(hwndDlg, IDC_AVATARNAME, szFinalName);
 +			break;
 +		}
 +
 +	case DM_REALODAVATAR:
 +		SaveTransparentData(hwndDlg, hContact, IsDlgButtonChecked(hwndDlg, IDC_PROTECTAVATAR));
 +		ChangeAvatar(hContact, TRUE);
 +		InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), NULL, TRUE);
 +		break;
 +
 +	case DM_AVATARCHANGED:
 +		InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), NULL, TRUE);
 +		break;
 +
 +	case WM_NCDESTROY:
 +		if (dat) {
 +			UnhookEvent(dat->hHook);
 +			free(dat);
 +		}
 +		SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
 +		break;
 +	}
 +	return FALSE;
 +}
 +
 +INT_PTR CALLBACK DlgProcAvatarUserInfo(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 +{
 +	HANDLE hContact;
 +	struct WindowData *dat = (struct WindowData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
 +
 +	if (dat)
 +		hContact = dat->hContact;
 +
 +	switch(msg) {
 +	case WM_INITDIALOG:
 +		{
 +			dat = (struct WindowData *) malloc(sizeof(struct WindowData));
 +			if (dat == NULL)
 +				return FALSE;
 +			dat->hContact = (HANDLE)lParam;
 +
 +			HWND protopic = GetDlgItem(hwndDlg, IDC_PROTOPIC);
 +			SendMessage(protopic, AVATAR_SETCONTACT, 0, (LPARAM) dat->hContact);
 +			SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, (LPARAM) GetSysColor(COLOR_BTNSHADOW));
 +			SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM) LPGENT("Contact has no avatar"));
 +			SendMessage(protopic, AVATAR_RESPECTHIDDEN, 0, (LPARAM) FALSE);
 +			SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, (LPARAM) FALSE);
 +
 +			SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
 +			hContact = (HANDLE)lParam;
 +			TranslateDialogDefault(hwndDlg);
 +			SendMessage(hwndDlg, DM_SETAVATARNAME, 0, 0);
 +			CheckDlgButton(hwndDlg, IDC_PROTECTAVATAR, DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0) ? TRUE : FALSE);
 +			CheckDlgButton(hwndDlg, IDC_HIDEAVATAR, DBGetContactSettingByte(hContact, "ContactPhoto", "Hidden", 0) ? TRUE : FALSE);
 +
 +			SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETBUDDY, (WPARAM)GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), 0);
 +			SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETRANGE, 0, MAKELONG(8, 2));
 +
 +			SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETBUDDY, (WPARAM)GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), 0);
 +			SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
 +
 +			LoadTransparentData(hwndDlg, GetContactThatHaveTheAvatar(hContact));
 +		}
 +		break;
 +
 +	case WM_COMMAND:
 +		switch(LOWORD(wParam)) {
 +		case ID_USE_DEFAULTS:
 +			hContact = GetContactThatHaveTheAvatar(hContact);
 +
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "MakeTransparentBkg");
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "TranspBkgNumPoints");
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "TranspBkgColorDiff");
 +
 +			LoadTransparentData(hwndDlg, hContact);
 +
 +			SendMessage(hwndDlg, DM_REALODAVATAR, 0, 0);
 +			break;
 +
 +		case IDC_CHANGE:
 +			SetAvatar((WPARAM)hContact, 0);
 +			SendMessage(hwndDlg, DM_SETAVATARNAME, 0, 0);
 +			CheckDlgButton(hwndDlg, IDC_PROTECTAVATAR, DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0) ? TRUE : FALSE);
 +			break;
 +
 +		case IDC_HIDEAVATAR:
 +			{
 +				int hidden = IsDlgButtonChecked(hwndDlg, IDC_HIDEAVATAR) ? 1 : 0;
 +				SetAvatarAttribute(hContact, AVS_HIDEONCLIST, hidden);
 +				if (hidden != DBGetContactSettingByte(hContact, "ContactPhoto", "Hidden", 0))
 +					DBWriteContactSettingByte(hContact, "ContactPhoto", "Hidden", hidden);
 +				break;
 +			}
 +
 +		case IDC_PROTECTAVATAR:
 +			{
 +				BOOL locked = IsDlgButtonChecked(hwndDlg, IDC_PROTECTAVATAR);
 +				SaveTransparentData(hwndDlg, hContact, locked);
 +				ProtectAvatar((WPARAM)hContact, locked ? 1 : 0);
 +
 +				break;
 +			}
 +		case IDC_BKG_NUM_POINTS:
 +		case IDC_BKG_COLOR_DIFFERENCE:
 +			if (HIWORD(wParam)!=EN_CHANGE || (HWND)lParam!=GetFocus())
 +				break;
 +		case IDC_MAKETRANSPBKG:
 +			{
 +				BOOL enable = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), enable);
 +
 +				SendMessage(hwndDlg, DM_REALODAVATAR, 0, 0);
 +				break;
 +			}
 +		case IDC_RESET:
 +			{
 +				char *szProto = ( char* )CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +				DBVARIANT dbv = {0};
 +
 +				ProtectAvatar((WPARAM)hContact, 0);
 +				if (MessageBox(0, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
 +					if (!DBGetContactSettingTString(hContact, "ContactPhoto", "File", &dbv)) {
 +						DeleteFile(dbv.ptszVal);
 +						DBFreeVariant(&dbv);
 +					}
 +				}
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Locked");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Backup");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "RFile");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "File");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Format");
 +				DBDeleteContactSetting(hContact, szProto, "AvatarHash");
 +				DBDeleteContactSetting(hContact, szProto, "AvatarSaved");
 +				DeleteAvatarFromCache(hContact, FALSE);
 +
 +				QueueAdd(hContact);
 +				break;
 +			}
 +		case IDC_DELETE:
 +			{
 +				DBVARIANT dbv = {0};
 +
 +				ProtectAvatar((WPARAM)hContact, 0);
 +				if (MessageBox(0, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
 +					if (!DBGetContactSettingTString(hContact, "ContactPhoto", "File", &dbv)) {
 +						DeleteFile(dbv.ptszVal);
 +						DBFreeVariant(&dbv);
 +					}
 +				}
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Locked");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Backup");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "RFile");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "File");
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Format");
 +				DeleteAvatarFromCache(hContact, FALSE);
 +				SendMessage(hwndDlg, DM_SETAVATARNAME, 0, 0);
 +				break;
 +			}
 +		}
 +		break;
 +
 +	case DM_REALODAVATAR:
 +		SaveTransparentData(hwndDlg, hContact, IsDlgButtonChecked(hwndDlg, IDC_PROTECTAVATAR));
 +		ChangeAvatar(hContact, TRUE);
 +		break;
 +
 +	case WM_NCDESTROY:
 +		if (dat)
 +			free(dat);
 +		SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
 +		break;
 +	}
 +	return FALSE;
 +}
 +
 +static char * GetSelectedProtocol(HWND hwndDlg)
 +{
 +	HWND hwndList = GetDlgItem(hwndDlg, IDC_PROTOCOLS);
 +
 +	// Get selection
 +	int iItem = ListView_GetSelectionMark(hwndList);
 +	if (iItem < 0)
 +		return NULL;
 +
 +	// Get protocol name
 +	LVITEM item = {0};
 +	item.mask = LVIF_PARAM;
 +	item.iItem = iItem;
 +	SendMessage(hwndList, LVM_GETITEMA, 0, (LPARAM)&item);
 +	return ( char* ) item.lParam;
 +}
 +
 +static void EnableDisableControls(HWND hwndDlg, char *proto)
 +{
 +	if (IsDlgButtonChecked(hwndDlg, IDC_PER_PROTO))
 +	{
 +		if (proto == NULL)
 +		{
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGE), FALSE);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
 +		}
 +		else
 +		{
 +			if (!ProtoServiceExists(proto, PS_SETMYAVATAR))
 +			{
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGE), FALSE);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
 +			}
 +			else
 +			{
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGE), TRUE);
 +
 +				int width, height;
 +				SendDlgItemMessage(hwndDlg, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM) &width, (LPARAM) &height);
 +				EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), (LPARAM) width != 0 || height != 0);
 +			}
 +		}
 +	}
 +	else
 +	{
 +		EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGE), TRUE);
 +
 +		if (DBGetContactSettingByte(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1))
 +		{
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), TRUE);
 +		}
 +		else
 +		{
 +			int width, height;
 +			SendDlgItemMessage(hwndDlg, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM) &width, (LPARAM) &height);
 +			EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), (LPARAM) width != 0 || height != 0);
 +		}
 +	}
 +}
 +
 +static void OffsetWindow(HWND parent, HWND hwnd, int dx, int dy)
 +{
 +	RECT rc;
 +	GetWindowRect(hwnd, &rc);
 +	ScreenToClient(parent, &rc);
 +	OffsetRect(&rc, dx, dy);
 +	MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
 +}
 +
 +static void EnableDisableProtocols(HWND hwndDlg, BOOL init)
 +{
 +	int diff = 147; // Pre-calc
 +	BOOL perProto = IsDlgButtonChecked(hwndDlg, IDC_PER_PROTO);
 +	HWND hwndList = GetDlgItem(hwndDlg, IDC_PROTOCOLS);
 +
 +	if (perProto)
 +	{
 +		if (!init && !IsWindowVisible(hwndList))
 +		{
 +			// Show list of protocols
 +			ShowWindow(hwndList, SW_SHOW);
 +
 +			// Move controls
 +			OffsetWindow(hwndDlg, GetDlgItem(hwndDlg, IDC_PROTOPIC), diff, 0);
 +			OffsetWindow(hwndDlg, GetDlgItem(hwndDlg, IDC_CHANGE), diff, 0);
 +			OffsetWindow(hwndDlg, GetDlgItem(hwndDlg, IDC_DELETE), diff, 0);
 +		}
 +
 +		char * proto = GetSelectedProtocol(hwndDlg);
 +		if (proto == NULL)
 +		{
 +			ListView_SetItemState(hwndList, 0, LVIS_FOCUSED | LVIS_SELECTED, 0x0F);
 +		}
 +		else
 +		{
 +			SendDlgItemMessage(hwndDlg, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, (LPARAM) proto);
 +			EnableDisableControls(hwndDlg, proto);
 +		}
 +	}
 +	else
 +	{
 +		if (init || IsWindowVisible(hwndList))
 +		{
 +			// Show list of protocols
 +			ShowWindow(hwndList, SW_HIDE);
 +
 +			// Move controls
 +			OffsetWindow(hwndDlg, GetDlgItem(hwndDlg, IDC_PROTOPIC), -diff, 0);
 +			OffsetWindow(hwndDlg, GetDlgItem(hwndDlg, IDC_CHANGE), -diff, 0);
 +			OffsetWindow(hwndDlg, GetDlgItem(hwndDlg, IDC_DELETE), -diff, 0);
 +		}
 +
 +		SendDlgItemMessage(hwndDlg, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, NULL);
 +	}
 +}
 +
 +INT_PTR CALLBACK DlgProcAvatarProtoInfo(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 +{
 +	switch(msg) {
 +	case WM_INITDIALOG:
 +		TranslateDialogDefault(hwndDlg);
 +		{
 +			HWND protopic = GetDlgItem(hwndDlg, IDC_PROTOPIC);
 +			SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, (LPARAM) GetSysColor(COLOR_BTNSHADOW));
 +			SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM) LPGENT("No avatar"));
 +			SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, (LPARAM) FALSE);
 +
 +			HWND hwndList = GetDlgItem(hwndDlg, IDC_PROTOCOLS);
 +			ListView_SetExtendedListViewStyleEx(hwndList, 0, LVS_EX_SUBITEMIMAGES);
 +
 +			HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | (IsWinVerXPPlus()? ILC_COLOR32 : ILC_COLOR16), 4, 0);
 +			ListView_SetImageList(hwndList, hIml, LVSIL_SMALL);
 +
 +			LVCOLUMN lvc = {0};
 +			lvc.mask = LVCF_FMT;
 +			lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT;
 +			ListView_InsertColumn(hwndList, 0, &lvc);
 +
 +			LVITEM item = {0};
 +			item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
 +			item.iItem = 1000;
 +
 +			// List protocols
 +			PROTOACCOUNT **accs;
 +			int i, count, num = 0;
 +
 +			ProtoEnumAccounts( &count, &accs );
 +			for (i = 0; i < count; i++)
 +			{
 +				if ( !ProtoServiceExists( accs[i]->szModuleName, PS_GETMYAVATAR))
 +					continue;
 +
 +				if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName ))
 +					continue;
 +
 +				ImageList_AddIcon(hIml, LoadSkinnedProtoIcon( accs[i]->szModuleName, ID_STATUS_ONLINE));
 +				item.pszText = accs[i]->tszAccountName;
 +				item.iImage = num;
 +				item.lParam = (LPARAM)accs[i]->szModuleName;
 +
 +				ListView_InsertItem(hwndList, &item);
 +				num++;
 +			}
 +
 +			ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
 +			ListView_Arrange(hwndList, LVA_ALIGNLEFT | LVA_ALIGNTOP);
 +
 +			// Check if should show per protocol avatars
 +			CheckDlgButton(hwndDlg, IDC_PER_PROTO, DBGetContactSettingByte(NULL, AVS_MODULE, "PerProtocolUserAvatars", 1));
 +			EnableDisableProtocols(hwndDlg, TRUE);
 +		}
 +		break;
 +
 +	case WM_NOTIFY:
 +		{
 +			LPNMHDR nm = (LPNMHDR) lParam;
 +			switch(nm->idFrom) {
 +			case IDC_PROTOCOLS:
 +				switch (nm->code) {
 +				case LVN_ITEMCHANGED:
 +					{
 +						LPNMLISTVIEW li = (LPNMLISTVIEW) nm;
 +						if (li->uNewState & LVIS_SELECTED)
 +						{
 +							SendDlgItemMessage(hwndDlg, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, li->lParam);
 +							EnableDisableControls(hwndDlg, ( char* ) li->lParam);
 +						}
 +					}
 +					break;
 +				}
 +				break;
 +
 +			case IDC_PROTOPIC:
 +				switch (nm->code) {
 +				case NM_AVATAR_CHANGED:
 +					EnableDisableControls(hwndDlg, GetSelectedProtocol(hwndDlg));
 +					break;
 +				}
 +				break;
 +			}
 +			break;
 +		}
 +	case WM_COMMAND:
 +		switch(LOWORD(wParam)) {
 +		case IDC_CHANGE:
 +			if (!IsDlgButtonChecked(hwndDlg, IDC_PER_PROTO))
 +				avSetMyAvatar(NULL, NULL);
 +			else {
 +				char *proto = GetSelectedProtocol(hwndDlg);
 +				if (proto != NULL)
 +					avSetMyAvatar(proto, NULL);
 +			}
 +			break;
 +
 +		case IDC_DELETE:
 +			if (!IsDlgButtonChecked(hwndDlg, IDC_PER_PROTO)) {
 +				if (MessageBox(hwndDlg, TranslateT("Are you sure you want to remove your avatar?"), TranslateT("Global Avatar"), MB_YESNO) == IDYES)
 +					avSetMyAvatar(NULL, _T(""));
 +			}
 +			else {
 +				char *proto = GetSelectedProtocol(hwndDlg);
 +				if (proto == NULL)
 +					break;
 +
 +				char description[256];
 +				CallProtoService(proto, PS_GETNAME, SIZEOF(description),(LPARAM) description);
 +				TCHAR *descr = mir_a2t(description);
 +				if (MessageBox(hwndDlg, TranslateT("Are you sure you want to remove your avatar?"), descr, MB_YESNO) == IDYES)
 +					avSetMyAvatar(proto, _T(""));
 +				mir_free(descr);
 +			}
 +			break;
 +
 +		case IDC_PER_PROTO:
 +			DBWriteContactSettingByte(NULL, AVS_MODULE, "PerProtocolUserAvatars", IsDlgButtonChecked(hwndDlg, IDC_PER_PROTO) ? 1 : 0);
 +			EnableDisableProtocols(hwndDlg, FALSE);
 +			break;
 +		}
 +		break;
 +	}
 +	return FALSE;
 +}
 diff --git a/plugins/AVS/src/poll.cpp b/plugins/AVS/src/poll.cpp new file mode 100644 index 0000000000..d93ef5309c --- /dev/null +++ b/plugins/AVS/src/poll.cpp @@ -0,0 +1,319 @@ +/* 
 +Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
 +
 +This is free software; you can redistribute it and/or
 +modify it under the terms of the GNU Library General Public
 +License as published by the Free Software Foundation; either
 +version 2 of the License, or (at your option) any later version.
 +
 +This 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
 +Library General Public License for more details.
 +
 +You should have received a copy of the GNU Library General Public
 +License along with this file; see the file license.txt.  If
 +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 +Boston, MA 02111-1307, USA.  
 +*/
 +
 +#include "commonheaders.h"
 +
 +/*
 +It has 1 queue:
 +A queue to request items. One request is done at a time, REQUEST_WAIT_TIME miliseconts after it has beeing fired
 +   ACKRESULT_STATUS. This thread only requests the avatar (and maybe add it to the cache queue)
 +*/
 +
 +#define REQUEST_WAIT_TIME 3000
 +
 +// Time to wait before re-requesting an avatar that failed
 +#define REQUEST_FAIL_WAIT_TIME (3 * 60 * 60 * 1000)
 +
 +// Time to wait before re-requesting an avatar that received an wait for
 +#define REQUEST_WAITFOR_WAIT_TIME (30 * 60 * 1000)
 +
 +// Number of mileseconds the threads wait until take a look if it is time to request another item
 +#define POOL_DELAY 1000
 +
 +// Number of mileseconds the threads wait after a GAIR_WAITFOR is returned
 +#define REQUEST_DELAY 18000
 +
 +
 +// Prototypes ///////////////////////////////////////////////////////////////////////////
 +
 +static void RequestThread(void *vParam);
 +
 +extern HANDLE hShutdownEvent;
 +extern char *g_szMetaName;
 +extern int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist = FALSE, int pa_format = 0);
 +extern int DeleteAvatar(HANDLE hContact);
 +extern void MakePathRelative(HANDLE hContact, TCHAR *path);
 +int Proto_GetDelayAfterFail(const char *proto);
 +BOOL Proto_IsFetchingAlwaysAllowed(const char *proto);
 +
 +struct CacheNode *FindAvatarInCache(HANDLE hContact, BOOL add, BOOL findAny = FALSE);
 +
 +extern HANDLE hEventContactAvatarChanged;
 +extern BOOL g_AvatarHistoryAvail;
 +extern FI_INTERFACE *fei;
 +
 +#ifdef _DEBUG
 +int _DebugTrace(const char *fmt, ...);
 +int _DebugTrace(HANDLE hContact, const char *fmt, ...);
 +#endif
 +
 +// Functions ////////////////////////////////////////////////////////////////////////////
 +
 +// Items with higher priority at end
 +static int QueueSortItems( const QueueItem* i1, const QueueItem* i2)
 +{
 +	return i2->check_time - i1->check_time;
 +}
 +
 +static OBJLIST<QueueItem> queue( 20, QueueSortItems );
 +static CRITICAL_SECTION cs;
 +static int waitTime;
 +
 +void InitPolls() 
 +{
 +	waitTime = REQUEST_WAIT_TIME;
 +	InitializeCriticalSection( &cs );
 +
 +	// Init request queue
 +	mir_forkthread(RequestThread, NULL);
 +}
 +
 +void FreePolls()
 +{
 +}
 +
 +// Return true if this protocol can have avatar requested
 +static BOOL PollProtocolCanHaveAvatar(const char *szProto)
 +{
 +	int pCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
 +	int status = CallProtoService(szProto, PS_GETSTATUS, 0, 0);
 +	return (pCaps & PF4_AVATARS)
 +		&& (g_szMetaName == NULL || strcmp(g_szMetaName, szProto))
 +		&& ((status > ID_STATUS_OFFLINE && status != ID_STATUS_INVISIBLE) || Proto_IsFetchingAlwaysAllowed(szProto));
 +}
 +
 +// Return true if this protocol has to be checked
 +static BOOL PollCheckProtocol(const char *szProto)
 +{
 +	return DBGetContactSettingByte(NULL, AVS_MODULE, szProto, 1);
 +}
 +
 +// Return true if this contact can have avatar requested
 +static BOOL PollContactCanHaveAvatar(HANDLE hContact, const char *szProto)
 +{
 +	int status = DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE);
 +	return (Proto_IsFetchingAlwaysAllowed(szProto) || status != ID_STATUS_OFFLINE)
 +		&& !DBGetContactSettingByte(hContact, "CList", "NotOnList", 0)
 +		&& DBGetContactSettingByte(hContact, "CList", "ApparentMode", 0) != ID_STATUS_OFFLINE;
 +}
 +
 +// Return true if this contact has to be checked
 +static BOOL PollCheckContact(HANDLE hContact, const char *szProto)
 +{
 +	return !DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0)
 +			&& FindAvatarInCache(hContact, FALSE, TRUE) != NULL;
 +}
 +
 +static void QueueRemove(HANDLE hContact)
 +{
 +	EnterCriticalSection(&cs);
 +
 +	for (int i = queue.getCount()-1 ; i >= 0 ; i-- ) {
 +		QueueItem& item = queue[i];
 +		if (item.hContact == hContact)
 +			queue.remove(i);
 +	}
 +
 +	LeaveCriticalSection(&cs);
 +}
 +
 +static void QueueAdd(HANDLE hContact, int waitTime)
 +{
 +	if(fei == NULL)
 +		return;
 +
 +	EnterCriticalSection(&cs);
 +
 +	// Only add if not exists yet
 +	int i;
 +	for (i = queue.getCount()-1; i >= 0; i--)
 +		if ( queue[i].hContact == hContact)
 +			break;
 +
 +	if (i < 0) {
 +		QueueItem *item = new QueueItem;
 +		if (item != NULL) {
 +			item->hContact = hContact;
 +			item->check_time = GetTickCount() + waitTime;
 +			queue.insert(item);
 +	}	}
 +
 +	LeaveCriticalSection(&cs);
 +}
 +
 +// Add an contact to a queue
 +void QueueAdd(HANDLE hContact)
 +{
 +	QueueAdd(hContact, waitTime);
 +}
 +
 +void ProcessAvatarInfo(HANDLE hContact, int type, PROTO_AVATAR_INFORMATIONT *pai, const char *szProto)
 +{
 +	QueueRemove(hContact);
 +
 +	if (type == GAIR_SUCCESS) 
 +	{
 +		if (pai == NULL)
 +			return;
 +
 +		// Fix settings in DB
 +		DBDeleteContactSetting(hContact, "ContactPhoto", "NeedUpdate");
 +		DBDeleteContactSetting(hContact, "ContactPhoto", "RFile");
 +		if (!DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0))
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "Backup");
 +		DBWriteContactSettingTString(hContact, "ContactPhoto", "File", pai->filename);
 +		DBWriteContactSettingWord(hContact, "ContactPhoto", "Format", pai->format);
 +
 +		if (pai->format == PA_FORMAT_PNG || pai->format == PA_FORMAT_JPEG 
 +			|| pai->format == PA_FORMAT_ICON  || pai->format == PA_FORMAT_BMP
 +			|| pai->format == PA_FORMAT_GIF)
 +		{
 +			// We can load it!
 +			MakePathRelative(hContact, pai->filename);
 +			ChangeAvatar(hContact, TRUE, TRUE, pai->format);
 +		}
 +		else
 +		{
 +			// As we can't load it, notify but don't load
 +			ChangeAvatar(hContact, FALSE, TRUE, pai->format);
 +		}
 +	}
 +	else if (type == GAIR_NOAVATAR) 
 +	{
 +		DBDeleteContactSetting(hContact, "ContactPhoto", "NeedUpdate");
 +
 +		if (DBGetContactSettingByte(NULL, AVS_MODULE, "RemoveAvatarWhenContactRemoves", 1)) 
 +		{
 +			// Delete settings
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "RFile");
 +			if (!DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0))
 +				DBDeleteContactSetting(hContact, "ContactPhoto", "Backup");
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "File");
 +			DBDeleteContactSetting(hContact, "ContactPhoto", "Format");
 +
 +			// Fix cache
 +			ChangeAvatar(hContact, FALSE, TRUE, 0);
 +		}
 +	}
 +	else if (type == GAIR_FAILED) 
 +	{
 +		int wait = Proto_GetDelayAfterFail(szProto);
 +		if (wait > 0)
 +		{
 +			// Reschedule to request after needed time (and avoid requests before that)
 +			EnterCriticalSection(&cs);
 +			QueueRemove(hContact);
 +			QueueAdd(hContact, wait);
 +			LeaveCriticalSection(&cs);
 +		}
 +	}
 +}
 +
 +int FetchAvatarFor(HANDLE hContact, char *szProto = NULL)
 +{
 +	int result = GAIR_NOAVATAR;
 +
 +	if (szProto == NULL)
 +		szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
 +
 +	if (szProto != NULL && PollProtocolCanHaveAvatar(szProto) && PollContactCanHaveAvatar(hContact, szProto))
 +	{
 +		// Can have avatar, but must request it?
 +		if (
 +			(g_AvatarHistoryAvail && CallService(MS_AVATARHISTORY_ENABLED, (WPARAM) hContact, 0))
 +			 || (PollCheckProtocol(szProto) && PollCheckContact(hContact, szProto))
 +			)
 +		{
 +			// Request it
 +			PROTO_AVATAR_INFORMATIONT pai_s = {0};
 +			pai_s.cbSize = sizeof(pai_s);
 +			pai_s.hContact = hContact;
 +			//_DebugTrace(hContact, "schedule request");
 +			INT_PTR res = CallProtoService(szProto, PS_GETAVATARINFOT, GAIF_FORCE, (LPARAM)&pai_s);
 +
 +			if (res == CALLSERVICE_NOTFOUND)
 +			{
 +				PROTO_AVATAR_INFORMATION pai = {0};
 +				pai.cbSize = sizeof(pai);
 +				pai.hContact = hContact;
 +				res = CallProtoService(szProto, PS_GETAVATARINFO, GAIF_FORCE, (LPARAM)&pai);
 +				MultiByteToWideChar( CP_ACP, 0, pai.filename, -1, pai_s.filename, SIZEOF(pai_s.filename));
 +				pai_s.format = pai.format;
 +			}
 +
 +			if (res != CALLSERVICE_NOTFOUND) result = res;
 +			ProcessAvatarInfo(pai_s.hContact, result, &pai_s, szProto);
 +		}
 +	}
 +
 +	return result;
 +}
 +
 +static void RequestThread(void *vParam)
 +{
 +	while (!g_shutDown)
 +	{
 +		EnterCriticalSection(&cs);
 +
 +		if ( queue.getCount() == 0 )
 +		{
 +			// No items, so supend thread
 +			LeaveCriticalSection(&cs);
 +
 +			mir_sleep(POOL_DELAY);
 +		}
 +		else
 +		{
 +			// Take a look at first item
 +			QueueItem& qi = queue[ queue.getCount()-1 ];
 +
 +			if (qi.check_time > GetTickCount()) 
 +			{
 +				// Not time to request yet, wait...
 +				LeaveCriticalSection(&cs);
 +				mir_sleep(POOL_DELAY);
 +			}
 +			else
 +			{
 +				// Will request this item
 +				HANDLE hContact = qi.hContact;
 +				queue.remove( queue.getCount()-1 );
 +
 +				QueueRemove(hContact);
 +
 +				LeaveCriticalSection(&cs);
 +
 +				if (FetchAvatarFor(hContact) == GAIR_WAITFOR)
 +				{
 +					// Mark to not request this contact avatar for more 30 min
 +					EnterCriticalSection(&cs);
 +					QueueRemove(hContact);
 +					QueueAdd(hContact, REQUEST_WAITFOR_WAIT_TIME);
 +					LeaveCriticalSection(&cs);
 +
 +					// Wait a little until requesting again
 +					mir_sleep(REQUEST_DELAY);
 +				}
 +			}
 +		}
 +	}
 +
 +	DeleteCriticalSection(&cs);
 +	queue.destroy();
 +}
 diff --git a/plugins/AVS/src/poll.h b/plugins/AVS/src/poll.h new file mode 100644 index 0000000000..9761e7360a --- /dev/null +++ b/plugins/AVS/src/poll.h @@ -0,0 +1,36 @@ +/* 
 +Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
 +
 +This is free software; you can redistribute it and/or
 +modify it under the terms of the GNU Library General Public
 +License as published by the Free Software Foundation; either
 +version 2 of the License, or (at your option) any later version.
 +
 +This 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
 +Library General Public License for more details.
 +
 +You should have received a copy of the GNU Library General Public
 +License along with this file; see the file license.txt.  If
 +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 +Boston, MA 02111-1307, USA.  
 +*/
 +
 +
 +#ifndef __POLL_H__
 +# define __POLL_H__
 +
 +struct QueueItem
 +{
 +	HANDLE hContact;
 +	DWORD check_time;
 +};
 +
 +void InitPolls();
 +void FreePolls();
 +
 +// Add an contact to a queue
 +void QueueAdd(HANDLE hContact);
 +
 +#endif // __POLL_H__
 diff --git a/plugins/AVS/src/resource.h b/plugins/AVS/src/resource.h new file mode 100644 index 0000000000..2d8ba9add0 --- /dev/null +++ b/plugins/AVS/src/resource.h @@ -0,0 +1,60 @@ +//{{NO_DEPENDENCIES}}
 +// Microsoft Developer Studio generated include file.
 +// Used by avs.rc
 +//
 +#define ID_USE_DEFAULTS                 3
 +#define IDD_OPTIONS                     101
 +#define IDD_OPTIONS_PICTS               101
 +#define IDD_OPENSUBCLASS                102
 +#define IDD_AVATAROPTIONS               103
 +#define IDI_AVATAR                      104
 +#define IDD_SET_OWN_SUBCLASS            105
 +#define IDD_DIALOG1                     106
 +#define IDD_USER_AVATAR                 106
 +#define IDD_PROTO_AVATARS               107
 +#define IDD_OPTIONS_AVATARS             109
 +#define IDD_OPTIONS_OWN                 110
 +#define IDC_PROTOCOLS                   1001
 +#define IDC_CLIST                       1002
 +#define IDC_SETPROTOPIC                 1003
 +#define IDC_PROTOPIC                    1004
 +#define IDC_REMOVEPROTOPIC              1005
 +#define IDC_PROTOAVATARNAME             1006
 +#define IDC_CHECK1                      1007
 +#define IDC_PROTECTAVATAR               1007
 +#define IDC_SHOWWARNINGS                1007
 +#define IDC_PER_PROTO                   1007
 +#define IDC_CHANGE                      1008
 +#define IDC_MAKE_TRANSPARENT_BKG        1008
 +#define IDC_GROW                        1008
 +#define IDC_RESET                       1009
 +#define IDC_MAKE_GRAYSCALE              1009
 +#define IDC_AVATARNAME                  1010
 +#define IDC_MAKE_GRAYSCALE2             1010
 +#define IDC_SET_MAKE_SQUARE             1010
 +#define IDC_HIDEAVATAR                  1011
 +#define IDC_DELETE                      1012
 +#define IDC_MAKETRANSPBKG               1013
 +#define IDC_GUESS_LEVEL                 1014
 +#define IDC_BKG_NUM_POINTS              1014
 +#define IDC_COMBO1                      1015
 +#define IDC_BKG_NUM_POINTS_SPIN         1015
 +#define IDC_BKG_COLOR_DIFFERENCE        1016
 +#define IDC_SIZELIMITSPIN3              1017
 +#define IDC_BKG_COLOR_DIFFERENCE_SPIN   1017
 +#define IDC_BKG_NUM_POINTS_L            1018
 +#define IDC_BKG_COLOR_DIFFERENCE_L      1019
 +#define IDC_MAKE_TRANSP_PROPORTIONAL    1020
 +#define IDC_MAKE_MY_AVATARS_TRANSP      1021
 +#define IDC_MAKE_SQUARE                 1023
 +
 +// Next default values for new objects
 +// 
 +#ifdef APSTUDIO_INVOKED
 +#ifndef APSTUDIO_READONLY_SYMBOLS
 +#define _APS_NEXT_RESOURCE_VALUE        108
 +#define _APS_NEXT_COMMAND_VALUE         40001
 +#define _APS_NEXT_CONTROL_VALUE         1024
 +#define _APS_NEXT_SYMED_VALUE           101
 +#endif
 +#endif
 diff --git a/plugins/AVS/src/version.h b/plugins/AVS/src/version.h new file mode 100644 index 0000000000..826d9fdefb --- /dev/null +++ b/plugins/AVS/src/version.h @@ -0,0 +1,5 @@ +#include "../../include/m_version.h"
 +
 +#define __FILEVERSION_STRING        MIRANDA_VERSION_FILEVERSION
 +#define __VERSION_STRING            MIRANDA_VERSION_STRING
 +#define __VERSION_DWORD             MIRANDA_VERSION_DWORD
  | 
