#include "stdafx.h" #include "CCarrierSlotGrid.h" #include #pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; #define SAFE_PORT(p) ((p) >= 0 && (p) < (int)m_ports.size()) #define SAFE_SLOT(s) ((s) >= 0 && (s) < m_nSlots) static bool s_gdiplusInited = false; static ULONG_PTR s_gdiplusToken = 0; static void EnsureGdiplus() { if (!s_gdiplusInited) { GdiplusStartupInput in; if (GdiplusStartup(&s_gdiplusToken, &in, nullptr) == Ok) s_gdiplusInited = true; } } BEGIN_MESSAGE_MAP(CCarrierSlotGrid, CWnd) ON_WM_ERASEBKGND() ON_WM_PAINT() ON_WM_CREATE() ON_WM_SIZE() ON_WM_HSCROLL() ON_WM_VSCROLL() // ¡ï ÐÂÔö ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEWHEEL() ON_WM_MOUSEMOVE() ON_WM_SETCURSOR() ON_WM_SHOWWINDOW() ON_WM_WINDOWPOSCHANGED() END_MESSAGE_MAP() CCarrierSlotGrid::CCarrierSlotGrid() {} CCarrierSlotGrid::~CCarrierSlotGrid() { if ((HFONT)m_fntText) m_fntText.DeleteObject(); if ((HFONT)m_fntBold) m_fntBold.DeleteObject(); if ((HFONT)m_fntSmall) m_fntSmall.DeleteObject(); } void CCarrierSlotGrid::PreSubclassWindow() { CWnd::PreSubclassWindow(); if (GetParent() && GetParent()->GetFont()) SetFont(GetParent()->GetFont()); EnsureFonts(); EnsureGdiplus(); // È·±£Ñùʽ°üº¬¹ö¶¯Ìõ ModifyStyle(0, WS_HSCROLL | WS_VSCROLL, 0); SetWindowPos(nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); } int CCarrierSlotGrid::OnCreate(LPCREATESTRUCT cs) { if (CWnd::OnCreate(cs) == -1) return -1; if (GetParent() && GetParent()->GetFont()) SetFont(GetParent()->GetFont()); EnsureFonts(); EnsureGdiplus(); ModifyStyle(0, WS_HSCROLL | WS_VSCROLL, 0); return 0; } void CCarrierSlotGrid::EnsureFonts() { if ((HFONT)m_fntText) return; LOGFONT lf{}; bool ok = false; if (GetParent() && GetParent()->GetFont()) { GetParent()->GetFont()->GetLogFont(&lf); ok = true; } else if (GetFont()) { GetFont()->GetLogFont(&lf); ok = true; } else { NONCLIENTMETRICS ncm = { 0 }; ncm.cbSize = sizeof(NONCLIENTMETRICS); if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0)) { lf = ncm.lfMessageFont; ok = true; } } if (!ok) { CFont* pDef = CFont::FromHandle((HFONT)GetStockObject(DEFAULT_GUI_FONT)); pDef->GetLogFont(&lf); } m_fntText.CreateFontIndirect(&lf); LOGFONT lfb = lf; lfb.lfWeight = FW_SEMIBOLD; m_fntBold.CreateFontIndirect(&lfb); LOGFONT lfs = lf; lfs.lfHeight = (LONG)(lf.lfHeight * 0.9); if (lfs.lfHeight == 0) lfs.lfHeight = lf.lfHeight - 1; m_fntSmall.CreateFontIndirect(&lfs); } void CCarrierSlotGrid::InitGrid(int nPorts, int nSlots) { ASSERT(nPorts >= 1 && nSlots >= 1); m_ports.assign(nPorts, PortColumn{}); m_portColCXs.assign(nPorts, 180); m_nSlots = nSlots; for (auto& pc : m_ports) pc.slots.assign(m_nSlots, SlotCell{}); UpdateScrollRange(); Invalidate(FALSE); } void CCarrierSlotGrid::SetColumnWidths(int slotColWidth, int portColWidth) { if (slotColWidth > 0) m_slotColCX = slotColWidth; if (portColWidth > 0) for (size_t i = 0; i < m_portColCXs.size(); ++i) m_portColCXs[i] = portColWidth; UpdateScrollRange(); Invalidate(FALSE); } void CCarrierSlotGrid::SetRowHeight(int cy) { m_rowHeight = max(18, min(cy, 64)); UpdateScrollRange(); Invalidate(FALSE); } void CCarrierSlotGrid::SetHeaderHeight(int cy) { m_headerCY = max(20, min(cy, 48)); UpdateScrollRange(); Invalidate(FALSE); } void CCarrierSlotGrid::SetShowMaterialToggle(BOOL bShow) { m_bShowMatToggle = bShow; Invalidate(FALSE); } void CCarrierSlotGrid::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; Invalidate(FALSE); } void CCarrierSlotGrid::SetPortAllocated(int portIndex, BOOL allocated, LPCTSTR byName) { if (!SAFE_PORT(portIndex)) return; auto& pc = m_ports[portIndex]; pc.allocated = !!allocated; pc.allocatedBy = byName ? byName : _T(""); if (pc.allocated) for (auto& cell : pc.slots) cell.checked = false; Invalidate(FALSE); } BOOL CCarrierSlotGrid::IsPortAllocated(int portIndex) const { if (!SAFE_PORT(portIndex)) return FALSE; return m_ports[portIndex].allocated; } void CCarrierSlotGrid::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; Invalidate(FALSE); } void CCarrierSlotGrid::SetSlotChecked(int portIndex, int slotIndex, BOOL checked) { if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return; auto& pc = m_ports[portIndex]; if (pc.allocated) return; auto& cell = pc.slots[slotIndex]; if (!cell.hasGlass) return; cell.checked = !!checked; NotifySelectionChanged(portIndex, slotIndex, cell.checked); Invalidate(FALSE); } BOOL CCarrierSlotGrid::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 CCarrierSlotGrid::GetSlotMaterialType(int portIndex, int slotIndex) const { if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return MAT_G1; return m_ports[portIndex].slots[slotIndex].material; } void CCarrierSlotGrid::SetSlotMaterialType(int portIndex, int slotIndex, int material, BOOL bNotify) { if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return; auto& pc = m_ports[portIndex]; if (pc.allocated) return; auto& cell = pc.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); Invalidate(FALSE); } } CString CCarrierSlotGrid::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 CCarrierSlotGrid::CheckAllInPort(int portIndex, BOOL checked, BOOL bNotify) { if (!SAFE_PORT(portIndex)) return; auto& pc = m_ports[portIndex]; if (pc.allocated) return; for (int r = 0; r < m_nSlots; ++r) { auto& cell = pc.slots[r]; if (!cell.hasGlass) continue; if (cell.checked != !!checked) { cell.checked = !!checked; if (bNotify) NotifySelectionChanged(portIndex, r, cell.checked); } } Invalidate(FALSE); } void CCarrierSlotGrid::RebuildTexts() { Invalidate(FALSE); } CSize CCarrierSlotGrid::CalcBestClientSize(int nSlotsOverride) const { const int slots = (nSlotsOverride > 0) ? nSlotsOverride : m_nSlots; int w = m_slotColCX; for (int cx : m_portColCXs) w += cx; int h = m_headerCY + slots * m_rowHeight; return CSize(w, h); } CSize CCarrierSlotGrid::CalcBestWindowSize(BOOL includeNonClient, int nSlotsOverride) const { CSize cli = CalcBestClientSize(nSlotsOverride); if (!includeNonClient) return cli; RECT rc = { 0, 0, cli.cx, cli.cy }; // Ä¿±êÊÇ¡°¸ÕºÃ²»³öÏÖ¹ö¶¯Ìõ¡±µÄ´°¿ÚÍâ¿ò´óС£º // Óõ±Ç°Ñùʽȥµô WS_HSCROLL/WS_VSCROLL ÔÙ×ö AdjustWindowRectEx DWORD style = GetStyle(); DWORD exStyle = GetExStyle(); style &= ~(WS_HSCROLL | WS_VSCROLL); ::AdjustWindowRectEx(&rc, style, FALSE, exStyle); return CSize(rc.right - rc.left, rc.bottom - rc.top); } // ---------- ¼¸ºÎ ---------- CRect CCarrierSlotGrid::GetClientRectNoSB() const { CRect rc; GetClientRect(&rc); return rc; } CRect CCarrierSlotGrid::GetHeaderRect() const { CRect rc = GetClientRectNoSB(); rc.bottom = rc.top + m_headerCY; return rc; } int CCarrierSlotGrid::GetTotalContentWidth() const { int w = m_slotColCX; for (int cx : m_portColCXs) w += cx; return w; } CRect CCarrierSlotGrid::GetHeaderItemRect(int iItem) const { CRect rcHeader = GetHeaderRect(); int x = rcHeader.left - m_scrollX; if (iItem == 0) return CRect(x, rcHeader.top, x + m_slotColCX, rcHeader.bottom); x += m_slotColCX; for (int c = 1; c < iItem; ++c) x += m_portColCXs[c - 1]; int w = m_portColCXs[iItem - 1]; return CRect(x, rcHeader.top, x + w, rcHeader.bottom); } BOOL CCarrierSlotGrid::GetCellRect(int row, int sub, CRect& rc) const { CRect cli = GetClientRectNoSB(); int y0 = cli.top + m_headerCY - m_scrollY; int top = y0 + row * m_rowHeight; int bottom = top + m_rowHeight; if (bottom <= cli.top + m_headerCY || top >= cli.bottom) return FALSE; int x = cli.left - m_scrollX; if (sub == 0) { rc = CRect(x, top, x + m_slotColCX, bottom); return TRUE; } x += m_slotColCX; for (int c = 1; c < sub; ++c) x += m_portColCXs[c - 1]; int w = m_portColCXs[sub - 1]; rc = CRect(x, top, x + w, bottom); return TRUE; } CRect CCarrierSlotGrid::GetHeaderCheckboxRect(int iItem) const { CRect rItem = GetHeaderItemRect(iItem); const int box = 16; const int padR = 6; int vpad = max(0, (rItem.Height() - box) / 2); return CRect(rItem.right - padR - box, rItem.top + vpad, rItem.right - padR, rItem.bottom - vpad); } CRect CCarrierSlotGrid::GetCheckboxRect(const CRect& cell) const { int sz = max(14, min(int(m_rowHeight * 0.70), 20)); int leftPad = 8; int top = cell.top + (cell.Height() - sz) / 2; return CRect(cell.left + leftPad, top, cell.left + leftPad + sz, top + sz); } CRect CCarrierSlotGrid::GetMaterialTagRect(const CRect& cell) const { int h = max(14, min(int(m_rowHeight * 0.65), m_rowHeight - 8)); int w = 32; int gap = 6; int rightPadForDot = 16; int top = cell.top + (cell.Height() - h) / 2; int right = cell.right - rightPadForDot - gap; return CRect(right - w, top, right, top + h); } CRect CCarrierSlotGrid::GetStatusDotRect(const CRect& cell) const { int d = max(8, min(int(m_rowHeight * 0.42), 12)); int rightPad = 6; int top = cell.top + (cell.Height() - d) / 2; return CRect(cell.right - rightPad - d, top, cell.right - rightPad, top + d); } void CCarrierSlotGrid::UpdateScrollRange() { CRect rc; GetClientRect(&rc); // ´¹Ö± const int contentH = m_headerCY + m_nSlots * m_rowHeight; const int pageY = max(1, rc.Height()); const int maxPosY = max(0, contentH - pageY); m_scrollY = max(0, min(m_scrollY, maxPosY)); SCROLLINFO siY = { sizeof(SCROLLINFO) }; siY.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; siY.nMin = 0; siY.nMax = contentH - 1; siY.nPage = pageY; siY.nPos = m_scrollY; SetScrollInfo(SB_VERT, &siY, TRUE); // ˮƽ const int contentW = GetTotalContentWidth(); const int pageX = max(1, rc.Width()); const int maxPosX = max(0, contentW - pageX); m_scrollX = max(0, min(m_scrollX, maxPosX)); SCROLLINFO siX = { sizeof(SCROLLINFO) }; siX.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; siX.nMin = 0; siX.nMax = contentW - 1; siX.nPage = pageX; siX.nPos = m_scrollX; SetScrollInfo(SB_HORZ, &siX, TRUE); } // ---------- ±íÍ··Ö¸ôÏßÃüÖÐ ---------- int CCarrierSlotGrid::HitHeaderEdge(CPoint pt) const { if (!m_bAllowResize) return -1; // ¡û ÐÂÔö if (!GetHeaderRect().PtInRect(pt)) return -1; const int tol = 4; int x = GetHeaderRect().left - m_scrollX + m_slotColCX; if (abs(pt.x - x) <= tol) return 0; int cum = GetHeaderRect().left - m_scrollX + m_slotColCX; for (int i = 0; i <= GetPortCount() - 2; ++i) { cum += m_portColCXs[i]; if (abs(pt.x - cum) <= tol) return i + 1; } return -1; } // ---------- »æÖÆ ---------- BOOL CCarrierSlotGrid::OnEraseBkgnd(CDC* /*pDC*/) { return TRUE; } void CCarrierSlotGrid::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); 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); } void CCarrierSlotGrid::PaintTo(CDC* pDC) { EnsureGdiplus(); CRect cli = GetClientRectNoSB(); pDC->FillSolidRect(cli, m_colBg); // Header CRect rh = GetHeaderRect(); pDC->FillSolidRect(rh, ::GetSysColor(COLOR_BTNFACE)); CPen penSep(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW)); CPen* pOldPen = pDC->SelectObject(&penSep); pDC->MoveTo(rh.left, rh.bottom - 1); pDC->LineTo(rh.right, rh.bottom - 1); pDC->SelectObject(pOldPen); for (int i = 0; i <= GetPortCount(); ++i) { CRect rItem = GetHeaderItemRect(i); // ÐÞ¸ÄΪ£º if (i < GetPortCount()) { // ¡ï ×îºóÒ»Áв»»­·Ö¸ôÏß CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW)); pOldPen = pDC->SelectObject(&pen); pDC->MoveTo(rItem.right - 1, rItem.top); pDC->LineTo(rItem.right - 1, rItem.bottom); pDC->SelectObject(pOldPen); } CString text; if (i == 0) { text = _T("Slot"); CRect rt = rItem; rt.DeflateRect(6, 0, 6, 0); pDC->SetBkMode(TRANSPARENT); pDC->SelectObject(&m_fntBold); pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT)); pDC->DrawText(text, rt, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); } else { const auto& pc = m_ports[i - 1]; int selected = 0; bool any = false, all = true; for (const auto& cell : pc.slots) { if (cell.hasGlass) { any = true; if (cell.checked) ++selected; else all = false; } } if (!any) all = false; CString leftTitle = pc.carrierName.IsEmpty() ? pc.portName : (pc.portName + _T(" (") + pc.carrierName + _T(")")); // ¹´Ñ¡¿ò¿¿ÓÒ CRect rcCb = GetHeaderCheckboxRect(i); DrawFlatCheckbox(pDC, rcCb, all, pc.allocated); // ¼ÆÊýÌù½ü¹´Ñ¡¿ò×ó²à CString cnt; cnt.Format(_T("%d/%d"), selected, m_nSlots); SIZE szCnt{ 0,0 }; { CFont* o = pDC->SelectObject(&m_fntBold); GetTextExtentPoint32(pDC->GetSafeHdc(), cnt, cnt.GetLength(), &szCnt); pDC->SelectObject(o); } const int gap = 6; CRect rcCnt(rcCb.left - gap - szCnt.cx, rItem.top, rcCb.left - gap, rItem.bottom); // ×ó²à±êÌâ CRect rt = rItem; rt.DeflateRect(6, 0, (rItem.right - rcCnt.left) + 6, 0); pDC->SetBkMode(TRANSPARENT); pDC->SelectObject(&m_fntBold); pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT)); pDC->DrawText(leftTitle, rt, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); // ¼ÆÊý pDC->SelectObject(&m_fntBold); pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT)); pDC->DrawText(cnt, rcCnt, DT_RIGHT | DT_VCENTER | DT_SINGLELINE); } } // Cells for (int r = 0; r < m_nSlots; ++r) { for (int s = 0; s <= GetPortCount(); ++s) { CRect rc; if (!GetCellRect(r, s, rc)) continue; COLORREF bk = m_colBg; if (s >= 1) { int port = s - 1; if (port % 2 == 0) bk = m_colAlt; if (SAFE_PORT(port) && m_ports[port].allocated) bk = m_colLock; } pDC->FillSolidRect(rc, bk); CPen penMajor(PS_SOLID, (s >= 1 && ((s - 1) % 2 == 0)) ? 2 : 1, m_gridMajor); CPen* pOld = pDC->SelectObject(&penMajor); 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(pOld); CPen penMinor(PS_SOLID, 1, m_gridMinor); pOld = pDC->SelectObject(&penMinor); if (s == GetPortCount()) { pDC->MoveTo(rc.right - 1, rc.top); pDC->LineTo(rc.right - 1, rc.bottom); } if (r == m_nSlots - 1) { pDC->MoveTo(rc.left, rc.bottom - 1); pDC->LineTo(rc.right, rc.bottom - 1); } pDC->SelectObject(pOld); if (s == 0) { CString sl; sl.Format(_T("Slot %d"), r + 1); CRect rt = rc; rt.DeflateRect(8, 0, 8, 0); pDC->SelectObject(&m_fntBold); pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(RGB(60, 60, 64)); pDC->DrawText(sl, rt, DT_LEFT | DT_VCENTER | DT_SINGLELINE); } else { int port = s - 1; const auto& pc = m_ports[port]; const auto& cell = pc.slots[r]; CRect rChk = GetCheckboxRect(rc); DrawFlatCheckbox(pDC, rChk, cell.checked, pc.allocated || !cell.hasGlass); CString t = GetDisplayId(port, r); 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); pDC->SelectObject(&m_fntText); pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(cell.hasGlass ? m_text : m_textDim); pDC->DrawText(t, rText, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); if (m_bShowMatToggle) { CRect rT = GetMaterialTagRect(rc); 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); CString tx; tx.Format(_T("G%d"), (cell.material == MAT_G2) ? 2 : 1); pDC->SelectObject(&m_fntSmall); pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(crText); pDC->DrawText(tx, rT, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } // ״̬µã£¨GDI+ ¿¹¾â³Ý£© { Graphics g(pDC->GetSafeHdc()); g.SetSmoothingMode(SmoothingModeAntiAlias); COLORREF c = cell.hasGlass ? (pc.allocated ? RGB(215, 160, 60) : RGB(60, 170, 80)) : RGB(160, 160, 160); SolidBrush brush(Color(255, GetRValue(c), GetGValue(c), GetBValue(c))); Pen outline(Color(255, 120, 120, 120), 1.f); g.FillEllipse(&brush, rDot.left, rDot.top, rDot.Width(), rDot.Height()); g.DrawEllipse(&outline, (REAL)rDot.left, (REAL)rDot.top, (REAL)rDot.Width(), (REAL)rDot.Height()); } } } } // ===== ÔÚÿ¸öÒÑ·ÖÅä(allocated)µÄÁÐÖÐÑë»æÖÆ°ë͸Ã÷ LOCK ˮӡ£¨Óà HDC+LOGFONT ¹¹Ôì×ÖÌ壩===== { Gdiplus::Graphics g(pDC->GetSafeHdc()); g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); CRect cli = GetClientRectNoSB(); CRect rh = GetHeaderRect(); // È¡µ±Ç° UI ×ÖÌ壨ÓÅÏÈ´ÖÌ壩 LOGFONT lf{}; if ((HFONT)m_fntBold) m_fntBold.GetLogFont(&lf); else if ((HFONT)m_fntText) m_fntText.GetLogFont(&lf); for (int i = 0; i < GetPortCount(); ++i) { if (!m_ports[i].allocated) continue; // ÁоØÐΣ¨³ýÈ¥±íÍ·£¬¿¼ÂÇˮƽ¹ö¶¯£© CRect rCol = GetHeaderItemRect(i + 1); rCol.top = rh.bottom; rCol.bottom = cli.bottom; if (rCol.right <= cli.left || rCol.left >= cli.right || rCol.Height() <= 0) continue; // ×ÔÊÊÓ¦Ò»¸öºÏÊʵÄÏñËØ¸ß¶È int availW = rCol.Width() - 12; int availH = rCol.Height() - 12; int emPx = max(16, min(min(availW / 3, availH / 5), 72)); if (emPx < 16) emPx = 16; // ×ֺżõ°ë£¨²¢¸ø¸ö¸üµÍµÄ¶µµ×£¬±ÜÃâ̫С£© emPx = max(12, emPx / 2); // Óà LOGFONTW + HDC ¹¹Ôì GDI+ ×ÖÌå LOGFONTW lfw{}; #ifdef UNICODE lfw = *reinterpret_cast(&lf); #else lfw.lfHeight = lf.lfHeight; lfw.lfWidth = lf.lfWidth; lfw.lfEscapement = lf.lfEscapement; lfw.lfOrientation = lf.lfOrientation; lfw.lfWeight = lf.lfWeight; lfw.lfItalic = lf.lfItalic; lfw.lfUnderline = lf.lfUnderline; lfw.lfStrikeOut = lf.lfStrikeOut; lfw.lfCharSet = lf.lfCharSet; lfw.lfOutPrecision = lf.lfOutPrecision; lfw.lfClipPrecision = lf.lfClipPrecision; lfw.lfQuality = lf.lfQuality; lfw.lfPitchAndFamily = lf.lfPitchAndFamily; MultiByteToWideChar(CP_ACP, 0, lf.lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE); #endif lfw.lfHeight = -emPx; // ¸ºÖµ=°´ÏñËØ¸ß¶È lfw.lfWeight = FW_BOLD; Gdiplus::Font gdifont(pDC->GetSafeHdc(), &lfw); // ¡ï ¼ÓÉÏ Gdiplus:: Gdiplus::StringFormat fmt; fmt.SetAlignment(Gdiplus::StringAlignmentCenter); fmt.SetLineAlignment(Gdiplus::StringAlignmentCenter); Gdiplus::Color col(140, 120, 100, 60); // °ë͸Ã÷ Gdiplus::SolidBrush brush(col); Gdiplus::RectF box((Gdiplus::REAL)rCol.left, (Gdiplus::REAL)rCol.top, (Gdiplus::REAL)rCol.Width(), (Gdiplus::REAL)rCol.Height()); if (gdifont.GetLastStatus() == Gdiplus::Ok) { g.DrawString(L"LOCK", -1, &gdifont, box, &fmt, &brush); } else { Gdiplus::Font fallback(L"Arial", (Gdiplus::REAL)emPx, Gdiplus::FontStyleBold, Gdiplus::UnitPixel); g.DrawString(L"LOCK", -1, &fallback, box, &fmt, &brush); } } } // === ¿Í»§ÇøÄÚ 1px »ÒÉ«±ß¿ò£¨²»°ü¹ö¶¯Ìõ£¬µ«²»»áȱ½Ç/ÇÀ»æÖÆ£©=== { CRect cli; GetClientRect(&cli); // Óà FrameRect ¸üÎÈ£¨±ÜÃâÓÒϽǶªÏߣ© CBrush br; br.CreateSolidBrush(::GetSysColor(COLOR_3DSHADOW)); CRect r = cli; // ×¢Ò⣺¿Í»§Çø×ø±êÊÇ [0..width, 0..height]£»FrameRect »áÔÚÄڲ໭ 1px pDC->FrameRect(&r, &br); br.DeleteObject(); } } void CCarrierSlotGrid::OnPaint() { CPaintDC dc(this); CRect rc; GetClientRect(&rc); CDC mem; mem.CreateCompatibleDC(&dc); CBitmap bmp; bmp.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height()); HGDIOBJ ob = mem.SelectObject(bmp); PaintTo(&mem); dc.BitBlt(0, 0, rc.Width(), rc.Height(), &mem, 0, 0, SRCCOPY); mem.SelectObject(ob); } void CCarrierSlotGrid::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); UpdateScrollRange(); Invalidate(FALSE); RedrawWindow(nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); } void CCarrierSlotGrid::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pBar) { UNREFERENCED_PARAMETER(pBar); SCROLLINFO si = { sizeof(SCROLLINFO) }; si.fMask = SIF_ALL; GetScrollInfo(SB_HORZ, &si); int pos = m_scrollX; const int maxPos = max(0, (int)si.nMax - (int)si.nPage + 1); switch (nSBCode) { case SB_LINELEFT: pos -= 30; break; case SB_LINERIGHT: pos += 30; break; case SB_PAGELEFT: pos -= (int)si.nPage; break; case SB_PAGERIGHT: pos += (int)si.nPage; break; case SB_THUMBTRACK: case SB_THUMBPOSITION: pos = (int)si.nTrackPos; // ¡ï 32 λÍ϶¯Î»Öà break; default: return; } pos = max(0, min(pos, maxPos)); if (pos != m_scrollX) { m_scrollX = pos; SetScrollPos(SB_HORZ, m_scrollX); Invalidate(FALSE); } } void CCarrierSlotGrid::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pBar) { UNREFERENCED_PARAMETER(pBar); SCROLLINFO si = { sizeof(SCROLLINFO) }; si.fMask = SIF_ALL; GetScrollInfo(SB_VERT, &si); int pos = m_scrollY; const int maxPos = max(0, (int)si.nMax - (int)si.nPage + 1); switch (nSBCode) { case SB_LINEUP: pos -= m_rowHeight; break; case SB_LINEDOWN: pos += m_rowHeight; break; case SB_PAGEUP: pos -= (int)si.nPage; break; case SB_PAGEDOWN: pos += (int)si.nPage; break; case SB_THUMBTRACK: case SB_THUMBPOSITION: pos = (int)si.nTrackPos; // ¡ï 32 λÍ϶¯Î»Öà break; default: return; } pos = max(0, min(pos, maxPos)); if (pos != m_scrollY) { m_scrollY = pos; SetScrollPos(SB_VERT, m_scrollY); Invalidate(FALSE); } } BOOL CCarrierSlotGrid::OnMouseWheel(UINT, short zDelta, CPoint) { int delta = (zDelta > 0 ? -1 : +1) * (m_rowHeight * 3); m_scrollY = max(0, m_scrollY + delta); UpdateScrollRange(); Invalidate(FALSE); return TRUE; } void CCarrierSlotGrid::OnShowWindow(BOOL bShow, UINT nStatus) { CWnd::OnShowWindow(bShow, nStatus); if (bShow) { UpdateScrollRange(); Invalidate(FALSE); } } void CCarrierSlotGrid::OnWindowPosChanged(WINDOWPOS* wp) { CWnd::OnWindowPosChanged(wp); if (wp && (wp->flags & SWP_SHOWWINDOW)) { UpdateScrollRange(); Invalidate(FALSE); } } void CCarrierSlotGrid::OnLButtonDown(UINT nFlags, CPoint pt) { // ÊÇ·ñÍ϶¯Áпí int edge = m_bAllowResize ? HitHeaderEdge(pt) : -1; // ¡û ÐÞ¸Ä if (edge >= 0) { m_bResizing = true; m_resizeEdge = edge; m_resizeStartX = pt.x; m_slotColCXStart = m_slotColCX; m_portColCXsStart = m_portColCXs; SetCapture(); return; } // Header µã»÷£¨½ö¸´Ñ¡¿òÇøÓò£© if (GetHeaderRect().PtInRect(pt)) { for (int i = 1; i <= GetPortCount(); ++i) { CRect rItem = GetHeaderItemRect(i); if (!rItem.PtInRect(pt)) continue; CRect rcCb = GetHeaderCheckboxRect(i); if (!rcCb.PtInRect(pt)) return; int port = i - 1; if (!SAFE_PORT(port) || m_ports[port].allocated) return; bool any = false, all = true; for (auto& cell : m_ports[port].slots) { if (!cell.hasGlass) continue; any = true; if (!cell.checked) { all = false; break; } } if (!any) return; CheckAllInPort(port, all ? FALSE : TRUE, TRUE); return; } return; } // Cell µã»÷ CRect cli = GetClientRectNoSB(); if (pt.y < cli.top + m_headerCY) return; int yIn = pt.y - (cli.top + m_headerCY) + m_scrollY; int row = yIn / m_rowHeight; if (!SAFE_SLOT(row)) return; int x = pt.x + m_scrollX - cli.left; int sub = 0; if (x < m_slotColCX) sub = 0; else { x -= m_slotColCX; sub = 1; for (size_t i = 0; i < m_portColCXs.size(); ++i) { if (x < m_portColCXs[i]) { sub = (int)i + 1; break; } x -= m_portColCXs[i]; sub = (int)i + 2; } if (sub < 1 || sub > GetPortCount()) return; } if (sub == 0) return; int port = sub - 1; if (!SAFE_PORT(port)) return; auto& pc = m_ports[port]; auto& cell = pc.slots[row]; CRect rc; if (!GetCellRect(row, sub, rc)) return; if (pc.allocated || !cell.hasGlass) return; if (GetCheckboxRect(rc).PtInRect(pt)) { cell.checked = !cell.checked; NotifySelectionChanged(port, row, cell.checked); Invalidate(FALSE); return; } if (m_bShowMatToggle && GetMaterialTagRect(rc).PtInRect(pt)) { cell.material = (cell.material == MAT_G1) ? MAT_G2 : MAT_G1; NotifyMaterialChanged(port, row, cell.material); Invalidate(FALSE); return; } CWnd::OnLButtonDown(nFlags, pt); } void CCarrierSlotGrid::OnLButtonUp(UINT nFlags, CPoint pt) { if (m_bResizing) { ReleaseCapture(); m_bResizing = false; m_resizeEdge = -1; UpdateScrollRange(); Invalidate(FALSE); } CWnd::OnLButtonUp(nFlags, pt); } void CCarrierSlotGrid::OnMouseMove(UINT nFlags, CPoint pt) { if (m_bResizing) { int dx = pt.x - m_resizeStartX; if (m_resizeEdge == 0) { int nw = max(m_slotColMin, m_slotColCXStart + dx); if (nw != m_slotColCX) { m_slotColCX = nw; UpdateScrollRange(); Invalidate(FALSE); } } else { int idx = m_resizeEdge - 1; // µ÷Õû Port idx µÄ¿í¶È int nw = max(m_portColMin, m_portColCXsStart[idx] + dx); if (nw != m_portColCXs[idx]) { m_portColCXs[idx] = nw; UpdateScrollRange(); Invalidate(FALSE); } } return; } int edge = HitHeaderEdge(pt); if (edge != m_hitEdgeHover) { m_hitEdgeHover = edge; if (m_hitEdgeHover >= 0) ::SetCursor(::LoadCursor(nullptr, IDC_SIZEWE)); else ::SetCursor(::LoadCursor(nullptr, IDC_ARROW)); } } BOOL CCarrierSlotGrid::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { CPoint pt; ::GetCursorPos(&pt); ScreenToClient(&pt); if (m_bAllowResize && (m_bResizing || HitHeaderEdge(pt) >= 0)) { ::SetCursor(::LoadCursor(nullptr, IDC_SIZEWE)); return TRUE; } return CWnd::OnSetCursor(pWnd, nHitTest, message); } // ---------- ֪ͨ ---------- void CCarrierSlotGrid::NotifySelectionChanged(int /*port*/, int /*slot*/, BOOL /*checked*/) { if (GetParent()) { const int code = 0x2001; GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), code), (LPARAM)m_hWnd); } } void CCarrierSlotGrid::NotifyMaterialChanged(int /*port*/, int /*slot*/, int /*material*/) { if (GetParent()) { const int code = 0x2002; GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), code), (LPARAM)m_hWnd); } }