702 lines
19 KiB
C++
702 lines
19 KiB
C++
#include "stdafx.h"
|
||
#include "shortcutkeymappingdlg.h"
|
||
#include "IniTools.h"
|
||
#include "CoreTools.h"
|
||
|
||
namespace
|
||
{
|
||
// 辅助函数:将 UTF-8 编码的 char* 转换为 MFC 的 CString
|
||
CString ConvertUTF8ToCString(const char *pUTF8, int nLen)
|
||
{
|
||
if (nLen == 0) return _T("");
|
||
|
||
// 1. 获取转换为 Unicode (UTF-16) 所需的缓冲区大小
|
||
int nWideLen = MultiByteToWideChar(CP_UTF8, 0, pUTF8, nLen, NULL, 0);
|
||
if (nWideLen == 0) return _T("");
|
||
|
||
std::vector<wchar_t> vecWide(nWideLen + 1);
|
||
|
||
// 2. 执行转换: UTF-8 -> UTF-16 (wchar_t)
|
||
MultiByteToWideChar(CP_UTF8, 0, pUTF8, nLen, &vecWide[0], nWideLen);
|
||
vecWide[nWideLen] = 0; // 确保 null 结尾
|
||
|
||
// 3. 将 wchar_t 赋值给 CString
|
||
// 如果工程是 Unicode (默认),CString 就是 wchar_t,直接赋值
|
||
// 如果工程是 MBCS,CString 会自动将 wchar_t 转换为 ANSI
|
||
return CString(&vecWide[0]);
|
||
}
|
||
|
||
std::string ConvertCStringToUTF8(const CString &strContent)
|
||
{
|
||
#ifdef _UNICODE
|
||
const wchar_t *pSrc = strContent.GetString();
|
||
int nLen = strContent.GetLength();
|
||
#else
|
||
CA2W wStr(strContent);
|
||
const wchar_t *pSrc = wStr;
|
||
int nLen = wcslen(pSrc);
|
||
#endif
|
||
if (nLen == 0) return "";
|
||
int nUTF8Len = WideCharToMultiByte(CP_UTF8, 0, pSrc, nLen, NULL, 0, NULL, NULL);
|
||
if (nUTF8Len == 0) return "";
|
||
std::string strUTF8(nUTF8Len, 0);
|
||
WideCharToMultiByte(CP_UTF8, 0, pSrc, nLen, &strUTF8[0], nUTF8Len, NULL, NULL);
|
||
return strUTF8;
|
||
}
|
||
|
||
}
|
||
|
||
IMPLEMENT_DYNAMIC(CShortcutKeyMappingDlg, CDialog)
|
||
|
||
CShortcutKeyMappingDlg::CShortcutKeyMappingDlg(CWnd* pParent /*=NULL*/)
|
||
: CDialog(CShortcutKeyMappingDlg::IDD, pParent) { }
|
||
|
||
CShortcutKeyMappingDlg::~CShortcutKeyMappingDlg() { }
|
||
|
||
void CShortcutKeyMappingDlg::DoDataExchange(CDataExchange* pDX)
|
||
{
|
||
CDialog::DoDataExchange(pDX);
|
||
DDX_Control(pDX, IDC_STATIC_LIST, m_wndStatic);
|
||
}
|
||
|
||
BEGIN_MESSAGE_MAP(CShortcutKeyMappingDlg, CDialog)
|
||
ON_BN_CLICKED(IDOK, &CShortcutKeyMappingDlg::OnBnClickedOk)
|
||
ON_BN_CLICKED(IDC_EXPORT, &CShortcutKeyMappingDlg::OnBnClickedExport)
|
||
ON_BN_CLICKED(IDC_IMPORT, &CShortcutKeyMappingDlg::OnBnClickedImport)
|
||
ON_NOTIFY(TVN_ENDLABELEDIT, IDC_SHORTCUT_MAPPING_LIST, &CShortcutKeyMappingDlg::OnEditEnd)
|
||
ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_SHORTCUT_MAPPING_LIST, &CShortcutKeyMappingDlg::OnEditBegin)
|
||
END_MESSAGE_MAP()
|
||
|
||
void CShortcutKeyMappingDlg::OnEditEnd(NMHDR * pNMHDR, LRESULT * pResult)
|
||
{
|
||
*pResult = 0; // 默认成功
|
||
|
||
NMTVDISPINFO *pItem = (NMTVDISPINFO*)pNMHDR;
|
||
CString sNewText = pItem->item.pszText;
|
||
if (sNewText.IsEmpty())
|
||
{
|
||
*pResult = 1;
|
||
return;
|
||
}
|
||
|
||
CString sCmd = m_wndMappingLst.GetItemText(pItem->item.hItem, 1);
|
||
|
||
std::map<CString, CString>::iterator it(m_pgpCmdMapping.begin());
|
||
for (; it != m_pgpCmdMapping.end(); ++it)
|
||
{
|
||
if (it->first.CompareNoCase(sCmd) != 0 && it->second.CompareNoCase(sNewText) == 0)
|
||
{
|
||
CString sMsg;
|
||
sMsg.Format(_T("“%s”与命令:“%s”的快捷键相同,请重新修改!"), sNewText, sCmd);
|
||
AfxMessageBox(sMsg);
|
||
*pResult = 1; // 失败
|
||
return;
|
||
}
|
||
}
|
||
|
||
m_pgpCmdMapping[sCmd] = sNewText;
|
||
}
|
||
|
||
void CShortcutKeyMappingDlg::OnEditBegin(NMHDR *pNMHDR, LRESULT *pResult)
|
||
{
|
||
NMTVDISPINFO *pItem = (NMTVDISPINFO*)pNMHDR;
|
||
m_sItemOldText = pItem->item.pszText;
|
||
}
|
||
|
||
void CShortcutKeyMappingDlg::initListCtrl()
|
||
{
|
||
CRect rc;
|
||
m_wndStatic.GetWindowRect(rc);
|
||
ScreenToClient(rc);
|
||
m_wndStatic.ShowWindow(SW_HIDE);
|
||
|
||
unsigned uStyle = TVS_HASBUTTONS
|
||
|TVS_HASBUTTONS
|
||
|TVS_HASLINES
|
||
|TVS_LINESATROOT
|
||
|TVS_EDITLABELS
|
||
|TVS_FULLROWSELECT;
|
||
|
||
unsigned uExStyle = TVS_EX_ITEMLINES
|
||
|TVS_EX_ITEMLINES
|
||
|TVS_EX_ALTERNATECOLOR
|
||
|TVS_EX_SUBSELECT
|
||
|TVS_EX_FULLROWMARK;
|
||
|
||
m_wndMappingLst.Create(uStyle|WS_CHILD|WS_VISIBLE|WS_BORDER, CRect(0, 0, 0, 0), this, IDC_SHORTCUT_MAPPING_LIST);
|
||
if (!m_wndMappingLst.GetSafeHwnd())
|
||
return;
|
||
m_wndMappingLst.SetExtendedStyle(uExStyle);
|
||
|
||
m_wndMappingLst.MoveWindow(rc);
|
||
|
||
CBitmap bmp;
|
||
bmp.Attach(::LoadBitmap(_hdllInstance, MAKEINTRESOURCE(IDB_MENUSTATE)));
|
||
m_imageList.Create(12, 12, ILC_COLOR24|ILC_MASK, 0, 0);
|
||
m_imageList.Add(&bmp, RGB(255, 0, 255));
|
||
|
||
m_wndMappingLst.SetImageList(&m_imageList,TVSIL_NORMAL);
|
||
|
||
double dScale = CoreTools::GetDPIScale();
|
||
|
||
LV_COLUMN lvColumn;
|
||
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
|
||
|
||
lvColumn.cx = 200 * dScale;
|
||
lvColumn.pszText = const_cast<LPTSTR>(_T("菜单名称"));
|
||
lvColumn.fmt = LVCFMT_LEFT;
|
||
m_wndMappingLst.InsertColumn(0, &lvColumn);
|
||
|
||
lvColumn.cx = 100 * dScale;
|
||
lvColumn.pszText = const_cast<LPTSTR>(_T("命令名称"));
|
||
lvColumn.fmt = LVCFMT_CENTER;
|
||
m_wndMappingLst.InsertColumn(1, &lvColumn);
|
||
|
||
lvColumn.pszText = const_cast<LPTSTR>(_T("快捷按键"));
|
||
m_wndMappingLst.InsertColumn(2, &lvColumn);
|
||
m_wndMappingLst.SetColumnAutoEdit(2, TVAE_EDIT | TVAE_DBLCLICK);
|
||
|
||
}
|
||
|
||
void CShortcutKeyMappingDlg::OnBnClickedOk()
|
||
{
|
||
UpdateData();
|
||
WriteToAcadPGPFile();
|
||
AfxMessageBox(_T("在弹出的对话框中,勾选上 “PGP 文件(F)” 后点击 “确定” 按钮。"));
|
||
acedPostCommand(_T("_REINIT "));
|
||
OnOK();
|
||
}
|
||
|
||
void CShortcutKeyMappingDlg::OnBnClickedExport()
|
||
{
|
||
#if ARX > 18
|
||
CString pgpFilePath = FilePath::GetSysDir() + _T("\\acad.pgp");
|
||
// 1. 基本校验:确保源文件存在
|
||
if (_taccess(pgpFilePath, 0) != 0)
|
||
{
|
||
AfxMessageBox(_T("源文件不存在,无法导出!"));
|
||
return;
|
||
}
|
||
|
||
// 2. 弹出文件夹选择对话框
|
||
// 参数1: 默认路径(NULL),参数2: 标志,参数3: 父窗口(NULL),参数4: 安全属性
|
||
CFolderPickerDialog dlg(NULL, OFN_FILEMUSTEXIST, NULL, 0);
|
||
dlg.m_ofn.lpstrTitle = _T("请选择导出文件夹");
|
||
|
||
if (dlg.DoModal() != IDOK)
|
||
{
|
||
return; // 用户取消
|
||
}
|
||
|
||
CString strDestFolder = dlg.GetPathName();
|
||
|
||
// 3. 获取源文件名
|
||
// 例如 C:\User\acad.pgp -> acad.pgp
|
||
CString strSrcPath(pgpFilePath);
|
||
int nPos = strSrcPath.ReverseFind('\\');
|
||
CString strFileName;
|
||
if (nPos != -1)
|
||
strFileName = strSrcPath.Mid(nPos + 1);
|
||
else
|
||
strFileName = strSrcPath; // 只有文件名的情况
|
||
|
||
// 4. 构建目标路径
|
||
// 简单的路径拼接,处理末尾斜杠问题
|
||
CString strDestPath = strDestFolder;
|
||
if (strDestPath.Right(1) != _T("\\"))
|
||
strDestPath += _T("\\");
|
||
strDestPath += strFileName;
|
||
|
||
// 检查源路径和目标路径是否完全一致(防止用户选了同一个文件夹)
|
||
if (strSrcPath.CompareNoCase(strDestPath) == 0)
|
||
{
|
||
AfxMessageBox(_T("源文件和目标文件路径相同,无需导出。"));
|
||
return;
|
||
}
|
||
|
||
// 5. 检查同名文件并询问覆盖
|
||
if (_taccess(strDestPath, 0) == 0)
|
||
{
|
||
CString strMsg;
|
||
strMsg.Format(_T("目标文件夹已存在文件:\n%s\n\n是否覆盖该文件?"), strFileName);
|
||
|
||
// IDYES, IDNO
|
||
if (AfxMessageBox(strMsg, MB_YESNO | MB_ICONQUESTION) == IDNO)
|
||
{
|
||
return; // 用户选择不覆盖,取消操作
|
||
}
|
||
}
|
||
|
||
// 6. 执行复制
|
||
// CopyFile 参数3: FALSE 表示如果文件存在则直接覆盖(因为我们上面已经询问过用户了)
|
||
// 如果上面不询问,这里设为 TRUE 会导致复制失败
|
||
if (::CopyFile(pgpFilePath, strDestPath, FALSE))
|
||
{
|
||
AfxMessageBox(_T("导出成功!"));
|
||
}
|
||
else
|
||
{
|
||
// 获取错误信息
|
||
DWORD dwError = GetLastError();
|
||
CString strErr;
|
||
strErr.Format(_T("导出失败,错误代码:%d"), dwError);
|
||
AfxMessageBox(strErr, MB_ICONERROR);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void CShortcutKeyMappingDlg::OnBnClickedImport()
|
||
{
|
||
// 1. 获取系统目录下的目标文件路径
|
||
CString strDestPath = FilePath::GetSysDir() + _T("\\acad.pgp");
|
||
|
||
// 2. 配置文件打开对话框
|
||
// 参数1: TRUE 为 "打开" 对话框
|
||
// 参数2: 默认扩展名 "pgp"
|
||
// 参数3: 默认文件名 NULL
|
||
// 参数4: 标志位 (文件必须存在 | 隐藏只读复选框)
|
||
// 参数5: 过滤器
|
||
LPCTSTR szFilter = _T("PGP Files (*.pgp)|*.pgp|All Files (*.*)|*.*||");
|
||
CFileDialog dlg(TRUE, _T("pgp"), NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilter, this);
|
||
|
||
dlg.m_ofn.lpstrTitle = _T("请选择要导入的 PGP 文件");
|
||
|
||
// 3. 显示对话框
|
||
if (dlg.DoModal() != IDOK)
|
||
{
|
||
return; // 用户取消
|
||
}
|
||
|
||
CString strSrcPath = dlg.GetPathName();
|
||
|
||
// 4. 校验:不能选择目标文件自己来覆盖自己
|
||
if (strSrcPath.CompareNoCase(strDestPath) == 0)
|
||
{
|
||
AfxMessageBox(_T("不能选择当前正在使用的文件进行导入!\n请选择其他备份文件。"), MB_ICONWARNING);
|
||
return;
|
||
}
|
||
|
||
// 5. 覆盖确认提示
|
||
CString strMsg;
|
||
strMsg.Format(_T("导入操作将使用选中的文件覆盖当前的配置:\n\n%s\n\n此操作不可撤销,是否继续?"), strDestPath);
|
||
if (AfxMessageBox(strMsg, MB_YESNO | MB_ICONQUESTION) != IDYES)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 6. 执行文件复制 (覆盖)
|
||
// 第三个参数 FALSE 表示:如果目标文件已存在,不失败(即允许覆盖)
|
||
if (::CopyFile(strSrcPath, strDestPath, FALSE))
|
||
{
|
||
// 7. 导入成功后的处理
|
||
AfxMessageBox(_T("导入成功!"));
|
||
|
||
// 【关键步骤】
|
||
// 此时磁盘上的文件已经变了,但内存里的 map 还是旧的。
|
||
// 你需要清空 map 并重新解析新文件,然后刷新列表控件。
|
||
|
||
m_wndMappingLst.DeleteAllItems();
|
||
m_pgpCmdMapping.clear(); // 清空你的 map
|
||
ParseAcadPGPFile(); // 重新解析
|
||
LoadMenus();
|
||
}
|
||
else
|
||
{
|
||
// 获取错误代码
|
||
DWORD dwError = GetLastError();
|
||
CString strErr;
|
||
strErr.Format(_T("导入失败,无法覆盖文件。\n错误代码:%d"), dwError);
|
||
AfxMessageBox(strErr, MB_ICONERROR);
|
||
}
|
||
}
|
||
|
||
BOOL CShortcutKeyMappingDlg::OnInitDialog()
|
||
{
|
||
CDialog::OnInitDialog();
|
||
|
||
initListCtrl();
|
||
|
||
ParseAcadPGPFile();
|
||
|
||
LoadMenus();
|
||
|
||
UpdateData(FALSE);
|
||
return TRUE;
|
||
}
|
||
|
||
void CShortcutKeyMappingDlg::start()
|
||
{
|
||
CAcModuleResourceOverride myResources;
|
||
CShortcutKeyMappingDlg dlg;
|
||
dlg.DoModal();
|
||
}
|
||
|
||
// 解析单个菜单文件
|
||
void CShortcutKeyMappingDlg::ParseMenuFile(const CString& strFilePath, const CString& strFileAlias, HTREEITEM hParent)
|
||
{
|
||
CStdioFile file;
|
||
// 尝试打开文件
|
||
if (!file.Open(strFilePath, CFile::modeRead | CFile::shareDenyNone | CFile::typeText))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 插入根节点(文件名中文别名)
|
||
HTREEITEM hRoot = m_wndMappingLst.InsertItem(strFileAlias, 1, 1, TVI_ROOT, TVI_LAST);
|
||
HTREEITEM hCurrentParent = hRoot;
|
||
|
||
std::map<CString, CString>::const_iterator itFind(m_pgpCmdMapping.end());
|
||
CString strLine;
|
||
while (file.ReadString(strLine))
|
||
{
|
||
strLine.Trim();
|
||
if (strLine.IsEmpty()) continue;
|
||
|
||
// 忽略分隔符
|
||
if (strLine.Find(_T("[--]")) >= 0) continue;
|
||
if (strLine.Find(_T("//")) == 0) continue;
|
||
if (strLine.Find(_T("**")) == 0) continue;
|
||
if (strLine.Find(_T("ID_SCREEN")) == 0) continue;
|
||
if (strLine.Find(_T("***RESOURCE")) == 0) break;
|
||
|
||
// 查找 [] 结构
|
||
int nStart = strLine.Find(_T('['));
|
||
int nEnd = strLine.Find(_T(']'));
|
||
|
||
// 如果没有合法的 [],则视为无效行或头部垃圾信息,跳过
|
||
if (nStart == -1 || nEnd == -1 || nEnd <= nStart)
|
||
continue;
|
||
|
||
// 1. 提取显示名称 (去掉 [])
|
||
CString strMenuText = strLine.Mid(nStart + 1, nEnd - nStart - 1);
|
||
strMenuText.Trim();
|
||
|
||
// 2. 提取命令 (] 之后的部分)
|
||
CString strCommand = strLine.Mid(nEnd + 1);
|
||
strCommand.Trim();
|
||
|
||
// 3. 判断层级标记 -> 或 <-
|
||
bool bIsSubStart = (strMenuText.Left(2) == _T("->"));
|
||
bool bIsSubEnd = (strMenuText.Left(2) == _T("<-"));
|
||
|
||
// 去掉标记符号,获取纯文本名称
|
||
if (bIsSubStart || bIsSubEnd)
|
||
{
|
||
strMenuText = strMenuText.Mid(2);
|
||
strMenuText.Trim();
|
||
}
|
||
|
||
// 4. 插入节点
|
||
HTREEITEM hNewItem = NULL;
|
||
|
||
if (!strMenuText.IsEmpty())
|
||
{
|
||
// 在当前父节点下插入
|
||
hNewItem = m_wndMappingLst.InsertItem(
|
||
strMenuText
|
||
, strCommand.IsEmpty() ? 2 : 3
|
||
, strCommand.IsEmpty() ? 2 : 3
|
||
, hCurrentParent);
|
||
|
||
// 如果有命令,设置到第1列 (索引1,对应“命令名称”)
|
||
if (!strCommand.IsEmpty())
|
||
{
|
||
m_wndMappingLst.SetItemText(hNewItem, strCommand, 1);
|
||
|
||
strCommand.MakeUpper();
|
||
itFind = m_pgpCmdMapping.find(strCommand);
|
||
if (itFind != m_pgpCmdMapping.end())
|
||
m_wndMappingLst.SetItemText(hNewItem, itFind->second, 2);
|
||
else
|
||
m_wndMappingLst.SetItemText(hNewItem, _T(""), 2);
|
||
}
|
||
}
|
||
|
||
// 5. 调整层级关系
|
||
if (bIsSubStart)
|
||
{
|
||
// 遇到 ->,说明刚才插入的项是下一级菜单的父节点
|
||
if (hNewItem)
|
||
{
|
||
hCurrentParent = hNewItem;
|
||
}
|
||
}
|
||
else if (bIsSubEnd)
|
||
{
|
||
// 遇到 <-,说明本项是当前层级的最后一项,或者本项结束后返回上一层
|
||
// 逻辑通常是:插入当前项,然后层级回退
|
||
|
||
HTREEITEM hGrandParent = m_wndMappingLst.GetParentItem(hCurrentParent);
|
||
if (hGrandParent)
|
||
{
|
||
hCurrentParent = hGrandParent;
|
||
}
|
||
else
|
||
{
|
||
// 如果已经退无可退,就回到根节点
|
||
hCurrentParent = hRoot;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 展开根节点
|
||
m_wndMappingLst.Expand(hRoot, TVE_EXPAND);
|
||
file.Close();
|
||
}
|
||
|
||
// 加载菜单主逻辑
|
||
void CShortcutKeyMappingDlg::LoadMenus()
|
||
{
|
||
std::vector<std::pair<CString, CString>> mm;
|
||
IniTools::getSectionContent(FilePath::GetSysDir()+_T("\\product.ini")
|
||
, _T("MENUS")
|
||
, mm);
|
||
if (mm.empty()) return;
|
||
|
||
CString menuPath = FilePath::GetSysDir() + _T("\\");
|
||
std::vector<std::pair<CString, CString>>::const_iterator it(mm.begin());
|
||
for (; it != mm.end(); ++it)
|
||
{
|
||
ParseMenuFile(menuPath + it->first + _T(".mnu"), it->second, NULL);
|
||
}
|
||
}
|
||
|
||
void CShortcutKeyMappingDlg::ParseAcadPGPFile()
|
||
{
|
||
CFile file;
|
||
CFileException ex;
|
||
|
||
// 1. 打开文件
|
||
if (!file.Open(FilePath::GetSysDir() + _T("\\acad.pgp")
|
||
, CFile::modeRead | CFile::shareDenyNone
|
||
, &ex))
|
||
{
|
||
TRACE(_T("无法打开文件: acad.pgp\n"));
|
||
return;
|
||
}
|
||
|
||
ULONGLONG uFileSize = file.GetLength();
|
||
if (uFileSize == 0)
|
||
{
|
||
file.Close();
|
||
return;
|
||
}
|
||
|
||
// 2. 读取二进制数据
|
||
std::vector<char> vecBuffer((size_t)uFileSize + 1);
|
||
file.Read(&vecBuffer[0], (UINT)uFileSize);
|
||
vecBuffer[(size_t)uFileSize] = 0;
|
||
file.Close();
|
||
|
||
// 处理 BOM
|
||
int nOffset = 0;
|
||
if (uFileSize >= 3 &&
|
||
(unsigned char)vecBuffer[0] == 0xEF &&
|
||
(unsigned char)vecBuffer[1] == 0xBB &&
|
||
(unsigned char)vecBuffer[2] == 0xBF)
|
||
{
|
||
nOffset = 3;
|
||
}
|
||
|
||
// 3. 转换为 CString
|
||
CString strContent = ConvertUTF8ToCString(&vecBuffer[nOffset], (int)(uFileSize - nOffset));
|
||
|
||
// 4. 解析逻辑
|
||
int nCurPos = 0;
|
||
CString strLine;
|
||
bool bMappingSection = false;
|
||
|
||
// 标记字符串
|
||
const CString strBeginMarker = _T(";shanghua soft command shortcut key mapping begin");
|
||
const CString strEndMarker = _T(";shanghua soft command shortcut key mapping end");
|
||
|
||
while (!(strLine = strContent.Tokenize(_T("\r\n"), nCurPos)).IsEmpty())
|
||
{
|
||
// 先备份原始行用于判断标记(因为标记行本身以 ; 开头,不能先去注释)
|
||
CString strRawLine = strLine;
|
||
strRawLine.Trim();
|
||
|
||
// ---------------------------------------------------------
|
||
// A. 检查区块标记 (Start / End)
|
||
// ---------------------------------------------------------
|
||
if (strRawLine.CompareNoCase(strBeginMarker) == 0)
|
||
{
|
||
bMappingSection = true;
|
||
continue; // 跳过开始标记行
|
||
}
|
||
|
||
if (strRawLine.CompareNoCase(strEndMarker) == 0)
|
||
{
|
||
bMappingSection = false;
|
||
break; // 遇到结束标记,停止解析
|
||
}
|
||
|
||
// 如果还没进入有效区域,跳过
|
||
if (!bMappingSection)
|
||
continue;
|
||
|
||
// ---------------------------------------------------------
|
||
// B. 去除注释 (核心修改)
|
||
// ---------------------------------------------------------
|
||
// 查找分号位置
|
||
int nCommentPos = strLine.Find(_T(';'));
|
||
if (nCommentPos != -1)
|
||
{
|
||
// 截取分号前面的部分
|
||
// 例如 "DJ, *PL_QTDJ ;注释" -> "DJ, *PL_QTDJ "
|
||
// 例如 ";整行注释" -> "" (空字符串)
|
||
strLine = strLine.Left(nCommentPos);
|
||
}
|
||
|
||
// 去除截取后可能产生的尾部空格
|
||
strLine.Trim();
|
||
|
||
// 如果去注释后为空行,说明该行无数据
|
||
if (strLine.IsEmpty())
|
||
continue;
|
||
|
||
// ---------------------------------------------------------
|
||
// C. 解析 Key 和 Value
|
||
// ---------------------------------------------------------
|
||
// 格式: VIEWPORTS, *VPORTS
|
||
int nCommaPos = strLine.Find(_T(','));
|
||
int nStarPos = strLine.Find(_T('*'));
|
||
|
||
// 确保既有逗号又有星号,且星号在逗号后面(通常情况)
|
||
if (nCommaPos != -1 && nStarPos != -1)
|
||
{
|
||
// 提取 Value: 逗号前的部分
|
||
CString strValue = strLine.Left(nCommaPos);
|
||
|
||
// 提取 Key: 星号后的部分
|
||
CString strKey = strLine.Mid(nStarPos + 1);
|
||
|
||
// 清理空格
|
||
strValue.Trim();
|
||
strKey.Trim();
|
||
|
||
if (!strKey.IsEmpty())
|
||
{
|
||
// 存入 Map
|
||
// 注意:如果 Key 重复,这里会覆盖旧值,根据需求可调整
|
||
m_pgpCmdMapping[strKey.MakeUpper()] = strValue;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void CShortcutKeyMappingDlg::WriteToAcadPGPFile()
|
||
{
|
||
const CString strBeginMarker = _T(";shanghua soft command shortcut key mapping begin");
|
||
const CString strEndMarker = _T(";shanghua soft command shortcut key mapping end");
|
||
|
||
// 1. 读取原文件(为了保留 begin 之前和 end 之后的内容)
|
||
CString strHeader, strFooter;
|
||
bool bFoundSection = false;
|
||
|
||
CString path(FilePath::GetSysDir() + _T("\\acad.pgp"));
|
||
|
||
CFile fileRead;
|
||
CFileException ex;
|
||
// 尝试打开读取,如果文件不存在则视为新文件
|
||
if (fileRead.Open( path , CFile::modeRead | CFile::shareDenyNone , &ex))
|
||
{
|
||
ULONGLONG uLen = fileRead.GetLength();
|
||
if (uLen > 0)
|
||
{
|
||
std::vector<char> vecBuf((size_t)uLen + 1);
|
||
fileRead.Read(&vecBuf[0], (UINT)uLen);
|
||
vecBuf[(size_t)uLen] = 0;
|
||
fileRead.Close();
|
||
|
||
// 跳过 BOM
|
||
int nOffset = 0;
|
||
if (uLen >= 3 && (unsigned char)vecBuf[0] == 0xEF && (unsigned char)vecBuf[1] == 0xBB && (unsigned char)vecBuf[2] == 0xBF)
|
||
nOffset = 3;
|
||
|
||
CString strFullContent = ConvertUTF8ToCString(&vecBuf[nOffset], (int)(uLen - nOffset));
|
||
|
||
int nBeginPos = strFullContent.Find(strBeginMarker);
|
||
int nEndPos = strFullContent.Find(strEndMarker);
|
||
|
||
if (nBeginPos != -1 && nEndPos != -1 && nEndPos > nBeginPos)
|
||
{
|
||
// 标记存在,切分头部和尾部
|
||
strHeader = strFullContent.Left(nBeginPos + strBeginMarker.GetLength());
|
||
strFooter = strFullContent.Mid(nEndPos);
|
||
bFoundSection = true;
|
||
}
|
||
else
|
||
{
|
||
// 标记不完整,保留全文作为 Header
|
||
strHeader = strFullContent;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fileRead.Close();
|
||
}
|
||
}
|
||
|
||
// 2. 构建中间数据段
|
||
CString strMiddle;
|
||
strMiddle += _T("\r\n"); // Begin 标记后换行
|
||
|
||
// -----------------------------------------------------------
|
||
// 核心逻辑修正区
|
||
// Input Mapping: Key = 命令 (Command), Value = 快捷键 (Shortcut)
|
||
// Target Format: 快捷键, *命令 (Shortcut, *Command)
|
||
// -----------------------------------------------------------
|
||
std::map<CString, CString>::const_iterator it(m_pgpCmdMapping.begin());
|
||
for (; it != m_pgpCmdMapping.end(); ++it)
|
||
{
|
||
const CString &strCommand = it->first; // Key: 命令 (例如 "LINE")
|
||
const CString &strShortcut = it->second; // Value: 快捷键 (例如 "L")
|
||
|
||
CString strLine;
|
||
// 格式化:Value + "," + 制表符 + "*" + Key
|
||
// 结果:L, *LINE
|
||
strLine.Format(_T("%s,\t*%s"), strShortcut, strCommand);
|
||
|
||
strMiddle += strLine + _T("\r\n");
|
||
}
|
||
|
||
// 3. 拼接最终字符串
|
||
CString strFinal;
|
||
if (bFoundSection)
|
||
{
|
||
strFinal = strHeader + strMiddle + strEndMarker;
|
||
}
|
||
else
|
||
{
|
||
// 如果原文件没有标记,追加到末尾
|
||
if (!strHeader.IsEmpty() && strHeader.Right(1) != _T("\n"))
|
||
strHeader += _T("\r\n");
|
||
|
||
strFinal = strHeader;
|
||
strFinal += strBeginMarker;
|
||
strFinal += strMiddle;
|
||
strFinal += strEndMarker;
|
||
strFinal += _T("\r\n");
|
||
}
|
||
|
||
// 4. 写入文件 (UTF-8 with BOM)
|
||
CFile fileWrite;
|
||
if (!fileWrite.Open(path, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone, &ex))
|
||
{
|
||
TRACE(_T("无法写入文件: %s\n"), path);
|
||
return;
|
||
}
|
||
|
||
const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
|
||
fileWrite.Write(bom, 3);
|
||
|
||
std::string strData = ConvertCStringToUTF8(strFinal);
|
||
if (!strData.empty())
|
||
{
|
||
fileWrite.Write(strData.c_str(), (UINT)strData.length());
|
||
}
|
||
|
||
fileWrite.Close();
|
||
}
|