1.Port物料选择控件完善;
2.继续处理CControlJobManagerDlg的功能;
| | |
| | | #include "stdafx.h" |
| | | #include "stdafx.h" |
| | | #include "CCarrierSlotGrid.h" |
| | | #include <gdiplus.h> |
| | | #pragma comment(lib, "gdiplus.lib") |
| | |
| | | ON_WM_CREATE() |
| | | ON_WM_SIZE() |
| | | ON_WM_HSCROLL() |
| | | ON_WM_VSCROLL() // ★ 新增 |
| | | ON_WM_VSCROLL() // ★ 新增 |
| | | ON_WM_LBUTTONDOWN() |
| | | ON_WM_LBUTTONUP() |
| | | ON_WM_MOUSEWHEEL() |
| | |
| | | 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); |
| | | } |
| | | |
| | |
| | | if (GetParent() && GetParent()->GetFont()) SetFont(GetParent()->GetFont()); |
| | | EnsureFonts(); |
| | | EnsureGdiplus(); |
| | | ModifyStyle(0, WS_HSCROLL | WS_VSCROLL, 0); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | |
| | | Invalidate(FALSE); |
| | | } |
| | | |
| | | void CCarrierSlotGrid::SetSlotChecked(int portIndex, int slotIndex, BOOL checked) |
| | | 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]; |
| | |
| | | auto& cell = pc.slots[slotIndex]; |
| | | if (!cell.hasGlass) return; |
| | | cell.checked = !!checked; |
| | | NotifySelectionChanged(portIndex, slotIndex, cell.checked); |
| | | if(bNotify) NotifySelectionChanged(portIndex, slotIndex, cell.checked); |
| | | Invalidate(FALSE); |
| | | } |
| | | |
| | |
| | | |
| | | CString CCarrierSlotGrid::GetDisplayId(int portIndex, int slotIndex) const |
| | | { |
| | | CString s(_T("—")); |
| | | 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; |
| | |
| | | return CSize(w, h); |
| | | } |
| | | |
| | | CSize CCarrierSlotGrid::CalcBestWindowSize(BOOL includeNonClient, int nSlotsOverride) const |
| | | CSize CCarrierSlotGrid::CalcBestWindowSize(BOOL includeNonClient, |
| | | int nSlotsOverride, |
| | | int extraPadX, |
| | | int extraPadY) const |
| | | { |
| | | CSize cli = CalcBestClientSize(nSlotsOverride); |
| | | if (!includeNonClient) return cli; |
| | | // 1) 基础客户区尺寸(含我们在客户区画的 1px 边框:左右+2/上下+2) |
| | | const CSize content = CalcBestClientSize(nSlotsOverride); |
| | | |
| | | RECT rc = { 0, 0, cli.cx, cli.cy }; |
| | | // 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 |
| | | |
| | | // 目标是“刚好不出现滚动条”的窗口外框大小: |
| | | // 用当前样式去掉 WS_HSCROLL/WS_VSCROLL 再做 AdjustWindowRectEx |
| | | DWORD style = GetStyle(); |
| | | // 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(); |
| | | style &= ~(WS_HSCROLL | WS_VSCROLL); |
| | | |
| | | ::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; |
| | |
| | | |
| | | 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); |
| | |
| | | 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); |
| | |
| | | SetScrollInfo(SB_HORZ, &siX, TRUE); |
| | | } |
| | | |
| | | // ---------- 表头分隔线命中 ---------- |
| | | // ---------- 表头分隔线命中 ---------- |
| | | int CCarrierSlotGrid::HitHeaderEdge(CPoint pt) const |
| | | { |
| | | if (!m_bAllowResize) return -1; // ← 新增 |
| | | if (!m_bAllowResize) return -1; // ← 新增 |
| | | if (!GetHeaderRect().PtInRect(pt)) return -1; |
| | | const int tol = 4; |
| | | int x = GetHeaderRect().left - m_scrollX + m_slotColCX; |
| | |
| | | return -1; |
| | | } |
| | | |
| | | // ---------- 绘制 ---------- |
| | | // ---------- 绘制 ---------- |
| | | BOOL CCarrierSlotGrid::OnEraseBkgnd(CDC* /*pDC*/) { return TRUE; } |
| | | |
| | | void CCarrierSlotGrid::DrawFlatCheckbox(CDC* pDC, const CRect& r, bool checked, bool disabled) |
| | |
| | | { |
| | | CRect rItem = GetHeaderItemRect(i); |
| | | |
| | | // 修改为: |
| | | if (i < GetPortCount()) { // ★ 最后一列不画分隔线 |
| | | // 修改为: |
| | | if (i < GetPortCount()) { // ★ 最后一列不画分隔线 |
| | | CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW)); |
| | | pOldPen = pDC->SelectObject(&pen); |
| | | pDC->MoveTo(rItem.right - 1, rItem.top); |
| | |
| | | ? 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); |
| | |
| | | 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); |
| | |
| | | pDC->DrawText(tx, rT, DT_CENTER | DT_VCENTER | DT_SINGLELINE); |
| | | } |
| | | |
| | | // 状态点(GDI+ 抗锯齿) |
| | | // 状态点(GDI+ 抗锯齿) |
| | | { |
| | | Graphics g(pDC->GetSafeHdc()); |
| | | g.SetSmoothingMode(SmoothingModeAntiAlias); |
| | |
| | | |
| | | |
| | | |
| | | // ===== 在每个已分配(allocated)的列中央绘制半透明 LOCK 水印(用 HDC+LOGFONT 构造字体)===== |
| | | // ===== 在每个已分配(allocated)的列中央绘制半透明 LOCK 水印(用 HDC+LOGFONT 构造字体)===== |
| | | { |
| | | Gdiplus::Graphics g(pDC->GetSafeHdc()); |
| | | g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); |
| | |
| | | CRect cli = GetClientRectNoSB(); |
| | | CRect rh = GetHeaderRect(); |
| | | |
| | | // 取当前 UI 字体(优先粗体) |
| | | // 取当前 UI 字体(优先粗体) |
| | | LOGFONT lf{}; |
| | | if ((HFONT)m_fntBold) m_fntBold.GetLogFont(&lf); |
| | | else if ((HFONT)m_fntText) m_fntText.GetLogFont(&lf); |
| | |
| | | { |
| | | 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 + HDC 构造 GDI+ 字体 |
| | | LOGFONTW lfw{}; |
| | | #ifdef UNICODE |
| | | lfw = *reinterpret_cast<LOGFONTW*>(&lf); |
| | |
| | | lfw.lfPitchAndFamily = lf.lfPitchAndFamily; |
| | | MultiByteToWideChar(CP_ACP, 0, lf.lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE); |
| | | #endif |
| | | lfw.lfHeight = -emPx; // 负值=按像素高度 |
| | | lfw.lfHeight = -emPx; // 负值=按像素高度 |
| | | lfw.lfWeight = FW_BOLD; |
| | | |
| | | Gdiplus::Font gdifont(pDC->GetSafeHdc(), &lfw); // ★ 加上 Gdiplus:: |
| | | 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::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()); |
| | |
| | | } |
| | | } |
| | | |
| | | // === 客户区内 1px 灰色边框(不包滚动条,但不会缺角/抢绘制)=== |
| | | // === 客户区内 1px 灰色边框(不包滚动条,但不会缺角/抢绘制)=== |
| | | { |
| | | CRect cli; GetClientRect(&cli); |
| | | |
| | | // 用 FrameRect 更稳(避免右下角丢线) |
| | | // 用 FrameRect 更稳(避免右下角丢线) |
| | | CBrush br; br.CreateSolidBrush(::GetSysColor(COLOR_3DSHADOW)); |
| | | CRect r = cli; |
| | | // 注意:客户区坐标是 [0..width, 0..height];FrameRect 会在内侧画 1px |
| | | // 注意:客户区坐标是 [0..width, 0..height];FrameRect 会在内侧画 1px |
| | | pDC->FrameRect(&r, &br); |
| | | br.DeleteObject(); |
| | | } |
| | |
| | | |
| | | void CCarrierSlotGrid::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pBar) |
| | | { |
| | | if (m_noScrollbars) return; // ← 新增 |
| | | |
| | | UNREFERENCED_PARAMETER(pBar); |
| | | |
| | | SCROLLINFO si = { sizeof(SCROLLINFO) }; |
| | |
| | | case SB_PAGERIGHT: pos += (int)si.nPage; break; |
| | | case SB_THUMBTRACK: |
| | | case SB_THUMBPOSITION: |
| | | pos = (int)si.nTrackPos; // ★ 32 位拖动位置 |
| | | pos = (int)si.nTrackPos; // ★ 32 位拖动位置 |
| | | break; |
| | | default: |
| | | return; |
| | |
| | | |
| | | void CCarrierSlotGrid::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pBar) |
| | | { |
| | | if (m_noScrollbars) return; // ← 新增 |
| | | |
| | | UNREFERENCED_PARAMETER(pBar); |
| | | |
| | | SCROLLINFO si = { sizeof(SCROLLINFO) }; |
| | |
| | | case SB_PAGEDOWN: pos += (int)si.nPage; break; |
| | | case SB_THUMBTRACK: |
| | | case SB_THUMBPOSITION: |
| | | pos = (int)si.nTrackPos; // ★ 32 位拖动位置 |
| | | pos = (int)si.nTrackPos; // ★ 32 位拖动位置 |
| | | break; |
| | | default: |
| | | return; |
| | |
| | | |
| | | 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(); |
| | |
| | | |
| | | void CCarrierSlotGrid::OnLButtonDown(UINT nFlags, CPoint pt) |
| | | { |
| | | // 是否拖动列宽 |
| | | int edge = m_bAllowResize ? HitHeaderEdge(pt) : -1; // ← 修改 |
| | | // 是否拖动列宽 |
| | | int edge = m_bAllowResize ? HitHeaderEdge(pt) : -1; // ← 修改 |
| | | if (edge >= 0) |
| | | { |
| | | m_bResizing = true; |
| | |
| | | return; |
| | | } |
| | | |
| | | // Header 点击(仅复选框区域) |
| | | // Header 点击(仅复选框区域) |
| | | if (GetHeaderRect().PtInRect(pt)) |
| | | { |
| | | for (int i = 1; i <= GetPortCount(); ++i) |
| | |
| | | return; |
| | | } |
| | | |
| | | // Cell 点击 |
| | | // Cell 点击 |
| | | CRect cli = GetClientRectNoSB(); |
| | | if (pt.y < cli.top + m_headerCY) return; |
| | | int yIn = pt.y - (cli.top + m_headerCY) + m_scrollY; |
| | |
| | | } |
| | | else |
| | | { |
| | | int idx = m_resizeEdge - 1; // 调整 Port idx 的宽度 |
| | | 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 CWnd::OnSetCursor(pWnd, nHitTest, message); |
| | | } |
| | | |
| | | // ---------- ֪ͨ ---------- |
| | | void CCarrierSlotGrid::NotifySelectionChanged(int /*port*/, int /*slot*/, BOOL /*checked*/) |
| | | void CCarrierSlotGrid::NotifySelectionChanged(int port, int slot, BOOL checked) |
| | | { |
| | | if (GetParent()) |
| | | { |
| | | // 兼容旧的 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*/) |
| | | } |
| | | |
| | | void CCarrierSlotGrid::NotifyMaterialChanged(int port, int slot, int material) |
| | | { |
| | | if (GetParent()) |
| | | { |
| | | 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); |
| | | } |
| | | } |
| | | |
| | |
| | | #include <afxwin.h> |
| | | #endif |
| | | |
| | | |
| | | // 放到 CCarrierSlotGrid 类定义前或内部 public: 区都可 |
| | | enum { CSGN_SEL_CHANGED = 1, CSGN_MAT_CHANGED = 2 }; |
| | | |
| | | struct CSG_SEL_CHANGE { |
| | | NMHDR hdr; // hdr.code = CSGN_SEL_CHANGED |
| | | int port; // 0..GetPortCount()-1 |
| | | int slot; // 0..GetSlotCount()-1 |
| | | BOOL checked; |
| | | }; |
| | | |
| | | struct CSG_MAT_CHANGE { |
| | | NMHDR hdr; // hdr.code = CSGN_MAT_CHANGED |
| | | int port; |
| | | int slot; |
| | | int material; // 1=G1, 2=G2 |
| | | }; |
| | | |
| | | |
| | | class CCarrierSlotGrid : public CWnd |
| | | { |
| | | public: |
| | |
| | | BOOL IsPortAllocated(int portIndex) const; |
| | | |
| | | void SetSlotGlass(int portIndex, int slotIndex, BOOL hasGlass, LPCTSTR coreId, int material); |
| | | void SetSlotChecked(int portIndex, int slotIndex, BOOL checked); |
| | | void SetSlotChecked(int portIndex, int slotIndex, BOOL checked, BOOL bNotify = FALSE); |
| | | BOOL GetSlotChecked(int portIndex, int slotIndex) const; |
| | | |
| | | int GetSlotMaterialType(int portIndex, int slotIndex) const; |
| | |
| | | // - CalcBestClientSize:内容区域(不含滚动条/非客户区)刚好容纳表头+所有行、全部列 |
| | | // - CalcBestWindowSize:在当前窗口样式下,将“内容大小”转换为窗口外框大小(会考虑 WS_BORDER/CLIENTEDGE 等) |
| | | // 默认按“隐藏滚动条”的目标来算(即不把 WS_HSCROLL/WS_VSCROLL 计入调整) |
| | | // 计算最佳大小(支持可选安全边距,默认按 DPI 约等于 2px) |
| | | CSize CalcBestClientSize(int nSlotsOverride = -1) const; |
| | | CSize CalcBestWindowSize(BOOL includeNonClient = TRUE, int nSlotsOverride = -1) const; |
| | | CSize CalcBestWindowSize(BOOL includeNonClient = TRUE, |
| | | int nSlotsOverride = -1, |
| | | int extraPadX = -1, // -1 表示按 DPI 自动 |
| | | int extraPadY = -1) const; |
| | | |
| | | // 永久禁用系统滚动条(去掉样式并刷新非客户区) |
| | | void DisableSystemScrollbars(); |
| | | |
| | | // 把窗口尺寸调到正好容纳所有内容(不出现滚动条) |
| | | void ResizeWindowToFitAll(BOOL includeNonClient = TRUE, int nSlotsOverride = -1); |
| | | |
| | | // 进入/退出无滚动条模式(去样式、清滚动、忽略滚动消息) |
| | | void SetNoScrollbarsMode(BOOL enable); |
| | | |
| | | // 在“无滚动条模式”下,把窗口尺寸调到刚好容纳所有内容(不出现滚动条) |
| | | void FitWindowToContentNoScroll(BOOL includeNonClient = TRUE, int nSlotsOverride = -1); |
| | | |
| | | protected: |
| | | // 数据 |
| | |
| | | std::vector<int> m_portColCXsStart; |
| | | int m_hitEdgeHover = -1; |
| | | bool m_bAllowResize = true; // ← 新增:是否允许拖动列宽 |
| | | bool m_noScrollbars = false; // ← 新增:无滚动条模式 |
| | | |
| | | // 工具 |
| | | void EnsureFonts(); |
| | |
| | | CCjPage2::CCjPage2(CWnd* pParent /*=nullptr*/) |
| | | : CCjPageBase(IDD_CJ_PAGE2, pParent) |
| | | { |
| | | |
| | | m_nSelRadioId = 0; |
| | | } |
| | | |
| | | CCjPage2::~CCjPage2() |
| | |
| | | ON_BN_CLICKED(IDC_RADIO2, &CCjPage2::OnBnClickedRadio2) |
| | | ON_BN_CLICKED(IDC_RADIO3, &CCjPage2::OnBnClickedRadio3) |
| | | ON_BN_CLICKED(IDC_RADIO4, &CCjPage2::OnBnClickedRadio4) |
| | | ON_NOTIFY(CSGN_SEL_CHANGED, IDC_GRID1, &CCjPage2::OnGridSelChanged) |
| | | ON_NOTIFY(CSGN_MAT_CHANGED, IDC_GRID1, &CCjPage2::OnGridMatChanged) |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | |
| | | m_grid.SubclassDlgItem(IDC_GRID1, this); |
| | | m_grid.InitGrid(4, 8); |
| | | m_grid.SetColumnWidths(100, 220); |
| | | m_grid.SetRowHeight(28); |
| | | m_grid.SetHeaderHeight(32); |
| | | m_grid.SetRowHeight(32); |
| | | m_grid.SetHeaderHeight(36); |
| | | m_grid.EnableColumnResize(FALSE); // 禁止拖动列宽 |
| | | m_grid.SetShowMaterialToggle(TRUE); |
| | | m_grid.DisableSystemScrollbars(); |
| | | m_grid.ResizeWindowToFitAll(TRUE); // TRUE=包含非客户区(边框、标题栏) |
| | | m_grid.SetNoScrollbarsMode(TRUE); // 彻底禁用滚动条 |
| | | m_grid.FitWindowToContentNoScroll(TRUE); // 窗口尺寸刚好容纳全部内容(不出现滚动条) |
| | | |
| | | m_grid.SetPortInfo(0, _T("Port 1"), _T("")); |
| | | m_grid.SetPortInfo(1, _T("Port 2"), _T("")); |
| | | m_grid.SetPortInfo(2, _T("Port 3"), _T("")); |
| | | m_grid.SetPortInfo(3, _T("Port 4"), _T("")); |
| | | |
| | | |
| | | // 测试数据 |
| | | char szBuffer[256]; |
| | | for (int port = 0; port < 4; port++) { |
| | | for (int slot = 0; slot < 8; slot++) { |
| | | sprintf_s(szBuffer, 256, "Gls%04d%04d", port + 1, slot + 1); |
| | | m_grid.SetSlotGlass(port, slot, TRUE, szBuffer, CCarrierSlotGrid::MAT_G1); |
| | | } |
| | | } |
| | | |
| | | UpdatePjData(); |
| | | |
| | |
| | | |
| | | // 让控件窗口尺寸自动匹配当前列宽/行数(不出现滚动条) |
| | | if (::IsWindow(m_grid.m_hWnd)) { |
| | | CSize best = m_grid.CalcBestWindowSize(TRUE); // 计算到含非客户区的最终窗口大小 |
| | | CSize best = m_grid.CalcBestWindowSize(TRUE, -1, 2, 2); |
| | | pItem->MoveWindow(rcItem.left, rcItem.top, best.cx, best.cy); |
| | | pItem->Invalidate(); |
| | | } |
| | | } |
| | | |
| | | void CCjPage2::OnApply() |
| | | int CCjPage2::OnApply() |
| | | { |
| | | //SERVO::CProcessJob* |
| | | if (m_pContext == nullptr) return; |
| | | if (m_pContext == nullptr) return -1; |
| | | PJWarp* pPjWarp = (PJWarp*)m_pContext; |
| | | SERVO::CProcessJob* pProcessJob = (SERVO::CProcessJob*)pPjWarp->pj; |
| | | |
| | |
| | | char szBuffer[256]; |
| | | GetDlgItemText(IDC_EDIT_PJ_ID, szBuffer, 256); |
| | | for (auto item : m_pjWarps) { |
| | | if (item.pj != m_pContext) { |
| | | if (item.pj != pProcessJob) { |
| | | SERVO::CProcessJob* temp = (SERVO::CProcessJob*)item.pj; |
| | | if (temp->id().compare(std::string(szBuffer)) == 0) { |
| | | bOkName = FALSE; |
| | |
| | | } |
| | | if (!bOkName) { |
| | | AfxMessageBox("不能使用和其它Process Job相同的ID"); |
| | | return; |
| | | return -1; |
| | | } |
| | | |
| | | |
| | |
| | | } |
| | | |
| | | |
| | | // 更新Port |
| | | int port = -1; |
| | | static int ids[] = { IDC_RADIO1, IDC_RADIO2, IDC_RADIO3, IDC_RADIO4 }; |
| | | for (int i = 0; i < 4; i++) { |
| | | int state = ((CButton*)GetDlgItem(ids[i]))->GetCheck(); |
| | | if (state == BST_CHECKED) port = i; |
| | | } |
| | | pPjWarp->port = port; |
| | | |
| | | if (pPjWarp->port != -1) { |
| | | for (int i = 0; i < 8; i++) { |
| | | pPjWarp->checkSlot[i] = m_grid.GetSlotChecked(pPjWarp->port, i); |
| | | pPjWarp->material[i] = m_grid.GetSlotMaterialType(pPjWarp->port, i); |
| | | } |
| | | } |
| | | |
| | | |
| | | ContentChanged(1); |
| | | return 0; |
| | | } |
| | | |
| | | void CCjPage2::UpdatePjData() |
| | | { |
| | | if (m_pContext == nullptr) return; |
| | | |
| | | m_bContentChangedLock = TRUE; |
| | | |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_RECIPE); |
| | |
| | | pComboBox->AddString(CString(recipe.c_str())); |
| | | } |
| | | |
| | | if (m_pContext) { |
| | | |
| | | // ComboBox |
| | | PJWarp* pPjWarp = (PJWarp*)m_pContext; |
| | | SERVO::CProcessJob* pProcessJob = (SERVO::CProcessJob*)pPjWarp->pj; |
| | | SetDlgItemText(IDC_EDIT_PJ_ID, pProcessJob->id().c_str()); |
| | | int idx = pComboBox->FindStringExact(-1, pProcessJob->recipeSpec().c_str()); |
| | | if (idx != CB_ERR) pComboBox->SetCurSel(idx); |
| | | |
| | | |
| | | // 4个checkbox |
| | | static int ids[] = { IDC_RADIO1, IDC_RADIO2, IDC_RADIO3, IDC_RADIO4}; |
| | | static char* pszUsed[] = { "Port1(已占用)", "Port2(已占用)", "Port3(已占用)", "Port4(已占用)" }; |
| | | static char* pszUnUsed[] = { "Port1(可用)", "Port2(可用)", "Port3(可用)", "Port4(可用)" }; |
| | | |
| | | int portIndex = -1; |
| | | bool enable[] = {true, true, true, true}; |
| | | bool checked[] = { false, false, false, false }; |
| | | for (auto item : m_pjWarps) { |
| | | if (0 <= item.port && item.port <= 4 && item.pj != ((PJWarp*)m_pContext)->pj) { |
| | | enable[item.port] = false; |
| | | } |
| | | } |
| | | if (0 <= ((PJWarp*)m_pContext)->port && ((PJWarp*)m_pContext)->port <= 3) { |
| | | checked[((PJWarp*)m_pContext)->port] = true; |
| | | portIndex = ((PJWarp*)m_pContext)->port; |
| | | m_nSelRadioId = ids[((PJWarp*)m_pContext)->port]; |
| | | } |
| | | |
| | | // 示例:设置Port信息、锁列、填充Glass |
| | | /* |
| | | m_grid.SetPortInfo(0, _T("Port 1"), _T("")); |
| | | m_grid.SetPortInfo(1, _T("Port 2"), _T("")); |
| | | m_grid.SetPortInfo(2, _T("Port 3"), _T("Carrier C")); |
| | | m_grid.SetPortInfo(3, _T("Port 4"), _T("Carrier D")); |
| | | m_grid.SetPortAllocated(2, TRUE, _T("ProcessJob 1")); |
| | | m_grid.SetSlotGlass(0, 0, TRUE, _T("A00123"), CCarrierSlotGrid::MAT_G1); |
| | | m_grid.SetSlotGlass(0, 1, TRUE, _T("A00124"), CCarrierSlotGrid::MAT_G1); |
| | | */ |
| | | for (int i = 0; i < 4; i++) { |
| | | CButton* pButton = (CButton*)GetDlgItem(ids[i]); |
| | | pButton->SetCheck(checked[i] ? BST_CHECKED : BST_UNCHECKED); |
| | | pButton->SetWindowText(enable[i] ? pszUnUsed[i] : pszUsed[i]); |
| | | pButton->EnableWindow(enable[i]); |
| | | |
| | | m_grid.SetPortAllocated(i, !checked[i], _T("")); |
| | | } |
| | | |
| | | |
| | | // 设置勾选数据 |
| | | if (portIndex != -1) { |
| | | for (int i = 0; i < 8; i++) { |
| | | m_grid.SetSlotChecked(portIndex, i, ((PJWarp*)m_pContext)->checkSlot[i]); |
| | | m_grid.SetSlotMaterialType(portIndex, i, ((PJWarp*)m_pContext)->material[i]); |
| | | } |
| | | } |
| | | |
| | | m_bContentChangedLock = FALSE; |
| | | } |
| | |
| | | |
| | | void CCjPage2::OnBnClickedRadio1() |
| | | { |
| | | m_grid.SetPortAllocated(0, FALSE, _T("")); |
| | | m_grid.SetPortAllocated(1, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(2, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(3, TRUE, _T("")); |
| | | BOOL lock[] = {TRUE, TRUE, TRUE, TRUE}; |
| | | if (IDC_RADIO1 == m_nSelRadioId) { |
| | | CheckRadioButton(IDC_RADIO1, IDC_RADIO4, 0); |
| | | m_nSelRadioId = 0; |
| | | } |
| | | else { |
| | | CheckRadioButton(IDC_RADIO1, IDC_RADIO4, IDC_RADIO1); |
| | | m_nSelRadioId = IDC_RADIO1; |
| | | lock[0] = FALSE; |
| | | } |
| | | |
| | | for (int i = 0; i < 4; i++) { |
| | | m_grid.SetPortAllocated(i, lock[i], _T("")); |
| | | } |
| | | |
| | | ContentChanged(0); |
| | | } |
| | | |
| | | void CCjPage2::OnBnClickedRadio2() |
| | | { |
| | | m_grid.SetPortAllocated(0, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(1, FALSE, _T("")); |
| | | m_grid.SetPortAllocated(2, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(3, TRUE, _T("")); |
| | | BOOL lock[] = { TRUE, TRUE, TRUE, TRUE }; |
| | | if (IDC_RADIO2 == m_nSelRadioId) { |
| | | CheckRadioButton(IDC_RADIO1, IDC_RADIO4, 0); |
| | | m_nSelRadioId = 0; |
| | | } |
| | | else { |
| | | CheckRadioButton(IDC_RADIO1, IDC_RADIO4, IDC_RADIO2); |
| | | m_nSelRadioId = IDC_RADIO2; |
| | | lock[1] = FALSE; |
| | | } |
| | | |
| | | for (int i = 0; i < 4; i++) { |
| | | m_grid.SetPortAllocated(i, lock[i], _T("")); |
| | | } |
| | | |
| | | ContentChanged(0); |
| | | } |
| | | |
| | | void CCjPage2::OnBnClickedRadio3() |
| | | { |
| | | m_grid.SetPortAllocated(0, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(1, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(2, FALSE, _T("")); |
| | | m_grid.SetPortAllocated(3, TRUE, _T("")); |
| | | BOOL lock[] = { TRUE, TRUE, TRUE, TRUE }; |
| | | if (IDC_RADIO3 == m_nSelRadioId) { |
| | | CheckRadioButton(IDC_RADIO1, IDC_RADIO4, 0); |
| | | m_nSelRadioId = 0; |
| | | } |
| | | else { |
| | | CheckRadioButton(IDC_RADIO1, IDC_RADIO4, IDC_RADIO3); |
| | | m_nSelRadioId = IDC_RADIO3; |
| | | lock[2] = FALSE; |
| | | } |
| | | |
| | | for (int i = 0; i < 4; i++) { |
| | | m_grid.SetPortAllocated(i, lock[i], _T("")); |
| | | } |
| | | |
| | | ContentChanged(0); |
| | | } |
| | | |
| | | void CCjPage2::OnBnClickedRadio4() |
| | | { |
| | | m_grid.SetPortAllocated(0, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(1, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(2, TRUE, _T("")); |
| | | m_grid.SetPortAllocated(3, FALSE, _T("")); |
| | | BOOL lock[] = { TRUE, TRUE, TRUE, TRUE }; |
| | | if (IDC_RADIO4 == m_nSelRadioId) { |
| | | CheckRadioButton(IDC_RADIO1, IDC_RADIO4, 0); |
| | | m_nSelRadioId = 0; |
| | | } |
| | | else { |
| | | CheckRadioButton(IDC_RADIO1, IDC_RADIO4, IDC_RADIO4); |
| | | m_nSelRadioId = IDC_RADIO4; |
| | | lock[3] = FALSE; |
| | | } |
| | | |
| | | for (int i = 0; i < 4; i++) { |
| | | m_grid.SetPortAllocated(i, lock[i], _T("")); |
| | | } |
| | | |
| | | ContentChanged(0); |
| | | } |
| | | |
| | | void CCjPage2::OnGridSelChanged(NMHDR* pNMHDR, LRESULT* pResult) |
| | | { |
| | | auto* nm = reinterpret_cast<CSG_SEL_CHANGE*>(pNMHDR); |
| | | const int port = nm->port; |
| | | const int slot = nm->slot; |
| | | const BOOL chk = nm->checked; |
| | | |
| | | // 这里写你的业务逻辑 |
| | | // 例如:更新状态栏 / 同步其它控件 / 统计数量 |
| | | ContentChanged(0); |
| | | |
| | | /* |
| | | if (m_pContext != nullptr) { |
| | | PJWarp* pjWarp = (PJWarp*)m_pContext; |
| | | for (int i = 0; i < 8; i++) { |
| | | pjWarp->checkSlot[i] = m_grid.GetSlotChecked(port, i); |
| | | pjWarp->material[i] = m_grid.GetSlotMaterialType(port, i); |
| | | } |
| | | } |
| | | */ |
| | | |
| | | *pResult = 0; |
| | | } |
| | | |
| | | void CCjPage2::OnGridMatChanged(NMHDR* pNMHDR, LRESULT* pResult) |
| | | { |
| | | auto* nm = reinterpret_cast<CSG_MAT_CHANGE*>(pNMHDR); |
| | | const int port = nm->port; |
| | | const int slot = nm->slot; |
| | | const int mat = nm->material; // 1/2 |
| | | |
| | | // 例如:即刻刷新右侧预览/记录日志等 |
| | | ContentChanged(0); |
| | | |
| | | /* |
| | | if (m_pContext != nullptr) { |
| | | PJWarp* pjWarp = (PJWarp*)m_pContext; |
| | | for (int i = 0; i < 8; i++) { |
| | | pjWarp->checkSlot[i] = m_grid.GetSlotChecked(port, i); |
| | | pjWarp->material[i] = m_grid.GetSlotMaterialType(port, i); |
| | | } |
| | | } |
| | | */ |
| | | |
| | | *pResult = 0; |
| | | } |
| | |
| | | |
| | | |
| | | struct PJWarp { |
| | | BOOL addToCj; |
| | | void* pj; |
| | | int port; |
| | | bool checkSlot[8]; |
| | | BOOL checkSlot[8]; |
| | | int material[8]; |
| | | }; |
| | | |
| | | // CPjPage1 对话框 |
| | |
| | | |
| | | protected: |
| | | void Resize(); |
| | | virtual void OnApply(); |
| | | virtual int OnApply(); |
| | | virtual void OnSetContext(void* pContext); |
| | | |
| | | private: |
| | |
| | | private: |
| | | CCarrierSlotGrid m_grid; |
| | | std::vector<PJWarp> m_pjWarps; |
| | | int m_nSelRadioId; |
| | | |
| | | // 对话框数据 |
| | | #ifdef AFX_DESIGN_TIME |
| | |
| | | afx_msg void OnBnClickedRadio2(); |
| | | afx_msg void OnBnClickedRadio3(); |
| | | afx_msg void OnBnClickedRadio4(); |
| | | afx_msg void OnGridSelChanged(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnGridMatChanged(NMHDR* pNMHDR, LRESULT* pResult); |
| | | }; |
| | |
| | | */ |
| | | } |
| | | |
| | | void CCjPage3::OnApply() |
| | | int CCjPage3::OnApply() |
| | | { |
| | | |
| | | return 0; |
| | | } |
| | |
| | | |
| | | protected: |
| | | void Resize(); |
| | | virtual void OnApply(); |
| | | virtual int OnApply(); |
| | | |
| | | // 对话框数据 |
| | | #ifdef AFX_DESIGN_TIME |
| | |
| | | |
| | | public: |
| | | void SetTitle(CString strTitle); |
| | | virtual void OnApply() {}; |
| | | virtual int OnApply() { return 0; }; |
| | | void SetOnContentChanged(ONCONTENTCHANGED onContentChanged); |
| | | void SetContext(void* pContext, int type); |
| | | void* GetContext(); |
| | |
| | | BEGIN_MESSAGE_MAP(CControlJobManagerDlg, CDialogEx) |
| | | ON_WM_SIZE() |
| | | ON_WM_GETMINMAXINFO() |
| | | ON_NOTIFY(TVN_SELCHANGED, IDC_TREE1, &CControlJobManagerDlg::OnTvnSelchangedTree1) |
| | | ON_NOTIFY(TVN_ITEMCHANGED, IDC_TREE1, &CControlJobManagerDlg::OnTvnItemChangedTree) |
| | | ON_NOTIFY(NM_CLICK, IDC_TREE1, &CControlJobManagerDlg::OnTreeClick) // 新增 |
| | | ON_NOTIFY(TVN_KEYDOWN, IDC_TREE1, &CControlJobManagerDlg::OnTreeKeyDown) // 新增 |
| | | ON_MESSAGE(WM_AFTER_TVCHECK, &CControlJobManagerDlg::OnAfterTvCheck) // 新增 |
| | | ON_WM_DESTROY() |
| | | ON_BN_CLICKED(IDC_BUTTON_APPLY, &CControlJobManagerDlg::OnBnClickedButtonApply) |
| | | ON_NOTIFY(TVN_SELCHANGING, IDC_TREE1, &CControlJobManagerDlg::OnTvnSelchangingTree1) |
| | | ON_BN_CLICKED(IDC_BUTTON_BATH_COMPLETION, &CControlJobManagerDlg::OnBnClickedButtonBathCompletion) |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | |
| | | |
| | | void CControlJobManagerDlg::UpdateCtrlState() |
| | | { |
| | | auto& master = theApp.m_model.getMaster(); |
| | | GetDlgItem(IDC_BUTTON_BATH_COMPLETION)->EnableWindow(false); |
| | | GetDlgItem(IDC_BUTTON_BATH_COMPLETION)->EnableWindow(true); |
| | | } |
| | | |
| | | void CControlJobManagerDlg::UpdateControlJob() |
| | |
| | | for (auto& item : m_pjWarps) { |
| | | HTREEITEM hItem = m_tree.InsertItem(((SERVO::CProcessJob*)item.pj)->id().c_str(), 0, 0, hRoot); |
| | | m_tree.SetItemData(hItem, (DWORD_PTR)&item); |
| | | m_tree.SetItemState(hItem, INDEXTOSTATEIMAGEMASK(false ? 2 : 1), TVIS_STATEIMAGEMASK); |
| | | m_tree.SetItemState(hItem, INDEXTOSTATEIMAGEMASK(item.addToCj ? 2 : 1), TVIS_STATEIMAGEMASK); |
| | | } |
| | | m_tree.Expand(hRoot, TVE_EXPAND); |
| | | } |
| | |
| | | return m_pControlJob->removePjPointer(pj->id()); |
| | | } |
| | | |
| | | void CControlJobManagerDlg::OnTvnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult) |
| | | void CControlJobManagerDlg::OnTvnItemChangedTree(NMHDR* pNMHDR, LRESULT* pResult) |
| | | { |
| | | auto* p = reinterpret_cast<LPNMTREEVIEW>(pNMHDR); |
| | | UINT oldState = p->itemOld.state, newState = p->itemNew.state; |
| | | HTREEITEM hItem = p->itemNew.hItem; |
| | | |
| | | if (((oldState ^ newState) & TVIS_STATEIMAGEMASK) != 0) { |
| | | const int idx = (newState & TVIS_STATEIMAGEMASK) >> 12; // 1=未选,2=已选 |
| | | const bool checked = (idx == 2); |
| | | |
| | | PJWarp* pjWarp = (PJWarp*)m_tree.GetItemData(hItem); |
| | | if (pjWarp != nullptr) { |
| | | CString s; s.Format("%s %d", ((SERVO::CProcessJob*)pjWarp->pj)->id().c_str(), |
| | | checked ? "" : ""); |
| | | AfxMessageBox(s); |
| | | } |
| | | } |
| | | |
| | | |
| | | *pResult = 0; |
| | | } |
| | | |
| | | // 命中复选框:用 NM_CLICK 做命中测试,然后“滞后”读取新状态 |
| | | void CControlJobManagerDlg::OnTreeClick(NMHDR* pNMHDR, LRESULT* pResult) |
| | | { |
| | | *pResult = 0; |
| | | |
| | | DWORD pos = ::GetMessagePos(); |
| | | CPoint pt(GET_X_LPARAM(pos), GET_Y_LPARAM(pos)); |
| | | m_tree.ScreenToClient(&pt); |
| | | |
| | | TVHITTESTINFO ht{}; ht.pt = pt; |
| | | HTREEITEM hItem = m_tree.HitTest(&ht); |
| | | if (hItem && (ht.flags & TVHT_ONITEMSTATEICON)) { |
| | | // 让 TreeView 先切换,再异步读取最终状态 |
| | | PostMessage(WM_AFTER_TVCHECK, (WPARAM)hItem, 0); |
| | | } |
| | | } |
| | | |
| | | // 空格键也会切换复选框 |
| | | void CControlJobManagerDlg::OnTreeKeyDown(NMHDR* pNMHDR, LRESULT* pResult) |
| | | { |
| | | *pResult = 0; |
| | | auto* p = reinterpret_cast<LPNMTVKEYDOWN>(pNMHDR); |
| | | if (p->wVKey == VK_SPACE) { |
| | | HTREEITEM hItem = m_tree.GetSelectedItem(); |
| | | if (hItem) PostMessage(WM_AFTER_TVCHECK, (WPARAM)hItem, 0); |
| | | } |
| | | } |
| | | |
| | | // 统一处理(读最终状态 + 你的业务) |
| | | LRESULT CControlJobManagerDlg::OnAfterTvCheck(WPARAM wParam, LPARAM /*lParam*/) |
| | | { |
| | | HTREEITEM hItem = (HTREEITEM)wParam; |
| | | if (!hItem) return 0; |
| | | |
| | | // 只处理第二层:根的直接子节点(可选) |
| | | auto getLevel = [&](HTREEITEM h) { |
| | | int lv = 0; for (HTREEITEM p = m_tree.GetParentItem(h); p; p = m_tree.GetParentItem(p)) ++lv; return lv; |
| | | }; |
| | | if (getLevel(hItem) != 1) return 0; |
| | | |
| | | BOOL checked = m_tree.GetCheck(hItem); |
| | | |
| | | // 你的业务逻辑(修正了 CString::Format 的参数类型) |
| | | auto* pjWarp = reinterpret_cast<PJWarp*>(m_tree.GetItemData(hItem)); |
| | | if (pjWarp) { |
| | | pjWarp->addToCj = checked; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void CControlJobManagerDlg::OnTvnSelchangingTree1(NMHDR* pNMHDR, LRESULT* pResult) |
| | | { |
| | | LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR); |
| | | |
| | | bool allow = FALSE; |
| | | HTREEITEM hOldSel = pNMTreeView->itemOld.hItem; |
| | | HTREEITEM hSel = pNMTreeView->itemNew.hItem; |
| | | if (hSel != nullptr) { |
| | | HTREEITEM hParent = m_tree.GetParentItem(hSel); |
| | | if (hParent == nullptr) { |
| | | SERVO::CControlJob* cj = (SERVO::CControlJob*)m_tree.GetItemData(hSel); |
| | | ASSERT(m_pages.size() == 3); |
| | | m_pages[0]->ShowWindow(SW_HIDE); |
| | | m_pages[1]->ShowWindow(SW_HIDE); |
| | | m_pages[2]->ShowWindow(SW_SHOW); |
| | | if (0 == ShowPage(2)) { |
| | | |
| | | } |
| | | } |
| | | else if (m_tree.GetParentItem(hParent) == nullptr) { |
| | | if (0 == ShowPage(1)) { |
| | | PJWarp* pjWarp = (PJWarp*)m_tree.GetItemData(hSel); |
| | | m_pages[0]->ShowWindow(SW_HIDE); |
| | | m_pages[1]->ShowWindow(SW_SHOW); |
| | | m_pages[2]->ShowWindow(SW_HIDE); |
| | | m_pages[1]->SetContext(pjWarp, 1); |
| | | |
| | | ((CCjPage2*)m_pages[1])->SetPjWarps(m_pjWarps); |
| | | m_pages[1]->SetContext(pjWarp, 1); |
| | | } |
| | | else { |
| | | allow = TRUE; |
| | | } |
| | | } |
| | | else { |
| | | // 有祖先 → 第三层及以下 → Glass |
| | | } |
| | | } |
| | | |
| | | *pResult = 0; |
| | | *pResult = allow; |
| | | } |
| | | |
| | | int CControlJobManagerDlg::ShowPage(int index) |
| | | { |
| | | ASSERT(0 <= index && index <= 2); |
| | | |
| | | for (int i = 0; i < 3; i++) { |
| | | if (m_pages[i]->IsWindowVisible()) { |
| | | int ret = m_pages[i]->OnApply(); |
| | | if (ret != 0) return -1; |
| | | } |
| | | |
| | | m_pages[i]->ShowWindow(index == i ? SW_SHOW : SW_HIDE); |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void CControlJobManagerDlg::OnDestroy() |
| | |
| | | for (int i = 0; i < 4; i++) { |
| | | sprintf_s(szBuffer, 256, "PJ%03d", i + 1); |
| | | SERVO::CProcessJob* pj = new SERVO::CProcessJob(std::string(szBuffer)); |
| | | PJWarp pjWarp; |
| | | PJWarp pjWarp = {}; |
| | | pjWarp.pj = pj; |
| | | pjWarp.port = -1; |
| | | m_pjWarps.push_back(pjWarp); |
| | |
| | | void CControlJobManagerDlg::OnBnClickedButtonApply() |
| | | { |
| | | for (auto item : m_pages) { |
| | | item->OnApply(); |
| | | } |
| | | if (item->IsWindowVisible()) { |
| | | if (0 == item->OnApply()) { |
| | | GetDlgItem(IDC_BUTTON_APPLY)->EnableWindow(FALSE); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | void CControlJobManagerDlg::UpProcessJobId(PJWarp* pjWarp) |
| | |
| | | m_state.pjWarps = m_pjWarps; |
| | | m_bHasState = true; |
| | | } |
| | | |
| | | void CControlJobManagerDlg::OnBnClickedButtonBathCompletion() |
| | | { |
| | | // 先应用 |
| | | for (int i = 0; i < 3; i++) { |
| | | if (m_pages[i]->IsWindowVisible()) { |
| | | int ret = m_pages[i]->OnApply(); |
| | | if (ret != 0) return ; |
| | | } |
| | | } |
| | | GetDlgItem(IDC_BUTTON_APPLY)->EnableWindow(FALSE); |
| | | |
| | | |
| | | // 先检查数据正确性 |
| | | int checkCount = 0; |
| | | for (auto item : m_pjWarps) { |
| | | if (!item.addToCj) continue; |
| | | checkCount++; |
| | | } |
| | | if (checkCount == 0) { |
| | | AfxMessageBox(_T("您没有选择要进行工艺处理的Process Job!\n请在要进行工艺处理的Process Job前打勾。")); |
| | | return; |
| | | } |
| | | |
| | | |
| | | |
| | | auto& master = theApp.m_model.getMaster(); |
| | | |
| | | std::vector<SERVO::CProcessJob*> pjs; |
| | | for (auto item : m_pjWarps) { |
| | | if (!item.addToCj) continue; |
| | | if (item.port == -1) continue; |
| | | BOOL bCheck = FALSE; |
| | | for (int i = 0; i < 8; i++) { |
| | | if (item.checkSlot[i]) { |
| | | bCheck = TRUE; |
| | | break; |
| | | } |
| | | } |
| | | if (!bCheck) continue; |
| | | |
| | | |
| | | SERVO::CProcessJob* pScr = (SERVO::CProcessJob*)item.pj; |
| | | SERVO::CProcessJob * pj = new SERVO::CProcessJob(pScr->id()); |
| | | pj->setRecipe(SERVO::RecipeMethod::NoTuning, pScr->recipeSpec()); |
| | | |
| | | std::vector<SERVO::CarrierSlotInfo> carriers; |
| | | SERVO::CarrierSlotInfo csi; |
| | | csi.carrierId = "Port" + std::to_string(item.port + 1); |
| | | for (int i = 0; i < 8; i++) { |
| | | if (item.checkSlot[i]) { |
| | | csi.slots.push_back(i); |
| | | } |
| | | } |
| | | carriers.push_back(csi); |
| | | pj->setCarriers(carriers); |
| | | pjs.push_back(pj); |
| | | |
| | | m_pControlJob->addPJ(pScr->id()); |
| | | } |
| | | |
| | | |
| | | if (pjs.empty()) { |
| | | AfxMessageBox(_T("没有需要进行工艺处理的Process Job!\n可能未选择Port或选择任何物料。")); |
| | | return; |
| | | } |
| | | |
| | | |
| | | m_pControlJob->setPJs(pjs); |
| | | int nRet = master.setProcessJobs(pjs); |
| | | master.setControlJob(*m_pControlJob); |
| | | } |
| | |
| | | #include "ApredTreeCtrl2.h" |
| | | |
| | | |
| | | #define WM_AFTER_TVCHECK (WM_USER + 1000) |
| | | |
| | | // CControlJobManagerDlg 对话框 |
| | | |
| | | class CControlJobManagerDlg : public CDialogEx |
| | |
| | | void InitData(); |
| | | void LoadState(); |
| | | void SaveState(); |
| | | int ShowPage(int index); |
| | | |
| | | private: |
| | | std::vector<CCjPageBase*> m_pages; |
| | |
| | | virtual BOOL OnInitDialog(); |
| | | afx_msg void OnSize(UINT nType, int cx, int cy); |
| | | afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI); |
| | | afx_msg void OnTvnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnTvnItemChangedTree(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnDestroy(); |
| | | afx_msg void OnBnClickedButtonApply(); |
| | | afx_msg void OnTvnSelchangingTree1(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnBnClickedButtonBathCompletion(); |
| | | afx_msg void OnTreeClick(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnTreeKeyDown(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg LRESULT OnAfterTvCheck(WPARAM wParam, LPARAM lParam); |
| | | }; |
| | |
| | | void CLoadPort::setIndex(unsigned int index) |
| | | { |
| | | m_nIndex = index; |
| | | |
| | | std::string id = "Port" + std::to_string(index + 1); |
| | | m_portStatusReport.setCassetteId(id.c_str()); |
| | | } |
| | | |
| | | unsigned int CLoadPort::getIndex() |
| | |
| | | } |
| | | } |
| | | else if (id == IDC_BUTTON_JOBS) { |
| | | static int i = 0; i++; |
| | | if (i % 2 == 0) { |
| | | CControlJobManagerDlg dlg; |
| | | dlg.DoModal(); |
| | | |
| | | /* |
| | | } |
| | | else { |
| | | CControlJobDlg dlg; |
| | | dlg.SetControlJob(theApp.m_model.m_master.getControlJob()); |
| | | dlg.DoModal(); |
| | | */ |
| | | } |
| | | } |
| | | else if (id == IDC_BUTTON_PORT_CONFIG) { |
| | | CPortConfigurationDlg dlg; |