#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(_T("属性")); lvcolumn.iSubItem = 0; lvcolumn.cx = 0; m_wndList.InsertColumn(0, &lvcolumn); lvcolumn.pszText = const_cast(_T("条件")); lvcolumn.iSubItem = 1; m_wndList.InsertColumn(1, &lvcolumn); lvcolumn.pszText = const_cast(_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; }