From 48540940b6c28bb4378abfeb500ec45a625b37b6 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 15 May 2012 10:38:20 +0000 Subject: initial commit git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- src/modules/skin/hotkeys.cpp | 1465 ++++++++++++++++++++++++++++++++++++++++ src/modules/skin/skinicons.cpp | 489 ++++++++++++++ src/modules/skin/sounds.cpp | 469 +++++++++++++ 3 files changed, 2423 insertions(+) create mode 100644 src/modules/skin/hotkeys.cpp create mode 100644 src/modules/skin/skinicons.cpp create mode 100644 src/modules/skin/sounds.cpp (limited to 'src/modules/skin') diff --git a/src/modules/skin/hotkeys.cpp b/src/modules/skin/hotkeys.cpp new file mode 100644 index 0000000000..cd87fda1a7 --- /dev/null +++ b/src/modules/skin/hotkeys.cpp @@ -0,0 +1,1465 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include + +#define DBMODULENAME "SkinHotKeys" +#define WM_HOTKEYUNREGISTERED (WM_USER+721) + +typedef enum { HKT_GLOBAL, HKT_LOCAL, HKT_MANUAL, HKT_COUNT } THotkeyType; + +typedef struct _THotkeyItem THotkeyItem; +struct _THotkeyItem +{ + THotkeyType type; + char *pszService, *pszName; // pszName is valid _only_ for "root" hotkeys + TCHAR *ptszSection, *ptszDescription; + TCHAR *ptszSection_tr, *ptszDescription_tr; + LPARAM lParam; + WORD DefHotkey, Hotkey; + bool Enabled; + ATOM idHotkey; + + THotkeyItem *rootHotkey; + int nSubHotkeys; + bool allowSubHotkeys; + + bool OptChanged, OptDeleted, OptNew; + WORD OptHotkey; + THotkeyType OptType; + bool OptEnabled; + + bool UnregisterHotkey; // valid only during WM_APP message in options UI, used to remove unregistered hotkeys from options +}; + +static int sttCompareHotkeys(const THotkeyItem *p1, const THotkeyItem *p2) +{ + int res; + if ( res = lstrcmp( p1->ptszSection_tr, p2->ptszSection_tr )) + return res; + if ( res = lstrcmp( p1->ptszDescription_tr, p2->ptszDescription_tr )) + return res; + if (!p1->rootHotkey && p2->rootHotkey) + return -1; + if (p1->rootHotkey && !p2->rootHotkey) + return 1; + return 0; +} + +static LIST hotkeys( 10, sttCompareHotkeys ); + +static void sttFreeHotkey(THotkeyItem *item); + +static BOOL bModuleInitialized = FALSE; +static HWND g_hwndHotkeyHost = NULL; +static HWND g_hwndOptions = NULL; +static DWORD g_pid = 0; +static int g_hotkeyCount = 0; +static HANDLE hEvChanged = 0; + +static LRESULT CALLBACK sttHotkeyHostWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + +static TCHAR *sttHokeyVkToName(WORD vkKey); +static void sttHotkeyEditCreate(HWND hwnd); +static void sttHotkeyEditDestroy(HWND hwnd); +static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); +static void sttRegisterHotkeys(); +static void sttUnregisterHotkeys(); + +static INT_PTR svcHotkeySubclass(WPARAM wParam, LPARAM lParam); +static INT_PTR svcHotkeyUnsubclass(WPARAM wParam, LPARAM lParam); +static INT_PTR svcHotkeyRegister(WPARAM wParam, LPARAM lParam); +static INT_PTR svcHotkeyUnregister(WPARAM wParam, LPARAM lParam); +static INT_PTR svcHotkeyCheck(WPARAM wParam, LPARAM lParam); + +HHOOK hhkKeyboard = NULL; +static LRESULT CALLBACK sttKeyboardProc(int code, WPARAM wParam, LPARAM lParam); + +static void sttWordToModAndVk(WORD w, BYTE *mod, BYTE *vk) +{ + *mod = 0; + if (HIBYTE(w) & HOTKEYF_CONTROL) *mod |= MOD_CONTROL; + if (HIBYTE(w) & HOTKEYF_SHIFT) *mod |= MOD_SHIFT; + if (HIBYTE(w) & HOTKEYF_ALT) *mod |= MOD_ALT; + if (HIBYTE(w) & HOTKEYF_EXT) *mod |= MOD_WIN; + *vk = LOBYTE(w); +} + +static LRESULT CALLBACK sttHotkeyHostWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_HOTKEY ) { + int i; + for (i = 0; i < hotkeys.getCount(); i++) { + THotkeyItem *item = hotkeys[i]; + if (item->type != HKT_GLOBAL) continue; + if (!item->Enabled) continue; + if (item->pszService && (wParam == item->idHotkey)) { + CallService(item->pszService, 0, item->lParam); + break; + } } + + return FALSE; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +static LRESULT CALLBACK sttKeyboardProc(int code, WPARAM wParam, LPARAM lParam) +{ + if (code == HC_ACTION && !(HIWORD(lParam) & KF_UP)) { + int i; + BYTE mod=0, vk=wParam; + + if ( vk ) { + if (GetAsyncKeyState(VK_CONTROL)) mod |= MOD_CONTROL; + if (GetAsyncKeyState(VK_MENU)) mod |= MOD_ALT; + if (GetAsyncKeyState(VK_SHIFT)) mod |= MOD_SHIFT; + if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) mod |= MOD_WIN; + + for ( i = 0; i < hotkeys.getCount(); i++ ) { + THotkeyItem *item = hotkeys[i]; + BYTE hkMod, hkVk; + if (item->type != HKT_LOCAL) continue; + sttWordToModAndVk(item->Hotkey, &hkMod, &hkVk); + if (!hkVk) continue; + if (!item->Enabled) continue; + if (item->pszService && (vk == hkVk) && (mod == hkMod)) { + CallService(item->pszService, 0, item->lParam); + return TRUE; + } } } } + + return CallNextHookEx(hhkKeyboard, code, wParam, lParam); +} + +static INT_PTR svcHotkeySubclass(WPARAM wParam, LPARAM) +{ + sttHotkeyEditCreate((HWND)wParam); + return 0; +} + +static INT_PTR svcHotkeyUnsubclass(WPARAM wParam, LPARAM) +{ + sttHotkeyEditDestroy((HWND)wParam); + return 0; +} + +static INT_PTR svcHotkeyRegister(WPARAM wParam, LPARAM lParam) +{ + HOTKEYDESC *desc = (HOTKEYDESC *)lParam; + if ( desc->cbSize != sizeof(HOTKEYDESC) && desc->cbSize != HOTKEYDESC_SIZE_V1 ) + return 0; + + THotkeyItem *item = ( THotkeyItem* )mir_alloc(sizeof(THotkeyItem)); + #if defined( _UNICODE ) + DWORD dwFlags = ( desc->cbSize >= sizeof(HOTKEYDESC)) ? desc->dwFlags : 0; + if ( dwFlags & HKD_UNICODE ) { + item->ptszSection = mir_tstrdup( desc->ptszSection ); + item->ptszDescription = mir_tstrdup( desc->ptszDescription ); + } + else { + item->ptszSection = mir_a2u( desc->pszSection ); + item->ptszDescription = mir_a2u( desc->pszDescription ); + } + #else + item->ptszSection = mir_tstrdup( desc->pszSection ); + item->ptszDescription = mir_tstrdup( desc->pszDescription ); + #endif + item->ptszSection_tr = TranslateTS(item->ptszSection); + item->ptszDescription_tr = TranslateTS(item->ptszDescription); + item->allowSubHotkeys = TRUE; + item->rootHotkey = NULL; + item->nSubHotkeys = 0; + + if ( item->rootHotkey = hotkeys.find( item )) { + if (item->rootHotkey->allowSubHotkeys) { + char nameBuf[MAXMODULELABELLENGTH]; + mir_snprintf(nameBuf, SIZEOF(nameBuf), "%s$%d", item->rootHotkey->pszName, item->rootHotkey->nSubHotkeys); + item->pszName = mir_strdup(nameBuf); + item->Enabled = TRUE; + + item->rootHotkey->nSubHotkeys++; + } + else { + mir_free(item->ptszSection); + mir_free(item->ptszDescription); + mir_free(item); + return 0; + } + } + else { + item->pszName = mir_strdup(desc->pszName); + item->Enabled = !DBGetContactSettingByte(NULL, DBMODULENAME "Off", item->pszName, 0); + } + + item->pszService = desc->pszService ? mir_strdup(desc->pszService) : 0; + item->DefHotkey = desc->DefHotKey & ~HKF_MIRANDA_LOCAL; + item->Hotkey = DBGetContactSettingWord(NULL, DBMODULENAME, item->pszName, item->DefHotkey); + item->type = item->pszService ? + ( THotkeyType )DBGetContactSettingByte(NULL, DBMODULENAME "Types", item->pszName, + (desc->DefHotKey & HKF_MIRANDA_LOCAL) ? HKT_LOCAL : HKT_GLOBAL) : HKT_MANUAL; + item->lParam = desc->lParam; + + char buf[256]; + mir_snprintf(buf, SIZEOF(buf), "mir_hotkey_%d_%d", g_pid, g_hotkeyCount++); + item->idHotkey = GlobalAddAtomA(buf); + if (item->type == HKT_GLOBAL) { + if (item->Enabled) { + BYTE mod, vk; + sttWordToModAndVk(item->Hotkey, &mod, &vk); + if (vk) RegisterHotKey(g_hwndHotkeyHost, item->idHotkey, mod, vk); + } } + + hotkeys.insert( item ); + + if ( !item->rootHotkey ) { + /* try to load alternatives from db */ + int count, i; + mir_snprintf(buf, SIZEOF(buf), "%s$count", item->pszName); + count = (int)DBGetContactSettingDword(NULL, DBMODULENAME, buf, -1); + for (i = 0; i < count; i++) { + mir_snprintf(buf, SIZEOF(buf), "%s$%d", item->pszName, i); + if (!DBGetContactSettingWord(NULL, DBMODULENAME, buf, 0)) + continue; + + svcHotkeyRegister(wParam, lParam); + } + item->allowSubHotkeys = count < 0; + } + else { + mir_free( item->pszName ); + item->pszName = NULL; + } + + return item->idHotkey; +} + +static INT_PTR svcHotkeyUnregister(WPARAM, LPARAM lParam) +{ + int i; + char *pszName = (char *)lParam; + char pszNamePrefix[MAXMODULELABELLENGTH]; + size_t cbNamePrefix; + mir_snprintf(pszNamePrefix, SIZEOF(pszNamePrefix), "%s$", pszName); + cbNamePrefix = strlen(pszNamePrefix); + + for (i = 0; i < hotkeys.getCount(); ++i) + { + char *pszCurrentName = hotkeys[i]->rootHotkey ? + hotkeys[i]->rootHotkey->pszName : + hotkeys[i]->pszName; + if (!pszCurrentName) continue; + + hotkeys[i]->UnregisterHotkey = + !lstrcmpA(pszCurrentName, pszName) || + !strncmp(pszCurrentName, pszNamePrefix, cbNamePrefix); + } + + if (g_hwndOptions) + SendMessage(g_hwndOptions, WM_HOTKEYUNREGISTERED, 0, 0); + + for (i = 0; i < hotkeys.getCount(); ++i) + if (hotkeys[i]->UnregisterHotkey) { + sttFreeHotkey(hotkeys[i]); + List_Remove((SortedList *)&hotkeys, i); + --i; + } + + return 0; +} + +static INT_PTR svcHotkeyCheck(WPARAM wParam, LPARAM lParam) +{ + MSG *msg = (MSG *)wParam; + TCHAR *pszSection = mir_a2t((char *)lParam); + + if ((msg->message == WM_KEYDOWN) || (msg->message == WM_SYSKEYDOWN)) { + int i; + BYTE mod=0, vk=msg->wParam; + + if (vk) { + if (GetAsyncKeyState(VK_CONTROL)) mod |= MOD_CONTROL; + if (GetAsyncKeyState(VK_MENU)) mod |= MOD_ALT; + if (GetAsyncKeyState(VK_SHIFT)) mod |= MOD_SHIFT; + if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) mod |= MOD_WIN; + + for ( i = 0; i < hotkeys.getCount(); i++ ) { + THotkeyItem *item = hotkeys[i]; + BYTE hkMod, hkVk; + if ((item->type != HKT_MANUAL) || lstrcmp(pszSection, item->ptszSection)) continue; + sttWordToModAndVk(item->Hotkey, &hkMod, &hkVk); + if (!hkVk) continue; + if (!item->Enabled) continue; + if ((vk == hkVk) && (mod == hkMod)) { + mir_free(pszSection); + return item->lParam; + } } } } + + mir_free(pszSection); + return 0; +} + +static void sttFreeHotkey(THotkeyItem *item) +{ + if ( item->type == HKT_GLOBAL && item->Enabled ) + UnregisterHotKey(g_hwndHotkeyHost, item->idHotkey); + GlobalDeleteAtom(item->idHotkey); + mir_free(item->pszName); + mir_free(item->pszService); + mir_free(item->ptszDescription); + mir_free(item->ptszSection); + mir_free(item); +} + +static void sttRegisterHotkeys() +{ + int i; + for ( i = 0; i < hotkeys.getCount(); i++ ) { + THotkeyItem *item = hotkeys[i]; + UnregisterHotKey(g_hwndHotkeyHost, item->idHotkey); + if (item->type != HKT_GLOBAL) continue; + if (item->Enabled) { + BYTE mod, vk; + sttWordToModAndVk(item->Hotkey, &mod, &vk); + if (vk) RegisterHotKey(g_hwndHotkeyHost, item->idHotkey, mod, vk); +} } } + +static void sttUnregisterHotkeys() +{ + int i; + for (i = 0; i < hotkeys.getCount(); i++) { + THotkeyItem *item = hotkeys[i]; + if ( item->type == HKT_GLOBAL && item->Enabled ) + UnregisterHotKey(g_hwndHotkeyHost, item->idHotkey); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Hotkey control +typedef struct +{ + WNDPROC oldWndProc; + BYTE shift; + BYTE key; +} + THotkeyBoxData; + +static TCHAR *sttHokeyVkToName(WORD vkKey) +{ + static TCHAR buf[256] = {0}; + DWORD code = MapVirtualKey(vkKey, 0) << 16; + + switch (vkKey) + { + case 0: + case VK_CONTROL: + case VK_SHIFT: + case VK_MENU: + case VK_LWIN: + case VK_RWIN: + case VK_PAUSE: + case VK_CANCEL: + case VK_NUMLOCK: + case VK_CAPITAL: + case VK_SCROLL: + return _T(""); + + case VK_DIVIDE: + case VK_INSERT: + case VK_HOME: + case VK_PRIOR: + case VK_DELETE: + case VK_END: + case VK_NEXT: + case VK_LEFT: + case VK_RIGHT: + case VK_UP: + case VK_DOWN: + code |= (1UL << 24); + } + + GetKeyNameText(code, buf, 256); + return buf; +} + +void HotkeyToName(TCHAR *buf, int size, BYTE shift, BYTE key) +{ + mir_sntprintf(buf, size, _T("%s%s%s%s%s"), + (shift & HOTKEYF_CONTROL) ? _T("Ctrl + ") : _T(""), + (shift & HOTKEYF_ALT) ? _T("Alt + ") : _T(""), + (shift & HOTKEYF_SHIFT) ? _T("Shift + ") : _T(""), + (shift & HOTKEYF_EXT) ? _T("Win + ") : _T(""), + sttHokeyVkToName(key)); +} + +WORD GetHotkeyValue(INT_PTR idHotkey) +{ + for (int i = 0; i < hotkeys.getCount(); i++) + if (hotkeys[i]->idHotkey == idHotkey) + return hotkeys[i]->Enabled ? hotkeys[i]->Hotkey : 0; + + return 0; +} + +static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + BOOL bKeyDown = FALSE; + if (!data) return 0; + + switch (msg) { + case HKM_GETHOTKEY: + return data->key ? MAKEWORD(data->key, data->shift) : 0; + + case HKM_SETHOTKEY: + { + TCHAR buf[256] = {0}; + data->key = (BYTE)LOWORD(wParam); + data->shift = (BYTE)HIWORD(wParam); + HotkeyToName(buf, SIZEOF(buf), data->shift, data->key); + SetWindowText(hwnd, buf); + return 0; + } + + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + case WM_KILLFOCUS: + break; + + case WM_CHAR: + case WM_SYSCHAR: + case WM_PASTE: + case WM_CONTEXTMENU: + return TRUE; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + bKeyDown = TRUE; + + case WM_KEYUP: + case WM_SYSKEYUP: + { + TCHAR buf[256] = {0}; + + BYTE shift = 0; + BYTE key = wParam; + TCHAR *name = sttHokeyVkToName(key); + if (!*name || !bKeyDown) key = 0; + + if (GetAsyncKeyState(VK_CONTROL)) shift |= HOTKEYF_CONTROL; + if (GetAsyncKeyState(VK_MENU)) shift |= HOTKEYF_ALT; + if (GetAsyncKeyState(VK_SHIFT)) shift |= HOTKEYF_SHIFT; + if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) shift |= HOTKEYF_EXT; + + if (bKeyDown || !data->key) { + data->shift = shift; + data->key = key; + } + + HotkeyToName(buf, SIZEOF(buf), data->shift, data->key); + SetWindowText(hwnd, buf); + + if (bKeyDown && data->key) + SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLongPtr(hwnd, GWL_ID), 0), (LPARAM)hwnd); + return TRUE; + } + + case WM_DESTROY: + { + WNDPROC saveOldWndProc = data->oldWndProc; + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)data->oldWndProc); + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + mir_free(data); + return CallWindowProc(saveOldWndProc, hwnd, msg, wParam, lParam); + } } + + return CallWindowProc(data->oldWndProc, hwnd, msg, wParam, lParam); +} + +static void sttHotkeyEditCreate(HWND hwnd) +{ + THotkeyBoxData *data = (THotkeyBoxData *)mir_alloc(sizeof(THotkeyBoxData)); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data); + data->oldWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (ULONG_PTR)sttHotkeyEditProc); +} + +static void sttHotkeyEditDestroy(HWND hwnd) +{ + THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (ULONG_PTR)data->oldWndProc); + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + mir_free(data); +} + +/////////////////////////////////////////////////////////////////////////////// +// Options + +enum { COL_NAME, COL_TYPE, COL_KEY, COL_RESET, COL_ADDREMOVE }; + +static void sttOptionsSetupItem(HWND hwndList, int idx, THotkeyItem *item) +{ + TCHAR buf[256]; + LVITEM lvi = {0}; + lvi.iItem = idx; + + if ( !item->rootHotkey ) { + lvi.mask = LVIF_TEXT|LVIF_IMAGE; + lvi.iSubItem = COL_NAME; + lvi.pszText = item->ptszDescription_tr; + lvi.iImage = item->OptType; + ListView_SetItem(hwndList, &lvi); + + ListView_SetCheckState(hwndList, lvi.iItem, item->Enabled); + } + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = COL_KEY; + HotkeyToName(buf, SIZEOF(buf), HIBYTE(item->OptHotkey), LOBYTE(item->OptHotkey)); + lvi.pszText = buf; + ListView_SetItem(hwndList, &lvi); + + if ( item->rootHotkey ) { + lvi.mask = LVIF_IMAGE; + lvi.iSubItem = COL_TYPE; + lvi.iImage = item->OptType; + ListView_SetItem(hwndList, &lvi); + } + + lvi.mask = LVIF_IMAGE; + lvi.iSubItem = COL_RESET; + lvi.iImage = (item->Hotkey != item->OptHotkey) ? 5 : -1; + ListView_SetItem(hwndList, &lvi); + + lvi.mask = LVIF_IMAGE|LVIF_TEXT; + lvi.iSubItem = COL_ADDREMOVE; + if (item->rootHotkey) { + lvi.iImage = 4; + lvi.pszText = TranslateT("Remove shortcut"); + } + else { + lvi.iImage = 3; + lvi.pszText = TranslateT("Add another shortcut"); + } + ListView_SetItem(hwndList, &lvi); +} + +static void sttOptionsDeleteHotkey(HWND hwndList, int idx, THotkeyItem *item) +{ + item->OptDeleted = TRUE; + ListView_DeleteItem(hwndList, idx); + if (item->rootHotkey) + item->rootHotkey->OptChanged = TRUE; +} + +static int CALLBACK sttOptionsSortList(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) +{ + TCHAR title1[256] = {0}, title2[256] = {0}; + THotkeyItem *item1 = NULL, *item2 = NULL; + LVITEM lvi = {0}; + int res; + + lvi.mask = LVIF_TEXT|LVIF_PARAM; + lvi.iItem = lParam1; + lvi.pszText = title1; + lvi.cchTextMax = SIZEOF(title1); + if (ListView_GetItem((HWND)lParamSort, &lvi)) + item1 = (THotkeyItem *)lvi.lParam; + + lvi.mask = LVIF_TEXT|LVIF_PARAM; + lvi.iItem = lParam2; + lvi.pszText = title2; + lvi.cchTextMax = SIZEOF(title2); + if (ListView_GetItem((HWND)lParamSort, &lvi)) + item2 = (THotkeyItem *)lvi.lParam; + + if (!item1 && !item2) + return lstrcmp(title1, title2); + + if (!item1) { + if (res = lstrcmp(title1, item2->ptszSection_tr)) + return res; + return -1; + } + + if (!item2) { + if (res = lstrcmp(item1->ptszSection_tr, title2)) + return res; + return 1; + } + return sttCompareHotkeys(item1, item2); +} + +static void sttOptionsAddHotkey(HWND hwndList, THotkeyItem *item) +{ + char buf[256]; + LVITEM lvi = {0}; + + THotkeyItem *newItem = (THotkeyItem *)mir_alloc(sizeof(THotkeyItem)); + newItem->pszName = NULL; + newItem->pszService = item->pszService ? mir_strdup(item->pszService) : NULL; + newItem->ptszSection = mir_tstrdup(item->ptszSection); + newItem->ptszDescription = mir_tstrdup(item->ptszDescription); + newItem->ptszSection_tr = item->ptszSection_tr; + newItem->ptszDescription_tr = item->ptszDescription_tr; + newItem->lParam = item->lParam; + mir_snprintf(buf, SIZEOF(buf), "mir_hotkey_%d_%d", g_pid, g_hotkeyCount++); + newItem->idHotkey = GlobalAddAtomA(buf); + newItem->rootHotkey = item; + newItem->Hotkey = newItem->DefHotkey = newItem->OptHotkey = 0; + newItem->type = newItem->OptType = item->OptType; + newItem->Enabled = newItem->OptEnabled = TRUE; + newItem->OptChanged = newItem->OptDeleted = FALSE; + newItem->OptNew = TRUE; + + hotkeys.insert( newItem ); + + SendMessage(hwndList, WM_SETREDRAW, FALSE, 0); + + lvi.mask |= LVIF_PARAM; + lvi.lParam = (LPARAM)newItem; + sttOptionsSetupItem(hwndList, ListView_InsertItem(hwndList, &lvi), newItem); + ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); + + SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); + RedrawWindow(hwndList, NULL, NULL, RDW_INVALIDATE); + + item->OptChanged = TRUE; +} + +static void sttOptionsSetChanged(THotkeyItem *item) +{ + item->OptChanged = TRUE; + if (item->rootHotkey) + item->rootHotkey->OptChanged = TRUE; +} + +static void sttOptionsSaveItem(THotkeyItem *item) +{ + int i; + char buf[MAXMODULELABELLENGTH]; + + if (item->rootHotkey) return; + if (!item->OptChanged) return; + + item->Hotkey = item->OptHotkey; + item->type = item->OptType; + item->Enabled = item->OptEnabled; + + DBWriteContactSettingWord(NULL, DBMODULENAME, item->pszName, item->Hotkey); + DBWriteContactSettingByte(NULL, DBMODULENAME "Off", item->pszName, (BYTE)!item->Enabled); + if (item->type != HKT_MANUAL) + DBWriteContactSettingByte(NULL, DBMODULENAME "Types", item->pszName, (BYTE)item->type); + + item->nSubHotkeys = 0; + for (i = 0; i < hotkeys.getCount(); i++) { + THotkeyItem *subItem = hotkeys[i]; + if (subItem->rootHotkey == item) { + subItem->Hotkey = subItem->OptHotkey; + subItem->type = subItem->OptType; + + mir_snprintf(buf, SIZEOF(buf), "%s$%d", item->pszName, item->nSubHotkeys); + DBWriteContactSettingWord(NULL, DBMODULENAME, buf, subItem->Hotkey); + if (subItem->type != HKT_MANUAL) + DBWriteContactSettingByte(NULL, DBMODULENAME "Types", buf, (BYTE)subItem->type); + + ++item->nSubHotkeys; + } } + + mir_snprintf(buf, SIZEOF(buf), "%s$count", item->pszName); + DBWriteContactSettingDword(NULL, DBMODULENAME, buf, item->nSubHotkeys); +} + +static void sttBuildHotkeyList(HWND hwndList, TCHAR *section) +{ + int i, nItems=0; + ListView_DeleteAllItems(hwndList); + + for (i = 0; i < hotkeys.getCount(); i++) { + LVITEM lvi = {0}; + THotkeyItem *item = hotkeys[i]; + + if (item->OptDeleted) continue; + if (section && lstrcmp(section, item->ptszSection)) continue; + + if ( !section && (!i || lstrcmp(item->ptszSection, ((THotkeyItem *)hotkeys[i-1])->ptszSection ))) { + lvi.mask = LVIF_TEXT|LVIF_PARAM; + lvi.iItem = nItems++; + lvi.iSubItem = 0; + lvi.lParam = 0; + lvi.pszText = item->ptszSection_tr; + ListView_InsertItem(hwndList, &lvi); + ListView_SetCheckState(hwndList, lvi.iItem, TRUE); + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + lvi.pszText = item->ptszSection; + ListView_SetItem(hwndList, &lvi); + + lvi.iSubItem = 0; + } + + lvi.mask = LVIF_PARAM; + if (!section) { + lvi.mask |= LVIF_INDENT; + lvi.iIndent = 1; + } + lvi.iItem = nItems++; + lvi.lParam = (LPARAM)item; + ListView_InsertItem(hwndList, &lvi); + sttOptionsSetupItem(hwndList, nItems-1, item); + } +} + +static void sttOptionsStartEdit(HWND hwndDlg, HWND hwndHotkey) +{ + LVITEM lvi; + THotkeyItem *item; + int iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); + if (iItem < 0) return; + + lvi.mask = LVIF_PARAM; + lvi.iItem = iItem; + ListView_GetItem(hwndHotkey, &lvi); + + if (item = (THotkeyItem *)lvi.lParam) { + RECT rc; + ListView_GetSubItemRect(hwndHotkey, iItem, COL_KEY, LVIR_BOUNDS, &rc); + MapWindowPoints(hwndHotkey, hwndDlg, (LPPOINT)&rc, 2); + SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_SETHOTKEY, MAKELONG(LOBYTE(item->OptHotkey), HIBYTE(item->OptHotkey)), 0); + + SetWindowPos(hwndHotkey, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwndDlg, IDC_HOTKEY), HWND_TOP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_SHOWWINDOW); + RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), NULL, NULL, RDW_INVALIDATE); + + SetFocus(GetDlgItem(hwndDlg, IDC_HOTKEY)); + RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), NULL, NULL, RDW_INVALIDATE); + } +} + +static void sttOptionsDrawTextChunk(HDC hdc, TCHAR *text, RECT *rc) +{ + SIZE sz; + DrawText(hdc, text, lstrlen(text), rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER|DT_WORD_ELLIPSIS); + GetTextExtentPoint32(hdc, text, lstrlen(text), &sz); + rc->left += sz.cx; +} + +static INT_PTR CALLBACK sttOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static BOOL initialized = FALSE; + static int colWidth = 0; + static WORD currentLanguage = 0; + + HWND hwndHotkey = GetDlgItem(hwndDlg, IDC_LV_HOTKEYS); + + switch (msg) { + case WM_INITDIALOG: + { + int i; + LVCOLUMN lvc; + RECT rc; + HIMAGELIST hIml; + + initialized = FALSE; + + TranslateDialogDefault(hwndDlg); + + sttHotkeyEditCreate(GetDlgItem(hwndDlg, IDC_HOTKEY)); + + hIml = ImageList_Create(16, 16, ILC_MASK + (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16), 3, 1); + ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_WINDOWS); + ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_MIRANDA); + ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_WINDOW); + ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_ADDCONTACT); + ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_DELETE); + ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_UNDO); + + // This is added to use for drawing operation only + ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_GROUPOPEN); + ImageList_AddIcon_IconLibLoaded(hIml, SKINICON_OTHER_GROUPSHUT); + + ListView_SetImageList(hwndHotkey, hIml, LVSIL_SMALL); + + ListView_SetExtendedListViewStyle(hwndHotkey, LVS_EX_CHECKBOXES|LVS_EX_SUBITEMIMAGES|LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER|LVS_EX_INFOTIP); + + GetClientRect(hwndHotkey, &rc); + colWidth = rc.right - GetSystemMetrics(SM_CXHTHUMB) - 3*GetSystemMetrics(SM_CXSMICON) - 5; + + lvc.mask = LVCF_WIDTH; + lvc.cx = colWidth * 2 / 3; + ListView_InsertColumn(hwndHotkey, COL_NAME, &lvc); + lvc.cx = GetSystemMetrics(SM_CXSMICON); + ListView_InsertColumn(hwndHotkey, COL_TYPE, &lvc); + lvc.cx = colWidth / 3; + ListView_InsertColumn(hwndHotkey, COL_KEY, &lvc); + lvc.cx = GetSystemMetrics(SM_CXSMICON); + ListView_InsertColumn(hwndHotkey, COL_RESET, &lvc); + lvc.cx = GetSystemMetrics(SM_CXSMICON); + ListView_InsertColumn(hwndHotkey, COL_ADDREMOVE, &lvc); + + for (i = 0; i < hotkeys.getCount(); i++) { + THotkeyItem *item = hotkeys[i]; + + item->OptChanged = FALSE; + item->OptDeleted = item->OptNew = FALSE; + item->OptEnabled = item->Enabled; + item->OptHotkey = item->Hotkey; + item->OptType = item->type; + } + + currentLanguage = LOWORD(GetKeyboardLayout(0)); + sttBuildHotkeyList(hwndHotkey, NULL); + SetTimer(hwndDlg, 1024, 1000, NULL); + + initialized = TRUE; + + { /* load group states */ + int count = ListView_GetItemCount(hwndHotkey); + TCHAR buf[128]; + LVITEM lvi = {0}; + lvi.pszText = buf; + lvi.cchTextMax = SIZEOF(buf); + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + char *szSetting; + + lvi.mask = LVIF_PARAM; + lvi.iSubItem = 0; + ListView_GetItem(hwndHotkey, &lvi); + if (lvi.lParam) continue; + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + ListView_GetItem(hwndHotkey, &lvi); + + szSetting = mir_t2a(lvi.pszText); + + ListView_SetCheckState(hwndHotkey, lvi.iItem, + DBGetContactSettingByte(NULL, DBMODULENAME "UI", szSetting, TRUE)); + + mir_free(szSetting); + } + } + + g_hwndOptions = hwndDlg; + + break; + } + + case WM_DESTROY: + { + int count = ListView_GetItemCount(hwndHotkey); + TCHAR buf[128]; + LVITEM lvi = {0}; + + g_hwndOptions = NULL; + + KillTimer(hwndDlg, 1024); + + lvi.pszText = buf; + lvi.cchTextMax = SIZEOF(buf); + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + char *szSetting; + + lvi.mask = LVIF_PARAM; + lvi.iSubItem = 0; + ListView_GetItem(hwndHotkey, &lvi); + if (lvi.lParam) continue; + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + ListView_GetItem(hwndHotkey, &lvi); + + szSetting = mir_t2a(lvi.pszText); + + DBWriteContactSettingByte(NULL, DBMODULENAME "UI", szSetting, + (BYTE) ListView_GetCheckState(hwndHotkey, lvi.iItem)); + + mir_free(szSetting); + } + break; + } + + case WM_TIMER: + { + WORD newLanguage; + int count; + LVITEM lvi = {0}; + + if (!initialized) break; + + newLanguage = LOWORD(GetKeyboardLayout(0)); + if (newLanguage == currentLanguage) break; + + count = ListView_GetItemCount(hwndHotkey); + lvi.mask = LVIF_PARAM; + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + ListView_GetItem(hwndHotkey, &lvi); + if (!lvi.lParam) continue; + + sttOptionsSetupItem(hwndHotkey, lvi.iItem, (THotkeyItem *)lvi.lParam); + } + + currentLanguage = newLanguage; + break; + } + + case WM_HOTKEYUNREGISTERED: + { + int count; + LVITEM lvi = {0}; + + count = ListView_GetItemCount(hwndHotkey); + lvi.mask = LVIF_PARAM; + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + ListView_GetItem(hwndHotkey, &lvi); + if (!lvi.lParam) continue; + + if (((THotkeyItem *)lvi.lParam)->UnregisterHotkey) { + ListView_DeleteItem(hwndHotkey, lvi.iItem); + --lvi.iItem; + --count; + } + } + break; + } + + case WM_DRAWITEM: + { + LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam; + RECT rc = lpdis->rcItem; + int prefix = 65; + int width = (lpdis->rcItem.right - lpdis->rcItem.left - prefix) / 3; + rc.left += 5; + + HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); + + if (lpdis->CtlID == IDC_CANVAS2) { + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Scope:"), &rc); + + rc.left = prefix + width * 0; + ImageList_Draw(hIml, 0, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("System"), &rc); + + rc.left = prefix + width * 1; + ImageList_Draw(hIml, 1, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Miranda"), &rc); + + rc.left = prefix + width * 2; + ImageList_Draw(hIml, 2, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Window"), &rc); + + return TRUE; + } + + if (lpdis->CtlID == IDC_CANVAS) { + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Actions:"), &rc); + rc.left += 10; + + rc.left = prefix + width * 0; + ImageList_Draw(hIml, 5, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Undo"), &rc); + + rc.left = prefix + width * 1; + ImageList_Draw(hIml, 3, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Add binding"), &rc); + + rc.left = prefix + width * 2; + ImageList_Draw(hIml, 4, lpdis->hDC, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Remove"), &rc); + + return TRUE; + } + + break; + } + + case WM_COMMAND: + if (( LOWORD( wParam ) == IDC_HOTKEY) && (( HIWORD( wParam ) == EN_KILLFOCUS) || (HIWORD(wParam) == 0 ))) { + LVITEM lvi; + THotkeyItem *item; + WORD wHotkey = (WORD)SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0); + + ShowWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), SW_HIDE); + SetFocus(hwndHotkey); + if ( !wHotkey || (wHotkey == VK_ESCAPE) || (HIWORD(wParam) != 0 )) + break; + + lvi.mask = LVIF_PARAM; + lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); + if (lvi.iItem >= 0) { + ListView_GetItem(hwndHotkey, &lvi); + if (item = (THotkeyItem *)lvi.lParam) { + item->OptHotkey = wHotkey; + + sttOptionsSetupItem(hwndHotkey, lvi.iItem, item); + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } } } + break; + + case WM_CONTEXTMENU: + if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_LV_HOTKEYS) + { + HWND hwndList = (HWND)wParam; + POINT pt = { (signed short)LOWORD( lParam ), (signed short)HIWORD( lParam ) }; + LVITEM lvi = {0}; + THotkeyItem *item = NULL; + + lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); + if (lvi.iItem < 0) return FALSE; + + lvi.mask = LVIF_PARAM; + ListView_GetItem(hwndList, &lvi); + if (!(item = (THotkeyItem *)lvi.lParam)) return FALSE; + + if (( pt.x == -1 ) && ( pt.y == -1 )) { + RECT rc; + ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL); + pt.x = rc.left; + pt.y = rc.bottom; + ClientToScreen(hwndList, &pt); + } + + { + enum { MI_CANCEL, MI_CHANGE, MI_SYSTEM, MI_LOCAL, MI_ADD, MI_REMOVE, MI_REVERT }; + + MENUITEMINFO mii = {0}; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STATE; + mii.fState = MFS_DEFAULT; + + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)MI_CHANGE, TranslateT("Modify")); + SetMenuItemInfo(hMenu, (UINT_PTR)MI_CHANGE, FALSE, &mii); + if (item->type != HKT_MANUAL) { + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(hMenu, MF_STRING| + ((item->OptType == HKT_GLOBAL) ? MF_CHECKED : 0), + (UINT_PTR)MI_SYSTEM, TranslateT("System scope")); + AppendMenu(hMenu, MF_STRING| + ((item->OptType == HKT_LOCAL) ? MF_CHECKED : 0), + (UINT_PTR)MI_LOCAL, TranslateT("Miranda scope")); + } + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + if (!item->rootHotkey) + AppendMenu(hMenu, MF_STRING, (UINT_PTR)MI_ADD, TranslateT("Add binding")); + else + AppendMenu(hMenu, MF_STRING, (UINT_PTR)MI_REMOVE, TranslateT("Remove")); + if (item->Hotkey != item->OptHotkey) { + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(hMenu, MF_STRING, (UINT_PTR)MI_REVERT, TranslateT("Undo")); + } + + switch (TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL)) { + case MI_CHANGE: + sttOptionsStartEdit(hwndDlg, hwndHotkey); + break; + case MI_SYSTEM: + item->OptType = HKT_GLOBAL; + sttOptionsSetupItem(hwndList, lvi.iItem, item); + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + case MI_LOCAL: + item->OptType = HKT_LOCAL; + sttOptionsSetupItem(hwndList, lvi.iItem, item); + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + case MI_ADD: + initialized = FALSE; + sttOptionsAddHotkey(hwndList, item); + initialized = FALSE; + break; + case MI_REMOVE: + sttOptionsDeleteHotkey(hwndList, lvi.iItem, item); + break; + case MI_REVERT: + item->OptHotkey = item->Hotkey; + sttOptionsSetupItem(hwndList, lvi.iItem, item); + break; + } + DestroyMenu( hMenu ); + } + + break; + } + break; + + case WM_NOTIFY: + { + LPNMHDR lpnmhdr = (LPNMHDR)lParam; + switch (lpnmhdr->idFrom) { + case 0: + { + int i; + + if (( lpnmhdr->code != PSN_APPLY) && (lpnmhdr->code != PSN_RESET )) + break; + + sttUnregisterHotkeys(); + + for (i = 0; i < hotkeys.getCount(); i++) { + THotkeyItem *item = hotkeys[i]; + if (item->OptNew && item->OptDeleted || + item->rootHotkey && !item->OptHotkey || + (lpnmhdr->code == PSN_APPLY) && item->OptDeleted || + (lpnmhdr->code == PSN_RESET) && item->OptNew) + { + sttFreeHotkey(item); + hotkeys.remove( i-- ); + } + } + + if (lpnmhdr->code == PSN_APPLY) { + LVITEM lvi = {0}; + int count = ListView_GetItemCount(hwndHotkey); + + for (i = 0; i < hotkeys.getCount(); i++) + sttOptionsSaveItem(hotkeys[i]); + + lvi.mask = LVIF_IMAGE; + lvi.iSubItem = COL_RESET; + lvi.iImage = -1; + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) + ListView_SetItem(hwndHotkey, &lvi); + } + + sttRegisterHotkeys(); + + NotifyEventHooks( hEvChanged, 0, 0 ); + break; + } + case IDC_LV_HOTKEYS: + switch (lpnmhdr->code) { + case NM_CLICK: + { + THotkeyItem *item = NULL; + LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam; + LVHITTESTINFO lvhti = {0}; + LVITEM lvi = {0}; + + lvi.mask = LVIF_PARAM|LVIF_IMAGE; + lvi.iItem = lpnmia->iItem; + ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi); + item = (THotkeyItem *)lvi.lParam; + + lvhti.pt = lpnmia->ptAction; + lvhti.iItem = lpnmia->iItem; + lvhti.iSubItem = lpnmia->iSubItem; + ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti); + + if (item && + (!item->rootHotkey && (lpnmia->iSubItem == COL_NAME) && ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEMICON) || + item->rootHotkey && (lpnmia->iSubItem == COL_TYPE)) && + ((item->OptType == HKT_GLOBAL) || (item->OptType == HKT_LOCAL))) + { + item->OptType = (item->OptType == HKT_GLOBAL) ? HKT_LOCAL : HKT_GLOBAL; + sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + else if (item && (lpnmia->iSubItem == COL_RESET)) { + item->OptHotkey = item->Hotkey; + sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); + } + else if (item && (lpnmia->iSubItem == COL_ADDREMOVE)) { + if (item->rootHotkey) + sttOptionsDeleteHotkey(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); + else { + initialized = FALSE; + sttOptionsAddHotkey(lpnmia->hdr.hwndFrom, item); + initialized = TRUE; + } + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + break; + } + case LVN_KEYDOWN: + { + LPNMLVKEYDOWN param = (LPNMLVKEYDOWN)lParam; + if ((param->wVKey == VK_SUBTRACT) || (param->wVKey == VK_LEFT) || + (param->wVKey == VK_ADD) || (param->wVKey == VK_RIGHT)) + { + LVITEM lvi = {0}; + lvi.mask = LVIF_PARAM; + lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); + if (lvi.iItem < 0) break; + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + if (lvi.lParam) break; + + if ((param->wVKey == VK_ADD) || (param->wVKey == VK_RIGHT)) + { + ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, TRUE); + } else + // if ((param->wVKey == VK_SUBTRACT) || (param->wVKey == VK_LEFT)) + { + ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, FALSE); + } + } + else if (param->wVKey == VK_F2) + sttOptionsStartEdit(hwndDlg, hwndHotkey); + + break; + } + case LVN_ITEMACTIVATE: + { + LVITEM lvi = {0}; + lvi.mask = LVIF_PARAM; + lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); + if (lvi.iItem < 0) break; + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + + if (lvi.lParam) + sttOptionsStartEdit(hwndDlg, hwndHotkey); + else + ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, !ListView_GetCheckState(lpnmhdr->hwndFrom, lvi.iItem)); + break; + } + case LVN_ITEMCHANGED: + { + LPNMLISTVIEW param = (LPNMLISTVIEW)lParam; + THotkeyItem *item = (THotkeyItem *)param->lParam; + if (!initialized || (param->uNewState>>12 == param->uOldState>>12)) + break; + + if (item && !item->rootHotkey) { + item->OptEnabled = ListView_GetCheckState(lpnmhdr->hwndFrom, param->iItem) ? 1 : 0; + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + else if (!item) { + TCHAR buf[256]; + LVITEM lvi = {0}; + lvi.mask = LVIF_TEXT; + lvi.iItem = param->iItem; + lvi.pszText = buf; + lvi.cchTextMax = SIZEOF(buf); + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + + if (param->uNewState>>12 == 1) { + int count = ListView_GetItemCount(lpnmhdr->hwndFrom); + LVITEM lvi = {0}; + lvi.mask = LVIF_PARAM; + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + THotkeyItem *item; + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + item = (THotkeyItem *)lvi.lParam; + if (!item) continue; + if (!lstrcmp(item->ptszSection_tr, buf)) { + ListView_DeleteItem(lpnmhdr->hwndFrom, lvi.iItem); + --lvi.iItem; + --count; + } } + } + else if (param->uNewState>>12 == 2) { + int i, nItems = ListView_GetItemCount(lpnmhdr->hwndFrom); + initialized = FALSE; + for (i = 0; i < hotkeys.getCount(); ++i) { + LVITEM lvi = {0}; + THotkeyItem *item = hotkeys[i]; + + if (item->OptDeleted) continue; + if (lstrcmp(buf, item->ptszSection_tr)) continue; + + lvi.mask = LVIF_PARAM|LVIF_INDENT; + lvi.iIndent = 1; + lvi.iItem = nItems++; + lvi.lParam = (LPARAM)item; + ListView_InsertItem(lpnmhdr->hwndFrom, &lvi); + sttOptionsSetupItem(lpnmhdr->hwndFrom, nItems-1, item); + } + ListView_SortItemsEx(lpnmhdr->hwndFrom, sttOptionsSortList, (LPARAM)lpnmhdr->hwndFrom); + initialized = TRUE; + } + } + break; + } + case NM_CUSTOMDRAW: + { + NMLVCUSTOMDRAW *param = (NMLVCUSTOMDRAW *) lParam; + switch (param->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + case CDDS_ITEMPREPAINT: + SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW ); + return TRUE; + + case CDDS_SUBITEM|CDDS_ITEMPREPAINT: + { + THotkeyItem *item; + TCHAR buf[256]; + LVITEM lvi = {0}; + lvi.mask = LVIF_TEXT|LVIF_PARAM; + lvi.iItem = param->nmcd.dwItemSpec; + lvi.pszText = buf; + lvi.cchTextMax = SIZEOF(buf); + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + item = (THotkeyItem *)lvi.lParam; + + if (!item) { + RECT rc; + HFONT hfnt; + + ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); + FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(param->nmcd.uItemState&CDIS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); + SetTextColor(param->nmcd.hdc, GetSysColor(param->nmcd.uItemState&CDIS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); + + if (param->iSubItem == 0) { + rc.left += 3; + HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); + ImageList_Draw(hIml, + ListView_GetCheckState(hwndHotkey, lvi.iItem) ? 6 : 7, + param->nmcd.hdc, rc.left, (rc.top+rc.bottom-16)/2, ILD_TRANSPARENT); + rc.left += 18; + hfnt = ( HFONT )SelectObject(param->nmcd.hdc, (HFONT)SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0)); + DrawText(param->nmcd.hdc, buf, -1, &rc, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER); + SelectObject(param->nmcd.hdc, hfnt); + } + + SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT ); + return TRUE; + } + + if (item->rootHotkey && (param->iSubItem == 0)) { + RECT rc; + ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); + FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); + SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT ); + return TRUE; + } + break; + } } + break; + } + break; + } } + break; + } /* case WM_NOTIFY */ + } /* switch */ + + return FALSE; +} + +static int sttOptionsInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {0}; + odp.cbSize = sizeof(odp); + odp.hInstance = hMirandaInst; + odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR; + odp.position = -180000000; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HOTKEYS); + odp.ptszTitle = TranslateT("Hotkeys"); + odp.ptszGroup = TranslateT("Customize"); + odp.pfnDlgProc = sttOptionsDlgProc; + CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp); + return 0; +} + +static int sttModulesLoaded(WPARAM, LPARAM) +{ + HookEvent(ME_OPT_INITIALISE, sttOptionsInit); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Hotkey manager + +static const char* oldSettings[] = { "ShowHide", "ReadMsg", "NetSearch", "ShowOptions" }; +static const char* newSettings[] = { "ShowHide", "ReadMessage", "SearchInWeb", "ShowOptions" }; + +int LoadSkinHotkeys(void) +{ + WNDCLASSEX wcl = {0}; + + bModuleInitialized = TRUE; + + wcl.cbSize = sizeof(wcl); + wcl.lpfnWndProc = sttHotkeyHostWndProc; + wcl.style = 0; + wcl.cbClsExtra = 0; + wcl.cbWndExtra = 0; + wcl.hInstance = hMirandaInst; + wcl.hIcon = NULL; + wcl.hCursor = LoadCursor(NULL, IDC_ARROW); + wcl.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH); + wcl.lpszMenuName = NULL; + wcl.lpszClassName = _T("MirandaHotkeyHostWnd"); + wcl.hIconSm = NULL; + RegisterClassEx(&wcl); + + g_pid = GetCurrentProcessId(); + + g_hwndHotkeyHost = CreateWindow(_T("MirandaHotkeyHostWnd"), NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hMirandaInst, NULL); + SetWindowPos(g_hwndHotkeyHost, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_DEFERERASE|SWP_NOSENDCHANGING|SWP_HIDEWINDOW); + + hhkKeyboard = SetWindowsHookEx(WH_KEYBOARD, sttKeyboardProc, NULL, GetCurrentThreadId()); + + hEvChanged = CreateHookableEvent(ME_HOTKEYS_CHANGED); + + CreateServiceFunction(MS_HOTKEY_SUBCLASS, svcHotkeySubclass); + CreateServiceFunction(MS_HOTKEY_UNSUBCLASS, svcHotkeyUnsubclass); + CreateServiceFunction(MS_HOTKEY_REGISTER, svcHotkeyRegister); + CreateServiceFunction(MS_HOTKEY_UNREGISTER, svcHotkeyUnregister); + CreateServiceFunction(MS_HOTKEY_CHECK, svcHotkeyCheck); + + HookEvent(ME_SYSTEM_MODULESLOADED, sttModulesLoaded); + { + WORD key; + int i; + for ( i = 0; i < SIZEOF( oldSettings ); i++ ) { + char szSetting[ 100 ]; + mir_snprintf( szSetting, SIZEOF(szSetting), "HK%s", oldSettings[i] ); + if (( key = DBGetContactSettingWord( NULL, "Clist", szSetting, 0 ))) { + DBDeleteContactSetting( NULL, "Clist", szSetting ); + DBWriteContactSettingWord( NULL, DBMODULENAME, newSettings[i], key ); + } + + mir_snprintf( szSetting, SIZEOF(szSetting), "HKEn%s", oldSettings[i] ); + if (( key = DBGetContactSettingByte( NULL, "Clist", szSetting, 0 ))) { + DBDeleteContactSetting( NULL, "Clist", szSetting ); + DBWriteContactSettingByte( NULL, DBMODULENAME "Off", newSettings[i], (BYTE)(key == 0) ); + } } } + + return 0; +} + +void UnloadSkinHotkeys(void) +{ + int i; + + if ( !bModuleInitialized ) return; + + DestroyHookableEvent(hEvChanged); + UnhookWindowsHookEx(hhkKeyboard); + sttUnregisterHotkeys(); + DestroyWindow(g_hwndHotkeyHost); + for ( i = 0; i < hotkeys.getCount(); i++ ) + sttFreeHotkey(hotkeys[i]); + hotkeys.destroy(); +} diff --git a/src/modules/skin/skinicons.cpp b/src/modules/skin/skinicons.cpp new file mode 100644 index 0000000000..9bfb0cf655 --- /dev/null +++ b/src/modules/skin/skinicons.cpp @@ -0,0 +1,489 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include + +struct StandardIconDescription +{ + int id; + const char* description; + int resource_id; + int pf2; + const char* section; +}; + +static const struct StandardIconDescription mainIcons[] = +{ + { SKINICON_OTHER_MIRANDA, LPGEN("Miranda IM"), -IDI_MIRANDA }, + { SKINICON_EVENT_MESSAGE, LPGEN("Message"), -IDI_RECVMSG }, + { SKINICON_EVENT_URL, LPGEN("URL"), -IDI_URL }, + { SKINICON_EVENT_FILE, LPGEN("File"), -IDI_FILE }, + { SKINICON_OTHER_USERONLINE, LPGEN("User Online"), -IDI_USERONLINE }, + { SKINICON_OTHER_GROUPOPEN, LPGEN("Group (Open)"), -IDI_GROUPOPEN }, + { SKINICON_OTHER_GROUPSHUT, LPGEN("Group (Closed)"), -IDI_GROUPSHUT }, + { SKINICON_OTHER_CONNECTING, LPGEN("Connecting"), -IDI_LOAD }, + { SKINICON_OTHER_ADDCONTACT, LPGEN("Add Contact"), -IDI_ADDCONTACT }, + { SKINICON_OTHER_USERDETAILS, LPGEN("User Details"), -IDI_USERDETAILS }, + { SKINICON_OTHER_HISTORY, LPGEN("History"), -IDI_HISTORY }, + { SKINICON_OTHER_DOWNARROW, LPGEN("Down Arrow"), -IDI_DOWNARROW }, + { SKINICON_OTHER_FINDUSER, LPGEN("Find User"), -IDI_FINDUSER }, + { SKINICON_OTHER_OPTIONS, LPGEN("Options"), -IDI_OPTIONS }, + { SKINICON_OTHER_SENDEMAIL, LPGEN("Send E-mail"), -IDI_SENDEMAIL }, + { SKINICON_OTHER_DELETE, LPGEN("Delete"), -IDI_DELETE }, + { SKINICON_OTHER_RENAME, LPGEN("Rename"), -IDI_RENAME }, + { SKINICON_OTHER_SMS, LPGEN("SMS"), -IDI_SMS }, + { SKINICON_OTHER_SEARCHALL, LPGEN("Search All"), -IDI_SEARCHALL }, + { SKINICON_OTHER_TICK, LPGEN("Tick"), -IDI_TICK }, + { SKINICON_OTHER_NOTICK, LPGEN("No Tick"), -IDI_NOTICK }, + { SKINICON_OTHER_HELP, LPGEN("Help"), -IDI_HELP }, + { SKINICON_OTHER_MIRANDAWEB, LPGEN("Miranda Website"), -IDI_MIRANDAWEBSITE }, + { SKINICON_OTHER_TYPING, LPGEN("Typing"), -IDI_TYPING }, + { SKINICON_OTHER_SMALLDOT, LPGEN("Small Dot"), -IDI_SMALLDOT }, + { SKINICON_OTHER_FILLEDBLOB, LPGEN("Filled Blob"), -IDI_FILLEDBLOB }, + { SKINICON_OTHER_EMPTYBLOB, LPGEN("Empty Blob"), -IDI_EMPTYBLOB }, + { SKINICON_OTHER_UNICODE, LPGEN("Unicode plugin"), -IDI_UNICODE }, + { SKINICON_OTHER_ANSI, LPGEN("ANSI plugin"), -IDI_ANSI }, + { SKINICON_OTHER_LOADED, LPGEN("Running plugin"), -IDI_LOADED }, + { SKINICON_OTHER_NOTLOADED, LPGEN("Unloaded plugin"), -IDI_NOTLOADED }, + { SKINICON_OTHER_UNDO, LPGEN("Undo"), -IDI_UNDO }, + { SKINICON_OTHER_WINDOW, LPGEN("Window"), -IDI_WINDOW }, + { SKINICON_OTHER_WINDOWS, LPGEN("System"), -IDI_WINDOWS }, + { SKINICON_OTHER_ACCMGR, LPGEN("Accounts"), -IDI_ACCMGR }, + { SKINICON_OTHER_SHOWHIDE, LPGEN("ShowHide"), -IDI_SHOWHIDE }, + { SKINICON_OTHER_EXIT, LPGEN("Exit"), -IDI_EXIT }, + { SKINICON_OTHER_MAINMENU, LPGEN("Main Menu"), -IDI_MIRANDA }, + { SKINICON_OTHER_STATUS, LPGEN("Status"), -IDI_ONLINE }, + { SKINICON_CHAT_JOIN, LPGEN("Join chat"), -IDI_JOINCHAT }, + { SKINICON_CHAT_LEAVE, LPGEN("Leave chat"), -IDI_LEAVECHAT }, + { SKINICON_OTHER_GROUP, LPGEN("Move to Group"), -IDI_MOVETOGROUP }, + { SKINICON_OTHER_ON, LPGEN("On"), -IDI_ON }, + { SKINICON_OTHER_OFF, LPGEN("Off"), -IDI_OFF }, + { SKINICON_OTHER_STATUS_LOCKED, LPGEN("Locked status"), -IDI_STATUS_LOCKED, 0, "Status Icons" }, +}; + +HANDLE hMainIcons[SIZEOF(mainIcons)]; + +static const struct StandardIconDescription statusIcons[] = +{ + { ID_STATUS_OFFLINE, LPGEN("Offline"), -IDI_OFFLINE, 0xFFFFFFFF }, + { ID_STATUS_ONLINE, LPGEN("Online"), -IDI_ONLINE, PF2_ONLINE }, + { ID_STATUS_AWAY, LPGEN("Away"), -IDI_AWAY, PF2_SHORTAWAY }, + { ID_STATUS_NA, LPGEN("NA"), -IDI_NA, PF2_LONGAWAY }, + { ID_STATUS_OCCUPIED, LPGEN("Occupied"), -IDI_OCCUPIED, PF2_LIGHTDND }, + { ID_STATUS_DND, LPGEN("DND"), -IDI_DND, PF2_HEAVYDND }, + { ID_STATUS_FREECHAT, LPGEN("Free for chat"), -IDI_FREE4CHAT, PF2_FREECHAT }, + { ID_STATUS_INVISIBLE, LPGEN("Invisible"), -IDI_INVISIBLE, PF2_INVISIBLE }, + { ID_STATUS_ONTHEPHONE, LPGEN("On the phone"), -IDI_ONTHEPHONE, PF2_ONTHEPHONE }, + { ID_STATUS_OUTTOLUNCH, LPGEN("Out to lunch"), -IDI_OUTTOLUNCH, PF2_OUTTOLUNCH } +}; + +HANDLE hStatusIcons[SIZEOF(statusIcons)]; + +const char* mainIconsFmt = "core_main_"; +const char* statusIconsFmt = "core_status_"; +const char* protoIconsFmt = LPGEN("%s Icons"); + +#define PROTOCOLS_PREFIX "Status Icons/" +#define GLOBAL_PROTO_NAME "*" + + + + +// load small icon (shared) it's not need to be destroyed + +static HICON LoadSmallIconShared(HINSTANCE hInstance, LPCTSTR lpIconName) +{ + int cx = GetSystemMetrics(SM_CXSMICON); + return ( HICON )LoadImage( hInstance, lpIconName, IMAGE_ICON,cx, cx, LR_DEFAULTCOLOR | LR_SHARED ); +} + +// load small icon (not shared) it IS NEED to be destroyed +static HICON LoadSmallIcon(HINSTANCE hInstance, LPCTSTR lpIconName) +{ + HICON hIcon = NULL; // icon handle + int index = -( int )lpIconName; + TCHAR filename[MAX_PATH] = {0}; + GetModuleFileName( hInstance, filename, MAX_PATH ); + ExtractIconEx( filename, index, NULL, &hIcon, 1 ); + return hIcon; +} + +// load small icon from hInstance +HICON LoadIconEx(HINSTANCE hInstance, LPCTSTR lpIconName, BOOL bShared) +{ + HICON hResIcon = bShared ? LoadSmallIcon(hInstance,lpIconName) : LoadSmallIconShared(hInstance,lpIconName); + if ( !hResIcon ) { //Icon not found in hInstance lets try to load it from core + HINSTANCE hCoreInstance=hMirandaInst; + if ( hCoreInstance != hInstance ) + hResIcon = bShared ? LoadSmallIcon(hCoreInstance,lpIconName) : LoadSmallIconShared(hCoreInstance,lpIconName); + } + return hResIcon; +} + +int ImageList_AddIcon_NotShared(HIMAGELIST hIml, LPCTSTR szResource) +{ + HICON hTempIcon=LoadIconEx( hMirandaInst, szResource, 0); + int res = ImageList_AddIcon(hIml, hTempIcon); + Safe_DestroyIcon(hTempIcon); + return res; +} + +int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId) +{ + HICON hIcon = LoadSkinIcon( iconId ); + int res = ImageList_AddIcon(hIml, hIcon); + IconLib_ReleaseIcon(hIcon,0); + return res; +} + +int ImageList_AddIcon_ProtoIconLibLoaded(HIMAGELIST hIml, const char* szProto, int iconId) +{ + HICON hIcon = LoadSkinProtoIcon( szProto, iconId ); + int res = ImageList_AddIcon(hIml, hIcon); + IconLib_ReleaseIcon(hIcon,0); + return res; +} + +int ImageList_ReplaceIcon_NotShared(HIMAGELIST hIml, int iIndex, HINSTANCE hInstance, LPCTSTR szResource) +{ + HICON hTempIcon = LoadIconEx(hInstance, szResource, 0); + int res = ImageList_ReplaceIcon(hIml, iIndex, hTempIcon); + Safe_DestroyIcon(hTempIcon); + return res; +} + +int ImageList_ReplaceIcon_IconLibLoaded(HIMAGELIST hIml, int nIndex, HICON hIcon) +{ + int res = ImageList_ReplaceIcon(hIml,nIndex, hIcon); + IconLib_ReleaseIcon(hIcon,0); + return res; +} + +void Window_SetIcon_IcoLib(HWND hWnd, int iconId) +{ + SendMessage(hWnd, WM_SETICON, ICON_BIG, ( LPARAM )LoadSkinIcon( iconId, true )); + SendMessage(hWnd, WM_SETICON, ICON_SMALL, ( LPARAM )LoadSkinIcon( iconId )); +} + +void Window_SetProtoIcon_IcoLib(HWND hWnd, const char* szProto, int iconId) +{ + SendMessage(hWnd, WM_SETICON, ICON_BIG, ( LPARAM )LoadSkinProtoIcon( szProto, iconId, true )); + SendMessage(hWnd, WM_SETICON, ICON_SMALL, ( LPARAM )LoadSkinProtoIcon( szProto, iconId )); +} + +void Window_FreeIcon_IcoLib(HWND hWnd) +{ + IconLib_ReleaseIcon(( HICON )SendMessage(hWnd, WM_SETICON, ICON_BIG, 0), NULL); + IconLib_ReleaseIcon(( HICON )SendMessage(hWnd, WM_SETICON, ICON_SMALL, 0), NULL); +} + +void Button_SetIcon_IcoLib(HWND hwndDlg, int itemId, int iconId, const char* tooltip) +{ + HWND hWnd = GetDlgItem( hwndDlg, itemId ); + SendMessage( hWnd, BM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadSkinIcon( iconId )); + SendMessage( hWnd, BUTTONSETASFLATBTN, 0, 0 ); + SendMessage( hWnd, BUTTONADDTOOLTIP, (WPARAM)tooltip, 0); +} + +void Button_FreeIcon_IcoLib(HWND hwndDlg, int itemId) +{ + HICON hIcon = ( HICON )SendDlgItemMessage(hwndDlg, itemId, BM_SETIMAGE, IMAGE_ICON, 0 ); + IconLib_ReleaseIcon(hIcon,0); +} + +// +// wParam = szProto +// lParam = status +// +HICON LoadSkinProtoIcon( const char* szProto, int status, bool big ) +{ + int i, statusIndx = -1; + char iconName[MAX_PATH]; + HICON hIcon; + DWORD caps2 = ( szProto == NULL ) ? ( DWORD )-1 : CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_2,0); + + if ( status >= ID_STATUS_CONNECTING && status < ID_STATUS_CONNECTING+MAX_CONNECT_RETRIES ) { + mir_snprintf( iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, 7 ); + return IcoLib_GetIcon( iconName, big ); + } + + for ( i = 0; i < SIZEOF(statusIcons); i++ ) { + if ( statusIcons[i].id == status ) { + statusIndx = i; + break; + } } + + if ( statusIndx == -1 ) + return NULL; + + if ( !szProto ) { + // Only return a protocol specific icon if there is only one protocol + // Otherwise return the global icon. This affects the global status menu mainly. + if ( accounts.getCount() == 1 ) { + HICON hIcon; + + // format: core_status_%proto%statusindex + mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx); + + hIcon = IcoLib_GetIcon( iconName, big ); + if ( hIcon ) + return hIcon; + } + + // format: core_status_%s%d + mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, statusIndx); + return IcoLib_GetIcon( iconName, big ); + } + + // format: core_status_%s%d + mir_snprintf(iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx); + hIcon = IcoLib_GetIcon( iconName, big ); + if ( hIcon == NULL && ( caps2 == 0 || ( caps2 & statusIcons[statusIndx].pf2 ))) { + PROTOACCOUNT* pa = Proto_GetAccount( szProto ); + if ( pa ) { + TCHAR szPath[MAX_PATH], szFullPath[MAX_PATH], *str; + SKINICONDESC sid = { 0 }; + + // + // Queried protocol isn't in list, adding + // + TCHAR tszSection[MAX_PATH]; + mir_sntprintf( tszSection, SIZEOF(tszSection), _T("%s%s"), _T(PROTOCOLS_PREFIX), pa->tszAccountName ); + sid.ptszSection = tszSection; + + sid.cbSize = sizeof(sid); + sid.flags = SIDF_ALL_TCHAR; + + GetModuleFileName( hMirandaInst, szPath, MAX_PATH ); + str = _tcsrchr( szPath, '\\' ); + if ( str != NULL ) + *str = 0; + mir_sntprintf( szFullPath, SIZEOF(szFullPath), _T("%s\\Icons\\proto_") _T(TCHAR_STR_PARAM) _T(".dll"), szPath, pa->szProtoName ); + if ( GetFileAttributes( szFullPath ) != INVALID_FILE_ATTRIBUTES ) + sid.ptszDefaultFile = szFullPath; + else { + mir_sntprintf( szFullPath, SIZEOF(szFullPath), _T("%s\\Plugins\\") _T(TCHAR_STR_PARAM) _T(".dll"), szPath, szProto ); + if (( int )ExtractIconEx( szFullPath, statusIcons[i].resource_id, NULL, &hIcon, 1 ) > 0 ) { + DestroyIcon( hIcon ); + sid.ptszDefaultFile = szFullPath; + hIcon = NULL; + } + + if ( sid.pszDefaultFile == NULL ) { + if ( str != NULL ) + *str = '\\'; + sid.ptszDefaultFile = szPath; + } } + + // + // Add global icons to list + // + { + int lowidx, highidx; + if ( caps2 == 0 ) + lowidx = statusIndx, highidx = statusIndx+1; + else + lowidx = 0, highidx = SIZEOF(statusIcons); + + for ( i = lowidx; i < highidx; i++ ) { + if ( caps2 == 0 || ( caps2 & statusIcons[i].pf2 )) { + // format: core_%s%d + mir_snprintf( iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, i ); + sid.pszName = iconName; + sid.ptszDescription = cli.pfnGetStatusModeDescription( statusIcons[i].id, 0 ); + sid.iDefaultIndex = statusIcons[i].resource_id; + IcoLib_AddNewIcon( &sid ); + } } } } + + // format: core_status_%s%d + mir_snprintf( iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, szProto, statusIndx ); + hIcon = IcoLib_GetIcon( iconName, big ); + if ( hIcon ) + return hIcon; + } + + if ( hIcon == NULL ) { + mir_snprintf( iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, statusIndx ); + hIcon = IcoLib_GetIcon( iconName, big ); + } + + return hIcon; +} + +HANDLE GetSkinIconHandle( int idx ) +{ + int i; + for ( i = 0; i < SIZEOF(mainIcons); i++ ) + if ( idx == mainIcons[i].id ) + return hMainIcons[i]; + + return NULL; +} + +HICON LoadSkinIcon( int idx, bool big ) +{ + // + // Query for global status icons + // + if ( idx < SKINICON_EVENT_MESSAGE ) { + if ( idx >= SIZEOF( statusIcons )) + return NULL; + + return LoadSkinProtoIcon( NULL, statusIcons[ idx ].id, big ); + } + + return IcoLib_GetIconByHandle( GetSkinIconHandle( idx ), big ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Initializes the icon skin module + +static void convertOneProtocol( char* moduleName, char* iconName ) +{ + char* pm = moduleName + strlen( moduleName ); + char* pi = iconName + strlen( iconName ); + DBVARIANT dbv; + int i; + + for ( i = 0; i < SIZEOF(statusIcons); i++ ) { + _itoa( statusIcons[i].id, pm, 10 ); + + if ( !DBGetContactSettingTString( NULL, "Icons", moduleName, &dbv ) ) { + _itoa( i, pi, 10 ); + + DBWriteContactSettingTString( NULL, "SkinIcons", iconName, dbv.ptszVal ); + DBFreeVariant( &dbv ); + + DBDeleteContactSetting( NULL, "Icons", moduleName ); +} } } + +static INT_PTR sttLoadSkinIcon( WPARAM wParam, LPARAM lParam ) +{ + switch (lParam) + { + case 0: + return (INT_PTR)LoadSkinIcon( wParam ); + + case 1: + return (INT_PTR)GetSkinIconHandle( wParam ); + + case 2: + return (INT_PTR)LoadSkinIcon( wParam, true ); + } + + return 0; +} + +static INT_PTR sttLoadSkinProtoIcon( WPARAM wParam, LPARAM lParam ) +{ + return (INT_PTR)LoadSkinProtoIcon( (char*)wParam, (int)lParam, false ); +} + +static INT_PTR sttLoadSkinProtoIconBig( WPARAM wParam, LPARAM lParam ) +{ + return (INT_PTR)LoadSkinProtoIcon( (char*)wParam, (int)lParam, true ); +} + +int LoadSkinIcons(void) +{ + SKINICONDESC sid; + int i, j = 0; + char iconName[MAX_PATH], moduleName[MAX_PATH]; + TCHAR modulePath[MAX_PATH]; + DBVARIANT dbv; + + // + // Perform "1st-time running import" + + for ( i = 0; i < SIZEOF(mainIcons); i++ ) { + _itoa( mainIcons[i].id, moduleName, 10 ); + if ( DBGetContactSettingTString( NULL, "Icons", moduleName, &dbv )) + break; + + mir_snprintf( iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, i ); + + DBWriteContactSettingTString( NULL, "SkinIcons", iconName, dbv.ptszVal ); + DBFreeVariant( &dbv ); + + DBDeleteContactSetting( NULL, "Icons", moduleName ); + } + + for ( ;; ) { + // get the next protocol name + moduleName[0] = 'p'; + moduleName[1] = 0; + _itoa( j++, moduleName+1, 100 ); + if ( DBGetContactSettingTString( NULL, "Icons", moduleName, &dbv )) + break; + + DBDeleteContactSetting( NULL, "Icons", moduleName ); + + // make old skinicons' prefix + mir_snprintf( moduleName, SIZEOF(moduleName), TCHAR_STR_PARAM, dbv.ptszVal ); + // make IcoLib's prefix + mir_snprintf( iconName, SIZEOF(iconName), "%s" TCHAR_STR_PARAM, statusIconsFmt, dbv.ptszVal ); + + convertOneProtocol( moduleName, iconName ); + DBFreeVariant( &dbv ); + } + moduleName[0] = 0; + strcpy(iconName, "core_status_" GLOBAL_PROTO_NAME); + convertOneProtocol( moduleName, iconName ); + + CreateServiceFunction( MS_SKIN_LOADICON, sttLoadSkinIcon ); + CreateServiceFunction( MS_SKIN_LOADPROTOICON, sttLoadSkinProtoIcon ); + CreateServiceFunction( MS_SKIN_LOADPROTOICONBIG, sttLoadSkinProtoIconBig ); + + ZeroMemory( &sid, sizeof(sid) ); + sid.cbSize = sizeof(sid); + GetModuleFileName(NULL, modulePath, SIZEOF(modulePath)); + sid.ptszDefaultFile = modulePath; + sid.flags = SIDF_PATH_TCHAR; + sid.pszName = iconName; + + // + // Add main icons to list + // + for ( i = 0; i < SIZEOF(mainIcons); i++ ) { + mir_snprintf( iconName, SIZEOF(iconName), "%s%d", mainIconsFmt, i ); + sid.pszSection = mainIcons[i].section == NULL ? "Main Icons" : (char*)mainIcons[i].section; + sid.pszDescription = (char*)mainIcons[i].description; + sid.iDefaultIndex = mainIcons[i].resource_id; + hMainIcons[i] = IcoLib_AddNewIcon( &sid ); + } + // + // Add global icons to list + // + sid.pszSection = PROTOCOLS_PREFIX "Global"; + // + // Asterisk is used, to avoid conflict with proto-plugins + // 'coz users can't rename it to name with '*' + for ( i = 0; i < SIZEOF(statusIcons); i++ ) { + mir_snprintf( iconName, SIZEOF(iconName), "%s%s%d", statusIconsFmt, GLOBAL_PROTO_NAME, i ); + sid.pszName = iconName; + sid.pszDescription = (char*)statusIcons[i].description; + sid.iDefaultIndex = statusIcons[i].resource_id; + hStatusIcons[i] = IcoLib_AddNewIcon( &sid ); + } + return 0; +} diff --git a/src/modules/skin/sounds.cpp b/src/modules/skin/sounds.cpp new file mode 100644 index 0000000000..734c319ac9 --- /dev/null +++ b/src/modules/skin/sounds.cpp @@ -0,0 +1,469 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2009 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" + +struct SoundItem { + char* name; + TCHAR* section; + TCHAR* description; + char* tempFile; +}; + +static BOOL bModuleInitialized = FALSE; +static struct SoundItem *soundList = NULL; +static int soundCount; +static HANDLE hPlayEvent = NULL; + +static INT_PTR ServiceSkinAddNewSound(WPARAM, LPARAM lParam) +{ + SKINSOUNDDESCEX *ssd = ( SKINSOUNDDESCEX* )lParam; + switch( ssd->cbSize ) { + case sizeof( SKINSOUNDDESCEX ): + case SKINSOUNDDESC_SIZE_V1: + case SKINSOUNDDESC_SIZE_V2: + break; + + default: + return 1; + } + + if ( ssd->pszName == NULL || ssd->pszDescription == NULL) + return 1; + + DBVARIANT dbv; + DWORD dwFlags = ( ssd->cbSize == sizeof(SKINSOUNDDESCEX)) ? ssd->dwFlags : 0; + + soundList=(struct SoundItem*)mir_realloc(soundList,sizeof(struct SoundItem)*(soundCount+1)); + SoundItem* item = &soundList[soundCount++]; + item->name = mir_strdup( ssd->pszName ); + item->tempFile = NULL; + #if defined( _UNICODE ) + TCHAR* ptszDefaultFile; + if ( dwFlags & SSDF_UNICODE ) { + item->description = mir_tstrdup( TranslateTS( ssd->ptszDescription )); + item->section = mir_tstrdup( TranslateTS( ssd->cbSize != SKINSOUNDDESC_SIZE_V1 && ssd->pszSection != NULL ? ssd->ptszSection : _T("Other"))); + ptszDefaultFile = mir_tstrdup( ssd->ptszDefaultFile ); + } + else { + item->description = LangPackPcharToTchar( ssd->pszDescription ); + item->section = LangPackPcharToTchar( ssd->cbSize != SKINSOUNDDESC_SIZE_V1 && ssd->pszSection != NULL ? ssd->pszSection : "Other" ); + ptszDefaultFile = mir_a2t( ssd->pszDefaultFile ); + } + + if ( ptszDefaultFile ) { + if ( DBGetContactSettingString(NULL, "SkinSounds", item->name, &dbv)) + DBWriteContactSettingTString(NULL, "SkinSounds", item->name, ptszDefaultFile); + else + DBFreeVariant(&dbv); + mir_free( ptszDefaultFile ); + } + #else + item->description = mir_tstrdup( TranslateTS( ssd->pszDescription )); + item->section = mir_tstrdup( TranslateTS( ssd->cbSize != SKINSOUNDDESC_SIZE_V1 && ssd->pszSection != NULL ? ssd->pszSection : "Other" )); + if ( ssd->pszDefaultFile ) { + if ( DBGetContactSettingString(NULL, "SkinSounds", item->name, &dbv)) + DBWriteContactSettingString(NULL, "SkinSounds", item->name, ssd->pszDefaultFile); + else + DBFreeVariant(&dbv); + } + #endif + return 0; +} + +static int SkinPlaySoundDefault(WPARAM wParam, LPARAM lParam) +{ + char * pszFile = (char *) lParam; + if ( pszFile && (DBGetContactSettingByte(NULL,"Skin","UseSound",0) || (int)wParam==1)) + PlaySoundA(pszFile, NULL, SND_ASYNC | SND_FILENAME | SND_NOWAIT); + + return 0; +} + +static INT_PTR ServiceSkinPlaySound(WPARAM, LPARAM lParam) +{ + char* pszSoundName = ( char* )lParam; + int j; + + for (j=0; jidFrom) { + case 0: + if (((LPNMHDR)lParam)->code == PSN_APPLY) + { + int i; + + DBWriteContactSettingByte(NULL, "Skin", "UseSound", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ENABLESOUNDS)); + for ( i=0; i < soundCount; i++ ) + if ( soundList[i].tempFile ) + DBWriteContactSettingString(NULL,"SkinSounds",soundList[i].name,soundList[i].tempFile); + { + TVITEM tvi,tvic; + tvi.hItem = TreeView_GetRoot(hwndTree); + while ( tvi.hItem != NULL ) { + tvi.mask = TVIF_PARAM | TVIF_HANDLE | TVIF_STATE; + TreeView_GetItem(hwndTree, &tvi); + if ( tvi.lParam == -1 ) { + tvic.hItem = TreeView_GetChild(hwndTree, tvi.hItem); + while ( tvic.hItem != NULL ) { + tvic.mask = TVIF_PARAM | TVIF_HANDLE | TVIF_STATE; + TreeView_GetItem(hwndTree, &tvic); + if ((( tvic.state & TVIS_STATEIMAGEMASK ) >> 12 == 2 )) { + DBCONTACTGETSETTING cgs; + cgs.szModule = "SkinSoundsOff"; + cgs.szSetting = soundList[tvic.lParam].name; + CallService(MS_DB_CONTACT_DELETESETTING,(WPARAM)(HANDLE)NULL,(LPARAM)&cgs); + } + else DBWriteContactSettingByte(NULL,"SkinSoundsOff",soundList[tvic.lParam].name,1); + tvic.hItem=TreeView_GetNextSibling(hwndTree,tvic.hItem); + } } + + tvi.hItem=TreeView_GetNextSibling(hwndTree,tvi.hItem); + } } + return TRUE; + } + break; + case IDC_SOUNDTREE: + switch(((NMHDR*)lParam)->code) { + case TVN_SELCHANGEDA: + { + NMTREEVIEW *pnmtv = (NMTREEVIEW*)lParam; + TVITEM tvi = pnmtv->itemNew; + + if (tvi.lParam==-1) { + SendMessage(hwndDlg, DM_HIDEPANE, 0, 0); + } + else { + TCHAR buf[256]; + DBVARIANT dbv; + + mir_sntprintf(buf, SIZEOF(buf), _T("%s: %s"), soundList[tvi.lParam].section, soundList[tvi.lParam].description); + SetDlgItemText(hwndDlg, IDC_NAMEVAL, buf); + if (soundList[tvi.lParam].tempFile) + SetDlgItemTextA(hwndDlg, IDC_LOCATION, soundList[tvi.lParam].tempFile); + else if(!DBGetContactSettingString(NULL,"SkinSounds",soundList[tvi.lParam].name,&dbv)) { + SetDlgItemTextA(hwndDlg, IDC_LOCATION, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemText(hwndDlg, IDC_LOCATION, TranslateT("")); + SendMessage(hwndDlg, DM_SHOWPANE, 0, 0); + } + } + break; + case TVN_KEYDOWN: + { + NMTVKEYDOWN* ptkd = (NMTVKEYDOWN*)lParam; + + if (ptkd&&ptkd->wVKey==VK_SPACE&&TreeView_GetSelection(ptkd->hdr.hwndFrom)) + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + break; + case NM_CLICK: + { + TVHITTESTINFO hti; + hti.pt.x=(short)LOWORD(GetMessagePos()); + hti.pt.y=(short)HIWORD(GetMessagePos()); + ScreenToClient(((LPNMHDR)lParam)->hwndFrom,&hti.pt); + if(TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom,&hti)) + if (hti.flags&TVHT_ONITEM) + if(hti.flags&TVHT_ONITEMSTATEICON) + if (TreeView_GetParent(hwndTree, hti.hItem)!=NULL) + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } } + break; + } + break; + } + return FALSE; +} + +static UINT iconsExpertOnlyControls[]={IDC_IMPORT}; + +static int SkinOptionsInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = { 0 }; + odp.cbSize = sizeof(odp); + odp.position = -200000000; + odp.hInstance = hMirandaInst; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SOUND); + odp.pszGroup = LPGEN("Customize"); + odp.pszTitle = LPGEN("Sounds"); + odp.pfnDlgProc = DlgProcSoundOpts; + odp.flags = ODPF_BOLDGROUPS; + CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp ); + return 0; +} + +static int SkinSystemModulesLoaded(WPARAM, LPARAM) +{ + HookEvent(ME_OPT_INITIALISE,SkinOptionsInit); + return 0; +} + +int LoadSkinSounds(void) +{ + bModuleInitialized = TRUE; + + soundList=NULL; + soundCount=0; + CreateServiceFunction(MS_SKIN_ADDNEWSOUND,ServiceSkinAddNewSound); + CreateServiceFunction(MS_SKIN_PLAYSOUND,ServiceSkinPlaySound); + HookEvent(ME_SYSTEM_MODULESLOADED,SkinSystemModulesLoaded); + hPlayEvent=CreateHookableEvent(ME_SKIN_PLAYINGSOUND); + SetHookDefaultForHookableEvent(hPlayEvent, SkinPlaySoundDefault); + return 0; +} + +void UnloadSkinSounds(void) +{ + int i; + + if ( !bModuleInitialized ) return; + + for(i=0;i