Files
envi-code/SourceCode/Code2026/CoreCommand/QuickSelectDlg.cpp
T
2026-09-28 15:28:50 +08:00

620 lines
16 KiB
C++

#include "stdafx.h"
#include "QuickSelectDlg.h"
#include "coretools.h"
#include "KTCadUtility.h"
#include "aecdbcurvebase.h"
#include "aecdbEntityBase.h"
#include "entityfilter.h"
#include "TypeConvert.h"
#include "COMPARE_DOUBLE.h"
#include "QuickModifyDlg.h"
using namespace SimpleProperty;
namespace
{
// 将 pNewList 追加到 pMainList 的末尾
// 注意:传入的是引用的指针 (struct resbuf*&),以便处理 pMainList 初始为空的情况
void AppendFilter(struct resbuf *&pMainList, struct resbuf *pNewList)
{
if (pNewList == NULL) return; // 没有新东西要加,直接返回
if (pMainList == NULL)
{
pMainList = pNewList; // 主链表为空,直接接管新链表
}
else
{
struct resbuf *pTail = pMainList;
// 找到尾巴
while (pTail->rbnext != NULL)
{
pTail = pTail->rbnext;
}
// 接上
pTail->rbnext = pNewList;
}
}
void getEntityProperty(AcDbEntity *pEnt, PropertySetByIndex &xData)
{
AecDbCurveBase *pCurveBase = AecDbCurveBase::cast(pEnt);
AecDbEntityBase *pEntBase = AecDbEntityBase::cast(pEnt);
if (pCurveBase)
{
if (!pCurveBase->GetProperties(xData)) return;
}
else if (pEntBase)
{
if (!pEntBase->GetProperties(xData)) return;
}
}
bool compareProperty(LPCTSTR pszKey
, const PropertySetByIndex &_src
, const PropertySetByIndex &_desc
, const CQuickSelectDlg::ItemData::Condition &cd)
{
typedef CQuickSelectDlg::ItemData ItemData;
if (!pszKey) return false;
// 找到每个 set 中的 pszKey 对应的数据
PropertySetByName srcSet, descSet;
convertSetByName(_src, srcSet);
convertSetByName(_desc, descSet);
PropertySetByName::SetType::const_iterator itSrc = srcSet.m_set.find(Property(pszKey));
PropertySetByName::SetType::const_iterator itGuest = descSet.m_set.find(Property(pszKey));
if (itSrc == srcSet.m_set.end()) return false;
if (itGuest== descSet.m_set.end()) return false;
// 类型不同,直接返回 false
const Property &src = *itSrc;
const Property &desc = *itGuest;
if (src.m_type != desc.m_type) return false;
bool bStr(src.m_type == eString)
, bInt(src.m_type == eInt)
, bDouble(src.m_type == eDouble);
// 对字符串保护一下
CString sSrc, sDesc;
if (bStr)
{
sSrc = src.m_sValueString;
sDesc = desc.m_sValueString;
}
switch (cd)
{
case ItemData::eEqual:
if (bStr)
return desc.m_sValueString.Compare(src.m_sValueString) == 0;
if (bInt)
return desc.m_iValueInt == src.m_iValueInt;
if (bDouble)
return COMPARE_DOUBLE::equal(desc.m_dValueDouble ,src.m_dValueDouble, .01);
case ItemData::eNotEqual:
if (bStr)
return desc.m_sValueString.Compare(src.m_sValueString) != 0;
if (bInt)
return desc.m_iValueInt != src.m_iValueInt;
if (bDouble)
return !COMPARE_DOUBLE::equal(desc.m_dValueDouble ,src.m_dValueDouble, .01);
case ItemData::eCreator:
if (bStr) return false;
if (bInt)
return desc.m_iValueInt > src.m_iValueInt;
if (bDouble)
return desc.m_dValueDouble > src.m_dValueDouble;
case ItemData::eCreatorEqual:
if (bStr) return false;
if (bInt)
return desc.m_iValueInt >= src.m_iValueInt;
if (bDouble)
return COMPARE_DOUBLE::upequal_zero(desc.m_dValueDouble - src.m_dValueDouble, .01);
case ItemData::eLess:
if (bStr) return false;
if (bInt)
return desc.m_iValueInt < src.m_iValueInt;
if (bDouble)
return desc.m_dValueDouble < src.m_dValueDouble;
case ItemData::eLessEqual:
if (bStr) return false;
if (bInt)
return desc.m_iValueInt <= src.m_iValueInt;
if (bDouble)
return COMPARE_DOUBLE::downequal_zero(desc.m_dValueDouble - src.m_dValueDouble, .01);
default:
return false;
}
return false;
}
CString makeString(const Property &prop)
{
int prec(2);
switch (prop.m_type)
{
case eString:
return prop.m_sValueString;
case eInt:
return TypeConvert::i2s(prop.m_iValueInt);
case eDouble:
return TypeConvert::d2s(prop.m_dValueDouble, &prec);
}
return _T("");
}
}
extern UINT WM_XLISTCTRL_COMBO_SELECTION;
extern UINT WM_XLISTCTRL_EDIT_END;
extern UINT WM_XLISTCTRL_CHECKBOX_CLICKED;
IMPLEMENT_DYNAMIC(CQuickSelectDlg, CDialog)
BEGIN_MESSAGE_MAP(CQuickSelectDlg, CDialog)
ON_MESSAGE( WM_ACAD_KEEPFOCUS, &CQuickSelectDlg::onAcadKeepFocus)
ON_BN_CLICKED(IDOK, &CQuickSelectDlg::OnBnClickedOk)
ON_WM_SIZE()
ON_WM_GETMINMAXINFO()
ON_REGISTERED_MESSAGE(WM_XLISTCTRL_COMBO_SELECTION, &CQuickSelectDlg::OnComboSelection)
ON_REGISTERED_MESSAGE(WM_XLISTCTRL_CHECKBOX_CLICKED, &CQuickSelectDlg::OnCheckBoxClicked)
END_MESSAGE_MAP()
CQuickSelectDlg::CQuickSelectDlg(AcDbObjectId &idSelected, CWnd *pParent /*=nullptr*/)
: CDialog(CQuickSelectDlg::IDD, pParent)
, m_idSelected(idSelected)
, m_iSearchType(0)
, m_bDirectModify(FALSE)
{
AcDbEntityPointer ent(m_idSelected, AcDb::kForRead);
if (ent.openStatus() != Acad::eOk) return;
m_sDXF = ent->isA()->dxfName();
getEntityProperty(ent, m_srcProperty);
}
CQuickSelectDlg::~CQuickSelectDlg() { clearList(); }
void CQuickSelectDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_OBJECTS, m_wndList);
DDX_Radio(pDX, IDC_RADIO_SELECT, m_iSearchType);
DDX_Check(pDX, IDC_CHECK_DIRECT_MODIFY, m_bDirectModify);
}
BOOL CQuickSelectDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetWindowText(CString(_T("选择同类 (")) + m_sDXF + _T(")"));
m_wndList.SetFont(GetFont());
m_wndList.SetExtendedStyle(
m_wndList.GetExtendedStyle()
| LVS_EX_FULLROWSELECT
| LVS_EX_GRIDLINES);
initColumns();
CRect rc;
GetWindowRect(rc);
m_szMinDlgSize = rc.Size();
layoutControls(0, 0);
refreshList();
UpdateData(FALSE);
return TRUE;
}
void CQuickSelectDlg::initColumns()
{
// 第一列,选择列
LV_COLUMN lvcolumn;
::memset(&lvcolumn, 0, sizeof(lvcolumn));
lvcolumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvcolumn.fmt = LVCFMT_CENTER;
lvcolumn.pszText = const_cast<LPTSTR>(_T("属性"));
lvcolumn.iSubItem = 0;
lvcolumn.cx = 0;
m_wndList.InsertColumn(0, &lvcolumn);
lvcolumn.pszText = const_cast<LPTSTR>(_T("条件"));
lvcolumn.iSubItem = 1;
m_wndList.InsertColumn(1, &lvcolumn);
lvcolumn.pszText = const_cast<LPTSTR>(_T("值"));
lvcolumn.iSubItem = 2;
m_wndList.InsertColumn(2, &lvcolumn);
// 设置第一列为选择项
if (m_wndList.m_HeaderCtrl.GetItemCount() > 0)
{
HDITEM hditem;
hditem.mask = HDI_FORMAT | HDI_IMAGE;
m_wndList.m_HeaderCtrl.GetItem(0, &hditem);
hditem.fmt |= HDF_IMAGE;
hditem.iImage = XHEADERCTRL_UNCHECKED_IMAGE;
m_wndList.m_HeaderCtrl.SetItem(0, &hditem);
}
}
void CQuickSelectDlg::resetWidthOfColumnHeader(const int &width)
{
int w = int(width / 3.);
m_wndList.CListCtrl::SetColumnWidth(0, w);
m_wndList.CListCtrl::SetColumnWidth(1, w);
m_wndList.CListCtrl::SetColumnWidth(2, w);
}
void CQuickSelectDlg::refreshList()
{
if (!m_idSelected.isValid()) return;
clearList();
// 类型
int idx(0);
int prec(1);
CString sVal;
bool bStr(false);
PropertySetByIndex::SetType::const_iterator it(m_srcProperty.m_set.begin());
for (; it != m_srcProperty.m_set.end(); ++it)
{
CStringArray *pArr(makeConditionList(it->m_type));
ItemData *pData = new ItemData(ItemData::eEqual
, it->m_sPropertyName
, makeString(*it)
, false);
if (it->m_iColor == -1)
idx = m_wndList.InsertItem(++idx, pData->m_sProperty);
else
idx = m_wndList.InsertItem(++idx, it->m_sPropertyName, 0, acedGetRGB(it->m_iColor));
m_wndList.SetCheckbox(idx, 0, pData->m_bSelected ? TRUE : FALSE);
m_wndList.SetComboBox(idx, 1, TRUE, pArr, (int)pArr->GetCount(), pData->m_iCondition, 0);
m_wndList.SetItemText(idx, 2, pData->m_sValue);
m_wndList.SetItemData(idx, (DWORD_PTR)pData);
if (idx % 2 && it->m_iColor == -1)
{
m_wndList.SetItemColors(idx, 0, RGB(0,0,0), RGB(255, 255, 225));
m_wndList.SetItemColors(idx, 1, RGB(0,0,0), RGB(255, 255, 225));
m_wndList.SetItemColors(idx, 2, RGB(0,0,0), RGB(255, 255, 225));
}
}
return;
}
void CQuickSelectDlg::clearList()
{
for (int i(0); i < m_wndList.GetItemCount(); ++i)
{
ItemData *pData = (ItemData *)m_wndList.GetItemData(i);
if (pData) delete pData;
}
m_wndList.DeleteAllItems();
}
CStringArray *CQuickSelectDlg::makeConditionList(const SimpleProperty::PropertyType &type)
{
if (type == SimpleProperty::eString)
{
CStringArray *arrStringCondition = new CStringArray;
arrStringCondition->Add(ItemData::getStringByCondition(ItemData::eEqual));
arrStringCondition->Add(ItemData::getStringByCondition(ItemData::eNotEqual));
return arrStringCondition;
}
else
{
CStringArray *arrDigitalCondition = new CStringArray;
arrDigitalCondition->Add(ItemData::getStringByCondition(ItemData::eEqual));
arrDigitalCondition->Add(ItemData::getStringByCondition(ItemData::eNotEqual));
arrDigitalCondition->Add(ItemData::getStringByCondition(ItemData::eCreator));
arrDigitalCondition->Add(ItemData::getStringByCondition(ItemData::eCreatorEqual));
arrDigitalCondition->Add(ItemData::getStringByCondition(ItemData::eLess));
arrDigitalCondition->Add(ItemData::getStringByCondition(ItemData::eLessEqual));
return arrDigitalCondition;
}
return nullptr;
}
void CQuickSelectDlg::OnGetMinMaxInfo(MINMAXINFO *lpMMI)
{
CDialog::OnGetMinMaxInfo(lpMMI);
// 只有当 m_minSize 有效(即 OnInitDialog 运行过)时才限制
// 防止窗口创建初期 m_minSize 为 0 导致窗口无法显示
if (m_szMinDlgSize.cx > 0 && m_szMinDlgSize.cy > 0)
{
// ptMinTrackSize 代表窗口允许的最小“追踪”尺寸 (拖拽时的尺寸)
lpMMI->ptMinTrackSize.x = m_szMinDlgSize.cx; // 限制最小宽度
lpMMI->ptMinTrackSize.y = m_szMinDlgSize.cy; // 限制最小高度
}
}
LRESULT CQuickSelectDlg::OnComboSelection(WPARAM nItem, LPARAM nSubItem)
{
ItemData *pItemData = (ItemData*)m_wndList.GetItemData((int)nItem);
if (pItemData)
{
CString sCondition = m_wndList.GetItemText((int)nItem, (int)nSubItem);
pItemData->m_iCondition = ItemData::getConditionByString(sCondition);
}
return 0;
}
LRESULT CQuickSelectDlg::OnCheckBoxClicked(WPARAM nItem, LPARAM nSubItem)
{
ItemData *pItemData = (ItemData*)m_wndList.GetItemData((int)nItem);
if (pItemData)
{
pItemData->m_bSelected = m_wndList.GetItemCheckedState((int)nItem, (int)nSubItem) != 0 ? true : false;
}
return 0;
}
void CQuickSelectDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
layoutControls(cx, cy);
}
bool CQuickSelectDlg::compareEntity(AcDbEntity *pEnt)
{
// 得到 实体的 属性表
PropertySetByIndex properties;
getEntityProperty(pEnt, properties);
ItemData *pItem(NULL);
for (int i(0); i < m_wndList.GetItemCount(); ++i)
{
pItem = (ItemData*)m_wndList.GetItemData(i);
if (!pItem) continue;
if (!pItem->m_bSelected) continue;
if (compareProperty(pItem->m_sProperty, m_srcProperty, properties, pItem->m_iCondition))
continue;
else
return false;
}
return true;
}
void CQuickSelectDlg::getEntities(AcDbObjectIdArray & arr)
{
struct resbuf * pRb = acutBuildList(410, _T("Model"), RTDXF0, m_sDXF, RTNONE);
int nRes;
ads_name ss;
if (m_iSearchType == 0) // 框选
{
nRes = CKTCadUtility::SelectEntities(_T("框选要挑选的实体:"), ss, pRb);
acutRelRb(pRb);
if (nRes != RTNORM) return;
}
else
{
nRes = acedSSGet(_T("X"), NULL, NULL, pRb, ss);
acutRelRb(pRb);
if (nRes != RTNORM) return;
}
ads_name ent;
AcDbObjectId id;
Adesk::Int32 lLen(0L);
acedSSLength(ss, &lLen);
for (int i(0); i < lLen; ++i)
{
if (acedSSName(ss, i, ent) != RTNORM) continue;
if (acdbGetObjectId(id, ent) != Acad::eOk) continue;
AcDbEntityPointer entPtr(id, AcDb::kForRead);
if (entPtr.openStatus() != Acad::eOk) continue;
// 排除隐藏实体
if (entPtr->visibility() == AcDb::kInvisible) continue;
if (!compareEntity(entPtr)) continue;
arr.append(id);
}
acedSSFree(ss);
}
LONG_PTR CQuickSelectDlg::onAcadKeepFocus(UINT_PTR, LONG_PTR)
{
POINT pt;
::GetCursorPos(&pt);
CRect rect;
GetWindowRect(&rect);
if(!(rect.PtInRect(pt)))
{
return FALSE;
}
return TRUE;
}
void CQuickSelectDlg::OnBnClickedOk()
{
OnOK();
if (!UpdateData(TRUE)) return;
if (m_wndList.GetItemCount() == 0)
{
AfxMessageBox(_T("没有可用的选择条件!"), MB_OK);
return;
}
getEntities(m_arids);
}
void CQuickSelectDlg::layoutControls(const int &cx, const int &cy)
{
if (!m_wndList.GetSafeHwnd()) return;
double dDpiScale = CoreTools::GetDPIScale();
int leftMargin(20), bottomMargin(20), rightMargin(20);
leftMargin = int((leftMargin * 1. * dDpiScale) + .5);
rightMargin = int((rightMargin * 1. * dDpiScale) + .5);
bottomMargin = int((bottomMargin * 1. * dDpiScale) + .5);
CRect rcDlg;
GetWindowRect(rcDlg);
ScreenToClient(rcDlg);
// 先排列下面三个控件
CWnd *pBtnRadio1 = GetDlgItem(IDC_RADIO_SELECT);
CWnd *pBtnRadio2 = GetDlgItem(IDC_RADIO_ALL);
CWnd *pBtnOK = GetDlgItem(IDOK);
CWnd *pCheckBox = GetDlgItem(IDC_CHECK_DIRECT_MODIFY);
if (!pBtnRadio1 || !pBtnRadio2 || !pBtnOK || !pCheckBox) return;
// 1. 获取各个按钮当前的尺寸 (宽和高)
// 我们只关心尺寸,不关心它当前的位置,所以获取后计算 Width/Height
CRect rcOK, rcRadio1, rcRadio2;
pBtnOK->GetWindowRect(&rcOK);
pBtnRadio1->GetWindowRect(&rcRadio1);
pBtnRadio2->GetWindowRect(&rcRadio2);
CSize szOK = rcOK.Size();
CSize szR1 = rcRadio1.Size();
CSize szR2 = rcRadio2.Size();
// --- 第一步:计算 pBtnOK 的位置 ---
// 规则:距离右边 rightMargin,距离底边 bottomMargin
int okLeft = rcDlg.right - rightMargin - szOK.cx;
int okTop = rcDlg.bottom - bottomMargin - szOK.cy;
CRect rcOKTarget(okLeft, okTop, okLeft + szOK.cx, okTop + szOK.cy);
// 计算对齐用的“中心线” (Y轴中间位置)
int centerY = rcOKTarget.top + int(rcOKTarget.Height() / 2.);
// --- 第二步:计算 pBtnRadio1 的位置 ---
// 规则:距离左边 leftMargin,垂直方向与 OK 按钮中线对齐
int r1Left = rcDlg.left + leftMargin;
// 垂直居中公式:中心线 - 高度的一半
int r1Top = centerY - int(szR1.cy / 2.);
CRect rcR1Target(r1Left, r1Top, r1Left + szR1.cx, r1Top + szR1.cy);
// --- 第三步:计算 pBtnRadio2 的位置 ---
// 规则:在 pBtnRadio1 右侧 10 像素,下边对齐 (且满足中线对齐)
// 注意:如果两个Radio高度相同,"下边对齐"和"中线对齐"是一样的。
// 这里我们优先使用"中线对齐"逻辑以确保整体美观,且水平紧跟 R1。
int r2Left = rcR1Target.right + int(20 * dDpiScale);
int r2Top = centerY - int(szR2.cy / 2.);
// 如果必须严格执行"下边对齐"(假设高度不同时),使用下面这行代替上面一行:
// int r2Top = rcR1Target.bottom - szR2.cy;
CRect rcR2Target(r2Left, r2Top, r2Left + szR2.cx, r2Top + szR2.cy);
// --- 第四步:应用坐标 ---
pBtnOK->MoveWindow(&rcOKTarget);
pBtnRadio1->MoveWindow(&rcR1Target);
pBtnRadio2->MoveWindow(&rcR2Target);
CRect rcList;
rcList.top = 0;
rcList.left = rcR1Target.left;
rcList.right = rcOKTarget.right;
rcList.bottom = rcOKTarget.top - bottomMargin;
m_wndList.MoveWindow(rcList);
resetWidthOfColumnHeader(rcList.Width());
// 调整一下checkbox, 从IDOK左侧20像素开始,
CRect rcCheckbox;
pCheckBox->GetWindowRect(&rcCheckbox);
int dh(rcCheckbox.Height());
rcCheckbox.right = rcOKTarget.left - int(20 * dDpiScale);
rcCheckbox.left = rcCheckbox.right - rcCheckbox.Width();
rcCheckbox.top = centerY - int(rcCheckbox.Height() / 2.);
rcCheckbox.bottom = rcCheckbox.top + dh;
pCheckBox->MoveWindow(&rcCheckbox);
Invalidate();
}
void CQuickSelectDlg::start()
{
ads_name adsname;
struct resbuf *pRb = acutBuildList(RTDXF0, _T("AEC*"), 0);
int nRes = CKTCadUtility::SelectEntity(_T("选择一个上华实体:"), adsname, pRb);
acutRelRb(pRb);
if (nRes != RTNORM) return;
AcDbObjectId idSelected;
acdbGetObjectId(idSelected, adsname);
if (!idSelected.isValid()) return;
CAcModuleResourceOverride myResources;
CQuickSelectDlg dlg(idSelected);
if (dlg.DoModal() != IDOK) return;
if (dlg.m_arids.isEmpty())
AfxMessageBox(_T("没有可选择的实体!"), MB_OK);
if (!dlg.m_bDirectModify)
CEntityProperty::addToPickFirst(dlg.m_arids, true);
else
CQuickModifyDlg::start(dlg.m_arids);
}
CString CQuickSelectDlg::ItemData::getStringByCondition(const Condition &co)
{
switch (co)
{
case eEqual:
return _T("= 等于");
case eNotEqual:
return _T("≠ 不等于");
case eCreator:
return _T("> 大于");
case eCreatorEqual:
return _T("≥ 大于等于");
case eLess:
return _T("< 小于");
case eLessEqual:
return _T("≤ 小于等于");
default:
return _T("");
}
}
CQuickSelectDlg::ItemData::Condition CQuickSelectDlg::ItemData::getConditionByString(const CString & sCo)
{
if (sCo == _T("= 等于"))
return eEqual;
if (sCo == _T("≠ 不等于"))
return eNotEqual;
if (sCo == _T("> 大于"))
return eCreator;
if (sCo == _T("≥ 大于等于"))
return eCreatorEqual;
if (sCo == _T("< 小于"))
return eLess;
if (sCo == _T("≤ 小于等于"))
return eLessEqual;
return eNone;
}