261 lines
8.5 KiB
C++
261 lines
8.5 KiB
C++
#include "stdafx.h"
|
||
#include "ZstaPickerDlg.h"
|
||
#include "LangManager.h"
|
||
#include <shobjidl.h>
|
||
#include <atlconv.h>
|
||
#include <algorithm>
|
||
|
||
#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<BYTE>& 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<TCHAR> 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<int> indices;
|
||
POSITION pos = m_List.GetFirstSelectedItemPosition();
|
||
while (pos) indices.push_back(m_List.GetNextSelectedItem(pos));
|
||
|
||
std::sort(indices.begin(), indices.end(), std::greater<int>());
|
||
for (int idx : indices) {
|
||
if (idx >= 0 && idx < (int)m_Paths.size()) {
|
||
m_Paths.erase(m_Paths.begin() + idx);
|
||
}
|
||
}
|
||
RefreshList();
|
||
}
|