#define APSTUDIO_READONLY_SYMBOLS #include "main.h" #include "api.h" #include "./spage_assoc.h" #include "./setup_resource.h" #include "../nu/ns_wc.h" #include "../nu/AutoWide.h" #include "./langutil.h" #include "./setupcommon.h" #include "../playlist/svc_playlisthandler.h" #include #include "../Agave/URIHandler/svc_urihandler.h" #include #include #define MF_SELECTED 0x0001 #define MF_TYPE_MASK 0xFF00 #define MF_TYPE_REREAD 0xFF #define MF_TYPE_UNKNOWN 0x00 #define MF_TYPE_AUDIO 0x01 #define MF_TYPE_VIDEO 0x02 #define MF_TYPE_PLAYLIST 0x03 #define MF_TYPE_AUXILIARY 0x04 #define ID_REGISTERCD ((TYPE_CATEGORIES_NUM) + 1) #define ID_REGISTERAGENT ((TYPE_CATEGORIES_NUM) + 2) #define SET_TYPE(_val, _type) ((_val) = ((_val) & ~MF_TYPE_MASK) | ((_type) << 8)) #define GET_TYPE(_val) ((_val) >> 8) #define IS_NEEDREREAD(_val) GET_TYPE((_val), MF_TYPE_REREAD) #define IS_SELECTED(_val) (MF_SELECTED & (_val)) #define INITMETA( _type, _selected) ((WORD)(((_type) << 8) | ((_selected) ? MF_SELECTED : 0x0000))) static wchar_t szAuxExt[] = L"wsz\0wal\0wlz\0"; static LRESULT WINAPI TreeViewProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static BOOL IsAgentScheduled(void) { HKEY hKey = NULL; BOOL bActive(FALSE); WCHAR szAgent[MAX_PATH*2] = {0}; DWORD cb = sizeof(szAgent); if (ERROR_SUCCESS != RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &hKey)) return FALSE; if (ERROR_SUCCESS != RegQueryValueExW(hKey, L"WinampAgent", NULL, NULL, (BYTE*)szAgent, &cb)) szAgent[0] = 0x00; RegCloseKey(hKey); if (*szAgent) { WCHAR szPath[MAX_PATH*2] = {0}; GetModuleFileNameW(NULL, szPath, sizeof(szPath)/sizeof(wchar_t)); PathUnquoteSpacesW(szAgent); PathRemoveFileSpecW(szAgent); PathRemoveFileSpecW(szPath); DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); bActive = (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, szPath, -1, szAgent, -1)); } return bActive; } static BOOL IsAgentExist(void) { wchar_t szAgent[MAX_PATH] = {0}; if (0 == GetModuleFileNameW(hMainInstance, szAgent, sizeof(szAgent)/sizeof(wchar_t))) return FALSE; PathRemoveFileSpecW(szAgent); if (NULL == PathCombineW(szAgent, szAgent, L"winampa.exe")) return FALSE; return (FALSE != PathFileExistsW(szAgent)); } static BOOL RefreshIcons(void) { SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, NULL, NULL); return TRUE; } static void RegisterProtocols() { if (config_no_registry) return; wchar_t winampexe[MAX_PATH] = {0}; GetModuleFileNameW(hMainInstance, winampexe, MAX_PATH); WCHAR szApplication[256] = {0}; INT r = MultiByteToWideCharSZ(CP_ACP, 0, app_name, -1, NULL, 0); if (r > ARRAYSIZE(szApplication) || 0 == r) return; if (0 == MultiByteToWideCharSZ(CP_ACP, 0, app_name, -1, szApplication, ARRAYSIZE(szApplication))) return; if (NULL != WASABI_API_SVC) { for(size_t i =0;;i++) { waServiceFactory *sf = WASABI_API_SVC->service_enumService(svc_urihandler::getServiceType(), i); if (NULL == sf) break; svc_urihandler *handler = (svc_urihandler *)sf->getInterface(); if (NULL != handler) { WCHAR szName[128] = {0}, szDesc[256] = {0}; for (size_t k = 0; ;k++) { INT ret = handler->EnumProtocols(k, szName, ARRAYSIZE(szName), szDesc, ARRAYSIZE(szDesc)); if (0 != ret) break; if (0 == handler->RegisterProtocol(szName, winampexe)) { IFileTypeRegistrar *registrar = 0; if (GetRegistrar(®istrar, true) == 0 && registrar) { registrar->RegisterMediaPlayerProtocol(szName, szApplication); registrar->Release(); } } } sf->releaseInterface(handler); } } } } setup_page_assoc::setup_page_assoc() : ref(1), hwnd(NULL), pszTypes(NULL), pMeta(NULL), bRegCD(FALSE), bAgent(FALSE), bExplorerMenu(TRUE) { ZeroMemory(expanded, sizeof(expanded)); ZeroMemory(szTopExt, sizeof(szTopExt)); ZeroMemory(szCaretExt, sizeof(szCaretExt)); } setup_page_assoc::~setup_page_assoc() { if (pszTypes) { free(pszTypes); pszTypes = NULL; } if (pMeta) { free(pMeta); pMeta = NULL; } } size_t setup_page_assoc::AddRef() { return ++ref; } size_t setup_page_assoc::Release() { if (1 == ref) { delete(this); return 0; } return --ref; } HRESULT setup_page_assoc::GetName(bool bShort, const wchar_t **pszName) { if (bShort) { static wchar_t szShortName[32] = {0}; *pszName = (*szShortName) ? szShortName : getStringW(IDS_PAGE_ASSOCIATIONS, szShortName, sizeof(szShortName)/sizeof(wchar_t)); } else { static wchar_t szLongName[64] = {0}; *pszName = (*szLongName) ? szLongName : getStringW(IDS_PAGE_ASSOCIATIONS_LONG, szLongName, sizeof(szLongName)/sizeof(wchar_t)); } return S_OK; } HRESULT setup_page_assoc::Save(HWND hwndText) { HRESULT hr(S_OK); WORD *pm; wchar_t ext_list[16384] = {0}, *p = 0, *pe = ext_list; BOOL bFirst(TRUE); size_t len = ARRAYSIZE(ext_list); // make sure that we honour the agent setting even if no settings changed // this allows the agent to be restarted correctly after a normal upgrade if (bAgent && IsAgentExist()) config_agent_add(); else config_agent_remove(); // temporary: always enumerate and register protocols RegisterProtocols(); if (S_FALSE == IsDirty()) return S_OK; if (!pszTypes) return S_FALSE; config_setup_filetypes(0); for(pm = pMeta, p = pszTypes; *p != 0; p += lstrlenW(p) + 1, pm++) { config_register_capability(p, 0); config_register(p, IS_SELECTED(*pm)); if (IS_SELECTED(*pm) && (S_OK == hr) && GET_TYPE(*pm) != MF_TYPE_AUXILIARY) { if (!len) { hr = S_FALSE; continue; } if (!bFirst) { pe[0] = L':'; pe++; len--; } else bFirst = FALSE; if (S_OK != StringCchCopyExW(pe, len, p, &pe, &len, STRSAFE_IGNORE_NULLS)) hr = S_FALSE; } } if (S_OK == hr) _w_sW("config_extlist", ext_list); config_regcdplayer(bRegCD, 0); (bExplorerMenu) ? config_adddircontext(0) : config_removedircontext(0); config_registermediaplayer(1); WritePrivateProfileStringW(L"Jump To File Extra", L"newIconLib", L"refresh", INI_FILE); RefreshIcons(); return hr; } static BOOL IsFirstSetup() { wchar_t szVer[512] = {0}; if (0 == GetPrivateProfileIntW(L"WinampReg", L"IsFirstInst", 1, INI_FILE)) return FALSE; GetPrivateProfileStringW(L"Winamp", L"config_extlist", L"", szVer, 512, INI_FILE); if (*szVer) return FALSE; GetPrivateProfileStringW(L"WinampReg", L"WAVer", L"", szVer, 512, INI_FILE); return (!*szVer || CSTR_EQUAL != CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, szVer, -1, AutoWide(APP_VERSION), -1)); } static wchar_t *BuildExtensionString(WORD **ppMeta, BOOL bFirstSetup) { INT cWA(0), cPL(0), cAux(0), lWA(0), lPL(0), lAux(0); wchar_t *pszType, *pWAExt = in_getextlistW(), *pPLExt, *p; if (pWAExt) { for(p = pWAExt; *p != 0; cWA++, p += lstrlenW(p) + 1); lWA = (INT)(p - pWAExt); } if (playlistManager) { size_t playlistEnum = 0; const wchar_t *playlistExt=0; while (NULL != (playlistExt = playlistManager->EnumExtension(playlistEnum++))) { lPL += (lstrlenW(playlistExt) + 1); cPL++; } lPL += 2; } for(p = szAuxExt; *p != 0; cAux++, p += lstrlenW(p) + 1); lAux = (INT)(p - szAuxExt); pszType = (wchar_t*)calloc((lWA + lPL + lAux + 1), sizeof(wchar_t)); if (ppMeta) { *ppMeta = (WORD*)calloc((cWA + cPL + cAux), sizeof(WORD)); } if (pszType) { if (pWAExt) CopyMemory(pszType, pWAExt, lWA*sizeof(wchar_t)); pPLExt = pszType + lWA; p = pPLExt; if (playlistManager) { size_t playlistEnum=0; const wchar_t *playlistExt=0; while (lPL > 0 && NULL != (playlistExt = playlistManager->EnumExtension(playlistEnum++))) { int c = lstrlenW(playlistExt) + 1; lstrcpynW(p, playlistExt, c); if (c) { p += c; lPL -= c; } } if (lPL > 1) *p = 0x00; } CopyMemory(p, szAuxExt, lAux*sizeof(wchar_t)); *(p+lAux)=0; } if(ppMeta && *ppMeta) { int i; WORD *pm = *ppMeta; p = pszType; for (i = 0; i < cWA; i++, pm++, p += lstrlenW(p) + 1) *pm = INITMETA(MF_TYPE_REREAD, ((!bFirstSetup) ? config_isregistered(p) : 1)); for (i = 0; i < cPL; i++, pm++, p += lstrlenW(p) + 1) *pm = INITMETA(MF_TYPE_PLAYLIST, ((!bFirstSetup) ? config_isregistered(p) : 1)); for (i = 0; i < cAux; i++, pm++, p += lstrlenW(p) + 1) *pm = INITMETA(MF_TYPE_AUXILIARY, ((!bFirstSetup) ? config_isregistered(p) : 1)); } if (pWAExt) GlobalFree(pWAExt); return pszType; } static BOOL GetPLExtensionName(LPCWSTR pszExt, LPWSTR pszDest, INT cchDest) { BOOL result(FALSE); int n(0); waServiceFactory *sf = 0; LPCWSTR ext; DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); while (NULL != (sf = WASABI_API_SVC->service_enumService(WaSvc::PLAYLISTHANDLER, n++))) { svc_playlisthandler * handler = static_cast(sf->getInterface()); if (handler) { int k(0); while (NULL != (ext = handler->EnumerateExtensions(k++))) { if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszExt, -1, ext, -1)) { result = (S_OK == StringCchCopyW(pszDest, cchDest, handler->GetName())); if (result && CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszExt, -1, L"M3U8", -1)) // ugly... result = (S_OK == StringCchCatW(pszDest, cchDest, L" (Unicode)")); break; } } sf->releaseInterface(handler); } } return result; } static BOOL GetAuxExtensionName(LPCWSTR pszExt, LPWSTR pszDest, INT cchDest) { BOOL result(FALSE); DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszExt, -1, L"wal", -1)) result = (S_OK == StringCchCopyW(pszDest, cchDest, getStringW(IDS_WINAMP_SKIN_MODERN, NULL, 0))); else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszExt, -1, L"wsz", -1)) result = (S_OK == StringCchCopyW(pszDest, cchDest, getStringW(IDS_WINAMP_SKIN_CLASSIC, NULL, 0))); else if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszExt, -1, L"wlz", -1)) result = (S_OK == StringCchCopyW(pszDest, cchDest, getStringW(IDS_WINAMP_LANG_PACK, NULL, 0))); return result; } static BOOL GetExtensionName(LPCWSTR pszFile, INT type, LPWSTR pszDest, INT cchDest) { switch(type) { case MF_TYPE_AUDIO: case MF_TYPE_VIDEO: return in_get_extended_fileinfoW(pszFile, L"family", pszDest, cchDest); case MF_TYPE_PLAYLIST: { const wchar_t *pszExt = PathFindExtensionW(pszFile); return (L'.' == *pszExt && 0x00 != *(++pszExt)) ? GetPLExtensionName(pszExt, pszDest, cchDest) : FALSE; } case MF_TYPE_AUXILIARY: { const wchar_t *pszExt = PathFindExtensionW(pszFile); return (L'.' == *pszExt && 0x00 != *(++pszExt)) ? GetAuxExtensionName(pszExt, pszDest, cchDest) : FALSE; } } return FALSE; } HRESULT setup_page_assoc::Revert(void) { HRESULT hr(S_OK); if (pszTypes) { free(pszTypes); pszTypes = NULL; } if (pMeta) { free(pMeta); pMeta = NULL; } BOOL firstSetup = IsFirstSetup(); pszTypes = BuildExtensionString(&pMeta, firstSetup); ZeroMemory(expanded, sizeof(expanded)); ZeroMemory(szTopExt, sizeof(szTopExt)); ZeroMemory(szCaretExt, sizeof(szCaretExt)); bRegCD = (firstSetup) ? TRUE : config_iscdplayer(); bAgent = (FALSE != IsAgentExist()) ? ((firstSetup) ? FALSE : IsAgentScheduled()) : FALSE; bExplorerMenu = (firstSetup) ? TRUE : config_isdircontext(); if (hwnd) UpdateUI(); return hr; } HRESULT setup_page_assoc::IsDirty(void) { if (IsFirstSetup()) return S_OK; HRESULT hr(S_FALSE); DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); wchar_t *pszOrigTypes = BuildExtensionString(NULL, TRUE), *p; if ((!pszOrigTypes && pszTypes) || (pszOrigTypes && !pszTypes)) { if (pszOrigTypes) free(pszOrigTypes); return S_OK; } else if (!pszOrigTypes && !pszTypes) return S_FALSE; INT cr = CompareStringW(lcid, NORM_IGNORECASE, pszTypes, -1, pszOrigTypes, -1); if (0 == cr) hr = E_UNEXPECTED; else if (CSTR_EQUAL == cr) { WORD *pm; for(pm = pMeta, p = pszTypes; *p != 0; p += lstrlenW(p) + 1, pm++) { if ((MF_SELECTED & *pm) != (BYTE)config_isregistered(p)) { hr = S_OK; break; } } } else hr = S_OK; if (pszOrigTypes) free(pszOrigTypes); if (S_FALSE == hr && bRegCD != config_iscdplayer()) hr = S_OK; if (S_FALSE == hr && bAgent != (IsAgentExist() && IsAgentScheduled())) hr = S_OK; if (S_FALSE == hr && bExplorerMenu != config_isdircontext()) hr = S_OK; return hr; } HRESULT setup_page_assoc::Validate(void) { return S_OK; } HRESULT setup_page_assoc::CreateView(HWND hwndParent, HWND *phwnd) { *phwnd = WACreateDialogParam(MAKEINTRESOURCEW((!IsWin8() ? IDD_SETUP_PAGE_ASSOC : IDD_SETUP_PAGE_ASSOC_WIN8)), hwndParent, ::DialogProc, (LPARAM)this); return S_OK; } void setup_page_assoc::UpdateUI(void) { TVINSERTSTRUCTW is = {0}; WORD *pm; HTREEITEM hBranch[TYPE_CATEGORIES_NUM], hFirst(NULL), hCaret(NULL), hItem; INT sBranch[TYPE_CATEGORIES_NUM] = {0}; INT ids[TYPE_CATEGORIES_NUM] = {IDS_FILETYPE_UNKNOWN, IDS_FILETYPE_AUDIO, IDS_FILETYPE_VIDEO, IDS_FILETYPE_PLAYLIST, IDS_FILETYPE_AUXILIARY}; INT i; wchar_t szText[MAX_PATH] = {0}, buf[MAX_PATH] = {0}, buf2[MAX_PATH] = {0}, *p, *test; if (!hwnd || !IsWindow(hwnd)) return; DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); HWND hwndTree = GetDlgItem(hwnd,IDC_TREE_TYPES); SendMessageW(hwndTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT); if (!pszTypes) return; for (i = 0; i < ARRAYSIZE(hBranch); i++) { hBranch[i] = NULL; sBranch[i] = -1; } INT len = ARRAYSIZE(buf); StringCchCopyW(buf, len, L"test."); test = buf + lstrlenW(buf); len -= (INT)(test - buf); SendMessageW(hwndTree, WM_SETREDRAW, FALSE, 0L); is.hInsertAfter = TVI_LAST; is.item.mask = TVIF_STATE | TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM; is.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_EXPANDED; for(i = 0, pm = pMeta, p = pszTypes; *p != 0 && *(p+1) != 0; pm++, p += lstrlenW(p) + 1, i++) { StringCchCopyW(test, len, p); OutputDebugStringW(test); OutputDebugStringW(L"\r\n"); if (MF_TYPE_REREAD == GET_TYPE(*pm)) { if (!in_get_extended_fileinfoW(buf, L"type", buf2, MAX_PATH)) ZeroMemory(buf2, sizeof(buf2)); *pm = *pm & 0x00FF; switch(buf2[0]) { case L'0': SET_TYPE(*pm, MF_TYPE_AUDIO); break; case L'1': SET_TYPE(*pm, MF_TYPE_VIDEO); break; } } INT index = GET_TYPE(*pm); if (!hBranch[index]) { is.hInsertAfter = TVI_SORT; is.hParent = TVI_ROOT; is.item.cChildren = 1; is.item.state = INDEXTOSTATEIMAGEMASK(1) | ((expanded[index]) ? TVIS_EXPANDED : 0); is.item.pszText = getStringW(ids[index], NULL, 0); is.item.lParam = -(index + 1); hBranch[index] = (HTREEITEM)SendMessageW(hwndTree, TVM_INSERTITEMW, 0, (LPARAM)&is); is.item.cChildren = 0; is.hInsertAfter = TVI_LAST; wchar_t t[32] = {0}; StringCchPrintfW(t, ARRAYSIZE(t), L"#%d", index + 1); if (!hFirst && CSTR_EQUAL == CompareStringW(lcid, 0, t, -1, szTopExt, -1)) hFirst = hBranch[index]; if (!hCaret && CSTR_EQUAL == CompareStringW(lcid, 0, t, -1, szCaretExt, -1)) hCaret = hBranch[index]; } is.hParent = hBranch[index]; if (MF_SELECTED & *pm) { if (-1 == sBranch[index]) sBranch[index] = 1; else if (0 == sBranch[index]) sBranch[index] = 2; } else { if (-1 == sBranch[index]) sBranch[index] = 0; else if (1 == sBranch[index]) sBranch[index] = 2; } lstrcpynW(szText, p, MAX_PATH); INT count = lstrlenW(szText); CharUpperW(szText); wchar_t szName[MAX_PATH] = {0}; if (GetExtensionName(buf, GET_TYPE(*pm), szName, MAX_PATH)) { if (count) StringCchCatW(szText, MAX_PATH, L"\t"); StringCchCatW(szText, MAX_PATH, szName); } is.item.pszText = szText; is.item.lParam = (LPARAM)i; is.item.state = INDEXTOSTATEIMAGEMASK((MF_SELECTED & *pm)?2:1); hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_INSERTITEMW, 0, (LPARAM)&is); if (hItem) { if (!hFirst && CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, p, -1, szTopExt, -1)) hFirst = hItem; if (!hCaret && CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, p, -1, szCaretExt, -1)) hCaret = hItem; } } // insert cd is.hParent = TVI_ROOT; is.item.state = INDEXTOSTATEIMAGEMASK(bRegCD + 1); is.item.pszText = getStringW(IDS_REGISTER_CDPLAYER, NULL, 0); is.item.lParam = -ID_REGISTERCD; hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_INSERTITEMW, 0, (LPARAM)&is); if (!hFirst || !hCaret) { wchar_t t[32] = {0}; StringCchPrintfW(t, ARRAYSIZE(t), L"#%d", ID_REGISTERCD); if (!hFirst && CSTR_EQUAL == CompareStringW(lcid, 0, t, -1, szTopExt, -1)) hFirst = hItem; if (!hCaret && CSTR_EQUAL == CompareStringW(lcid, 0, t, -1, szCaretExt, -1)) hCaret = hItem; } // agent BOOL bAgentExist = IsAgentExist(); HWND hwndCtrl = GetDlgItem(hwnd, IDC_CHK_AGENT); if (hwndCtrl) EnableWindow(hwndCtrl, bAgentExist); hwndCtrl = GetDlgItem(hwnd, IDC_LBL_AGENT_DESC); if (hwndCtrl) EnableWindow(hwndCtrl, bAgentExist); CheckDlgButton(hwnd, IDC_CHK_AGENT, (bAgent && bAgentExist) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hwnd, IDC_CHK_CD, (bRegCD) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hwnd, IDC_CHK_EXPLORER_MENU, (bExplorerMenu) ? BST_CHECKED : BST_UNCHECKED); is.item.mask = TVIF_STATE; is.item.stateMask = TVIS_STATEIMAGEMASK; for (int i = 0; i < sizeof(hBranch)/sizeof(HTREEITEM); i++) { if (!hBranch[i]) continue; is.item.hItem = hBranch[i]; is.item.state = INDEXTOSTATEIMAGEMASK(sBranch[i] + 1); SendMessageW(hwndTree, TVM_SETITEM, 0, (LPARAM)&is.item); SendMessageW(hwndTree, TVM_SORTCHILDREN, FALSE, (LPARAM)hBranch[i]); } if (hCaret) PostMessageW(hwndTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hCaret); if (hFirst) PostMessageW(hwndTree, TVM_SELECTITEM, TVGN_FIRSTVISIBLE, (LPARAM)hFirst); SendMessageW(hwndTree, WM_SETREDRAW, TRUE, 0L); } INT setup_page_assoc::TreeView_OnCustomDraw(NMTVCUSTOMDRAW *ptvcd) { switch(ptvcd->nmcd.dwDrawStage) { case CDDS_PREPAINT: return CDRF_DODEFAULT | CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: return CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT; case CDDS_ITEMPOSTPAINT: { RECT rt; WCHAR szText[256] = {0}; TVITEMW item = {0}; item.hItem = (HTREEITEM)ptvcd->nmcd.dwItemSpec; item.mask = TVIF_TEXT | TVIF_STATE; item.stateMask = TVIS_SELECTED; item.pszText = szText; item.cchTextMax = 256; SendMessageW(ptvcd->nmcd.hdr.hwndFrom, TVM_GETITEMW, 0, (LPARAM)&item); *(DWORD_PTR*)&rt = ptvcd->nmcd.dwItemSpec; SendMessageW(ptvcd->nmcd.hdr.hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rt); SetTextColor(ptvcd->nmcd.hdc, ptvcd->clrText); SetBkColor(ptvcd->nmcd.hdc, ptvcd->clrTextBk); DrawTextW(ptvcd->nmcd.hdc, item.pszText, -1, &rt, DT_EXPANDTABS | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT); rt.right += 8; rt.top = ptvcd->nmcd.rc.top; rt.bottom = ptvcd->nmcd.rc.bottom; ExtTextOutW(ptvcd->nmcd.hdc, 0, 0, ETO_OPAQUE, &rt, L"", 0, NULL); rt.left += 4; DrawTextW(ptvcd->nmcd.hdc, item.pszText, -1, &rt, DT_EXPANDTABS | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER); if ((TVIS_SELECTED & item.state) && ptvcd->nmcd.hdr.hwndFrom == GetFocus()) { if (0 == (0x01/*UISF_HIDEFOCUS*/ & SendMessageW(ptvcd->nmcd.hdr.hwndFrom, 0x0129/*WM_QUERYUISTATE*/, 0, 0L))) { rt.left -= 4; SetTextColor(ptvcd->nmcd.hdc, GetSysColor(COLOR_WINDOWTEXT)); SetBkColor(ptvcd->nmcd.hdc, GetSysColor(COLOR_WINDOW)); DrawFocusRect(ptvcd->nmcd.hdc, &rt); } } } break; } return CDRF_DODEFAULT; } BOOL setup_page_assoc::TreeView_OnClick(NMHDR *pnmh) { TVHITTESTINFO ht; GetCursorPos(&ht.pt); MapWindowPoints(HWND_DESKTOP, pnmh->hwndFrom, &ht.pt, 1); if(NULL != SendMessageW(pnmh->hwndFrom, TVM_HITTEST, 0, (LPARAM)&ht)) { if ((TVHT_ONITEM | TVHT_ONITEMRIGHT) & ht.flags) { TreeView_OnItemStateClick(pnmh->hwndFrom, ht.hItem); if (TVHT_ONITEMSTATEICON & ht.flags) return TRUE; } } return FALSE; } BOOL setup_page_assoc::TreeView_OnKeyDown(NMTVKEYDOWN *ptvkd) { switch(ptvkd->wVKey) { case VK_SPACE: { HTREEITEM hItem = (HTREEITEM)(HTREEITEM)SendMessageW(ptvkd->hdr.hwndFrom, TVM_GETNEXTITEM, (WPARAM)TVGN_CARET, 0L); if (hItem) TreeView_OnItemStateClick(ptvkd->hdr.hwndFrom, hItem); return TRUE; } } return FALSE; } void setup_page_assoc::TreeView_OnItemStateClick(HWND hwndTree, HTREEITEM hItem) { HTREEITEM hParent = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_PARENT, (LPARAM)hItem); HTREEITEM hChild = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)hItem); TVITEMW item = {0}; item.hItem = hItem; item.mask = TVIF_STATE | TVIF_PARAM; item.stateMask = TVIS_STATEIMAGEMASK; if (!SendMessageW(hwndTree, TVM_GETITEMW, 0, (LPARAM)&item)) return; INT state = ((item.state>>12) - 1); INT param = (INT)item.lParam; SendMessageW(hwndTree, WM_SETREDRAW, FALSE, 0L); state = (2 == state) ? 1 : !state; item.mask = TVIF_STATE; item.state = INDEXTOSTATEIMAGEMASK(state + 1); SendMessageW(hwndTree, TVM_SETITEMW, 0, (LPARAM)&item); if (!hChild) { INT count = 1, selcount = state; item.hItem = hItem; while(NULL != (item.hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_PREVIOUS, (LPARAM)item.hItem))) { count++; if (!SendMessageW(hwndTree, TVM_GETITEMW, 0, (LPARAM)&item)) continue; if (2 == (item.state>>12)) selcount++; } item.hItem = hItem; while(NULL != (item.hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)item.hItem))) { count++; if (!SendMessageW(hwndTree, TVM_GETITEMW, 0, (LPARAM)&item)) continue; if (2 == (item.state>>12)) selcount++; } item.hItem = hParent; item.state = INDEXTOSTATEIMAGEMASK(((!selcount) ? 1 : ((selcount == count) ? 2 :3))); SendMessageW(hwndTree, TVM_SETITEMW, 0, (LPARAM)&item); if (param >= 0) pMeta[param] = (pMeta[param] & ~MF_SELECTED) | ((state) ? MF_SELECTED : 0); else { switch(-param) { case ID_REGISTERCD: bRegCD = state; break; } } } else { item.hItem = hChild; while(item.hItem) { item.mask = TVIF_PARAM; if (SendMessageW(hwndTree, TVM_GETITEMW, 0, (LPARAM)&item) && item.lParam >= 0) pMeta[item.lParam] = (pMeta[item.lParam] & ~MF_SELECTED) | ((state) ? MF_SELECTED : 0); item.mask = TVIF_STATE; item.state = INDEXTOSTATEIMAGEMASK(state + 1); SendMessageW(hwndTree, TVM_SETITEMW, 0, (LPARAM)&item); item.hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)item.hItem); } } UpdateWindow(hwnd); SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); SendMessageW(hwndTree, WM_SETREDRAW, TRUE, 0L); SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); InvalidateRect(hwndTree, NULL, FALSE); } INT_PTR setup_page_assoc::OnInitDialog(HWND hwndFocus, LPARAM lParam) { HWND hwndTree = GetDlgItem(hwnd, IDC_TREE_TYPES); if (hwndTree) { HIMAGELIST himl = ImageList_LoadImage(hMainInstance, MAKEINTRESOURCE(IDB_CHECKBOX), 16, 1, CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION | LR_LOADTRANSPARENT); if (himl) himl = (HIMAGELIST) SendMessageW(hwndTree, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)himl); if (himl) ImageList_Destroy(himl); WNDPROC fnOldProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(hwndTree, GWLP_WNDPROC, (LONGX86)(LONG_PTR)TreeViewProc); if (fnOldProc) SetPropW(hwndTree, L"TVPROC", fnOldProc); } UpdateUI(); return 0; } void setup_page_assoc::OnDestroy(void) { HWND hwndTree = GetDlgItem(hwnd, IDC_TREE_TYPES); if (hwndTree) { TVITEMW item = {0}; HIMAGELIST himl; INT index1(-1), index2(-1); wchar_t *p; ZeroMemory(expanded, sizeof(expanded)); ZeroMemory(szTopExt, sizeof(szTopExt)); ZeroMemory(szCaretExt, sizeof(szCaretExt)); item.mask = TVIF_PARAM | TVIF_STATE; item.stateMask = TVIS_EXPANDED; item.hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_ROOT, 0L); while(item.hItem) { if (SendMessageW(hwndTree, TVM_GETITEMW, 0, (LPARAM)&item)) { INT param = (INT)-item.lParam; if (param < TYPE_CATEGORIES_NUM) expanded[param] = (BYTE)(TVIS_EXPANDED & item.state); } item.hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)item.hItem); } item.mask = TVIF_PARAM; item.hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_FIRSTVISIBLE, 0L); if (item.hItem && SendMessageW(hwndTree, TVM_GETITEMW, 0, (LPARAM)&item)) index1 = (INT)item.lParam; item.hItem = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_CARET, 0L); if (item.hItem && SendMessageW(hwndTree, TVM_GETITEMW, 0, (LPARAM)&item)) index2 = (INT)item.lParam; if (index1 < 0) StringCchPrintfW(szTopExt, ARRAYSIZE(szTopExt), L"#%d", -index1); if (index2 < 0) StringCchPrintfW(szCaretExt, ARRAYSIZE(szCaretExt), L"#%d", -index2); if (index1 >= 0 || index2 >= 0) { INT i; for(i = 0, p = pszTypes; *p != 0; p += lstrlenW(p) + 1, i++) { if (index1 == i) { StringCchCopyW(szTopExt, ARRAYSIZE(szTopExt), p); if (index2 < 0) break; index1 = -1; } if (index2 == i) { StringCchCopyW(szCaretExt, ARRAYSIZE(szCaretExt), p); if (index1 < 0) break; index2 = -1; } } } himl = (HIMAGELIST) SendMessageW(hwndTree, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)NULL); if (himl) ImageList_Destroy(himl); } } void setup_page_assoc::OnSize(UINT nType, INT cx, INT cy) { RECT rw; INT h, r; h = cy; r = cx; HWND hwndCtrl = GetDlgItem(hwnd, IDC_LBL_HEADER); if (hwndCtrl) { GetWindowRect(hwndCtrl, &rw); MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); SetWindowPos(hwndCtrl, NULL, 0, 0, cx - rw.left*2, rw.bottom - rw.top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); } if (IsWin8()) { // hide the treeview (as needed to process things nicely but needs to be hidden on Windows 8 and higher as they work differently) hwndCtrl = GetDlgItem(hwnd, IDC_TREE_TYPES); if (hwndCtrl) { SetWindowPos(hwndCtrl, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_HIDEWINDOW); } return; } hwndCtrl = GetDlgItem(hwnd, IDC_CHK_EXPLORER_MENU); if (hwndCtrl) { GetWindowRect(hwndCtrl, &rw); MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); h = cy - (((rw.bottom - rw.top)*3) + 6); r = max(0, (cx - (rw.right - rw.left))/2) + (rw.right - rw.left); SetWindowPos(hwndCtrl, NULL, r - (rw.right - rw.left) - 12, h, rw.right - rw.left + 26, rw.bottom - rw.top, SWP_NOACTIVATE | SWP_NOZORDER); } hwndCtrl = GetDlgItem(hwnd, IDC_CHK_AGENT); if (hwndCtrl) { GetWindowRect(hwndCtrl, &rw); MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); SetWindowPos(hwndCtrl, NULL, r - (rw.right - rw.left) - 12, h + (rw.bottom - rw.top) + 2, rw.right - rw.left + 26, rw.bottom - rw.top, SWP_NOACTIVATE | SWP_NOZORDER); } hwndCtrl = GetDlgItem(hwnd, IDC_LBL_AGENT_DESC); if (hwndCtrl) { GetWindowRect(hwndCtrl, &rw); MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); SetWindowPos(hwndCtrl, NULL, r - (rw.right - rw.left) - 12, h + (rw.bottom - rw.top)*2 + 2, rw.right - rw.left + 26, rw.bottom - rw.top, SWP_NOACTIVATE | SWP_NOZORDER); } hwndCtrl = GetDlgItem(hwnd, IDC_TREE_TYPES); if (hwndCtrl) { GetWindowRect(hwndCtrl, &rw); MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); SetWindowPos(hwndCtrl, NULL, r - (rw.right - rw.left) - 12, rw.top, rw.right - rw.left + 26, h - rw.top - 4, SWP_NOACTIVATE | SWP_NOZORDER); } } void setup_page_assoc::OnCommand(INT nCtrlID, INT nEvntID, HWND hwndCtrl) { switch(nCtrlID) { case IDC_CHK_EXPLORER_MENU: { switch (nEvntID) { case BN_CLICKED: bExplorerMenu = (BST_CHECKED == (BST_CHECKED & (INT)SendMessageW(hwndCtrl, BM_GETSTATE, 0, 0L))); break; } break; } case IDC_CHK_AGENT: { switch (nEvntID) { case BN_CLICKED: bAgent = (BST_CHECKED == (BST_CHECKED & (INT)SendMessageW(hwndCtrl, BM_GETSTATE, 0, 0L))); break; } break; } case IDC_CHK_CD: { switch (nEvntID) { case BN_CLICKED: bRegCD = (BST_CHECKED == (BST_CHECKED & (INT)SendMessageW(hwndCtrl, BM_GETSTATE, 0, 0L))); break; } break; } } } BOOL setup_page_assoc::OnNotify(INT nCtrlID, NMHDR *pnmh, LRESULT *pResult) { switch(nCtrlID) { case IDC_TREE_TYPES: switch(pnmh->code) { case NM_CUSTOMDRAW: *pResult = TreeView_OnCustomDraw((NMTVCUSTOMDRAW*)pnmh); return TRUE; case NM_CLICK: *pResult = TreeView_OnClick(pnmh); return TRUE; case TVN_KEYDOWN: *pResult = TreeView_OnKeyDown((NMTVKEYDOWN*)pnmh); return TRUE; } } return FALSE; } INT_PTR setup_page_assoc::PageDlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_INITDIALOG: return OnInitDialog((HWND)wParam, lParam); case WM_DESTROY: OnDestroy(); break; case WM_SIZE: OnSize((UINT)wParam, LOWORD(lParam), HIWORD(lParam)); break; case WM_COMMAND: OnCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break; case WM_NOTIFY: { LRESULT result = 0; if (OnNotify((INT)wParam, (NMHDR*)lParam, &result)) { SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (LONGX86)(LONG_PTR)result); return TRUE; } } break; } return 0; } static INT_PTR WINAPI DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { setup_page_assoc *pInst = (setup_page_assoc*)GetPropW(hwnd, L"SETUPPAGE"); switch(uMsg) { case WM_INITDIALOG: pInst = (setup_page_assoc*)lParam; if (pInst) { pInst->hwnd = hwnd; SetPropW(hwnd, L"SETUPPAGE", pInst); } break; case WM_DESTROY: if (pInst) { pInst->PageDlgProc(uMsg, wParam, lParam); RemovePropW(hwnd, L"SETUPPAGE"); pInst = NULL; } break; } return (pInst) ? pInst->PageDlgProc(uMsg, wParam, lParam) : 0; } static void TreeViewCheckItems(HWND hwnd, TVITEMW *pItem) { while (pItem->hItem) { SendMessageW(hwnd, TVM_SETITEMW, 0, (LPARAM)pItem); HTREEITEM hChild = (HTREEITEM)SendMessageW(hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)pItem->hItem); if (hChild) { HTREEITEM hTemp = pItem->hItem; pItem->hItem = hChild; TreeViewCheckItems(hwnd, pItem); pItem->hItem = hTemp; } pItem->hItem = (HTREEITEM)SendMessageW(hwnd, TVM_GETNEXTITEM , TVGN_NEXT, (LPARAM)pItem->hItem); } } static LRESULT WINAPI TreeViewProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WNDPROC fnOldProc = (WNDPROC)GetPropW(hwnd, L"TVPROC"); if (!fnOldProc) return DefWindowProcW(hwnd, uMsg, wParam, lParam); switch(uMsg) { case WM_DESTROY: RemovePropW(hwnd, L"TVPROC"); SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)fnOldProc); break; case WM_CHAR: if (0x01/*(CTRL_A)*/ == wParam) { TVITEMW item = {0}; item.mask = TVIF_HANDLE | TVIF_STATE; item.stateMask = TVIS_STATEIMAGEMASK; item.state = INDEXTOSTATEIMAGEMASK(2); item.hItem = (HTREEITEM)SendMessageW(hwnd, TVM_GETNEXTITEM , TVGN_ROOT, 0L); SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); TreeViewCheckItems(hwnd, &item); SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); return 0; } break; } return CallWindowProcW(fnOldProc, hwnd, uMsg, wParam, lParam); } #ifdef CBCLASS #undef CBCLASS #endif #define CBCLASS setup_page_assoc START_DISPATCH CB(ADDREF, AddRef) CB(RELEASE, Release) CB(API_SETUPPAGE_GET_NAME, GetName) CB(API_SETUPPAGE_CREATEVIEW, CreateView) CB(API_SETUPPAGE_SAVE, Save) CB(API_SETUPPAGE_REVERT, Revert) CB(API_SETUPPAGE_ISDIRTY, IsDirty) CB(API_SETUPPAGE_VALIDATE, Validate) END_DISPATCH #undef CBCLASS