From 62bec5118f5e5fe750017cf2f12d4a428ab092df Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期三, 17 九月 2025 18:06:30 +0800
Subject: [PATCH] 1.继续CControlJobManagerDlg的功能实现;
---
SourceCode/Bond/Servo/CCarrierSlotSelector.cpp | 673 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 673 insertions(+), 0 deletions(-)
diff --git a/SourceCode/Bond/Servo/CCarrierSlotSelector.cpp b/SourceCode/Bond/Servo/CCarrierSlotSelector.cpp
new file mode 100644
index 0000000..20be4d4
--- /dev/null
+++ b/SourceCode/Bond/Servo/CCarrierSlotSelector.cpp
@@ -0,0 +1,673 @@
+#include "stdafx.h"
+#include "CCarrierSlotSelector.h"
+
+#define SAFE_PORT(p) ((p) >= 0 && (p) < (int)m_ports.size())
+#define SAFE_SLOT(s) ((s) >= 0 && (s) < m_nSlots)
+
+#ifndef LVS_EX_DOUBLEBUFFER
+#define LVS_EX_DOUBLEBUFFER 0x00010000
+#endif
+
+BEGIN_MESSAGE_MAP(CCarrierSlotSelector, CListCtrl)
+ ON_WM_SHOWWINDOW()
+ ON_WM_WINDOWPOSCHANGED()
+ ON_WM_SIZE()
+ ON_WM_ERASEBKGND()
+ ON_WM_PAINT() // ★ 新增
+ ON_WM_LBUTTONDOWN()
+ ON_WM_MOUSEMOVE()
+ ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CCarrierSlotSelector::OnCustomDraw)
+END_MESSAGE_MAP()
+
+CCarrierSlotSelector::CCarrierSlotSelector() {}
+CCarrierSlotSelector::~CCarrierSlotSelector()
+{
+ if ((HFONT)m_fntText) m_fntText.DeleteObject();
+ if ((HFONT)m_fntBold) m_fntBold.DeleteObject();
+ if ((HFONT)m_fntSmall) m_fntSmall.DeleteObject();
+ if ((HIMAGELIST)m_ilRowHeight) m_ilRowHeight.DeleteImageList();
+}
+
+void CCarrierSlotSelector::PreSubclassWindow()
+{
+ CListCtrl::PreSubclassWindow();
+
+ ModifyStyle(LVS_TYPEMASK, LVS_REPORT | LVS_SHOWSELALWAYS);
+ ModifyStyle(LVS_OWNERDRAWFIXED | LVS_OWNERDATA, 0);
+
+ DWORD ex = GetExtendedStyle();
+ ex |= LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER;
+ ex &= ~LVS_EX_GRIDLINES; // 关闭系统网格,改自绘
+ SetExtendedStyle(ex);
+
+ // 让默认绘制用我们的底色,进一步降低白底机会(即使哪处走了默认路径)
+ ListView_SetBkColor(m_hWnd, m_colBgNorm);
+ ListView_SetTextBkColor(m_hWnd, m_colBgNorm);
+
+ EnsureFonts();
+ SetRowHeight(m_rowHeight);
+}
+
+void CCarrierSlotSelector::OnShowWindow(BOOL bShow, UINT nStatus)
+{
+ CListCtrl::OnShowWindow(bShow, nStatus);
+ if (bShow && !m_bFirstShown)
+ {
+ m_bFirstShown = TRUE;
+ RedrawWindow(nullptr, nullptr,
+ RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_UPDATENOW);
+ }
+}
+
+void CCarrierSlotSelector::OnWindowPosChanged(WINDOWPOS* wp)
+{
+ CListCtrl::OnWindowPosChanged(wp);
+ if (wp && (wp->flags & SWP_SHOWWINDOW))
+ {
+ RedrawWindow(nullptr, nullptr,
+ RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_UPDATENOW);
+ }
+}
+
+void CCarrierSlotSelector::EnsureFonts()
+{
+ if (!(HFONT)m_fntText)
+ {
+ LOGFONT lf = { 0 };
+ CFont* pSys = GetFont();
+ if (pSys) pSys->GetLogFont(&lf);
+ else { lf.lfHeight = -14; lstrcpy(lf.lfFaceName, _T("Segoe UI")); }
+ m_fntText.CreateFontIndirect(&lf);
+
+ lf.lfWeight = FW_SEMIBOLD;
+ m_fntBold.CreateFontIndirect(&lf);
+
+ lf.lfWeight = FW_NORMAL; lf.lfHeight = -12;
+ m_fntSmall.CreateFontIndirect(&lf);
+ }
+}
+
+void CCarrierSlotSelector::InitGrid(int nPorts, int nSlots)
+{
+ ASSERT(nPorts >= 1 && nSlots >= 1);
+ m_ports.clear();
+ m_ports.resize(nPorts);
+ m_nSlots = nSlots;
+ for (auto& pc : m_ports) pc.slots.resize(m_nSlots);
+
+ SetRedraw(FALSE);
+ DeleteAllItems();
+ while (GetHeaderCtrl() && GetHeaderCtrl()->GetItemCount() > 0)
+ DeleteColumn(0);
+
+ InsertColumn(0, _T("Slot"), LVCFMT_LEFT, m_slotColWidth);
+ for (int c = 0; c < nPorts; ++c)
+ {
+ CString col; col.Format(_T("Port %d"), c + 1);
+ InsertColumn(c + 1, col, LVCFMT_LEFT, m_portColWidth);
+ m_ports[c].portName = col;
+ m_ports[c].carrierName.Empty();
+ }
+
+ UpdateRowCount();
+ RebuildTexts();
+ SetRedraw(TRUE);
+ if (IsWindowVisible()) Invalidate(FALSE);
+}
+
+void CCarrierSlotSelector::SetColumnWidths(int slotColWidth, int portColWidth)
+{
+ if (slotColWidth > 0) m_slotColWidth = slotColWidth;
+ if (portColWidth > 0) m_portColWidth = portColWidth;
+ SetColumnWidth(0, m_slotColWidth);
+ for (int c = 0; c < (int)m_ports.size(); ++c) SetColumnWidth(c + 1, m_portColWidth);
+ if (IsWindowVisible()) Invalidate(FALSE);
+}
+
+void CCarrierSlotSelector::SetRowHeight(int cy)
+{
+ cy = max(18, min(cy, 64));
+ m_rowHeight = cy;
+
+ if ((HIMAGELIST)m_ilRowHeight) m_ilRowHeight.DeleteImageList();
+ m_ilRowHeight.Create(1, m_rowHeight, ILC_COLOR32, 1, 1);
+
+ // 1×cy 透明位图
+ CBitmap bmp; bmp.CreateBitmap(1, m_rowHeight, 1, 32, nullptr);
+ m_ilRowHeight.Add(&bmp, RGB(0, 0, 0));
+ SetImageList(&m_ilRowHeight, LVSIL_SMALL);
+
+ if (IsWindowVisible()) Invalidate(FALSE);
+}
+
+void CCarrierSlotSelector::UpdateRowCount()
+{
+ int cur = GetItemCount();
+ if (cur < m_nSlots)
+ {
+ for (int i = cur; i < m_nSlots; ++i)
+ {
+ CString s; s.Format(_T("Slot %d"), i + 1);
+ InsertItem(i, s, 0); // 设 iImage=0,行高来自 small image list
+ }
+ }
+ else if (cur > m_nSlots)
+ {
+ for (int i = cur - 1; i >= m_nSlots; --i) DeleteItem(i);
+ }
+
+ // 已有行也统一设 iImage=0
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_IMAGE; lvi.iImage = 0;
+ for (int i = 0; i < m_nSlots; ++i) { lvi.iItem = i; SetItem(&lvi); }
+}
+
+void CCarrierSlotSelector::RebuildTexts()
+{
+ // 列头:显示 “已选数/总槽数 + [x]/[ ]”
+ for (int c = 0; c < (int)m_ports.size(); ++c)
+ {
+ int selected = 0;
+ int total = m_nSlots;
+
+ bool anyCheckable = false;
+ bool allChecked = true;
+
+ for (int r = 0; r < m_nSlots; ++r)
+ {
+ const auto& cell = m_ports[c].slots[r];
+ if (cell.hasGlass)
+ {
+ anyCheckable = true;
+ if (cell.checked) ++selected; else allChecked = false;
+ }
+ }
+ if (!anyCheckable) allChecked = false;
+
+ CString head;
+ CString chk = allChecked ? _T("[x]") : _T("[ ]");
+
+ if (!m_ports[c].carrierName.IsEmpty())
+ head.Format(_T("%s (%s) %d/%d %s%s"),
+ m_ports[c].portName.GetString(), m_ports[c].carrierName.GetString(),
+ selected, total,
+ m_ports[c].allocated ? _T("[LOCK] ") : _T(""),
+ chk.GetString());
+ else
+ head.Format(_T("%s %d/%d %s%s"),
+ m_ports[c].portName.GetString(), selected, total,
+ m_ports[c].allocated ? _T("[LOCK] ") : _T(""),
+ chk.GetString());
+
+ LVCOLUMN lvc = { 0 }; lvc.mask = LVCF_TEXT; lvc.pszText = head.GetBuffer();
+ SetColumn(c + 1, &lvc);
+ head.ReleaseBuffer();
+ }
+
+ // 行头文本
+ for (int r = 0; r < m_nSlots; ++r)
+ {
+ CString s; s.Format(_T("Slot %d"), r + 1);
+ SetItemText(r, 0, s);
+ }
+
+ // 单元格临时文本
+ for (int r = 0; r < m_nSlots; ++r)
+ for (int c = 0; c < (int)m_ports.size(); ++c)
+ SetItemText(r, c + 1, GetDisplayId(c, r));
+}
+
+void CCarrierSlotSelector::SetShowMaterialToggle(BOOL bShow)
+{
+ m_bShowMatToggle = bShow;
+ if (IsWindowVisible()) Invalidate(FALSE);
+}
+
+void CCarrierSlotSelector::SetPortInfo(int portIndex, LPCTSTR portName, LPCTSTR carrierName)
+{
+ if (!SAFE_PORT(portIndex)) return;
+ if (portName) m_ports[portIndex].portName = portName;
+ if (carrierName) m_ports[portIndex].carrierName = carrierName;
+ RebuildTexts();
+ if (IsWindowVisible()) Invalidate(FALSE);
+}
+
+void CCarrierSlotSelector::SetPortAllocated(int portIndex, BOOL allocated, LPCTSTR byName)
+{
+ if (!SAFE_PORT(portIndex)) return;
+ m_ports[portIndex].allocated = !!allocated;
+ if (byName) m_ports[portIndex].allocatedBy = byName; else m_ports[portIndex].allocatedBy.Empty();
+
+ if (allocated)
+ for (int r = 0; r < m_nSlots; ++r) m_ports[portIndex].slots[r].checked = false;
+
+ // 刷新整列
+ for (int r = 0; r < m_nSlots; ++r)
+ {
+ CRect rc; GetSubItemRect(r, portIndex + 1, LVIR_BOUNDS, rc);
+ InvalidateRect(&rc, FALSE);
+ }
+ RebuildTexts();
+}
+
+BOOL CCarrierSlotSelector::IsPortAllocated(int portIndex) const
+{
+ if (!SAFE_PORT(portIndex)) return FALSE;
+ return m_ports[portIndex].allocated;
+}
+
+void CCarrierSlotSelector::SetSlotGlass(int portIndex, int slotIndex, BOOL hasGlass, LPCTSTR coreId, int material)
+{
+ if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return;
+ auto& cell = m_ports[portIndex].slots[slotIndex];
+ cell.hasGlass = !!hasGlass;
+ cell.coreId = coreId ? coreId : _T("");
+ cell.material = (material == MAT_G2) ? MAT_G2 : MAT_G1;
+ if (!cell.hasGlass) cell.checked = false;
+
+ CRect rc; GetSubItemRect(slotIndex, portIndex + 1, LVIR_BOUNDS, rc);
+ InvalidateRect(&rc, FALSE);
+ RebuildTexts();
+}
+
+void CCarrierSlotSelector::SetSlotChecked(int portIndex, int slotIndex, BOOL checked)
+{
+ if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return;
+ if (m_ports[portIndex].allocated) return;
+ auto& cell = m_ports[portIndex].slots[slotIndex];
+ if (!cell.hasGlass) return;
+
+ cell.checked = !!checked;
+ NotifySelectionChanged(portIndex, slotIndex, cell.checked);
+
+ CRect rc; GetSubItemRect(slotIndex, portIndex + 1, LVIR_BOUNDS, rc);
+ InvalidateRect(&rc, FALSE);
+ RebuildTexts();
+}
+
+BOOL CCarrierSlotSelector::GetSlotChecked(int portIndex, int slotIndex) const
+{
+ if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return FALSE;
+ return m_ports[portIndex].slots[slotIndex].checked ? TRUE : FALSE;
+}
+
+int CCarrierSlotSelector::GetSlotMaterialType(int portIndex, int slotIndex) const
+{
+ if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return MAT_G1;
+ return m_ports[portIndex].slots[slotIndex].material;
+}
+
+void CCarrierSlotSelector::SetSlotMaterialType(int portIndex, int slotIndex, int material, BOOL bNotify)
+{
+ if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return;
+ if (m_ports[portIndex].allocated) return;
+ auto& cell = m_ports[portIndex].slots[slotIndex];
+ if (!cell.hasGlass) return;
+
+ int mt = (material == MAT_G2) ? MAT_G2 : MAT_G1;
+ if (cell.material != mt)
+ {
+ cell.material = mt;
+ if (bNotify) NotifyMaterialChanged(portIndex, slotIndex, cell.material);
+ CRect rc; GetSubItemRect(slotIndex, portIndex + 1, LVIR_BOUNDS, rc);
+ InvalidateRect(&rc, FALSE);
+ }
+}
+
+CString CCarrierSlotSelector::GetDisplayId(int portIndex, int slotIndex) const
+{
+ CString s(_T("—"));
+ if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return s;
+ const auto& cell = m_ports[portIndex].slots[slotIndex];
+ if (!cell.hasGlass) return s;
+ s.Format(_T("G%d-%s"), (cell.material == MAT_G2) ? 2 : 1, cell.coreId.GetString());
+ return s;
+}
+
+void CCarrierSlotSelector::CheckAllInPort(int portIndex, BOOL checked, BOOL bNotify)
+{
+ if (!SAFE_PORT(portIndex)) return;
+ if (m_ports[portIndex].allocated) return;
+
+ for (int r = 0; r < m_nSlots; ++r)
+ {
+ auto& cell = m_ports[portIndex].slots[r];
+ if (!cell.hasGlass) continue;
+ if (cell.checked != !!checked)
+ {
+ cell.checked = !!checked;
+ if (bNotify) NotifySelectionChanged(portIndex, r, cell.checked);
+ }
+ CRect rc; GetSubItemRect(r, portIndex + 1, LVIR_BOUNDS, rc);
+ InvalidateRect(&rc, FALSE);
+ }
+ RebuildTexts();
+}
+
+BOOL CCarrierSlotSelector::OnEraseBkgnd(CDC* /*pDC*/)
+{
+ // 关键:不让系统擦白背景;用自绘在 PREPAINT 阶段统一铺底
+ return TRUE;
+}
+
+void CCarrierSlotSelector::OnPaint()
+{
+ CPaintDC dc(this);
+ CRect rc; GetClientRect(&rc);
+
+ CDC memDC;
+ memDC.CreateCompatibleDC(&dc);
+ CBitmap bmp;
+ bmp.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height());
+ HGDIOBJ hOldBmp = memDC.SelectObject(bmp);
+
+ // 统一底色(避免任何擦白)
+ memDC.FillSolidRect(rc, m_colBgNorm);
+
+ // 让控件把客户区内容“画到”内存DC(会触发 NM_CUSTOMDRAW,走你现有自绘逻辑)
+ // PRF_ERASEBKGND 让内部如果想擦背景,也在内存里擦,不会闪屏
+ SendMessage(WM_PRINTCLIENT,
+ reinterpret_cast<WPARAM>(memDC.m_hDC),
+ PRF_CLIENT | PRF_ERASEBKGND | PRF_CHILDREN | PRF_OWNED);
+
+ // 回显到屏幕
+ dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);
+
+ memDC.SelectObject(hOldBmp);
+ // bmp, memDC 析构自动释放
+}
+
+void CCarrierSlotSelector::OnSize(UINT nType, int cx, int cy)
+{
+ CListCtrl::OnSize(nType, cx, cy);
+}
+
+CRect CCarrierSlotSelector::GetCheckboxRect(const CRect& cell) const
+{
+ int sz = max(14, min(int(m_rowHeight * 0.70), 20));
+ int leftPad = 8;
+ int top = cell.top + (m_rowHeight - sz) / 2;
+ return CRect(cell.left + leftPad, top, cell.left + leftPad + sz, top + sz);
+}
+
+CRect CCarrierSlotSelector::GetMaterialTagRect(const CRect& cell) const
+{
+ int tagH = max(14, min(int(m_rowHeight * 0.65), m_rowHeight - 8));
+ int tagW = 32;
+ int rightPadForDot = 16;
+ int gap = 6;
+ int top = cell.top + (m_rowHeight - tagH) / 2;
+ int right = cell.right - rightPadForDot - gap;
+ return CRect(right - tagW, top, right, top + tagH);
+}
+
+CRect CCarrierSlotSelector::GetStatusDotRect(const CRect& cell) const
+{
+ int d = max(8, min(int(m_rowHeight * 0.42), 12));
+ int rightPad = 6;
+ int top = cell.top + (m_rowHeight - d) / 2;
+ return CRect(cell.right - rightPad - d, top, cell.right - rightPad, top + d);
+}
+
+void CCarrierSlotSelector::DrawFlatCheckbox(CDC* pDC, const CRect& r, bool checked, bool disabled)
+{
+ CBrush br(disabled ? RGB(245, 245, 245) : RGB(255, 255, 255));
+ CPen pen(PS_SOLID, 1, disabled ? RGB(200, 200, 200) : RGB(130, 130, 135));
+ CBrush* pOldB = pDC->SelectObject(&br);
+ CPen* pOldP = pDC->SelectObject(&pen);
+ pDC->RoundRect(r, CPoint(3, 3));
+ pDC->SelectObject(pOldB);
+ pDC->SelectObject(pOldP);
+ br.DeleteObject(); pen.DeleteObject();
+
+ if (!checked) return;
+
+ COLORREF c = disabled ? RGB(160, 160, 160) : RGB(40, 150, 90);
+ CPen penTick(PS_SOLID, max(2, r.Height() / 8), c);
+ CPen* pOld = pDC->SelectObject(&penTick);
+ POINT p1 = { r.left + r.Width() * 2 / 9, r.top + r.Height() * 5 / 9 };
+ POINT p2 = { r.left + r.Width() * 4 / 9, r.top + r.Height() * 7 / 9 };
+ POINT p3 = { r.left + r.Width() * 7 / 9, r.top + r.Height() * 3 / 9 };
+ pDC->MoveTo(p1); pDC->LineTo(p2); pDC->LineTo(p3);
+ pDC->SelectObject(pOld);
+ penTick.DeleteObject();
+}
+
+void CCarrierSlotSelector::OnLButtonDown(UINT nFlags, CPoint point)
+{
+ LVHITTESTINFO ht = { 0 }; ht.pt = point;
+ int row = SubItemHitTest(&ht);
+ int sub = (row >= 0) ? ht.iSubItem : -1;
+
+ if (row >= 0 && sub >= 1)
+ {
+ int port = sub - 1;
+ if (SAFE_PORT(port) && SAFE_SLOT(row))
+ {
+ auto& pc = m_ports[port];
+ auto& cell = pc.slots[row];
+ CRect rcCell; GetSubItemRect(row, sub, LVIR_BOUNDS, rcCell);
+
+ if (pc.allocated || !cell.hasGlass)
+ {
+ CListCtrl::OnLButtonDown(nFlags, point);
+ return;
+ }
+
+ if (GetCheckboxRect(rcCell).PtInRect(point))
+ {
+ cell.checked = !cell.checked;
+ NotifySelectionChanged(port, row, cell.checked);
+ InvalidateRect(&rcCell, FALSE);
+ RebuildTexts();
+ return;
+ }
+
+ if (m_bShowMatToggle && GetMaterialTagRect(rcCell).PtInRect(point))
+ {
+ cell.material = (cell.material == MAT_G1) ? MAT_G2 : MAT_G1;
+ NotifyMaterialChanged(port, row, cell.material);
+ InvalidateRect(&rcCell, FALSE);
+ return;
+ }
+ }
+ }
+
+ CListCtrl::OnLButtonDown(nFlags, point);
+}
+
+void CCarrierSlotSelector::OnMouseMove(UINT nFlags, CPoint point)
+{
+ CListCtrl::OnMouseMove(nFlags, point);
+}
+
+void CCarrierSlotSelector::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ NMLVCUSTOMDRAW* pCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
+
+ switch (pCD->nmcd.dwDrawStage)
+ {
+ case CDDS_PREPAINT:
+ {
+ // 关键:整控件预先铺底,避免初次 hover/显示时“刷白”
+ CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);
+ CRect rcClient; GetClientRect(&rcClient);
+ pDC->FillSolidRect(rcClient, m_colBgNorm);
+ *pResult = CDRF_NOTIFYITEMDRAW;
+ return;
+ }
+
+ case CDDS_ITEMPREPAINT:
+ *pResult = CDRF_NOTIFYSUBITEMDRAW;
+ return;
+
+ case (CDDS_SUBITEM | CDDS_ITEMPREPAINT):
+ {
+ CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);
+ int row = (int)pCD->nmcd.dwItemSpec;
+ int sub = pCD->iSubItem;
+
+ CRect rc; GetSubItemRect(row, sub, LVIR_BOUNDS, rc);
+
+ // 背景
+ COLORREF bk = m_colBgNorm;
+ if (sub >= 1)
+ {
+ int port = sub - 1;
+ if (port % 2 == 0) bk = m_colBgAlt; // 交替列
+ if (SAFE_PORT(port) && m_ports[port].allocated) // 锁定列
+ bk = m_colLockBg;
+ }
+ pDC->FillSolidRect(rc, bk);
+
+ // 网格(上/左加粗;右/下细线)
+ CPen penTopLeft(PS_SOLID, (sub >= 1 && ((sub - 1) % 2 == 0)) ? 2 : 1, RGB(210, 214, 220));
+ CPen* pOldPen = pDC->SelectObject(&penTopLeft);
+ pDC->MoveTo(rc.left, rc.top); pDC->LineTo(rc.right, rc.top);
+ pDC->MoveTo(rc.left, rc.top); pDC->LineTo(rc.left, rc.bottom);
+ pDC->SelectObject(pOldPen); penTopLeft.DeleteObject();
+
+ CPen penThin(PS_SOLID, 1, RGB(220, 224, 230));
+ pOldPen = pDC->SelectObject(&penThin);
+ if (sub == GetHeaderCtrl()->GetItemCount() - 1) { pDC->MoveTo(rc.right - 1, rc.top); pDC->LineTo(rc.right - 1, rc.bottom); }
+ if (row == m_nSlots - 1) { pDC->MoveTo(rc.left, rc.bottom - 1); pDC->LineTo(rc.right, rc.bottom - 1); }
+ pDC->SelectObject(pOldPen); penThin.DeleteObject();
+
+ if (sub == 0)
+ {
+ CString s; s.Format(_T("Slot %d"), row + 1);
+ CFont* pOld = pDC->SelectObject(&m_fntBold);
+ pDC->SetBkMode(TRANSPARENT);
+ pDC->SetTextColor(RGB(60, 60, 64));
+ CRect rText = rc; rText.DeflateRect(8, 0, 8, 0);
+ pDC->DrawText(s, rText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
+ pDC->SelectObject(pOld);
+ }
+ else if (SAFE_PORT(sub - 1) && SAFE_SLOT(row))
+ {
+ int port = sub - 1;
+ const auto& pc = m_ports[port];
+ const auto& cell = pc.slots[row];
+
+ // 扁平复选框
+ CRect rChk = GetCheckboxRect(rc);
+ DrawFlatCheckbox(pDC, rChk, cell.checked, pc.allocated || !cell.hasGlass);
+
+ // 文本
+ CString s = GetDisplayId(port, row);
+ CRect rText = rc;
+ int leftPad = rChk.right + 6;
+ CRect rDot = GetStatusDotRect(rc);
+ CRect rTag = GetMaterialTagRect(rc);
+ int rightPad = rc.right - min(rTag.left - 6, rDot.left - 6);
+ rText.DeflateRect(leftPad - rc.left, 0, rightPad, 0);
+
+ CFont* pOld = pDC->SelectObject(&m_fntText);
+ pDC->SetBkMode(TRANSPARENT);
+ pDC->SetTextColor(cell.hasGlass ? RGB(40, 40, 40) : RGB(150, 150, 150));
+ pDC->DrawText(s, rText, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+ pDC->SelectObject(pOld);
+
+ // 物料标签(可隐藏)
+ if (m_bShowMatToggle)
+ {
+ CRect rT = rTag;
+ COLORREF crBorder = (cell.material == MAT_G2) ? RGB(180, 150, 220) : RGB(120, 160, 220);
+ COLORREF crFill = (cell.material == MAT_G2) ? RGB(243, 235, 250) : RGB(233, 240, 252);
+ COLORREF crText = (cell.material == MAT_G2) ? RGB(90, 60, 150) : RGB(50, 90, 160);
+ if (pc.allocated || !cell.hasGlass) { crBorder = RGB(210, 210, 210); crFill = RGB(245, 245, 245); crText = RGB(160, 160, 160); }
+ CBrush br(crFill); CPen tagPen(PS_SOLID, 1, crBorder);
+ CPen* pOldP = pDC->SelectObject(&tagPen);
+ CBrush* pOldB = pDC->SelectObject(&br);
+ pDC->RoundRect(rT, CPoint(6, 6));
+ pDC->SelectObject(pOldB); pDC->SelectObject(pOldP);
+ br.DeleteObject(); tagPen.DeleteObject();
+
+ CString t; t.Format(_T("G%d"), (cell.material == MAT_G2) ? 2 : 1);
+ CFont* pOldS = pDC->SelectObject(&m_fntSmall);
+ pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(crText);
+ pDC->DrawText(t, rT, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+ pDC->SelectObject(pOldS);
+ }
+
+ // 状态点
+ CRect rD = GetStatusDotRect(rc);
+ COLORREF dot = cell.hasGlass ? (pc.allocated ? RGB(215, 160, 60) : RGB(60, 170, 80)) : RGB(160, 160, 160);
+ CBrush bDot(dot); CBrush* pOldB = pDC->SelectObject(&bDot);
+ pDC->Ellipse(rD);
+ pDC->SelectObject(pOldB); bDot.DeleteObject();
+ }
+
+ *pResult = CDRF_SKIPDEFAULT; // 子项完全自绘
+ return;
+ }
+
+ default:
+ break;
+ }
+
+ *pResult = 0;
+}
+
+BOOL CCarrierSlotSelector::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
+{
+ NMHDR* pNM = reinterpret_cast<NMHDR*>(lParam);
+ if (pNM && pNM->hwndFrom == GetHeaderCtrl()->GetSafeHwnd())
+ {
+ switch (pNM->code)
+ {
+ case HDN_ITEMCLICKA:
+ case HDN_ITEMCLICKW:
+ {
+ HD_NOTIFY* phdn = reinterpret_cast<HD_NOTIFY*>(lParam);
+ OnHeaderClick(phdn->iItem);
+ if (pResult) *pResult = 0;
+ return TRUE; // 我们已处理
+ }
+ default: break;
+ }
+ }
+ return CListCtrl::OnNotify(wParam, lParam, pResult);
+}
+
+void CCarrierSlotSelector::OnHeaderClick(int iItem)
+{
+ // iItem: 0=Slot 列,>=1 为 Port 列
+ if (iItem <= 0) return;
+ int port = iItem - 1;
+ if (!SAFE_PORT(port) || m_ports[port].allocated) return;
+
+ // 计算是否“全选”
+ bool anyCheckable = false;
+ bool allChecked = true;
+ for (int r = 0; r < m_nSlots; ++r)
+ {
+ const auto& cell = m_ports[port].slots[r];
+ if (!cell.hasGlass) continue;
+ anyCheckable = true;
+ if (!cell.checked) { allChecked = false; break; }
+ }
+ if (!anyCheckable) return;
+
+ // 切换:若已全选 -> 取消全选;否则 -> 全选
+ CheckAllInPort(port, allChecked ? FALSE : TRUE, TRUE);
+}
+
+// ==== 通知 ====
+void CCarrierSlotSelector::NotifySelectionChanged(int /*port*/, int /*slot*/, BOOL /*checked*/)
+{
+ if (GetParent())
+ {
+ const int code = 0x2001; // CSSN_SELECTION_CHANGED
+ GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), code), (LPARAM)m_hWnd);
+ }
+}
+
+void CCarrierSlotSelector::NotifyMaterialChanged(int /*port*/, int /*slot*/, int /*material*/)
+{
+ if (GetParent())
+ {
+ const int code = 0x2002; // CSSN_MATERIAL_CHANGED
+ GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), code), (LPARAM)m_hWnd);
+ }
+}
--
Gitblit v1.9.3