/* 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; char * GetPluginNameByInstance( HINSTANCE hInstance ); 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 *pszTitle, *pszGroup, *pszTab; 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; }; static 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) { TVITEM tvi; TCHAR str[128]; 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; pt.x = 0; pt.y = 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 hashstr(page->pszGroup) + hashstr(page->pszTitle) + hashstr(page->pszTab); } 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->pszGroup, page->pszTitle, page->pszTab, 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->pszGroup); mir_free(opd->pszTab); mir_free(opd->pszTitle); 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)hMirandaInst); 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 == hMirandaInst) continue; for (j = 0; jarOpd[i]; int pages = 0; if (opd->pszTab != 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->pszTitle, opd->pszTitle) || lstrcmpnull(opd2->pszGroup, opd->pszGroup)) 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->dwInitParam = src->dwInitParam; if ( src->pszTitle == NULL ) dst->pszTitle = NULL; else if ( src->flags & ODPF_UNICODE ) { dst->pszTitle = ( TCHAR* )mir_wstrdup( src->ptszTitle ); } else dst->pszTitle = ( TCHAR* )mir_strdup( src->pszTitle ); if ( src->pszGroup == NULL ) dst->pszGroup = NULL; else if ( src->flags & ODPF_UNICODE ) { dst->pszGroup = ( TCHAR* )mir_wstrdup( src->ptszGroup ); } else dst->pszGroup = ( TCHAR* )mir_strdup( src->pszGroup ); if ( src->pszTab == NULL ) dst->pszTab = NULL; else if ( src->flags & ODPF_UNICODE ) { dst->pszTab = ( TCHAR* )mir_wstrdup( src->ptszTab ); } else dst->pszTab = ( TCHAR* )mir_strdup( 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) { 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 = LangPackPcharToTchar( ood->pszGroup ); } else { lastPage = LangPackPcharToTchar( ood->pszPage ); lastGroup = ( ood->pszGroup == NULL ) ? NULL : LangPackPcharToTchar( ood->pszGroup ); } if ( ood->pszTab == NULL ) { if ( !DBGetContactSettingTString( NULL, "Options", "LastTab", &dbv )) { lastTab = mir_tstrdup( dbv.ptszVal ); DBFreeVariant( &dbv ); } } else lastTab = LangPackPcharToTchar( ood->pszTab ); OPTIONSDIALOGPAGE *odp = ( OPTIONSDIALOGPAGE* )psh->ppsp; for (size_t i = 0; i < psh->nPages; i++, odp++) { OptionsPageData* 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 = 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; if ( dat->currentPage != (-1)) { oldWnd = dat->arOpd[dat->currentPage]->hwnd; if ( dat->arOpd[dat->currentPage]->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; OptionsPageData* opd = dat->arOpd[i]; tvis.hParent = NULL; if ( FilterInst != NULL ) { size_t sz = opd->pszGroup ? _tcslen(opd->pszGroup)+1:0; if (sz) sz+=3; sz += opd->pszTitle ? _tcslen(opd->pszTitle)+1 : 0; fullTitle = ( TCHAR* )mir_alloc(sz*sizeof(TCHAR)); mir_sntprintf(fullTitle, sz, (opd->pszGroup && opd->pszTitle)?_T("%s - %s"):_T("%s%s"), opd->pszGroup ? opd->pszGroup : _T(""), opd->pszTitle ? opd->pszTitle : _T("")); } TCHAR *useTitle = fullTitle ? fullTitle : opd->pszTitle; if (opd->pszGroup != NULL && FilterInst == NULL) { tvis.hParent = FindNamedTreeItemAtRoot(hwndTree, opd->pszGroup); if (tvis.hParent == NULL) { tvis.item.lParam = -1; tvis.item.pszText = opd->pszGroup; 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 ( opd->pszTab != 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 ) { if ( dat->currentPage == -1 || oldWnd != dat->arOpd[dat->currentPage]->hwnd ) { ShowWindow( oldWnd, SW_HIDE); if ( oldTab && ( dat->currentPage == -1 || !dat->arOpd[dat->currentPage]->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); if (dat->currentPage != (-1)) dat->arOpd[dat->currentPage]->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: if (dat->currentPage != -1 && dat->arOpd[dat->currentPage]->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); if (dat->currentPage != -1 && dat->arOpd[dat->currentPage]->hwnd != NULL) ShowWindow(dat->arOpd[dat->currentPage]->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); } if ( dat->currentPage != -1 ) { OptionsPageData* p = dat->arOpd[dat->currentPage]; 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 i, pages = 0, sel = 0; TCITEM tie; HWND hwndTab = GetDlgItem(hdlg, IDC_TAB); TabCtrl_DeleteAllItems(hwndTab); tie.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM; tie.iImage = -1; for ( i = 0; i < dat->arOpd.getCount(); i++ ) { if (!CheckPageShow( hdlg, dat, i ) ) continue; if ( lstrcmp(dat->arOpd[i]->pszTitle, p->pszTitle) || lstrcmpnull(dat->arOpd[i]->pszGroup, p->pszGroup) ) continue; tie.pszText = dat->arOpd[i]->pszTab; tie.lParam = i; TabCtrl_InsertItem(hwndTab, pages, &tie); if ( !lstrcmp(dat->arOpd[i]->pszTab, p->pszTab) ) 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); } else ShowWindow(GetDlgItem(hdlg, IDC_STNOPAGE), SW_SHOW); break; } } 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++) { OptionsPageData *opd = dat->arOpd[i]; if (opd->hwnd == NULL) continue; if (!CheckPageShow( hdlg, dat, i ) ) continue; //if (( opd->flags & ODPF_SIMPLEONLY ) && expert) continue; //if (( opd->flags & ODPF_EXPERTONLY ) && !expert) 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); if (dat->currentPage != (-1)) { pshn.hdr.idFrom = 0; pshn.lParam = 0; pshn.hdr.code = PSN_KILLACTIVE; pshn.hdr.hwndFrom = dat->arOpd[dat->currentPage]->hwnd; if (SendMessage(dat->arOpd[dat->currentPage]->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 (dat->currentPage != (-1)) ShowWindow(dat->arOpd[dat->currentPage]->hwnd, SW_HIDE); dat->currentPage = i; if (dat->currentPage != (-1)) ShowWindow(dat->arOpd[dat->currentPage]->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 ); if ( dat->currentPage != -1 ) { if ( dat->arOpd[dat->currentPage]->pszTab ) DBWriteContactSettingTString( NULL, "Options", "LastTab", dat->arOpd[dat->currentPage]->pszTab ); else DBDeleteContactSetting( NULL, "Options", "LastTab" ); if ( dat->arOpd[dat->currentPage]->pszGroup ) DBWriteContactSettingTString( NULL, "Options", "LastGroup", dat->arOpd[dat->currentPage]->pszGroup ); else DBDeleteContactSetting( NULL, "Options", "LastGroup" ); DBWriteContactSettingTString( NULL, "Options", "LastPage", dat->arOpd[dat->currentPage]->pszTitle ); } 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(hMirandaInst, 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 = LangPackPcharToTchar(pszPage); HTREEITEM hItem = NULL; if (pszGroup != NULL) { TCHAR *ptszGroup = LangPackPcharToTchar(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(hMirandaInst, 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 && odp->cbSize != OPTIONPAGE_OLD_SIZE2 && odp->cbSize != OPTIONPAGE_OLD_SIZE3) 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_DONTTRANSLATE ) { if ( odp->flags & ODPF_UNICODE ) dst->ptszTitle = mir_wstrdup( odp->ptszTitle ); else { dst->ptszTitle = mir_a2u( odp->pszTitle ); dst->flags |= ODPF_UNICODE; } } else { if ( odp->flags & ODPF_UNICODE ) dst->ptszTitle = mir_wstrdup( TranslateW( odp->ptszTitle )); else { dst->ptszTitle = LangPackPcharToTchar( odp->pszTitle ); dst->flags |= ODPF_UNICODE; } } } if ( odp->ptszGroup != NULL ) { if ( odp->flags & ODPF_UNICODE ) dst->ptszGroup = mir_wstrdup( TranslateW( odp->ptszGroup )); else { dst->ptszGroup = LangPackPcharToTchar( odp->pszGroup ); dst->flags |= ODPF_UNICODE; } } if ( odp->cbSize > OPTIONPAGE_OLD_SIZE2 && odp->ptszTab != NULL ) { if ( odp->flags & ODPF_UNICODE ) dst->ptszTab = mir_wstrdup( TranslateW( odp->ptszTab )); else { dst->ptszTab = LangPackPcharToTchar( 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(MS_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; }