diff options
author | Kirill Volinsky <mataes2007@gmail.com> | 2012-05-17 17:37:22 +0000 |
---|---|---|
committer | Kirill Volinsky <mataes2007@gmail.com> | 2012-05-17 17:37:22 +0000 |
commit | 78d71d2cad6f243c6ff31d41380b8c5b58407de5 (patch) | |
tree | d0c05983b315352c5e66d23420da4b8fd8b5aff4 /plugins/FavContacts/src | |
parent | a9e8daee448c229aa3f8ded0c5f5c0fe7aa42529 (diff) |
added some plugins
git-svn-id: http://svn.miranda-ng.org/main/trunk@20 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/FavContacts/src')
-rw-r--r-- | plugins/FavContacts/src/contact_cache.cpp | 231 | ||||
-rw-r--r-- | plugins/FavContacts/src/contact_cache.h | 61 | ||||
-rw-r--r-- | plugins/FavContacts/src/cserver.cpp | 82 | ||||
-rw-r--r-- | plugins/FavContacts/src/cserver.h | 42 | ||||
-rw-r--r-- | plugins/FavContacts/src/csocket.cpp | 22 | ||||
-rw-r--r-- | plugins/FavContacts/src/csocket.h | 16 | ||||
-rw-r--r-- | plugins/FavContacts/src/favlist.cpp | 0 | ||||
-rw-r--r-- | plugins/FavContacts/src/favlist.h | 134 | ||||
-rw-r--r-- | plugins/FavContacts/src/headers.h | 94 | ||||
-rw-r--r-- | plugins/FavContacts/src/http_api.cpp | 165 | ||||
-rw-r--r-- | plugins/FavContacts/src/http_api.h | 7 | ||||
-rw-r--r-- | plugins/FavContacts/src/main.cpp | 1453 |
12 files changed, 2307 insertions, 0 deletions
diff --git a/plugins/FavContacts/src/contact_cache.cpp b/plugins/FavContacts/src/contact_cache.cpp new file mode 100644 index 0000000000..c61635cc26 --- /dev/null +++ b/plugins/FavContacts/src/contact_cache.cpp @@ -0,0 +1,231 @@ +#include "headers.h"
+#include <time.h>
+#include <math.h>
+
+int __cdecl CContactCache::OnDbEventAdded(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ HANDLE hEvent = (HANDLE)lParam;
+
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)&dbei);
+ if (dbei.eventType != EVENTTYPE_MESSAGE) return 0;
+
+ float weight = GetEventWeight(time(NULL) - dbei.timestamp);
+ float q = GetTimeWeight(time(NULL) - m_lastUpdate);
+ m_lastUpdate = time(NULL);
+ if (!weight) return 0;
+
+ Lock();
+ bool found = false;
+ for (int i = 0; i < m_cache.getCount(); ++i)
+ {
+ m_cache[i].rate *= q;
+ if (m_cache[i].hContact == hContact)
+ {
+ found = true;
+ m_cache[i].rate += weight;
+ }
+ }
+
+ if (!found)
+ {
+ TContactInfo *info = new TContactInfo;
+ info->hContact = hContact;
+ info->rate = weight;
+ m_cache.insert(info);
+ } else
+ {
+ qsort(m_cache.getArray(), m_cache.getCount(), sizeof(TContactInfo *), TContactInfo::cmp2);
+ }
+
+ Unlock();
+ return 0;
+}
+
+float CContactCache::GetEventWeight(unsigned long age)
+{
+ const float ceil = 1000.f;
+ const float floor = 0.0001f;
+ const int depth = 60 * 60 * 24 * 30;
+ if (age > depth) return 0;
+ return exp(log(ceil) - age * (log(ceil) - log(floor)) / depth);
+}
+
+float CContactCache::GetTimeWeight(unsigned long age)
+{
+ const float ceil = 1000.f;
+ const float floor = 0.0001f;
+ const int depth = 60 * 60 * 24 * 30;
+ if (age > depth) return 0;
+ return exp(age * (log(ceil) - log(floor)) / depth);
+}
+
+CContactCache::CContactCache(): m_cache(50, TContactInfo::cmp)
+{
+ InitializeCriticalSection(&m_cs);
+
+ int (__cdecl CContactCache::*pfn)(WPARAM, LPARAM);
+ pfn = &CContactCache::OnDbEventAdded;
+ m_hOnDbEventAdded = HookEventObj(ME_DB_EVENT_ADDED, *(MIRANDAHOOKOBJ *)&pfn, this);
+
+ Rebuild();
+}
+
+CContactCache::~CContactCache()
+{
+ UnhookEvent(m_hOnDbEventAdded);
+ DeleteCriticalSection(&m_cs);
+}
+
+void CContactCache::Rebuild()
+{
+ unsigned long timestamp = time(NULL);
+ m_lastUpdate = time(NULL);
+
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ TContactInfo *info = new TContactInfo;
+ info->hContact = hContact;
+ info->rate = 0;
+
+ HANDLE hEvent = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0);
+ while (hEvent)
+ {
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ if (!CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)&dbei))
+ {
+ if (float weight = GetEventWeight(timestamp - dbei.timestamp))
+ {
+ if (dbei.eventType == EVENTTYPE_MESSAGE)
+ info->rate += weight;
+ } else
+ {
+ break;
+ }
+ }
+ hEvent = (HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hEvent, 0);
+ }
+
+ m_cache.insert(info);
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+}
+
+HANDLE CContactCache::get(int rate)
+{
+ if (rate >= 0 && rate < m_cache.getCount())
+ return m_cache[rate].hContact;
+ return NULL;
+}
+
+float CContactCache::getWeight(int rate)
+{
+ if (rate >= 0 && rate < m_cache.getCount())
+ return m_cache[rate].rate;
+ return -1;
+}
+
+static bool AppendInfo(TCHAR *buf, int size, HANDLE hContact, int info)
+{
+ CONTACTINFO ci = {0};
+ ci.cbSize = sizeof(ci);
+ ci.hContact = hContact;
+ ci.dwFlag = info;
+#if defined(UNICODE) || defined(_UNICODE)
+ ci.dwFlag |= CNF_UNICODE;
+#endif
+
+ bool ret = false;
+
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci) && (ci.type == CNFT_ASCIIZ) && ci.pszVal)
+ {
+ if (*ci.pszVal && (lstrlen(ci.pszVal) < size-2))
+ {
+ lstrcpy(buf, ci.pszVal);
+ ret = true;
+ }
+ mir_free(ci.pszVal);
+ }
+
+ return ret;
+}
+
+void CContactCache::TContactInfo::LoadInfo()
+{
+ if (infoLoaded) return;
+ TCHAR *p = info;
+
+ p[0] = p[1] = 0;
+
+ static const int items[] = {
+ CNF_FIRSTNAME, CNF_LASTNAME, CNF_NICK , CNF_CUSTOMNICK, CNF_EMAIL, CNF_CITY, CNF_STATE,
+ CNF_COUNTRY, CNF_PHONE, CNF_HOMEPAGE, CNF_ABOUT, CNF_UNIQUEID, CNF_MYNOTES, CNF_STREET,
+ CNF_CONAME, CNF_CODEPT, CNF_COCITY, CNF_COSTATE, CNF_COSTREET, CNF_COCOUNTRY
+ };
+
+ for (int i = 0; i < SIZEOF(items); ++i)
+ {
+ if (AppendInfo(p, SIZEOF(info) - (p - info), hContact, items[i]))
+ p += lstrlen(p) + 1;
+ }
+ *p = 0;
+
+ infoLoaded = true;
+}
+
+TCHAR *nb_stristr(TCHAR *str, TCHAR *substr)
+{
+ if (!substr || !*substr) return str;
+ if (!str || !*str) return NULL;
+
+ TCHAR *str_up = NEWTSTR_ALLOCA(str);
+ TCHAR *substr_up = NEWTSTR_ALLOCA(substr);
+
+ CharUpperBuff(str_up, lstrlen(str_up));
+ CharUpperBuff(substr_up, lstrlen(substr_up));
+
+ TCHAR* p = _tcsstr(str_up, substr_up);
+ return p ? (str + (p - str_up)) : NULL;
+}
+
+bool CContactCache::filter(int rate, TCHAR *str)
+{
+ if (!str || !*str)
+ return true;
+ m_cache[rate].LoadInfo();
+
+ HKL kbdLayoutActive = GetKeyboardLayout(GetCurrentThreadId());
+ HKL kbdLayouts[10];
+ int nKbdLayouts = GetKeyboardLayoutList(SIZEOF(kbdLayouts), kbdLayouts);
+
+ TCHAR buf[256];
+ BYTE keyState[256] = {0};
+
+ for (int iLayout = 0; iLayout < nKbdLayouts; ++iLayout)
+ {
+ if (kbdLayoutActive == kbdLayouts[iLayout])
+ {
+ lstrcpy(buf, str);
+ } else
+ {
+ int i;
+ for (i = 0; str[i]; ++i)
+ {
+ UINT vk = VkKeyScanEx(str[i], kbdLayoutActive);
+ UINT scan = MapVirtualKeyEx(vk, 0, kbdLayoutActive);
+ ToUnicodeEx(vk, scan, keyState, buf+i, SIZEOF(buf)-i, 0, kbdLayouts[iLayout]);
+ }
+ buf[i] = 0;
+ }
+
+ for (TCHAR *p = m_cache[rate].info; p && *p; p = p + lstrlen(p) + 1)
+ if (nb_stristr(p, buf))
+ return true;
+ }
+
+ return false;
+}
diff --git a/plugins/FavContacts/src/contact_cache.h b/plugins/FavContacts/src/contact_cache.h new file mode 100644 index 0000000000..905ba4b469 --- /dev/null +++ b/plugins/FavContacts/src/contact_cache.h @@ -0,0 +1,61 @@ +#ifndef __contact_cache__
+#define __contact_cache__
+
+class CContactCache
+{
+public:
+ enum { INFOSIZE = 1024 };
+
+private:
+
+ struct TContactInfo
+ {
+ HANDLE hContact;
+ float rate;
+ TCHAR info[INFOSIZE];
+ bool infoLoaded;
+
+ static int cmp(const TContactInfo *p1, const TContactInfo *p2)
+ {
+ if (p1->rate > p2->rate) return -1;
+ if (p1->rate < p2->rate) return 1;
+ return 0;
+ }
+
+ static int cmp2(const void *a1, const void *a2)
+ {
+ return cmp(*(const TContactInfo **)a1, *(const TContactInfo **)a2);
+ }
+
+ TContactInfo()
+ {
+ info[0] = info[1] = 0;
+ infoLoaded = false;
+ }
+
+ void LoadInfo();
+ };
+
+ OBJLIST<TContactInfo> m_cache;
+ unsigned long m_lastUpdate;
+ CRITICAL_SECTION m_cs;
+ HANDLE m_hOnDbEventAdded;
+
+ int __cdecl OnDbEventAdded(WPARAM wParam, LPARAM lParam);
+ float GetEventWeight(unsigned long age);
+ float GetTimeWeight(unsigned long age);
+
+public:
+ CContactCache();
+ ~CContactCache();
+
+ void Lock() { EnterCriticalSection(&m_cs); }
+ void Unlock() { LeaveCriticalSection(&m_cs); }
+ void Rebuild();
+
+ HANDLE get(int rate);
+ float getWeight(int rate);
+ bool filter(int rate, TCHAR *str);
+};
+
+#endif // __contact_cache__
diff --git a/plugins/FavContacts/src/cserver.cpp b/plugins/FavContacts/src/cserver.cpp new file mode 100644 index 0000000000..957d1a6048 --- /dev/null +++ b/plugins/FavContacts/src/cserver.cpp @@ -0,0 +1,82 @@ +#include <winsock2.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+
+#include "csocket.h"
+#include "cserver.h"
+
+void CServer::Start(int port, IConnectionProcessorFactory *connectionProcessorFactory, bool background)
+{
+ m_socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (m_socket == INVALID_SOCKET) return;
+
+ sockaddr_in addr = {0};
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_port = htons((WORD)port);
+ if (bind(m_socket, (sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
+ {
+ closesocket(m_socket);
+ m_socket = INVALID_SOCKET;
+ return;
+ }
+
+ listen(m_socket, SOMAXCONN);
+
+ m_connectionProcessorFactory = connectionProcessorFactory;
+
+ if (background)
+ {
+ CreateThread(NULL, 0,
+ GlobalConnectionAcceptThread,
+ this,
+ 0, NULL);
+ } else
+ {
+ ConnectionAcceptThread();
+ }
+}
+
+void CServer::Stop()
+{
+ shutdown(m_socket, SD_BOTH);
+ closesocket(m_socket);
+}
+
+DWORD CServer::ConnectionAcceptThread()
+{
+ while (1)
+ {
+ SOCKET s = accept(m_socket, NULL, NULL);
+ if (s == INVALID_SOCKET) break;
+
+ CreateThread(NULL, 0,
+ GlobalConnectionProcessThread,
+ new GlobalConnectionProcessThreadArgs(this, s),
+ 0, NULL);
+ }
+ return 0;
+}
+
+DWORD CServer::ConnectionProcessThread(SOCKET s)
+{
+ CSocket sock(s);
+ IConnectionProcessor *processor = m_connectionProcessorFactory->Create(&sock);
+ processor->ProcessConnection();
+ delete processor;
+ return 0;
+}
+
+DWORD WINAPI CServer::GlobalConnectionAcceptThread(void *arg)
+{
+ CServer *server = (CServer *)arg;
+ return server->ConnectionAcceptThread();
+}
+
+DWORD WINAPI CServer::GlobalConnectionProcessThread(void *arg)
+{
+ GlobalConnectionProcessThreadArgs *args = (GlobalConnectionProcessThreadArgs *)arg;
+ DWORD result = args->m_server->ConnectionProcessThread(args->m_socket);
+ delete args;
+ return result;
+}
diff --git a/plugins/FavContacts/src/cserver.h b/plugins/FavContacts/src/cserver.h new file mode 100644 index 0000000000..007a341896 --- /dev/null +++ b/plugins/FavContacts/src/cserver.h @@ -0,0 +1,42 @@ +#ifndef cserver_h__
+#define cserver_h__
+
+class IConnectionProcessor
+{
+public:
+ virtual ~IConnectionProcessor() {}
+ virtual void ProcessConnection() = 0;
+};
+
+class IConnectionProcessorFactory
+{
+public:
+ virtual IConnectionProcessor *Create(CSocket *s) = 0;
+};
+
+class CServer
+{
+private:
+ SOCKET m_socket;
+ IConnectionProcessorFactory *m_connectionProcessorFactory;
+
+ DWORD ConnectionAcceptThread();
+ DWORD ConnectionProcessThread(SOCKET s);
+
+ static DWORD WINAPI GlobalConnectionAcceptThread(void *arg);
+
+ struct GlobalConnectionProcessThreadArgs
+ {
+ CServer *m_server;
+ SOCKET m_socket;
+
+ GlobalConnectionProcessThreadArgs(CServer *server, SOCKET s): m_server(server), m_socket(s) {}
+ };
+ static DWORD WINAPI GlobalConnectionProcessThread(void *arg);
+
+public:
+ void Start(int port, IConnectionProcessorFactory *connectionProcessorFactory, bool background);
+ void Stop();
+};
+
+#endif // cserver_h__
diff --git a/plugins/FavContacts/src/csocket.cpp b/plugins/FavContacts/src/csocket.cpp new file mode 100644 index 0000000000..dfff16fbf1 --- /dev/null +++ b/plugins/FavContacts/src/csocket.cpp @@ -0,0 +1,22 @@ +#include <winsock2.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+
+#include "csocket.h"
+
+int CSocket::Recv(char *buf, int count)
+{
+ return recv(m_socket, buf, count, 0);
+}
+
+int CSocket::Send(char *buf, int count)
+{
+ if (count < 0) count = strlen(buf);
+ return send(m_socket, buf, count, 0);
+}
+
+void CSocket::Close()
+{
+ shutdown(m_socket, SD_BOTH);
+ closesocket(m_socket);
+}
diff --git a/plugins/FavContacts/src/csocket.h b/plugins/FavContacts/src/csocket.h new file mode 100644 index 0000000000..3fa1c87852 --- /dev/null +++ b/plugins/FavContacts/src/csocket.h @@ -0,0 +1,16 @@ +#ifndef csocket_h__
+#define csocket_h__
+
+class CSocket
+{
+protected:
+ SOCKET m_socket;
+
+public:
+ CSocket(SOCKET socket = INVALID_SOCKET): m_socket(socket) {}
+ int Recv(char *buf, int count);
+ int Send(char *buf, int count = -1);
+ void Close();
+};
+
+#endif // csocket_h__
diff --git a/plugins/FavContacts/src/favlist.cpp b/plugins/FavContacts/src/favlist.cpp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/plugins/FavContacts/src/favlist.cpp diff --git a/plugins/FavContacts/src/favlist.h b/plugins/FavContacts/src/favlist.h new file mode 100644 index 0000000000..6201126b44 --- /dev/null +++ b/plugins/FavContacts/src/favlist.h @@ -0,0 +1,134 @@ +#ifndef favlist_h__
+#define favlist_h__
+
+struct TContactInfo
+{
+private:
+ HANDLE hContact;
+ DWORD status;
+ TCHAR *name;
+ TCHAR *group;
+ bool bManual;
+ float fRate;
+
+public:
+ TContactInfo(HANDLE hContact, bool bManual, float fRate = 0)
+ {
+ DBVARIANT dbv = {0};
+
+ this->hContact = hContact;
+ this->bManual = bManual;
+ this->fRate = fRate;
+ name = mir_tstrdup((TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR));
+ if (g_Options.bUseGroups && !DBGetContactSettingTString(hContact, "CList", "Group", &dbv))
+ {
+ group = mir_tstrdup(dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else
+ if (g_Options.bUseGroups)
+ {
+ group = mir_tstrdup(TranslateT("<no group>"));
+ } else
+ {
+ group = mir_tstrdup(TranslateT("Favourite Contacts"));
+ }
+ status = DBGetContactSettingWord(hContact, (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0), "Status", ID_STATUS_OFFLINE);
+ }
+
+ ~TContactInfo()
+ {
+ mir_free(name);
+ mir_free(group);
+ }
+
+ HANDLE getHandle() const
+ {
+ return hContact;
+ }
+
+ TCHAR *getGroup() const
+ {
+ return group;
+ }
+
+ static int cmp(const TContactInfo *p1, const TContactInfo *p2)
+ {
+ if (p1->bManual && !p2->bManual) return -1;
+ if (!p1->bManual && p2->bManual) return 1;
+
+ if (!p1->bManual)
+ {
+ if (p1->fRate > p2->fRate) return -1;
+ if (p1->fRate < p2->fRate) return 1;
+ }
+
+ int res = 0;
+ if (res = lstrcmp(p1->group, p2->group)) return res;
+ //if (p1->status < p2->status) return -1;
+ //if (p1->status < p2->status) return +1;
+ if (res = lstrcmp(p1->name, p2->name)) return res;
+ return 0;
+ }
+};
+
+class TFavContacts: public LIST<TContactInfo>
+{
+private:
+ int nGroups;
+
+public:
+ TFavContacts(): LIST<TContactInfo>(5, TContactInfo::cmp) {}
+ ~TFavContacts()
+ {
+ for (int i = 0; i < this->getCount(); ++i)
+ delete (*this)[i];
+ }
+
+ int groupCount()
+ {
+ return nGroups;
+ }
+
+ TContactInfo *addContact(HANDLE hContact, bool bManual)
+ {
+ TContactInfo *info = new TContactInfo(hContact, bManual);
+ this->insert(info);
+ return info;
+ }
+
+ void build()
+ {
+ TCHAR *prevGroup = NULL;
+ int i;
+
+ nGroups = 1;
+
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ for ( ; hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ if (DBGetContactSettingByte(hContact, "FavContacts", "IsFavourite", 0))
+ {
+ TCHAR *group = addContact(hContact, true)->getGroup();
+ if (prevGroup && lstrcmp(prevGroup, group))
+ ++nGroups;
+ prevGroup = group;
+ }
+
+ int nRecent = 0;
+ for (i = 0; nRecent < g_Options.wMaxRecent; ++i)
+ {
+ hContact = g_contactCache->get(i);
+ if (!hContact) break;
+ if (!DBGetContactSettingByte(hContact, "FavContacts", "IsFavourite", 0))
+ {
+ TCHAR *group = addContact(hContact, false)->getGroup();
+ if (prevGroup && lstrcmp(prevGroup, group))
+ ++nGroups;
+ prevGroup = group;
+
+ ++nRecent;
+ }
+ }
+ }
+};
+
+#endif // favlist_h__
diff --git a/plugins/FavContacts/src/headers.h b/plugins/FavContacts/src/headers.h new file mode 100644 index 0000000000..8863317199 --- /dev/null +++ b/plugins/FavContacts/src/headers.h @@ -0,0 +1,94 @@ +/*
+Favourite Contacts for Miranda IM
+
+Copyright 2007 Victor Pavlychko
+
+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 _CRT_SECURE_NO_DEPRECATE
+
+#if defined(UNICODE) && !defined(_UNICODE)
+ #define _UNICODE
+#endif
+
+#include <tchar.h>
+#define _WIN32_WINNT 0x0501
+#include <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <malloc.h>
+
+#define MIRANDA_VER 0x800
+#include <win2k.h>
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_database.h>
+#include <m_langpack.h>
+#include <m_button.h>
+#include <m_clist.h>
+#include <m_clc.h>
+#include <m_clui.h>
+#include <m_options.h>
+#include <m_protosvc.h>
+#include <m_utils.h>
+#include <m_skin.h>
+#include <m_contacts.h>
+#include <m_userinfo.h>
+#include <m_history.h>
+#include <m_addcontact.h>
+#include <m_message.h>
+#include <m_file.h>
+#include <m_icolib.h>
+#include <m_idle.h>
+#include <m_awaymsg.h>
+#include <m_avatars.h>
+#include <m_toolbar.h>
+#include <m_fontservice.h>
+#include <m_hotkeys.h>
+
+#include "../resource.h"
+
+#define NEWTSTR_ALLOCA(A) (A==NULL)?NULL:_tcscpy((TCHAR*)alloca(sizeof(TCHAR)*(_tcslen(A)+1)),A)
+
+struct Options
+{
+ BYTE bSecondLine;
+ BYTE bAvatars;
+ BYTE bAvatarBorder;
+ WORD wAvatarRadius;
+ BYTE bNoTransparentBorder;
+ BYTE bSysColors;
+ BYTE bCenterHotkey;
+ BYTE bUseGroups;
+ BYTE bUseColumns;
+ BYTE bRightAvatars;
+ BYTE bDimIdle;
+ WORD wMaxRecent;
+
+ COLORREF clLine1, clLine2, clBack;
+ COLORREF clLine1Sel, clLine2Sel, clBackSel;
+ HFONT hfntName, hfntSecond;
+};
+
+
+#include "contact_cache.h"
+
+extern Options g_Options;
+extern CContactCache *g_contactCache;
+
+#include "favlist.h"
+#include "http_api.h"
diff --git a/plugins/FavContacts/src/http_api.cpp b/plugins/FavContacts/src/http_api.cpp new file mode 100644 index 0000000000..147cabdd7d --- /dev/null +++ b/plugins/FavContacts/src/http_api.cpp @@ -0,0 +1,165 @@ +#include "headers.h"
+
+#pragma comment(lib,"ws2_32.lib")
+
+#include "csocket.h"
+#include "cserver.h"
+
+#define MS_FAVCONTACTS_OPEN_CONTACT "FavContacts/OpenContact"
+
+class CHttpProcessor: public IConnectionProcessor
+{
+private:
+ CSocket *m_socket;
+
+ char *FetchURL(char *s)
+ {
+ char *p;
+ if (p = strstr(s, "\r\n")) *p = 0;
+ if (p = strrchr(s, ' ')) *p = 0;
+ if (p = strchr(s, ' ')) while (*p && *p == ' ') p++;
+ return mir_strdup(p);
+ }
+
+public:
+ CHttpProcessor(CSocket *s): m_socket(s) {}
+
+ void ProcessConnection()
+ {
+ char buf[1024];
+ int n = m_socket->Recv(buf, sizeof(buf));
+ buf[n] = 0;
+
+ char *s = FetchURL(buf);
+
+ if (!strncmp(s, "/fav/list/", 10))
+ {
+ SendList();
+ } else
+ if (!strncmp(s, "/fav/open/", 10))
+ {
+ OpenContact(s);
+ }
+
+ mir_free(s);
+ m_socket->Close();
+ }
+
+ void OpenContact(char *s)
+ {
+ m_socket->Send("HTTP 200 OK\r\n\r\n");
+
+ int hContact;
+ sscanf(s, "/fav/open/%d", &hContact);
+ if (CallService(MS_DB_CONTACT_IS, hContact, 0))
+ CallServiceSync(MS_FAVCONTACTS_OPEN_CONTACT, (WPARAM)hContact, 0);
+ }
+
+ void SendList()
+ {
+ TFavContacts favList;
+ favList.build();
+
+ m_socket->Send(
+ "HTTP 200 OK\r\n"
+ "Content-Type: text/javascript\r\n"
+ "\r\n");
+
+ Send("try {\r\n");
+ Send("SetContactCount(");
+ Send(favList.getCount());
+ Send(");\r\n");
+
+ for (int i = 0; i < favList.getCount(); ++i)
+ {
+ HANDLE hContact = favList[i]->getHandle();
+ TCHAR *name = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR);
+ AVATARCACHEENTRY *avatar = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0);
+ int status = DBGetContactSettingWord(hContact, (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0), "Status", ID_STATUS_OFFLINE);
+
+ Send("SetContact(");
+ Send(i);
+ Send(", ");
+ Send((int)hContact);
+ Send(", '");
+ SendQuoted(name);
+ Send("', ");
+ Send(status);
+ Send(", '");
+ SendQuoted(avatar ? avatar->szFilename : "");
+ Send("');\r\n");
+ }
+ Send("} catch(e) {}\r\n");
+ }
+
+ void Send(char *s)
+ {
+ m_socket->Send(s);
+ }
+
+#ifdef UNICODE
+ void Send(WCHAR *ws)
+ {
+ char *s = mir_utf8encodeW(ws);
+ m_socket->Send(s);
+ mir_free(s);
+ }
+#endif
+
+ void Send(int i)
+ {
+ char buf[32];
+ wsprintfA(buf, "%d", i);
+ Send(buf);
+ }
+
+ template<class XCHAR>
+ void SendQuoted(const XCHAR *s)
+ {
+ int length = 0;
+ const XCHAR *p;
+ for (p = s; *p; p++)
+ {
+ if (*p == '\'' || *p == '\\' || *p == '\"')
+ length++;
+ length++;
+ }
+ XCHAR *buf = (XCHAR *)mir_alloc(sizeof(XCHAR) * (length + 1));
+ XCHAR *q = buf;
+ for (p = s; *p; p++)
+ {
+ if (*p == '\'' || *p == '\\' || *p == '\"')
+ {
+ *q = '\\';
+ q++;
+ }
+ *q = *p;
+ q++;
+ }
+ *q = 0;
+ Send(buf);
+ mir_free(buf);
+ }
+};
+
+class CHttpProcessorFactory: public IConnectionProcessorFactory
+{
+public:
+ IConnectionProcessor *Create(CSocket *s)
+ {
+ return new CHttpProcessor(s);
+ }
+};
+
+static CHttpProcessorFactory g_httpProcessorFactory;
+static CServer g_httpServer;
+
+void LoadHttpApi()
+{
+ g_httpServer.Start(60888, &g_httpProcessorFactory, true);
+}
+
+void UnloadHttpApi()
+{
+ g_httpServer.Stop();
+}
diff --git a/plugins/FavContacts/src/http_api.h b/plugins/FavContacts/src/http_api.h new file mode 100644 index 0000000000..cccb7ea13b --- /dev/null +++ b/plugins/FavContacts/src/http_api.h @@ -0,0 +1,7 @@ +#ifndef http_api_h__
+#define http_api_h__
+
+void LoadHttpApi();
+void UnloadHttpApi();
+
+#endif // http_api_h__
diff --git a/plugins/FavContacts/src/main.cpp b/plugins/FavContacts/src/main.cpp new file mode 100644 index 0000000000..5e5a1f1f9a --- /dev/null +++ b/plugins/FavContacts/src/main.cpp @@ -0,0 +1,1453 @@ +/*
+Favourite Contacts for Miranda IM
+
+Copyright 2007 Victor Pavlychko
+
+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 "headers.h"
+
+DWORD g_mirandaVersion;
+PLUGINLINK* pluginLink = NULL;
+HINSTANCE g_hInst;
+
+struct LIST_INTERFACE li;
+struct MM_INTERFACE mmi;
+struct UTF8_INTERFACE utfi;
+
+// {AC8B66B3-AFE1-4475-BABA-49783BA39A66}
+#define MIID_FAVCONTACTS { 0xac8b66b3, 0xafe1, 0x4475, { 0xba, 0xba, 0x49, 0x78, 0x3b, 0xa3, 0x9a, 0x66 } }
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ "Favourite Contacts",
+ PLUGIN_MAKE_VERSION(0, 0, 0, 6),
+ "Favourite contacts menu",
+ "code by Victor Pavlychko, icons by Angeli-Ka",
+ "nullbie@gmail.com",
+ "Copyright 2007-2009 Victor Pavlychko",
+ "http://nullbie.miranda.im/",
+ UNICODE_AWARE,
+ 0, // replace internal version (if any)
+#ifdef _UNICODE
+ // {CE2C0401-F9E0-40d7-8E95-1A4197D7AB04}
+ { 0xce2c0401, 0xf9e0, 0x40d7, { 0x8e, 0x95, 0x1a, 0x41, 0x97, 0xd7, 0xab, 0x4 } }
+#else
+ // {DE1D765C-9DC2-4679-8633-EDAD492C8479}
+ { 0xde1d765c, 0x9dc2, 0x4679, { 0x86, 0x33, 0xed, 0xad, 0x49, 0x2c, 0x84, 0x79 } }
+#endif
+};
+
+#define MS_FAVCONTACTS_SHOWMENU "FavContacts/ShowMenu"
+#define MS_FAVCONTACTS_SHOWMENU_CENTERED "FavContacts/ShowMenuCentered"
+#define MS_FAVCONTACTS_OPEN_CONTACT "FavContacts/OpenContact"
+
+HWND g_hwndMenuHost = NULL;
+static LRESULT CALLBACK MenuHostWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+static BOOL CALLBACK OptionsDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+static void sttLoadOptions();
+static void sttSaveOptions();
+
+int sttShowMenu(bool centered);
+
+INT_PTR svcOpenContact(WPARAM wParam, LPARAM lParam);
+
+INT_PTR svcShowMenu(WPARAM wParam, LPARAM lParam);
+INT_PTR svcShowMenuCentered(WPARAM wParam, LPARAM lParam);
+int ProcessSrmmEvent(WPARAM wParam, LPARAM lParam);
+int ProcessSrmmIconClick(WPARAM wParam, LPARAM lParam);
+
+int g_icoFavourite=0, g_icoRegular=0;
+float g_widthMultiplier = 0;
+int g_maxItemWidth = 0;
+
+CContactCache *g_contactCache = NULL;
+TCHAR g_filter[1024] = {0};
+
+Options g_Options = {0};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ g_hInst = hinstDLL;
+ return TRUE;
+}
+
+bool CoreCheck()
+{
+ char version[1024], exepath[1024];
+ GetModuleFileNameA(GetModuleHandle(NULL), exepath, sizeof(exepath));
+ CallService(MS_SYSTEM_GETVERSIONTEXT, sizeof(version), (LPARAM)version);
+ _strlwr(exepath); _strlwr(version);
+ if (!strstr(strrchr(exepath, '\\'), "miranda") ||
+ strstr(version, "coffee") ||
+ (*version && (!strncmp(version, "1.", 2) || strstr(version, " 1."))) ||
+ (g_mirandaVersion >= PLUGIN_MAKE_VERSION(1,0,0,0)))
+ {
+ MessageBoxA(0,
+ Translate(
+ "Favourite Contacts plugin was designed to be used with Miranda IM only.\n"
+ "For use with any other application, please contact author.\n"
+ ),
+ "Favourite Contacts Error",
+ MB_ICONSTOP|MB_OK);
+ return false;
+ }
+ return true;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ g_mirandaVersion = mirandaVersion;
+ return &pluginInfo;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfo(DWORD mirandaVersion)
+{
+ g_mirandaVersion = mirandaVersion;
+ return &pluginInfo;
+}
+
+extern "C" __declspec(dllexport) const MUUID *MirandaPluginInterfaces(void)
+{
+ static const MUUID interfaces[] = { MIID_FAVCONTACTS, MIID_LAST };
+ return interfaces;
+}
+
+static __forceinline COLORREF sttShadeColor(COLORREF clLine1, COLORREF clBack)
+{
+ return RGB(
+ (GetRValue(clLine1) * 66UL + GetRValue(clBack) * 34UL) / 100,
+ (GetGValue(clLine1) * 66UL + GetGValue(clBack) * 34UL) / 100,
+ (GetBValue(clLine1) * 66UL + GetBValue(clBack) * 34UL) / 100
+ );
+}
+
+HANDLE hhkProcessTBLoaded = NULL;
+int ProcessTBLoaded(WPARAM wParam, LPARAM lParam)
+{
+ TBButton button = {0};
+ button.cbSize = sizeof(button);
+ button.pszButtonID = "FavContacts/ShowMenu";
+ button.pszTooltipUp = button.pszTooltipUp =
+ button.pszButtonName = "Favourite Contacts";
+ button.pszServiceName = MS_FAVCONTACTS_SHOWMENU;
+ button.defPos = 200;
+ button.tbbFlags = TBBF_SHOWTOOLTIP|TBBF_VISIBLE;
+ button.hSecondaryIconHandle = button.hPrimaryIconHandle = (HANDLE)g_icoFavourite;
+ CallService(MS_TB_ADDBUTTON, 0, (LPARAM)&button);
+
+ return 0;
+}
+
+int ProcessReloadFonts(WPARAM wParam, LPARAM lParam)
+{
+ if (g_Options.hfntName) DeleteObject(g_Options.hfntName);
+ if (g_Options.hfntSecond) DeleteObject(g_Options.hfntSecond);
+
+ LOGFONT lf = {0};
+ FontIDT fontid = {0};
+ fontid.cbSize = sizeof(fontid);
+ lstrcpy(fontid.group, _T("Favourite Contacts"));
+
+ lstrcpy(fontid.name, _T("Contact name"));
+ g_Options.clLine1 = CallService(MS_FONT_GETT, (WPARAM)&fontid, (LPARAM)&lf);
+ g_Options.hfntName = CreateFontIndirect(&lf);
+
+ lstrcpy(fontid.name, _T("Second line"));
+ g_Options.clLine2 = CallService(MS_FONT_GETT, (WPARAM)&fontid, (LPARAM)&lf);
+ g_Options.hfntSecond = CreateFontIndirect(&lf);
+
+ lstrcpy(fontid.name, _T("Selected contact name (color)"));
+ g_Options.clLine1Sel = CallService(MS_FONT_GETT, (WPARAM)&fontid, (LPARAM)&lf);
+
+ lstrcpy(fontid.name, _T("Selected second line (color)"));
+ g_Options.clLine2Sel = CallService(MS_FONT_GETT, (WPARAM)&fontid, (LPARAM)&lf);
+
+ ColourIDT colourid = {0};
+ colourid.cbSize = sizeof(colourid);
+ lstrcpy(colourid.group, _T("Favourite Contacts"));
+
+ lstrcpy(colourid.name, _T("Background"));
+ g_Options.clBack = CallService(MS_COLOUR_GETT, (WPARAM)&colourid, (LPARAM)&lf);
+
+ lstrcpy(colourid.name, _T("Selected background"));
+ g_Options.clBackSel = CallService(MS_COLOUR_GETT, (WPARAM)&colourid, (LPARAM)&lf);
+
+ return 0;
+}
+
+int ProcessModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ if (ServiceExists(MS_MSG_ADDICON))
+ {
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = "FavContacts";
+ sid.szTooltip = Translate("Favourite Contacts");
+ sid.hIcon = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, g_icoFavourite);
+ sid.hIconDisabled = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, g_icoRegular);
+ CallService(MS_MSG_ADDICON, 0, (LPARAM)&sid);
+
+ HookEvent(ME_MSG_ICONPRESSED, ProcessSrmmIconClick);
+ HookEvent(ME_MSG_WINDOWEVENT, ProcessSrmmEvent);
+ }
+
+ if (true /* ServiceExists(MS_FONT_REGISTERT) */)
+ {
+ //LOGFONT lf;
+ //GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(lf), &lf);
+
+ FontIDT fontid = {0};
+ fontid.cbSize = sizeof(fontid);
+ lstrcpy(fontid.group, _T("Favourite Contacts"));
+ lstrcpyA(fontid.dbSettingsGroup, "FavContacts");
+ lstrcpy(fontid.backgroundGroup, _T("Favourite Contacts"));
+ fontid.flags = FIDF_DEFAULTVALID;
+ fontid.deffontsettings.charset = DEFAULT_CHARSET;
+ fontid.deffontsettings.size = -11;
+ lstrcpy(fontid.deffontsettings.szFace, _T("MS Shell Dlg"));
+ fontid.deffontsettings.style = 0;
+
+ lstrcpy(fontid.backgroundName, _T("Background"));
+
+ lstrcpy(fontid.name, _T("Contact name"));
+ lstrcpyA(fontid.prefix, "fntName");
+ fontid.deffontsettings.colour = GetSysColor(COLOR_MENUTEXT);
+ fontid.deffontsettings.style = DBFONTF_BOLD;
+ CallService(MS_FONT_REGISTERT, (WPARAM)&fontid, 0);
+
+ lstrcpy(fontid.name, _T("Second line"));
+ lstrcpyA(fontid.prefix, "fntSecond");
+ fontid.deffontsettings.colour = sttShadeColor(GetSysColor(COLOR_MENUTEXT), GetSysColor(COLOR_MENU));
+ fontid.deffontsettings.style = 0;
+ CallService(MS_FONT_REGISTERT, (WPARAM)&fontid, 0);
+
+ lstrcpy(fontid.backgroundName, _T("Selected background"));
+
+ lstrcpy(fontid.name, _T("Selected contact name (color)"));
+ lstrcpyA(fontid.prefix, "fntNameSel");
+ fontid.deffontsettings.colour = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ fontid.deffontsettings.style = DBFONTF_BOLD;
+ CallService(MS_FONT_REGISTERT, (WPARAM)&fontid, 0);
+
+ lstrcpy(fontid.name, _T("Selected second line (color)"));
+ lstrcpyA(fontid.prefix, "fntSecondSel");
+ fontid.deffontsettings.colour = sttShadeColor(GetSysColor(COLOR_HIGHLIGHTTEXT), GetSysColor(COLOR_HIGHLIGHT));
+ fontid.deffontsettings.style = 0;
+ CallService(MS_FONT_REGISTERT, (WPARAM)&fontid, 0);
+
+ ColourIDT colourid = {0};
+ colourid.cbSize = sizeof(colourid);
+ lstrcpy(colourid.group, _T("Favourite Contacts"));
+ lstrcpyA(colourid.dbSettingsGroup, "FavContacts");
+
+ lstrcpy(colourid.name, _T("Background"));
+ lstrcpyA(colourid.setting, "BackColour");
+ colourid.defcolour = GetSysColor(COLOR_MENU);
+ CallService(MS_COLOUR_REGISTERT, (WPARAM)&colourid, 0);
+
+ lstrcpy(colourid.name, _T("Selected background"));
+ lstrcpyA(colourid.setting, "SelectedColour");
+ colourid.defcolour = GetSysColor(COLOR_HIGHLIGHT);
+ CallService(MS_COLOUR_REGISTERT, (WPARAM)&colourid, 0);
+
+ HookEvent(ME_FONT_RELOAD, ProcessReloadFonts);
+ HookEvent(ME_COLOUR_RELOAD, ProcessReloadFonts);
+ ProcessReloadFonts(0, 0);
+ }
+
+ if (ServiceExists(MS_HOTKEY_REGISTER))
+ {
+ HOTKEYDESC hotkey = {0};
+ hotkey.cbSize = sizeof(hotkey);
+ hotkey.pszName = "FavContacts/ShowMenu";
+ hotkey.pszDescription = "Show favourite contacts";
+ hotkey.pszSection = "Contacts";
+ hotkey.pszService = MS_FAVCONTACTS_SHOWMENU_CENTERED;
+ hotkey.DefHotKey = MAKEWORD('Q', HOTKEYF_EXT);
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&hotkey);
+ }
+
+ if (ServiceExists(MS_AV_GETAVATARBITMAP))
+ {
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ for ( ; hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ if (DBGetContactSettingByte(hContact, "FavContacts", "IsFavourite", 0))
+ CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0);
+ }
+
+ if (!hhkProcessTBLoaded) hhkProcessTBLoaded = HookEvent(ME_TB_MODULELOADED, ProcessTBLoaded);
+
+ return 0;
+}
+
+int ProcessOptInitialise(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = g_hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.ptszGroup = TranslateT("Contact List");
+ odp.ptszTitle = TranslateT("Favourites");
+ odp.groupPosition = 910000000;
+ odp.flags = ODPF_BOLDGROUPS|ODPF_TCHAR;
+ odp.pfnDlgProc = OptionsDlgProc;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Load(PLUGINLINK * link)
+{
+ pluginLink = link;
+
+ if (!CoreCheck()) return 1;
+
+ mir_getLI(&li);
+ mir_getMMI(&mmi);
+ mir_getUTFI(&utfi);
+
+ g_contactCache = new CContactCache;
+
+ WNDCLASSEX wcl = {0};
+ wcl.cbSize = sizeof(wcl);
+ wcl.lpfnWndProc = MenuHostWndProc;
+ wcl.style = 0;
+ wcl.cbClsExtra = 0;
+ wcl.cbWndExtra = 0;
+ wcl.hInstance = g_hInst;
+ wcl.hIcon = NULL;
+ wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcl.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
+ wcl.lpszMenuName = NULL;
+ wcl.lpszClassName = _T("FavContactsMenuHostWnd");
+ wcl.hIconSm = NULL;
+ RegisterClassEx(&wcl);
+
+ g_hwndMenuHost = CreateWindow(_T("FavContactsMenuHostWnd"), NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, g_hInst, NULL);
+ SetWindowPos(g_hwndMenuHost, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_DEFERERASE|SWP_NOSENDCHANGING|SWP_HIDEWINDOW);
+
+ sttLoadOptions();
+
+ CreateServiceFunction(MS_FAVCONTACTS_SHOWMENU, svcShowMenu);
+ CreateServiceFunction(MS_FAVCONTACTS_SHOWMENU_CENTERED, svcShowMenuCentered);
+ CreateServiceFunction(MS_FAVCONTACTS_OPEN_CONTACT, svcOpenContact);
+
+ HookEvent(ME_OPT_INITIALISE, ProcessOptInitialise);
+ HookEvent(ME_SYSTEM_MODULESLOADED, ProcessModulesLoaded);
+ hhkProcessTBLoaded = HookEvent(ME_TB_MODULELOADED, ProcessTBLoaded);
+
+ if (true /*ServiceExists(MS_SKIN2_ADDICON)*/)
+ {
+ TCHAR buf[MAX_PATH];
+ GetModuleFileName(g_hInst, buf, SIZEOF(buf));
+
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.ptszSection = _T("Favourites");
+ sid.ptszDefaultFile = buf;
+ sid.cx = sid.cy = 16;
+ sid.flags = SIDF_ALL_TCHAR;
+
+ sid.pszName = "favcontacts_favourite";
+ sid.ptszDescription = _T("Favourite Contact");
+ sid.iDefaultIndex = -IDI_FAVOURITE;
+ g_icoFavourite = CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+
+ sid.pszName = "favcontacts_regular";
+ sid.ptszDescription = _T("Regular Contact");
+ sid.iDefaultIndex = -IDI_REGULAR;
+ g_icoRegular = CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+
+ LoadHttpApi();
+
+#ifdef _DEBUG
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_OPTIONS);
+ mi.position = 1900000000;
+ mi.pszName = LPGEN("&Favourite Contacts...");
+ mi.pszService = MS_FAVCONTACTS_SHOWMENU;
+ CallService( MS_CLIST_ADDMAINMENUITEM, 0, ( LPARAM )&mi );
+#endif
+
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ UnloadHttpApi();
+ if (g_hwndMenuHost) DestroyWindow(g_hwndMenuHost);
+ if (g_Options.hfntName) DeleteObject(g_Options.hfntName);
+ if (g_Options.hfntSecond) DeleteObject(g_Options.hfntSecond);
+ delete g_contactCache;
+ return 0;
+}
+
+static void sttLoadOptions()
+{
+ g_Options.bSecondLine = DBGetContactSettingByte(NULL, "FavContacts", "SecondLine", 1);
+ g_Options.bAvatars = DBGetContactSettingByte(NULL, "FavContacts", "Avatars", 1);
+ g_Options.bAvatarBorder = DBGetContactSettingByte(NULL, "FavContacts", "AvatarBorder", 0);
+ g_Options.wAvatarRadius = DBGetContactSettingWord(NULL, "FavContacts", "AvatarRadius", 3);
+ g_Options.bNoTransparentBorder = DBGetContactSettingByte(NULL, "FavContacts", "NoTransparentBorder",
+ !DBGetContactSettingByte(NULL, "FavContacts", "AvatarBorderTransparent", 1));
+ g_Options.bSysColors = DBGetContactSettingByte(NULL, "FavContacts", "SysColors", 0);
+ g_Options.bCenterHotkey = DBGetContactSettingByte(NULL, "FavContacts", "CenterHotkey", 1);
+ g_Options.bUseGroups = DBGetContactSettingByte(NULL, "FavContacts", "UseGroups", 0);
+ g_Options.bUseColumns = DBGetContactSettingByte(NULL, "FavContacts", "UseColumns", 1);
+ g_Options.bRightAvatars = DBGetContactSettingByte(NULL, "FavContacts", "RightAvatars", 0);
+ g_Options.bDimIdle = DBGetContactSettingByte(NULL, "FavContacts", "DimIdle", 1);
+
+ g_Options.wMaxRecent = DBGetContactSettingByte(NULL, "FavContacts", "MaxRecent", 10);
+}
+
+static void sttSaveOptions()
+{
+ DBWriteContactSettingByte(NULL, "FavContacts", "SecondLine", g_Options.bSecondLine);
+ DBWriteContactSettingByte(NULL, "FavContacts", "Avatars", g_Options.bAvatars);
+ DBWriteContactSettingByte(NULL, "FavContacts", "AvatarBorder", g_Options.bAvatarBorder);
+ DBWriteContactSettingWord(NULL, "FavContacts", "AvatarRadius", g_Options.wAvatarRadius);
+ DBWriteContactSettingByte(NULL, "FavContacts", "NoTransparentBorder", g_Options.bNoTransparentBorder);
+ DBWriteContactSettingByte(NULL, "FavContacts", "SysColors", g_Options.bSysColors);
+ DBWriteContactSettingByte(NULL, "FavContacts", "CenterHotkey", g_Options.bCenterHotkey);
+ DBWriteContactSettingByte(NULL, "FavContacts", "UseGroups", g_Options.bUseGroups);
+ DBWriteContactSettingByte(NULL, "FavContacts", "UseColumns", g_Options.bUseColumns);
+ DBWriteContactSettingByte(NULL, "FavContacts", "RightAvatars", g_Options.bRightAvatars);
+ DBWriteContactSettingByte(NULL, "FavContacts", "DimIdle", g_Options.bDimIdle);
+ DBWriteContactSettingWord(NULL, "FavContacts", "MaxRecent", g_Options.wMaxRecent);
+}
+
+static bool sttIsGroup(int id)
+{
+ if (id == 1) return true;
+
+ DBVARIANT dbv = {0};
+ char buf[32];
+ wsprintfA(buf, "%d", (int)(id-2));
+ if (!DBGetContactSettingTString(NULL, "CListGroups", buf, &dbv))
+ {
+ DBFreeVariant(&dbv);
+ return true;
+ }
+ return false;
+}
+
+static TCHAR *sttGetGroupName(int id)
+{
+ if (id == 1)
+ {
+ if (g_Options.bUseGroups)
+ return mir_tstrdup(TranslateT("<No group>"));
+ return mir_tstrdup(TranslateT("Favourite Contacts"));
+ }
+
+ DBVARIANT dbv = {0};
+ char buf[32];
+ wsprintfA(buf, "%d", (int)(id-2));
+ if (!DBGetContactSettingTString(NULL, "CListGroups", buf, &dbv))
+ {
+ TCHAR *res = mir_tstrdup(dbv.ptszVal+1);
+ DBFreeVariant(&dbv);
+ return res;
+ }
+ return NULL;
+}
+
+static int sttGetGroupId(TCHAR *name)
+{
+ for (int i = 0; ; ++i)
+ {
+ DBVARIANT dbv = {0};
+ char buf[32];
+ wsprintfA(buf, "%d", (int)i);
+ if (!DBGetContactSettingTString(NULL, "CListGroups", buf, &dbv))
+ {
+ if (!lstrcmp(dbv.ptszVal+1, name))
+ {
+ DBFreeVariant(&dbv);
+ return i+2;
+ }
+
+ DBFreeVariant(&dbv);
+ } else
+ {
+ // default is root
+ return 1;
+ }
+ }
+}
+
+static BOOL sttMeasureItem_Group(LPMEASUREITEMSTRUCT lpmis, Options *options)
+{
+ if (true)
+ {
+ HDC hdc = GetDC(g_hwndMenuHost);
+ HFONT hfntSave = (HFONT)SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
+ TCHAR *name = sttGetGroupName(lpmis->itemData);
+ SIZE sz;
+ if (!options->bSysColors) SelectObject(hdc, g_Options.hfntName);
+ GetTextExtentPoint32(hdc, name, lstrlen(name), &sz);
+ lpmis->itemHeight = sz.cy + 8;
+ lpmis->itemWidth = sz.cx + 10;
+ mir_free(name);
+ SelectObject(hdc, hfntSave);
+ ReleaseDC(g_hwndMenuHost, hdc);
+ }
+
+ return TRUE;
+}
+
+static BOOL sttMeasureItem_Contact(LPMEASUREITEMSTRUCT lpmis, Options *options)
+{
+ HANDLE hContact = (HANDLE)lpmis->itemData;
+
+ lpmis->itemHeight = 4;
+ lpmis->itemWidth = 8+10;
+
+ if (true)
+ {
+ lpmis->itemWidth += 20;
+ }
+
+ if (true)
+ {
+ SIZE sz;
+ int textWidth = 0;
+
+ HDC hdc = GetDC(g_hwndMenuHost);
+ HFONT hfntSave = (HFONT)SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
+
+ if (options->bSecondLine)
+ {
+ DBVARIANT dbv;
+ TCHAR *title;
+ bool bFree = false;
+ if (DBGetContactSettingTString(hContact, "CList", "StatusMsg", &dbv) || !*dbv.ptszVal)
+ {
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ int status = DBGetContactSettingWord(hContact, proto, "Status", ID_STATUS_OFFLINE);
+ title = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, status, GSMDF_TCHAR);
+ } else
+ {
+ title = dbv.ptszVal;
+ bFree = true;
+ }
+
+ if (!options->bSysColors) SelectObject(hdc, g_Options.hfntSecond);
+ GetTextExtentPoint32(hdc, title, lstrlen(title), &sz);
+ if (bFree) DBFreeVariant(&dbv);
+ textWidth = sz.cx;
+ lpmis->itemHeight += sz.cy + 3;
+ }
+
+ TCHAR *name = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR);
+
+ if (!options->bSysColors) SelectObject(hdc, g_Options.hfntName);
+ GetTextExtentPoint32(hdc, name, lstrlen(name), &sz);
+ textWidth = max(textWidth, sz.cx);
+
+ SelectObject(hdc, hfntSave);
+ ReleaseDC(g_hwndMenuHost, hdc);
+
+ lpmis->itemWidth += textWidth;
+ lpmis->itemHeight += sz.cy;
+ }
+
+ if (options->bAvatars)
+ {
+ AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0);
+ if (ace && (ace != (AVATARCACHEENTRY *)CALLSERVICE_NOTFOUND))
+ {
+ int avatarWidth = lpmis->itemHeight;
+ if (ace->bmWidth < ace->bmHeight)
+ avatarWidth = lpmis->itemHeight * ace->bmWidth / ace->bmHeight;
+
+ lpmis->itemWidth += avatarWidth + 5;
+ }
+ }
+
+ if (lpmis->itemHeight < 18) lpmis->itemHeight = 18;
+
+ return TRUE;
+}
+
+static BOOL sttMeasureItem(LPMEASUREITEMSTRUCT lpmis, Options *options=NULL)
+{
+ if (!options) options = &g_Options;
+
+ if (!lpmis->itemData)
+ return FALSE;
+
+ BOOL res = FALSE;
+ if (sttIsGroup(lpmis->itemData))
+ res = sttMeasureItem_Group(lpmis, options);
+ else if (CallService(MS_DB_CONTACT_IS, lpmis->itemData, 0))
+ res = sttMeasureItem_Contact(lpmis, options);
+
+ if (res && (lpmis->itemWidth > g_maxItemWidth)) lpmis->itemWidth = g_maxItemWidth;
+ if (res && g_widthMultiplier) lpmis->itemWidth *= g_widthMultiplier;
+
+ return FALSE;
+}
+
+static BOOL sttDrawItem_Group(LPDRAWITEMSTRUCT lpdis, Options *options = NULL)
+{
+ lpdis->rcItem.top++;
+ lpdis->rcItem.bottom--;
+
+ HFONT hfntSave = (HFONT)SelectObject(lpdis->hDC, GetStockObject(DEFAULT_GUI_FONT));
+ SetBkMode(lpdis->hDC, TRANSPARENT);
+ if (options->bSysColors)
+ {
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ //FrameRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ } else
+ {
+ HBRUSH hbr;
+ hbr = CreateSolidBrush(g_Options.clBackSel);
+ FillRect(lpdis->hDC, &lpdis->rcItem, hbr);
+ DeleteObject(hbr);
+ //hbr = CreateSolidBrush(g_Options.clBackSel);
+ //FrameRect(lpdis->hDC, &lpdis->rcItem, hbr);
+ //DeleteObject(hbr);
+ SetTextColor(lpdis->hDC, g_Options.clLine1Sel);
+ }
+
+ if (true)
+ {
+ TCHAR *name = sttGetGroupName(lpdis->itemData);
+ if (!options->bSysColors) SelectObject(lpdis->hDC, g_Options.hfntName);
+ DrawText(lpdis->hDC, name, lstrlen(name), &lpdis->rcItem, DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_CENTER);
+ mir_free(name);
+ }
+
+ SelectObject(lpdis->hDC, hfntSave);
+
+ return TRUE;
+}
+
+void ImageList_DrawDimmed(HIMAGELIST himl, int i, HDC hdc, int left, int top, UINT fStyle)
+{
+ typedef BOOL (WINAPI *TFnAlphaBlend)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION);
+ static TFnAlphaBlend pfnAlphaBlend = NULL;
+ bool load_funcs = true;
+
+ if (load_funcs)
+ {
+ pfnAlphaBlend = (TFnAlphaBlend)GetProcAddress(GetModuleHandleA("msimg32"), "AlphaBlend");
+ load_funcs = false;
+ }
+
+ int dx, dy;
+ ImageList_GetIconSize(himl, &dx, &dy);
+
+ HDC dcMem = CreateCompatibleDC(hdc);
+ HBITMAP hbm = CreateCompatibleBitmap(hdc, dx, dy);
+ HBITMAP hbmOld = (HBITMAP)SelectObject(dcMem, hbm);
+ BitBlt(dcMem, 0, 0, dx, dx, hdc, left, top, SRCCOPY);
+ ImageList_Draw(himl, i, dcMem, 0, 0, fStyle);
+ if (pfnAlphaBlend)
+ {
+ BLENDFUNCTION bf = {0};
+ bf.SourceConstantAlpha = 180;
+ pfnAlphaBlend(hdc, left, top, dx, dy, dcMem, 0, 0, dx, dy, bf);
+ } else
+ {
+ SetStretchBltMode(hdc, HALFTONE);
+ StretchBlt(hdc, left, top, dx, dy, dcMem, 0, 0, dx, dy, SRCCOPY);
+ }
+ SelectObject(dcMem, hbmOld);
+ DeleteObject(hbm);
+ DeleteDC(dcMem);
+}
+
+static BOOL sttDrawItem_Contact(LPDRAWITEMSTRUCT lpdis, Options *options = NULL)
+{
+ HANDLE hContact = (HANDLE)lpdis->itemData;
+
+ HDC hdcTemp = CreateCompatibleDC(lpdis->hDC);
+ HBITMAP hbmTemp = CreateCompatibleBitmap(lpdis->hDC, lpdis->rcItem.right-lpdis->rcItem.left, lpdis->rcItem.bottom-lpdis->rcItem.top);
+ HBITMAP hbmSave = (HBITMAP)SelectObject(hdcTemp, hbmTemp);
+ RECT rcSave = lpdis->rcItem;
+
+ OffsetRect(&lpdis->rcItem, -lpdis->rcItem.left, -lpdis->rcItem.top);
+
+ HFONT hfntSave = (HFONT)SelectObject(hdcTemp, GetStockObject(DEFAULT_GUI_FONT));
+ SetBkMode(hdcTemp, TRANSPARENT);
+ COLORREF clBack, clLine1, clLine2;
+ if (lpdis->itemState & ODS_SELECTED)
+ {
+ if (options->bSysColors)
+ {
+ FillRect(hdcTemp, &lpdis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ clBack = GetSysColor(COLOR_HIGHLIGHT);
+ clLine1 = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ } else
+ {
+ clBack = g_Options.clBackSel;
+ clLine1 = g_Options.clLine1Sel;
+ clLine2 = g_Options.clLine2Sel;
+ }
+ } else
+ {
+ if (options->bSysColors)
+ {
+ FillRect(hdcTemp, &lpdis->rcItem, GetSysColorBrush(COLOR_MENU));
+ clBack = GetSysColor(COLOR_MENU);
+ clLine1 = GetSysColor(COLOR_MENUTEXT);
+ } else
+ {
+ clBack = g_Options.clBack;
+ clLine1 = g_Options.clLine1;
+ clLine2 = g_Options.clLine2;
+ }
+ }
+ if (options->bSysColors)
+ {
+ clLine2 = RGB(
+ (GetRValue(clLine1) * 66UL + GetRValue(clBack) * 34UL) / 100,
+ (GetGValue(clLine1) * 66UL + GetGValue(clBack) * 34UL) / 100,
+ (GetBValue(clLine1) * 66UL + GetBValue(clBack) * 34UL) / 100
+ );
+ } else
+ {
+ HBRUSH hbr = CreateSolidBrush(clBack);
+ FillRect(hdcTemp, &lpdis->rcItem, hbr);
+ DeleteObject(hbr);
+ }
+
+ lpdis->rcItem.left += 4;
+ lpdis->rcItem.right -= 4;
+
+ lpdis->rcItem.top += 2;
+ lpdis->rcItem.bottom -= 2;
+
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+
+ if (true)
+ {
+ HIMAGELIST hIml = (HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0);
+ int iIcon = CallService(MS_CLIST_GETCONTACTICON, (WPARAM)hContact, 0);
+
+ if (DBGetContactSettingDword(hContact, proto, "IdleTS", 0))
+ {
+ ImageList_DrawDimmed(hIml, iIcon, hdcTemp,
+ lpdis->rcItem.left, (lpdis->rcItem.top + lpdis->rcItem.bottom - 16) / 2,
+ ILD_TRANSPARENT);
+ } else
+ {
+ ImageList_Draw(hIml, iIcon, hdcTemp,
+ lpdis->rcItem.left, (lpdis->rcItem.top + lpdis->rcItem.bottom - 16) / 2,
+ ILD_TRANSPARENT);
+ }
+
+ lpdis->rcItem.left += 20;
+ }
+
+ if (options->wMaxRecent && DBGetContactSettingByte(hContact, "FavContacts", "IsFavourite", 0))
+ {
+ DrawIconEx(hdcTemp, lpdis->rcItem.right - 18, (lpdis->rcItem.top + lpdis->rcItem.bottom - 16) / 2,
+ (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, g_icoFavourite), 16, 16, 0, NULL, DI_NORMAL);
+ lpdis->rcItem.right -= 20;
+ }
+
+ if (options->bAvatars)
+ {
+ AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0);
+ if (ace && (ace != (AVATARCACHEENTRY *)CALLSERVICE_NOTFOUND))
+ {
+ int avatarWidth = lpdis->rcItem.bottom - lpdis->rcItem.top;
+ if (ace->bmWidth < ace->bmHeight)
+ avatarWidth = (lpdis->rcItem.bottom - lpdis->rcItem.top) * ace->bmWidth / ace->bmHeight;
+
+ AVATARDRAWREQUEST avdr = {0};
+ avdr.cbSize = sizeof(avdr);
+ avdr.hContact = hContact;
+ avdr.hTargetDC = hdcTemp;
+ avdr.rcDraw = lpdis->rcItem;
+ if (options->bRightAvatars)
+ avdr.rcDraw.left = avdr.rcDraw.right - avatarWidth;
+ else
+ avdr.rcDraw.right = avdr.rcDraw.left + avatarWidth;
+ avdr.dwFlags = AVDRQ_FALLBACKPROTO;
+ if (options->bAvatarBorder)
+ {
+ avdr.dwFlags |= AVDRQ_DRAWBORDER;
+ avdr.clrBorder = clLine1;
+ if (options->bNoTransparentBorder)
+ avdr.dwFlags |= AVDRQ_HIDEBORDERONTRANSPARENCY;
+ if (options->wAvatarRadius)
+ {
+ avdr.dwFlags |= AVDRQ_ROUNDEDCORNER;
+ avdr.radius = (unsigned char)options->wAvatarRadius;
+ }
+ }
+ avdr.alpha = 255;
+ CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdr);
+
+ if (options->bRightAvatars)
+ lpdis->rcItem.right += avatarWidth + 5;
+ else
+ lpdis->rcItem.left += avatarWidth + 5;
+ }
+ }
+
+ if (true)
+ {
+ TCHAR *name = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR);
+
+ if (!options->bSysColors) SelectObject(hdcTemp, g_Options.hfntName);
+ SetTextColor(hdcTemp, clLine1);
+ DrawText(hdcTemp, name, lstrlen(name), &lpdis->rcItem, DT_NOPREFIX|DT_SINGLELINE|DT_TOP|DT_LEFT);
+
+ SIZE sz; GetTextExtentPoint32(hdcTemp, name, lstrlen(name), &sz);
+ lpdis->rcItem.top += sz.cy + 3;
+ }
+
+ if (options->bSecondLine)
+ {
+ DBVARIANT dbv;
+ TCHAR *title;
+ bool bFree = false;
+ if (DBGetContactSettingTString(hContact, "CList", "StatusMsg", &dbv) || !*dbv.ptszVal)
+ {
+ int status = DBGetContactSettingWord(hContact, proto, "Status", ID_STATUS_OFFLINE);
+ title = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, status, GSMDF_TCHAR);
+ } else
+ {
+ title = dbv.ptszVal;
+ bFree = true;
+ }
+
+ if (!options->bSysColors) SelectObject(hdcTemp, g_Options.hfntSecond);
+ SetTextColor(hdcTemp, clLine2);
+ DrawText(hdcTemp, title, lstrlen(title), &lpdis->rcItem, DT_NOPREFIX|DT_SINGLELINE|DT_TOP|DT_LEFT);
+
+ if (bFree) DBFreeVariant(&dbv);
+ }
+
+ SelectObject(hdcTemp, hfntSave);
+
+ BitBlt(lpdis->hDC,
+ rcSave.left, rcSave.top,
+ rcSave.right-rcSave.left, rcSave.bottom-rcSave.top,
+ hdcTemp, 0, 0, SRCCOPY);
+
+ SelectObject(hdcTemp, hbmSave);
+ DeleteObject(hbmTemp);
+ DeleteDC(hdcTemp);
+
+ return TRUE;
+}
+
+static BOOL sttDrawItem(LPDRAWITEMSTRUCT lpdis, Options *options=NULL)
+{
+ if (!options) options = &g_Options;
+
+ if (!lpdis->itemData)
+ return FALSE;
+
+ if (sttIsGroup(lpdis->itemData))
+ return sttDrawItem_Group(lpdis, options);
+
+ if (CallService(MS_DB_CONTACT_IS, lpdis->itemData, 0))
+ return sttDrawItem_Contact(lpdis, options);
+
+ return FALSE;
+}
+
+static LRESULT CALLBACK MenuHostWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static HANDLE hContact = NULL;
+
+ switch (message)
+ {
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+
+ if (lpmis->CtlType != ODT_MENU) return FALSE;
+
+ if ((lpmis->itemID >= CLISTMENUIDMIN) && (lpmis->itemID <= CLISTMENUIDMAX))
+ return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+
+ return sttMeasureItem(lpmis);
+ }
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+
+ if (lpdis->CtlType != ODT_MENU) return FALSE;
+
+ if ((lpdis->itemID >= CLISTMENUIDMIN) && (lpdis->itemID <= CLISTMENUIDMAX))
+ return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+
+ return sttDrawItem(lpdis);
+ }
+
+ case WM_MENUCHAR:
+ {
+ while (GetMenuItemCount((HMENU)lParam) > 1)
+ RemoveMenu((HMENU)lParam, 1, MF_BYPOSITION);
+
+ if (LOWORD(wParam) == VK_BACK)
+ {
+ if (int l = lstrlen(g_filter))
+ g_filter[l-1] = 0;
+ } else
+ if (_istalnum(LOWORD(wParam)))
+ {
+ if (lstrlen(g_filter) < SIZEOF(g_filter)-1)
+ {
+ TCHAR s[] = { LOWORD(wParam), 0 };
+ lstrcat(g_filter, s);
+ }
+ }
+
+ int nRecent = 0;
+ int maxRecent = g_Options.wMaxRecent ? g_Options.wMaxRecent : 10;
+ for (int i = 0; nRecent < maxRecent; ++i)
+ {
+ HANDLE hContact = g_contactCache->get(i);
+ if (!hContact) break;
+ if (!g_contactCache->filter(i, g_filter)) continue;
+
+ AppendMenu((HMENU)lParam, MF_OWNERDRAW, nRecent+1, (LPCTSTR)hContact);
+ ++nRecent;
+ }
+ return MAKELRESULT(1, MNC_SELECT);
+ }
+
+ case WM_MENURBUTTONUP:
+ {
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA;
+ GetMenuItemInfo((HMENU)lParam, wParam, TRUE, &mii);
+ HANDLE hContact = (HANDLE)mii.dwItemData;
+ if (!CallService(MS_DB_CONTACT_IS, mii.dwItemData, 0)) return FALSE;
+
+ HMENU hMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hContact, 0);
+
+ POINT pt;
+ GetCursorPos(&pt);
+ HWND hwndSave = GetForegroundWindow();
+ SetForegroundWindow(g_hwndMenuHost);
+ int res = TrackPopupMenu(hMenu, TPM_RECURSE|TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, g_hwndMenuHost, NULL);
+ SetForegroundWindow(hwndSave);
+ DestroyMenu(hMenu);
+
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(res, MPCF_CONTACTMENU), (LPARAM)hContact);
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+int sttShowMenu(bool centered)
+{
+ TFavContacts favList;
+ HMENU hMenu = CreatePopupMenu();
+ SIZE szMenu = {0};
+ SIZE szColumn = {0};
+ TCHAR *prevGroup = NULL;
+ int i, idItem;
+ HANDLE hContact;
+
+ favList.build();
+
+ g_widthMultiplier = 0;
+
+ g_maxItemWidth = GetSystemMetrics(SM_CXSCREEN);
+ if (g_Options.bUseColumns) g_maxItemWidth /= favList.groupCount();
+
+ prevGroup = NULL;
+ for (i = 0; i < favList.getCount(); ++i)
+ {
+ hContact = favList[i]->getHandle();
+
+ MEASUREITEMSTRUCT mis = {0};
+ mis.CtlID = 0;
+ mis.CtlType = ODT_MENU;
+
+ if (!prevGroup || lstrcmp(prevGroup, favList[i]->getGroup()))
+ {
+ if (prevGroup && g_Options.bUseColumns)
+ {
+ szMenu.cx += szColumn.cx;
+ szMenu.cy = max(szMenu.cy, szColumn.cy);
+ szColumn.cx = szColumn.cy = 0;
+ }
+
+ DWORD groupID = sttGetGroupId(favList[i]->getGroup());
+
+ AppendMenu(hMenu,
+ MF_OWNERDRAW|MF_SEPARATOR| ((prevGroup && g_Options.bUseColumns) ? MF_MENUBREAK : 0),
+ ++idItem, (LPCTSTR)groupID);
+
+ mis.itemData = groupID;
+ mis.itemID = idItem;
+ sttMeasureItem(&mis);
+ szColumn.cx = max(szColumn.cx, mis.itemWidth);
+ szColumn.cy += mis.itemHeight;
+ }
+
+ AppendMenu(hMenu, MF_OWNERDRAW, ++idItem, (LPCTSTR)hContact);
+
+ mis.itemData = (DWORD)hContact;
+ mis.itemID = idItem;
+ sttMeasureItem(&mis);
+ szColumn.cx = max(szColumn.cx, mis.itemWidth);
+ szColumn.cy += mis.itemHeight;
+
+ prevGroup = favList[i]->getGroup();
+ }
+ szMenu.cx += szColumn.cx;
+ szMenu.cy = max(szMenu.cy, szColumn.cy);
+ szColumn.cx = szColumn.cy = 0;
+
+ unsigned maxWidth = GetSystemMetrics(SM_CXSCREEN) * DBGetContactSettingByte(NULL, "FavContacts", "MenuWidth", 66) / 100;
+ if (szMenu.cx > maxWidth)
+ {
+ g_widthMultiplier = (float)maxWidth / szMenu.cx;
+ szMenu.cx *= g_widthMultiplier;
+ }
+
+ POINT pt;
+
+// RECT rc;
+// GetMenuItemRect(g_hwndMenuHost, hMenu, 1, &rc);
+
+ if (centered)
+ {
+ if ((pt.x = (GetSystemMetrics(SM_CXSCREEN) - szMenu.cx) / 2) < 0) pt.x = 0;
+ if ((pt.y = (GetSystemMetrics(SM_CYSCREEN) - szMenu.cy) / 2) < 0) pt.y = 0;
+ } else
+ {
+ GetCursorPos(&pt);
+ }
+
+ HWND hwndSave = GetForegroundWindow();
+ SetForegroundWindow(g_hwndMenuHost);
+ hContact = NULL;
+ g_filter[0] = 0;
+ if (int res = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, g_hwndMenuHost, NULL))
+ {
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA;
+ GetMenuItemInfo(hMenu, res, FALSE, &mii);
+ hContact = (HANDLE)mii.dwItemData;
+ }
+ SetForegroundWindow(hwndSave);
+ DestroyMenu(hMenu);
+
+ if (hContact)
+ CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)hContact, 0);
+
+ return 0;
+}
+
+INT_PTR svcShowMenu(WPARAM wParam, LPARAM lParam)
+{
+ sttShowMenu(false);
+ return 0;
+}
+
+INT_PTR svcShowMenuCentered(WPARAM wParam, LPARAM lParam)
+{
+ sttShowMenu(g_Options.bCenterHotkey ? true : false);
+ return 0;
+}
+
+static HANDLE hDialogsList = NULL;
+static HANDLE hContactToActivate = NULL;
+
+INT_PTR svcOpenContact(WPARAM wParam, LPARAM lParam)
+{
+ hContactToActivate = (HANDLE)wParam;
+ CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)hContactToActivate, 0);
+ return 0;
+}
+
+int ProcessSrmmEvent( WPARAM wParam, LPARAM lParam )
+{
+ MessageWindowEventData *event = (MessageWindowEventData *)lParam;
+
+ if ( event->uType == MSG_WINDOW_EVT_OPEN )
+ {
+ if ( !hDialogsList )
+ hDialogsList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0);
+ WindowList_Add(hDialogsList, event->hwndWindow, event->hContact);
+
+ BYTE fav = DBGetContactSettingByte(event->hContact, "FavContacts", "IsFavourite", 0);
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = "FavContacts";
+ sid.flags = fav ? 0 : MBF_DISABLED;
+ CallService(MS_MSG_MODIFYICON, (WPARAM)event->hContact, (LPARAM)&sid);
+
+ if (event->hContact == hContactToActivate)
+ {
+ HWND hwndRoot = event->hwndWindow;
+ while (HWND hwndParent = GetParent(hwndRoot))
+ hwndRoot = hwndParent;
+
+ AttachThreadInput(GetWindowThreadProcessId(GetForegroundWindow(), NULL), GetCurrentThreadId(), TRUE);
+ SetForegroundWindow(hwndRoot);
+ SetActiveWindow(hwndRoot);
+ SetFocus(hwndRoot);
+ AttachThreadInput(GetWindowThreadProcessId(GetForegroundWindow(), NULL), GetCurrentThreadId(), FALSE);
+ }
+
+ hContactToActivate = NULL;
+ }
+ else if ( event->uType == MSG_WINDOW_EVT_CLOSING )
+ {
+ if (hDialogsList)
+ WindowList_Remove(hDialogsList, event->hwndWindow);
+ }
+
+ return 0;
+}
+
+int ProcessSrmmIconClick( WPARAM wParam, LPARAM lParam )
+{
+ StatusIconClickData *sicd = (StatusIconClickData *)lParam;
+ if (lstrcmpA(sicd->szModule, "FavContacts")) return 0;
+
+ HANDLE hContact = (HANDLE)wParam;
+ if (!hContact) return 0;
+
+ if (sicd->flags & MBCF_RIGHTBUTTON)
+ {
+ BYTE fav = !DBGetContactSettingByte(hContact, "FavContacts", "IsFavourite", 0);
+ DBWriteContactSettingByte(hContact, "FavContacts", "IsFavourite", fav);
+ if (fav) CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0);
+
+ StatusIconData sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.szModule = "FavContacts";
+ sid.flags = fav ? 0 : MBF_DISABLED;
+ CallService(MS_MSG_MODIFYICON, (WPARAM)hContact, (LPARAM)&sid);
+ } else
+ {
+ sttShowMenu(false);
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////
+// Options
+static void sttResetListOptions(HWND hwndList)
+{
+ int i;
+ SendMessage(hwndList,CLM_SETBKBITMAP,0,(LPARAM)(HBITMAP)NULL);
+ SendMessage(hwndList,CLM_SETBKCOLOR,GetSysColor(COLOR_WINDOW),0);
+ SendMessage(hwndList,CLM_SETGREYOUTFLAGS,0,0);
+ SendMessage(hwndList,CLM_SETLEFTMARGIN,4,0);
+ SendMessage(hwndList,CLM_SETINDENT,10,0);
+ SendMessage(hwndList,CLM_SETHIDEEMPTYGROUPS,1,0);
+ SendMessage(hwndList,CLM_SETHIDEOFFLINEROOT,1,0);
+ for (i = 0; i <= FONTID_MAX; ++i)
+ SendMessage(hwndList, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT));
+}
+
+static void sttActivateOptionsPage(HWND hwnd, TCHAR *aSection, TCHAR *aPage)
+{
+ TCHAR buf[256];
+ TCHAR *section = TranslateTS(aSection);
+
+ HWND hwndTree = FindWindowEx(GetParent(hwnd), NULL, WC_TREEVIEW, NULL);
+ for (HTREEITEM htiSection = TreeView_GetRoot(hwndTree); htiSection; htiSection = TreeView_GetNextSibling(hwndTree, htiSection))
+ {
+ TVITEM tvi = {0};
+ tvi.mask = TVIF_TEXT;
+ tvi.hItem = htiSection;
+ tvi.pszText = buf;
+ tvi.cchTextMax = SIZEOF(buf);
+ TreeView_GetItem(hwndTree, &tvi);
+
+ if (!lstrcmp(buf, section))
+ {
+ if (!aPage)
+ {
+ TreeView_Select(hwndTree, htiSection, TVGN_CARET);
+ return;
+ } else
+ {
+ TreeView_Expand(hwndTree, htiSection, TVE_EXPAND);
+ }
+
+ TCHAR *page = TranslateTS(aPage);
+ for (HTREEITEM htiPage = TreeView_GetChild(hwndTree, htiSection); htiPage; htiPage = TreeView_GetNextSibling(hwndTree, htiPage))
+ {
+ TVITEM tvi = {0};
+ tvi.mask = TVIF_TEXT;
+ tvi.hItem = htiPage;
+ tvi.pszText = buf;
+ tvi.cchTextMax = SIZEOF(buf);
+ TreeView_GetItem(hwndTree, &tvi);
+
+ if (!lstrcmp(buf, page))
+ {
+ TreeView_Select(hwndTree, htiPage, TVGN_CARET);
+ return;
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+static BOOL CALLBACK OptionsDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static bool bInitialized = false;
+ static HANDLE hSelectedContact = 0;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ bInitialized = false;
+
+ TranslateDialogDefault(hwnd);
+
+ CheckDlgButton(hwnd, IDC_CHK_GROUPS, g_Options.bUseGroups ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_GROUPCOLUMS, g_Options.bUseColumns ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_SECONDLINE, g_Options.bSecondLine ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_AVATARS, g_Options.bAvatars ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_AVATARBORDER, g_Options.bAvatarBorder ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_NOTRANSPARENTBORDER, g_Options.bNoTransparentBorder ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_SYSCOLORS, g_Options.bSysColors ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_CENTERHOTKEY, g_Options.bCenterHotkey ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_RIGHTAVATARS, g_Options.bRightAvatars ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwnd, IDC_CHK_DIMIDLE, g_Options.bDimIdle ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemInt(hwnd, IDC_TXT_RADIUS, g_Options.wAvatarRadius, FALSE);
+ SetDlgItemInt(hwnd, IDC_TXT_MAXRECENT, g_Options.wMaxRecent, FALSE);
+
+ SetWindowLong(GetDlgItem(hwnd, IDC_CLIST), GWL_STYLE,
+ GetWindowLong(GetDlgItem(hwnd, IDC_CLIST), GWL_STYLE)|CLS_CHECKBOXES|CLS_HIDEEMPTYGROUPS|CLS_USEGROUPS|CLS_GREYALTERNATE|CLS_GROUPCHECKBOXES);
+ SendMessage(GetDlgItem(hwnd, IDC_CLIST), CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP|CLS_EX_TRACKSELECT, 0);
+ sttResetListOptions(GetDlgItem(hwnd, IDC_CLIST));
+
+ hSelectedContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ for ( ; hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ {
+ SendDlgItemMessage(hwnd, IDC_CLIST, CLM_SETCHECKMARK,
+ SendDlgItemMessage(hwnd, IDC_CLIST, CLM_FINDCONTACT, (WPARAM)hContact, 0),
+ DBGetContactSettingByte(hContact, "FavContacts", "IsFavourite", 0));
+ }
+
+ if (!ServiceExists(MS_HOTKEY_REGISTER))
+ EnableWindow(GetDlgItem(hwnd, IDC_BTN_HOTKEYS), FALSE);
+
+ bInitialized = true;
+
+ PostMessage(hwnd, WM_APP, 0, 0);
+
+ return TRUE;
+ }
+
+ case WM_APP:
+ {
+ BOOL bGroups = IsDlgButtonChecked(hwnd, IDC_CHK_GROUPS);
+ EnableWindow(GetDlgItem(hwnd, IDC_CHK_GROUPCOLUMS), bGroups);
+
+ BOOL bAvatars = IsDlgButtonChecked(hwnd, IDC_CHK_AVATARS);
+ BOOL bBorders = IsDlgButtonChecked(hwnd, IDC_CHK_AVATARBORDER);
+ EnableWindow(GetDlgItem(hwnd, IDC_CHK_AVATARBORDER), bAvatars);
+ EnableWindow(GetDlgItem(hwnd, IDC_CHK_RIGHTAVATARS), bAvatars);
+ EnableWindow(GetDlgItem(hwnd, IDC_CHK_NOTRANSPARENTBORDER), bAvatars && bBorders);
+ EnableWindow(GetDlgItem(hwnd, IDC_TXT_RADIUS), bAvatars && bBorders);
+ return TRUE;
+ }
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ if (lpdis->CtlID == IDC_CANVAS)
+ {
+ MEASUREITEMSTRUCT mis = {0};
+ DRAWITEMSTRUCT dis = *lpdis;
+
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(COLOR_BTNFACE));
+ if (hSelectedContact)
+ {
+ Options options;
+ options.bSecondLine = IsDlgButtonChecked(hwnd, IDC_CHK_SECONDLINE);
+ options.bAvatars = IsDlgButtonChecked(hwnd, IDC_CHK_AVATARS);
+ options.bAvatarBorder = IsDlgButtonChecked(hwnd, IDC_CHK_AVATARBORDER);
+ options.bNoTransparentBorder = IsDlgButtonChecked(hwnd, IDC_CHK_NOTRANSPARENTBORDER);
+ options.bSysColors = IsDlgButtonChecked(hwnd, IDC_CHK_SYSCOLORS);
+ options.bCenterHotkey = IsDlgButtonChecked(hwnd, IDC_CHK_CENTERHOTKEY);
+ options.bRightAvatars = IsDlgButtonChecked(hwnd, IDC_CHK_RIGHTAVATARS);
+ options.bDimIdle = IsDlgButtonChecked(hwnd, IDC_CHK_DIMIDLE);
+ options.wAvatarRadius = GetDlgItemInt(hwnd, IDC_TXT_RADIUS, NULL, FALSE);
+ options.wMaxRecent = GetDlgItemInt(hwnd, IDC_TXT_MAXRECENT, NULL, FALSE);
+
+ mis.CtlID = 0;
+ mis.CtlType = ODT_MENU;
+ mis.itemData = (DWORD)hSelectedContact;
+ sttMeasureItem(&mis, &options);
+ dis.rcItem.bottom = dis.rcItem.top + mis.itemHeight;
+
+ dis.CtlID = 0;
+ dis.CtlType = ODT_MENU;
+ dis.itemData = (DWORD)hSelectedContact;
+ sttDrawItem(&dis, &options);
+
+ RECT rc = lpdis->rcItem;
+ rc.bottom = rc.top + mis.itemHeight;
+ FrameRect(lpdis->hDC, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_CHK_SECONDLINE:
+ case IDC_CHK_AVATARS:
+ case IDC_CHK_AVATARBORDER:
+ case IDC_CHK_NOTRANSPARENTBORDER:
+ case IDC_CHK_SYSCOLORS:
+ case IDC_CHK_CENTERHOTKEY:
+ case IDC_CHK_GROUPS:
+ case IDC_CHK_GROUPCOLUMS:
+ case IDC_CHK_RIGHTAVATARS:
+ SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0);
+ RedrawWindow(GetDlgItem(hwnd, IDC_CANVAS), NULL, NULL, RDW_INVALIDATE);
+ PostMessage(hwnd, WM_APP, 0, 0);
+ break;
+
+ case IDC_BTN_HOTKEYS:
+ if (ServiceExists(MS_HOTKEY_REGISTER))
+ {
+ sttActivateOptionsPage(hwnd, _T("Customize"), _T("Hotkeys"));
+ }
+ break;
+
+ case IDC_BTN_FONTS:
+ sttActivateOptionsPage(hwnd, _T("Customize"), _T("Fonts"));
+ break;
+
+ case IDC_TXT_RADIUS:
+ if ((HIWORD(wParam) == EN_CHANGE) && bInitialized)
+ {
+ RedrawWindow(GetDlgItem(hwnd, IDC_CANVAS), NULL, NULL, RDW_INVALIDATE);
+ SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0);
+ }
+ break;
+
+ case IDC_TXT_MAXRECENT:
+ if ((HIWORD(wParam) == EN_CHANGE) && bInitialized)
+ SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ if ((((LPNMHDR)lParam)->idFrom == 0) && (((LPNMHDR)lParam)->code == PSN_APPLY))
+ {
+ g_Options.bSecondLine = IsDlgButtonChecked(hwnd, IDC_CHK_SECONDLINE);
+ g_Options.bAvatars = IsDlgButtonChecked(hwnd, IDC_CHK_AVATARS);
+ g_Options.bAvatarBorder = IsDlgButtonChecked(hwnd, IDC_CHK_AVATARBORDER);
+ g_Options.bNoTransparentBorder = IsDlgButtonChecked(hwnd, IDC_CHK_NOTRANSPARENTBORDER);
+ g_Options.bSysColors = IsDlgButtonChecked(hwnd, IDC_CHK_SYSCOLORS);
+ g_Options.bCenterHotkey = IsDlgButtonChecked(hwnd, IDC_CHK_CENTERHOTKEY);
+ g_Options.bUseGroups = IsDlgButtonChecked(hwnd, IDC_CHK_GROUPS);
+ g_Options.bUseColumns = IsDlgButtonChecked(hwnd, IDC_CHK_GROUPCOLUMS);
+ g_Options.bRightAvatars = IsDlgButtonChecked(hwnd, IDC_CHK_RIGHTAVATARS);
+ g_Options.bDimIdle = IsDlgButtonChecked(hwnd, IDC_CHK_DIMIDLE);
+ g_Options.wAvatarRadius = GetDlgItemInt(hwnd, IDC_TXT_RADIUS, NULL, FALSE);
+ g_Options.wMaxRecent = GetDlgItemInt(hwnd, IDC_TXT_MAXRECENT, NULL, FALSE);
+
+ sttSaveOptions();
+
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ for ( ; hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ {
+ BYTE fav = SendDlgItemMessage(hwnd, IDC_CLIST, CLM_GETCHECKMARK,
+ SendDlgItemMessage(hwnd, IDC_CLIST, CLM_FINDCONTACT, (WPARAM)hContact, 0), 0);
+ if (fav != DBGetContactSettingByte(hContact, "FavContacts", "IsFavourite", 0))
+ DBWriteContactSettingByte(hContact, "FavContacts", "IsFavourite", fav);
+ if (fav) CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0);
+ }
+ } else
+ if (((LPNMHDR)lParam)->idFrom == IDC_CLIST)
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case CLN_OPTIONSCHANGED:
+ {
+ sttResetListOptions(GetDlgItem(hwnd,IDC_CLIST));
+ break;
+ }
+
+ case CLN_NEWCONTACT:
+ {
+ int iSelection = (int)((NMCLISTCONTROL *)lParam)->hItem;
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ for ( ; hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ if (SendDlgItemMessage(hwnd, IDC_CLIST, CLM_FINDCONTACT, (WPARAM)hContact, 0) == iSelection)
+ {
+ SendDlgItemMessage(hwnd, IDC_CLIST, CLM_SETCHECKMARK, iSelection,
+ DBGetContactSettingByte(hContact, "FavContacts", "IsFavourite", 0));
+ break;
+ }
+ break;
+ }
+
+ case CLN_CHECKCHANGED:
+ {
+ int iSelection = (int)((NMCLISTCONTROL *)lParam)->hItem;
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ for ( ; hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ if (SendDlgItemMessage(hwnd, IDC_CLIST, CLM_FINDCONTACT, (WPARAM)hContact, 0) == iSelection)
+ break;
+ if (hContact)
+ {
+ hSelectedContact = hContact;
+ RedrawWindow(GetDlgItem(hwnd, IDC_CANVAS), NULL, NULL, RDW_INVALIDATE);
+ }
+ SendMessage(GetParent(hwnd), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return FALSE;
+}
|