| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #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); |
| | | } |
| | | } |