From 78d71d2cad6f243c6ff31d41380b8c5b58407de5 Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Thu, 17 May 2012 17:37:22 +0000 Subject: added some plugins git-svn-id: http://svn.miranda-ng.org/main/trunk@20 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/FavContacts/favcontacts.sln | 19 + plugins/FavContacts/favcontacts.vcxproj | 131 ++ plugins/FavContacts/favcontacts.vcxproj.filters | 76 ++ plugins/FavContacts/res/favlist.ico | Bin 0 -> 2550 bytes plugins/FavContacts/res/favorite.ico | Bin 0 -> 2550 bytes plugins/FavContacts/res/regular.ico | Bin 0 -> 2550 bytes plugins/FavContacts/res/userprefs.ico | Bin 0 -> 2550 bytes plugins/FavContacts/resource.h | 39 + plugins/FavContacts/resource.rc | 166 +++ plugins/FavContacts/src/contact_cache.cpp | 231 ++++ plugins/FavContacts/src/contact_cache.h | 61 + plugins/FavContacts/src/cserver.cpp | 82 ++ plugins/FavContacts/src/cserver.h | 42 + plugins/FavContacts/src/csocket.cpp | 22 + plugins/FavContacts/src/csocket.h | 16 + plugins/FavContacts/src/favlist.cpp | 0 plugins/FavContacts/src/favlist.h | 134 +++ plugins/FavContacts/src/headers.h | 94 ++ plugins/FavContacts/src/http_api.cpp | 165 +++ plugins/FavContacts/src/http_api.h | 7 + plugins/FavContacts/src/main.cpp | 1453 +++++++++++++++++++++++ 21 files changed, 2738 insertions(+) create mode 100644 plugins/FavContacts/favcontacts.sln create mode 100644 plugins/FavContacts/favcontacts.vcxproj create mode 100644 plugins/FavContacts/favcontacts.vcxproj.filters create mode 100644 plugins/FavContacts/res/favlist.ico create mode 100644 plugins/FavContacts/res/favorite.ico create mode 100644 plugins/FavContacts/res/regular.ico create mode 100644 plugins/FavContacts/res/userprefs.ico create mode 100644 plugins/FavContacts/resource.h create mode 100644 plugins/FavContacts/resource.rc create mode 100644 plugins/FavContacts/src/contact_cache.cpp create mode 100644 plugins/FavContacts/src/contact_cache.h create mode 100644 plugins/FavContacts/src/cserver.cpp create mode 100644 plugins/FavContacts/src/cserver.h create mode 100644 plugins/FavContacts/src/csocket.cpp create mode 100644 plugins/FavContacts/src/csocket.h create mode 100644 plugins/FavContacts/src/favlist.cpp create mode 100644 plugins/FavContacts/src/favlist.h create mode 100644 plugins/FavContacts/src/headers.h create mode 100644 plugins/FavContacts/src/http_api.cpp create mode 100644 plugins/FavContacts/src/http_api.h create mode 100644 plugins/FavContacts/src/main.cpp (limited to 'plugins/FavContacts') diff --git a/plugins/FavContacts/favcontacts.sln b/plugins/FavContacts/favcontacts.sln new file mode 100644 index 0000000000..4572aa3063 --- /dev/null +++ b/plugins/FavContacts/favcontacts.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "favcontacts", "favcontacts.vcxproj", "{2D0B4CB0-3ACA-4612-B745-FF3050E1500A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug Unicode|Win32 = Debug Unicode|Win32 + Release Unicode|Win32 = Release Unicode|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2D0B4CB0-3ACA-4612-B745-FF3050E1500A}.Debug Unicode|Win32.ActiveCfg = Debug Unicode|Win32 + {2D0B4CB0-3ACA-4612-B745-FF3050E1500A}.Debug Unicode|Win32.Build.0 = Debug Unicode|Win32 + {2D0B4CB0-3ACA-4612-B745-FF3050E1500A}.Release Unicode|Win32.ActiveCfg = Release Unicode|Win32 + {2D0B4CB0-3ACA-4612-B745-FF3050E1500A}.Release Unicode|Win32.Build.0 = Release Unicode|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/plugins/FavContacts/favcontacts.vcxproj b/plugins/FavContacts/favcontacts.vcxproj new file mode 100644 index 0000000000..8c99da3f7b --- /dev/null +++ b/plugins/FavContacts/favcontacts.vcxproj @@ -0,0 +1,131 @@ + + + + + Debug Unicode + Win32 + + + Release Unicode + Win32 + + + + {2D0B4CB0-3ACA-4612-B745-FF3050E1500A} + favcontacts + Win32Proj + + + + DynamicLibrary + Unicode + true + + + DynamicLibrary + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)/Plugins\ + $(SolutionDir)$(Configuration)/Obj/$(ProjectName)\ + $(SolutionDir)$(Configuration)/Plugins\ + $(SolutionDir)$(Configuration)/Obj/$(ProjectName)\ + true + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ../../include;../ExternalAPI;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;FAVCONTACTS_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + comctl32.lib;%(AdditionalDependencies) + true + Windows + false + + + MachineX86 + + + + + Full + Size + ../../include;../ExternalAPI;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;FAVCONTACTS_EXPORTS;%(PreprocessorDefinitions) + + + MultiThreadedDLL + false + + + Level3 + ProgramDatabase + + + comctl32.lib;%(AdditionalDependencies) + Windows + true + true + + + false + + + MachineX86 + $(IntDir)$(TargetName).lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/FavContacts/favcontacts.vcxproj.filters b/plugins/FavContacts/favcontacts.vcxproj.filters new file mode 100644 index 0000000000..81cec32c10 --- /dev/null +++ b/plugins/FavContacts/favcontacts.vcxproj.filters @@ -0,0 +1,76 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + {190cde56-eadb-44f2-a0cd-1cb46fe8de29} + + + + + Source Files + + + Source Files + + + Source Files + + + http + + + http + + + http + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + http + + + http + + + http + + + + + Resource Files + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/plugins/FavContacts/res/favlist.ico b/plugins/FavContacts/res/favlist.ico new file mode 100644 index 0000000000..9b55cd0afa Binary files /dev/null and b/plugins/FavContacts/res/favlist.ico differ diff --git a/plugins/FavContacts/res/favorite.ico b/plugins/FavContacts/res/favorite.ico new file mode 100644 index 0000000000..4b0bff0f6b Binary files /dev/null and b/plugins/FavContacts/res/favorite.ico differ diff --git a/plugins/FavContacts/res/regular.ico b/plugins/FavContacts/res/regular.ico new file mode 100644 index 0000000000..6ddb31921a Binary files /dev/null and b/plugins/FavContacts/res/regular.ico differ diff --git a/plugins/FavContacts/res/userprefs.ico b/plugins/FavContacts/res/userprefs.ico new file mode 100644 index 0000000000..7028f10d03 Binary files /dev/null and b/plugins/FavContacts/res/userprefs.ico differ diff --git a/plugins/FavContacts/resource.h b/plugins/FavContacts/resource.h new file mode 100644 index 0000000000..4e3f557cf7 --- /dev/null +++ b/plugins/FavContacts/resource.h @@ -0,0 +1,39 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#define IDI_FAVOURITE 101 +#define IDI_ICON1 102 +#define IDI_REGULAR 102 +#define IDD_DIALOG1 103 +#define IDD_LIST 103 +#define IDD_PROPPAGE_LARGE 107 +#define IDD_OPTIONS 107 +#define IDC_CLIST 1001 +#define IDC_CHK_SECONDLINE 1003 +#define IDC_CHK_AVATARS 1004 +#define IDC_CHK_AVATARBORDER 1005 +#define IDC_TXT_RADIUS 1007 +#define IDC_CHK_NOTRANSPARENTBORDER 1008 +#define IDC_CHK_CENTERHOTKEY 1009 +#define IDC_CHK_SYSCOLORS 1010 +#define IDC_CANVAS 1011 +#define IDC_CHK_DIMIDLE 1012 +#define IDC_CHK_GROUPS 1013 +#define IDC_CHK_GROUPCOLUMS 1014 +#define IDC_BTN_FONTS 1015 +#define IDC_TXT_RADIUS2 1016 +#define IDC_TXT_MAXRECENT 1016 +#define IDC_BTN_HOTKEYS 1018 +#define IDC_CHK_RIGHTAVATARS 1019 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1016 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/FavContacts/resource.rc b/plugins/FavContacts/resource.rc new file mode 100644 index 0000000000..a96aabaf04 --- /dev/null +++ b/plugins/FavContacts/resource.rc @@ -0,0 +1,166 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.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(1251) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_FAVOURITE ICON "res\\favorite.ico" +IDI_REGULAR ICON "res\\regular.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPTIONS DIALOGEX 0, 0, 316, 251 +STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Customize",IDC_STATIC,5,5,149,241 + CONTROL "Show second line",IDC_CHK_SECONDLINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,43,137,10 + CONTROL "Show avatars",IDC_CHK_AVATARS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,56,137,10 + CONTROL "Draw avatar border",IDC_CHK_AVATARBORDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,26,82,122,10 + RTEXT "Round corners by",IDC_STATIC,26,95,71,10,SS_CENTERIMAGE + EDITTEXT IDC_TXT_RADIUS,99,94,29,12,ES_CENTER | ES_AUTOHSCROLL | ES_NUMBER + LTEXT "px.",IDC_STATIC,132,95,16,10,SS_CENTERIMAGE + CONTROL "Hide for transparent avatars",IDC_CHK_NOTRANSPARENTBORDER, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,40,108,108,10 + CONTROL "Use system colors and fonts",IDC_CHK_SYSCOLORS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,134,137,10 + CONTROL "Hotkey shows menu centered on screen",IDC_CHK_CENTERHOTKEY, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,147,139,10 + LTEXT "Menu item preview:",IDC_STATIC,11,177,137,8 + CONTROL "",IDC_CANVAS,"Static",SS_OWNERDRAW,26,189,122,26 + GROUPBOX "Favourite Contacts",IDC_STATIC,161,5,150,241 + CONTROL "",IDC_CLIST,"CListControl",WS_TABSTOP | 0x1,167,17,138,223,WS_EX_CLIENTEDGE + CONTROL "Enable groups",IDC_CHK_GROUPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,17,137,10 + CONTROL "Multicolumn menu",IDC_CHK_GROUPCOLUMS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,26,30,122,10 + CONTROL "Configure fonts and colors...",IDC_BTN_FONTS,"Hyperlink",WS_TABSTOP,11,229,137,11 + CONTROL "Set up menu hotkey...",IDC_BTN_HOTKEYS,"Hyperlink",WS_TABSTOP,11,218,137,11 + CONTROL "Align to the right",IDC_CHK_RIGHTAVATARS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,26,69,122,10 + CONTROL "Dim idle contact icons",IDC_CHK_DIMIDLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,121,139,10 + RTEXT "Show",IDC_STATIC,12,160,34,10,SS_CENTERIMAGE + EDITTEXT IDC_TXT_MAXRECENT,50,159,29,12,ES_CENTER | ES_AUTOHSCROLL | ES_NUMBER + LTEXT "recent contacts",IDC_STATIC,84,160,65,10,SS_CENTERIMAGE +END + +IDD_LIST DIALOGEX 0, 0, 316, 183 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_BORDER | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPTIONS, DIALOG + BEGIN + VERTGUIDE, 5 + VERTGUIDE, 11 + VERTGUIDE, 26 + VERTGUIDE, 148 + VERTGUIDE, 154 + VERTGUIDE, 161 + VERTGUIDE, 167 + VERTGUIDE, 305 + VERTGUIDE, 311 + HORZGUIDE, 5 + HORZGUIDE, 17 + HORZGUIDE, 229 + HORZGUIDE, 240 + HORZGUIDE, 246 + END + + IDD_LIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 309 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END +END +#endif // APSTUDIO_INVOKED + +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Ukrainian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_UKR) +#ifdef _WIN32 +LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT +#pragma code_page(1251) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Ukrainian resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + 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 +#include + +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 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 +#include +#include + +#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 +#include +#include + +#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 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("")); + } 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 +{ +private: + int nGroups; + +public: + TFavContacts(): LIST(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 +#define _WIN32_WINNT 0x0501 +#include +#include +#include +#include + +#define MIRANDA_VER 0x800 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + 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("")); + 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; +} -- cgit v1.2.3