From 3bc9bffbf055648ea36d90912f3372c8dc75e5ef Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Sat, 16 Jun 2012 21:56:46 +0300 Subject: [PATCH] Added Packager --- SkinInstaller/Application.cpp | 20 +- SkinInstaller/Application.h | 6 + SkinInstaller/DialogInstall.h | 2 +- SkinInstaller/DialogPackage.cpp | 1256 +++++++++++++++++++ SkinInstaller/DialogPackage.h | 123 ++ SkinInstaller/SkinInstaller.rc | 103 +- SkinInstaller/SkinInstaller.vcxproj | 12 +- SkinInstaller/SkinInstaller.vcxproj.filters | 12 +- SkinInstaller/StdAfx.h | 2 + SkinInstaller/resource.h | 57 +- 10 files changed, 1550 insertions(+), 43 deletions(-) create mode 100644 SkinInstaller/DialogPackage.cpp create mode 100644 SkinInstaller/DialogPackage.h diff --git a/SkinInstaller/Application.cpp b/SkinInstaller/Application.cpp index 388bbeea..56a50f57 100644 --- a/SkinInstaller/Application.cpp +++ b/SkinInstaller/Application.cpp @@ -17,13 +17,21 @@ */ #include "StdAfx.h" -#include "DialogBackup.h" +#include "DialogPackage.h" #include "DialogInstall.h" #include "resource.h" #include "Application.h" GlobalData g_Data; +OsNameVersion g_OsNameVersions[] = +{ + { L"XP", L"5.1" }, + { L"Vista", L"6.0" }, + { L"7", L"6.1" }, +// { L"8", L"6.2" } +}; + /* ** Entry point ** @@ -90,14 +98,14 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi else { std::wstring error = L"SkinPath not found.\nMake sure that Rainmeter has been run at least once."; - MessageBox(NULL, error.c_str(), L"Backup Rainmeter", MB_ERROR); + MessageBox(NULL, error.c_str(), L"Rainmeter Skin Installer", MB_ERROR); return 1; } } else { std::wstring error = L"Rainmeter.ini not found.\nMake sure that Rainmeter has been run at least once."; - MessageBox(NULL, error.c_str(), L"Backup Rainmeter", MB_ERROR); + MessageBox(NULL, error.c_str(), L"Rainmeter Skin Installer", MB_ERROR); return 1; } } @@ -107,7 +115,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi // Skip "/LoadTheme " lpCmdLine += 11; - if (CloseRainmeterIfActive() && *lpCmdLine) + if (*lpCmdLine && CloseRainmeterIfActive()) { CDialogInstall::LoadTheme(lpCmdLine, true); @@ -123,9 +131,9 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi return 0; } - else if (wcscmp(lpCmdLine, L"/BACKUP") == 0) + else if (wcscmp(lpCmdLine, L"/Packager") == 0) { - CDialogBackup::Create(hInstance, lpCmdLine); + CDialogPackage::Create(hInstance, lpCmdLine); } else { diff --git a/SkinInstaller/Application.h b/SkinInstaller/Application.h index f130739a..c4b30376 100644 --- a/SkinInstaller/Application.h +++ b/SkinInstaller/Application.h @@ -40,6 +40,12 @@ struct GlobalData std::wstring iniFile; }; +struct OsNameVersion +{ + const WCHAR* name; + const WCHAR* version; +}; + enum OSPLATFORM { OSPLATFORM_UNKNOWN = 0, diff --git a/SkinInstaller/DialogInstall.h b/SkinInstaller/DialogInstall.h index 26c46b1d..031d77a6 100644 --- a/SkinInstaller/DialogInstall.h +++ b/SkinInstaller/DialogInstall.h @@ -41,7 +41,7 @@ protected: virtual CTab& GetActiveTab(); private: - friend class CDialogBackup; + friend class CDialogPackage; class CTabInstall : public CTab { diff --git a/SkinInstaller/DialogPackage.cpp b/SkinInstaller/DialogPackage.cpp new file mode 100644 index 00000000..89069101 --- /dev/null +++ b/SkinInstaller/DialogPackage.cpp @@ -0,0 +1,1256 @@ +/* + Copyright (C) 2011 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[]; + +CDialogPackage* CDialogPackage::c_Dialog = NULL; + +CDialogPackage::CDialogPackage(HWND wnd) : CDialog(wnd), + m_TabInfo(wnd), + m_TabOptions(wnd), + m_TabAdvanced(wnd), + m_LoadTheme(false), + m_MergeSkins(false), + m_PackagerThread(), + m_ZipFile() +{ +} + +CDialogPackage::~CDialogPackage() +{ +} + +void CDialogPackage::Create(HINSTANCE hInstance, LPWSTR lpCmdLine) +{ + HANDLE hMutex; + if (IsRunning(L"RainmeterBackup", &hMutex)) + { + HWND hwnd = FindWindow(L"#32770", L"Rainmeter Packager"); + SetForegroundWindow(hwnd); + } + else + { + DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PACKAGE_DIALOG), NULL, (DLGPROC)DlgProc, (LPARAM)lpCmdLine); + ReleaseMutex(hMutex); + } +} + +CDialog::CTab& CDialogPackage::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 CDialogPackage::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (!c_Dialog) + { + if (uMsg == WM_INITDIALOG) + { + c_Dialog = new CDialogPackage(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: + EndDialog(hWnd, 0); + return TRUE; + + case WM_DESTROY: + delete c_Dialog; + c_Dialog = NULL; + return FALSE; + } + } + + return FALSE; +} + +INT_PTR CDialogPackage::OnInitDialog(WPARAM wParam, LPARAM lParam) +{ + HICON hIcon = (HICON)LoadImage(GetModuleHandle(NULL), 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 CDialogPackage::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_TabInfo.GetWindow(), IDC_PACKAGE_INPROGRESS_TEXT); + //ShowWindow(item, SW_SHOWNORMAL); + + //item = GetDlgItem(m_TabInfo.GetWindow(), IDC_PACKAGE_PROGRESS); + //ShowWindow(item, SW_SHOWNORMAL); + //SendMessage(item, PBM_SETMARQUEE, (WPARAM)TRUE, 0); + + m_PackagerThread = (HANDLE)_beginthreadex(NULL, 0, PackagerThreadProc, this, 0, NULL); + if (!m_PackagerThread) + { + MessageBox(m_Window, L"Unknown error.", L"Rainmeter Packager", MB_ERROR); + EndDialog(m_Window, 0); + } + } + break; + + case IDCLOSE: + if (!m_PackagerThread) + { + EndDialog(m_Window, 0); + } + break; + + default: + return FALSE; + } + + return TRUE; +} + +INT_PTR CDialogPackage::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 CDialogPackage::SetNextButtonState() +{ + BOOL state = !(m_Name.empty() || m_Author.empty() || m_Version.empty() || m_SkinFolder.second.empty()); + EnableWindow(GetDlgItem(m_Window, IDC_PACKAGE_NEXT_BUTTON), state); +} + +bool CDialogPackage::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_LoadTheme ? L"Theme" : 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(), 0); + + auto cleanup = [&]()->bool + { + zipClose(m_ZipFile, NULL); + return false; + }; + + if (!m_ZipFile || + !AddFileToPackage(ConvertToAscii(tempFile).c_str(), "RMSKIN.ini") || + !(!c_Dialog->m_HeaderFile.empty() && AddFileToPackage(ConvertToAscii(c_Dialog->m_HeaderFile.c_str()).c_str(), "RMSKIN.bmp"))) + { + std::wstring error = L"Unable to create package."; + error += L"\n\nClick OK to close Packager."; + MessageBox(m_Window, error.c_str(), L"Rainmeter Packager", MB_OK | MB_TOPMOST); + DeleteFile(tempFile); + return cleanup(); + } + + // Add skin + { + std::string zipPrefix = "Skins\\"; + zipPrefix += ConvertToAscii(m_SkinFolder.first.c_str()); + if (!AddFolderToPackage(m_SkinFolder.second, L"", zipPrefix.c_str(), true)) + { + return cleanup(); + } + } + + // Add themes + for (auto iter = m_ThemeFolders.cbegin(); iter != m_ThemeFolders.cend(); ++iter) + { + std::string realPath = ConvertToAscii((*iter).second.c_str()); + realPath += "Rainmeter.thm"; + std::string zipPath = "Themes\\"; + zipPath += ConvertToAscii((*iter).first.c_str()); + zipPath += "\\Rainmeter.thm"; + if (!AddFileToPackage(realPath.c_str(), zipPath.c_str())) + { + std::wstring error = L"Error adding theme '"; + error += (*iter).first; + error += L"'."; + error += L"\n\nClick OK to close Packager."; + MessageBox(m_Window, error.c_str(), L"Rainmeter Packager", MB_OK | MB_TOPMOST); + 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) + { + std::string realPath = ConvertToAscii((i == 0) ? (*iter).second.first.c_str() : (*iter).second.second.c_str()); + std::string zipPath = (i == 0) ? "Plugins\\32bit\\" : "Plugins\\64bit\\"; + zipPath += ConvertToAscii((*iter).first.c_str()); + 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 Packager", MB_OK | MB_TOPMOST); + return cleanup(); + } + } + } + + // Add footer + FILE* file; + if (zipClose(m_ZipFile, NULL) == ZIP_OK && + (file = _wfopen(m_TargetFile.c_str(), L"r+b")) != NULL) + { + fseek(file, 0, SEEK_END); + CDialogInstall::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 Packager", MB_OK | MB_TOPMOST); + return false; + } + + return true; +} + +unsigned __stdcall CDialogPackage::PackagerThreadProc(void* pParam) +{ + CDialogPackage* dialog = (CDialogPackage*)pParam; + + if (dialog->CreatePackage()) + { + // Stop the progress bar +// HWND item = GetDlgItem(dialog->m_Window, IDC_PACKAGE_PROGRESS); +// SendMessage(item, PBM_SETMARQUEE, (WPARAM)FALSE, 0); + + FlashWindow(dialog->m_Window, TRUE); + MessageBox(dialog->m_Window, L"The .rmskin file has been successfully created.", L"Rainmeter Packager", MB_OK | MB_ICONINFORMATION); + } + else + { + DeleteFile(dialog->m_TargetFile.c_str()); + } + + EndDialog(dialog->m_Window, 0); + + return 0; +} + +bool CDialogPackage::AddFileToPackage(const char* realPath, const char* zipPath) +{ + zip_fileinfo zi = {0}; + + FILETIME ftLocal; + WIN32_FIND_DATAA ff32; + HANDLE hFind = FindFirstFileA(realPath, &ff32); + if (hFind != INVALID_HANDLE_VALUE) + { + FileTimeToLocalFileTime(&ff32.ftLastWriteTime, &ftLocal); + FileTimeToDosDateTime(&ftLocal, ((LPWORD)&zi.dosDate) + 1, ((LPWORD)&zi.dosDate) + 0); + FindClose(hFind); + } + + int err = zipOpenNewFileInZip(m_ZipFile, zipPath, &zi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + if (err != ZIP_OK) return false; + + FILE* fin = fopen(realPath, "rb"); + if (fin) + { + size_t readSize; + do + { + const size_t bufferSize = 16 * 1024; + BYTE buffer[bufferSize]; + readSize = fread(buffer, 1, bufferSize, fin); + if (readSize < bufferSize && feof(fin) == 0) + { + err = ZIP_ERRNO; + } + else if (readSize > 0) + { + err = zipWriteInFileInZip(m_ZipFile, buffer, (UINT)readSize); + if (err < 0) + { + err = ZIP_ERRNO; + } + } + } + while ((err == ZIP_OK) && (readSize > 0)); + + fclose(fin); + } + else + { + err = ZIP_ERRNO; + } + + if (zipCloseFileInZip(m_ZipFile) != ZIP_OK) return false; + + return err == ZIP_OK; +} + +bool CDialogPackage::AddFolderToPackage(const std::wstring& path, std::wstring base, const char* zipPrefix, bool recursive) +{ + std::wstring filter = path + base; + if (!base.empty()) + { + filter += L'\\'; + } + std::string asciiBase = ConvertToAscii(filter.c_str()); + filter += L'*'; + + WIN32_FIND_DATA fd; + HANDLE hFind = FindFirstFileEx( + filter.c_str(), + FindExInfoStandard, + &fd, + FindExSearchNameMatch, + NULL, + 0); + + if (hFind == INVALID_HANDLE_VALUE) return false; + + bool ret = true; + + std::list folders; + do + { + if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) + { + // Ignore hidden 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::string asciiFile = asciiBase + ConvertToAscii(fd.cFileName); + std::string zipFile = zipPrefix; + zipFile += &asciiFile[path.length()]; + + ret = AddFileToPackage(asciiFile.c_str(), zipFile.c_str()); + if (!ret) + { + std::wstring error = L"Error adding file:\n\n"; + error += path; + error += base; + error += L"\\"; + error += fd.cFileName; + MessageBox(m_Window, error.c_str(), L"Rainmeter Packager", MB_OK | MB_TOPMOST); + break; + } + } + } + while (FindNextFile(hFind, &fd)); + FindClose(hFind); + + if (recursive && ret) + { + base += L'\\'; + std::list::const_iterator iter = folders.begin(); + for ( ; iter != folders.end(); ++iter) + { + ret = AddFolderToPackage(path, base + (*iter), zipPrefix, recursive); + if (!ret) break; + } + } + + return ret; +} + +std::wstring CDialogPackage::SelectFolder(HWND parent, const std::wstring& existingPath) +{ + LPCWSTR dialog = MAKEINTRESOURCE(IDD_PACKAGESELECTFOLDER_DIALOG); + std::wstring folder = existingPath; + if (DialogBoxParam(GetModuleHandle(NULL), dialog, parent, SelectFolderDlgProc, (LPARAM)&folder) != 1) + { + folder.clear(); + } + return folder; +} + +INT_PTR CALLBACK CDialogPackage::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, NULL, 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); + item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT); + EnableWindow(item, FALSE); + item = GetDlgItem(hWnd, IDC_PACKAGESELECTFOLDER_CUSTOMBROWSE_BUTTON); + EnableWindow(item, FALSE); + } + 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, IDCLOSE), 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 IDCLOSE: + { + 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 CDialogPackage::SelectPlugin(HWND parent) +{ + LPCWSTR dialog = MAKEINTRESOURCE(IDD_PACKAGESELECTPLUGIN_DIALOG); + std::pair plugins; + if (DialogBoxParam(GetModuleHandle(NULL), dialog, parent, SelectPluginDlgProc, (LPARAM)&plugins) != 1) + { + plugins.first.clear(); + plugins.second.clear(); + } + return plugins; +} + +INT_PTR CALLBACK CDialogPackage::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.nFilterIndex = 0; + ofn.lpstrFile = buffer; + ofn.nMaxFile = _countof(buffer); + ofn.lpstrTitle = L"Select plugin file"; + ofn.lpstrDefExt = L"dll"; + + if (!GetOpenFileName(&ofn)) + { + break; + } + + bool x32 = LOWORD(wParam) == IDC_PACKAGESELECTPLUGIN_32BITBROWSE_BUTTON; + + LOADED_IMAGE* loadedImage = ImageLoad(ConvertToAscii(buffer).c_str(), NULL); + if (loadedImage) + { + if ((x32 && loadedImage->FileHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) || + (!x32 && loadedImage->FileHeader->FileHeader.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 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, IDCLOSE), TRUE); + } + break; + } + } + + MessageBox(hWnd, L"Invalid plugin.", L"Rainmeter Packager", MB_OK | MB_TOPMOST); + } + break; + + case IDCLOSE: + EndDialog(hWnd, 1); + break; + } + break; + + case WM_CLOSE: + EndDialog(hWnd, 0); + break; + + default: + return FALSE; + } + + return TRUE; +} + +// ----------------------------------------------------------------------------------------------- +// +// Info tab +// +// ----------------------------------------------------------------------------------------------- + +CDialogPackage::CTabInfo::CTabInfo(HWND wnd) : CTab(GetModuleHandle(NULL), wnd, IDD_PACKAGEINFO_TAB, DlgProc) +{ +} + +void CDialogPackage::CTabInfo::Initialize() +{ + m_Initialized = true; + + HWND item = GetDlgItem(m_Window, IDC_INSTALLTAB_NAME_TEXT); + Edit_SetCueBannerText(item, L"Specify name"); + + item = GetDlgItem(m_Window, IDC_INSTALLTAB_AUTHOR_TEXT); + Edit_SetCueBannerText(item, L"Specify author"); + + item = GetDlgItem(m_Window, IDC_INSTALLTAB_VERSION_TEXT); + Edit_SetCueBannerText(item, L"Specify version"); + + 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", NULL); + } + + 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 = 395; + 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"Themes"; + ListView_InsertGroup(item, -1, &lvg); + lvg.iGroupId = 2; + lvg.pszHeader = L"Plguins"; + ListView_InsertGroup(item, -1, &lvg); +} + +INT_PTR CALLBACK CDialogPackage::CTabInfo::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 CDialogPackage::CTabInfo::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"Themes\\"); + if (!folder.empty()) + { + std::wstring name = PathFindFileName(folder.c_str()); + name.pop_back(); // Remove slash + + if (c_Dialog->m_ThemeFolders.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_ThemeFolders.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_NAME_EDIT: + if (HIWORD(wParam) == EN_CHANGE) + { + WCHAR buffer[64]; + int len = GetWindowText((HWND)lParam, buffer, _countof(buffer)); + c_Dialog->m_Name.assign(buffer, len); + c_Dialog->SetNextButtonState(); + } + break; + + case IDC_PACKAGEINFO_AUTHOR_EDIT: + if (HIWORD(wParam) == EN_CHANGE) + { + WCHAR buffer[64]; + int len = GetWindowText((HWND)lParam, buffer, _countof(buffer)); + c_Dialog->m_Author.assign(buffer, len); + c_Dialog->SetNextButtonState(); + } + break; + + case IDC_PACKAGEINFO_VERSION_EDIT: + if (HIWORD(wParam) == EN_CHANGE) + { + WCHAR buffer[64]; + int len = GetWindowText((HWND)lParam, buffer, _countof(buffer)); + c_Dialog->m_Version .assign(buffer, len); + c_Dialog->SetNextButtonState(); + } + break; + + + default: + return FALSE; + } + + return TRUE; +} + +INT_PTR CDialogPackage::CTabInfo::OnNotify(WPARAM wParam, LPARAM lParam) +{ + LPNMHDR nm = (LPNMHDR)lParam; + switch (nm->code) + { + case LVN_GETEMPTYMARKUP: + { + NMLVEMPTYMARKUP* lvem = (NMLVEMPTYMARKUP*)lParam; + lvem->dwFlags = EMF_CENTERED; + wcscpy_s(lvem->szMarkup, L"Use the buttons below to add components to the .rmskin."); + SetWindowLongPtr(m_Window, DWLP_MSGRESULT, TRUE); + } + break; + + default: + return FALSE; + } + + return TRUE; +} + +// ----------------------------------------------------------------------------------------------- +// +// Options tab +// +// ----------------------------------------------------------------------------------------------- + +CDialogPackage::CTabOptions::CTabOptions(HWND wnd) : CTab(GetModuleHandle(NULL), wnd, IDD_PACKAGEOPTIONS_TAB, DlgProc) +{ +} + +void CDialogPackage::CTabOptions::Initialize() +{ + m_Initialized = true; + + std::wstring fileName = c_Dialog->m_Author + L'_'; + fileName += c_Dialog->m_Version; + + // Escape reserved chars + for (int i = 0, isize = (int)fileName.length(); i < isize; ++i) + { + if (wcschr(L"\\/:*?\"<>|", fileName[i])) + { + fileName[i] = L'_'; + } + } + + WCHAR buffer[MAX_PATH]; + SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, buffer); + + c_Dialog->m_TargetFile = buffer; + c_Dialog->m_TargetFile += L'\\'; + c_Dialog->m_TargetFile += fileName; + 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_ThemeFolders.empty()) + { + EnableWindow(item, FALSE); + + item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_DONOTHING_RADIO); + Button_SetCheck(item, BST_CHECKED); + } + else + { + Button_SetCheck(item, BST_CHECKED); + + item = GetDlgItem(m_Window, IDC_PACKAGEOPTIONS_LOADTHEME_COMBO); + ShowWindow(item, SW_SHOWNORMAL); + + for (auto iter = c_Dialog->m_ThemeFolders.cbegin(); iter != c_Dialog->m_ThemeFolders.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 CDialogPackage::CTabOptions::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 CDialogPackage::CTabOptions::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_LoadTheme = 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_LoadTheme = 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); + } + } + } + break; + + case IDC_PACKAGEOPTIONS_RAINMETERVERSION_EDIT: + if (HIWORD(wParam) == EN_CHANGE) + { + WCHAR buffer[32]; + GetWindowText((HWND)lParam, buffer, _countof(buffer)); + + // Get selection + 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); + break; + } + } + + // Reset selection + Edit_SetSel((HWND)lParam, LOWORD(sel), HIWORD(sel)); + + 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 +// +// ----------------------------------------------------------------------------------------------- + +CDialogPackage::CTabAdvanced::CTabAdvanced(HWND wnd) : CTab(GetModuleHandle(NULL), wnd, IDD_PACKAGEADVANCED_TAB, DlgProc) +{ +} + +void CDialogPackage::CTabAdvanced::Initialize() +{ + m_Initialized = true; +} + +INT_PTR CALLBACK CDialogPackage::CTabAdvanced::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_COMMAND: + return c_Dialog->m_TabAdvanced.OnCommand(wParam, lParam); + } + + return FALSE; +} + +INT_PTR CDialogPackage::CTabAdvanced::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); + } + break; + + case IDC_PACKAGEADVANCED_MERGESKINS_CHECK: + c_Dialog->m_MergeSkins = !c_Dialog->m_MergeSkins; + break; + + default: + return FALSE; + } + + return TRUE; +} diff --git a/SkinInstaller/DialogPackage.h b/SkinInstaller/DialogPackage.h new file mode 100644 index 00000000..9b3f71ae --- /dev/null +++ b/SkinInstaller/DialogPackage.h @@ -0,0 +1,123 @@ +/* + Copyright (C) 2011 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. +*/ + +#ifndef SKININSTALLER_DIALOGBACKUP_H_ +#define SKININSTALLER_DIALOGBACKUP_H_ + +#include +#include "zip.h" +#include "../Library/Dialog.h" + +class CDialogPackage : public CDialog +{ +public: + static void Create(HINSTANCE hInstance, LPWSTR lpCmdLine); + + static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + INT_PTR OnInitDialog(WPARAM wParam, LPARAM lParam); + INT_PTR OnCommand(WPARAM wParam, LPARAM lParam); + INT_PTR OnNotify(WPARAM wParam, LPARAM lParam); + + static CDialogPackage* c_Dialog; + +protected: + virtual CTab& GetActiveTab(); + +private: + class CTabInfo : public CTab + { + public: + CTabInfo(HWND window); + + virtual void Initialize(); + + static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + INT_PTR OnCommand(WPARAM wParam, LPARAM lParam); + INT_PTR OnNotify(WPARAM wParam, LPARAM lParam); + }; + + class CTabOptions : public CTab + { + public: + CTabOptions(HWND window); + + virtual void Initialize(); + + static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + INT_PTR OnCommand(WPARAM wParam, LPARAM lParam); + }; + + class CTabAdvanced : public CTab + { + public: + CTabAdvanced(HWND window); + + virtual void Initialize(); + + static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + INT_PTR OnCommand(WPARAM wParam, LPARAM lParam); + }; + + CDialogPackage(HWND wnd); + virtual ~CDialogPackage(); + + void SetNextButtonState(); + + bool CreatePackage(); + static unsigned __stdcall PackagerThreadProc(void* pParam); + + bool AddFileToPackage(const char* realPath, const char* zipPath); + bool AddFolderToPackage(const std::wstring& path, std::wstring base, const char* zipPrefix, bool recursive); + + static std::wstring SelectFolder(HWND parent, const std::wstring& existingPath); + static INT_PTR CALLBACK SelectFolderDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + static std::pair SelectPlugin(HWND parent); + static INT_PTR CALLBACK SelectPluginDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + CTabInfo m_TabInfo; + CTabOptions m_TabOptions; + CTabAdvanced m_TabAdvanced; + + std::wstring m_BackupTime; + + // Info tab + std::wstring m_Name; + std::wstring m_Author; + std::wstring m_Version; + std::pair m_SkinFolder; + std::map m_ThemeFolders; + std::map> m_PluginFolders; + + // Options tab + std::wstring m_TargetFile; + std::wstring m_MinimumRainmeter; + std::wstring m_MinimumWindows; + bool m_LoadTheme; + std::wstring m_Load; + + // Advanced tab + std::wstring m_HeaderFile; + std::wstring m_VariableFiles; + bool m_MergeSkins; + + HANDLE m_PackagerThread; + zipFile m_ZipFile; +}; + +#endif diff --git a/SkinInstaller/SkinInstaller.rc b/SkinInstaller/SkinInstaller.rc index a30e76da..42f9ed64 100644 --- a/SkinInstaller/SkinInstaller.rc +++ b/SkinInstaller/SkinInstaller.rc @@ -11,7 +11,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,0,0,0 + FILEVERSION FILEVER PRODUCTVERSION PRODUCTVER FILEFLAGSMASK 0x17L #ifdef _DEBUG @@ -28,7 +28,7 @@ VS_VERSION_INFO VERSIONINFO BLOCK "040904E4" { VALUE "FileDescription", "Rainmeter Skin Installer" - VALUE "FileVersion", "4.0.0.0" + VALUE "FileVersion", STRFILEVER VALUE "LegalCopyright", "© 2012 - Birunthan Mohanathas" VALUE "OriginalFilename", "SkinInstaller.exe" VALUE "ProductName", "Rainmeter" @@ -66,26 +66,103 @@ IDR_INSTALL_MENU MENU // Dialog // -IDD_BACKUP_DIALOG DIALOGEX 0, 0, 280, 120 +IDD_PACKAGE_DIALOG DIALOGEX 0, 0, 320, 290 STYLE DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Backup Rainmeter" +CAPTION "Rainmeter Packager" FONT 8, "MS Shell Dlg 2" { - CONTROL "", IDC_BACKUP_TAB, "SysTabControl32", TCS_TABS | TCS_FIXEDWIDTH, 6, 6, 268, 91 - DEFPUSHBUTTON "Start backup", IDC_BACKUP_BACKUP_BUTTON, 149, 101, 70, 14 - PUSHBUTTON "Close", IDCLOSE, 224, 101, 50, 14 + CONTROL "", IDC_PACKAGE_TAB, "SysTabControl32", TCS_TABS | TCS_FIXEDWIDTH, 6, 6, 308, 261 + DEFPUSHBUTTON "Next", IDC_PACKAGE_NEXT_BUTTON, 208, 271, 50, 14, WS_DISABLED + PUSHBUTTON "Create package", IDC_PACKAGE_CREATEPACKAGE_BUTTON, 178, 271, 80, 14, NOT WS_VISIBLE + PUSHBUTTON "Close", IDCLOSE, 263, 271, 50, 14 } -IDD_BACKUP_TABDIALOG DIALOGEX 15, 30, 250, 58 +IDD_PACKAGEINFO_TAB DIALOGEX 15, 15, 290, 242 STYLE DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS FONT 8, "MS Shell Dlg 2" { - LTEXT "A backup allows you to save a complete copy of your current Rainmeter skins and themes.\n\nThe backup will be created at:", IDC_BACKUP_DESCRIPTION_TEXT, 0, 0, 245, 39 - EDITTEXT IDC_BACKUP_FILE_TEXT, 0, 42, 220, 14, WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "...", IDC_BACKUP_BROWSE_BUTTON, 225, 42, 25, 14, WS_TABSTOP + CONTROL "Fill in the information, add a skin, and click Next to continue.\n\nFor information about .rmskin packages, check the manual.", IDC_STATIC, "SysLink", 0x0, 0, 0, 284, 39 - LTEXT "Backup in progress...", IDC_BACKUP_INPROGRESS_TEXT, 0, 0, 245, 39, NOT WS_VISIBLE - CONTROL "", IDC_BACKUP_PROGRESS, "msctls_progress32", PBS_MARQUEE | NOT WS_VISIBLE | WS_BORDER, 0, 15, 250, 11 + GROUPBOX "Information", IDC_STATIC, 0, 40, 290, 70 + LTEXT "Name:", IDC_STATIC, 6, 56, 35, 9 + EDITTEXT IDC_PACKAGEINFO_NAME_EDIT, 56, 53, 160, 14 + LTEXT "Author:", IDC_STATIC, 6, 74, 35, 9 + EDITTEXT IDC_PACKAGEINFO_AUTHOR_EDIT, 56, 71, 160, 14 + LTEXT "Version:", IDC_STATIC, 6, 92, 35, 9 + EDITTEXT IDC_PACKAGEINFO_VERSION_EDIT, 56, 89, 80, 14 + + GROUPBOX "Components", IDC_STATIC, 0, 115, 290, 127 + CONTROL "", IDC_PACKAGEINFO_COMPONENTS_LIST, "SysListView32", LVS_REPORT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP, 6, 130, 278, 86 + PUSHBUTTON "Add skin...", IDC_PACKAGEINFO_ADDSKIN_BUTTON, 6, 220, 70, 14 + PUSHBUTTON "Add theme...", IDC_PACKAGEINFO_ADDTHEME_BUTTON, 81, 220, 70, 14 + PUSHBUTTON "Add plugin...", IDC_PACKAGEINFO_ADDPLUGIN_BUTTON, 156, 220, 70, 14 +} + +IDD_PACKAGEOPTIONS_TAB DIALOGEX 15, 30, 290, 227 +STYLE DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS +FONT 8, "MS Shell Dlg 2" +{ + CONTROL "Select the install options below. For information about the options, check the manual.", IDC_STATIC, "SysLink", 0x0, 0, 0, 284, 29 + + LTEXT "Save package to:", IDC_STATIC, 0, 30, 290, 9 + EDITTEXT IDC_PACKAGEOPTIONS_FILE_EDIT, 0, 47, 260, 14, ES_READONLY | ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + PUSHBUTTON "...", IDC_PACKAGEOPTIONS_FILEBROWSE_BUTTON, 265, 47, 25, 14, WS_TABSTOP + + + GROUPBOX "After installation", IDC_STATIC, 0, 111, 290, 58 + AUTORADIOBUTTON "Do nothing", IDC_PACKAGEOPTIONS_DONOTHING_RADIO, 6, 126, 85, 9, WS_TABSTOP + AUTORADIOBUTTON "Load skin", IDC_PACKAGEOPTIONS_LOADSKIN_RADIO, 6, 139, 85, 9, WS_TABSTOP + EDITTEXT IDC_PACKAGEOPTIONS_LOADSKIN_EDIT, 96, 136, 159, 14, ES_READONLY | ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | NOT WS_VISIBLE + PUSHBUTTON "...", IDC_PACKAGEOPTIONS_LOADSKINBROWSE_BUTTON, 260, 136, 25, 14, WS_TABSTOP | NOT WS_VISIBLE + AUTORADIOBUTTON "Load theme", IDC_PACKAGEOPTIONS_LOADTHEME_RADIO, 6, 152, 85, 9, WS_TABSTOP + COMBOBOX IDC_PACKAGEOPTIONS_LOADTHEME_COMBO, 96, 149, 100, 14, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | NOT WS_VISIBLE + + GROUPBOX "Minimum requirements", IDC_STATIC, 0, 174, 290, 53 + LTEXT "Rainmeter version:", IDC_STATIC, 6, 190, 85, 9 + EDITTEXT IDC_PACKAGEOPTIONS_RAINMETERVERSION_EDIT, 96, 187, 80, 14 + LTEXT "Windows version:", IDC_STATIC, 6, 208, 85, 9 + COMBOBOX IDC_PACKAGEOPTIONS_WINDOWSVERSION_COMBO, 96, 205, 80, 14, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +} + +IDD_PACKAGEADVANCED_TAB DIALOGEX 15, 30, 290, 227 +STYLE DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS +FONT 8, "MS Shell Dlg 2" +{ + LTEXT "Header image:", IDC_STATIC, 0, 3, 85, 9 + EDITTEXT IDC_PACKAGEADVANCED_HEADER_EDIT, 90, 0, 170, 14, ES_READONLY | ES_AUTOHSCROLL | WS_TABSTOP + PUSHBUTTON "...", IDC_PACKAGEADVANCED_HEADERROWSE_BUTTON, 265, 0, 25, 14, WS_TABSTOP + LTEXT "Variable files:", IDC_STATIC, 0, 23, 85, 9 + EDITTEXT IDC_PACKAGEADVANCED_VARIABLEFILES_EDIT, 90, 20, 200, 14, ES_AUTOHSCROLL | WS_TABSTOP + AUTOCHECKBOX "Merge skins", IDC_PACKAGEADVANCED_MERGESKINS_CHECK, 0, 40, 85, 9, WS_TABSTOP +} + +IDD_PACKAGESELECTFOLDER_DIALOG DIALOGEX 0, 0, 200, 100 +STYLE DS_CENTER | WS_POPUP | WS_TILEDWINDOW +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Add" +FONT 8, "MS Shell Dlg 2" +{ + AUTORADIOBUTTON "", IDC_PACKAGESELECTFOLDER_EXISTING_RADIO, 6, 6, 220, 9, WS_TABSTOP | BST_CHECKED + COMBOBOX IDC_PACKAGESELECTFOLDER_EXISTING_COMBO, 16, 19, 177, 14, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + AUTORADIOBUTTON "Add custom folder:", IDC_PACKAGESELECTFOLDER_CUSTOM_RADIO, 6, 40, 220, 9, WS_TABSTOP + EDITTEXT IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT, 16, 53, 149, 14, WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL | WS_DISABLED + PUSHBUTTON "...", IDC_PACKAGESELECTFOLDER_CUSTOMBROWSE_BUTTON, 170, 53, 25, 14, WS_TABSTOP | WS_DISABLED + DEFPUSHBUTTON "Add", IDCLOSE, 144, 82, 50, 14 +} + +IDD_PACKAGESELECTPLUGIN_DIALOG DIALOGEX 0, 0, 200, 100 +STYLE DS_CENTER | WS_POPUP | WS_TILEDWINDOW +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Add" +FONT 8, "MS Shell Dlg 2" +{ + LTEXT "32-bit DLL:", IDC_STATIC, 6, 6, 220, 9 + EDITTEXT IDC_PACKAGESELECTPLUGIN_32BIT_EDIT, 16, 19, 149, 14, ES_READONLY | WS_BORDER + PUSHBUTTON "...", IDC_PACKAGESELECTPLUGIN_32BITBROWSE_BUTTON, 170, 19, 25, 14, WS_TABSTOP + LTEXT "64-bit DLL:", IDC_STATIC, 6, 40, 220, 9 + EDITTEXT IDC_PACKAGESELECTPLUGIN_64BIT_EDIT, 16, 53, 149, 14, ES_READONLY | WS_BORDER + PUSHBUTTON "...", IDC_PACKAGESELECTPLUGIN_64BITBROWSE_BUTTON, 170, 53, 25, 14, WS_TABSTOP + DEFPUSHBUTTON "Add", IDCLOSE, 144, 82, 50, 14, WS_DISABLED } IDD_INSTALL_DIALOG DIALOGEX 0, 0, 266, 250 diff --git a/SkinInstaller/SkinInstaller.vcxproj b/SkinInstaller/SkinInstaller.vcxproj index 4c8bb86a..a0068dab 100644 --- a/SkinInstaller/SkinInstaller.vcxproj +++ b/SkinInstaller/SkinInstaller.vcxproj @@ -91,7 +91,7 @@ Windows true MachineX86 - Rainmeter.lib;Wininet.lib;Comctl32.lib;Version.lib;UxTheme.lib;shlwapi.lib;%(AdditionalDependencies) + Rainmeter.lib;Imagehlp.lib;Wininet.lib;Comctl32.lib;Version.lib;UxTheme.lib;shlwapi.lib;%(AdditionalDependencies) ..\Library\x32\$(Configuration);%(AdditionalLibraryDirectories) wWinMainCRTStartup .rdata=.text @@ -126,7 +126,7 @@ ..\TestBench\x64\$(Configuration)\SkinInstaller.exe Windows true - Rainmeter.lib;Wininet.lib;Comctl32.lib;Version.lib;UxTheme.lib;shlwapi.lib;%(AdditionalDependencies) + Rainmeter.lib;Imagehlp.lib;Wininet.lib;Comctl32.lib;Version.lib;UxTheme.lib;shlwapi.lib;%(AdditionalDependencies) ..\Library\x64\$(Configuration);%(AdditionalLibraryDirectories) wWinMainCRTStartup .rdata=.text @@ -166,7 +166,7 @@ true MachineX86 wWinMainCRTStartup - Rainmeter.lib;Wininet.lib;Comctl32.lib;Version.lib;UxTheme.lib;shlwapi.lib;%(AdditionalDependencies) + Rainmeter.lib;Imagehlp.lib;Wininet.lib;Comctl32.lib;Version.lib;UxTheme.lib;shlwapi.lib;%(AdditionalDependencies) ..\Library\x32\$(Configuration);%(AdditionalLibraryDirectories) $(IntDir)$(TargetName).pdb @@ -198,7 +198,7 @@ Windows true wWinMainCRTStartup - Rainmeter.lib;Wininet.lib;Comctl32.lib;Version.lib;UxTheme.lib;shlwapi.lib;%(AdditionalDependencies) + Rainmeter.lib;Imagehlp.lib;Wininet.lib;Comctl32.lib;Version.lib;UxTheme.lib;shlwapi.lib;%(AdditionalDependencies) ..\Library\x64\$(Configuration);%(AdditionalLibraryDirectories) $(IntDir)$(TargetName).pdb @@ -209,8 +209,8 @@ - + @@ -245,8 +245,8 @@ - + diff --git a/SkinInstaller/SkinInstaller.vcxproj.filters b/SkinInstaller/SkinInstaller.vcxproj.filters index 666428b9..b830088f 100644 --- a/SkinInstaller/SkinInstaller.vcxproj.filters +++ b/SkinInstaller/SkinInstaller.vcxproj.filters @@ -27,9 +27,6 @@ Source Files - - Source Files - Source Files @@ -87,6 +84,9 @@ pcre + + Source Files + @@ -95,9 +95,6 @@ Header Files - - Header Files - Header Files @@ -155,6 +152,9 @@ pcre + + Header Files + diff --git a/SkinInstaller/StdAfx.h b/SkinInstaller/StdAfx.h index 166d56a8..fecead05 100644 --- a/SkinInstaller/StdAfx.h +++ b/SkinInstaller/StdAfx.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/SkinInstaller/resource.h b/SkinInstaller/resource.h index d5125b1f..6f6aa3df 100644 --- a/SkinInstaller/resource.h +++ b/SkinInstaller/resource.h @@ -3,17 +3,13 @@ #define IDB_INSTALLHEADER 101 #define IDD_INSTALL_DIALOG 102 #define IDD_INSTALL_TAB 103 -#define IDD_BACKUP_DIALOG 104 -#define IDD_BACKUP_TABDIALOG 105 -#define IDR_INSTALL_MENU 106 - -#define IDC_BACKUP_TAB 1019 -#define IDC_BACKUP_FILE_TEXT 1020 -#define IDC_BACKUP_BROWSE_BUTTON 1021 -#define IDC_BACKUP_PROGRESS 1022 -#define IDC_BACKUP_BACKUP_BUTTON 1023 -#define IDC_BACKUP_DESCRIPTION_TEXT 1024 -#define IDC_BACKUP_INPROGRESS_TEXT 1025 +#define IDR_INSTALL_MENU 104 +#define IDD_PACKAGE_DIALOG 105 +#define IDD_PACKAGEINFO_TAB 106 +#define IDD_PACKAGEOPTIONS_TAB 107 +#define IDD_PACKAGEADVANCED_TAB 108 +#define IDD_PACKAGESELECTFOLDER_DIALOG 109 +#define IDD_PACKAGESELECTPLUGIN_DIALOG 110 #define IDC_INSTALL_TAB 1000 #define IDC_INSTALL_HEADER_BITMAP 1001 @@ -28,6 +24,45 @@ #define IDC_INSTALLTAB_INPROGRESS_TEXT 1005 #define IDC_INSTALLTAB_PROGRESS 1006 +#define IDC_PACKAGE_TAB 1000 +#define IDC_PACKAGE_NEXT_BUTTON 1001 +#define IDC_PACKAGE_CREATEPACKAGE_BUTTON 1002 + +#define IDC_PACKAGEINFO_NAME_EDIT 1000 +#define IDC_PACKAGEINFO_AUTHOR_EDIT 1001 +#define IDC_PACKAGEINFO_VERSION_EDIT 1002 +#define IDC_PACKAGEINFO_COMPONENTS_LIST 1003 +#define IDC_PACKAGEINFO_ADDSKIN_BUTTON 1004 +#define IDC_PACKAGEINFO_ADDTHEME_BUTTON 1005 +#define IDC_PACKAGEINFO_ADDPLUGIN_BUTTON 1006 + +#define IDC_PACKAGEOPTIONS_FILE_EDIT 1000 +#define IDC_PACKAGEOPTIONS_FILEBROWSE_BUTTON 1001 +#define IDC_PACKAGEOPTIONS_DONOTHING_RADIO 1002 +#define IDC_PACKAGEOPTIONS_LOADSKIN_RADIO 1003 +#define IDC_PACKAGEOPTIONS_LOADSKIN_EDIT 1004 +#define IDC_PACKAGEOPTIONS_LOADSKINBROWSE_BUTTON 1005 +#define IDC_PACKAGEOPTIONS_LOADTHEME_RADIO 1006 +#define IDC_PACKAGEOPTIONS_LOADTHEME_COMBO 1007 +#define IDC_PACKAGEOPTIONS_RAINMETERVERSION_EDIT 1008 +#define IDC_PACKAGEOPTIONS_WINDOWSVERSION_COMBO 1009 + +#define IDC_PACKAGEADVANCED_HEADER_EDIT 1000 +#define IDC_PACKAGEADVANCED_HEADERROWSE_BUTTON 1001 +#define IDC_PACKAGEADVANCED_VARIABLEFILES_EDIT 1002 +#define IDC_PACKAGEADVANCED_MERGESKINS_CHECK 1003 + +#define IDC_PACKAGESELECTFOLDER_EXISTING_RADIO 1000 +#define IDC_PACKAGESELECTFOLDER_EXISTING_COMBO 1001 +#define IDC_PACKAGESELECTFOLDER_CUSTOM_RADIO 1002 +#define IDC_PACKAGESELECTFOLDER_CUSTOM_EDIT 1003 +#define IDC_PACKAGESELECTFOLDER_CUSTOMBROWSE_BUTTON 1004 + +#define IDC_PACKAGESELECTPLUGIN_32BIT_EDIT 1000 +#define IDC_PACKAGESELECTPLUGIN_32BITBROWSE_BUTTON 1001 +#define IDC_PACKAGESELECTPLUGIN_64BIT_EDIT 1002 +#define IDC_PACKAGESELECTPLUGIN_64BITBROWSE_BUTTON 1003 + #define IDM_INSTALL_BACKUPSKINS 4200 #define IDM_INSTALL_SYSTEMFONTS 4201 #define IDM_INSTALL_FORMAT 4202