/* Copyright (C) 2012 Birunthan Mohanathas 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "StdAfx.h" #include "Application.h" #include "DialogPackage.h" #include "DialogInstall.h" #include "resource.h" #include "../Version.h" #define WM_DELAYED_CLOSE WM_APP + 0 extern GlobalData g_Data; extern OsNameVersion g_OsNameVersions[]; DialogPackage* DialogPackage::c_Dialog = nullptr; DialogPackage::DialogPackage(HWND wnd) : Dialog(wnd), m_TabInfo(wnd), m_TabOptions(wnd), m_TabAdvanced(wnd), m_LoadLayout(false), m_MergeSkins(false), m_PackagerThread(), m_ZipFile() { } DialogPackage::~DialogPackage() { } void DialogPackage::Create(HINSTANCE hInstance, LPWSTR lpCmdLine) { HANDLE hMutex; if (IsRunning(L"Rainmeter Skin Packager", &hMutex)) { HWND hwnd = FindWindow(L"#32770", L"Rainmeter Skin Packager"); SetForegroundWindow(hwnd); } else { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PACKAGE_DIALOG), nullptr, (DLGPROC)DlgProc, (LPARAM)lpCmdLine); ReleaseMutex(hMutex); } } Dialog::Tab& DialogPackage::GetActiveTab() { int sel = TabCtrl_GetCurSel(GetDlgItem(m_Window, IDC_PACKAGE_TAB)); if (sel == -1) { return m_TabInfo; } else if (sel == 0) { return m_TabOptions; } else // if (sel == 1) { return m_TabAdvanced; } } INT_PTR CALLBACK DialogPackage::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (!c_Dialog) { if (uMsg == WM_INITDIALOG) { c_Dialog = new DialogPackage(hWnd); return c_Dialog->OnInitDialog(wParam, lParam); } } else { switch (uMsg) { case WM_COMMAND: return c_Dialog->OnCommand(wParam, lParam); case WM_NOTIFY: return c_Dialog->OnNotify(wParam, lParam); case WM_CLOSE: { if (!c_Dialog->m_PackagerThread) { EndDialog(hWnd, 0); } } return TRUE; case WM_DESTROY: delete c_Dialog; c_Dialog = nullptr; return FALSE; } } return FALSE; } INT_PTR DialogPackage::OnInitDialog(WPARAM wParam, LPARAM lParam) { HICON hIcon = (HICON)LoadImage(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_SKININSTALLER), IMAGE_ICON, 16, 16, LR_SHARED); SendMessage(m_Window, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); if (GetOSPlatform() >= OSPLATFORM_VISTA) { SetDialogFont(); } m_TabInfo.Activate(); return FALSE; } INT_PTR DialogPackage::OnCommand(WPARAM wParam, LPARAM lParam) { switch (LOWORD(wParam)) { case IDC_PACKAGE_NEXT_BUTTON: { HWND item = GetDlgItem(m_Window, IDC_PACKAGE_TAB); TCITEM tci = {0}; tci.mask = TCIF_TEXT; tci.pszText = L"Options"; TabCtrl_InsertItem(item, 0, &tci); tci.pszText = L"Advanced"; TabCtrl_InsertItem(item, 1, &tci); item = GetDlgItem(m_Window, IDC_PACKAGE_NEXT_BUTTON); ShowWindow(item, SW_HIDE); item = GetDlgItem(m_Window, IDC_PACKAGE_CREATEPACKAGE_BUTTON); ShowWindow(item, SW_SHOWNORMAL); SendMessage(m_Window, DM_SETDEFID, IDC_PACKAGE_CREATEPACKAGE_BUTTON, 0); ShowWindow(m_TabInfo.GetWindow(), SW_HIDE); m_TabOptions.Activate(); } break; case IDC_PACKAGE_CREATEPACKAGE_BUTTON: { HWND item = GetDlgItem(m_Window, IDC_PACKAGE_CREATEPACKAGE_BUTTON); EnableWindow(item, FALSE); item = GetDlgItem(m_Window, IDCANCEL); EnableWindow(item, FALSE); m_TabOptions.Activate(); item = GetDlgItem(m_Window, IDC_PACKAGE_TAB); TabCtrl_SetCurSel(item, 0); EnableWindow(item, FALSE); EnableWindow(m_TabOptions.GetWindow(), FALSE); EnableWindow(m_TabAdvanced.GetWindow(), FALSE); item = GetDlgItem(m_TabOptions.GetWindow(), IDC_INSTALLTAB_CREATING_TEXT); ShowWindow(item, SW_SHOWNORMAL); item = GetDlgItem(m_TabOptions.GetWindow(), IDC_INSTALLTAB_CREATING_BAR); ShowWindow(item, SW_SHOWNORMAL); SendMessage(item, PBM_SETMARQUEE, (WPARAM)TRUE, 0); m_PackagerThread = (HANDLE)_beginthreadex(nullptr, 0, PackagerThreadProc, this, 0, nullptr); if (!m_PackagerThread) { MessageBox(m_Window, L"Unknown error.", L"Rainmeter Skin Packager", MB_ERROR); EndDialog(m_Window, 0); } } break; case IDCANCEL: if (!m_PackagerThread) { EndDialog(m_Window, 0); } break; default: return FALSE; } return TRUE; } INT_PTR DialogPackage::OnNotify(WPARAM wParam, LPARAM lParam) { LPNMHDR nm = (LPNMHDR)lParam; switch (nm->idFrom) { case IDC_PACKAGE_TAB: if (nm->code == TCN_SELCHANGE) { // Disable all tab windows first EnableWindow(m_TabInfo.GetWindow(), FALSE); EnableWindow(m_TabOptions.GetWindow(), FALSE); EnableWindow(m_TabAdvanced.GetWindow(), FALSE); GetActiveTab().Activate(); } break; default: return 1; } return 0; } void DialogPackage::SetNextButtonState() { BOOL state = !(m_Name.empty() || m_Author.empty() || m_SkinFolder.second.empty()); EnableWindow(GetDlgItem(m_Window, IDC_PACKAGE_NEXT_BUTTON), state); } bool DialogPackage::CreatePackage() { // Create options file WCHAR tempFile[MAX_PATH]; GetTempPath(MAX_PATH, tempFile); GetTempFileName(tempFile, L"ini", 0, tempFile); WritePrivateProfileString(L"rmskin", L"Name", m_Name.c_str(), tempFile); WritePrivateProfileString(L"rmskin", L"Author", m_Author.c_str(), tempFile); WritePrivateProfileString(L"rmskin", L"Version", m_Version.c_str(), tempFile); if (!c_Dialog->m_Load.empty()) { WritePrivateProfileString(L"rmskin", L"LoadType", c_Dialog->m_LoadLayout ? L"Layout" : L"Skin", tempFile); WritePrivateProfileString(L"rmskin", L"Load", c_Dialog->m_Load.c_str(), tempFile); } if (!c_Dialog->m_VariableFiles.empty()) { WritePrivateProfileString(L"rmskin", L"VariableFiles", m_VariableFiles.c_str(), tempFile); } if (c_Dialog->m_MergeSkins) { WritePrivateProfileString(L"rmskin", L"MergeSkins", L"1", tempFile); } WritePrivateProfileString(L"rmskin", L"MinimumRainmeter", m_MinimumRainmeter.c_str(), tempFile); WritePrivateProfileString(L"rmskin", L"MinimumWindows", m_MinimumWindows.c_str(), tempFile); // Create archive and add options file and header bitmap m_ZipFile = zipOpen(ConvertToAscii(m_TargetFile.c_str()).c_str(), APPEND_STATUS_CREATE); auto cleanup = [&]()->bool { zipClose(m_ZipFile, nullptr); return false; }; if (!m_ZipFile || (!c_Dialog->m_HeaderFile.empty() && !AddFileToPackage(c_Dialog->m_HeaderFile.c_str(), L"RMSKIN.bmp")) || !AddFileToPackage(tempFile, L"RMSKIN.ini")) { std::wstring error = L"Unable to create package."; error += L"\n\nClick OK to close Packager."; MessageBox(m_Window, error.c_str(), L"Rainmeter Skin Packager", MB_OK | MB_ICONERROR); DeleteFile(tempFile); return cleanup(); } // Add skin { std::wstring zipPrefix = L"Skins\\" + m_SkinFolder.first; zipPrefix += L'\\'; if (!AddFolderToPackage(m_SkinFolder.second, L"", zipPrefix.c_str())) { return cleanup(); } } // Add layouts for (auto iter = m_LayoutFolders.cbegin(); iter != m_LayoutFolders.cend(); ++iter) { std::wstring realPath = (*iter).second + L"Rainmeter.ini"; std::wstring zipPath = L"Layouts\\" + (*iter).first; zipPath += L"\\Rainmeter.ini"; if (!AddFileToPackage(realPath.c_str(), zipPath.c_str())) { std::wstring error = L"Error adding layout '"; error += (*iter).first; error += L"'."; error += L"\n\nClick OK to close Packager."; MessageBox(m_Window, error.c_str(), L"Rainmeter Skin Packager", MB_OK | MB_ICONERROR); return cleanup(); } } // Add plugins for (auto iter = m_PluginFolders.cbegin(); iter != m_PluginFolders.cend(); ++iter) { // Add 32bit and 64bit versions for (int i = 0; i < 2; ++i) { const std::wstring& realPath = (i == 0) ? (*iter).second.first : (*iter).second.second; std::wstring zipPath = ((i == 0) ? L"Plugins\\32bit\\" : L"Plugins\\64bit\\") + (*iter).first; if (!AddFileToPackage(realPath.c_str(), zipPath.c_str())) { std::wstring error = L"Error adding plugin '"; error += (*iter).first; error += L"'."; error += L"\n\nClick OK to close Packager."; MessageBox(m_Window, error.c_str(), L"Rainmeter Skin Packager", MB_OK | MB_ICONERROR); return cleanup(); } } } // Add footer FILE* file; if (zipClose(m_ZipFile, nullptr) == ZIP_OK && (file = _wfopen(m_TargetFile.c_str(), L"r+b")) != nullptr) { fseek(file, 0, SEEK_END); DialogInstall::PackageFooter footer = { _ftelli64(file), 0, "RMSKIN" }; fwrite(&footer, sizeof(footer), 1, file); fclose(file); } else { std::wstring error = L"Unable to create package."; error += L"\n\nClick OK to close Packager."; MessageBox(m_Window, error.c_str(), L"Rainmeter Skin Packager", MB_OK | MB_ICONERROR); return false; } return true; } unsigned __stdcall DialogPackage::PackagerThreadProc(void* pParam) { DialogPackage* dialog = (DialogPackage*)pParam; if (dialog->CreatePackage()) { // Stop the progress bar HWND item = GetDlgItem(dialog->m_TabOptions.GetWindow(), IDC_INSTALLTAB_CREATING_BAR); SendMessage(item, PBM_SETMARQUEE, (WPARAM)FALSE, 0); FlashWindow(dialog->m_Window, TRUE); std::wstring message = L"The skin package has been successfully created."; message += L"\n\nClick OK to close Packager."; MessageBox(c_Dialog->GetWindow(), message.c_str(), L"Rainmeter Skin Packager", MB_OK | MB_ICONINFORMATION); } else { DeleteFile(dialog->m_TargetFile.c_str()); } EndDialog(dialog->m_Window, 0); return 0; } bool DialogPackage::AddFileToPackage(const WCHAR* filePath, const WCHAR* zipPath) { std::string zipPathAscii = ConvertToAscii(zipPath); for (int i = 0, isize = zipPathAscii.length(); i < isize; ++i) { if (zipPathAscii[i] == '\\') { zipPathAscii[i] = '/'; } } int open = zipOpenNewFileInZip(m_ZipFile, zipPathAscii.c_str(), nullptr, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION); if (open != ZIP_OK) { return false; } bool result = true; if (filePath) { HANDLE file = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (file == INVALID_HANDLE_VALUE) { result = false; } else { do { const DWORD bufferSize = 16 * 1024; BYTE buffer[bufferSize]; DWORD readSize; if (!ReadFile(file, buffer, bufferSize, &readSize, nullptr)) { result = false; } else if (readSize != 0) { result = zipWriteInFileInZip(m_ZipFile, buffer, (UINT)readSize) == ZIP_OK; } else { // EOF break; } } while (result); CloseHandle(file); } } else { // Directory entry, so nothing needs to be written. } return zipCloseFileInZip(m_ZipFile) == ZIP_OK && result; } bool DialogPackage::AddFolderToPackage(const std::wstring& path, std::wstring base, const WCHAR* zipPrefix) { std::wstring currentPath = path + base; currentPath += L'*'; WIN32_FIND_DATA fd; HANDLE hFind = FindFirstFileEx( currentPath.c_str(), FindExInfoStandard, &fd, FindExSearchNameMatch, nullptr, 0); if (hFind == INVALID_HANDLE_VALUE) { return false; } currentPath.pop_back(); // Remove * bool result = true; bool filesAdded = false; std::list folders; do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { // Ignore hidden files and folders continue; } if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!(fd.cFileName[0] == L'.' && (!fd.cFileName[1] || fd.cFileName[1] == L'.' && !fd.cFileName[2]))) { folders.push_back(fd.cFileName); } } else { std::wstring filePath = currentPath + fd.cFileName; std::wstring zipPath = zipPrefix; zipPath.append(filePath, path.length(), filePath.length() - path.length()); result = AddFileToPackage(filePath.c_str(), zipPath.c_str()); if (!result) { std::wstring error = L"Error adding file:\n"; error += filePath; error += L"\n\nClick OK to close Packager."; MessageBox(m_Window, error.c_str(), L"Rainmeter Skin Packager", MB_OK | MB_ICONERROR); break; } filesAdded = true; } } while (FindNextFile(hFind, &fd)); FindClose(hFind); if (result) { if (!filesAdded && folders.empty()) { // Add directory entry if folder is empty. std::wstring zipPath = zipPrefix; zipPath.append(currentPath, path.length(), currentPath.length() - path.length()); AddFileToPackage(nullptr, zipPath.c_str()); } std::list::const_iterator iter = folders.begin(); for ( ; iter != folders.end(); ++iter) { std::wstring newBase = base + (*iter); newBase += L'\\'; result = AddFolderToPackage(path, newBase, zipPrefix); if (!result) break; } } return result; } void DialogPackage::ShowHelp() { std::wstring url = L"http://rainmeter.net/cms/UsingApplication-SkinPackager"; if (revision_beta) { url += L"_beta"; } ShellExecute(m_Window, L"open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL); } std::wstring DialogPackage::SelectFolder(HWND parent, const std::wstring& existingPath) { LPCWSTR dialog = MAKEINTRESOURCE(IDD_PACKAGESELECTFOLDER_DIALOG); std::wstring folder = existingPath; if (DialogBoxParam(GetModuleHandle(nullptr), dialog, parent, SelectFolderDlgProc, (LPARAM)&folder) != 1) { folder.clear(); } return folder; } INT_PTR CALLBACK DialogPackage::SelectFolderDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { EnableThemeDialogTexture(hWnd, ETDT_ENABLETAB); c_Dialog->SetDialogFont(hWnd); std::wstring* existingPath = (std::wstring*)lParam; SetWindowLongPtr(hWnd, GWLP_USERDATA, lParam); *existingPath += L'*'; WIN32_FIND_DATA fd; HANDLE hFind = FindFirstFileEx(existingPath->c_str(), FindExInfoStandard, &fd, FindExSearchNameMatch, nullptr, 0); existingPath->pop_back(); if (hFind != INVALID_HANDLE_VALUE) { const WCHAR* folder = PathFindFileName(existingPath->c_str()); HWND item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_EXISTING_RADIO); std::wstring text = L"Add folder from "; text.append(folder, wcslen(folder) - 1); text += L':'; SetWindowText(item, text.c_str()); Button_SetCheck(item, BST_CHECKED); item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_EXISTING_COMBO); do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && !(fd.cFileName[0] == L'.' && (!fd.cFileName[1] || fd.cFileName[1] == L'.' && !fd.cFileName[2])) && wcscmp(fd.cFileName, L"Backup") != 0 && wcscmp(fd.cFileName, L"@Backup") != 0) { ComboBox_InsertString(item, -1, fd.cFileName); } } while (FindNextFile(hFind, &fd)); ComboBox_SetCurSel(item, 0); FindClose(hFind); } } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_PACKAGESELECTFOLDER_EXISTING_RADIO: { HWND item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_EXISTING_COMBO); EnableWindow(item, TRUE); int sel = ComboBox_GetCurSel(item); item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT); EnableWindow(item, FALSE); item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_CUSTOMBROWSE_BUTTON); EnableWindow(item, FALSE); item = GetDlgItem(hWnd, IDOK); EnableWindow(item, sel != -1); } break; case IDC_PACKAGESELECTFOLDER_CUSTOM_RADIO: { HWND item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_EXISTING_COMBO); EnableWindow(item, FALSE); item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT); EnableWindow(item, TRUE); item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_CUSTOMBROWSE_BUTTON); EnableWindow(item, TRUE); SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT, EN_CHANGE), 0); } break; case IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT: if (HIWORD(wParam) == EN_CHANGE) { WCHAR buffer[MAX_PATH]; int len = Edit_GetText((HWND)lParam, buffer, MAX_PATH); // Disable Add button if invalid directory DWORD attributes = GetFileAttributes(buffer); BOOL state = (attributes != INVALID_FILE_ATTRIBUTES && attributes & FILE_ATTRIBUTE_DIRECTORY); EnableWindow(GetDlgItem(hWnd, IDOK), state); } break; case IDC_PACKAGESELECTFOLDER_CUSTOMBROWSE_BUTTON: { WCHAR buffer[MAX_PATH]; BROWSEINFO bi = {0}; bi.hwndOwner = hWnd; bi.ulFlags = BIF_USENEWUI | BIF_NONEWFOLDERBUTTON | BIF_RETURNONLYFSDIRS; PIDLIST_ABSOLUTE pidl = SHBrowseForFolder(&bi); if (pidl && SHGetPathFromIDList(pidl, buffer)) { HWND item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT); SetWindowText(item, buffer); CoTaskMemFree(pidl); } } break; case IDOK: { WCHAR buffer[MAX_PATH]; HWND item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_EXISTING_RADIO); bool existing = Button_GetCheck(item) == BST_CHECKED; item = GetDlgItem(hWnd, existing ? IDC_PACKAGESELECTFOLDER_EXISTING_COMBO : IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT); GetWindowText(item, buffer, _countof(buffer)); std::wstring* result = (std::wstring*)GetWindowLongPtr(hWnd, GWLP_USERDATA); if (existing) { *result += buffer; } else { *result = buffer; } *result += L'\\'; EndDialog(hWnd, 1); } } break; case WM_CLOSE: EndDialog(hWnd, 0); break; default: return FALSE; } return TRUE; } std::pair DialogPackage::SelectPlugin(HWND parent) { LPCWSTR dialog = MAKEINTRESOURCE(IDD_PACKAGESELECTPLUGIN_DIALOG); std::pair plugins; if (DialogBoxParam(GetModuleHandle(nullptr), dialog, parent, SelectPluginDlgProc, (LPARAM)&plugins) != 1) { plugins.first.clear(); plugins.second.clear(); } return plugins; } INT_PTR CALLBACK DialogPackage::SelectPluginDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { EnableThemeDialogTexture(hWnd, ETDT_ENABLETAB); c_Dialog->SetDialogFont(hWnd); auto plugins = (std::pair*)lParam; SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)plugins); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_PACKAGESELECTPLUGIN_32BITBROWSE_BUTTON: case IDC_PACKAGESELECTPLUGIN_64BITBROWSE_BUTTON: { WCHAR buffer[MAX_PATH]; buffer[0] = L'\0'; OPENFILENAME ofn = { sizeof(OPENFILENAME) }; ofn.Flags = OFN_FILEMUSTEXIST; ofn.lpstrFilter = L"Plugins (.dll)\0*.dll"; ofn.lpstrTitle = L"Select plugin file"; ofn.lpstrDefExt = L"dll"; ofn.nFilterIndex = 0; ofn.lpstrFile = buffer; ofn.nMaxFile = _countof(buffer); ofn.hwndOwner = c_Dialog->GetWindow(); if (!GetOpenFileName(&ofn)) { break; } bool x32 = LOWORD(wParam) == IDC_PACKAGESELECTPLUGIN_32BITBROWSE_BUTTON; LOADED_IMAGE* loadedImage = ImageLoad(ConvertToAscii(buffer).c_str(), nullptr); if (loadedImage) { WORD machine = loadedImage->FileHeader->FileHeader.Machine; ImageUnload(loadedImage); if ((x32 && machine == IMAGE_FILE_MACHINE_I386) || (!x32 && machine == IMAGE_FILE_MACHINE_AMD64)) { // Check if same name as other DLL auto plugins = (std::pair*)GetWindowLongPtr(hWnd, GWLP_USERDATA); const WCHAR* otherName = PathFindFileName(x32 ? plugins->second.c_str() : plugins->first.c_str()); if (*otherName && _wcsicmp(otherName, PathFindFileName(buffer)) != 0) { MessageBox(hWnd, L"Plugins must have same name.", L"Rainmeter Skin Packager", MB_OK | MB_TOPMOST); break; } PathSetDlgItemPath(hWnd, x32 ? IDC_PACKAGESELECTPLUGIN_32BIT_EDIT : IDC_PACKAGESELECTPLUGIN_64BIT_EDIT, buffer); (x32 ? plugins->first : plugins->second) = buffer; if (!plugins->first.empty() && !plugins->second.empty()) { // Enable Add button if both plugins have been selected EnableWindow(GetDlgItem(hWnd, IDOK), TRUE); } break; } } MessageBox(hWnd, L"Invalid plugin.", L"Rainmeter Skin Packager", MB_OK | MB_TOPMOST); } break; case IDOK: EndDialog(hWnd, 1); break; } break; case WM_CLOSE: EndDialog(hWnd, 0); break; default: return FALSE; } return TRUE; } // ----------------------------------------------------------------------------------------------- // // Info tab // // ----------------------------------------------------------------------------------------------- DialogPackage::TabInfo::TabInfo(HWND wnd) : Tab(GetModuleHandle(nullptr), wnd, IDD_PACKAGEINFO_TAB, DlgProc) { } void DialogPackage::TabInfo::Initialize() { m_Initialized = true; HWND item = GetDlgItem(m_Window, IDC_INSTALLTAB_NAME_TEXT); Edit_SetCueBannerText(item, L"..."); item = GetDlgItem(m_Window, IDC_INSTALLTAB_AUTHOR_TEXT); Edit_SetCueBannerText(item, L"..."); item = GetDlgItem(m_Window, IDC_INSTALLTAB_VERSION_TEXT); Edit_SetCueBannerText(item, L"..."); item = GetDlgItem(m_Window, IDC_PACKAGEINFO_COMPONENTS_LIST); DWORD extendedFlags = LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT; if (GetOSPlatform() >= OSPLATFORM_VISTA) { extendedFlags |= LVS_EX_DOUBLEBUFFER; SetWindowTheme(item, L"explorer", nullptr); } ListView_EnableGroupView(item, TRUE); ListView_SetExtendedListViewStyleEx(item, 0, extendedFlags); // Add columns LVCOLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvc.fmt = LVCFMT_LEFT; lvc.iSubItem = 0; lvc.cx = 252; lvc.pszText = L"Name"; ListView_InsertColumn(item, 0, &lvc); // Add groups LVGROUP lvg; lvg.cbSize = sizeof(LVGROUP); lvg.mask = LVGF_HEADER | LVGF_GROUPID | LVGF_STATE; lvg.state = (GetOSPlatform() >= OSPLATFORM_VISTA) ? LVGS_COLLAPSIBLE : LVGS_NORMAL; lvg.iGroupId = 0; lvg.pszHeader = L"Skin"; ListView_InsertGroup(item, -1, &lvg); lvg.iGroupId = 1; lvg.pszHeader = L"Layouts"; ListView_InsertGroup(item, -1, &lvg); lvg.iGroupId = 2; lvg.pszHeader = L"Plugins"; ListView_InsertGroup(item, -1, &lvg); } INT_PTR CALLBACK DialogPackage::TabInfo::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: return c_Dialog->m_TabInfo.OnCommand(wParam, lParam); case WM_NOTIFY: return c_Dialog->m_TabInfo.OnNotify(wParam, lParam); } return FALSE; } INT_PTR DialogPackage::TabInfo::OnCommand(WPARAM wParam, LPARAM lParam) { switch (LOWORD(wParam)) { case IDC_PACKAGEINFO_ADDSKIN_BUTTON: { c_Dialog->m_SkinFolder.second = SelectFolder(m_Window, g_Data.skinsPath); if (!c_Dialog->m_SkinFolder.second.empty()) { c_Dialog->m_SkinFolder.first = PathFindFileName(c_Dialog->m_SkinFolder.second.c_str()); c_Dialog->m_SkinFolder.first.pop_back(); // Remove slash HWND item = GetDlgItem(m_Window, IDC_PACKAGEINFO_COMPONENTS_LIST); LVITEM lvi; lvi.mask = LVIF_TEXT | LVIF_GROUPID; lvi.iItem = 1; lvi.iSubItem = 0; lvi.iGroupId = 0; lvi.pszText = (WCHAR*)c_Dialog->m_SkinFolder.first.c_str(); ListView_InsertItem(item, &lvi); EnableWindow((HWND)lParam, FALSE); c_Dialog->SetNextButtonState(); } } break; case IDC_PACKAGEINFO_ADDTHEME_BUTTON: { std::wstring folder = SelectFolder(m_Window, g_Data.settingsPath + L"Layouts\\"); if (!folder.empty()) { std::wstring name = PathFindFileName(folder.c_str()); name.pop_back(); // Remove slash if (c_Dialog->m_LayoutFolders.insert(std::make_pair(name, folder)).second) { HWND item = GetDlgItem(m_Window, IDC_PACKAGEINFO_COMPONENTS_LIST); LVITEM lvi; lvi.mask = LVIF_TEXT | LVIF_GROUPID; lvi.iItem = c_Dialog->m_LayoutFolders.size() + 1; lvi.iSubItem = 0; lvi.iGroupId = 1; lvi.pszText = (WCHAR*)name.c_str(); ListView_InsertItem(item, &lvi); } } } break; case IDC_PACKAGEINFO_ADDPLUGIN_BUTTON: { std::pair plugins = SelectPlugin(m_Window); std::wstring name = PathFindFileName(plugins.first.c_str()); if (!name.empty() && c_Dialog->m_PluginFolders.insert(std::make_pair(name, plugins)).second) { HWND item = GetDlgItem(m_Window, IDC_PACKAGEINFO_COMPONENTS_LIST); LVITEM lvi; lvi.mask = LVIF_TEXT | LVIF_GROUPID; lvi.iItem = c_Dialog->m_PluginFolders.size() + 1; lvi.iSubItem = 0; lvi.iGroupId = 2; lvi.pszText = (WCHAR*)name.c_str(); ListView_InsertItem(item, &lvi); } } break; case IDC_PACKAGEINFO_REMOVE_BUTTON: { HWND item = GetDlgItem(m_Window, IDC_PACKAGEINFO_COMPONENTS_LIST); int sel = ListView_GetNextItem(item, -1, LVNI_FOCUSED | LVNI_SELECTED); if (sel != -1) { WCHAR buffer[MAX_PATH]; // Remove unchecked items from the component sets LVITEM lvi; lvi.mask = LVIF_GROUPID | LVIF_TEXT; lvi.iSubItem = 0; lvi.iItem = sel; lvi.pszText = buffer; lvi.cchTextMax = _countof(buffer); ListView_GetItem(item, &lvi); ListView_DeleteItem(item, sel); const std::wstring name = buffer; switch (lvi.iGroupId) { case 0: { item = GetDlgItem(m_Window, IDC_PACKAGEINFO_ADDSKIN_BUTTON); EnableWindow(item, TRUE); c_Dialog->m_SkinFolder.first.clear(); c_Dialog->m_SkinFolder.second.clear(); c_Dialog->SetNextButtonState(); } break; case 1: c_Dialog->m_LayoutFolders.erase(c_Dialog->m_LayoutFolders.find(name)); break; case 2: c_Dialog->m_PluginFolders.erase(c_Dialog->m_PluginFolders.find(name)); break; } } } break; case IDC_PACKAGEINFO_NAME_EDIT: case IDC_PACKAGEINFO_AUTHOR_EDIT: case IDC_PACKAGEINFO_VERSION_EDIT: if (HIWORD(wParam) == EN_CHANGE) { WCHAR buffer[64]; int len = GetWindowText((HWND)lParam, buffer, _countof(buffer)); if (LOWORD(wParam) == IDC_PACKAGEINFO_NAME_EDIT) { c_Dialog->m_Name.assign(buffer, len); } else if (LOWORD(wParam) == IDC_PACKAGEINFO_AUTHOR_EDIT) { c_Dialog->m_Author.assign(buffer, len); } else // if (LOWORD(wParam) == IDC_PACKAGEINFO_VERSION_EDIT) { c_Dialog->m_Version.assign(buffer, len); } c_Dialog->SetNextButtonState(); } break; default: return FALSE; } return TRUE; } INT_PTR DialogPackage::TabInfo::OnNotify(WPARAM wParam, LPARAM lParam) { LPNMHDR nm = (LPNMHDR)lParam; switch (nm->code) { case LVN_ITEMCHANGED: { NMLISTVIEW* nmlv = (NMLISTVIEW*)lParam; if (nm->idFrom == IDC_PACKAGEINFO_COMPONENTS_LIST) { BOOL selected = (nmlv->uNewState & LVIS_SELECTED); HWND item = GetDlgItem(m_Window, IDC_PACKAGEINFO_REMOVE_BUTTON); EnableWindow(item, selected); } } break; case NM_CLICK: { if (nm->idFrom == IDC_PACKAGEINFO_WHATIS_LINK) { c_Dialog->ShowHelp(); } } break; default: return FALSE; } return TRUE; } // ----------------------------------------------------------------------------------------------- // // Options tab // // ----------------------------------------------------------------------------------------------- DialogPackage::TabOptions::TabOptions(HWND wnd) : Tab(GetModuleHandle(nullptr), wnd, IDD_PACKAGEOPTIONS_TAB, DlgProc) { } void DialogPackage::TabOptions::Initialize() { m_Initialized = true; WCHAR buffer[MAX_PATH]; SHGetFolderPath(nullptr, CSIDL_DESKTOPDIRECTORY, nullptr, SHGFP_TYPE_CURRENT, buffer); c_Dialog->m_TargetFile = buffer; c_Dialog->m_TargetFile += L'\\'; int pos = (int)c_Dialog->m_TargetFile.length() + 1; c_Dialog->m_TargetFile += c_Dialog->m_Name; c_Dialog->m_TargetFile += L'_'; c_Dialog->m_TargetFile += c_Dialog->m_Version; // Escape reserved chars for (int i = pos, isize = (int)c_Dialog->m_TargetFile.length(); i < isize; ++i) { if (wcschr(L"\\/:*?\"<>|", c_Dialog->m_TargetFile[i])) { c_Dialog->m_TargetFile[i] = L'_'; } } c_Dialog->m_TargetFile += L".rmskin"; HWND item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_FILE_EDIT); SetWindowText(item,c_Dialog->m_TargetFile.c_str()); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADTHEME_RADIO); if (c_Dialog->m_LayoutFolders.empty()) { EnableWindow(item, FALSE); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_DONOTHING_RADIO); Button_SetCheck(item, BST_CHECKED); } else { c_Dialog->m_LoadLayout = true; c_Dialog->m_Load = (*c_Dialog->m_LayoutFolders.cbegin()).first; Button_SetCheck(item, BST_CHECKED); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADTHEME_COMBO); ShowWindow(item, SW_SHOWNORMAL); for (auto iter = c_Dialog->m_LayoutFolders.cbegin(); iter != c_Dialog->m_LayoutFolders.cend(); ++iter) { ComboBox_AddString(item, (*iter).first.c_str()); } ComboBox_SetCurSel(item, 0); } item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADSKIN_EDIT); Edit_SetCueBannerText(item, L"Select skin"); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_RAINMETERVERSION_EDIT); _snwprintf_s(buffer, _TRUNCATE, L"%s.%i", APPVERSION, revision_number); SetWindowText(item, buffer); c_Dialog->m_MinimumRainmeter = buffer; item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_WINDOWSVERSION_COMBO); ComboBox_AddString(item, L"XP"); ComboBox_AddString(item, L"Vista"); ComboBox_AddString(item, L"7"); ComboBox_SetCurSel(item, 0); c_Dialog->m_MinimumWindows = g_OsNameVersions[0].version; } INT_PTR CALLBACK DialogPackage::TabOptions::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: return c_Dialog->m_TabOptions.OnCommand(wParam, lParam); } return FALSE; } INT_PTR DialogPackage::TabOptions::OnCommand(WPARAM wParam, LPARAM lParam) { switch (LOWORD(wParam)) { case IDC_PACKAGEOPTIONS_FILEBROWSE_BUTTON: { WCHAR buffer[MAX_PATH]; HWND item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_FILE_EDIT); GetWindowText(item, buffer, _countof(buffer)); OPENFILENAME ofn = { sizeof(OPENFILENAME) }; ofn.lpstrFilter = L"Rainmeter skin package (.rmskin)\0*.rmskin"; ofn.lpstrTitle = L"Select Rainmeter skin package"; ofn.lpstrDefExt = L"dll"; ofn.lpstrFile = buffer; ofn.nMaxFile = _countof(buffer); ofn.hwndOwner = c_Dialog->GetWindow(); if (GetOpenFileName(&ofn)) { c_Dialog->m_TargetFile = buffer; SetWindowText(item, buffer); } } break; case IDC_PACKAGEOPTIONS_DONOTHING_RADIO: { HWND item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADSKIN_EDIT); ShowWindow(item, SW_HIDE); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADSKINBROWSE_BUTTON); ShowWindow(item, SW_HIDE); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADTHEME_COMBO); ShowWindow(item, SW_HIDE); c_Dialog->m_Load.clear(); } break; case IDC_PACKAGEOPTIONS_LOADSKIN_RADIO: { HWND item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADSKIN_EDIT); ShowWindow(item, SW_SHOWNORMAL); WCHAR buffer[MAX_PATH]; GetWindowText(item, buffer, _countof(buffer)); c_Dialog->m_Load = buffer; c_Dialog->m_LoadLayout = false; item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADSKINBROWSE_BUTTON); ShowWindow(item, SW_SHOWNORMAL); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADTHEME_COMBO); ShowWindow(item, SW_HIDE); } break; case IDC_PACKAGEOPTIONS_LOADTHEME_RADIO: { HWND item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADSKIN_EDIT); ShowWindow(item, SW_HIDE); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADSKINBROWSE_BUTTON); ShowWindow(item, SW_HIDE); item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADTHEME_COMBO); ShowWindow(item, SW_SHOWNORMAL); WCHAR buffer[MAX_PATH]; GetWindowText(item, buffer, _countof(buffer)); c_Dialog->m_Load = buffer; c_Dialog->m_LoadLayout = true; } break; case IDC_PACKAGEOPTIONS_LOADSKINBROWSE_BUTTON: { WCHAR buffer[MAX_PATH]; HWND item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADSKIN_EDIT); GetWindowText(item, buffer, _countof(buffer)); OPENFILENAME ofn = { sizeof(OPENFILENAME) }; ofn.Flags = OFN_FILEMUSTEXIST; ofn.FlagsEx = OFN_EX_NOPLACESBAR; ofn.lpstrFilter = L"Rainmeter skin file (.ini)\0*.ini"; ofn.lpstrTitle = L"Select Rainmeter skin file"; ofn.lpstrDefExt = L"ini"; ofn.lpstrFile = buffer; ofn.nMaxFile = _countof(buffer); ofn.lpstrInitialDir = c_Dialog->m_SkinFolder.second.c_str(); ofn.hwndOwner = c_Dialog->GetWindow(); if (GetOpenFileName(&ofn)) { // Make sure user didn't browse to some random folder if (_wcsnicmp(ofn.lpstrInitialDir, buffer, c_Dialog->m_SkinFolder.second.length()) == 0) { // Skip everything before actual skin folder const WCHAR* folderPath = buffer + c_Dialog->m_SkinFolder.second.length() - c_Dialog->m_SkinFolder.first.length() - 1; SetWindowText(item, folderPath); c_Dialog->m_Load = folderPath; } } } break; case IDC_PACKAGEOPTIONS_RAINMETERVERSION_EDIT: if (HIWORD(wParam) == EN_CHANGE) { WCHAR buffer[32]; GetWindowText((HWND)lParam, buffer, _countof(buffer)); // Get caret position DWORD sel = Edit_GetSel((HWND)lParam); // Only allow numbers and period WCHAR* version = buffer; while (*version) { if (iswdigit(*version) || *version == L'.') { ++version; } else { *version = L'\0'; SetWindowText((HWND)lParam, buffer); // Reset caret position Edit_SetSel((HWND)lParam, LOWORD(sel), HIWORD(sel)); break; } } c_Dialog->m_MinimumRainmeter = buffer; } break; case IDC_PACKAGEOPTIONS_WINDOWSVERSION_COMBO: if (HIWORD(wParam) == CBN_SELCHANGE) { int sel = ComboBox_GetCurSel((HWND)lParam); c_Dialog->m_MinimumWindows = g_OsNameVersions[sel].version; } break; default: return FALSE; } return TRUE; } // ----------------------------------------------------------------------------------------------- // // Advanced tab // // ----------------------------------------------------------------------------------------------- DialogPackage::TabAdvanced::TabAdvanced(HWND wnd) : Tab(GetModuleHandle(nullptr), wnd, IDD_PACKAGEADVANCED_TAB, DlgProc) { } void DialogPackage::TabAdvanced::Initialize() { m_Initialized = true; } INT_PTR CALLBACK DialogPackage::TabAdvanced::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: return c_Dialog->m_TabAdvanced.OnCommand(wParam, lParam); case WM_NOTIFY: return c_Dialog->m_TabAdvanced.OnNotify(wParam, lParam); } return FALSE; } INT_PTR DialogPackage::TabAdvanced::OnCommand(WPARAM wParam, LPARAM lParam) { switch (LOWORD(wParam)) { case IDC_PACKAGEADVANCED_HEADERROWSE_BUTTON: { WCHAR buffer[MAX_PATH]; HWND item = GetDlgItem(m_Window, IDC_PACKAGEADVANCED_HEADER_EDIT); GetWindowText(item, buffer, _countof(buffer)); OPENFILENAME ofn = { sizeof(OPENFILENAME) }; ofn.Flags = OFN_FILEMUSTEXIST; ofn.lpstrFilter = L"Bitmap file (.bmp)\0*.bmp"; ofn.lpstrTitle = L"Select header image"; ofn.lpstrDefExt = L"bmp"; ofn.lpstrFile = buffer; ofn.nMaxFile = _countof(buffer); ofn.hwndOwner = c_Dialog->GetWindow(); if (GetOpenFileName(&ofn)) { c_Dialog->m_HeaderFile = buffer; SetWindowText(item, buffer); } } break; case IDC_PACKAGEADVANCED_VARIABLEFILES_EDIT: if (HIWORD(wParam) == EN_CHANGE) { int length = GetWindowTextLength((HWND)lParam); c_Dialog->m_VariableFiles.resize(length); GetWindowText((HWND)lParam, &c_Dialog->m_VariableFiles[0], length + 1); } break; case IDC_PACKAGEADVANCED_MERGESKINS_CHECK: c_Dialog->m_MergeSkins = !c_Dialog->m_MergeSkins; break; default: return FALSE; } return TRUE; } INT_PTR DialogPackage::TabAdvanced::OnNotify(WPARAM wParam, LPARAM lParam) { LPNMHDR nm = (LPNMHDR)lParam; switch (nm->code) { case NM_CLICK: { if (nm->idFrom == IDC_PACKAGEADVANCED_HELP_LINK) { c_Dialog->ShowHelp(); } } break; default: return FALSE; } return TRUE; }