#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();
|
|
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();
|
|
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, BOOL bNotify/* = FALSE*/)
|
{
|
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;
|
if(bNotify) 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,
|
int extraPadX,
|
int extraPadY) const
|
{
|
// 1) 基础客户区尺寸(含我们在客户区画的 1px 边框:左右+2/上下+2)
|
const CSize content = CalcBestClientSize(nSlotsOverride);
|
|
// 2) 取 DPI、滚动条尺寸(尽量用 ForDpi,回退到普通)
|
UINT dpi = 96;
|
#if (_WIN32_WINNT >= 0x0603)
|
if (m_hWnd) {
|
HMODULE hUser32 = ::GetModuleHandleW(L"user32.dll");
|
if (hUser32) {
|
typedef UINT(WINAPI* PFN_GETDPIFORWINDOW)(HWND);
|
auto pGetDpiForWindow = (PFN_GETDPIFORWINDOW)::GetProcAddress(hUser32, "GetDpiForWindow");
|
if (pGetDpiForWindow) dpi = pGetDpiForWindow(m_hWnd);
|
}
|
}
|
#endif
|
int cxVScroll = ::GetSystemMetrics(SM_CXVSCROLL);
|
int cyHScroll = ::GetSystemMetrics(SM_CYHSCROLL);
|
#if (_WIN32_WINNT >= 0x0A00) // Win10: 可用 GetSystemMetricsForDpi
|
HMODULE hUser32_2 = ::GetModuleHandleW(L"user32.dll");
|
if (hUser32_2) {
|
typedef int (WINAPI* PFN_GSMFD)(int, UINT);
|
auto pGsmForDpi = (PFN_GSMFD)::GetProcAddress(hUser32_2, "GetSystemMetricsForDpi");
|
if (pGsmForDpi) {
|
cxVScroll = pGsmForDpi(SM_CXVSCROLL, dpi);
|
cyHScroll = pGsmForDpi(SM_CYHSCROLL, dpi);
|
}
|
}
|
#endif
|
|
// 3) DPI 自适应安全余量(避免取整误差/主题差异)
|
const int autoPad = max(1, MulDiv(2, (int)dpi, 96)); // 约等于 2px@96DPI
|
const int padX = (extraPadX >= 0) ? extraPadX : autoPad;
|
const int padY = (extraPadY >= 0) ? extraPadY : autoPad;
|
|
// 4) 迭代:考虑滚动条相互影响,直到稳定不需要滚动条
|
int needCx = content.cx + padX;
|
int needCy = content.cy + padY;
|
|
while (true) {
|
bool needV = (GetTotalContentWidth() > needCx); // 宽不够→会出现横向滚动条?(注意:横条占高度)
|
bool needH = (m_headerCY + (nSlotsOverride > 0 ? nSlotsOverride : m_nSlots) * m_rowHeight + 2 /*客户区边框*/ > needCy); // 高不够→会出现纵条?(纵条占宽度)
|
|
// 注意:出现“纵向条”会减少可用宽度;出现“横向条”会减少可用高度
|
// 我们目标是让“即使扣掉这些占位”后也仍然 >= 内容尺寸
|
int adjCx = content.cx + padX + (needH ? cxVScroll : 0);
|
int adjCy = content.cy + padY + (needV ? cyHScroll : 0);
|
|
if (adjCx <= needCx && adjCy <= needCy) break; // 稳定:当前 needCx/needCy 足够
|
needCx = max(needCx, adjCx);
|
needCy = max(needCy, adjCy);
|
}
|
|
if (!includeNonClient) return CSize(needCx, needCy);
|
|
// 5) 把“理想客户区尺寸”换算成窗口外框尺寸(去掉 WS_H/VSCROLL 做换算)
|
RECT rc = { 0, 0, needCx, needCy };
|
DWORD style = GetStyle(); // ✅ 用真实样式,让系统把滚动条非客户区一并算进去
|
DWORD exStyle = GetExStyle();
|
::AdjustWindowRectEx(&rc, style, FALSE, exStyle);
|
return CSize(rc.right - rc.left, rc.bottom - rc.top);
|
}
|
|
void CCarrierSlotGrid::DisableSystemScrollbars()
|
{
|
// 去掉样式
|
ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, 0);
|
// 隐藏(兼容性)
|
ShowScrollBar(SB_HORZ, FALSE);
|
ShowScrollBar(SB_VERT, FALSE);
|
// 清空滚动信息
|
SCROLLINFO si{ sizeof(SCROLLINFO) }; si.fMask = SIF_ALL; si.nMin = 0; si.nMax = 0; si.nPage = 0; si.nPos = 0;
|
SetScrollInfo(SB_HORZ, &si, TRUE);
|
SetScrollInfo(SB_VERT, &si, TRUE);
|
// 让非客户区重算
|
SetWindowPos(nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
Invalidate(FALSE);
|
}
|
|
// 注意:这里用“无滚动条样式”来换算窗口外框尺寸,确保客户区=内容尺寸 + 我们客户区边框
|
void CCarrierSlotGrid::ResizeWindowToFitAll(BOOL includeNonClient, int nSlotsOverride)
|
{
|
// 计算内容所需客户区(CalcBestClientSize 内已包含我们客户区1px边框的 +2)
|
CSize need = CalcBestClientSize(nSlotsOverride);
|
|
if (!includeNonClient) {
|
SetWindowPos(nullptr, 0, 0, need.cx, need.cy,
|
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
return;
|
}
|
|
RECT rc = { 0, 0, need.cx, need.cy };
|
DWORD style = GetStyle() & ~(WS_HSCROLL | WS_VSCROLL); // ← 用“无滚动条”的样式来换算
|
DWORD exStyle = GetExStyle();
|
::AdjustWindowRectEx(&rc, style, FALSE, exStyle);
|
|
int w = rc.right - rc.left;
|
int h = rc.bottom - rc.top;
|
|
SetWindowPos(nullptr, 0, 0, w, h,
|
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
}
|
|
void CCarrierSlotGrid::SetNoScrollbarsMode(BOOL enable)
|
{
|
m_noScrollbars = !!enable;
|
|
if (m_noScrollbars) {
|
// 1) 偏移清零
|
m_scrollX = 0;
|
m_scrollY = 0;
|
|
// 2) 去掉样式并隐藏条
|
ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, 0);
|
ShowScrollBar(SB_BOTH, FALSE);
|
|
// 3) 清空滚动信息(即便资源里原本带了样式,也不再影响)
|
SCROLLINFO si{ sizeof(SCROLLINFO) }; si.fMask = SIF_ALL;
|
SetScrollInfo(SB_HORZ, &si, TRUE);
|
SetScrollInfo(SB_VERT, &si, TRUE);
|
|
// 4) 通知系统非客户区刷新,确保条被彻底移除
|
SetWindowPos(nullptr, 0, 0, 0, 0,
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
|
Invalidate(FALSE);
|
}
|
else {
|
// 退出无滚动条模式:仅恢复样式,实际范围会在 UpdateScrollRange 中重新设置
|
ModifyStyle(0, WS_HSCROLL | WS_VSCROLL, 0);
|
SetWindowPos(nullptr, 0, 0, 0, 0,
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
UpdateScrollRange();
|
Invalidate(FALSE);
|
}
|
}
|
|
void CCarrierSlotGrid::FitWindowToContentNoScroll(BOOL includeNonClient, int nSlotsOverride)
|
{
|
// 确保已处于“无滚动条模式”,防止系统在 AdjustWindowRectEx 时预留滚动条非客户区
|
SetNoScrollbarsMode(TRUE);
|
|
// 你自己的 CalcBestClientSize 已包含客户区 1px 边框(+2)的修正
|
CSize needCli = CalcBestClientSize(nSlotsOverride);
|
|
if (!includeNonClient) {
|
SetWindowPos(nullptr, 0, 0, needCli.cx, needCli.cy,
|
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
return;
|
}
|
|
RECT rc{ 0, 0, needCli.cx, needCli.cy };
|
// 注意:此时窗口样式已经没有 WS_H/VSCROLL 了——用真实样式换算即可
|
::AdjustWindowRectEx(&rc, GetStyle(), FALSE, GetExStyle());
|
const int w = rc.right - rc.left;
|
const int h = rc.bottom - rc.top;
|
|
SetWindowPos(nullptr, 0, 0, w, h,
|
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
}
|
|
// ---------- 几何 ----------
|
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()
|
{
|
if (m_noScrollbars) {
|
// 确保偏移一直为 0,不设任何滚动信息
|
m_scrollX = 0;
|
m_scrollY = 0;
|
return;
|
}
|
|
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)
|
{
|
if (m_noScrollbars) return; // ← 新增
|
|
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)
|
{
|
if (m_noScrollbars) return; // ← 新增
|
|
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)
|
{
|
if (m_noScrollbars) return FALSE; // ← 新增:彻底不滚
|
|
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)
|
{
|
// 兼容旧的 WM_COMMAND(可留,也可注释掉)
|
if (GetParent()) {
|
const int code = 0x2001;
|
GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), code), (LPARAM)m_hWnd);
|
}
|
// 新的 WM_NOTIFY,带上索引与状态
|
if (GetParent()) {
|
CSG_SEL_CHANGE nm{};
|
nm.hdr.hwndFrom = m_hWnd;
|
nm.hdr.idFrom = (UINT)GetDlgCtrlID();
|
nm.hdr.code = CSGN_SEL_CHANGED;
|
nm.port = port;
|
nm.slot = slot;
|
nm.checked = checked;
|
GetParent()->SendMessage(WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm);
|
}
|
}
|
|
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);
|
}
|
if (GetParent()) {
|
CSG_MAT_CHANGE nm{};
|
nm.hdr.hwndFrom = m_hWnd;
|
nm.hdr.idFrom = (UINT)GetDlgCtrlID();
|
nm.hdr.code = CSGN_MAT_CHANGED;
|
nm.port = port;
|
nm.slot = slot;
|
nm.material = material;
|
GetParent()->SendMessage(WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm);
|
}
|
}
|