/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2010 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 "..\..\core\commonheaders.h" #include "filter.h" #define OPENOPTIONSDIALOG_OLD_SIZE 12 #define FILTER_TIMEOUT_TIMER 10012 #define ALL_MODULES_FILTER _T("") #define CORE_MODULES_FILTER _T("") static HANDLE hOptionsInitEvent; static HWND hwndOptions = NULL; static HWND hFilterSearchWnd = NULL; // Thread for search keywords in dialogs static BYTE bSearchState = 0; // 0 - not executed; 1 - in progress; 2 - completed; static int FilterPage = 0; static int FilterLoadProgress = 100; static int FilterTimerId = 0; struct OptionsPageInit { int pageCount; OPTIONSDIALOGPAGE *odp; }; struct DlgTemplateExBegin { WORD dlgVer; WORD signature; DWORD helpID; DWORD exStyle; DWORD style; WORD cDlgItems; short x; short y; short cx; short cy; }; struct OptionsPageData { DLGTEMPLATE *pTemplate; DLGPROC dlgProc; HINSTANCE hInst; HTREEITEM hTreeItem; HWND hwnd; int changed; int simpleHeight, expertHeight; int simpleWidth, expertWidth; int simpleBottomControlId, simpleRightControlId; int nExpertOnlyControls; UINT *expertOnlyControls; DWORD flags; TCHAR *ptszTitle, *ptszGroup, *ptszTab; int hLangpack; BOOL insideTab; LPARAM dwInitParam; int offsetX; int offsetY; }; struct OptionsDlgData { OptionsDlgData() : arOpd(10) {} int currentPage; HTREEITEM hCurrentPage; LIST arOpd; RECT rcDisplay; RECT rcTab; HFONT hBoldFont; TCHAR szFilterString[1024]; HANDLE hPluginLoad, hPluginUnload; OptionsPageData* getCurrent() const { return (currentPage == -1) ? NULL : arOpd[currentPage]; } }; HTREEITEM FindNamedTreeItemAtRoot(HWND hwndTree, const TCHAR* name) { TVITEM tvi; TCHAR str[128]; tvi.mask = TVIF_TEXT; tvi.pszText = str; tvi.cchTextMax = SIZEOF(str); tvi.hItem = TreeView_GetRoot(hwndTree); while (tvi.hItem != NULL) { SendMessage(hwndTree, TVM_GETITEM, 0, (LPARAM)&tvi); if ( !_tcsicmp(str, name)) return tvi.hItem; tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem); } return NULL; } static HTREEITEM FindNamedTreeItemAtChildren(HWND hwndTree, HTREEITEM hItem, const TCHAR* name) { TCHAR str[128]; TVITEM tvi; tvi.mask = TVIF_TEXT; tvi.pszText = str; tvi.cchTextMax = SIZEOF(str); tvi.hItem = TreeView_GetChild(hwndTree, hItem); while (tvi.hItem != NULL) { SendMessage(hwndTree, TVM_GETITEM, 0, (LPARAM)&tvi); if ( !_tcsicmp(str, name)) return tvi.hItem; tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem); } return NULL; } static BOOL CALLBACK BoldGroupTitlesEnumChildren(HWND hwnd, LPARAM lParam) { TCHAR szClass[64]; GetClassName(hwnd, szClass, SIZEOF(szClass)); if ( !lstrcmp(szClass, _T("Button")) && (GetWindowLongPtr(hwnd, GWL_STYLE)&0x0F) == BS_GROUPBOX) SendMessage(hwnd, WM_SETFONT, lParam, 0); return TRUE; } struct MoveChildParam { HWND hDlg; POINT offset; }; static BOOL CALLBACK MoveEnumChildren(HWND hwnd, LPARAM lParam) { struct MoveChildParam * param = (struct MoveChildParam *) lParam; RECT rcWnd; GetWindowRect(hwnd, &rcWnd); HWND hwndParent = GetParent(hwnd); if (hwndParent != param->hDlg) return TRUE; // Do not move subchilds POINT pt = { 0, 0 }; ClientToScreen(hwndParent, &pt); OffsetRect(&rcWnd, -pt.x, -pt.y); SetWindowPos(hwnd, NULL, rcWnd.left + param->offset.x, rcWnd.top + param->offset.y, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); return TRUE; } #define OPTSTATE_PREFIX "s_" static void SaveOptionsTreeState(HWND hdlg) { TVITEMA tvi; char buf[130], str[128]; tvi.mask = TVIF_TEXT | TVIF_STATE; tvi.pszText = str; tvi.cchTextMax = SIZEOF(str); tvi.hItem = TreeView_GetRoot(GetDlgItem(hdlg, IDC_PAGETREE)); while (tvi.hItem != NULL) { if (SendMessageA(GetDlgItem(hdlg, IDC_PAGETREE), TVM_GETITEMA, 0, (LPARAM)&tvi)) { mir_snprintf(buf, SIZEOF(buf), "%s%s", OPTSTATE_PREFIX, str); DBWriteContactSettingByte(NULL, "Options", buf, (BYTE)((tvi.state&TVIS_EXPANDED)?1:0)); } tvi.hItem = TreeView_GetNextSibling(GetDlgItem(hdlg, IDC_PAGETREE), tvi.hItem); } } #define DM_FOCUSPAGE (WM_USER+10) #define DM_REBUILDPAGETREE (WM_USER+11) #define HM_MODULELOAD (WM_USER+12) #define HM_MODULEUNLOAD (WM_USER+13) static void ThemeDialogBackground(HWND hwnd, BOOL tabbed) { if (enableThemeDialogTexture) enableThemeDialogTexture(hwnd, (tabbed ? ETDT_ENABLE : ETDT_DISABLE) | ETDT_USETABTEXTURE); } static int lstrcmpnull(TCHAR *str1, TCHAR *str2) { if (str1 == NULL && str2 == NULL) return 0; if (str1 != NULL && str2 == NULL) return 1; if (str1 == NULL && str2 != NULL) return -1; return lstrcmp(str1, str2); } static TCHAR *GetPluginName(HINSTANCE hInstance, TCHAR *buffer, int size) { TCHAR tszModuleName[MAX_PATH]; GetModuleFileName(hInstance, tszModuleName, SIZEOF(tszModuleName)); TCHAR *dllName = _tcsrchr(tszModuleName, '\\'); if ( !dllName) { dllName = tszModuleName; } else { dllName++; } _tcsncpy(buffer, dllName, size); return buffer; } PageHash GetPluginPageHash(const OptionsPageData *page) { return mir_hashstrT(page->ptszGroup) + mir_hashstrT(page->ptszTitle) + mir_hashstrT(page->ptszTab); } static void FindFilterStrings(int enableKeywordFiltering, int current, HWND hWndParent, const OptionsPageData *page) { TCHAR pluginName[MAX_PATH]; HWND hWnd = 0; if (enableKeywordFiltering) { if (current) hWnd = page->hwnd; else { hWnd = CreateDialogIndirectParamA(page->hInst, page->pTemplate, hWndParent, page->dlgProc, page->dwInitParam); //create the options dialog page so we can parse it ShowWindow(hWnd, SW_HIDE); //make sure it's hidden } } DWORD key = GetPluginPageHash(page); //get the plugin page hash TCHAR *PluginFullName = NULL; char * temp = GetPluginNameByInstance(page->hInst); if (temp) PluginFullName = mir_a2t(temp); GetDialogStrings(enableKeywordFiltering, key, GetPluginName(page->hInst, pluginName, SIZEOF(pluginName)), hWnd, page->ptszGroup, page->ptszTitle, page->ptszTab, PluginFullName); if (PluginFullName) mir_free(PluginFullName); if (enableKeywordFiltering && !current) DestroyWindow(hWnd); //destroy the page, we're done with it } static int MatchesFilter(const OptionsPageData *page, TCHAR *szFilterString) { DWORD key = GetPluginPageHash(page); return ContainsFilterString(key, szFilterString); } static WNDPROC OptionsFilterDefaultProc = NULL; static LRESULT CALLBACK OptionsFilterSubclassProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message != WM_PAINT && message != WM_PRINT) return CallWindowProc(OptionsFilterDefaultProc, hWnd, message, wParam, lParam); if (GetFocus() == hWnd || GetWindowTextLength(hWnd)) return CallWindowProc(OptionsFilterDefaultProc, hWnd, message, wParam, lParam); RECT rc; GetClientRect(hWnd, &rc); HDC hdc; PAINTSTRUCT paint; if (message == WM_PAINT) hdc = BeginPaint(hWnd, &paint); else hdc = (HDC)wParam; TCHAR buf[255]; if (bSearchState == 1 && FilterLoadProgress < 100 && FilterLoadProgress > 0) mir_sntprintf(buf, SIZEOF(buf), TranslateT("Loading... %d%%"), FilterLoadProgress); else mir_sntprintf(buf, SIZEOF(buf), TranslateT("Search")); BOOL bDrawnByTheme = FALSE; int oldMode = SetBkMode(hdc, TRANSPARENT); if (openThemeData) { HTHEME hTheme = openThemeData(hWnd, L"EDIT"); if (hTheme) { if (isThemeBackgroundPartiallyTransparent(hTheme, EP_EDITTEXT, ETS_NORMAL)) drawThemeParentBackground(hWnd, hdc, &rc); RECT rc2; getThemeBackgroundContentRect(hTheme, hdc, EP_EDITTEXT, ETS_NORMAL, &rc, &rc2); rc2.top = 2 * rc.top - rc2.top; rc2.left = 2 * rc.left - rc2.left; rc2.bottom = 2 * rc.bottom - rc2.bottom; rc2.right = 2 * rc.right - rc2.right; drawThemeBackground(hTheme, hdc, EP_EDITTEXT, ETS_NORMAL, &rc2, &rc); HFONT hFont = (HFONT) SendMessage(hWnd, WM_GETFONT, 0, 0); HFONT oldFont = (HFONT) SelectObject(hdc, hFont); wchar_t *bufW = mir_t2u(buf); drawThemeText(hTheme, hdc, EP_EDITTEXT, ETS_DISABLED, bufW, -1, 0, 0, &rc); mir_free(bufW); SelectObject(hdc, oldFont); closeThemeData(hTheme); bDrawnByTheme = TRUE; } } SetBkMode(hdc, oldMode); if ( !bDrawnByTheme) { HFONT hFont = (HFONT) SendMessage(hWnd, WM_GETFONT, 0, 0); HFONT oldFont = (HFONT) SelectObject(hdc, hFont); SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); FillRect(hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); int oldMode = SetBkMode(hdc, TRANSPARENT); DrawText(hdc, buf, -1, &rc, 0); SetBkMode(hdc, oldMode); SelectObject(hdc, oldFont); } if (message == WM_PAINT) EndPaint(hWnd, &paint); return 0; } static BOOL CheckPageShow(HWND hdlg, OptionsDlgData* dat, int i) { OptionsPageData* opd = dat->arOpd[i]; if (dat->szFilterString && dat->szFilterString[0] && !MatchesFilter(opd, dat->szFilterString)) return FALSE; if ((opd->flags & ODPF_SIMPLEONLY) && IsDlgButtonChecked(hdlg, IDC_EXPERT)) return FALSE; if ((opd->flags & ODPF_EXPERTONLY) && !IsDlgButtonChecked(hdlg, IDC_EXPERT)) return FALSE; return TRUE; } static BOOL IsAeroMode() { BOOL result; return dwmIsCompositionEnabled && (dwmIsCompositionEnabled(&result) == S_OK) && result; } static void FreeOptionsData(OptionsPageInit* popi) { for (int i=0; i < popi->pageCount; i++) { mir_free((char*)popi->odp[i].pszTitle); mir_free(popi->odp[i].pszGroup); mir_free(popi->odp[i].pszTab); if ((DWORD_PTR)popi->odp[i].pszTemplate & 0xFFFF0000) mir_free((char*)popi->odp[i].pszTemplate); } mir_free(popi->odp); } static void FreeOptionsPageData(OptionsPageData *opd) { if (opd->hwnd != NULL) DestroyWindow(opd->hwnd); mir_free(opd->ptszGroup); mir_free(opd->ptszTab); mir_free(opd->ptszTitle); mir_free(opd->pTemplate); mir_free(opd); } static void AeroPaintControl(HWND hwnd, HDC hdc, WNDPROC OldWndProc, UINT msg = WM_PRINT, LPARAM lpFlags = PRF_CLIENT|PRF_NONCLIENT) { HBITMAP hBmp, hOldBmp; RECT rc; GetClientRect(hwnd, &rc); BYTE *pBits; HDC tempDC = CreateCompatibleDC(hdc); BITMAPINFO bmi; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = rc.right; bmi.bmiHeader.biHeight = -rc.bottom; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; hBmp = CreateDIBSection(tempDC, &bmi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0); hOldBmp = (HBITMAP)SelectObject(tempDC, hBmp); //paint SetPropA(hwnd, "Miranda.AeroRender.Active", (HANDLE)TRUE); CallWindowProc(OldWndProc, hwnd, msg, (WPARAM)tempDC, lpFlags); SetPropA(hwnd, "Miranda.AeroRender.Active", (HANDLE)FALSE); // Fix alpha channel GdiFlush(); for (int i=0; i < rc.right*rc.bottom; ++i, pBits += 4) if ( !pBits[3]) pBits[3] = 255; //Copy to output BitBlt(hdc, 0, 0, rc.right, rc.bottom, tempDC, 0, 0, SRCCOPY); SelectObject(tempDC, hOldBmp); DeleteObject(hBmp); DeleteDC(tempDC); } static LRESULT CALLBACK AeroPaintSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { WNDPROC OldWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA); switch (msg) { case WM_CTLCOLOREDIT: if ( !GetPropA((HWND)lParam, "Miranda.AeroRender.Active")) RedrawWindow((HWND)lParam, NULL, NULL, RDW_INVALIDATE); break; case WM_ERASEBKGND: return TRUE; case WM_PRINT: case WM_PRINTCLIENT: AeroPaintControl(hwnd, (HDC)wParam, OldWndProc, msg, lParam); return TRUE; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); AeroPaintControl(hwnd, hdc, OldWndProc); EndPaint(hwnd, &ps); return TRUE; } case WM_DESTROY: RemovePropA(hwnd, "Miranda.AeroRender.Active"); break; } return CallWindowProc(OldWndProc, hwnd, msg, wParam, lParam); } static void CALLBACK FilterSearchTimerFunc(HWND hwnd, UINT, UINT_PTR, DWORD) { OptionsDlgData* dat = (OptionsDlgData*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if ( !dat) return; if (hFilterSearchWnd == NULL) hFilterSearchWnd = CreateWindowA("STATIC", "Test", WS_OVERLAPPED|WS_DISABLED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), 0); // Fake window to keep option page focused if (FilterPage < dat->arOpd.getCount()) FindFilterStrings(TRUE, dat->currentPage == FilterPage, hFilterSearchWnd, dat->arOpd[FilterPage]); FilterPage++; FilterLoadProgress = FilterPage*100/((dat->arOpd.getCount()) ? dat->arOpd.getCount() : FilterPage); if (FilterPage >= dat->arOpd.getCount()) { KillTimer(hwnd, FilterTimerId); FilterTimerId = 0; bSearchState = 2; FilterLoadProgress = 100; DestroyWindow(hFilterSearchWnd); hFilterSearchWnd = NULL; } RedrawWindow(GetDlgItem(hwnd, IDC_KEYWORD_FILTER), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE); } static void ExecuteFindFilterStringsTimer(HWND hdlg) { bSearchState = 1; FilterPage = 0; if (FilterTimerId) KillTimer(hdlg, FilterTimerId); FilterTimerId = SetTimer(hdlg, NULL, 1, FilterSearchTimerFunc); } static void FillFilterCombo(int enableKeywordFiltering, HWND hDlg, OptionsDlgData* dat) { HINSTANCE* KnownInstances = (HINSTANCE*)alloca(sizeof(HINSTANCE)*dat->arOpd.getCount()); int countKnownInst = 0; SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER, (UINT) CB_RESETCONTENT, 0, 0); int index = SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER, (UINT) CB_ADDSTRING, (WPARAM)0, (LPARAM)TranslateTS(ALL_MODULES_FILTER)); SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER, (UINT) CB_SETITEMDATA, (WPARAM)index, (LPARAM)NULL); index = SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER, (UINT) CB_ADDSTRING, (WPARAM)0, (LPARAM)TranslateTS(CORE_MODULES_FILTER)); SendDlgItemMessage(hDlg, IDC_KEYWORD_FILTER, (UINT) CB_SETITEMDATA, (WPARAM)index, (LPARAM)hInst); TCHAR* tszModuleName = (TCHAR*)alloca(MAX_PATH*sizeof(TCHAR)); for (int i=0; i < dat->arOpd.getCount(); i++) { TCHAR *dllName = NULL; int j; HINSTANCE inst = dat->arOpd[i]->hInst; if ( !enableKeywordFiltering) FindFilterStrings(enableKeywordFiltering, FALSE, hDlg, dat->arOpd[i]); // only modules name (fast enougth) if (inst == hInst) continue; for (j = 0; jarOpd[i]; int pages = 0; if (opd->ptszTab != NULL) { // Count tabs to calc position for (int j = 0; j < dat->arOpd.getCount() && pages < 2; j++) { OptionsPageData* opd2 = dat->arOpd[j]; if ( !CheckPageShow(hdlg, dat, j)) continue; if ( lstrcmp(opd2->ptszTitle, opd->ptszTitle) || lstrcmpnull(opd2->ptszGroup, opd->ptszGroup)) continue; pages++; } } return (pages > 1); } static bool LoadOptionsPage(OPTIONSDIALOGPAGE *src, OptionsPageData *dst) { HRSRC hrsrc = FindResourceA(src->hInstance, src->pszTemplate, MAKEINTRESOURCEA(5)); if (hrsrc == NULL) return false; HGLOBAL hglb = LoadResource(src->hInstance, hrsrc); if (hglb == NULL) return false; DWORD resSize = SizeofResource(src->hInstance, hrsrc); dst->pTemplate = (DLGTEMPLATE*)mir_alloc(resSize); memcpy(dst->pTemplate, LockResource(hglb), resSize); DlgTemplateExBegin *dte = (struct DlgTemplateExBegin*)dst->pTemplate; if (dte->signature == 0xFFFF) { //this feels like an access violation, and is according to boundschecker //...but it works - for now //may well have to remove and sort out the original dialogs dte->style &= ~(WS_VISIBLE|WS_CHILD|WS_POPUP|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|DS_MODALFRAME|DS_CENTER); dte->style |= WS_CHILD; } else { dst->pTemplate->style &= ~(WS_VISIBLE|WS_CHILD|WS_POPUP|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|DS_MODALFRAME|DS_CENTER); dst->pTemplate->style |= WS_CHILD; } dst->dlgProc = src->pfnDlgProc; dst->hInst = src->hInstance; dst->hwnd = NULL; dst->changed = 0; dst->simpleHeight = dst->expertHeight = 0; dst->simpleBottomControlId = src->nIDBottomSimpleControl; dst->simpleWidth = dst->expertWidth = 0; dst->simpleRightControlId = src->nIDRightSimpleControl; dst->nExpertOnlyControls = src->nExpertOnlyControls; dst->expertOnlyControls = src->expertOnlyControls; dst->flags = src->flags; dst->hLangpack = src->hLangpack; dst->dwInitParam = src->dwInitParam; if (src->pszTitle == NULL) dst->ptszTitle = NULL; else if (src->flags & ODPF_UNICODE) dst->ptszTitle = mir_tstrdup(src->ptszTitle); else dst->ptszTitle = mir_a2t(src->pszTitle); if (src->pszGroup == NULL) dst->ptszGroup = NULL; else if (src->flags & ODPF_UNICODE) dst->ptszGroup = mir_tstrdup(src->ptszGroup); else dst->ptszGroup = mir_a2t(src->pszGroup); if (src->pszTab == NULL) dst->ptszTab = NULL; else if (src->flags & ODPF_UNICODE) dst->ptszTab = mir_tstrdup(src->ptszTab); else dst->ptszTab = mir_a2t(src->pszTab); return true; } static void LoadOptionsModule(HWND hdlg, OptionsDlgData *dat, HINSTANCE hInst) { OptionsPageInit opi = { 0 }; CallPluginEventHook(hInst, hOptionsInitEvent, (WPARAM)&opi, 0); if (opi.pageCount == 0) return; for (int i=0; i < opi.pageCount; i++) { OptionsPageData* opd = (OptionsPageData*)mir_calloc(sizeof(OptionsPageData)); if ( LoadOptionsPage(&opi.odp[i], opd)) dat->arOpd.insert(opd); else mir_free(opd); } FreeOptionsData(&opi); PostMessage(hdlg, DM_REBUILDPAGETREE, 0, 0); } static void UnloadOptionsModule(HWND hdlg, OptionsDlgData *dat, HINSTANCE hInst) { bool bToRebuildTree = false; for (int i=0; i < dat->arOpd.getCount(); i++) { OptionsPageData* opd = dat->arOpd[i]; if (opd->hInst != hInst) continue; if (dat->currentPage > i) dat->currentPage--; FreeOptionsPageData(opd); dat->arOpd.remove(i); bToRebuildTree = true; } if (bToRebuildTree) PostMessage(hdlg, DM_REBUILDPAGETREE, 0, 0); } static INT_PTR CALLBACK OptionsDlgProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { OptionsPageData *opd; OptionsDlgData *dat = (OptionsDlgData*)GetWindowLongPtr(hdlg, GWLP_USERDATA); HWND hwndTree = GetDlgItem(hdlg, IDC_PAGETREE); switch (message) { case WM_CTLCOLORSTATIC: switch (GetDlgCtrlID((HWND)lParam)) { case IDC_WHITERECT: case IDC_KEYWORD_FILTER: SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); } break; case WM_INITDIALOG: TranslateDialogDefault(hdlg); { PROPSHEETHEADER *psh = (PROPSHEETHEADER*)lParam; OPENOPTIONSDIALOG *ood = (OPENOPTIONSDIALOG*)psh->pStartPage; TCHAR *lastPage = NULL, *lastGroup = NULL, *lastTab = NULL; DBVARIANT dbv; typedef BOOL (STDAPICALLTYPE *pfnGetComboBoxInfo)(HWND, PCOMBOBOXINFO); pfnGetComboBoxInfo getComboBoxInfo = (pfnGetComboBoxInfo)GetProcAddress(GetModuleHandleA("user32"), "GetComboBoxInfo"); if (getComboBoxInfo) { COMBOBOXINFO cbi; cbi.cbSize = sizeof(COMBOBOXINFO); getComboBoxInfo(GetDlgItem(hdlg, IDC_KEYWORD_FILTER), &cbi); OptionsFilterDefaultProc = (WNDPROC)SetWindowLongPtr(cbi.hwndItem, GWLP_WNDPROC, (LONG_PTR) OptionsFilterSubclassProc); if (IsAeroMode()) { SetWindowLongPtr(cbi.hwndCombo, GWLP_USERDATA, GetWindowLongPtr(cbi.hwndCombo, GWLP_WNDPROC)); SetWindowLongPtr(cbi.hwndCombo, GWLP_WNDPROC, (LONG_PTR)AeroPaintSubclassProc); SetWindowLongPtr(cbi.hwndItem, GWLP_USERDATA, GetWindowLongPtr(cbi.hwndItem, GWLP_WNDPROC)); SetWindowLongPtr(cbi.hwndItem, GWLP_WNDPROC, (LONG_PTR)AeroPaintSubclassProc); } } Utils_RestoreWindowPositionNoSize(hdlg, NULL, "Options", ""); Window_SetIcon_IcoLib(hdlg, SKINICON_OTHER_OPTIONS); CheckDlgButton(hdlg, IDC_EXPERT, DBGetContactSettingByte(NULL, "Options", "Expert", SETTING_SHOWEXPERT_DEFAULT)?BST_CHECKED:BST_UNCHECKED); EnableWindow(GetDlgItem(hdlg, IDC_APPLY), FALSE); dat = new OptionsDlgData; SetWindowLongPtr(hdlg, GWLP_USERDATA, (LONG_PTR)dat); SetWindowText(hdlg, psh->pszCaption); LOGFONT lf; dat->hBoldFont = (HFONT)SendDlgItemMessage(hdlg, IDC_EXPERT, WM_GETFONT, 0, 0); GetObject(dat->hBoldFont, sizeof(lf), &lf); lf.lfWeight = FW_BOLD; dat->hBoldFont = CreateFontIndirect(&lf); dat->hPluginLoad = HookEventMessage(ME_SYSTEM_MODULELOAD, hdlg, HM_MODULELOAD); dat->hPluginUnload = HookEventMessage(ME_SYSTEM_MODULEUNLOAD, hdlg, HM_MODULEUNLOAD); dat->currentPage = -1; if (ood->pszPage == NULL) { if ( !DBGetContactSettingTString(NULL, "Options", "LastPage", &dbv)) { lastPage = mir_tstrdup(dbv.ptszVal); DBFreeVariant(&dbv); } if (ood->pszGroup == NULL) { if ( !DBGetContactSettingTString(NULL, "Options", "LastGroup", &dbv)) { lastGroup = mir_tstrdup(dbv.ptszVal); DBFreeVariant(&dbv); } } else lastGroup = Langpack_PcharToTchar(ood->pszGroup); } else { lastPage = Langpack_PcharToTchar(ood->pszPage); lastGroup = (ood->pszGroup == NULL) ? NULL : Langpack_PcharToTchar(ood->pszGroup); } if (ood->pszTab == NULL) { if ( !DBGetContactSettingTString(NULL, "Options", "LastTab", &dbv)) { lastTab = mir_tstrdup(dbv.ptszVal); DBFreeVariant(&dbv); } } else lastTab = Langpack_PcharToTchar(ood->pszTab); OPTIONSDIALOGPAGE *odp = (OPTIONSDIALOGPAGE*)psh->ppsp; for (size_t i=0; i < psh->nPages; i++, odp++) { opd = (OptionsPageData*)mir_calloc(sizeof(OptionsPageData)); if ( !LoadOptionsPage(odp, opd)) { mir_free(opd); continue; } dat->arOpd.insert(opd); if ( !lstrcmp(lastPage, odp->ptszTitle) && !lstrcmpnull(lastGroup, odp->ptszGroup) && ((ood->pszTab == NULL && dat->currentPage == -1) || !lstrcmpnull(lastTab, odp->ptszTab))) dat->currentPage = (int)i; } mir_free(lastGroup); mir_free(lastPage); mir_free(lastTab); GetWindowRect(GetDlgItem(hdlg, IDC_STNOPAGE), &dat->rcDisplay); MapWindowPoints(NULL, hdlg, (LPPOINT)&dat->rcDisplay, 2); // Add an item to count in height TCITEM tie; tie.mask = TCIF_TEXT | TCIF_IMAGE; tie.iImage = -1; tie.pszText = _T("X"); TabCtrl_InsertItem(GetDlgItem(hdlg, IDC_TAB), 0, &tie); GetWindowRect(GetDlgItem(hdlg, IDC_TAB), &dat->rcTab); MapWindowPoints(NULL, hdlg, (LPPOINT)&dat->rcTab, 2); TabCtrl_AdjustRect(GetDlgItem(hdlg, IDC_TAB), FALSE, &dat->rcTab); //!!!!!!!!!! int enableKeywordFiltering = DBGetContactSettingWord(NULL, "Options", "EnableKeywordFiltering", TRUE); FillFilterCombo(0, hdlg, dat); //!!!!!!!!!! enableKeywordFiltering, SendMessage(hdlg, DM_REBUILDPAGETREE, 0, 0); return TRUE; } case DM_REBUILDPAGETREE: { BOOL bRemoveFocusFromFilter = FALSE; HINSTANCE FilterInst = NULL; LPARAM oldSel = SendDlgItemMessage(hdlg, IDC_KEYWORD_FILTER, CB_GETEDITSEL, 0, 0); GetDlgItemText(hdlg, IDC_KEYWORD_FILTER, dat->szFilterString, SIZEOF(dat->szFilterString)); //if filter string is set to all modules then make the filter string empty (this will return all modules) if (_tcscmp(dat->szFilterString, TranslateTS(ALL_MODULES_FILTER)) == 0) { dat->szFilterString[0] = 0; bRemoveFocusFromFilter = TRUE; } //if filter string is set to core modules replace it with the name of the executable (this will return all core modules) else if (_tcscmp(dat->szFilterString, TranslateTS(CORE_MODULES_FILTER)) == 0) { //replace string with process name - that will show core settings TCHAR szFileName[300]; GetModuleFileName(NULL, szFileName, SIZEOF(szFileName)); TCHAR *pos = _tcsrchr(szFileName, _T('\\')); if (pos) pos++; else pos = szFileName; _tcsncpy(dat->szFilterString, pos, SIZEOF(dat->szFilterString)); } else { int sel = SendMessage(GetDlgItem(hdlg, IDC_KEYWORD_FILTER), (UINT) CB_GETCURSEL, 0, 0); if (sel != -1) { HINSTANCE hinst = (HINSTANCE)SendMessage(GetDlgItem(hdlg, IDC_KEYWORD_FILTER), (UINT) CB_GETITEMDATA, sel , 0); TCHAR szFileName[300]; GetModuleFileName(hinst, szFileName, SIZEOF(szFileName)); TCHAR *pos = _tcsrchr(szFileName, _T('\\')); if (pos) pos++; else pos = szFileName; _tcsncpy(dat->szFilterString, pos, SIZEOF(dat->szFilterString)); } } _tcslwr_locale(dat->szFilterString); //all strings are stored as lowercase ... make sure filter string is lowercase too ShowWindow(hwndTree, SW_HIDE); //deleteall is annoyingly visible HWND oldWnd = NULL; HWND oldTab = NULL; opd = dat->getCurrent(); if (opd != NULL) { oldWnd = opd->hwnd; if (opd->insideTab) oldTab = GetDlgItem(hdlg, IDC_TAB); } dat->hCurrentPage = NULL; TreeView_SelectItem(hwndTree, NULL); TreeView_DeleteAllItems(hwndTree); TVINSERTSTRUCT tvis; tvis.hParent = NULL; tvis.hInsertAfter = TVI_SORT; tvis.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM; tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED; for (int i=0; i < dat->arOpd.getCount(); i++) { static TCHAR *fullTitle = NULL; mir_free(fullTitle); fullTitle = NULL; if ( !CheckPageShow(hdlg, dat, i)) continue; opd = dat->arOpd[i]; TCHAR* ptszGroup = TranslateTH(opd->hLangpack, opd->ptszGroup); TCHAR* ptszTitle = TranslateTH(opd->hLangpack, opd->ptszTitle); TCHAR* ptszTab = TranslateTH(opd->hLangpack, opd->ptszTab); tvis.hParent = NULL; if (FilterInst != NULL) { size_t sz = ptszGroup ? _tcslen(ptszGroup)+1:0; if (sz) sz+=3; sz += ptszTitle ? _tcslen(ptszTitle)+1 : 0; fullTitle = (TCHAR*)mir_alloc(sz*sizeof(TCHAR)); mir_sntprintf(fullTitle, sz, (ptszGroup && ptszTitle)?_T("%s - %s"):_T("%s%s"), ptszGroup ? ptszGroup : _T(""), ptszTitle ? ptszTitle : _T("")); } TCHAR *useTitle = fullTitle ? fullTitle : ptszTitle; if (ptszGroup != NULL && FilterInst == NULL) { tvis.hParent = FindNamedTreeItemAtRoot(hwndTree, ptszGroup); if (tvis.hParent == NULL) { tvis.item.lParam = -1; tvis.item.pszText = ptszGroup; tvis.hParent = TreeView_InsertItem(hwndTree, &tvis); } } else { TVITEM tvi; tvi.hItem = FindNamedTreeItemAtRoot(hwndTree, useTitle); if (tvi.hItem != NULL) { if (i == dat->currentPage) dat->hCurrentPage = tvi.hItem; tvi.mask = TVIF_PARAM; TreeView_GetItem(hwndTree, &tvi); if (tvi.lParam == -1) { tvi.lParam = i; TreeView_SetItem(hwndTree, &tvi); continue; } } } if (ptszTab != NULL) { HTREEITEM hItem; if (tvis.hParent == NULL) hItem = FindNamedTreeItemAtRoot(hwndTree, useTitle); else hItem = FindNamedTreeItemAtChildren(hwndTree, tvis.hParent, useTitle); if (hItem != NULL) { if (i == dat->currentPage) { TVITEM tvi; tvi.hItem = hItem; tvi.mask = TVIF_PARAM; tvi.lParam = dat->currentPage; TreeView_SetItem(hwndTree, &tvi); dat->hCurrentPage = hItem; } continue; } } tvis.item.pszText = useTitle; tvis.item.lParam = i; opd->hTreeItem = TreeView_InsertItem(hwndTree, &tvis); if (i == dat->currentPage) dat->hCurrentPage = opd->hTreeItem; if (fullTitle) mir_free(fullTitle); fullTitle = NULL; } { char str[128]; TVITEMA tvi; tvi.mask = TVIF_TEXT | TVIF_STATE; tvi.pszText = str; tvi.cchTextMax = SIZEOF(str); tvi.hItem = TreeView_GetRoot(hwndTree); while (tvi.hItem != NULL) { if (SendMessageA(hwndTree, TVM_GETITEMA, 0, (LPARAM)&tvi)) { char buf[130]; mir_snprintf(buf, SIZEOF(buf), "%s%s", OPTSTATE_PREFIX, str); if ( !DBGetContactSettingByte(NULL, "Options", buf, 1)) TreeView_Expand(hwndTree, tvi.hItem, TVE_COLLAPSE); } tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem); } } if (dat->hCurrentPage == NULL) { dat->hCurrentPage = TreeView_GetRoot(hwndTree); dat->currentPage = -1; } TreeView_SelectItem(hwndTree, dat->hCurrentPage); if (oldWnd) { opd = dat->getCurrent(); if (opd && oldWnd != opd->hwnd) { ShowWindow(oldWnd, SW_HIDE); if (oldTab && (opd == NULL || !opd->insideTab)) ShowWindow(oldTab, SW_HIDE); } } if (dat->szFilterString[0] == 0) // Clear the keyword combo box SetWindowText(GetDlgItem(hdlg, IDC_KEYWORD_FILTER), _T("")); if ( !bRemoveFocusFromFilter) SetFocus(GetDlgItem(hdlg, IDC_KEYWORD_FILTER)); //set the focus back to the combo box SendDlgItemMessage(hdlg, IDC_KEYWORD_FILTER, CB_SETEDITSEL, 0, oldSel); //but don't select any of the text ShowWindow(hwndTree, SW_SHOW); } break; case HM_MODULELOAD: LoadOptionsModule(hdlg, dat, (HINSTANCE)lParam); break; case HM_MODULEUNLOAD: UnloadOptionsModule(hdlg, dat, (HINSTANCE)lParam); break; case PSM_CHANGED: EnableWindow(GetDlgItem(hdlg, IDC_APPLY), TRUE); opd = dat->getCurrent(); if (opd) opd->changed = 1; return TRUE; case PSM_ISEXPERT: SetWindowLongPtr(hdlg, DWLP_MSGRESULT, IsDlgButtonChecked(hdlg, IDC_EXPERT)); return TRUE; case PSM_GETBOLDFONT: SetWindowLongPtr(hdlg, DWLP_MSGRESULT, (LONG_PTR)dat->hBoldFont); return TRUE; case WM_NOTIFY: switch(wParam) { case IDC_TAB: case IDC_PAGETREE: switch(((LPNMHDR)lParam)->code) { case TVN_ITEMEXPANDING: SetWindowLongPtr(hdlg, DWLP_MSGRESULT, FALSE); return TRUE; case TCN_SELCHANGING: case TVN_SELCHANGING: opd = dat->getCurrent(); if (opd && opd->hwnd != NULL) { PSHNOTIFY pshn; pshn.hdr.code = PSN_KILLACTIVE; pshn.hdr.hwndFrom = dat->arOpd[dat->currentPage]->hwnd; pshn.hdr.idFrom = 0; pshn.lParam = 0; if (SendMessage(dat->arOpd[dat->currentPage]->hwnd, WM_NOTIFY, 0, (LPARAM)&pshn)) { SetWindowLongPtr(hdlg, DWLP_MSGRESULT, TRUE); return TRUE; } } break; case TCN_SELCHANGE: case TVN_SELCHANGED: ShowWindow(GetDlgItem(hdlg, IDC_STNOPAGE), SW_HIDE); opd = dat->getCurrent(); if (opd && opd->hwnd != NULL) ShowWindow(opd->hwnd, SW_HIDE); if ((wParam != IDC_TAB)) { TVITEM tvi; tvi.hItem = dat->hCurrentPage = TreeView_GetSelection(hwndTree); if (tvi.hItem == NULL) break; tvi.mask = TVIF_HANDLE|TVIF_PARAM; TreeView_GetItem(hwndTree, &tvi); dat->currentPage = tvi.lParam; ShowWindow(GetDlgItem(hdlg, IDC_TAB), SW_HIDE); } else { TCITEM tie; TVITEM tvi; tie.mask = TCIF_PARAM; TabCtrl_GetItem(GetDlgItem(hdlg, IDC_TAB), TabCtrl_GetCurSel(GetDlgItem(hdlg, IDC_TAB)), &tie); dat->currentPage = tie.lParam; tvi.hItem = dat->hCurrentPage; tvi.mask = TVIF_PARAM; tvi.lParam = dat->currentPage; TreeView_SetItem(hwndTree, &tvi); } { OptionsPageData* p = dat->getCurrent(); if (p == NULL) { ShowWindow(GetDlgItem(hdlg, IDC_STNOPAGE), SW_SHOW); break; } if (p->hwnd == NULL) { RECT rcPage; RECT rcControl, rc; int w, h; p->hwnd = CreateDialogIndirectParamA(p->hInst, p->pTemplate, hdlg, p->dlgProc, p->dwInitParam); if (p->flags & ODPF_BOLDGROUPS) EnumChildWindows(p->hwnd, BoldGroupTitlesEnumChildren, (LPARAM)dat->hBoldFont); GetClientRect(p->hwnd, &rcPage); p->expertWidth = rcPage.right; p->expertHeight = rcPage.bottom; GetWindowRect(p->hwnd, &rc); if (p->simpleBottomControlId) { GetWindowRect(GetDlgItem(p->hwnd, p->simpleBottomControlId), &rcControl); p->simpleHeight = rcControl.bottom-rc.top; } else p->simpleHeight = p->expertHeight; if (p->simpleRightControlId) { GetWindowRect(GetDlgItem(p->hwnd, p->simpleRightControlId), &rcControl); p->simpleWidth = rcControl.right-rc.left; } else p->simpleWidth = p->expertWidth; if (IsDlgButtonChecked(hdlg, IDC_EXPERT)) { w = p->expertWidth; h = p->expertHeight; } else { for (int i=0; i < p->nExpertOnlyControls; i++) ShowWindow(GetDlgItem(p->hwnd, p->expertOnlyControls[i]), SW_HIDE); w = p->simpleWidth; h = p->simpleHeight; } p->offsetX = 0; p->offsetY = 0; p->insideTab = IsInsideTab(hdlg, dat, dat->currentPage); if (p->insideTab) { SetWindowPos(p->hwnd, HWND_TOP, (dat->rcTab.left+dat->rcTab.right-w)>>1, dat->rcTab.top, w, h, 0); ThemeDialogBackground(p->hwnd, TRUE); } else { SetWindowPos(p->hwnd, HWND_TOP, (dat->rcDisplay.left+dat->rcDisplay.right-w)>>1, (dat->rcDisplay.top+dat->rcDisplay.bottom-h)>>1, w, h, 0); ThemeDialogBackground(p->hwnd, FALSE); } } if (wParam != IDC_TAB) { p->insideTab = IsInsideTab(hdlg, dat, dat->currentPage); if (p->insideTab) { // Make tabbed pane int pages = 0, sel = 0; HWND hwndTab = GetDlgItem(hdlg, IDC_TAB); TabCtrl_DeleteAllItems(hwndTab); TCITEM tie; tie.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM; tie.iImage = -1; for (int i=0; i < dat->arOpd.getCount(); i++) { if ( !CheckPageShow(hdlg, dat, i)) continue; opd = dat->arOpd[i]; if ( lstrcmp(opd->ptszTitle, p->ptszTitle) || lstrcmpnull(opd->ptszGroup, p->ptszGroup)) continue; tie.pszText = TranslateTH(opd->hLangpack, opd->ptszTab); tie.lParam = i; TabCtrl_InsertItem(hwndTab, pages, &tie); if ( !lstrcmp(opd->ptszTab, p->ptszTab)) sel = pages; pages++; } TabCtrl_SetCurSel(hwndTab, sel); ShowWindow(hwndTab, p->insideTab ? SW_SHOW : SW_HIDE); } if (p->insideTab) ThemeDialogBackground(p->hwnd, TRUE); else ThemeDialogBackground(p->hwnd, FALSE); } // Resizing if ( !p->simpleBottomControlId) { int pageWidth, pageHeight; if (IsDlgButtonChecked(hdlg, IDC_EXPERT)) { pageWidth = p->expertWidth; pageHeight = p->expertHeight; } else { pageWidth = p->simpleWidth; pageHeight = p->simpleHeight; } RECT* parentPageRect = &dat->rcDisplay; if (p->insideTab) parentPageRect = &dat->rcTab; pageHeight = min(pageHeight, parentPageRect->bottom - parentPageRect->top); pageWidth = min(pageWidth, parentPageRect->right - parentPageRect->left); int newOffsetX = (parentPageRect->right - parentPageRect->left - pageWidth) >> 1; int newOffsetY = p->insideTab ? 0 : (parentPageRect->bottom - parentPageRect->top - pageHeight) >> 1; struct MoveChildParam mcp; mcp.hDlg = p->hwnd; mcp.offset.x = newOffsetX - p->offsetX; mcp.offset.y = newOffsetY - p->offsetY; if (mcp.offset.x || mcp.offset.y) { EnumChildWindows(p->hwnd, MoveEnumChildren, (LPARAM)(&mcp)); SetWindowPos(p->hwnd, NULL, parentPageRect->left, parentPageRect->top, parentPageRect->right - parentPageRect->left, parentPageRect->bottom - parentPageRect->top, SWP_NOZORDER | SWP_NOACTIVATE); p->offsetX = newOffsetX; p->offsetY = newOffsetY; } } ShowWindow(p->hwnd, SW_SHOW); if (((LPNMTREEVIEW)lParam)->action == TVC_BYMOUSE) PostMessage(hdlg, DM_FOCUSPAGE, 0, 0); else SetFocus(hwndTree); } } } break; case DM_FOCUSPAGE: if (dat->currentPage != -1) SetFocus(dat->arOpd[dat->currentPage]->hwnd); break; case WM_TIMER: if (wParam == FILTER_TIMEOUT_TIMER) { SaveOptionsTreeState(hdlg); SendMessage(hdlg, DM_REBUILDPAGETREE, 0, 0); KillTimer(hdlg, FILTER_TIMEOUT_TIMER); } break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_KEYWORD_FILTER: //add a timer - when the timer elapses filter the option pages if ((HIWORD(wParam) == CBN_SELCHANGE) || (HIWORD(wParam) == CBN_EDITCHANGE)) if ( !SetTimer(hdlg, FILTER_TIMEOUT_TIMER, 400, NULL)) MessageBeep(MB_ICONSTOP); break; case IDC_EXPERT: { int expert = IsDlgButtonChecked(hdlg, IDC_EXPERT); RECT rcPage; int neww, newh; DBWriteContactSettingByte(NULL, "Options", "Expert", (BYTE)expert); PSHNOTIFY pshn; pshn.hdr.idFrom = 0; pshn.lParam = expert; pshn.hdr.code = PSN_EXPERTCHANGED; for (int i=0; i arOpd.getCount(); i++) { opd = dat->arOpd[i]; if (opd->hwnd == NULL) continue; if ( !CheckPageShow(hdlg, dat, i)) continue; pshn.hdr.hwndFrom = opd->hwnd; SendMessage(opd->hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); for (int j = 0; j < opd->nExpertOnlyControls; j++) ShowWindow(GetDlgItem(opd->hwnd, opd->expertOnlyControls[j]), expert ? SW_SHOW : SW_HIDE); opd->insideTab = IsInsideTab(hdlg, dat, i); GetWindowRect(opd->hwnd, &rcPage); if (opd->simpleBottomControlId) newh = expert ? opd->expertHeight : opd->simpleHeight; else newh = rcPage.bottom - rcPage.top; if (opd->simpleRightControlId) neww = expert ? opd->expertWidth : opd->simpleWidth; else neww = rcPage.right - rcPage.left; if (i == dat->currentPage) { POINT ptStart, ptEnd, ptNow; DWORD thisTick, startTick; RECT rc; ptNow.x = ptNow.y = 0; ClientToScreen(hdlg, &ptNow); GetWindowRect(opd->hwnd, &rc); ptStart.x = rc.left-ptNow.x; ptStart.y = rc.top-ptNow.y; if (opd->insideTab) { ptEnd.x = (dat->rcTab.left+dat->rcTab.right-neww)>>1; ptEnd.y = dat->rcTab.top; } else { ptEnd.x = (dat->rcDisplay.left+dat->rcDisplay.right-neww)>>1; ptEnd.y = (dat->rcDisplay.top+dat->rcDisplay.bottom-newh)>>1; } if (abs(ptEnd.x-ptStart.x)>5 || abs(ptEnd.y-ptStart.y)>5) { startTick = GetTickCount(); SetWindowPos(opd->hwnd, HWND_TOP, 0, 0, min(neww, rcPage.right), min(newh, rcPage.bottom), SWP_NOMOVE); UpdateWindow(opd->hwnd); for (;;) { thisTick = GetTickCount(); if (thisTick>startTick+100) break; ptNow.x = ptStart.x+(ptEnd.x-ptStart.x)*(int)(thisTick-startTick)/100; ptNow.y = ptStart.y+(ptEnd.y-ptStart.y)*(int)(thisTick-startTick)/100; SetWindowPos(opd->hwnd, 0, ptNow.x, ptNow.y, 0, 0, SWP_NOZORDER|SWP_NOSIZE); } } if (opd->insideTab) ShowWindow(GetDlgItem(hdlg, IDC_TAB), SW_SHOW); else ShowWindow(GetDlgItem(hdlg, IDC_TAB), SW_HIDE); } if (opd->insideTab) { SetWindowPos(opd->hwnd, HWND_TOP, (dat->rcTab.left+dat->rcTab.right-neww)>>1, dat->rcTab.top, neww, newh, 0); ThemeDialogBackground(opd->hwnd, TRUE); } else { SetWindowPos(opd->hwnd, HWND_TOP, (dat->rcDisplay.left+dat->rcDisplay.right-neww)>>1, (dat->rcDisplay.top+dat->rcDisplay.bottom-newh)>>1, neww, newh, 0); ThemeDialogBackground(opd->hwnd, FALSE); } } SaveOptionsTreeState(hdlg); SendMessage(hdlg, DM_REBUILDPAGETREE, 0, 0); break; } case IDCANCEL: { PSHNOTIFY pshn; pshn.hdr.idFrom = 0; pshn.lParam = 0; pshn.hdr.code = PSN_RESET; for (int i=0;iarOpd.getCount();i++) { if (dat->arOpd[i]->hwnd == NULL || !dat->arOpd[i]->changed) continue; pshn.hdr.hwndFrom = dat->arOpd[i]->hwnd; SendMessage(dat->arOpd[i]->hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); } DestroyWindow(hdlg); } break; case IDOK: case IDC_APPLY: { if (LOWORD(wParam) == IDOK && GetParent(GetFocus()) == GetDlgItem(hdlg, IDC_KEYWORD_FILTER)) return TRUE; PSHNOTIFY pshn; EnableWindow(GetDlgItem(hdlg, IDC_APPLY), FALSE); SetFocus(hwndTree); opd = dat->getCurrent(); if (opd != NULL) { pshn.hdr.idFrom = 0; pshn.lParam = 0; pshn.hdr.code = PSN_KILLACTIVE; pshn.hdr.hwndFrom = opd->hwnd; if (SendMessage(opd->hwnd, WM_NOTIFY, 0, (LPARAM)&pshn)) break; } pshn.hdr.code = PSN_APPLY; for (int i=0;iarOpd.getCount();i++) { if (dat->arOpd[i]->hwnd == NULL || !dat->arOpd[i]->changed) continue; dat->arOpd[i]->changed = 0; pshn.hdr.hwndFrom = dat->arOpd[i]->hwnd; if (SendMessage(dat->arOpd[i]->hwnd, WM_NOTIFY, 0, (LPARAM)&pshn) == PSNRET_INVALID_NOCHANGEPAGE) { dat->hCurrentPage = dat->arOpd[i]->hTreeItem; TreeView_SelectItem(hwndTree, dat->hCurrentPage); if (opd) ShowWindow(opd->hwnd, SW_HIDE); dat->currentPage = i; if (opd) ShowWindow(opd->hwnd, SW_SHOW); return 0; } } if (LOWORD(wParam) == IDOK) DestroyWindow(hdlg); } break; } break; case WM_DESTROY: if (FilterTimerId) KillTimer (hdlg, FilterTimerId); DestroyWindow (hFilterSearchWnd); ClearFilterStrings(); dat->szFilterString[0] = 0; UnhookEvent(dat->hPluginLoad); UnhookEvent(dat->hPluginUnload); SaveOptionsTreeState(hdlg); Window_FreeIcon_IcoLib(hdlg); opd = dat->getCurrent(); if (opd) { if (opd->ptszTab) DBWriteContactSettingTString(NULL, "Options", "LastTab", opd->ptszTab); else DBDeleteContactSetting(NULL, "Options", "LastTab"); if (opd->ptszGroup) DBWriteContactSettingTString(NULL, "Options", "LastGroup", opd->ptszGroup); else DBDeleteContactSetting(NULL, "Options", "LastGroup"); DBWriteContactSettingTString(NULL, "Options", "LastPage", opd->ptszTitle); } else { DBDeleteContactSetting(NULL, "Options", "LastTab"); DBDeleteContactSetting(NULL, "Options", "LastGroup"); DBDeleteContactSetting(NULL, "Options", "LastPage"); } Utils_SaveWindowPosition(hdlg, NULL, "Options", ""); { for (int i=0; i < dat->arOpd.getCount(); i++) FreeOptionsPageData(dat->arOpd[i]); } DeleteObject(dat->hBoldFont); delete dat; hwndOptions = NULL; CallService(MS_MODERNOPT_RESTORE, 0, 0); break; } return FALSE; } void OpenAccountOptions(PROTOACCOUNT* pa) { OptionsPageInit opi = { 0 }; if (pa->ppro == NULL) return; pa->ppro->OnEvent(EV_PROTO_ONOPTIONS, (WPARAM)&opi, 0); if (opi.pageCount > 0) { TCHAR tszTitle[ 100 ]; OPENOPTIONSDIALOG ood = { 0 }; PROPSHEETHEADER psh = { 0 }; mir_sntprintf(tszTitle, SIZEOF(tszTitle), TranslateT("%s options"), pa->tszAccountName); ood.cbSize = sizeof(ood); ood.pszGroup = LPGEN("Network"); ood.pszPage = mir_t2a(pa->tszAccountName); psh.dwSize = sizeof(psh); psh.dwFlags = PSH_PROPSHEETPAGE|PSH_NOAPPLYNOW; psh.hwndParent = NULL; psh.nPages = opi.pageCount; psh.pStartPage = (LPCTSTR)&ood; psh.pszCaption = tszTitle; psh.ppsp = (PROPSHEETPAGE*)opi.odp; hwndOptions = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_OPTIONSPAGE), NULL, OptionsDlgProc, (LPARAM)&psh); mir_free((void*)ood.pszPage); FreeOptionsData(&opi); } } static void OpenOptionsNow(const char *pszGroup, const char *pszPage, const char *pszTab, bool bSinglePage = false) { if (IsWindow(hwndOptions)) { ShowWindow(hwndOptions, SW_RESTORE); SetForegroundWindow(hwndOptions); if (pszPage != NULL) { TCHAR *ptszPage = Langpack_PcharToTchar(pszPage); HTREEITEM hItem = NULL; if (pszGroup != NULL) { TCHAR *ptszGroup = Langpack_PcharToTchar(pszGroup); hItem = FindNamedTreeItemAtRoot(GetDlgItem(hwndOptions, IDC_PAGETREE), ptszGroup); if (hItem != NULL) { hItem = FindNamedTreeItemAtChildren(GetDlgItem(hwndOptions, IDC_PAGETREE), hItem, ptszPage); } mir_free(ptszGroup); } else { hItem = FindNamedTreeItemAtRoot(GetDlgItem(hwndOptions, IDC_PAGETREE), ptszPage); } if (hItem != NULL) { TreeView_SelectItem(GetDlgItem(hwndOptions, IDC_PAGETREE), hItem); } mir_free(ptszPage); } } else { OptionsPageInit opi = { 0 }; NotifyEventHooks(hOptionsInitEvent, (WPARAM)&opi, 0); if (opi.pageCount > 0) { OPENOPTIONSDIALOG ood = { 0 }; ood.pszGroup = pszGroup; ood.pszPage = pszPage; ood.pszTab = pszTab; PROPSHEETHEADER psh = { 0 }; psh.dwSize = sizeof(psh); psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW; psh.nPages = opi.pageCount; psh.pStartPage = (LPCTSTR)&ood; //more structure misuse psh.pszCaption = TranslateT("Miranda IM Options"); psh.ppsp = (PROPSHEETPAGE*)opi.odp; //blatent misuse of the structure, but what the hell hwndOptions = CreateDialogParam(hInst, MAKEINTRESOURCE(bSinglePage ? IDD_OPTIONSPAGE : IDD_OPTIONS), NULL, OptionsDlgProc, (LPARAM)&psh); FreeOptionsData(&opi); } } } static INT_PTR OpenOptions(WPARAM, LPARAM lParam) { OPENOPTIONSDIALOG *ood = (OPENOPTIONSDIALOG*)lParam; if (ood == NULL) return 1; if (ood->cbSize == OPENOPTIONSDIALOG_OLD_SIZE) OpenOptionsNow(ood->pszGroup, ood->pszPage, NULL); else if (ood->cbSize == sizeof(OPENOPTIONSDIALOG)) OpenOptionsNow(ood->pszGroup, ood->pszPage, ood->pszTab); else return 1; return 0; } static INT_PTR OpenOptionsPage(WPARAM, LPARAM lParam) { OPENOPTIONSDIALOG *ood = (OPENOPTIONSDIALOG*)lParam; if (ood == NULL) return 1; if (ood->cbSize == OPENOPTIONSDIALOG_OLD_SIZE) OpenOptionsNow(ood->pszGroup, ood->pszPage, NULL, true); else if (ood->cbSize == sizeof(OPENOPTIONSDIALOG)) OpenOptionsNow(ood->pszGroup, ood->pszPage, ood->pszTab, true); else return 1; return (INT_PTR)hwndOptions; } static INT_PTR OpenOptionsDialog(WPARAM, LPARAM) { if (hwndOptions || GetAsyncKeyState(VK_CONTROL) || !ServiceExists(MS_MODERNOPT_SHOW)) OpenOptionsNow(NULL, NULL, NULL); else CallService(MS_MODERNOPT_SHOW, 0, 0); return 0; } static INT_PTR AddOptionsPage(WPARAM wParam, LPARAM lParam) { OPTIONSDIALOGPAGE *odp = (OPTIONSDIALOGPAGE*)lParam, *dst; OptionsPageInit *opi = (OptionsPageInit*)wParam; if (odp == NULL || opi == NULL) return 1; if (odp->cbSize != sizeof(OPTIONSDIALOGPAGE) && odp->cbSize != OPTIONPAGE_OLD_SIZE) return 1; opi->odp = (OPTIONSDIALOGPAGE*)mir_realloc(opi->odp, sizeof(OPTIONSDIALOGPAGE)*(opi->pageCount+1)); dst = opi->odp + opi->pageCount; memset(dst, 0, sizeof(OPTIONSDIALOGPAGE)); memcpy(dst, odp, odp->cbSize); if (odp->ptszTitle != NULL) { if (odp->flags & ODPF_UNICODE) dst->ptszTitle = mir_wstrdup(odp->ptszTitle); else { dst->ptszTitle = mir_a2u(odp->pszTitle); dst->flags |= ODPF_UNICODE; } } if (odp->ptszGroup != NULL) { if (odp->flags & ODPF_UNICODE) dst->ptszGroup = mir_wstrdup(odp->ptszGroup); else { dst->ptszGroup = Langpack_PcharToTchar(odp->pszGroup); dst->flags |= ODPF_UNICODE; } } if (odp->ptszTab != NULL) { if (odp->flags & ODPF_UNICODE) dst->ptszTab = mir_wstrdup(odp->ptszTab); else { dst->ptszTab = Langpack_PcharToTchar(odp->pszTab); dst->flags |= ODPF_UNICODE; } } if ((DWORD_PTR)odp->pszTemplate & 0xFFFF0000) dst->pszTemplate = mir_strdup(odp->pszTemplate); opi->pageCount++; return 0; } static int OptModulesLoaded(WPARAM, LPARAM) { CLISTMENUITEM mi = { 0 }; mi.cbSize = sizeof(mi); mi.flags = CMIF_ICONFROMICOLIB; mi.icolibItem = GetSkinIconHandle(SKINICON_OTHER_OPTIONS); mi.position = 1900000000; mi.pszName = LPGEN("&Options..."); mi.pszService = "Options/OptionsCommand"; Menu_AddMainMenuItem(&mi); return 0; } int ShutdownOptionsModule(WPARAM, LPARAM) { if (IsWindow(hwndOptions)) DestroyWindow(hwndOptions); hwndOptions = NULL; //!!!!!!!!!! UnhookFilterEvents(); return 0; } int LoadOptionsModule(void) { hwndOptions = NULL; hOptionsInitEvent = CreateHookableEvent(ME_OPT_INITIALISE); CreateServiceFunction("Opt/AddPage", AddOptionsPage); CreateServiceFunction(MS_OPT_OPENOPTIONS, OpenOptions); CreateServiceFunction(MS_OPT_OPENOPTIONSPAGE, OpenOptionsPage); CreateServiceFunction("Options/OptionsCommand", OpenOptionsDialog); HookEvent(ME_SYSTEM_MODULESLOADED, OptModulesLoaded); HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownOptionsModule); //!!!!!!!!!! HookFilterEvents(); return 0; }