599 lines
14 KiB
C++
599 lines
14 KiB
C++
#include "stdafx.h"
|
|
#include "QuickModifyDlg.h"
|
|
#include "coretools.h"
|
|
#include "aecdbcurvebase.h"
|
|
#include "aecdbEntityBase.h"
|
|
#include "COMPARE_DOUBLE.h"
|
|
#include "KTCadUtility.h"
|
|
|
|
#define DIFF_VALUE _T("<多值>")
|
|
|
|
using namespace SimpleProperty;
|
|
|
|
namespace
|
|
{
|
|
void getEntityProperty(AcDbEntity *pEnt, PropertySetByIndex &properties)
|
|
{
|
|
AecDbCurveBase *pCurveBase = AecDbCurveBase::cast(pEnt);
|
|
AecDbEntityBase *pEntBase = AecDbEntityBase::cast(pEnt);
|
|
if (pCurveBase)
|
|
{
|
|
pCurveBase->GetProperties(properties);
|
|
}
|
|
else if (pEntBase)
|
|
{
|
|
pEntBase->GetProperties(properties);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void getEntityProperty(const AcDbObjectId &id, PropertySetByIndex &properties)
|
|
{
|
|
AcDbEntityPointer ent(id, AcDb::kForRead);
|
|
if (ent.openStatus() != Acad::eOk) return;
|
|
getEntityProperty(ent, properties);
|
|
}
|
|
|
|
|
|
void setEntityProperty(AcDbEntity *pEnt, PropertySetByName &properties)
|
|
{
|
|
AecDbCurveBase *pCurveBase = AecDbCurveBase::cast(pEnt);
|
|
AecDbEntityBase *pEntBase = AecDbEntityBase::cast(pEnt);
|
|
if (pCurveBase)
|
|
{
|
|
pCurveBase->SetProperties(properties);
|
|
}
|
|
else if (pEntBase)
|
|
{
|
|
pEntBase->SetProperties(properties);
|
|
}
|
|
return;
|
|
}
|
|
|
|
CString getPropertyValueString(const Property &prop)
|
|
{
|
|
CString sVal;
|
|
switch (prop.m_type)
|
|
{
|
|
case eInt:
|
|
sVal.Format(_T("%d"), prop.m_iValueInt);
|
|
break;
|
|
case eDouble:
|
|
// 如果需要统一调整精度(如改为 3 位小数),只需修改此处
|
|
sVal.Format(_T("%.2f"), prop.m_dValueDouble);
|
|
break;
|
|
case eString:
|
|
sVal = prop.m_sValueString;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return sVal;
|
|
}
|
|
|
|
CString getUIContent(const PropertySetByIndex &props, LPCTSTR pszProp)
|
|
{
|
|
const Property *prop(NULL);
|
|
props.find(pszProp, prop);
|
|
if (prop)
|
|
{
|
|
return getPropertyValueString(*prop);
|
|
}
|
|
return _T("");
|
|
}
|
|
|
|
// prosets 是所有实体的属性列表,pszProp要保证一定会在每个属性表里面现
|
|
CString getUIContent(const std::vector<PropertySetByIndex> &proSets, LPCTSTR pszProp)
|
|
{
|
|
if (!pszProp) return _T("");
|
|
if (proSets.empty()) return _T("");
|
|
|
|
CString str(getUIContent(proSets[0], pszProp));
|
|
|
|
for (size_t i(1); i < proSets.size(); ++i)
|
|
{
|
|
const PropertySetByIndex &props(proSets[i]);
|
|
if (str != getUIContent(props, pszProp))
|
|
{
|
|
str = DIFF_VALUE;
|
|
return str;
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
// 求出所有实体属性表的交集
|
|
void getIntersection(const std::vector<PropertySetByIndex> &proSets, PropertySetByIndex &output)
|
|
{
|
|
typedef std::set<Property, compare_properties> PropIntersectionSet;
|
|
typedef PropertySetByIndex::SetType::const_iterator PorpIndexSetIter;
|
|
|
|
output.m_set.clear();
|
|
|
|
if (proSets.empty()) return;
|
|
|
|
PropIntersectionSet candidates;
|
|
const PropertySetByIndex &firstSet = proSets[0];
|
|
|
|
//for (PorpIndexSetIter it = firstSet.m_set.begin()
|
|
// ; it != firstSet.m_set.end()
|
|
// ; ++it)
|
|
//{
|
|
// candidates.insert(*it);
|
|
//}
|
|
candidates.insert(proSets[0].m_set.begin(), proSets[0].m_set.end());
|
|
|
|
PropIntersectionSet currentCompSet, resultSet;
|
|
for (size_t i = 1; i < proSets.size(); ++i)
|
|
{
|
|
if (candidates.empty()) break;
|
|
currentCompSet.clear();
|
|
resultSet.clear();
|
|
|
|
//const PropertySetByIndex ¤tInputSet = proSets[i];
|
|
|
|
//for (PorpIndexSetIter it = currentInputSet.m_set.begin()
|
|
// ; it != currentInputSet.m_set.end()
|
|
// ; ++it)
|
|
//{
|
|
// currentCompSet.insert(*it);
|
|
//}
|
|
currentCompSet.insert(proSets[i].m_set.begin(), proSets[i].m_set.end());
|
|
|
|
std::set_intersection(candidates.begin(), candidates.end(),
|
|
currentCompSet.begin(), currentCompSet.end(),
|
|
std::inserter(resultSet, resultSet.begin()),
|
|
compare_properties());
|
|
|
|
candidates = resultSet;
|
|
}
|
|
|
|
Property prop;
|
|
i = (0);
|
|
for (PropIntersectionSet::const_iterator it = candidates.begin()
|
|
; it != candidates.end()
|
|
; ++it, ++i)
|
|
{
|
|
prop = *it;
|
|
prop.m_iIndex = i;
|
|
|
|
// 在这里把 output 里面property内的m_sUIContent设置一下
|
|
// 到了外面就没有机会了
|
|
prop.m_sUIContent = getUIContent(proSets, prop.m_sPropertyName);
|
|
|
|
output.m_set.insert(prop);
|
|
}
|
|
}
|
|
|
|
bool verifyIntger(LPCTSTR pszStr, int *pInt = NULL)
|
|
{
|
|
if (pszStr == NULL || *pszStr == _T('\0'))
|
|
return false; // 空字符串无效
|
|
TCHAR *pEnd = NULL;
|
|
long lVal = _tcstol(pszStr, &pEnd, 10);
|
|
|
|
if (*pEnd == _T('\0'))
|
|
{
|
|
if (pInt) *pInt = (int)lVal;
|
|
return true; // 成功识别为整数
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool verifyDouble(LPCTSTR pszStr, double *pDouble = NULL)
|
|
{
|
|
if (pszStr == NULL || *pszStr == _T('\0'))
|
|
return false; // 空字符串无效
|
|
TCHAR *pEnd = NULL;
|
|
double dVal = _tcstod(pszStr, &pEnd);
|
|
|
|
if (*pEnd == _T('\0'))
|
|
{
|
|
if (pDouble) *pDouble = dVal;
|
|
return true; // 成功识别为浮点数
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 判断用户的输入是否符合 PropertyType
|
|
bool verifyUserInput(const PropertyType &tp, LPCTSTR pszStr)
|
|
{
|
|
if (pszStr == NULL || *pszStr == _T('\0'))
|
|
return false; // 空字符串无效
|
|
|
|
if (tp == eInt && verifyIntger(pszStr))
|
|
return true;
|
|
|
|
// 如果是浮点数类型,那么整型也可以
|
|
if (tp == eDouble
|
|
&& (verifyIntger(pszStr)
|
|
|| verifyDouble(pszStr)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isSameValue(Property &prop, LPCTSTR psz)
|
|
{
|
|
if (!psz) return false;
|
|
|
|
bool bDiffValue(false);
|
|
if (prop.m_sUIContent == DIFF_VALUE)
|
|
bDiffValue = true;
|
|
|
|
|
|
if (prop.m_type == eString)
|
|
return prop.m_sUIContent == psz;
|
|
|
|
double dV;
|
|
int iV;
|
|
bool bDouble(verifyDouble(psz, &dV));
|
|
bool bInt(verifyIntger(psz, &iV));
|
|
|
|
if (prop.m_type == eInt && bInt)
|
|
{
|
|
// 目前是"多值"情况,输入什么都算是有效修改,只要格式正确
|
|
if (bDiffValue)
|
|
return false;
|
|
return iV == prop.m_iValueInt;
|
|
}
|
|
|
|
if (prop.m_type == eDouble)
|
|
{
|
|
// 目前是"多值"情况,输入什么都算是有效修改,只要格式正确
|
|
if (bDiffValue)
|
|
return false;
|
|
|
|
if (bDouble)
|
|
return COMPARE_DOUBLE::equal(dV, prop.m_dValueDouble, .01);
|
|
if (bInt)
|
|
return COMPARE_DOUBLE::equal(iV * 1., prop.m_dValueDouble, .01);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
extern UINT WM_XLISTCTRL_COMBO_SELECTION;
|
|
extern UINT WM_XLISTCTRL_EDIT_END;
|
|
extern UINT WM_XLISTCTRL_CHECKBOX_CLICKED;
|
|
|
|
IMPLEMENT_DYNAMIC(CQuickModifyDlg, CDialog)
|
|
|
|
BEGIN_MESSAGE_MAP(CQuickModifyDlg, CDialog)
|
|
ON_MESSAGE( WM_ACAD_KEEPFOCUS, &CQuickModifyDlg::onAcadKeepFocus)
|
|
ON_BN_CLICKED(IDOK, &CQuickModifyDlg::OnBnClickedOk)
|
|
ON_WM_SIZE()
|
|
ON_WM_GETMINMAXINFO()
|
|
ON_REGISTERED_MESSAGE(WM_XLISTCTRL_COMBO_SELECTION, &CQuickModifyDlg::OnComboSelection)
|
|
ON_REGISTERED_MESSAGE(WM_XLISTCTRL_EDIT_END, &CQuickModifyDlg::OnEditItemEnd)
|
|
END_MESSAGE_MAP()
|
|
|
|
CQuickModifyDlg::CQuickModifyDlg(const AcDbObjectIdArray &ids, CWnd *pParent /*=nullptr*/)
|
|
: CDialog(CQuickModifyDlg::IDD, pParent)
|
|
, m_arIds(ids)
|
|
{
|
|
}
|
|
|
|
CQuickModifyDlg::~CQuickModifyDlg() { }
|
|
|
|
void CQuickModifyDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
DDX_Control(pDX, IDC_LIST_OBJECTS, m_wndList);
|
|
}
|
|
|
|
BOOL CQuickModifyDlg::OnInitDialog()
|
|
{
|
|
CDialog::OnInitDialog();
|
|
|
|
if (!m_arIds.isEmpty())
|
|
{
|
|
CString strTitle;
|
|
strTitle.Format(_T("共选择了%d个<%s>实体"), m_arIds.length(), m_arIds.first().objectClass()->dxfName());
|
|
SetWindowText(strTitle);
|
|
}
|
|
|
|
m_wndList.SetFont(GetFont());
|
|
|
|
m_wndList.SetExtendedStyle(
|
|
m_wndList.GetExtendedStyle()
|
|
| LVS_EX_FULLROWSELECT
|
|
| LVS_EX_GRIDLINES
|
|
| LVS_EX_SINGLEROW);
|
|
|
|
initColumns();
|
|
|
|
CRect rc;
|
|
GetWindowRect(rc);
|
|
m_szMinDlgSize = rc.Size();
|
|
layoutControls(0, 0);
|
|
|
|
refreshList();
|
|
|
|
UpdateData(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CQuickModifyDlg::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);
|
|
}
|
|
|
|
void CQuickModifyDlg::resetWidthOfColumnHeader(const int &width)
|
|
{
|
|
int w = int(width * .5);
|
|
m_wndList.CListCtrl::SetColumnWidth(0, w);
|
|
m_wndList.CListCtrl::SetColumnWidth(1, w);
|
|
}
|
|
|
|
void CQuickModifyDlg::refreshList()
|
|
{
|
|
m_wndList.DeleteAllItems();
|
|
m_properties.m_set.clear();
|
|
|
|
std::vector<PropertySetByIndex> proSets;
|
|
PropertySetByIndex _properties;
|
|
for (int i(0); i < m_arIds.length(); ++i)
|
|
{
|
|
_properties.m_set.clear();
|
|
getEntityProperty(m_arIds[i], _properties);
|
|
if (_properties.m_set.empty()) continue;
|
|
proSets.push_back(_properties);
|
|
}
|
|
|
|
getIntersection(proSets, m_properties);
|
|
|
|
// 将 properties 显示到列表控件中
|
|
int idx(0), nHeight(1), nCurSel(0);
|
|
PropertySetByIndex::SetType::const_iterator it(m_properties.m_set.begin());
|
|
for (; it != m_properties.m_set.end(); ++it)
|
|
{
|
|
if (!it->m_bCanModify) continue;
|
|
|
|
if (it->m_iColor == -1)
|
|
idx = m_wndList.InsertItem(++idx, it->m_sPropertyName);
|
|
else
|
|
idx = m_wndList.InsertItem(++idx, it->m_sPropertyName, 0, acedGetRGB(it->m_iColor));
|
|
|
|
if (!it->m_arOptions.empty())
|
|
{
|
|
nCurSel = 0;
|
|
CStringArray *pArr = new CStringArray;
|
|
for (size_t i = 0; i < it->m_arOptions.size(); ++i)
|
|
{
|
|
const CString &s(it->m_arOptions[i]);
|
|
if (s == it->m_sUIContent)
|
|
nCurSel = i;
|
|
pArr->Add(s);
|
|
}
|
|
|
|
// 如果数量量大于10,则取10
|
|
nHeight = pArr->GetCount() > 10 ? 10 : pArr->GetCount();
|
|
|
|
m_wndList.SetComboBox(idx, 1, TRUE, pArr, nHeight, nCurSel, 0);
|
|
}
|
|
else
|
|
{
|
|
m_wndList.SetEdit(idx, 1);
|
|
}
|
|
m_wndList.SetItemText(idx, 1, it->m_sUIContent);
|
|
m_wndList.SetItemData(idx, (DWORD_PTR)&(*it));
|
|
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 CQuickModifyDlg::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 CQuickModifyDlg::OnComboSelection(WPARAM nItem, LPARAM nSubItem)
|
|
{
|
|
Property* pItemData = (Property*)m_wndList.GetItemData((int)nItem);
|
|
if (pItemData)
|
|
{
|
|
CString sValue = m_wndList.GetItemText((int)nItem, (int)nSubItem);
|
|
if (pItemData->m_sUIContent != sValue)
|
|
{
|
|
pItemData->m_sUIContent = m_wndList.GetItemText((int)nItem, (int)nSubItem);
|
|
pItemData->m_bIsChange = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CQuickModifyDlg::OnEditItemEnd(WPARAM nItem, LPARAM nSubItem)
|
|
{
|
|
Property* pItemData = (Property*)m_wndList.GetItemData((int)nItem);
|
|
if (pItemData)
|
|
{
|
|
CString sValue = m_wndList.GetItemText((int)nItem, (int)nSubItem);
|
|
|
|
if (pItemData->m_type != eString)
|
|
{
|
|
if (!verifyUserInput(pItemData->m_type, sValue))
|
|
{
|
|
AfxMessageBox(_T("无效的输入,请输入数字!"));
|
|
m_wndList.SetItemText(nItem, nSubItem, pItemData->m_sUIContent);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// 判断两个字符串是否相同,从而决定是否标记为修改
|
|
if (!isSameValue(*pItemData, sValue))
|
|
{
|
|
pItemData->m_bIsChange = true;
|
|
pItemData->m_sUIContent = sValue;
|
|
}
|
|
else
|
|
{
|
|
m_wndList.SetItemText(nItem, nSubItem, pItemData->m_sUIContent);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CQuickModifyDlg::OnSize(UINT nType, int cx, int cy)
|
|
{
|
|
CDialog::OnSize(nType, cx, cy);
|
|
layoutControls(cx, cy);
|
|
}
|
|
|
|
LONG_PTR CQuickModifyDlg::onAcadKeepFocus(UINT_PTR, LONG_PTR)
|
|
{
|
|
POINT pt;
|
|
::GetCursorPos(&pt);
|
|
CRect rect;
|
|
GetWindowRect(&rect);
|
|
if(!(rect.PtInRect(pt)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CQuickModifyDlg::OnBnClickedOk()
|
|
{
|
|
OnOK();
|
|
|
|
if (!UpdateData(TRUE)) return;
|
|
|
|
}
|
|
|
|
void CQuickModifyDlg::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 *pBtnOK = GetDlgItem(IDOK);
|
|
if (!pBtnOK) return;
|
|
|
|
// 1. 获取各个按钮当前的尺寸 (宽和高)
|
|
// 我们只关心尺寸,不关心它当前的位置,所以获取后计算 Width/Height
|
|
CRect rcOK;
|
|
pBtnOK->GetWindowRect(&rcOK);
|
|
CSize szOK = rcOK.Size();
|
|
|
|
int okLeft = int((rcDlg.Width() - szOK.cx) * .5);
|
|
int okTop = rcDlg.bottom - bottomMargin - szOK.cy;
|
|
|
|
CRect rcOKTarget(okLeft, okTop, okLeft + szOK.cx, okTop + szOK.cy);
|
|
pBtnOK->MoveWindow(&rcOKTarget);
|
|
|
|
CRect rcList;
|
|
rcList.top = 0;
|
|
rcList.left = rcDlg.left + leftMargin;
|
|
rcList.right = rcDlg.right - rightMargin;
|
|
rcList.bottom = rcOKTarget.top - bottomMargin;
|
|
|
|
m_wndList.MoveWindow(rcList);
|
|
|
|
resetWidthOfColumnHeader(rcList.Width());
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
void CQuickModifyDlg::getMoidfiedProperties(PropertySetByName & properties) const
|
|
{
|
|
properties.m_set.clear();
|
|
PropertySetByIndex::SetType::const_iterator it = m_properties.m_set.begin();
|
|
for (; it != m_properties.m_set.end(); ++it)
|
|
{
|
|
if (!it->m_bIsChange) continue;
|
|
properties.m_set.insert(*it);
|
|
}
|
|
}
|
|
|
|
void CQuickModifyDlg::setPropertiesOfEntities() const
|
|
{
|
|
PropertySetByName properties;
|
|
getMoidfiedProperties(properties);
|
|
|
|
for (int i(0); i < m_arIds.length(); ++i)
|
|
{
|
|
AcDbEntityPointer ent(m_arIds[i], AcDb::kForWrite);
|
|
if (ent.openStatus() != Acad::eOk) continue;
|
|
setEntityProperty(ent, properties);
|
|
}
|
|
}
|
|
|
|
void CQuickModifyDlg::start(const AcDbObjectIdArray &ids)
|
|
{
|
|
CAcModuleResourceOverride myResources;
|
|
CQuickModifyDlg dlg(ids);
|
|
if (dlg.DoModal() == IDOK)
|
|
dlg.setPropertiesOfEntities();
|
|
}
|
|
|
|
void CQuickModifyDlg::command()
|
|
{
|
|
ads_name ss, ent;
|
|
struct resbuf* pRb = acutBuildList(RTDXF0, _T("AEC*"), 0);
|
|
|
|
int nRes = CKTCadUtility::SelectEntities(_T("\n选择上华实体:"), ss, pRb);
|
|
|
|
acutRelRb(pRb);
|
|
if (nRes != RTNORM) return;
|
|
|
|
Adesk::Int32 nLen = 0;
|
|
acedSSLength(ss, &nLen);
|
|
|
|
AcDbObjectId id;
|
|
AcDbObjectIdArray arIds;
|
|
for (int i = 0; i < nLen; ++i)
|
|
{
|
|
acedSSName(ss, i, ent);
|
|
acdbGetObjectId(id, ent);
|
|
if (id.isValid())
|
|
arIds.append(id);
|
|
}
|
|
acedSSFree(ss);
|
|
|
|
start(arIds);
|
|
}
|