#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);
|
}
|
}
|