diff --git a/C/Util/7zipInstall/7zipInstall.c b/C/Util/7zipInstall/7zipInstall.c index 7d8e8c4..d7733e1 100644 --- a/C/Util/7zipInstall/7zipInstall.c +++ b/C/Util/7zipInstall/7zipInstall.c @@ -59,6 +59,8 @@ typedef enum { static LPCSTR const k_7zip = "7-Zip"; static LPCWSTR const k_Reg_Software_7zip = L"Software\\7-Zip"; +static LPCWSTR const k_7zip_DisplayVersion = LLL(MY_VERSION); +static LPCWSTR const k_7zip_Publisher = LLL(MY_AUTHOR_NAME); // #define Z7_64BIT_INSTALLER 1 @@ -944,7 +946,7 @@ static void WriteShellEx(void) if (res == ERROR_SUCCESS) { MyRegistry_SetString(destKey, L"DisplayName", k_7zip_with_Ver_str); - MyRegistry_SetString(destKey, L"DisplayVersion", LLL(MY_VERSION_NUMBERS)); + MyRegistry_SetString(destKey, L"DisplayVersion", k_7zip_DisplayVersion); MyRegistry_SetString(destKey, L"DisplayIcon", destPath); MyRegistry_SetString(destKey, L"InstallLocation", path); @@ -964,7 +966,7 @@ static void WriteShellEx(void) MyRegistry_SetDWORD(destKey, L"VersionMajor", MY_VER_MAJOR); MyRegistry_SetDWORD(destKey, L"VersionMinor", MY_VER_MINOR); - MyRegistry_SetString(destKey, L"Publisher", LLL(MY_AUTHOR_NAME)); + MyRegistry_SetString(destKey, L"Publisher", k_7zip_Publisher); // MyRegistry_SetString(destKey, L"HelpLink", L"http://www.7-zip.org/support.html"); // MyRegistry_SetString(destKey, L"URLInfoAbout", L"http://www.7-zip.org/"); diff --git a/C/Util/7zipUninstall/7zipUninstall.c b/C/Util/7zipUninstall/7zipUninstall.c index e7051e2..1419987 100644 --- a/C/Util/7zipUninstall/7zipUninstall.c +++ b/C/Util/7zipUninstall/7zipUninstall.c @@ -27,6 +27,9 @@ typedef enum { #else #include #endif +#ifndef UNDER_CE +#include +#endif #include "../../7zVersion.h" @@ -464,7 +467,7 @@ static BoolInt AreEqual_Path_PrefixName(const wchar_t *s, const wchar_t *prefix, return AreStringsEqual_NoCase(s + wcslen(prefix), name); } -static void WriteCLSID(void) +static void RemoveShellExtensionCLSID(void) { WCHAR s[MAX_PATH + 30]; @@ -540,7 +543,13 @@ static void WriteCLSID(void) } #endif +} +static void WriteCLSID(void) +{ + WCHAR s[MAX_PATH + 30]; + + RemoveShellExtensionCLSID(); if (MyRegistry_QueryString2(HKEY_LOCAL_MACHINE, k_AppPaths_7zFm, NULL, s)) { @@ -616,6 +625,216 @@ static BOOL RemoveFileAfterReboot(void) return RemoveFileAfterReboot2(path); } +static BOOL DeleteFileOrScheduleForReboot(const WCHAR *s, WRes *winRes, BoolInt *needReboot) +{ + const DWORD attrib = GetFileAttributesW(s); + if (attrib == INVALID_FILE_ATTRIBUTES) + return TRUE; + if (attrib & FILE_ATTRIBUTE_READONLY) + SetFileAttributesW(s, 0); + if (DeleteFileW(s)) + return TRUE; + if (RemoveFileAfterReboot2(s)) + { + if (needReboot) + *needReboot = True; + return TRUE; + } + if (winRes) + *winRes = GetLastError(); + return FALSE; +} + +#ifndef UNDER_CE +static HMODULE GetRemoteModuleBase(DWORD processId, const WCHAR *moduleName) +{ + HMODULE res = NULL; + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processId); + if (snapshot != INVALID_HANDLE_VALUE) + { + MODULEENTRY32W me; + me.dwSize = sizeof(me); + if (Module32FirstW(snapshot, &me)) + do + { + if (AreStringsEqual_NoCase(me.szModule, moduleName)) + { + res = (HMODULE)me.modBaseAddr; + break; + } + } + while (Module32NextW(snapshot, &me)); + CloseHandle(snapshot); + } + return res; +} + +static LPVOID GetRemoteProcAddress(DWORD processId, const WCHAR *moduleName, const char *procName) +{ + HMODULE localModule = GetModuleHandleW(moduleName); + BoolInt needFree = False; + HMODULE remoteModule; + FARPROC localProc; + + if (!localModule) + { + localModule = LoadLibraryW(moduleName); + needFree = (localModule != NULL); + } + if (!localModule) + return NULL; + + localProc = GetProcAddress(localModule, procName); + remoteModule = GetRemoteModuleBase(processId, moduleName); + + if (needFree) + FreeLibrary(localModule); + + if (!localProc || !remoteModule) + return NULL; + + return (LPVOID)((const Byte *)remoteModule + ((const Byte *)localProc - (const Byte *)localModule)); +} + +static BoolInt UnloadModuleInProcess(DWORD processId, const WCHAR *moduleName) +{ + HMODULE remoteModule = GetRemoteModuleBase(processId, moduleName); + LPTHREAD_START_ROUTINE remoteFreeLibrary; + HANDLE process; + HANDLE thread; + DWORD exitCode = 0; + + if (!remoteModule) + return True; + + remoteFreeLibrary = (LPTHREAD_START_ROUTINE)GetRemoteProcAddress(processId, L"kernel32.dll", "FreeLibrary"); + if (!remoteFreeLibrary) + return False; + + process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, processId); + if (!process) + return False; + + thread = CreateRemoteThread(process, NULL, 0, remoteFreeLibrary, (LPVOID)remoteModule, 0, NULL); + if (!thread) + { + CloseHandle(process); + return False; + } + + if (WaitForSingleObject(thread, 5000) == WAIT_OBJECT_0) + GetExitCodeThread(thread, &exitCode); + + CloseHandle(thread); + CloseHandle(process); + return (exitCode != 0); +} + +static void RequestExplorerToUnloadModule(const WCHAR *moduleName) +{ + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot != INVALID_HANDLE_VALUE) + { + PROCESSENTRY32W pe; + pe.dwSize = sizeof(pe); + if (Process32FirstW(snapshot, &pe)) + do + { + if (AreStringsEqual_NoCase(pe.szExeFile, L"explorer.exe")) + { + unsigned i; + for (i = 0; i < 4; i++) + { + if (!GetRemoteModuleBase(pe.th32ProcessID, moduleName)) + break; + if (!UnloadModuleInProcess(pe.th32ProcessID, moduleName)) + break; + Sleep(100); + } + } + } + while (Process32NextW(snapshot, &pe)); + CloseHandle(snapshot); + } +} + +static void RequestExplorerToUnloadShellExtensions(void) +{ + RequestExplorerToUnloadModule(L"7-zip.dll"); + #ifdef USE_7ZIP_32_DLL + RequestExplorerToUnloadModule(L"7-zip32.dll"); + #endif +} +#endif + +static BoolInt IsTempCleanupTarget(const WCHAR *s) +{ + if (AreStringsEqual_NoCase(s, L"7-zip.dll")) + return True; + #ifdef USE_7ZIP_32_DLL + if (AreStringsEqual_NoCase(s, L"7-zip32.dll")) + return True; + #endif + return False; +} + +static BOOL DeleteTempVariantsForBasePath(const WCHAR *basePath, WRes *winRes, BoolInt *needReboot) +{ + #ifndef UNDER_CE + BOOL result = TRUE; + WCHAR dirPath[MAX_PATH * 2 + 80]; + WCHAR mask[MAX_PATH * 2 + 80]; + WCHAR *name; + WIN32_FIND_DATAW fd; + HANDLE h; + + wcscpy(mask, basePath); + CatAscii(mask, ".tmp*"); + + wcscpy(dirPath, basePath); + name = dirPath; + { + WCHAR *s = dirPath; + for (;;) + { + const WCHAR c = *s++; + if (c == 0) + break; + if (c == WCHAR_PATH_SEPARATOR) + name = s; + } + } + + if (!name) + return TRUE; + + *name = 0; + + h = FindFirstFileW(mask, &fd); + if (h == INVALID_HANDLE_VALUE) + return TRUE; + + do + { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + wcscpy(name, fd.cFileName); + if (!DeleteFileOrScheduleForReboot(dirPath, winRes, needReboot)) + result = FALSE; + } + } + while (FindNextFileW(h, &fd)); + + FindClose(h); + return result; + #else + UNUSED_VAR(basePath) + UNUSED_VAR(winRes) + UNUSED_VAR(needReboot) + return TRUE; + #endif +} + // #define IS_LIMIT_CHAR(c) (c == 0 || c == ' ') static BoolInt IsThereSpace(const wchar_t *s) @@ -640,6 +859,55 @@ static void AddPathParam(wchar_t *dest, const wchar_t *src) CatAscii(dest, "\""); } +static BoolInt IsProcessElevated(void) +{ + #ifndef UNDER_CE + SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; + PSID adminGroup = NULL; + BOOL isMember = FALSE; + + if (!AllocateAndInitializeSid(&ntAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &adminGroup)) + return False; + + CheckTokenMembership(NULL, adminGroup, &isMember); + FreeSid(adminGroup); + return isMember ? True : False; + #else + return True; + #endif +} + +static BoolInt RelaunchAsAdmin(const WCHAR *params, DWORD *errorCode) +{ + #ifndef UNDER_CE + SHELLEXECUTEINFOW sei; + memset(&sei, 0, sizeof(sei)); + sei.cbSize = sizeof(sei); + sei.fMask = SEE_MASK_NOCLOSEPROCESS; + sei.lpVerb = L"runas"; + sei.lpFile = modulePath; + sei.lpParameters = (params[0] == 0 ? NULL : params); + sei.nShow = SW_SHOWNORMAL; + + if (ShellExecuteExW(&sei)) + { + if (sei.hProcess) + CloseHandle(sei.hProcess); + return True; + } + + if (errorCode) + *errorCode = GetLastError(); + return False; + #else + UNUSED_VAR(params) + UNUSED_VAR(errorCode) + return False; + #endif +} + static BoolInt GetErrorMessage(DWORD errorCode, WCHAR *message) @@ -713,7 +981,7 @@ static int Install(void) SRes res = SZ_OK; WRes winRes = 0; - // BoolInt needReboot = False; + BoolInt needReboot = False; const size_t pathLen = wcslen(path); if (!g_SilentMode) @@ -723,6 +991,14 @@ static int Install(void) SendMessage(g_Progress_HWND, PBM_SETRANGE32, 0, NUM_FILES); } + RemoveShellExtensionCLSID(); + #ifndef UNDER_CE + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); + Sleep(200); + RequestExplorerToUnloadShellExtensions(); + Sleep(100); + #endif + { unsigned i; const char *curName = k_Names; @@ -780,24 +1056,17 @@ static int Install(void) if (!g_SilentMode) SetWindowTextW(g_InfoLine_HWND, temp); + if (IsTempCleanupTarget(temp)) { - const DWORD attrib = GetFileAttributesW(path); - if (attrib == INVALID_FILE_ATTRIBUTES) - continue; - if (attrib & FILE_ATTRIBUTE_READONLY) - SetFileAttributesW(path, 0); - if (!DeleteFileW(path)) - { - if (!RemoveFileAfterReboot()) - { - winRes = GetLastError(); - } - /* - else - needReboot = True; - */ - } + DeleteTempVariantsForBasePath(path, &winRes, &needReboot); + #ifndef UNDER_CE + RequestExplorerToUnloadModule(temp); + Sleep(100); + #endif } + + if (!DeleteFileOrScheduleForReboot(path, &winRes, &needReboot)) + break; } CpyAscii(path + pathLen, k_Lang); @@ -824,7 +1093,11 @@ static int Install(void) if (res == SZ_OK) { - // if (!g_SilentMode && needReboot); + if (!g_SilentMode && needReboot) + MessageBoxW(g_HWND, + L"Some files were in use and are scheduled to be removed after restart.", + k_7zip_with_Ver_Uninstall, + MB_ICONINFORMATION | MB_OK); return 0; } @@ -1051,6 +1324,21 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, *name = 0; // keep only prefix for modulePrefix } + if (!IsProcessElevated()) + { + DWORD errorCode = ERROR_SUCCESS; + if (RelaunchAsAdmin(cmdParams, &errorCode)) + return 0; + if (!g_SilentMode && errorCode != ERROR_CANCELLED) + { + WCHAR m[MAX_PATH + 100]; + if (!GetErrorMessage(errorCode, m)) + CpyAscii(m, "Can't relaunch with administrator rights"); + PrintErrorMessage("Administrator rights are required for uninstall.", m); + } + return 1; + } + if (useTemp) { diff --git a/CPP/7zip/Bundles/Fm/makefile b/CPP/7zip/Bundles/Fm/makefile index de74f76..d7d8822 100644 --- a/CPP/7zip/Bundles/Fm/makefile +++ b/CPP/7zip/Bundles/Fm/makefile @@ -1,4 +1,8 @@ PROG = 7zFM.exe +UI_FILEMANAGER_PROGPATH = ..\..\UI\FileManager\$(O)\$(PROG) +REPO_LANG_DIR = ..\..\..\..\Lang +FM_LANG_DIR = $(O)\Lang +UI_FILEMANAGER_LANG_DIR = ..\..\UI\FileManager\$(O)\Lang # CFLAGS = $(CFLAGS) -DZ7_LARGE_PAGES @@ -82,3 +86,22 @@ GUI_OBJS = \ !include "../../7zip.mak" + +all: copy_lang $(UI_FILEMANAGER_PROGPATH) + +copy_lang: + if not exist "$(FM_LANG_DIR)" mkdir "$(FM_LANG_DIR)" + copy /Y "$(REPO_LANG_DIR)\*" "$(FM_LANG_DIR)\" >nul + if not exist "$(UI_FILEMANAGER_LANG_DIR)" mkdir "$(UI_FILEMANAGER_LANG_DIR)" + copy /Y "$(REPO_LANG_DIR)\*" "$(UI_FILEMANAGER_LANG_DIR)\" >nul + +$(UI_FILEMANAGER_PROGPATH): $(PROGPATH) + if not exist "..\..\UI\FileManager\$(O)" mkdir "..\..\UI\FileManager\$(O)" + copy /Y "$(PROGPATH)" "$(UI_FILEMANAGER_PROGPATH)" >nul + +$O\CompressDialog.obj: ../../UI/GUI/CompressDialog.cpp ../../UI/GUI/CompressDialog.h ../../UI/GUI/CompressDialogRes.h +$O\CompressCall2.obj: ../../UI/Common/CompressCall2.cpp ../../UI/Common/Update.h ../../UI/GUI/UpdateGUI.h +$O\Update.obj: ../../UI/Common/Update.cpp ../../UI/Common/Update.h +$O\UpdateCallbackGUI.obj: ../../UI/GUI/UpdateCallbackGUI.cpp ../../UI/Common/Update.h ../../UI/GUI/UpdateCallbackGUI.h +$O\UpdateGUI.obj: ../../UI/GUI/UpdateGUI.cpp ../../UI/Common/Update.h ../../UI/GUI/UpdateGUI.h ../../UI/GUI/CompressDialog.h ../../UI/GUI/CompressDialogRes.h +$O\resource.res: resource.rc ../../UI/GUI/resource2.rc ../../UI/GUI/CompressDialog.rc ../../UI/GUI/CompressDialogRes.h ../../UI/FileManager/AboutDialog.rc ../../UI/FileManager/AboutDialogRes.h diff --git a/CPP/7zip/UI/Common/Update.h b/CPP/7zip/UI/Common/Update.h index ae141e5..0a237f7 100644 --- a/CPP/7zip/UI/Common/Update.h +++ b/CPP/7zip/UI/Common/Update.h @@ -111,6 +111,9 @@ struct CUpdateOptions CObjectVector Commands; CArchivePath ArchivePath; + bool SeparateItemMode; + UStringVector SeparateItemPaths; + UStringVector SeparateItemArchivePaths; FString SfxModule; UString StdInFileName; @@ -143,7 +146,8 @@ struct CUpdateOptions RenameMode(false), ArcNameMode(k_ArcNameMode_Smart), - PathMode(NWildcard::k_RelatPath) + PathMode(NWildcard::k_RelatPath), + SeparateItemMode(false) {} diff --git a/CPP/7zip/UI/FileManager/LangUtils.cpp b/CPP/7zip/UI/FileManager/LangUtils.cpp index 4712192..12a81c3 100644 --- a/CPP/7zip/UI/FileManager/LangUtils.cpp +++ b/CPP/7zip/UI/FileManager/LangUtils.cpp @@ -5,6 +5,8 @@ #include "../../../Common/Lang.h" #include "../../../Windows/DLL.h" +#include "../../../Windows/FileFind.h" +#include "../../../Windows/FileName.h" #include "../../../Windows/Synchronization.h" #include "../../../Windows/Window.h" @@ -30,9 +32,45 @@ bool LangOpen(CLang &lang, CFSTR fileName) return lang.Open(fileName, "7-Zip"); } +static bool GetParentDirPrefix(FString &dirPrefix) +{ + if (dirPrefix.IsEmpty()) + return false; + + NFile::NName::NormalizeDirPathPrefix(dirPrefix); + + FString parent = dirPrefix; + if (!NFile::NName::IsDriveRootPath_SuperAllowed(parent)) + parent.DeleteBack(); + + const int pos = parent.ReverseFind_PathSepar(); + if (pos < 0) + return false; + + parent.DeleteFrom((unsigned)(pos + 1)); + if (parent.IsEmpty() || parent == dirPrefix) + return false; + + dirPrefix = parent; + return true; +} + FString GetLangDirPrefix() { - return NDLL::GetModuleDirPrefix() + FTEXT("Lang") FSTRING_PATH_SEPARATOR; + const FString moduleDir = NDLL::GetModuleDirPrefix(); + FString dirPrefix = moduleDir; + NFile::NName::NormalizeDirPathPrefix(dirPrefix); + + for (;;) + { + const FString langDir = dirPrefix + FTEXT("Lang") FSTRING_PATH_SEPARATOR; + if (NFile::NFind::DoesDirExist(langDir)) + return langDir; + if (!GetParentDirPrefix(dirPrefix)) + break; + } + + return moduleDir + FTEXT("Lang") FSTRING_PATH_SEPARATOR; } #ifdef Z7_LANG diff --git a/CPP/7zip/UI/FileManager/LangUtils.h b/CPP/7zip/UI/FileManager/LangUtils.h index d53d270..9c96994 100644 --- a/CPP/7zip/UI/FileManager/LangUtils.h +++ b/CPP/7zip/UI/FileManager/LangUtils.h @@ -37,6 +37,7 @@ void LangString_OnlyFromLangFile(UInt32 langID, UString &dest); inline UString LangString(UInt32 langID) { return NWindows::MyLoadString(langID); } inline void LangString(UInt32 langID, UString &dest) { NWindows::MyLoadString(langID, dest); } inline void AddLangString(UString &s, UInt32 langID) { s += NWindows::MyLoadString(langID); } +inline void LangString_OnlyFromLangFile(UInt32, UString &dest) { dest.Empty(); } #endif diff --git a/CPP/7zip/UI/GUI/CompressDialog.cpp b/CPP/7zip/UI/GUI/CompressDialog.cpp index 53e56fe..7db697b 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.cpp +++ b/CPP/7zip/UI/GUI/CompressDialog.cpp @@ -45,6 +45,7 @@ extern bool g_IsNT; static const UInt32 kLangIDs[] = { IDT_COMPRESS_ARCHIVE, + IDT_COMPRESS_OUTPUT_PATHS_NUM, IDT_COMPRESS_UPDATE_MODE, IDT_COMPRESS_FORMAT, IDT_COMPRESS_LEVEL, @@ -78,6 +79,8 @@ static const UInt32 kLangIDs[] = }; #endif +extern HINSTANCE g_hInstance; + using namespace NWindows; using namespace NFile; using namespace NName; @@ -92,6 +95,99 @@ static const UInt32 kLzmaMaxDictSize = (UInt32)15 << 28; static const UINT k_Message_ArcChanged = WM_APP + 1; +extern void AddUniqueString(UStringVector &strings, const UString &srcString); + +static const unsigned kArchivePathComboIds[] = +{ + IDC_COMPRESS_ARCHIVE, + IDC_COMPRESS_ARCHIVE2, + IDC_COMPRESS_ARCHIVE3, + IDC_COMPRESS_ARCHIVE4, + IDC_COMPRESS_ARCHIVE5 +}; + +static const unsigned kArchivePathButtonIds[] = +{ + IDB_COMPRESS_SET_ARCHIVE, + IDB_COMPRESS_SET_ARCHIVE2, + IDB_COMPRESS_SET_ARCHIVE3, + IDB_COMPRESS_SET_ARCHIVE4, + IDB_COMPRESS_SET_ARCHIVE5 +}; + +static const unsigned kOutputPathLayoutMoveIds[] = +{ + IDT_COMPRESS_FORMAT, + IDC_COMPRESS_FORMAT, + IDT_COMPRESS_LEVEL, + IDC_COMPRESS_LEVEL, + IDT_COMPRESS_METHOD, + IDC_COMPRESS_METHOD, + IDT_COMPRESS_DICTIONARY, + IDC_COMPRESS_DICTIONARY, + IDT_COMPRESS_ORDER, + IDC_COMPRESS_ORDER, + IDT_COMPRESS_SOLID, + IDC_COMPRESS_SOLID, + IDT_COMPRESS_THREADS, + IDC_COMPRESS_THREADS, + IDT_COMPRESS_HARDWARE_THREADS, + IDT_COMPRESS_MEMORY, + IDC_COMPRESS_MEM_USE, + IDT_COMPRESS_MEMORY_VALUE, + IDT_COMPRESS_MEMORY_DE, + IDT_COMPRESS_MEMORY_DE_VALUE, + IDT_SPLIT_TO_VOLUMES, + IDC_COMPRESS_VOLUME, + IDT_COMPRESS_PARAMETERS, + IDE_COMPRESS_PARAMETERS, + IDB_COMPRESS_OPTIONS, + IDT_COMPRESS_OPTIONS, + IDT_COMPRESS_UPDATE_MODE, + IDC_COMPRESS_UPDATE_MODE, + IDT_COMPRESS_PATH_MODE, + IDC_COMPRESS_PATH_MODE, + IDG_COMPRESS_OPTIONS, + IDX_COMPRESS_SFX, + IDX_COMPRESS_SHARED, + IDX_COMPRESS_DEL, + IDG_COMPRESS_ENCRYPTION, + IDT_PASSWORD_ENTER, + IDE_COMPRESS_PASSWORD1, + IDT_PASSWORD_REENTER, + IDE_COMPRESS_PASSWORD2, + IDX_PASSWORD_SHOW, + IDT_COMPRESS_ENCRYPTION_METHOD, + IDC_COMPRESS_ENCRYPTION_METHOD, + IDX_COMPRESS_ENCRYPT_FILE_NAMES, + IDOK, + IDCANCEL, + IDHELP +}; + +static const unsigned kDynamicOutputGroupIdBase = 50000; +static const unsigned kDynamicOutputGroupIdStep = 100; + +static unsigned GetDynamicGroupBase(unsigned groupIndex) +{ + return kDynamicOutputGroupIdBase + groupIndex * kDynamicOutputGroupIdStep; +} + +static unsigned GetDynamicGroupItemLabelId(unsigned groupIndex) { return GetDynamicGroupBase(groupIndex) + 1; } +static unsigned GetDynamicGroupCountLabelId(unsigned groupIndex) { return GetDynamicGroupBase(groupIndex) + 2; } +static unsigned GetDynamicGroupCountComboId(unsigned groupIndex) { return GetDynamicGroupBase(groupIndex) + 3; } +static unsigned GetDynamicGroupArchiveLabelId(unsigned groupIndex) { return GetDynamicGroupBase(groupIndex) + 4; } +static unsigned GetDynamicGroupArchiveComboId(unsigned groupIndex, unsigned pathIndex) { return GetDynamicGroupBase(groupIndex) + 10 + pathIndex; } +static unsigned GetDynamicGroupArchiveButtonId(unsigned groupIndex, unsigned pathIndex) { return GetDynamicGroupBase(groupIndex) + 20 + pathIndex; } + +static int FindArchivePathButtonIndex(unsigned id) +{ + for (unsigned i = 0; i < Z7_ARRAY_SIZE(kArchivePathButtonIds); i++) + if (kArchivePathButtonIds[i] == id) + return (int)i; + return -1; +} + /* static const UInt32 kZstd_MAX_DictSize = (UInt32)1 << MY_ZSTD_WINDOWLOG_MAX; */ @@ -466,6 +562,9 @@ bool CCompressDialog::OnInit() _default_encryptionMethod_Index = -1; m_ArchivePath.Attach(GetItem(IDC_COMPRESS_ARCHIVE)); + m_OutputPathCount.Attach(GetItem(IDC_COMPRESS_OUTPUT_PATHS_NUM)); + for (unsigned i = 1; i < Z7_ARRAY_SIZE(kArchivePathComboIds); i++) + m_ExtraArchivePaths[i - 1].Attach(GetItem(kArchivePathComboIds[i])); m_Format.Attach(GetItem(IDC_COMPRESS_FORMAT)); // that combo has CBS_SORT style in resources m_Level.Attach(GetItem(IDC_COMPRESS_LEVEL)); m_Method.Attach(GetItem(IDC_COMPRESS_METHOD)); @@ -523,15 +622,67 @@ bool CCompressDialog::OnInit() CheckButton(IDX_COMPRESS_SFX, Info.SFXMode); + for (unsigned i = 0; i < m_RegistryInfo.ArcPaths.Size() && i < kHistorySize; i++) { + m_ArchivePath.AddString(m_RegistryInfo.ArcPaths[i]); + for (unsigned k = 0; k < Z7_ARRAY_SIZE(m_ExtraArchivePaths); k++) + m_ExtraArchivePaths[k].AddString(m_RegistryInfo.ArcPaths[i]); + } + + for (unsigned i = 1; i <= Z7_ARRAY_SIZE(kArchivePathComboIds); i++) + { + wchar_t s[16]; + ConvertUInt32ToString(i, s); + m_OutputPathCount.AddString_SetItemData(s, (LPARAM)i); + } + + ShowItem_Bool(IDX_COMPRESS_SEPARATE_ITEMS, false); + + if (IsMultiItemMode()) + { + DirPrefix.Empty(); + StartDirPrefix.Empty(); + InitItemOutputGroups(); + if (!_itemOutputGroups.IsEmpty()) + _outputArcPaths = _itemOutputGroups[0].ArcPaths; + if (_outputArcPaths.IsEmpty() && !Info.ItemPaths.IsEmpty()) + { + UString path; + BuildItemArcPath(Info.ItemPaths.Front(), path); + _outputArcPaths.Add(path); + } + if (_outputArcPaths.Size() > Z7_ARRAY_SIZE(kArchivePathComboIds)) + _outputArcPaths.DeleteFrom(Z7_ARRAY_SIZE(kArchivePathComboIds)); + if (!_outputArcPaths.IsEmpty()) + m_ArchivePath.SetText(_outputArcPaths.Front()); + } + else + { + if (Info.ArcPaths.IsEmpty() && !Info.ArcPath.IsEmpty()) + Info.ArcPaths.Add(Info.ArcPath); + _outputArcPaths = Info.ArcPaths; + if (_outputArcPaths.Size() > Z7_ARRAY_SIZE(kArchivePathComboIds)) + _outputArcPaths.DeleteFrom(Z7_ARRAY_SIZE(kArchivePathComboIds)); + + UString initPath = Info.ArcPath; + if (!_outputArcPaths.IsEmpty()) + initPath = _outputArcPaths.Front(); + UString fileName; - SetArcPathFields(Info.ArcPath, fileName, true); + SetArcPathFields(initPath, fileName, true); StartDirPrefix = DirPrefix; SetArchiveName(fileName); + SyncPrimaryArcPathFromControl(); } - - for (unsigned i = 0; i < m_RegistryInfo.ArcPaths.Size() && i < kHistorySize; i++) - m_ArchivePath.AddString(m_RegistryInfo.ArcPaths[i]); + + SetOutputPathCount(_outputArcPaths.IsEmpty() ? 1 : (unsigned)_outputArcPaths.Size(), false); + if (IsMultiItemMode()) + { + CreateDynamicItemOutputControls(); + UpdateOutputPathControls(); + UpdateOutputPathLayout(); + } + UpdateSeparateItemModeControls(); AddComboItems(m_UpdateMode, k_UpdateMode_IDs, Z7_ARRAY_SIZE(k_UpdateMode_IDs), k_UpdateMode_Vals, Info.UpdateMode); @@ -585,13 +736,26 @@ void CCompressDialog::UpdatePasswordControl() bool CCompressDialog::OnButtonClicked(unsigned buttonID, HWND buttonHWND) { + const int archivePathButtonIndex = FindArchivePathButtonIndex(buttonID); + if (archivePathButtonIndex >= 0) + { + OnButtonSetArchivePath((unsigned)archivePathButtonIndex); + return true; + } + + if (IsMultiItemMode()) + { + for (unsigned groupIndex = 1; groupIndex < _itemOutputGroups.Size(); groupIndex++) + for (unsigned pathIndex = 0; pathIndex < kNumOutputPathRows; pathIndex++) + if (buttonID == GetDynamicGroupArchiveButtonId(groupIndex, pathIndex)) + { + BrowseItemOutputPath(groupIndex, pathIndex); + return true; + } + } + switch (buttonID) { - case IDB_COMPRESS_SET_ARCHIVE: - { - OnButtonSetArchive(); - return true; - } case IDX_COMPRESS_SFX: { SetMethod(GetMethodID()); @@ -778,8 +942,34 @@ static int GetExtDotPos(const UString &s) return -1; } +static void RemoveTailExtensionIfMatches(UString &fileName, const UString &fullExtension) +{ + const unsigned extLen = fullExtension.Len(); + if (fileName.Len() >= extLen) + if (StringsAreEqualNoCase(fileName.RightPtr(extLen), fullExtension)) + fileName.DeleteFrom(fileName.Len() - extLen); +} + +static void AppendExtensionWithoutDup(UString &fileName, const UString &fullExtension) +{ + if (fullExtension.IsEmpty()) + return; + RemoveTailExtensionIfMatches(fileName, fullExtension); + fileName += fullExtension; +} + void CCompressDialog::OnButtonSFX() { + const bool prevWasSFX = !IsSFX(); + const int prevFormat = m_PrevFormat; + + if (IsMultiItemMode()) + { + SyncAllItemOutputGroupsFromControls(); + UpdateExtraArcPathsForFormatChange(prevFormat, prevWasSFX); + return; + } + UString fileName; m_ArchivePath.GetText(fileName); const int dotPos = GetExtDotPos(fileName); @@ -805,6 +995,9 @@ void CCompressDialog::OnButtonSFX() } // CheckVolumeEnable(); + SyncPrimaryArcPathFromControl(); + SyncExtraArcPathsFromControls(); + UpdateExtraArcPathsForFormatChange(prevFormat, prevWasSFX); } @@ -827,6 +1020,965 @@ bool CCompressDialog::GetFinalPath_Smart(UString &resPath) const } +static void SetWindowFont_Simple(HWND hwnd, HFONT font) +{ + if (hwnd != NULL && font != NULL) + ::SendMessage(hwnd, WM_SETFONT, (WPARAM)font, TRUE); +} + + +static bool CreateDialogChildWindow_Simple( + NWindows::CWindow &window, + LPCTSTR className, + LPCTSTR text, + DWORD style, + const RECT &rect, + HWND parent, + unsigned id, + HFONT font) +{ + if (!window.Create(className, text, style, + rect.left, rect.top, RECT_SIZE_X(rect), RECT_SIZE_Y(rect), + parent, (HMENU)(INT_PTR)id, g_hInstance, NULL)) + return false; + SetWindowFont_Simple(window, font); + return true; +} + + +void CCompressDialog::InitItemOutputGroups() +{ + _itemOutputGroups.Clear(); + + FOR_VECTOR (i, Info.ItemPaths) + { + CItemOutputGroup group; + group.ItemPath = Info.ItemPaths[i]; + _itemOutputGroups.Add(group); + } + + if (Info.ItemOutputItemPaths.Size() == Info.ItemArcPaths.Size()) + { + FOR_VECTOR (i, Info.ItemOutputItemPaths) + { + FOR_VECTOR (k, _itemOutputGroups) + if (_itemOutputGroups[k].ItemPath == Info.ItemOutputItemPaths[i]) + { + _itemOutputGroups[k].ArcPaths.Add(Info.ItemArcPaths[i]); + break; + } + } + } + + FOR_VECTOR (i, _itemOutputGroups) + { + CItemOutputGroup &group = _itemOutputGroups[i]; + if (group.ArcPaths.IsEmpty()) + { + UString path; + BuildItemArcPath(group.ItemPath, path); + group.ArcPaths.Add(path); + } + if (group.ArcPaths.Size() > kNumOutputPathRows) + group.ArcPaths.DeleteFrom(kNumOutputPathRows); + } +} + + +void CCompressDialog::InitOutputPathLayout() +{ + if (_outputLayout_Inited) + return; + + RECT windowRect; + if (GetWindowRect(&windowRect)) + { + _outputLayout_BaseWindowX = RECT_SIZE_X(windowRect); + _outputLayout_BaseWindowY = RECT_SIZE_Y(windowRect); + } + + _outputPathRowStep = 0; + if (GetItem(IDC_COMPRESS_ARCHIVE) != NULL && GetItem(IDC_COMPRESS_ARCHIVE2) != NULL) + { + RECT r1; + RECT r2; + GetClientRectOfItem(IDC_COMPRESS_ARCHIVE, r1); + GetClientRectOfItem(IDC_COMPRESS_ARCHIVE2, r2); + _outputPathRowStep = r2.top - r1.top; + } + + _outputLayout_Items.Clear(); + _outputLayout_Items.Reserve(Z7_ARRAY_SIZE(kOutputPathLayoutMoveIds)); + for (unsigned i = 0; i < Z7_ARRAY_SIZE(kOutputPathLayoutMoveIds); i++) + { + const unsigned id = kOutputPathLayoutMoveIds[i]; + if (GetItem(id) == NULL) + continue; + CLayoutItem item; + item.Id = id; + GetClientRectOfItem(id, item.Rect); + _outputLayout_Items.Add(item); + } + + _outputLayout_BaseControlsTop = 0; + if (!_outputLayout_Items.IsEmpty()) + _outputLayout_BaseControlsTop = _outputLayout_Items[0].Rect.top; + + GetClientRectOfItem(IDT_COMPRESS_ARCHIVE_FOLDER, _outputTemplate_ItemLabelRect); + GetClientRectOfItem(IDT_COMPRESS_OUTPUT_PATHS_NUM, _outputTemplate_CountLabelRect); + GetClientRectOfItem(IDC_COMPRESS_OUTPUT_PATHS_NUM, _outputTemplate_CountComboRect); + GetClientRectOfItem(IDT_COMPRESS_ARCHIVE, _outputTemplate_ArchiveLabelRect); + for (unsigned i = 0; i < Z7_ARRAY_SIZE(kArchivePathComboIds); i++) + { + GetClientRectOfItem(kArchivePathComboIds[i], _outputTemplate_ArchiveComboRects[i]); + GetClientRectOfItem(kArchivePathButtonIds[i], _outputTemplate_ArchiveButtonRects[i]); + } + + _outputGroupGapY = _outputPathRowStep / 2; + if (_outputGroupGapY < 8) + _outputGroupGapY = 8; + + _outputLayout_Inited = true; +} + + +void CCompressDialog::CreateDynamicItemOutputControls() +{ + if (!IsMultiItemMode() || _itemOutputGroups.Size() <= 1) + return; + + InitOutputPathLayout(); + + HFONT labelFont = (HFONT)::SendMessage(GetItem(IDT_COMPRESS_ARCHIVE_FOLDER), WM_GETFONT, 0, 0); + HFONT comboFont = (HFONT)::SendMessage(GetItem(IDC_COMPRESS_ARCHIVE), WM_GETFONT, 0, 0); + + UString countLabel; + UString archiveLabel; + GetItemText(IDT_COMPRESS_OUTPUT_PATHS_NUM, countLabel); + GetItemText(IDT_COMPRESS_ARCHIVE, archiveLabel); + const CSysString countLabelSys = GetSystemString(countLabel); + const CSysString archiveLabelSys = GetSystemString(archiveLabel); + + for (unsigned groupIndex = 1; groupIndex < _itemOutputGroups.Size(); groupIndex++) + { + const unsigned itemLabelId = GetDynamicGroupItemLabelId(groupIndex); + if (GetItem(itemLabelId) != NULL) + continue; + + NWindows::CWindow itemLabel; + CreateDialogChildWindow_Simple(itemLabel, TEXT("STATIC"), TEXT(""), + WS_CHILD | WS_VISIBLE | SS_NOPREFIX, + _outputTemplate_ItemLabelRect, *this, itemLabelId, labelFont); + + NWindows::CWindow countText; + CreateDialogChildWindow_Simple(countText, TEXT("STATIC"), countLabelSys, + WS_CHILD | WS_VISIBLE, + _outputTemplate_CountLabelRect, *this, GetDynamicGroupCountLabelId(groupIndex), labelFont); + + NWindows::NControl::CComboBox countCombo; + CreateDialogChildWindow_Simple(countCombo, TEXT("COMBOBOX"), TEXT(""), + WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | CBS_DROPDOWNLIST, + _outputTemplate_CountComboRect, *this, GetDynamicGroupCountComboId(groupIndex), comboFont); + for (unsigned i = 1; i <= kNumOutputPathRows; i++) + { + wchar_t s[16]; + ConvertUInt32ToString(i, s); + countCombo.AddString_SetItemData(s, (LPARAM)i); + } + + NWindows::CWindow archiveText; + CreateDialogChildWindow_Simple(archiveText, TEXT("STATIC"), archiveLabelSys, + WS_CHILD | WS_VISIBLE, + _outputTemplate_ArchiveLabelRect, *this, GetDynamicGroupArchiveLabelId(groupIndex), labelFont); + + for (unsigned pathIndex = 0; pathIndex < kNumOutputPathRows; pathIndex++) + { + NWindows::NControl::CComboBox combo; + CreateDialogChildWindow_Simple(combo, TEXT("COMBOBOX"), TEXT(""), + WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | CBS_DROPDOWN | CBS_AUTOHSCROLL, + _outputTemplate_ArchiveComboRects[pathIndex], *this, + GetDynamicGroupArchiveComboId(groupIndex, pathIndex), comboFont); + for (unsigned i = 0; i < m_RegistryInfo.ArcPaths.Size() && i < kHistorySize; i++) + combo.AddString(m_RegistryInfo.ArcPaths[i]); + + NWindows::CWindow button; + CreateDialogChildWindow_Simple(button, TEXT("BUTTON"), TEXT("..."), + WS_CHILD | WS_VISIBLE | WS_TABSTOP, + _outputTemplate_ArchiveButtonRects[pathIndex], *this, + GetDynamicGroupArchiveButtonId(groupIndex, pathIndex), labelFont); + } + } +} + + +void CCompressDialog::LayoutItemOutputGroups() +{ + InitOutputPathLayout(); + if (!_outputLayout_Inited || !IsMultiItemMode()) + return; + + const int templateTop = _outputTemplate_ItemLabelRect.top; + const int templateBottom = + _outputTemplate_ArchiveComboRects[kNumOutputPathRows - 1].bottom; + const int baseGapToControls = _outputLayout_BaseControlsTop - templateBottom; + + SetRedraw(false); + + int currentTop = templateTop; + for (unsigned groupIndex = 0; groupIndex < _itemOutputGroups.Size(); groupIndex++) + { + const unsigned count = _itemOutputGroups[groupIndex].ArcPaths.IsEmpty() ? 1 : + (unsigned)_itemOutputGroups[groupIndex].ArcPaths.Size(); + const int deltaY = currentTop - templateTop; + + if (groupIndex > 0 && GetItem(GetDynamicGroupItemLabelId(groupIndex)) != NULL) + { + RECT r = _outputTemplate_ItemLabelRect; + r.top += deltaY; + r.bottom += deltaY; + MoveItem_RECT(GetDynamicGroupItemLabelId(groupIndex), r, false); + + r = _outputTemplate_CountLabelRect; + r.top += deltaY; + r.bottom += deltaY; + MoveItem_RECT(GetDynamicGroupCountLabelId(groupIndex), r, false); + + r = _outputTemplate_CountComboRect; + r.top += deltaY; + r.bottom += deltaY; + MoveItem_RECT(GetDynamicGroupCountComboId(groupIndex), r, false); + + r = _outputTemplate_ArchiveLabelRect; + r.top += deltaY; + r.bottom += deltaY; + MoveItem_RECT(GetDynamicGroupArchiveLabelId(groupIndex), r, false); + + for (unsigned pathIndex = 0; pathIndex < kNumOutputPathRows; pathIndex++) + { + r = _outputTemplate_ArchiveComboRects[pathIndex]; + r.top += deltaY; + r.bottom += deltaY; + MoveItem_RECT(GetDynamicGroupArchiveComboId(groupIndex, pathIndex), r, false); + + r = _outputTemplate_ArchiveButtonRects[pathIndex]; + r.top += deltaY; + r.bottom += deltaY; + MoveItem_RECT(GetDynamicGroupArchiveButtonId(groupIndex, pathIndex), r, false); + } + } + + const int groupBottom = currentTop + + (_outputTemplate_ArchiveComboRects[count - 1].bottom - templateTop); + currentTop = groupBottom + _outputGroupGapY; + } + + const int desiredControlsTop = currentTop + baseGapToControls; + const int deltaY = desiredControlsTop - _outputLayout_BaseControlsTop; + + FOR_VECTOR (i, _outputLayout_Items) + { + const CLayoutItem &item = _outputLayout_Items[i]; + RECT r = item.Rect; + r.top += deltaY; + r.bottom += deltaY; + MoveItem_RECT(item.Id, r, false); + } + + RECT windowRect; + if (GetWindowRect(&windowRect)) + Move(windowRect.left, windowRect.top, + _outputLayout_BaseWindowX, _outputLayout_BaseWindowY + deltaY, false); + + SetRedraw(true); + InvalidateRect(NULL, true); + Update(); +} + + +void CCompressDialog::UpdateOutputPathLayout() +{ + InitOutputPathLayout(); + if (!_outputLayout_Inited || _outputPathRowStep == 0) + return; + + if (IsMultiItemMode()) + { + LayoutItemOutputGroups(); + return; + } + + const int deltaY = ((int)_outputPathCount - (int)kNumOutputPathRows) * _outputPathRowStep; + + SetRedraw(false); + + FOR_VECTOR (i, _outputLayout_Items) + { + const CLayoutItem &item = _outputLayout_Items[i]; + RECT r = item.Rect; + r.top += deltaY; + r.bottom += deltaY; + MoveItem_RECT(item.Id, r, false); + } + + RECT windowRect; + if (GetWindowRect(&windowRect)) + Move(windowRect.left, windowRect.top, _outputLayout_BaseWindowX, _outputLayout_BaseWindowY + deltaY, false); + + SetRedraw(true); + InvalidateRect(NULL, true); + Update(); +} + + +void CCompressDialog::UpdateOutputPathControls() +{ + if (IsMultiItemMode()) + { + UpdateItemOutputGroupControls(0); + for (unsigned i = 1; i < _itemOutputGroups.Size(); i++) + UpdateItemOutputGroupControls(i); + return; + } + + m_OutputPathCount.SetCurSel((int)_outputPathCount - 1); + + for (unsigned i = 1; i < Z7_ARRAY_SIZE(kArchivePathComboIds); i++) + { + const bool show = (i < _outputPathCount); + ShowItem_Bool(kArchivePathComboIds[i], show); + ShowItem_Bool(kArchivePathButtonIds[i], show); + + UString path; + if (i < _outputArcPaths.Size()) + path = _outputArcPaths[i]; + m_ExtraArchivePaths[i - 1].SetText(path); + } +} + + +void CCompressDialog::UpdateItemOutputGroupControls(unsigned groupIndex) +{ + if (groupIndex >= _itemOutputGroups.Size()) + return; + + const CItemOutputGroup &group = _itemOutputGroups[groupIndex]; + const unsigned count = group.ArcPaths.IsEmpty() ? 1 : (unsigned)group.ArcPaths.Size(); + + if (groupIndex == 0) + { + m_OutputPathCount.SetCurSel((int)count - 1); + if (!group.ArcPaths.IsEmpty()) + m_ArchivePath.SetText(group.ArcPaths.Front()); + for (unsigned i = 1; i < Z7_ARRAY_SIZE(kArchivePathComboIds); i++) + { + const bool show = (i < count); + ShowItem_Bool(kArchivePathComboIds[i], show); + ShowItem_Bool(kArchivePathButtonIds[i], show); + + UString path; + if (i < group.ArcPaths.Size()) + path = group.ArcPaths[i]; + m_ExtraArchivePaths[i - 1].SetText(path); + } + return; + } + + if (GetItem(GetDynamicGroupItemLabelId(groupIndex)) == NULL) + return; + + SetItemText(GetDynamicGroupItemLabelId(groupIndex), group.ItemPath); + + NWindows::NControl::CComboBox countCombo; + countCombo.Attach(GetItem(GetDynamicGroupCountComboId(groupIndex))); + countCombo.SetCurSel((int)count - 1); + + for (unsigned pathIndex = 0; pathIndex < kNumOutputPathRows; pathIndex++) + { + const bool show = (pathIndex < count); + ShowItem_Bool(GetDynamicGroupArchiveComboId(groupIndex, pathIndex), show); + ShowItem_Bool(GetDynamicGroupArchiveButtonId(groupIndex, pathIndex), show); + + NWindows::NControl::CComboBox combo; + combo.Attach(GetItem(GetDynamicGroupArchiveComboId(groupIndex, pathIndex))); + + UString path; + if (pathIndex < group.ArcPaths.Size()) + path = group.ArcPaths[pathIndex]; + combo.SetText(path); + } +} + + +void CCompressDialog::UpdateSeparateItemModeControls() +{ + ShowItem_Bool(IDX_COMPRESS_SEPARATE_ITEMS, false); + RefreshArchivePathInfo(); +} + + +void CCompressDialog::BuildItemArcPath(const UString &inputPath, UString &path) +{ + UString dirPrefix; + UString fileName; + SplitPathToParts_Smart(inputPath, dirPrefix, fileName); + if (fileName.IsEmpty()) + fileName = ExtractFileNameFromPath(inputPath); + + const CArcInfoEx &ai = Get_ArcInfoEx(); + if (!ai.Flags_KeepName()) + { + const int dotPos = GetExtDotPos(fileName); + if (dotPos >= 0) + fileName.DeleteFrom(dotPos); + } + + if (IsSFX()) + AppendExtensionWithoutDup(fileName, UString(kExeExt)); + else + { + UString ext = ai.GetMainExt(); + if (ai.Flags_HashHandler()) + { + UString estimatedName; + GetMethodSpec(estimatedName); + if (!estimatedName.IsEmpty()) + { + ext = estimatedName; + ext.MakeLower_Ascii(); + } + } + UString fullExt; + fullExt.Add_Dot(); + fullExt += ext; + AppendExtensionWithoutDup(fileName, fullExt); + } + + path = dirPrefix + fileName; +} + + +void CCompressDialog::SyncItemOutputGroupFromControls(unsigned groupIndex) +{ + if (!IsMultiItemMode() || groupIndex >= _itemOutputGroups.Size()) + return; + + CItemOutputGroup &group = _itemOutputGroups[groupIndex]; + + unsigned count = (group.ArcPaths.IsEmpty() ? 1 : (unsigned)group.ArcPaths.Size()); + if (groupIndex == 0) + count = _outputPathCount; + else + { + NWindows::NControl::CComboBox countCombo; + countCombo.Attach(GetItem(GetDynamicGroupCountComboId(groupIndex))); + const int sel = countCombo.GetCurSel(); + if (sel >= 0) + count = (unsigned)sel + 1; + } + + if (count < 1) + count = 1; + if (count > kNumOutputPathRows) + count = kNumOutputPathRows; + + if (group.ArcPaths.Size() > count) + group.ArcPaths.DeleteFrom(count); + + UString defaultPath; + if (!group.ArcPaths.IsEmpty()) + defaultPath = group.ArcPaths.Front(); + if (defaultPath.IsEmpty()) + BuildItemArcPath(group.ItemPath, defaultPath); + + if (group.ArcPaths.IsEmpty()) + group.ArcPaths.Add(defaultPath); + while (group.ArcPaths.Size() < count) + group.ArcPaths.Add(UString()); + + for (unsigned pathIndex = 0; pathIndex < count; pathIndex++) + { + UString path; + if (groupIndex == 0) + { + if (pathIndex == 0) + { + if (!GetFinalPath_Smart(path)) + { + m_ArchivePath.GetText(path); + path.Trim(); + } + } + else + { + m_ExtraArchivePaths[pathIndex - 1].GetText(path); + path.Trim(); + } + } + else + { + NWindows::NControl::CComboBox combo; + combo.Attach(GetItem(GetDynamicGroupArchiveComboId(groupIndex, pathIndex))); + combo.GetText(path); + path.Trim(); + } + + group.ArcPaths[pathIndex] = path; + } + + if (groupIndex == 0) + _outputArcPaths = group.ArcPaths; +} + + +void CCompressDialog::SyncAllItemOutputGroupsFromControls() +{ + if (!IsMultiItemMode()) + return; + for (unsigned i = 0; i < _itemOutputGroups.Size(); i++) + SyncItemOutputGroupFromControls(i); +} + + +void CCompressDialog::SetItemOutputGroupCount(unsigned groupIndex, unsigned count, bool syncFromControls) +{ + if (!IsMultiItemMode() || groupIndex >= _itemOutputGroups.Size()) + return; + + if (count < 1) + count = 1; + if (count > kNumOutputPathRows) + count = kNumOutputPathRows; + + if (syncFromControls) + SyncItemOutputGroupFromControls(groupIndex); + + CItemOutputGroup &group = _itemOutputGroups[groupIndex]; + UString defaultPath; + if (!group.ArcPaths.IsEmpty()) + defaultPath = group.ArcPaths.Front(); + if (defaultPath.IsEmpty()) + BuildItemArcPath(group.ItemPath, defaultPath); + + if (group.ArcPaths.IsEmpty()) + group.ArcPaths.Add(defaultPath); + if (group.ArcPaths.Size() > count) + group.ArcPaths.DeleteFrom(count); + while (group.ArcPaths.Size() < count) + group.ArcPaths.Add(UString()); + + if (groupIndex == 0) + { + _outputPathCount = count; + _outputArcPaths = group.ArcPaths; + } + + UpdateItemOutputGroupControls(groupIndex); + RefreshArchivePathInfo(); + UpdateOutputPathLayout(); +} + + +bool CCompressDialog::BrowseItemOutputPath(unsigned groupIndex, unsigned pathIndex) +{ + if (!IsMultiItemMode() || groupIndex >= _itemOutputGroups.Size() || pathIndex >= kNumOutputPathRows) + return false; + + SyncAllItemOutputGroupsFromControls(); + + CItemOutputGroup &group = _itemOutputGroups[groupIndex]; + if (group.ArcPaths.IsEmpty()) + { + UString defaultPath; + BuildItemArcPath(group.ItemPath, defaultPath); + group.ArcPaths.Add(defaultPath); + } + while (group.ArcPaths.Size() <= pathIndex) + group.ArcPaths.Add(UString()); + + UString path = group.ArcPaths[pathIndex]; + if (path.IsEmpty()) + BuildItemArcPath(group.ItemPath, path); + + const int prevFormat = m_PrevFormat; + const bool prevWasSFX = IsSFX(); + bool formatWasChanged = false; + if (!BrowseArchivePath(path, true, formatWasChanged)) + return false; + + group.ArcPaths[pathIndex] = path; + if (groupIndex == 0) + _outputArcPaths = group.ArcPaths; + + if (formatWasChanged) + { + SaveOptionsInMem(); + FormatChanged(true); + UpdateExtraArcPathsForFormatChange(prevFormat, prevWasSFX); + } + + UpdateItemOutputGroupControls(groupIndex); + RefreshArchivePathInfo(); + return true; +} + + +bool CCompressDialog::GetItemOutputGroupPaths(UStringVector &itemPaths, UStringVector &paths) const +{ + itemPaths.Clear(); + paths.Clear(); + if (!IsMultiItemMode() || _itemOutputGroups.IsEmpty()) + return false; + + FOR_VECTOR (g, _itemOutputGroups) + { + const CItemOutputGroup &group = _itemOutputGroups[g]; + FOR_VECTOR (i, group.ArcPaths) + { + UString path = group.ArcPaths[i]; + path.Trim(); + if (path.IsEmpty()) + return false; + + FString fullPath; + if (!MyGetFullPathName(us2fs(path), fullPath)) + return false; + + const UString fullPathU = fs2us(fullPath); + const unsigned prevSize = paths.Size(); + AddUniqueString(paths, fullPathU); + if (paths.Size() == prevSize) + return false; + + itemPaths.Add(group.ItemPath); + } + } + + return (paths.Size() == itemPaths.Size() && !paths.IsEmpty()); +} + + +bool CCompressDialog::GetItemArcPaths(UStringVector &itemPaths, UStringVector &paths) const +{ + return GetItemOutputGroupPaths(itemPaths, paths); +} + + +void CCompressDialog::RefreshArchivePathInfo() +{ + if (IsMultiItemMode()) + { + if (!_itemOutputGroups.IsEmpty()) + SetItemText(IDT_COMPRESS_ARCHIVE_FOLDER, _itemOutputGroups[0].ItemPath); + return; + } + + UString s = DirPrefix; + + if (_outputPathCount > 1) + { + if (!s.IsEmpty()) + s.Add_Space(); + s += "(+"; + s.Add_UInt32((UInt32)_outputPathCount - 1); + s += " more)"; + } + SetItemText(IDT_COMPRESS_ARCHIVE_FOLDER, s); +} + + +void CCompressDialog::SyncPrimaryArcPathFromControl() +{ + UString path; + if (!GetFinalPath_Smart(path)) + { + m_ArchivePath.GetText(path); + path.Trim(); + } + + if (_outputArcPaths.IsEmpty()) + _outputArcPaths.Add(path); + else + _outputArcPaths[0] = path; + + if (IsMultiItemMode() && !_itemOutputGroups.IsEmpty()) + { + while (_itemOutputGroups[0].ArcPaths.IsEmpty()) + _itemOutputGroups[0].ArcPaths.Add(UString()); + _itemOutputGroups[0].ArcPaths[0] = path; + } + + RefreshArchivePathInfo(); +} + + +void CCompressDialog::SyncExtraArcPathsFromControls() +{ + if (_outputArcPaths.Size() > _outputPathCount) + _outputArcPaths.DeleteFrom(_outputPathCount); + while (_outputArcPaths.Size() < _outputPathCount) + _outputArcPaths.Add(UString()); + + for (unsigned i = 1; i < _outputPathCount; i++) + { + UString path; + m_ExtraArchivePaths[i - 1].GetText(path); + path.Trim(); + _outputArcPaths[i] = path; + } + + if (IsMultiItemMode() && !_itemOutputGroups.IsEmpty()) + _itemOutputGroups[0].ArcPaths = _outputArcPaths; +} + + +void CCompressDialog::SetOutputPathCount(unsigned count, bool syncFromControls) +{ + const unsigned maxCount = Z7_ARRAY_SIZE(kArchivePathComboIds); + if (count < 1) + count = 1; + if (count > maxCount) + count = maxCount; + + if (IsMultiItemMode()) + { + SetItemOutputGroupCount(0, count, syncFromControls); + return; + } + + if (syncFromControls) + { + SyncPrimaryArcPathFromControl(); + SyncExtraArcPathsFromControls(); + } + + _outputPathCount = count; + + if (_outputArcPaths.Size() > count) + _outputArcPaths.DeleteFrom(count); + while (_outputArcPaths.Size() < count) + _outputArcPaths.Add(UString()); + + UpdateOutputPathControls(); + RefreshArchivePathInfo(); + UpdateOutputPathLayout(); +} + + +void CCompressDialog::SetOutputArcPaths(const UStringVector &paths) +{ + _outputArcPaths.Clear(); + FOR_VECTOR (i, paths) + { + UString s = paths[i]; + s.Trim(); + if (!s.IsEmpty()) + AddUniqueString(_outputArcPaths, s); + } + if (_outputArcPaths.Size() > Z7_ARRAY_SIZE(kArchivePathComboIds)) + _outputArcPaths.DeleteFrom(Z7_ARRAY_SIZE(kArchivePathComboIds)); + + if (IsMultiItemMode() && !_itemOutputGroups.IsEmpty()) + _itemOutputGroups[0].ArcPaths = _outputArcPaths; + + SetOutputPathCount(_outputArcPaths.IsEmpty() ? 1 : (unsigned)_outputArcPaths.Size(), false); +} + + +bool CCompressDialog::GetOutputArcPaths(UStringVector &paths) const +{ + paths.Clear(); + + if (IsMultiItemMode()) + { + if (_itemOutputGroups.IsEmpty()) + return false; + + FOR_VECTOR (i, _itemOutputGroups[0].ArcPaths) + { + UString path = _itemOutputGroups[0].ArcPaths[i]; + path.Trim(); + if (path.IsEmpty()) + return false; + + FString fullPath; + if (!MyGetFullPathName(us2fs(path), fullPath)) + return false; + AddUniqueString(paths, fs2us(fullPath)); + } + return (paths.Size() == _itemOutputGroups[0].ArcPaths.Size()); + } + + UString s; + if (!GetFinalPath_Smart(s)) + return false; + s.Trim(); + if (s.IsEmpty()) + return false; + AddUniqueString(paths, s); + + for (unsigned i = 1; i < _outputPathCount; i++) + { + UString path; + m_ExtraArchivePaths[i - 1].GetText(path); + path.Trim(); + if (path.IsEmpty()) + return false; + + FString fullPath; + if (!MyGetFullPathName(us2fs(path), fullPath)) + return false; + AddUniqueString(paths, fs2us(fullPath)); + } + + return (paths.Size() == _outputPathCount); +} + + +void CCompressDialog::UpdateItemArcPathToCurrentFormat(UString &path, int prevFormat, bool prevWasSFX) +{ + UString trimmed = path; + trimmed.Trim(); + if (trimmed.IsEmpty()) + { + path.Empty(); + return; + } + + UString dirPrefix; + UString fileName; + SplitPathToParts_2(path, dirPrefix, fileName); + + if (prevWasSFX) + RemoveTailExtensionIfMatches(fileName, UString(kExeExt)); + else if (prevFormat >= 0) + { + const CArcInfoEx &prevArchiverInfo = (*ArcFormats)[(unsigned)prevFormat]; + UString prevExtension; + prevExtension.Add_Dot(); + prevExtension += prevArchiverInfo.GetMainExt(); + RemoveTailExtensionIfMatches(fileName, prevExtension); + } + + const CArcInfoEx &ai = Get_ArcInfoEx(); + if (IsSFX()) + AppendExtensionWithoutDup(fileName, UString(kExeExt)); + else + { + UString ext = ai.GetMainExt(); + if (ai.Flags_HashHandler()) + { + UString estimatedName; + GetMethodSpec(estimatedName); + if (!estimatedName.IsEmpty()) + { + ext = estimatedName; + ext.MakeLower_Ascii(); + } + } + UString fullExt; + fullExt.Add_Dot(); + fullExt += ext; + AppendExtensionWithoutDup(fileName, fullExt); + } + + path = dirPrefix + fileName; +} + + +void CCompressDialog::UpdateArcPathToCurrentFormat(UString &path, int prevFormat, bool prevWasSFX) +{ + UString trimmed = path; + trimmed.Trim(); + if (trimmed.IsEmpty()) + { + path.Empty(); + return; + } + + UString dirPrefix; + UString fileName; + SplitPathToParts_2(path, dirPrefix, fileName); + + const CArcInfoEx &prevArchiverInfo = (*ArcFormats)[(unsigned)prevFormat]; + if (prevArchiverInfo.Flags_KeepName() || Info.KeepName) + { + UString prevExtension; + if (prevWasSFX) + prevExtension = kExeExt; + else + { + prevExtension.Add_Dot(); + prevExtension += prevArchiverInfo.GetMainExt(); + } + const unsigned prevExtensionLen = prevExtension.Len(); + if (fileName.Len() >= prevExtensionLen) + if (StringsAreEqualNoCase(fileName.RightPtr(prevExtensionLen), prevExtension)) + fileName.DeleteFrom(fileName.Len() - prevExtensionLen); + } + + const CArcInfoEx &ai = Get_ArcInfoEx(); + if (ai.Flags_KeepName()) + fileName = OriginalFileName; + else if (!Info.KeepName) + { + const int dotPos = GetExtDotPos(fileName); + if (dotPos >= 0) + fileName.DeleteFrom(dotPos); + } + + if (IsSFX()) + { + AppendExtensionWithoutDup(fileName, UString(kExeExt)); + } + else + { + UString ext = ai.GetMainExt(); + if (ai.Flags_HashHandler()) + { + UString estimatedName; + GetMethodSpec(estimatedName); + if (!estimatedName.IsEmpty()) + { + ext = estimatedName; + ext.MakeLower_Ascii(); + } + } + UString fullExt; + fullExt.Add_Dot(); + fullExt += ext; + AppendExtensionWithoutDup(fileName, fullExt); + } + + path = dirPrefix + fileName; +} + + +void CCompressDialog::UpdateExtraArcPathsForFormatChange(int prevFormat, bool prevWasSFX) +{ + if (prevFormat >= 0) + { + if (IsMultiItemMode()) + { + FOR_VECTOR (g, _itemOutputGroups) + FOR_VECTOR (i, _itemOutputGroups[g].ArcPaths) + UpdateItemArcPathToCurrentFormat(_itemOutputGroups[g].ArcPaths[i], prevFormat, prevWasSFX); + + if (!_itemOutputGroups.IsEmpty()) + _outputArcPaths = _itemOutputGroups[0].ArcPaths; + } + else + { + if (_outputPathCount > 1) + for (unsigned i = 1; i < _outputPathCount; i++) + UpdateArcPathToCurrentFormat(_outputArcPaths[i], prevFormat, prevWasSFX); + } + } + + UpdateOutputPathControls(); + RefreshArchivePathInfo(); +} + + bool CCompressDialog::SetArcPathFields(const UString &path) { UString name; @@ -843,6 +1995,19 @@ bool CCompressDialog::SetArcPathFields(const UString &path, UString &name, bool { DirPrefix = fs2us(resDirPrefix); name = fs2us(resFileName); + if (name.IsEmpty() && !DirPrefix.IsEmpty()) + { + UString smartDirPrefix; + UString smartName; + SplitPathToParts_Smart(DirPrefix, smartDirPrefix, smartName); + if (!smartName.IsEmpty()) + { + DirPrefix = smartDirPrefix; + name = smartName; + } + } + if (name.IsEmpty() && !OriginalFileName.IsEmpty()) + name = OriginalFileName; } else { @@ -851,7 +2016,7 @@ bool CCompressDialog::SetArcPathFields(const UString &path, UString &name, bool DirPrefix.Empty(); name = path; } - SetItemText(IDT_COMPRESS_ARCHIVE_FOLDER, DirPrefix); + RefreshArchivePathInfo(); m_ArchivePath.SetText(name); return res; } @@ -876,14 +2041,9 @@ static void AddFilter(CObjectVector &filters, static const char * const k_DontSave_Exts = "xpi odt ods docx xlsx "; -void CCompressDialog::OnButtonSetArchive() +bool CCompressDialog::BrowseArchivePath(UString &path, bool allowFormatChange, bool &formatWasChanged) { - UString path; - if (!GetFinalPath_Smart(path)) - { - ShowErrorMessage(*this, k_IncorrectPathMessage); - return; - } + formatWasChanged = false; int filterIndex; CObjectVector filters; @@ -898,55 +2058,80 @@ void CCompressDialog::OnButtonSetArchive() } else { - filterIndex = m_Format.GetCurSel(); - numFormats = (unsigned)m_Format.GetCount(); - - // filters [0, ... numFormats - 1] corresponds to items in m_Format combo - UString desc; - UStringVector masks; - CStringFinder finder; - - for (unsigned i = 0; i < numFormats; i++) + if (allowFormatChange) { - const CArcInfoEx &ai = (*ArcFormats)[(unsigned)m_Format.GetItemData(i)]; + filterIndex = m_Format.GetCurSel(); + numFormats = (unsigned)m_Format.GetCount(); + + // filters [0, ... numFormats - 1] corresponds to items in m_Format combo + UString desc; + UStringVector masks; + CStringFinder finder; + + for (unsigned i = 0; i < numFormats; i++) + { + const CArcInfoEx &ai = (*ArcFormats)[(unsigned)m_Format.GetItemData(i)]; + CBrowseFilterInfo &f = filters.AddNew(); + f.Description = ai.Name; + f.Description += " ("; + bool needSpace_desc = false; + + FOR_VECTOR (k, ai.Exts) + { + const UString &ext = ai.Exts[k].Ext; + UString mask ("*."); + mask += ext; + + if (finder.FindWord_In_LowCaseAsciiList_NoCase(k_DontSave_Exts, ext)) + continue; + + f.Masks.Add(mask); + masks.Add(mask); + if (needSpace_desc) + f.Description.Add_Space(); + needSpace_desc = true; + f.Description += ext; + } + f.Description += ")"; + if (i != 0) + desc.Add_Space(); + desc += ai.GetMainExt(); + } + + CBrowseFilterInfo &f = filters.AddNew(); + f.Description = LangString(IDT_COMPRESS_ARCHIVE); + if (f.Description.IsEmpty()) + GetItemText(IDT_COMPRESS_ARCHIVE, f.Description); + f.Description.RemoveChar(L'&'); + f.Description += " ("; + f.Description += desc; + f.Description += ")"; + f.Masks = masks; + } + else + { + filterIndex = 0; + const CArcInfoEx &ai = Get_ArcInfoEx(); CBrowseFilterInfo &f = filters.AddNew(); f.Description = ai.Name; f.Description += " ("; bool needSpace_desc = false; - + CStringFinder finder; FOR_VECTOR (k, ai.Exts) { const UString &ext = ai.Exts[k].Ext; UString mask ("*."); mask += ext; - if (finder.FindWord_In_LowCaseAsciiList_NoCase(k_DontSave_Exts, ext)) continue; - f.Masks.Add(mask); - masks.Add(mask); if (needSpace_desc) f.Description.Add_Space(); needSpace_desc = true; f.Description += ext; } f.Description += ")"; - // we use only main ext in desc to reduce the size of list - if (i != 0) - desc.Add_Space(); - desc += ai.GetMainExt(); } - - CBrowseFilterInfo &f = filters.AddNew(); - f.Description = LangString(IDT_COMPRESS_ARCHIVE); // IDS_ARCHIVES_COLON; - if (f.Description.IsEmpty()) - GetItemText(IDT_COMPRESS_ARCHIVE, f.Description); - f.Description.RemoveChar(L'&'); - // f.Description = "archive"; - f.Description += " ("; - f.Description += desc; - f.Description += ")"; - f.Masks = masks; } AddFilter(filters, LangString(IDS_OPEN_TYPE_ALL_FILES), UString("*")); @@ -962,7 +2147,7 @@ void CCompressDialog::OnButtonSetArchive() bi.FilePath = path; if (!bi.BrowseForFile(filters)) - return; + return false; path = bi.FilePath; @@ -974,13 +2159,13 @@ void CCompressDialog::OnButtonSetArchive() path += kExeExt; } else - // if (bi.FilterIndex >= 0) - // if (bi.FilterIndex != filterIndex) - if ((unsigned)bi.FilterIndex < numFormats) + if (allowFormatChange ? ((unsigned)bi.FilterIndex < numFormats) : !filters.IsEmpty()) { - // archive format was confirmed. So we try to set format extension + const unsigned arcIndex = allowFormatChange ? + (unsigned)m_Format.GetItemData((unsigned)bi.FilterIndex) : + GetFormatIndex(); + const CArcInfoEx &ai = (*ArcFormats)[arcIndex]; bool needAddExt = true; - const CArcInfoEx &ai = (*ArcFormats)[(unsigned)m_Format.GetItemData((unsigned)bi.FilterIndex)]; const int dotPos = GetExtDotPos(path); if (dotPos >= 0) { @@ -996,24 +2181,89 @@ void CCompressDialog::OnButtonSetArchive() } } - SetArcPathFields(path); - - if (!isSFX) + if (!isSFX && allowFormatChange) if ((unsigned)bi.FilterIndex < numFormats) if (bi.FilterIndex != m_Format.GetCurSel()) { m_Format.SetCurSel(bi.FilterIndex); - SaveOptionsInMem(); - FormatChanged(true); // isChanged - return; + formatWasChanged = true; } - - ArcPath_WasChanged(path); + + return true; } -// in ExtractDialog.cpp -extern void AddUniqueString(UStringVector &strings, const UString &srcString); +void CCompressDialog::OnButtonSetArchivePath(unsigned index) +{ + if (IsMultiItemMode()) + { + BrowseItemOutputPath(0, index); + return; + } + + if (index == 0) + { + UString path; + if (!GetFinalPath_Smart(path)) + { + ShowErrorMessage(*this, k_IncorrectPathMessage); + return; + } + + SyncExtraArcPathsFromControls(); + + const int prevFormat = m_PrevFormat; + const bool prevWasSFX = IsSFX(); + bool formatWasChanged; + if (!BrowseArchivePath(path, true, formatWasChanged)) + return; + + SetArcPathFields(path); + SyncPrimaryArcPathFromControl(); + + if (formatWasChanged) + { + SaveOptionsInMem(); + FormatChanged(true); // isChanged + UpdateExtraArcPathsForFormatChange(prevFormat, prevWasSFX); + m_PrevFormat = (int)GetFormatIndex(); + return; + } + + ArcPath_WasChanged(path); + return; + } + + SyncPrimaryArcPathFromControl(); + SyncExtraArcPathsFromControls(); + + while (_outputArcPaths.Size() <= index) + _outputArcPaths.Add(UString()); + + UString path = _outputArcPaths[index]; + if (path.IsEmpty()) + { + if (index > 0 && index - 1 < _outputArcPaths.Size()) + path = _outputArcPaths[index - 1]; + if (path.IsEmpty()) + GetFinalPath_Smart(path); + } + + bool formatWasChanged; + if (!BrowseArchivePath(path, false, formatWasChanged)) + return; + + _outputArcPaths[index] = path; + m_ExtraArchivePaths[index - 1].SetText(path); + RefreshArchivePathInfo(); +} + + +void CCompressDialog::OnButtonSetArchive() +{ + OnButtonSetArchivePath(0); +} + static bool IsAsciiString(const UString &s) { @@ -1121,16 +2371,54 @@ void CCompressDialog::OnOK() SaveOptionsInMem(); - UStringVector arcPaths; + if (IsMultiItemMode()) + SyncAllItemOutputGroupsFromControls(); + + const bool separateItemMode = IsMultiItemMode(); + Info.SeparateItemArchives = separateItemMode; + + UStringVector outputArcPaths; + if (separateItemMode) { - UString s; - if (!GetFinalPath_Smart(s)) + UStringVector outputItemPaths; + if (!GetItemArcPaths(outputItemPaths, outputArcPaths)) + { + MessageBoxError(L"Specify one unique output path for each selected item output"); + return; + } + Info.ItemOutputItemPaths = outputItemPaths; + Info.ItemArcPaths = outputArcPaths; + Info.ArcPaths = outputArcPaths; + Info.ArcPath = outputArcPaths.Front(); + + if (IsButtonCheckedBool(IDX_COMPRESS_DEL)) + { + FOR_VECTOR (i, outputItemPaths) + FOR_VECTOR (k, outputItemPaths) + if (i != k && outputItemPaths[i] == outputItemPaths[k]) + { + MessageBoxError(L"Delete after compression is not supported when the same item is mapped to multiple outputs"); + return; + } + } + } + else + { + Info.ItemOutputItemPaths.Clear(); + Info.ItemArcPaths.Clear(); + if (!GetOutputArcPaths(outputArcPaths)) { ShowErrorMessage(*this, k_IncorrectPathMessage); return; } - Info.ArcPath = s; - AddUniqueString(arcPaths, s); + Info.ArcPaths = outputArcPaths; + Info.ArcPath = outputArcPaths.Front(); + } + + if (!separateItemMode && outputArcPaths.Size() > 1 && IsButtonCheckedBool(IDX_COMPRESS_DEL)) + { + MessageBoxError(L"Delete after compression is not supported for multiple output paths"); + return; } Info.UpdateMode = (NCompressDialog::NUpdateMode::EEnum)k_UpdateMode_Vals[m_UpdateMode.GetCurSel()]; @@ -1238,6 +2526,8 @@ void CCompressDialog::OnOK() m_RegistryInfo.ArcType = (*ArcFormats)[Info.FormatIndex].Name; m_RegistryInfo.ShowPassword = IsShowPasswordChecked(); + UStringVector arcPaths; + AddUniqueString(arcPaths, Info.ArcPath); FOR_VECTOR (i, m_RegistryInfo.ArcPaths) { if (arcPaths.Size() >= kHistorySize) @@ -1262,6 +2552,9 @@ void CCompressDialog::OnHelp() void CCompressDialog::ArcPath_WasChanged(const UString &path) { + const int prevFormat = m_PrevFormat; + const bool prevWasSFX = IsSFX(); + SyncExtraArcPathsFromControls(); const int dotPos = GetExtDotPos(path); if (dotPos < 0) return; @@ -1281,6 +2574,8 @@ void CCompressDialog::ArcPath_WasChanged(const UString &path) m_Format.SetCurSel(i); SaveOptionsInMem(); FormatChanged(true); // isChanged + UpdateExtraArcPathsForFormatChange(prevFormat, prevWasSFX); + m_PrevFormat = (int)i; return; } } @@ -1300,7 +2595,15 @@ bool CCompressDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) // if (path == m_RegistryInfo.ArcPaths[select]) { const UString &path = m_RegistryInfo.ArcPaths[select]; - SetArcPathFields(path); + if (_outputArcPaths.IsEmpty()) + _outputArcPaths.Add(path); + else + _outputArcPaths[0] = path; + if (IsMultiItemMode()) + m_ArchivePath.SetText(path); + else + SetArcPathFields(path); + SyncPrimaryArcPathFromControl(); // ArcPath_WasChanged(path); } return 0; @@ -1314,10 +2617,38 @@ bool CCompressDialog::OnCommand(unsigned code, unsigned itemID, LPARAM lParam) { if (code == CBN_SELCHANGE) { + if (IsMultiItemMode()) + { + for (unsigned groupIndex = 1; groupIndex < _itemOutputGroups.Size(); groupIndex++) + if (itemID == GetDynamicGroupCountComboId(groupIndex)) + { + NWindows::NControl::CComboBox countCombo; + countCombo.Attach(GetItem(itemID)); + const int select = countCombo.GetCurSel(); + if (select >= 0) + SetItemOutputGroupCount(groupIndex, (unsigned)select + 1); + return true; + } + } + switch (itemID) { + case IDC_COMPRESS_OUTPUT_PATHS_NUM: + { + const int select = m_OutputPathCount.GetCurSel(); + if (select >= 0) + SetOutputPathCount((unsigned)select + 1); + return true; + } + case IDC_COMPRESS_ARCHIVE: { + if (IsMultiItemMode()) + { + SyncPrimaryArcPathFromControl(); + return true; + } + /* CBN_SELCHANGE is called before actual value of combo text will be changed. So GetText() here returns old value (before change) of combo text. So here we can change all controls except of m_ArchivePath. @@ -1339,9 +2670,19 @@ bool CCompressDialog::OnCommand(unsigned code, unsigned itemID, LPARAM lParam) case IDC_COMPRESS_FORMAT: { const bool isSFX = IsSFX(); + const int prevFormat = m_PrevFormat; + if (IsMultiItemMode()) + SyncAllItemOutputGroupsFromControls(); + else + SyncExtraArcPathsFromControls(); SaveOptionsInMem(); FormatChanged(true); // isChanged - SetArchiveName2(isSFX); + if (!IsMultiItemMode()) + { + SetArchiveName2(isSFX); + SyncPrimaryArcPathFromControl(); + } + UpdateExtraArcPathsForFormatChange(prevFormat, isSFX); return true; } @@ -1375,7 +2716,13 @@ bool CCompressDialog::OnCommand(unsigned code, unsigned itemID, LPARAM lParam) CheckSFXNameChange(); SetMemoryUsage(); if (Get_ArcInfoEx().Flags_HashHandler()) - SetArchiveName2(false); + { + if (!IsMultiItemMode()) + { + SetArchiveName2(false); + SyncPrimaryArcPathFromControl(); + } + } return true; } @@ -1442,9 +2789,20 @@ bool CCompressDialog::OnCommand(unsigned code, unsigned itemID, LPARAM lParam) void CCompressDialog::CheckSFXNameChange() { const bool isSFX = IsSFX(); + const int prevFormat = m_PrevFormat; CheckSFXControlsEnable(); if (isSFX != IsSFX()) - SetArchiveName2(isSFX); + { + if (IsMultiItemMode()) + SyncAllItemOutputGroupsFromControls(); + else + { + SetArchiveName2(isSFX); + SyncPrimaryArcPathFromControl(); + SyncExtraArcPathsFromControls(); + } + UpdateExtraArcPathsForFormatChange(prevFormat, isSFX); + } } void CCompressDialog::SetArchiveName2(bool prevWasSFX) @@ -1495,10 +2853,9 @@ void CCompressDialog::SetArchiveName(const UString &name) } if (IsSFX()) - fileName += kExeExt; + AppendExtensionWithoutDup(fileName, UString(kExeExt)); else { - fileName.Add_Dot(); UString ext = ai.GetMainExt(); if (ai.Flags_HashHandler()) { @@ -1510,7 +2867,10 @@ void CCompressDialog::SetArchiveName(const UString &name) ext.MakeLower_Ascii(); } } - fileName += ext; + UString fullExt; + fullExt.Add_Dot(); + fullExt += ext; + AppendExtensionWithoutDup(fileName, fullExt); } m_ArchivePath.SetText(fileName); } @@ -3377,9 +4737,6 @@ void CCompressDialog::ShowOptionsString() } - - - // ---------- OPTIONS ---------- diff --git a/CPP/7zip/UI/GUI/CompressDialog.h b/CPP/7zip/UI/GUI/CompressDialog.h index e0f3aa5..20a2a32 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.h +++ b/CPP/7zip/UI/GUI/CompressDialog.h @@ -70,6 +70,11 @@ namespace NCompressDialog CBoolPair SetArcMTime; UString ArcPath; // in: Relative or abs ; out: Relative or abs + UStringVector ArcPaths; + UStringVector ItemPaths; + UStringVector ItemOutputItemPaths; + UStringVector ItemArcPaths; + bool SeparateItemArchives; // FString CurrentDirPrefix; bool KeepName; @@ -88,6 +93,7 @@ namespace NCompressDialog SFXMode(false), OpenShareForWrite(false), DeleteAfterCompressing(false), + SeparateItemArchives(false), FormatIndex(-1) { Level = Order = (UInt32)(Int32)-1; @@ -148,9 +154,41 @@ public: CBool1 NtSecurity; CBool1 PreserveATime; private: + struct CLayoutItem + { + unsigned Id; + RECT Rect; + }; + + struct CItemOutputGroup + { + UString ItemPath; + UStringVector ArcPaths; + }; + + static const unsigned kNumOutputPathRows = 5; + bool _ramSize_Defined; + bool _outputLayout_Inited; + UStringVector _outputArcPaths; + CObjectVector _itemOutputGroups; + CRecordVector _outputLayout_Items; + unsigned _outputPathCount; + int _outputPathRowStep; + int _outputLayout_BaseWindowX; + int _outputLayout_BaseWindowY; + int _outputLayout_BaseControlsTop; + int _outputGroupGapY; + RECT _outputTemplate_ItemLabelRect; + RECT _outputTemplate_CountLabelRect; + RECT _outputTemplate_CountComboRect; + RECT _outputTemplate_ArchiveLabelRect; + RECT _outputTemplate_ArchiveComboRects[kNumOutputPathRows]; + RECT _outputTemplate_ArchiveButtonRects[kNumOutputPathRows]; NWindows::NControl::CComboBox m_ArchivePath; + NWindows::NControl::CComboBox m_OutputPathCount; + NWindows::NControl::CComboBox m_ExtraArchivePaths[kNumOutputPathRows - 1]; NWindows::NControl::CComboBox m_Format; NWindows::NControl::CComboBox m_Level; NWindows::NControl::CComboBox m_Method; @@ -339,8 +377,34 @@ public: void UpdatePasswordControl(); bool IsShowPasswordChecked() const { return IsButtonCheckedBool(IDX_PASSWORD_SHOW); } + bool IsMultiItemMode() const { return Info.ItemPaths.Size() > 1; } unsigned GetFormatIndex(); + void InitOutputPathLayout(); + void UpdateOutputPathLayout(); + void UpdateOutputPathControls(); + void RefreshArchivePathInfo(); + void SyncPrimaryArcPathFromControl(); + void SyncExtraArcPathsFromControls(); + void SetOutputPathCount(unsigned count, bool syncFromControls = true); + void SetOutputArcPaths(const UStringVector &paths); + bool GetOutputArcPaths(UStringVector &paths) const; + void InitItemOutputGroups(); + void CreateDynamicItemOutputControls(); + void LayoutItemOutputGroups(); + void UpdateItemOutputGroupControls(unsigned groupIndex); + void SyncItemOutputGroupFromControls(unsigned groupIndex); + void SyncAllItemOutputGroupsFromControls(); + void SetItemOutputGroupCount(unsigned groupIndex, unsigned count, bool syncFromControls = true); + bool GetItemOutputGroupPaths(UStringVector &itemPaths, UStringVector &paths) const; + bool BrowseItemOutputPath(unsigned groupIndex, unsigned pathIndex); + void UpdateSeparateItemModeControls(); + bool GetItemArcPaths(UStringVector &itemPaths, UStringVector &paths) const; + void BuildItemArcPath(const UString &inputPath, UString &path); + void UpdateItemArcPathToCurrentFormat(UString &path, int prevFormat, bool prevWasSFX); + void UpdateArcPathToCurrentFormat(UString &path, int prevFormat, bool prevWasSFX); + void UpdateExtraArcPathsForFormatChange(int prevFormat, bool prevWasSFX); + bool BrowseArchivePath(UString &path, bool allowFormatChange, bool &formatWasChanged); bool SetArcPathFields(const UString &path, UString &name, bool always); bool SetArcPathFields(const UString &path); bool GetFinalPath_Smart(UString &resPath) const; @@ -351,6 +415,7 @@ public: void EnableMultiCombo(unsigned id); void FormatChanged(bool isChanged); + void OnButtonSetArchivePath(unsigned index); void OnButtonSetArchive(); bool IsSFX(); void OnButtonSFX(); @@ -381,11 +446,18 @@ public: INT_PTR Create(HWND wndParent = NULL) { - BIG_DIALOG_SIZE(400, 320); + BIG_DIALOG_SIZE(400, 430); return CModalDialog::Create(SIZED_DIALOG(IDD_COMPRESS), wndParent); } - CCompressDialog() {} + CCompressDialog(): + _ramSize_Defined(false), + _outputLayout_Inited(false), + _outputPathCount(1), + _outputPathRowStep(0), + _outputLayout_BaseWindowX(0), + _outputLayout_BaseWindowY(0) + {} }; diff --git a/CPP/7zip/UI/GUI/CompressDialog.rc b/CPP/7zip/UI/GUI/CompressDialog.rc index df1516c..89a0b02 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.rc +++ b/CPP/7zip/UI/GUI/CompressDialog.rc @@ -2,7 +2,7 @@ #include "../../GuiCommon.rc" #define xc 400 -#define yc 320 +#define yc 430 #undef gSize #undef gSpace @@ -34,7 +34,9 @@ #define g4xs (xc - gSize - gSpace) #define g4xs2 (g4xs - m - m) -#define yOpt 80 +#define yTopShift 105 + +#define yOpt (80 + yTopShift) #define xArcFolderOffs 40 @@ -59,61 +61,78 @@ IDD_COMPRESS DIALOG 0, 0, xs, ys MY_MODAL_DIALOG_STYLE MY_FONT CAPTION "Add to Archive" BEGIN LTEXT "", IDT_COMPRESS_ARCHIVE_FOLDER, m + xArcFolderOffs, m, xc - xArcFolderOffs, 8 - LTEXT "&Archive:", IDT_COMPRESS_ARCHIVE, m, 12, xArcFolderOffs, 8 - COMBOBOX IDC_COMPRESS_ARCHIVE, m + xArcFolderOffs, 18, xc - bxsDots - 12 - xArcFolderOffs, 126, MY_COMBO_WITH_EDIT - PUSHBUTTON "...", IDB_COMPRESS_SET_ARCHIVE, xs - m - bxsDots, 16, bxsDots, bys, WS_GROUP + LTEXT "&Count:", IDT_COMPRESS_OUTPUT_PATHS_NUM, m, 20, xArcFolderOffs, 8 + COMBOBOX IDC_COMPRESS_OUTPUT_PATHS_NUM, m + xArcFolderOffs, 18, 56, 80, MY_COMBO + CONTROL "Map selected items to output archives", IDX_COMPRESS_SEPARATE_ITEMS, MY_CHECKBOX, + m + xArcFolderOffs + 122, 20, xc - xArcFolderOffs - 122, 10 - LTEXT "Archive &format:", IDT_COMPRESS_FORMAT, m, 41, g0xs, 8 - COMBOBOX IDC_COMPRESS_FORMAT, g1x, 39, g1xs, 80, MY_COMBO | CBS_SORT - - LTEXT "Compression &level:", IDT_COMPRESS_LEVEL, m, 62, g0xs, 8 - COMBOBOX IDC_COMPRESS_LEVEL, g1x, 60, g1xs, 80, MY_COMBO - - LTEXT "Compression &method:", IDT_COMPRESS_METHOD, m, 83, g0xs, 8 - COMBOBOX IDC_COMPRESS_METHOD, g1x, 81, g1xs, 80, MY_COMBO + LTEXT "&Archive:", IDT_COMPRESS_ARCHIVE, m, 41, xArcFolderOffs, 8 + COMBOBOX IDC_COMPRESS_ARCHIVE, m + xArcFolderOffs, 39, xc - bxsDots - 12 - xArcFolderOffs, 126, MY_COMBO_WITH_EDIT + PUSHBUTTON "...", IDB_COMPRESS_SET_ARCHIVE, xs - m - bxsDots, 37, bxsDots, bys, WS_GROUP - LTEXT "&Dictionary size:", IDT_COMPRESS_DICTIONARY, m, 104, g0xs, 8 - COMBOBOX IDC_COMPRESS_DICTIONARY, g1x, 102, g1xs, 167, MY_COMBO + COMBOBOX IDC_COMPRESS_ARCHIVE2, m + xArcFolderOffs, 60, xc - bxsDots - 12 - xArcFolderOffs, 126, MY_COMBO_WITH_EDIT + PUSHBUTTON "...", IDB_COMPRESS_SET_ARCHIVE2, xs - m - bxsDots, 58, bxsDots, bys + + COMBOBOX IDC_COMPRESS_ARCHIVE3, m + xArcFolderOffs, 81, xc - bxsDots - 12 - xArcFolderOffs, 126, MY_COMBO_WITH_EDIT + PUSHBUTTON "...", IDB_COMPRESS_SET_ARCHIVE3, xs - m - bxsDots, 79, bxsDots, bys + + COMBOBOX IDC_COMPRESS_ARCHIVE4, m + xArcFolderOffs, 102, xc - bxsDots - 12 - xArcFolderOffs, 126, MY_COMBO_WITH_EDIT + PUSHBUTTON "...", IDB_COMPRESS_SET_ARCHIVE4, xs - m - bxsDots, 100, bxsDots, bys + + COMBOBOX IDC_COMPRESS_ARCHIVE5, m + xArcFolderOffs, 123, xc - bxsDots - 12 - xArcFolderOffs, 126, MY_COMBO_WITH_EDIT + PUSHBUTTON "...", IDB_COMPRESS_SET_ARCHIVE5, xs - m - bxsDots, 121, bxsDots, bys + + LTEXT "Archive &format:", IDT_COMPRESS_FORMAT, m, 41 + yTopShift, g0xs, 8 + COMBOBOX IDC_COMPRESS_FORMAT, g1x, 39 + yTopShift, g1xs, 80, MY_COMBO | CBS_SORT + + LTEXT "Compression &level:", IDT_COMPRESS_LEVEL, m, 62 + yTopShift, g0xs, 8 + COMBOBOX IDC_COMPRESS_LEVEL, g1x, 60 + yTopShift, g1xs, 80, MY_COMBO + + LTEXT "Compression &method:", IDT_COMPRESS_METHOD, m, 83 + yTopShift, g0xs, 8 + COMBOBOX IDC_COMPRESS_METHOD, g1x, 81 + yTopShift, g1xs, 80, MY_COMBO + + LTEXT "&Dictionary size:", IDT_COMPRESS_DICTIONARY, m, 104 + yTopShift, g0xs, 8 + COMBOBOX IDC_COMPRESS_DICTIONARY, g1x, 102 + yTopShift, g1xs, 167, MY_COMBO // LTEXT "&Dictionary size:", IDT_COMPRESS_DICTIONARY, m, 104, DICT_x - m, 16 // 8, SS_LEFTNOWORDWRAP // LTEXT "", IDT_COMPRESS_PARAMS_INFO, m, 283, xs, MY_TEXT_NOPREFIX // CTEXT "-", 0, DICT_x - DICT_SIZE_SPACE, 104, DICT_SIZE_SPACE, 8 // COMBOBOX IDC_COMPRESS_DICTIONARY2, DICT2_x, 102, DICT_SIZE, 140, MY_COMBO // COMBOBOX IDC_COMPRESS_DICTIONARY, DICT_x, 102, DICT_SIZE, 140, MY_COMBO - LTEXT "&Word size:", IDT_COMPRESS_ORDER, m, 125, g0xs, 8 - COMBOBOX IDC_COMPRESS_ORDER, g1x, 123, g1xs, 140, MY_COMBO + LTEXT "&Word size:", IDT_COMPRESS_ORDER, m, 125 + yTopShift, g0xs, 8 + COMBOBOX IDC_COMPRESS_ORDER, g1x, 123 + yTopShift, g1xs, 140, MY_COMBO - LTEXT "&Solid Block size:", IDT_COMPRESS_SOLID, m, 146, g0xs, 8 - COMBOBOX IDC_COMPRESS_SOLID, g1x, 144, g1xs, 140, MY_COMBO + LTEXT "&Solid Block size:", IDT_COMPRESS_SOLID, m, 146 + yTopShift, g0xs, 8 + COMBOBOX IDC_COMPRESS_SOLID, g1x, 144 + yTopShift, g1xs, 140, MY_COMBO - LTEXT "Number of CPU &threads:", IDT_COMPRESS_THREADS, m, 167, g0xs, 8 - COMBOBOX IDC_COMPRESS_THREADS, g1x, 165, g1xs - 40, 140, MY_COMBO - RTEXT "", IDT_COMPRESS_HARDWARE_THREADS, g1x + g1xs - 40, 167, 40, 16, SS_NOPREFIX + LTEXT "Number of CPU &threads:", IDT_COMPRESS_THREADS, m, 167 + yTopShift, g0xs, 8 + COMBOBOX IDC_COMPRESS_THREADS, g1x, 165 + yTopShift, g1xs - 40, 140, MY_COMBO + RTEXT "", IDT_COMPRESS_HARDWARE_THREADS, g1x + g1xs - 40, 167 + yTopShift, 40, 16, SS_NOPREFIX - LTEXT "Memory usage for Compressing:", IDT_COMPRESS_MEMORY, m, 184, g2xs, 8 - COMBOBOX IDC_COMPRESS_MEM_USE, g3x, 188, g3xs, 140, MY_COMBO - LTEXT "", IDT_COMPRESS_MEMORY_VALUE, m, 194, g2xs, MY_TEXT_NOPREFIX + LTEXT "Memory usage for Compressing:", IDT_COMPRESS_MEMORY, m, 184 + yTopShift, g2xs, 8 + COMBOBOX IDC_COMPRESS_MEM_USE, g3x, 188 + yTopShift, g3xs, 140, MY_COMBO + LTEXT "", IDT_COMPRESS_MEMORY_VALUE, m, 194 + yTopShift, g2xs, MY_TEXT_NOPREFIX - LTEXT "Memory usage for Decompressing:", IDT_COMPRESS_MEMORY_DE, m, 208, g2xs, 8 - RTEXT "", IDT_COMPRESS_MEMORY_DE_VALUE, g3x, 208, g3xs, MY_TEXT_NOPREFIX + LTEXT "Memory usage for Decompressing:", IDT_COMPRESS_MEMORY_DE, m, 208 + yTopShift, g2xs, 8 + RTEXT "", IDT_COMPRESS_MEMORY_DE_VALUE, g3x, 208 + yTopShift, g3xs, MY_TEXT_NOPREFIX - LTEXT "Split to &volumes, bytes:", IDT_SPLIT_TO_VOLUMES, m, 225, gSize, 8 - COMBOBOX IDC_COMPRESS_VOLUME, m, 237, gSize, 73, MY_COMBO_WITH_EDIT + LTEXT "Split to &volumes, bytes:", IDT_SPLIT_TO_VOLUMES, m, 225 + yTopShift, gSize, 8 + COMBOBOX IDC_COMPRESS_VOLUME, m, 237 + yTopShift, gSize, 73, MY_COMBO_WITH_EDIT - LTEXT "Parameters:", IDT_COMPRESS_PARAMETERS, m, 256, gSize, 8 - EDITTEXT IDE_COMPRESS_PARAMETERS, m, 268, gSize, 14, ES_AUTOHSCROLL + LTEXT "Parameters:", IDT_COMPRESS_PARAMETERS, m, 256 + yTopShift, gSize, 8 + EDITTEXT IDE_COMPRESS_PARAMETERS, m, 268 + yTopShift, gSize, 14, ES_AUTOHSCROLL - PUSHBUTTON "Options", IDB_COMPRESS_OPTIONS, m, 292, bxs, bys - LTEXT "", IDT_COMPRESS_OPTIONS, m + bxs + m, 294, gSize - bxs - m, 16, SS_NOPREFIX + PUSHBUTTON "Options", IDB_COMPRESS_OPTIONS, m, 292 + yTopShift, bxs, bys + LTEXT "", IDT_COMPRESS_OPTIONS, m + bxs + m, 294 + yTopShift, gSize - bxs - m, 16, SS_NOPREFIX - LTEXT "&Update mode:", IDT_COMPRESS_UPDATE_MODE, g4x, 41, 80, 8 - COMBOBOX IDC_COMPRESS_UPDATE_MODE, g4x + 84, 39, g4xs - 84, 80, MY_COMBO + LTEXT "&Update mode:", IDT_COMPRESS_UPDATE_MODE, g4x, 41 + yTopShift, 80, 8 + COMBOBOX IDC_COMPRESS_UPDATE_MODE, g4x + 84, 39 + yTopShift, g4xs - 84, 80, MY_COMBO - LTEXT "Path mode:", IDT_COMPRESS_PATH_MODE, g4x, 61, 80, 8 - COMBOBOX IDC_COMPRESS_PATH_MODE, g4x + 84, 59, g4xs - 84, 80, MY_COMBO + LTEXT "Path mode:", IDT_COMPRESS_PATH_MODE, g4x, 61 + yTopShift, 80, 8 + COMBOBOX IDC_COMPRESS_PATH_MODE, g4x + 84, 59 + yTopShift, g4xs - 84, 80, MY_COMBO GROUPBOX "Options", IDG_COMPRESS_OPTIONS, g4x, yOpt, g4xs, GROUP_Y_SIZE @@ -147,7 +166,6 @@ BEGIN PUSHBUTTON "Help", IDHELP, bx1, by, bxs, bys END - #ifdef UNDER_CE #undef m diff --git a/CPP/7zip/UI/GUI/CompressDialogRes.h b/CPP/7zip/UI/GUI/CompressDialogRes.h index d04d4b9..c90ce90 100644 --- a/CPP/7zip/UI/GUI/CompressDialogRes.h +++ b/CPP/7zip/UI/GUI/CompressDialogRes.h @@ -27,9 +27,19 @@ #define IDE_COMPRESS_PASSWORD1 120 #define IDE_COMPRESS_PASSWORD2 121 #define IDC_COMPRESS_ENCRYPTION_METHOD 122 +#define IDC_COMPRESS_OUTPUT_PATHS_NUM 125 #define IDT_COMPRESS_ARCHIVE_FOLDER 130 +#define IDC_COMPRESS_ARCHIVE2 180 +#define IDB_COMPRESS_SET_ARCHIVE2 181 +#define IDC_COMPRESS_ARCHIVE3 182 +#define IDB_COMPRESS_SET_ARCHIVE3 183 +#define IDC_COMPRESS_ARCHIVE4 184 +#define IDB_COMPRESS_SET_ARCHIVE4 185 +#define IDC_COMPRESS_ARCHIVE5 186 +#define IDB_COMPRESS_SET_ARCHIVE5 187 + // #define IDB_COMPRESS_OPTIONS 140 #define IDB_COMPRESS_OPTIONS 2100 #define IDT_COMPRESS_OPTIONS 141 @@ -67,6 +77,8 @@ #define IDT_COMPRESS_MEMORY_DE 4018 #define IDX_COMPRESS_DEL 4019 +#define IDT_COMPRESS_OUTPUT_PATHS_NUM 4021 +#define IDX_COMPRESS_SEPARATE_ITEMS 4022 #define IDX_COMPRESS_NT_SYM_LINKS 4040 #define IDX_COMPRESS_NT_HARD_LINKS 4041 diff --git a/CPP/7zip/UI/GUI/UpdateGUI.cpp b/CPP/7zip/UI/GUI/UpdateGUI.cpp index a600a8b..74ca145 100644 --- a/CPP/7zip/UI/GUI/UpdateGUI.cpp +++ b/CPP/7zip/UI/GUI/UpdateGUI.cpp @@ -29,12 +29,35 @@ using namespace NFile; using namespace NDir; static const char * const kDefaultSfxModule = "7z.sfx"; -static const char * const kSFXExtension = "exe"; extern void AddMessageToString(UString &dest, const UString &src); UString HResultToMessage(HRESULT errorCode); +static void AddUniquePath(UStringVector &paths, const UString &path) +{ + FOR_VECTOR (i, paths) + if (path.IsEqualTo_NoCase(paths[i])) + return; + paths.Add(path); +} + +static void PrepareWorkingDirForArchivePath(const UString &userArchivePath, CUpdateOptions &options) +{ + NWorkDir::CInfo workDirInfo; + workDirInfo.Load(); + options.WorkingDir.Empty(); + if (workDirInfo.Mode != NWorkDir::NMode::kCurrent) + { + FString fullPath; + if (!MyGetFullPathName(us2fs(userArchivePath), fullPath)) + return; + FString namePart; + options.WorkingDir = GetWorkDir(workDirInfo, fullPath, namePart); + CreateComplexDir(options.WorkingDir); + } +} + class CThreadUpdating: public CProgressThreadVirt { HRESULT ProcessVirt() Z7_override; @@ -50,15 +73,67 @@ public: HRESULT CThreadUpdating::ProcessVirt() { - CUpdateErrorInfo ei; - HRESULT res = UpdateArchive(codecs, *formatIndices, *cmdArcPath, - *WildcardCensor, *Options, - ei, UpdateCallbackGUI, UpdateCallbackGUI, needSetPath); - FinalMessage.ErrorMessage.Message = ei.Message.Ptr(); - ErrorPaths = ei.FileNames; - if (res != S_OK) - return res; - return HRESULT_FROM_WIN32(ei.SystemError); + if (!Options->SeparateItemMode) + { + CUpdateErrorInfo ei; + HRESULT res = UpdateArchive(codecs, *formatIndices, *cmdArcPath, + *WildcardCensor, *Options, + ei, UpdateCallbackGUI, UpdateCallbackGUI, needSetPath); + FinalMessage.ErrorMessage.Message = ei.Message.Ptr(); + ErrorPaths = ei.FileNames; + if (res != S_OK) + return res; + return HRESULT_FROM_WIN32(ei.SystemError); + } + + if (Options->SeparateItemPaths.IsEmpty() || + Options->SeparateItemPaths.Size() != Options->SeparateItemArchivePaths.Size()) + { + FinalMessage.ErrorMessage.Message = L"Invalid per-item output configuration"; + return E_FAIL; + } + + NUpdateArchive::CActionSet actionSet = NUpdateArchive::k_ActionSet_Add; + if (!Options->Commands.IsEmpty()) + actionSet = Options->Commands.Front().ActionSet; + + FOR_VECTOR (i, Options->SeparateItemPaths) + { + CUpdateOptions options = *Options; + options.SeparateItemMode = false; + options.SeparateItemPaths.Clear(); + options.SeparateItemArchivePaths.Clear(); + options.Commands.Clear(); + + CUpdateArchiveCommand command; + command.ActionSet = actionSet; + command.UserArchivePath = Options->SeparateItemArchivePaths[i]; + options.Commands.Add(command); + + if (!options.SetArcPath(codecs, command.UserArchivePath)) + { + FinalMessage.ErrorMessage.Message = L"Update is not supported"; + return E_NOTIMPL; + } + + PrepareWorkingDirForArchivePath(command.UserArchivePath, options); + + NWildcard::CCensor censor; + censor.AddPreItem_NoWildcard(Options->SeparateItemPaths[i]); + + CUpdateErrorInfo ei; + HRESULT res = UpdateArchive(codecs, *formatIndices, command.UserArchivePath, + censor, options, + ei, UpdateCallbackGUI, UpdateCallbackGUI, false); + FinalMessage.ErrorMessage.Message = ei.Message.Ptr(); + ErrorPaths = ei.FileNames; + if (res != S_OK) + return res; + if (ei.ThereIsError()) + return ei.Get_HRESULT_Error(); + } + + return S_OK; } @@ -318,8 +393,8 @@ static HRESULT ShowDialog( CUpdateOptions &options, CUpdateCallbackGUI *callback, HWND hwndParent) { - if (options.Commands.Size() != 1) - throw "It must be one command"; + if (options.Commands.IsEmpty()) + options.SetActionCommand_Add(); /* FString currentDirPrefix; #ifndef UNDER_CE @@ -418,9 +493,34 @@ static HRESULT ShowDialog( return E_FAIL; } - // di.ArchiveName = options.ArchivePath.GetFinalPath(); - di.ArcPath = options.ArchivePath.GetPathWithoutExt(); + di.ArcPaths.Clear(); + FOR_VECTOR (i, options.Commands) + { + const CUpdateArchiveCommand &command = options.Commands[i]; + UString path = command.UserArchivePath; + if (path.IsEmpty()) + path = command.ArchivePath.GetFinalPath(); + if (!path.IsEmpty()) + di.ArcPaths.Add(path); + } + if (di.ArcPaths.IsEmpty()) + di.ArcPaths.Add(options.ArchivePath.GetFinalPath()); + di.ArcPath = di.ArcPaths.Front(); dialog.OriginalFileName = fs2us(fileInfo.Name); + di.ItemPaths.Clear(); + FOR_VECTOR (i, censor) + { + const NWildcard::CCensorPath &cp = censor[i]; + if (!cp.Include) + continue; + UString path = cp.Path; + path.Trim(); + if (!path.IsEmpty()) + AddUniquePath(di.ItemPaths, path); + } + di.ItemOutputItemPaths = options.SeparateItemPaths; + di.ItemArcPaths = options.SeparateItemArchivePaths; + di.SeparateItemArchives = options.SeparateItemMode; di.PathMode = options.PathMode; @@ -443,7 +543,7 @@ static HRESULT ShowDialog( di.KeepName = !oneFile; - NUpdateArchive::CActionSet &actionSet = options.Commands.Front().ActionSet; + NUpdateArchive::CActionSet actionSet = options.Commands.Front().ActionSet; { int index = FindActionSet(actionSet); @@ -513,30 +613,39 @@ static HRESULT ShowDialog( options.OpenShareForWrite = di.OpenShareForWrite; ParseAndAddPropertires(options.MethodMode.Properties, optionStrings); - if (di.SFXMode) - options.SfxMode = true; + options.SfxMode = di.SFXMode; options.MethodMode.Type = COpenType(); options.MethodMode.Type_Defined = true; options.MethodMode.Type.FormatIndex = di.FormatIndex; - options.ArchivePath.VolExtension = archiverInfo.GetMainExt(); - if (di.SFXMode) - options.ArchivePath.BaseExtension = kSFXExtension; - else - options.ArchivePath.BaseExtension = options.ArchivePath.VolExtension; - options.ArchivePath.ParseFromPath(di.ArcPath, k_ArcNameMode_Smart); - - NWorkDir::CInfo workDirInfo; - workDirInfo.Load(); - options.WorkingDir.Empty(); - if (workDirInfo.Mode != NWorkDir::NMode::kCurrent) + options.Commands.Clear(); + FOR_VECTOR (i, di.ArcPaths) { - FString fullPath; - MyGetFullPathName(us2fs(di.ArcPath), fullPath); - FString namePart; - options.WorkingDir = GetWorkDir(workDirInfo, fullPath, namePart); - CreateComplexDir(options.WorkingDir); + CUpdateArchiveCommand command; + command.ActionSet = actionSet; + command.UserArchivePath = di.ArcPaths[i]; + options.Commands.Add(command); } + if (options.Commands.IsEmpty()) + { + CUpdateArchiveCommand command; + command.ActionSet = actionSet; + command.UserArchivePath = di.ArcPath; + options.Commands.Add(command); + } + + // Paths returned by the dialog are explicit user choices. Re-parse them in + // smart mode so names like "Download" get one ".7z", while "Download.7z" + // doesn't get the archive extension appended twice. + options.ArcNameMode = k_ArcNameMode_Smart; + options.SeparateItemMode = di.SeparateItemArchives; + options.SeparateItemPaths = di.ItemOutputItemPaths; + options.SeparateItemArchivePaths = di.ItemArcPaths; + + if (!options.SetArcPath(codecs, options.Commands.Front().UserArchivePath)) + return E_NOTIMPL; + + PrepareWorkingDirForArchivePath(options.Commands.Front().UserArchivePath, options); return S_OK; } diff --git a/CPP/7zip/UI/GUI/makefile b/CPP/7zip/UI/GUI/makefile index b879a5d..336f104 100644 --- a/CPP/7zip/UI/GUI/makefile +++ b/CPP/7zip/UI/GUI/makefile @@ -147,3 +147,10 @@ C_OBJS = \ !include "../../Sort.mak" !include "../../7zip.mak" + +$O\CompressDialog.obj: CompressDialog.cpp CompressDialog.h CompressDialogRes.h +$O\ArchiveCommandLine.obj: ..\Common\ArchiveCommandLine.cpp ..\Common\Update.h +$O\Update.obj: ..\Common\Update.cpp ..\Common\Update.h +$O\UpdateCallbackGUI.obj: UpdateCallbackGUI.cpp ..\Common\Update.h UpdateCallbackGUI.h +$O\UpdateGUI.obj: UpdateGUI.cpp ..\Common\Update.h UpdateGUI.h CompressDialog.h CompressDialogRes.h +$O\resource.res: resource.rc resource2.rc CompressDialog.rc CompressDialogRes.h diff --git a/CPP/Build.mak b/CPP/Build.mak index 86cc2af..b06ff2a 100644 --- a/CPP/Build.mak +++ b/CPP/Build.mak @@ -237,7 +237,7 @@ $(PROGPATH): $O $O/asm $(OBJS) $(DEF_FILE) !IFNDEF NO_DEFAULT_RES $O\resource.res: $(*B).rc - rc $(RFLAGS) -fo$@ $** + rc $(RFLAGS) -fo$@ $(@B).rc !ENDIF $O\StdAfx.obj: $(*B).cpp $(COMPL_PCH)