From c846d11efa0069c078360ba15abec067615ca0c3 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Sat, 30 May 2026 17:18:45 +0200 Subject: [PATCH] Feature: add menu-driven compress/extract via custom file+folder picker --- server/2015Remote/2015Remote.rc | Bin 155976 -> 156620 bytes server/2015Remote/2015RemoteDlg.cpp | 116 +++++++- server/2015Remote/2015RemoteDlg.h | 4 +- server/2015Remote/2015Remote_vs2015.vcxproj | 5 + .../2015Remote_vs2015.vcxproj.filters | 5 + server/2015Remote/ZstaPickerDlg.cpp | 260 ++++++++++++++++++ server/2015Remote/ZstaPickerDlg.h | 52 ++++ server/2015Remote/lang/en_US.ini | 30 ++ server/2015Remote/lang/zh_TW.ini | 30 ++ server/2015Remote/res/Bitmap/compress.bmp | Bin 0 -> 822 bytes server/2015Remote/res/Bitmap/uncompress.bmp | Bin 0 -> 822 bytes server/2015Remote/resource.h | 14 +- 12 files changed, 511 insertions(+), 5 deletions(-) create mode 100644 server/2015Remote/ZstaPickerDlg.cpp create mode 100644 server/2015Remote/ZstaPickerDlg.h create mode 100644 server/2015Remote/res/Bitmap/compress.bmp create mode 100644 server/2015Remote/res/Bitmap/uncompress.bmp diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc index 5aa5b6e54d68dfb751fd7cb4e3bcd9acdd0f6174..d0513862f01b25f5b0fde87f188650f9ec064d24 100644 GIT binary patch delta 221 zcmX?ci1W;G&W0_F^Y*iJ2W!?(UvPoZWby`Hw&@e3nAoQ8`^d;Ky=Eq(1^44lkhBJa z8bj!G!6l4_(-YP)vQ6Lfk&$b9#Y{%4=?b$Mt+vnG&)6|zdcc21F@9$Te+FNM0EQq2 zSB7AQ;OQ6tF}lkqF=R57Fyt~MG88bx0AVsiK9E&$jg9Ve?_CFC!J{$nNH$s~L delta 28 mcmV+%0OSA6#|g;734pW#p1-%^zyXMum%Jzfu(w)U0&)n|{tZz8 diff --git a/server/2015Remote/2015RemoteDlg.cpp b/server/2015Remote/2015RemoteDlg.cpp index 2132eb2..3e6e813 100644 --- a/server/2015Remote/2015RemoteDlg.cpp +++ b/server/2015Remote/2015RemoteDlg.cpp @@ -637,7 +637,8 @@ CMy2015RemoteDlg::CMy2015RemoteDlg(CWnd* pParent): CDialogLangEx(CMy2015RemoteDl m_bmOnline[52].LoadBitmap(IDB_BITMAP_WEBDESKTOP); m_bmOnline[53].LoadBitmap(IDB_BITMAP_PLUGINCONFIG); m_bmOnline[54].LoadBitmap(IDB_BITMAP_SNAPSHOT); // "播放快照" 菜单的眼睛图标 - + m_bmOnline[55].LoadBitmap(IDB_BITMAP_COMPRESS); + m_bmOnline[56].LoadBitmap(IDB_BITMAP_UNCOMPRESS); for (int i = 0; i < PAYLOAD_MAXTYPE; i++) { m_ServerDLL[i] = nullptr; m_ServerBin[i] = nullptr; @@ -919,6 +920,8 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx) ON_COMMAND(ID_WEB_REMOTE_CONTROL, &CMy2015RemoteDlg::OnWebRemoteControl) ON_COMMAND(ID_PROXY_PORT_AUTORUN, &CMy2015RemoteDlg::OnProxyPortAutorun) ON_COMMAND(ID_SCREENPREVIEW_LOOP, &CMy2015RemoteDlg::OnScreenpreviewLoop) + ON_COMMAND(ID_MENU_COMPRESS, &CMy2015RemoteDlg::OnMenuCompress) + ON_COMMAND(ID_MENU_UNCOMPRESS, &CMy2015RemoteDlg::OnMenuUncompress) END_MESSAGE_MAP() @@ -989,6 +992,8 @@ VOID CMy2015RemoteDlg::CreateSolidMenu() m_MainMenu.SetMenuItemBitmaps(ID_MAIN_NETWORK, MF_BYCOMMAND, &m_bmOnline[29], &m_bmOnline[29]); m_MainMenu.SetMenuItemBitmaps(ID_TRIGGER_SETTINGS, MF_BYCOMMAND, &m_bmOnline[51], &m_bmOnline[51]); m_MainMenu.SetMenuItemBitmaps(ID_MAIN_EXIT, MF_BYCOMMAND, &m_bmOnline[26], &m_bmOnline[26]); + m_MainMenu.SetMenuItemBitmaps(ID_MENU_COMPRESS, MF_BYCOMMAND, &m_bmOnline[55], &m_bmOnline[55]); + m_MainMenu.SetMenuItemBitmaps(ID_MENU_UNCOMPRESS, MF_BYCOMMAND, &m_bmOnline[56], &m_bmOnline[56]); // Tools menu m_MainMenu.SetMenuItemBitmaps(ID_TOOL_INPUT_PASSWORD, MF_BYCOMMAND, &m_bmOnline[30], &m_bmOnline[30]); m_MainMenu.SetMenuItemBitmaps(ID_TOOL_IMPORT_LICENSE, MF_BYCOMMAND, &m_bmOnline[31], &m_bmOnline[31]); @@ -10915,3 +10920,112 @@ void CMy2015RemoteDlg::OnScreenpreviewLoop() ShowMessage(_TR("提示"), msg); } } + +// ===== ZSTA 压缩 / 解压 ===== +#include "ZstdArchive.h" +#include "ZstaPickerDlg.h" + +namespace { + +// 把一组源路径压缩为单个 .zsta 文件(弹出保存对话框选择输出) +// 调用者只负责传路径,输出路径与压缩过程由本函数处理 +void CompressPathsToZsta(CWnd* parent, const std::vector& srcPaths) +{ + if (srcPaths.empty()) return; + + // 默认输出文件名:第一个源的 basename 或 archive + std::string defaultName; + { + std::string first = srcPaths[0]; + while (!first.empty() && (first.back() == '/' || first.back() == '\\')) + first.pop_back(); + size_t pos = first.find_last_of("/\\"); + defaultName = (pos == std::string::npos) ? first : first.substr(pos + 1); + if (srcPaths.size() > 1) defaultName = "archive"; + defaultName += ".zsta"; + } + + CFileDialog saveDlg(FALSE, _T("zsta"), CString(defaultName.c_str()), + OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, + _T("ZSTA Files (*.zsta)|*.zsta|All Files (*.*)|*.*||"), + parent); + CString saveTitle = _TR("选择压缩输出文件"); + saveDlg.m_ofn.lpstrTitle = saveTitle; + if (saveDlg.DoModal() != IDOK) return; + + std::string outPath = std::string(CT2A(saveDlg.GetPathName().GetString())); + auto err = zsta::CZstdArchive::Compress(srcPaths, outPath, 3); + if (err == zsta::Error::Success) { + Mprintf("ZSTA 压缩成功: %u 项 -> %s\n", + (unsigned)srcPaths.size(), outPath.c_str()); + CString msg; + msg.FormatL("压缩成功 (%u 项):\n%s", + (unsigned)srcPaths.size(), outPath.c_str()); + parent->MessageBox(msg, _TR("提示"), MB_OK | MB_ICONINFORMATION); + } else { + Mprintf("ZSTA 压缩失败 [%s]: -> %s\n", + zsta::CZstdArchive::GetErrorString(err), outPath.c_str()); + CString msg; + msg.FormatL("压缩失败: %s", zsta::CZstdArchive::GetErrorString(err)); + parent->MessageBox(msg, _TR("错误"), MB_OK | MB_ICONERROR); + } +} + +} // namespace + +void CMy2015RemoteDlg::OnMenuCompress() +{ + CZstaPickerDlg picker(this); + if (picker.DoModal() != IDOK) return; + if (picker.m_Paths.empty()) { + MessageBoxL("未选择任何文件或文件夹", "提示", MB_OK | MB_ICONINFORMATION); + return; + } + CompressPathsToZsta(this, picker.m_Paths); +} + +void CMy2015RemoteDlg::OnMenuUncompress() +{ + const DWORD MAX_BUF = 64 * 1024; + std::vector buf(MAX_BUF, 0); + CFileDialog dlg(TRUE, _T("zsta"), NULL, + OFN_ALLOWMULTISELECT | OFN_EXPLORER | + OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, + _T("ZSTA Files (*.zsta)|*.zsta|All Files (*.*)|*.*||"), this); + dlg.m_ofn.lpstrFile = buf.data(); + dlg.m_ofn.nMaxFile = MAX_BUF; + CString title = _TR("请选择要解压的 ZSTA 文件 (可多选)"); + dlg.m_ofn.lpstrTitle = title; + if (dlg.DoModal() != IDOK) return; + + int ok = 0, fail = 0; + POSITION pos = dlg.GetStartPosition(); + while (pos) { + CString cpath = dlg.GetNextPathName(pos); + std::string src(CT2A(cpath.GetString())); + + // 目标目录:去掉 .zsta 后缀;若无该后缀则追加 _extract + std::string dst; + if (src.size() >= 5) { + std::string ext = src.substr(src.size() - 5); + for (char& c : ext) c = (char)tolower((unsigned char)c); + if (ext == ".zsta") dst = src.substr(0, src.size() - 5); + } + if (dst.empty()) dst = src + "_extract"; + + auto err = zsta::CZstdArchive::Extract(src, dst); + if (err == zsta::Error::Success) { + ++ok; + Mprintf("ZSTA 解压成功: %s -> %s\n", src.c_str(), dst.c_str()); + } else { + ++fail; + Mprintf("ZSTA 解压失败 [%s]: %s\n", + zsta::CZstdArchive::GetErrorString(err), src.c_str()); + } + } + + CString msg; + msg.FormatL("解压完成: 成功 %d, 失败 %d", ok, fail); + MessageBox(msg, _TR("提示"), + MB_OK | (fail > 0 ? MB_ICONWARNING : MB_ICONINFORMATION)); +} diff --git a/server/2015Remote/2015RemoteDlg.h b/server/2015Remote/2015RemoteDlg.h index a0e53eb..8aad020 100644 --- a/server/2015Remote/2015RemoteDlg.h +++ b/server/2015Remote/2015RemoteDlg.h @@ -364,7 +364,7 @@ public: bool IsDllRequestLimited(const std::string& ip); void RecordDllRequest(const std::string& ip); CMenu m_MainMenu; - CBitmap m_bmOnline[55]; // 21 original + 4 context menu + 2 tray menu + 23 main menu + 3 new menu icons + 1 snapshot + CBitmap m_bmOnline[57]; // 21 original + 4 context menu + 2 tray menu + 25 main menu + 3 new menu icons + 1 snapshot uint64_t m_superID; std::map m_RemoteWnds; FileTransformCmd m_CmdList; @@ -604,4 +604,6 @@ public: afx_msg void OnWebRemoteControl(); afx_msg void OnProxyPortAutorun(); afx_msg void OnScreenpreviewLoop(); + afx_msg void OnMenuCompress(); + afx_msg void OnMenuUncompress(); }; diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj b/server/2015Remote/2015Remote_vs2015.vcxproj index cb57770..405aab8 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj +++ b/server/2015Remote/2015Remote_vs2015.vcxproj @@ -334,6 +334,7 @@ + @@ -459,6 +460,7 @@ + @@ -525,7 +527,9 @@ + + @@ -569,6 +573,7 @@ + diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj.filters b/server/2015Remote/2015Remote_vs2015.vcxproj.filters index e4bcc74..49b78e3 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj.filters +++ b/server/2015Remote/2015Remote_vs2015.vcxproj.filters @@ -19,6 +19,7 @@ + @@ -107,6 +108,7 @@ + @@ -275,6 +277,9 @@ + + + diff --git a/server/2015Remote/ZstaPickerDlg.cpp b/server/2015Remote/ZstaPickerDlg.cpp new file mode 100644 index 0000000..76419cc --- /dev/null +++ b/server/2015Remote/ZstaPickerDlg.cpp @@ -0,0 +1,260 @@ +#include "stdafx.h" +#include "ZstaPickerDlg.h" +#include "LangManager.h" +#include +#include +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + +namespace { + +bool IsDirectoryPath(const std::string& p) +{ + DWORD attr = GetFileAttributesA(p.c_str()); + return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY); +} + +// 构造一个无控件的 DLGTEMPLATE:cdit=0,控件由 OnInitDialog 动态创建。 +void BuildDialogTemplate(std::vector& out, LPCWSTR caption, + short cx, short cy) +{ + out.clear(); + auto append = [&](const void* p, size_t n) { + const BYTE* b = (const BYTE*)p; + out.insert(out.end(), b, b + n); + }; + auto appendW = [&](WORD v) { + out.push_back((BYTE)(v & 0xFF)); + out.push_back((BYTE)((v >> 8) & 0xFF)); + }; + auto appendWStr = [&](LPCWSTR s) { + size_t n = wcslen(s); + append(s, (n + 1) * sizeof(WCHAR)); + }; + + DLGTEMPLATE dt = { 0 }; + dt.style = DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | + WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME; + dt.dwExtendedStyle = 0; + dt.cdit = 0; + dt.x = 0; dt.y = 0; + dt.cx = cx; dt.cy = cy; + append(&dt, sizeof(dt)); + appendW(0); // no menu + appendW(0); // default dialog class + appendWStr(caption); // caption + appendW(8); // font point size + appendWStr(L"MS Shell Dlg"); // typeface + + while (out.size() % 4) out.push_back(0); // DWORD align +} + +} // namespace + + +CZstaPickerDlg::CZstaPickerDlg(CWnd* parent) + : CDialog((LPCTSTR)NULL, parent) +{ +} + +BEGIN_MESSAGE_MAP(CZstaPickerDlg, CDialog) + ON_BN_CLICKED(IDC_ZSTA_ADDFILES, &CZstaPickerDlg::OnAddFiles) + ON_BN_CLICKED(IDC_ZSTA_ADDFOLDERS, &CZstaPickerDlg::OnAddFolders) + ON_BN_CLICKED(IDC_ZSTA_REMOVE, &CZstaPickerDlg::OnRemove) + ON_WM_SIZE() +END_MESSAGE_MAP() + +INT_PTR CZstaPickerDlg::DoModal() +{ + CString title = _TR("选择要压缩的文件 / 文件夹"); + USES_CONVERSION; + BuildDialogTemplate(m_Template, T2CW(title), 360, 220); + InitModalIndirect((LPCDLGTEMPLATE)m_Template.data()); + return CDialog::DoModal(); +} + +BOOL CZstaPickerDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CRect cli; + GetClientRect(&cli); + + // 占位 rect,真正布局在 LayoutControls 里 + CRect r0(0, 0, 10, 10); + + m_List.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | + LVS_REPORT | LVS_SHOWSELALWAYS, + r0, this, IDC_ZSTA_LIST); + m_List.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); + m_List.InsertColumn(0, _TR("类型"), LVCFMT_LEFT, 60); + m_List.InsertColumn(1, _TR("路径"), LVCFMT_LEFT, 400); + + auto mkBtn = [&](CButton& b, LPCTSTR text, int id, DWORD extra = 0) { + b.Create(text, + WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | extra, + r0, this, id); + }; + mkBtn(m_BtnAddFiles, _TR("添加文件..."), IDC_ZSTA_ADDFILES); + mkBtn(m_BtnAddFolders, _TR("添加文件夹..."), IDC_ZSTA_ADDFOLDERS); + mkBtn(m_BtnRemove, _TR("移除选中"), IDC_ZSTA_REMOVE); + mkBtn(m_BtnOK, _TR("确定"), IDOK, BS_DEFPUSHBUTTON); + mkBtn(m_BtnCancel, _TR("取消"), IDCANCEL); + + // 子控件继承对话框的字体 (DS_SETFONT) + HFONT hFont = (HFONT)::SendMessage(GetSafeHwnd(), WM_GETFONT, 0, 0); + if (hFont) { + m_List.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); + m_BtnAddFiles.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); + m_BtnAddFolders.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); + m_BtnRemove.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); + m_BtnOK.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); + m_BtnCancel.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0)); + } + + LayoutControls(cli.Width(), cli.Height()); + RefreshList(); + return TRUE; +} + +void CZstaPickerDlg::OnSize(UINT nType, int cx, int cy) +{ + CDialog::OnSize(nType, cx, cy); + if (m_List.GetSafeHwnd()) LayoutControls(cx, cy); +} + +void CZstaPickerDlg::LayoutControls(int cx, int cy) +{ + const int margin = 10; + const int btnW = 120; + const int btnH = 26; + const int gap = 6; + + int listRight = cx - margin - btnW - margin; + if (listRight < margin + 100) listRight = margin + 100; + + m_List.MoveWindow(margin, margin, listRight - margin, cy - margin * 2); + + int x = listRight + margin; + int y = margin; + m_BtnAddFiles.MoveWindow(x, y, btnW, btnH); y += btnH + gap; + m_BtnAddFolders.MoveWindow(x, y, btnW, btnH); y += btnH + gap; + m_BtnRemove.MoveWindow(x, y, btnW, btnH); + + int bottomY = cy - margin - btnH; + m_BtnCancel.MoveWindow(x, bottomY, btnW, btnH); + m_BtnOK.MoveWindow(x, bottomY - btnH - gap, btnW, btnH); + + // 让"路径"列填满剩余宽度 + if (m_List.GetSafeHwnd()) { + CRect lr; + m_List.GetClientRect(&lr); + int w0 = m_List.GetColumnWidth(0); + int w1 = lr.Width() - w0 - GetSystemMetrics(SM_CXVSCROLL) - 4; + if (w1 > 100) m_List.SetColumnWidth(1, w1); + } +} + +void CZstaPickerDlg::AddPath(const std::string& path) +{ + if (path.empty()) return; + if (std::find(m_Paths.begin(), m_Paths.end(), path) == m_Paths.end()) { + m_Paths.push_back(path); + } +} + +void CZstaPickerDlg::RefreshList() +{ + m_List.DeleteAllItems(); + for (size_t i = 0; i < m_Paths.size(); ++i) { + bool isDir = IsDirectoryPath(m_Paths[i]); + m_List.InsertItem((int)i, isDir ? _TR("文件夹") : _TR("文件")); + m_List.SetItemText((int)i, 1, CString(m_Paths[i].c_str())); + } +} + +void CZstaPickerDlg::OnAddFiles() +{ + const DWORD MAX_BUF = 64 * 1024; + std::vector buf(MAX_BUF, 0); + CFileDialog dlg(TRUE, NULL, NULL, + OFN_ALLOWMULTISELECT | OFN_EXPLORER | + OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, + _T("All Files (*.*)|*.*||"), this); + dlg.m_ofn.lpstrFile = buf.data(); + dlg.m_ofn.nMaxFile = MAX_BUF; + CString title = _TR("选择文件 (可多选)"); + dlg.m_ofn.lpstrTitle = title; + if (dlg.DoModal() != IDOK) return; + + POSITION pos = dlg.GetStartPosition(); + while (pos) { + CString p = dlg.GetNextPathName(pos); + AddPath(std::string(CT2A(p.GetString()))); + } + RefreshList(); +} + +void CZstaPickerDlg::OnAddFolders() +{ + IFileOpenDialog* pfd = nullptr; + HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd)); + if (FAILED(hr) || !pfd) return; + + DWORD flags = 0; + pfd->GetOptions(&flags); + pfd->SetOptions(flags | FOS_PICKFOLDERS | FOS_ALLOWMULTISELECT | + FOS_PATHMUSTEXIST | FOS_FORCEFILESYSTEM); + + USES_CONVERSION; + CString title = _TR("选择文件夹 (可多选)"); + pfd->SetTitle(T2CW(title)); + + if (SUCCEEDED(pfd->Show(GetSafeHwnd()))) { + IShellItemArray* psia = nullptr; + if (SUCCEEDED(pfd->GetResults(&psia)) && psia) { + DWORD count = 0; + psia->GetCount(&count); + for (DWORD i = 0; i < count; ++i) { + IShellItem* psi = nullptr; + if (SUCCEEDED(psia->GetItemAt(i, &psi)) && psi) { + PWSTR wpath = nullptr; + if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &wpath)) && wpath) { + int n = WideCharToMultiByte(CP_ACP, 0, wpath, -1, + NULL, 0, NULL, NULL); + if (n > 1) { + std::string s(n - 1, '\0'); + WideCharToMultiByte(CP_ACP, 0, wpath, -1, + &s[0], n, NULL, NULL); + AddPath(s); + } + CoTaskMemFree(wpath); + } + psi->Release(); + } + } + psia->Release(); + } + } + pfd->Release(); + RefreshList(); +} + +void CZstaPickerDlg::OnRemove() +{ + std::vector indices; + POSITION pos = m_List.GetFirstSelectedItemPosition(); + while (pos) indices.push_back(m_List.GetNextSelectedItem(pos)); + + std::sort(indices.begin(), indices.end(), std::greater()); + for (int idx : indices) { + if (idx >= 0 && idx < (int)m_Paths.size()) { + m_Paths.erase(m_Paths.begin() + idx); + } + } + RefreshList(); +} diff --git a/server/2015Remote/ZstaPickerDlg.h b/server/2015Remote/ZstaPickerDlg.h new file mode 100644 index 0000000..f339470 --- /dev/null +++ b/server/2015Remote/ZstaPickerDlg.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +// 让用户在同一个对话框里累加要压缩的"文件 + 文件夹"组合: +// [添加文件...] 调出多选文件对话框 +// [添加文件夹...] 调出多选文件夹对话框 (IFileOpenDialog + FOS_PICKFOLDERS) +// [移除选中] 从列表里删除 +// DoModal() 返回 IDOK 时,m_Paths 即为结果 (ANSI/MBCS 路径,与 ZSTA 管道一致)。 +class CZstaPickerDlg : public CDialog +{ +public: + explicit CZstaPickerDlg(CWnd* parent = nullptr); + + virtual INT_PTR DoModal(); + + std::vector m_Paths; + +protected: + virtual BOOL OnInitDialog(); + + afx_msg void OnAddFiles(); + afx_msg void OnAddFolders(); + afx_msg void OnRemove(); + afx_msg void OnSize(UINT nType, int cx, int cy); + + DECLARE_MESSAGE_MAP() + +private: + enum CtrlId { + IDC_ZSTA_LIST = 1001, + IDC_ZSTA_ADDFILES = 1002, + IDC_ZSTA_ADDFOLDERS = 1003, + IDC_ZSTA_REMOVE = 1004, + }; + + CListCtrl m_List; + CButton m_BtnAddFiles; + CButton m_BtnAddFolders; + CButton m_BtnRemove; + CButton m_BtnOK; + CButton m_BtnCancel; + + std::vector m_Template; // in-memory DLGTEMPLATE bytes + + void AddPath(const std::string& path); + void RefreshList(); + void LayoutControls(int cx, int cy); +}; diff --git a/server/2015Remote/lang/en_US.ini b/server/2015Remote/lang/en_US.ini index f1ed8fd..6919b96 100644 --- a/server/2015Remote/lang/en_US.ini +++ b/server/2015Remote/lang/en_US.ini @@ -1894,3 +1894,33 @@ FRPC Զ AVIļʧ=Create AVI file failed H264 Ӳ=Enable HW H264 Encoding AV1 Ӳ=Enable HW AV1 Encoding +; ZSTA Compress / Picker Dialog - English Translation +; Format: Simplified Chinese=English + +; --- Picker dialog (CZstaPickerDlg) --- +ѡҪѹļ / ļ=Select Files / Folders to Compress +ļ...=Add Files... +ļ...=Add Folders... +Ƴѡ=Remove Selected +=Type +·=Path +ļ=File +ļ=Folder +ѡļ (ɶѡ)=Select Files (multi-select) +ѡļ (ɶѡ)=Select Folders (multi-select) + +; --- Compress / Extract handlers (CMy2015RemoteDlg) --- +δѡκļļ=No file or folder selected +ѡѹļ=Choose Output Archive +ѡҪѹ ZSTA ļ (ɶѡ)=Choose ZSTA File(s) to Extract (multi-select) +ѹɹ (%u ):\n%s=Compression succeeded (%u item(s)):\n%s +ѹʧ: %s=Compression failed: %s +ѹ: ɹ %d, ʧ %d=Extraction complete: %d succeeded, %d failed + +; --- Common (likely already present in en_US.ini; included for completeness) --- +ȷ=OK +ȡ=Cancel +ʾ=Notice +=Error +ѹ(&C)=&Compress +ѹ(&U)=&Uncompress diff --git a/server/2015Remote/lang/zh_TW.ini b/server/2015Remote/lang/zh_TW.ini index ca519bf..163a7ac 100644 --- a/server/2015Remote/lang/zh_TW.ini +++ b/server/2015Remote/lang/zh_TW.ini @@ -1885,3 +1885,33 @@ FRPC Զ AVIļʧ=AVIļʧ H264 Ӳ= H264 Ӳ AV1 Ӳ= AV1 Ӳ +; ZSTA Compress / Picker Dialog - Traditional Chinese Translation +; Format: Simplified Chinese=Traditional Chinese + +; --- Picker dialog (CZstaPickerDlg) --- +ѡҪѹļ / ļ=xҪsęn / YϊA +ļ...=n... +ļ...=YϊA... +Ƴѡ=Ƴxȡ += +·=· +ļ=n +ļ=YϊA +ѡļ (ɶѡ)=xn (ɶx) +ѡļ (ɶѡ)=xYϊA (ɶx) + +; --- Compress / Extract handlers (CMy2015RemoteDlg) --- +δѡκļļ=δxκΙnYϊA +ѡѹļ=x񉺿sݔn +ѡҪѹ ZSTA ļ (ɶѡ)=ՈxҪ≺s ZSTA n (ɶx) +ѹɹ (%u ):\n%s=sɹ (%u ):\n%s +ѹʧ: %s=sʧ: %s +ѹ: ɹ %d, ʧ %d=≺s: ɹ %d, ʧ %d + +; --- Common (likely already present in zh_TW.ini; included for completeness) --- +ȷ=_ +ȡ=ȡ +ʾ=ʾ +=e` +ѹ(&C)=s(&C) +ѹ(&U)=≺s(&U) diff --git a/server/2015Remote/res/Bitmap/compress.bmp b/server/2015Remote/res/Bitmap/compress.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9ee2665d19070bd21d302971fcaf236a05fcf5bf GIT binary patch literal 822 zcmZ?rHDhJ~12Z700mK4O%*Y@C76%bW_#hZ2@PPuLfdk+%5>p2r#SkfEEd&)KYavoG jb(JBzmKJR4x|T@OARZw}F_Bs@g9wi;n2L$C1&^5khkq(! literal 0 HcmV?d00001 diff --git a/server/2015Remote/res/Bitmap/uncompress.bmp b/server/2015Remote/res/Bitmap/uncompress.bmp new file mode 100644 index 0000000000000000000000000000000000000000..79bd7874db2aeb4fc578c99475a71f1e89abf523 GIT binary patch literal 822 zcmZ?rHDhJ~12Z700mK4O%*Y@C76%bW_#hZ2@PQgY0}O~X38I)tEy#)qYQa=YPz#Z6 nCSDmXH&9EQc$ZMF5|^9tn2f0fk79@vvKE4hk+l%17{4+Ahkq(! literal 0 HcmV?d00001 diff --git a/server/2015Remote/resource.h b/server/2015Remote/resource.h index f93e873..580f564 100644 --- a/server/2015Remote/resource.h +++ b/server/2015Remote/resource.h @@ -263,6 +263,9 @@ #define IDR_WEB_XTERM_CSS 383 #define IDR_WEB_XTERM_FIT_JS 384 #define IDR_WEB_INDEX_HTML 385 +#define IDB_BITMAP_COMPRESS 386 +#define IDB_BITMAP9 387 +#define IDB_BITMAP_UNCOMPRESS 387 #define IDC_MESSAGE 1000 #define IDC_ONLINE 1001 #define IDC_STATIC_TIPS 1002 @@ -985,14 +988,19 @@ #define ID_PARAM_THUMBNAIL_PREVIEW 33050 #define ID_LICENSE_AUTO_FRP 33051 #define ID_LICENSE_REVOKE_FRP 33052 +#define ID_Menu 33053 +#define ID_33054 33054 +#define ID_MENU_COMPRESS 33055 +#define ID_33056 33056 +#define ID_MENU_UNCOMPRESS 33057 #define ID_EXIT_FULLSCREEN 40001 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 386 -#define _APS_NEXT_COMMAND_VALUE 33053 +#define _APS_NEXT_RESOURCE_VALUE 388 +#define _APS_NEXT_COMMAND_VALUE 33058 #define _APS_NEXT_CONTROL_VALUE 2542 #define _APS_NEXT_SYMED_VALUE 105 #endif