summaryrefslogtreecommitdiff
path: root/plugins/AVS
diff options
context:
space:
mode:
authorKirill Volinsky <mataes2007@gmail.com>2012-05-20 18:33:39 +0000
committerKirill Volinsky <mataes2007@gmail.com>2012-05-20 18:33:39 +0000
commit67c6f233d7e60ad59918e734fe565f5f5dfd28d5 (patch)
tree213ab09cf6250d2bee311dc20e0c580838681580 /plugins/AVS
parentc9bf0c30a59a922b96713db54e2293fdf7334adf (diff)
projects renames and fixes
git-svn-id: http://svn.miranda-ng.org/main/trunk@105 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/AVS')
-rw-r--r--plugins/AVS/CHANGELOG.AVS182
-rw-r--r--plugins/AVS/README94
-rw-r--r--plugins/AVS/README.SOURCE25
-rw-r--r--plugins/AVS/acc.cpp908
-rw-r--r--plugins/AVS/acc.h44
-rw-r--r--plugins/AVS/avs.rc251
-rw-r--r--plugins/AVS/avs_10.vcxproj290
-rw-r--r--plugins/AVS/avs_10.vcxproj.filters70
-rw-r--r--plugins/AVS/commonheaders.h119
-rw-r--r--plugins/AVS/image_utils.cpp757
-rw-r--r--plugins/AVS/image_utils.h58
-rw-r--r--plugins/AVS/license.txt340
-rw-r--r--plugins/AVS/main.cpp2684
-rw-r--r--plugins/AVS/mir_thread.cpp50
-rw-r--r--plugins/AVS/mir_thread.h49
-rw-r--r--plugins/AVS/options.cpp1147
-rw-r--r--plugins/AVS/poll.cpp319
-rw-r--r--plugins/AVS/poll.h36
-rw-r--r--plugins/AVS/res/avatar.icobin0 -> 2550 bytes
-rw-r--r--plugins/AVS/resource.h60
-rw-r--r--plugins/AVS/vc6.rc2
-rw-r--r--plugins/AVS/version.h5
-rw-r--r--plugins/AVS/version.rc113
23 files changed, 7603 insertions, 0 deletions
diff --git a/plugins/AVS/CHANGELOG.AVS b/plugins/AVS/CHANGELOG.AVS
new file mode 100644
index 0000000000..a673bdff73
--- /dev/null
+++ b/plugins/AVS/CHANGELOG.AVS
@@ -0,0 +1,182 @@
+
+ Version history:
+
+0.0.2.6 - 2006/08/03
+
+ * added file hashing (pescuma)
+ * rewrote the picture loading code. It is now running in a background thread
+ at low priority. The loader thread is the only instance which actually writes
+ to the cache entries. It does this with a small delay and notifies the hook
+ subscribers when a picture has been loaded. While it is not fully loaded,
+ the fallback protocol picture may be used. This makes connecting or
+ loading the clist with lots of avatars significantly faster at the cost
+ of delayed picture appearance.
+ It may also help with the sporadic lockups since the part of the code which
+ runs in the callers thread context is now much simpler.
+
+0.0.2.5 - 2006/07/30
+
+ * fixed updater support
+ * moved InitPolls() to ModulesLoaded() to avoid a problem with missing
+ core services.
+ * fixed keyboard navigation glitch in the Customize->Contact pictures dialog.
+
+0.0.2.4 - 2006/07/30
+
+ * bugfix (missing protocol avatars after migrating to new relpath)
+ * added proper version info resource record (showing ansi/unicode)
+ * added project files for Visual C++ 6 (source code release only)
+
+0.0.2.3 - 2006/07/11
+
+ * should now work better with metacontacts. Subcontact avatar changes are
+ "forwarded" to the master contact.
+
+ * relative path names are now relative to the DATABASE directory, not
+ Mirandas root directory.
+
+ * missing picture files and/or invalid database entries will now result
+ in a refresh request.
+
+0.0.2.0 introduced big internal changes. New request queue system (written by Pescuma)
+ loadavatars no longer depends on imgdecoder.dll, but needs a recent png2dib
+ plugin (supplied with Miranda 0.4.3.x nightly builds)
+
+0.0.1.12 - 0.0.2.2 - released via nightlys (see SVN changelog)
+
+0.0.1.11 - 2005/11/10
+
+ + added patch by pescuma for making normal avatars transparent (or semi-
+ transparent).
+ There are global (under Customize->Contact pictures) and per contact settings
+ (contact menu -> Contact picture...). In order to get good results, you will need
+ to tweak the settings a bit - it works for most avatar pictures with a uni-color
+ background, but it really depends on the image.
+
+ + built with Visual Studio 2005 (Visual C++ 8.0). The DLL is now statically
+ linked, so they are somewhat bigger, but it avoids error messages because of the
+ new VC++ 8 runtime which is not installed on most PCs.
+
+ + added unicode version (loadavatarsW.dll) which will display unicode nicknames in
+ the "per user" contact picture dialog.
+
+ + added services and events to manage own avatar pictures. See m_avatars.h for more
+ information. Also, the drawing service was enhanced to allow drawing your own
+ avatars.
+
+0.0.1.10 - 2005/10/19
+
+ ! added support for jabber avatar updates.
+
+ + better cleanup on Unload()
+
+0.0.1.9 - 2005/10/18
+
+ * accept .dat as valid image extension (sometimes, icq avatars are saved as .dat
+ files, even if they are actually JPG images (reason unknown, but we can load
+ them anyway. MS_UTILS_LOADBITMAP will return an error when the file is not
+ really a valid image - the extension doesn't matter).
+
+0.0.1.8 - 2005/10/17
+
+ ! fix. Don't show tray warning messages about invalid avatar file size when
+ the option "Show warning messages" on Customize->Contact pictures is unchecked.
+
+ * changed way how protocols are enabled/disabled. After protocols have been added
+ or removed, "new" protocols are automatically enabled, so you don't have to do
+ that manually any more.
+
+ * you can completely disable the size limit check by setting the size limit on
+ Customize->Contact List to 0 (zero).
+
+0.0.1.7 - 2005/10/16
+
+ * bugfix: check filenames more aggressivly.
+
+ * bugfix: premultiply was broken (thanks FYR)
+
+ + added a service to draw a contacts picture to a target device context.
+ See m_avatars.h for description on how to use that service.
+
+ + added file size limitation to the picture loader. The default are 70Kbytes,
+ should be enough for most avatar pictures (they have to be small images).
+ The limit can be increased on the option page (Customize->contact pictures).
+
+0.0.1.5 - 2005/09/15
+
+ + added updater support
+
+ * moved option page to Customize->Contact pictures
+
+0.0.1.4 - 2005/08/31
+
+ * cache reallocs will now send avatar change notifies (realloc() may move the
+ cache in memory, so pointers to avatar cache entries may become invalid).
+
+ ! bugfix - badly written protocols which load themself too late are now
+ skipped and won't cause troubles because of the protocol list reallocation.
+
+0.0.1.3 - 2005/08/30
+
+ + ability to lock the avatar (protect from automatic updates). Useful, if you
+ have set a local contact picture and don't want it to be overwritten.
+ Actually, the feature does not really lock the avatar updating - it will
+ only save the picture from being overwritten.
+
+ + added UI to set/change/lock avatar. DEVELOPERS please check m_avatars.h on how
+ to use the service from your own plugins. There are now services to lock and/or
+ set the avatar, either by providing a valid image filename or by using a file
+ selection dialog. Also, there is a service to call the avatar dialog for a
+ hContact.
+
+ * struct avatarCacheEntry has changed too. It now provides szFilename which
+ will contain the full image filename of the current avatar in use.
+
+ * added a menu item to the contact menu which allows to set avatar options (local
+ picture, locked avatar, and a "hidden" attribute).
+ The hidden attribute is set in the struct avatarCacheEntry
+
+ + added support for PNG images (imgdecoder.dll needs to be present in either the
+ main miranda directory or the \Plugins subfolder. Transparent PNG images are
+ supported and the avatar service will perform the premultiplication of alpha
+ values so that the image can (and should) be rendered with the AlphaBlend()
+ API.
+
+0.0.1.2 - 2005/08/20
+
+ + the service now creates and uses relative filenames for all contact pictures,
+ including protocol avatars, if possible.
+
+ * for protocols which are in "invisible" status, no active avatar fetching
+ is performed. This is to avoid problems with MSN and privacy issues for
+ other protocols (if you're invisible, then the protocol should not initiate
+ any outbound communications which may require direct p2p connections).
+
+ * an option page has been added. Currently, you can:
+
+ + select, for which protocols the service should ACTIVELY fetch avatars.
+ If you set a protocols to "inactive" (uncheck it), already existing
+ avatars for contacts of that protocol will continue to work, but the
+ service will no longer actively refresh avatars.
+
+ + set protocol pictures (pseudo avatars). You can select local pictures
+ for each installed protocol. These will be used as a fallback then, if
+ no avatar is available for a contact. You can also remove these pictures.
+
+ * don't fetch avatars for contacts which are either blocked or set on the
+ invisibility list (ApparentMode == ID_STATUS_OFFLINE). Reason: No active
+ outbound communications w/o user intervention should be initiated for blocked
+ contacts.
+
+ + added support for the updater plugin.
+
+0.0.1.1 - 2005/08/06
+
+ * changed API. Don't return HBITMAP directly, but instead, it returns
+ a pointer to a struct avatarCacheEntry which can be used to obtain
+ the bitmap handle.
+
+0.0.1.0 - 2005/08/05
+
+ * initial release, cvs import.
+
diff --git a/plugins/AVS/README b/plugins/AVS/README
new file mode 100644
index 0000000000..d82d1d7fec
--- /dev/null
+++ b/plugins/AVS/README
@@ -0,0 +1,94 @@
+
+ Load avatars 0.0.1.0
+ --------------------
+
+This is a SERVICE plugin, which means, it doesn't provide anything useful
+on its own except for a few service(s) and event(s) which can be used by
+other plugins.
+
+What it does?
+-------------
+
+It loads avatars on demand and maintains an internal cache of avatar
+bitmap handles. It also handles avatar changes transparently and can
+notify event subscribers about avatar changes.
+
+How it works?
+-------------
+
+The service MS_AV_GETAVATARBITMAP returns a pointer to a cache entry, if an
+avatar is present for that contact. The service MAY return 0, in which
+case, there is no valid avatar yet. However, that doesn't mean there
+isn't ANY avatar, just that the avatar is not yet ready for use. When
+someone calls the service requesting an avatar, the plugin will try
+to get the avatar (if possible) and notify all subscribers via a
+hookable event as soon as the avatar is ready. If the avatar is
+already in the cache, it simply returns the cached entry.
+
+Whenever an avatar changes, the plugin fires an event, passing the
+contacts handle in wParam and a data structure with the avatar information
+in lParam. Plugins which use the bitmap handles returned by
+MS_AV_GETAVATARBITMAP MUST subscribe to ME_AV_AVATARCHANGED, because the
+original bitmap handle may become invalid when the avatar changes.
+
+Fetching avatars is done in a separate thread with reasonable delays to
+avoid getting into troubles with flood protection(s). Avatars are cached
+"in memory".
+
+
+The included clist_nicer_plus.dll is a demonstration of how the avatar
+service works and can be used by developers. Having a central instance
+which maintains avatars saves resources and increases performance.
+
+
+// example, how to use it (FOR DEVS only)
+
+#include "m_avatars.h"
+
+struct avatarCacheEntry *ace = 0;
+HBITMAP hbmAvatar = 0;
+
+ace = (struct avatarCacheEntry *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0);
+
+/*
+ now, check the return value. if it is 0, then the avatar is not yet ready or unavailble
+ for that contact. if it was only "not ready", your plugin will be notified by the
+ hookable event ME_AV_AVATARCHANGED
+
+ if the return value is != 0, then it is a valid bitmap handle. DON'T DESTROY IT IN YOUR CODE
+*/
+
+
+/*
+ * event function
+ * initialise with:
+ * HANDLE hEvent = HookEvent(ME_AV_AVATARCHANGED, AvatarChanged);
+ */
+
+static int AvatarChanged(WPARAM wParam, LPARAM lParam)
+{
+ struct avatarCacheEntry *ace = (struct avatarCacheEntry *)lParam;
+ HANDLE hContact = (HANDLE)wParam;
+
+ if(ace == NULL)
+ return 0;
+ if(ace->cbSize != sizeof(struct avatarCacheEntry))
+ return 0; // safety check(s)
+
+ HBITMAP hbmAvatar = ace->hbmPic;
+ ...
+ ...
+}
+
+
+TODO: maybe more intelligent cache managment, including auto-expire of avatars which
+ have not been used for a while.
+
+
+Copyright and license:
+----------------------
+
+This plugin is released under the terms of the GNU general public license V2 or any later
+version.
+
+Written, 2005 by Nightwish, silvercircle@gmail.com
diff --git a/plugins/AVS/README.SOURCE b/plugins/AVS/README.SOURCE
new file mode 100644
index 0000000000..5f87280afe
--- /dev/null
+++ b/plugins/AVS/README.SOURCE
@@ -0,0 +1,25 @@
+
+Source code for the loadavatars (avatar service plugin) is available via
+anonymous SVN at:
+
+http://svn.berlios.de/svnroot/repos/mimplugins/trunk/avs/
+
+You need a SVN client to access this, I recommend TortoiseSVN for Windows which
+is easy to use and provides a good UI.
+
+To compile, you need a complete checkout of Mirandas main source repository and
+Visual C++ 6 with service pack 6 + the latest platform SDK installed.
+
+Project files are provided for Visual C++ 6 and Visual Studio 2005 (aka Visual
+C++ 8). The sources may compile with Mingw32/GCC, but this is not supported at
+this time.
+
+This plugin for the Miranda Instant messenger is licensed under the GNU
+General Public License Version 2.
+
+The code was written by:
+
+Nightwish (silvercircle@gmail.com) (original idea and implementation)
+Pescuma (major rewrite for version 0.0.2.0 with new request/poll code and most of
+ the image manipulation utilities).
+
diff --git a/plugins/AVS/acc.cpp b/plugins/AVS/acc.cpp
new file mode 100644
index 0000000000..f62009b43d
--- /dev/null
+++ b/plugins/AVS/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:
+ {
+ SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | BS_OWNERDRAW);
+ SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(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/acc.h b/plugins/AVS/acc.h
new file mode 100644
index 0000000000..11b235adc0
--- /dev/null
+++ b/plugins/AVS/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/avs.rc b/plugins/AVS/avs.rc
new file mode 100644
index 0000000000..e1818ede8d
--- /dev/null
+++ b/plugins/AVS/avs.rc
@@ -0,0 +1,251 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutral resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPTIONS_PICTS DIALOGEX 0, 0, 299, 214
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_CLIPSIBLINGS
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CTEXT "The pictures you can set here are used as default avatars\nfor contacts that don't have their own.\nUse the checkboxes to enable/disable showing avatars for the protocols.",IDC_STATIC,0,13,294,27
+ CONTROL "",IDC_PROTOCOLS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_AUTOARRANGE | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,19,83,150,85
+ PUSHBUTTON "Set default picture",IDC_SETPROTOPIC,176,83,106,14
+ PUSHBUTTON "Delete default picture",IDC_REMOVEPROTOPIC,176,99,106,14
+ CONTROL "",IDC_PROTOPIC,"Button",BS_OWNERDRAW,197,118,56,50
+ EDITTEXT IDC_PROTOAVATARNAME,18,176,264,30,ES_MULTILINE | ES_READONLY,WS_EX_CLIENTEDGE
+ CTEXT "CAUTION: These pictures are NOT your own avatars.\nTo set your own Avatar goto Main Menu | View/Change My Details | Avatars",IDC_STATIC,0,53,294,18
+END
+
+IDD_OPTIONS_AVATARS DIALOGEX 0, 0, 302, 189
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_CLIPSIBLINGS
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "Try to draw avatar background transparent (for images without transparency)",IDC_MAKE_TRANSPARENT_BKG,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,19,17,278,12
+ LTEXT "Num of points to define bkg:",IDC_BKG_NUM_POINTS_L,33,31,135,11
+ EDITTEXT IDC_BKG_NUM_POINTS,173,30,36,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_BKG_NUM_POINTS_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,209,30,11,12
+ LTEXT "Color difference allowed:",IDC_BKG_COLOR_DIFFERENCE_L,33,44,135,11
+ EDITTEXT IDC_BKG_COLOR_DIFFERENCE,173,43,36,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_BKG_COLOR_DIFFERENCE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,209,42,11,12
+ CONTROL "Make transparency proportional to color diff",IDC_MAKE_TRANSP_PROPORTIONAL,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,57,247,10
+ CONTROL "Draw avatars grayscale",IDC_MAKE_GRAYSCALE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,19,72,269,13
+ CONTROL "Show warning messages",IDC_SHOWWARNINGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,19,87,269,13
+END
+
+IDD_OPTIONS_OWN DIALOGEX 0, 0, 302, 75
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_CLIPSIBLINGS
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "When setting avatars, always make them square",IDC_SET_MAKE_SQUARE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,17,281,11
+ CONTROL "Try to draw own avatar background transparent (for images without transparency)",IDC_MAKE_MY_AVATARS_TRANSP,
+ "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,7,30,289,16
+ LTEXT "This uses the same additional options as in ""Contact Avatars"" tab",IDC_STATIC,17,50,257,13
+END
+
+IDD_OPENSUBCLASS DIALOGEX 0, 0, 246, 18
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "Protect the picture and prevent automatic avatars from overwriting it",IDC_PROTECTAVATAR,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,237,8
+END
+
+IDD_SET_OWN_SUBCLASS DIALOG 0, 0, 323, 31
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "Make the avatar square",IDC_MAKE_SQUARE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,68,0,237,8
+ CONTROL "Resize the avatar to fit max allowed protocol size",IDC_GROW,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,68,12,237,8
+END
+
+IDD_AVATAROPTIONS DIALOGEX 0, 0, 213, 212
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Contact picture",IDC_STATIC,5,5,202,96
+ CONTROL "",IDC_PROTOPIC,"Button",BS_OWNERDRAW,12,16,56,50
+ PUSHBUTTON "Change",IDC_CHANGE,138,15,61,14
+ PUSHBUTTON "Delete",IDC_DELETE,138,32,61,14
+ PUSHBUTTON "Reset",IDC_RESET,138,49,61,14
+ EDITTEXT IDC_AVATARNAME,12,68,187,29,ES_MULTILINE | ES_READONLY,WS_EX_CLIENTEDGE
+ GROUPBOX "Picture options",IDC_STATIC,5,102,202,92
+ CONTROL "Protect the picture",IDC_PROTECTAVATAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,112,186,8
+ CONTROL "Set as hidden",IDC_HIDEAVATAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,122,186,11
+ CONTROL "Try to make picture background transparent",IDC_MAKETRANSPBKG,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,133,186,11
+ LTEXT "Num of points to define bkg:",IDC_BKG_NUM_POINTS_L,23,148,118,11
+ EDITTEXT IDC_BKG_NUM_POINTS,145,146,42,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_BKG_NUM_POINTS_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,187,147,11,12
+ LTEXT "Color difference allowed:",IDC_BKG_COLOR_DIFFERENCE_L,23,162,118,11
+ EDITTEXT IDC_BKG_COLOR_DIFFERENCE,145,160,42,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_BKG_COLOR_DIFFERENCE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,188,160,11,12
+ DEFPUSHBUTTON "Use defaults",ID_USE_DEFAULTS,11,177,63,12
+ DEFPUSHBUTTON "OK",IDOK,93,197,50,14
+ DEFPUSHBUTTON "Cancel",IDCANCEL,151,197,50,14
+END
+
+IDD_USER_AVATAR DIALOGEX 0, 0, 222, 152
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "",IDC_PROTOPIC,"MAvatarControlClass",0x0,3,4,96,89
+ PUSHBUTTON "Change",IDC_CHANGE,3,102,96,14
+ PUSHBUTTON "Delete",IDC_DELETE,3,118,96,14
+ PUSHBUTTON "Reset",IDC_RESET,3,134,96,14
+ GROUPBOX " Options ",IDC_STATIC,107,3,111,50
+ CONTROL "Protect the picture",IDC_PROTECTAVATAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,118,18,93,8
+ CONTROL "Set as hidden",IDC_HIDEAVATAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,118,34,93,11
+ GROUPBOX " Background ",IDC_STATIC,107,59,111,89
+ CONTROL "Transparent",IDC_MAKETRANSPBKG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,118,74,93,11
+ LTEXT "Points:",IDC_BKG_NUM_POINTS_L,129,91,38,11
+ EDITTEXT IDC_BKG_NUM_POINTS,169,89,42,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_BKG_NUM_POINTS_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,199,80,11,12
+ LTEXT "Color diff:",IDC_BKG_COLOR_DIFFERENCE_L,129,108,38,11
+ EDITTEXT IDC_BKG_COLOR_DIFFERENCE,169,106,42,12,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_BKG_COLOR_DIFFERENCE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,199,117,11,12
+ PUSHBUTTON "Use defaults",ID_USE_DEFAULTS,118,130,63,12
+END
+
+IDD_PROTO_AVATARS DIALOGEX 0, 0, 222, 152
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "",IDC_PROTOCOLS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_AUTOARRANGE | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,3,4,101,126
+ CONTROL "",IDC_PROTOPIC,"MAvatarControlClass",0x0,114,4,96,89
+ PUSHBUTTON "Set",IDC_CHANGE,114,100,96,14
+ PUSHBUTTON "Delete",IDC_DELETE,114,116,96,14
+ CONTROL "Use per protocol avatars",IDC_PER_PROTO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,137,215,11
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_OPTIONS_PICTS, DIALOG
+ BEGIN
+ RIGHTMARGIN, 294
+ END
+
+ IDD_OPENSUBCLASS, DIALOG
+ BEGIN
+ RIGHTMARGIN, 208
+ TOPMARGIN, 7
+ END
+
+ IDD_AVATAROPTIONS, DIALOG
+ BEGIN
+ RIGHTMARGIN, 207
+ BOTTOMMARGIN, 211
+ END
+
+ IDD_USER_AVATAR, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 218
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 148
+ END
+
+ IDD_PROTO_AVATARS, DIALOG
+ BEGIN
+ LEFTMARGIN, 3
+ RIGHTMARGIN, 218
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 148
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_AVATAR ICON "res/avatar.ico"
+#endif // Neutral resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// German (Germany) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
+#ifdef _WIN32
+LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // German (Germany) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/plugins/AVS/avs_10.vcxproj b/plugins/AVS/avs_10.vcxproj
new file mode 100644
index 0000000000..c512086a8d
--- /dev/null
+++ b/plugins/AVS/avs_10.vcxproj
@@ -0,0 +1,290 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>AVS</ProjectName>
+ <ProjectGuid>{7711F563-6473-4ABD-B5E3-477CE8384AD6}</ProjectGuid>
+ <RootNamespace>avs</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</IgnoreImportLibrary>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(Configuration)64/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(Configuration)64/Obj/$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</IgnoreImportLibrary>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</IgnoreImportLibrary>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Configuration)64/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Configuration)64/Obj/$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</IgnoreImportLibrary>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Debug_Unicode/avatars.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;UNICODE;_USRDLL;LOADAVATARS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>commonheaders.h</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../include/msapi</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <SubSystem>Windows</SubSystem>
+ <BaseAddress>0x5130000</BaseAddress>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Debug_Unicode/avatars.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;UNICODE;_USRDLL;LOADAVATARS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>commonheaders.h</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0809</Culture>
+ <AdditionalIncludeDirectories>../../include/msapi</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalManifestDependencies>type=%27Win32%27 name=%27Microsoft.Windows.Common-Controls%27 version=%276.0.0.0%27 processorArchitecture=%27*%27 publicKeyToken=%276595b64144ccf1df%27 language=%27*%27;%(AdditionalManifestDependencies)</AdditionalManifestDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX64</TargetMachine>
+ <BaseAddress>0x5130000</BaseAddress>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Release_Unicode/avatars.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;UNICODE;_USRDLL;LOADAVATARS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>commonheaders.h</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../include/msapi</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/IGNORE:4089 /filealign:512 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>comctl32.lib;msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress>0x5130000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Release_Unicode/avatars.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;UNICODE;_USRDLL;LOADAVATARS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>commonheaders.h</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../include/msapi</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/IGNORE:4089 /filealign:512 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>comctl32.lib;msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalManifestDependencies>type=%27Win32%27 name=%27Microsoft.Windows.Common-Controls%27 version=%276.0.0.0%27 processorArchitecture=%27*%27 publicKeyToken=%276595b64144ccf1df%27 language=%27*%27;%(AdditionalManifestDependencies)</AdditionalManifestDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress>0x5130000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="acc.cpp" />
+ <ClCompile Include="image_utils.cpp" />
+ <ClCompile Include="main.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="mir_thread.cpp" />
+ <ClCompile Include="options.cpp" />
+ <ClCompile Include="poll.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="acc.h" />
+ <ClInclude Include="commonheaders.h" />
+ <ClInclude Include="image_utils.h" />
+ <ClInclude Include="mir_thread.h" />
+ <ClInclude Include="poll.h" />
+ <ClInclude Include="version.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="res\avatar.ico" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="avs.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="version.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/AVS/avs_10.vcxproj.filters b/plugins/AVS/avs_10.vcxproj.filters
new file mode 100644
index 0000000000..6f6d39f743
--- /dev/null
+++ b/plugins/AVS/avs_10.vcxproj.filters
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{8803e79e-1c62-4fcc-8daf-35634ce5e990}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{533b0c62-6839-47b0-bf0d-03e20a1a4e06}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{214ac298-955e-4931-a01c-3516a7206928}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="acc.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="image_utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="mir_thread.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="options.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="poll.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="acc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="commonheaders.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="image_utils.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="mir_thread.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="poll.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="version.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="res\avatar.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="avs.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="version.rc">
+ <Filter>Source Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/AVS/commonheaders.h b/plugins/AVS/commonheaders.h
new file mode 100644
index 0000000000..70b0c88fae
--- /dev/null
+++ b/plugins/AVS/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/image_utils.cpp b/plugins/AVS/image_utils.cpp
new file mode 100644
index 0000000000..f6b6c7a98b
--- /dev/null
+++ b/plugins/AVS/image_utils.cpp
@@ -0,0 +1,757 @@
+#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;
+}
+
+#if defined(_UNICODE)
+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;
+}
+#endif
+
+// 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 defined( _UNICODE )
+ 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);
+ }
+ #endif
+
+ return result;
+}
diff --git a/plugins/AVS/image_utils.h b/plugins/AVS/image_utils.h
new file mode 100644
index 0000000000..2dd75d8211
--- /dev/null
+++ b/plugins/AVS/image_utils.h
@@ -0,0 +1,58 @@
+#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);
+#if defined(_UNICODE)
+ INT_PTR BmpFilterSaveBitmapW(WPARAM wParam,LPARAM lParam);
+ #define BmpFilterSaveBitmapT BmpFilterSaveBitmapW
+#else
+ #define BmpFilterSaveBitmapT BmpFilterSaveBitmap
+#endif
+
+// 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);
+#if defined(_UNICODE)
+ int BmpFilterSaveBitmapW(HBITMAP hBmp, wchar_t *wszFile, int flags);
+ #define BmpFilterSaveBitmapT BmpFilterSaveBitmapW
+#else
+ #define BmpFilterSaveBitmapT BmpFilterSaveBitmap
+#endif
+
+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/license.txt b/plugins/AVS/license.txt
new file mode 100644
index 0000000000..7f1161073d
--- /dev/null
+++ b/plugins/AVS/license.txt
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/plugins/AVS/main.cpp b/plugins/AVS/main.cpp
new file mode 100644
index 0000000000..bb4ebc236f
--- /dev/null
+++ b/plugins/AVS/main.cpp
@@ -0,0 +1,2684 @@
+/*
+
+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;
+PLUGINLINK *pluginLink;
+MM_INTERFACE mmi;
+LIST_INTERFACE li;
+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),
+#if defined(_UNICODE)
+ "Avatar service (Unicode)",
+#else
+ "Avatar service",
+#endif
+ __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,
+ 0,
+#if defined(_UNICODE)
+// {E00F1643-263C-4599-B84B-053E5C511D29}
+ { 0xe00f1643, 0x263c, 0x4599, { 0xb8, 0x4b, 0x5, 0x3e, 0x5c, 0x51, 0x1d, 0x29 } }
+#else
+// {C9E01EB0-A119-42d2-B340-E8678F5FEAD8}
+ { 0xc9e01eb0, 0xa119, 0x42d2, { 0xb3, 0x40, 0xe8, 0x67, 0x8f, 0x5f, 0xea, 0xd8 } }
+#endif
+};
+
+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';
+ }
+#if defined( _UNICODE )
+ 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 ));
+ }
+#endif
+ 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 ));
+}
+
+#if defined( _UNICODE )
+INT_PTR SetAvatarW(WPARAM wParam, LPARAM lParam)
+{ return avSetAvatar(( HANDLE )wParam, ( TCHAR* )lParam );
+}
+#endif
+
+/*
+ * 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 ));
+}
+
+#if defined( _UNICODE )
+static INT_PTR SetMyAvatarW( WPARAM wParam, LPARAM lParam )
+{ return avSetMyAvatar(( char* )wParam, ( TCHAR* )lParam );
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+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;
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = 0;
+ odp.hInstance = g_hInst;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY | ODPF_TCHAR;
+ odp.ptszGroup = LPGENT("Customize");
+ odp.ptszTitle = LPGENT("Avatars");
+
+ odp.ptszTab = LPGENT("Protocols");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_PICTS);
+ odp.pfnDlgProc = DlgProcOptionsProtos;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+
+ odp.ptszTab = LPGENT("Contact Avatars");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_AVATARS);
+ odp.pfnDlgProc = DlgProcOptionsAvatars;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+
+ odp.ptszTab = LPGENT("Own Avatars");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_OWN);
+ odp.pfnDlgProc = DlgProcOptionsOwn;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &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.flags = ODPF_TCHAR;
+ odp.hIcon = g_hIcon;
+ odp.hInstance = g_hInst;
+ odp.pfnDlgProc = DlgProcAvatarProtoInfo;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_PROTO_AVATARS);
+ odp.ptszTitle = LPGENT("Avatar");
+ CallService(MS_USERINFO_ADDPAGE, wParam, (LPARAM)&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.flags = ODPF_TCHAR;
+ odp.hIcon = g_hIcon;
+ odp.hInstance = g_hInst;
+ odp.pfnDlgProc = DlgProcAvatarUserInfo;
+ odp.position = -2000000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_USER_AVATAR);
+ odp.ptszTitle = LPGENT("Avatar");
+ CallService(MS_USERINFO_ADDPAGE, wParam, (LPARAM)&odp);
+ }
+ }
+ return 0;
+}
+
+static int LoadAvatarModule()
+{
+ mir_getMMI ( &mmi );
+ mir_getLI ( &li );
+ 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 ));
+
+ #if defined( _UNICODE )
+ arServices.insert( CreateServiceFunction( MS_AV_SETAVATARW, SetAvatarW ));
+ arServices.insert( CreateServiceFunction( MS_AV_SETMYAVATARW, SetMyAvatarW ));
+ #endif
+
+ 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)
+{
+ if (mirandaVersion < MIRANDA_VERSION_CORE)
+ return NULL;
+ 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(PLUGINLINK * link)
+{
+ INT_PTR result = CALLSERVICE_NOTFOUND;
+
+ pluginLink = link;
+ 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/mir_thread.cpp b/plugins/AVS/mir_thread.cpp
new file mode 100644
index 0000000000..d12a0c9731
--- /dev/null
+++ b/plugins/AVS/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/mir_thread.h b/plugins/AVS/mir_thread.h
new file mode 100644
index 0000000000..0f4f20fe8e
--- /dev/null
+++ b/plugins/AVS/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/options.cpp b/plugins/AVS/options.cpp
new file mode 100644
index 0000000000..58502a9655
--- /dev/null
+++ b/plugins/AVS/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/poll.cpp b/plugins/AVS/poll.cpp
new file mode 100644
index 0000000000..32553a9c33
--- /dev/null
+++ b/plugins/AVS/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);
+#ifdef _UNICODE
+ 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;
+ }
+#endif
+ 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/poll.h b/plugins/AVS/poll.h
new file mode 100644
index 0000000000..9761e7360a
--- /dev/null
+++ b/plugins/AVS/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/res/avatar.ico b/plugins/AVS/res/avatar.ico
new file mode 100644
index 0000000000..3525625479
--- /dev/null
+++ b/plugins/AVS/res/avatar.ico
Binary files differ
diff --git a/plugins/AVS/resource.h b/plugins/AVS/resource.h
new file mode 100644
index 0000000000..2d8ba9add0
--- /dev/null
+++ b/plugins/AVS/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/vc6.rc b/plugins/AVS/vc6.rc
new file mode 100644
index 0000000000..99a8f9da1d
--- /dev/null
+++ b/plugins/AVS/vc6.rc
@@ -0,0 +1,2 @@
+#include "avs.rc"
+#include "version.rc"
diff --git a/plugins/AVS/version.h b/plugins/AVS/version.h
new file mode 100644
index 0000000000..826d9fdefb
--- /dev/null
+++ b/plugins/AVS/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
diff --git a/plugins/AVS/version.rc b/plugins/AVS/version.rc
new file mode 100644
index 0000000000..43d374df60
--- /dev/null
+++ b/plugins/AVS/version.rc
@@ -0,0 +1,113 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+#include "version.h"
+#include "winres.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page( 1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "\r\n"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+END
+
+#endif // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION __FILEVERSION_STRING
+ PRODUCTVERSION __FILEVERSION_STRING
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+#if !defined(UNICODE)
+ VALUE "Comments", "Service plugin to load and update avatars for Miranda IM"
+ VALUE "CompanyName", "Written by Nightwish and Pescuma for Miranda IM project"
+ VALUE "FileDescription", "Service plugin to load and update avatars for Miranda IM"
+ VALUE "FileVersion", __FILEVERSION_STRING
+ VALUE "InternalName", "avs"
+ VALUE "LegalCopyright", "Copyright (C) 2010, Nightwish, Pescuma"
+ VALUE "LegalTrademarks", "Licensed under the Gnu General Public License V2 or any later version"
+ VALUE "OriginalFilename", "avs.dll"
+ VALUE "ProductName", "Miranda IM Avatar Service plugin"
+ VALUE "ProductVersion", __FILEVERSION_STRING
+#else
+ VALUE "Comments", "Service plugin to load and update avatars for Miranda IM"
+ VALUE "CompanyName", "Written by Nightwish and Pescuma for Miranda IM project"
+ VALUE "FileDescription", "Service plugin to load and update avatars for Miranda IM"
+ VALUE "FileVersion", __FILEVERSION_STRING
+ VALUE "InternalName", "avsW"
+ VALUE "LegalCopyright", "Copyright (C) 2010, Nightwish, Pescuma"
+ VALUE "LegalTrademarks", "Licensed under the Gnu General Public License V2 or any later version"
+ VALUE "OriginalFilename", "avs.dll"
+ VALUE "ProductName", "Miranda IM Avatar Service (Unicode)"
+ VALUE "ProductVersion", __FILEVERSION_STRING
+#endif
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
+#endif // AFX_RESOURCE_DLL