| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #include "stdafx.h" |
| | | #include "CCarrierSlotGrid.h" |
| | | #include <gdiplus.h> |
| | | #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<LOGFONTW*>(&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); |
| | | } |
| | | } |