// SearchBarDlg.cpp - 浮动搜索工具栏实现 #include "stdafx.h" #include "SearchBarDlg.h" #include "2015RemoteDlg.h" IMPLEMENT_DYNAMIC(CSearchBarDlg, CDialogEx) CSearchBarDlg::CSearchBarDlg(CMy2015RemoteDlg* pParent) : CDialogLangEx(IDD_SEARCH_BAR, pParent) , m_pParent(pParent) , m_nCurrentIndex(-1) { m_brushEdit.CreateSolidBrush(RGB(60, 60, 60)); m_brushStatic.CreateSolidBrush(RGB(40, 40, 40)); } CSearchBarDlg::~CSearchBarDlg() { m_brushEdit.DeleteObject(); m_brushStatic.DeleteObject(); } void CSearchBarDlg::DoDataExchange(CDataExchange* pDX) { __super::DoDataExchange(pDX); DDX_Control(pDX, IDC_SEARCH_EDIT, m_editSearch); DDX_Control(pDX, IDC_SEARCH_COUNT, m_staticCount); } BEGIN_MESSAGE_MAP(CSearchBarDlg, CDialogEx) ON_BN_CLICKED(IDC_SEARCH_PREV, &CSearchBarDlg::OnBnClickedPrev) ON_BN_CLICKED(IDC_SEARCH_NEXT, &CSearchBarDlg::OnBnClickedNext) ON_BN_CLICKED(IDC_SEARCH_CLOSE, &CSearchBarDlg::OnBnClickedClose) ON_EN_CHANGE(IDC_SEARCH_EDIT, &CSearchBarDlg::OnEnChangeSearch) ON_WM_TIMER() ON_WM_ERASEBKGND() ON_WM_CTLCOLOR() END_MESSAGE_MAP() BOOL CSearchBarDlg::OnInitDialog() { __super::OnInitDialog(); // 设置分层窗口样式(透明 + 不抢焦点) ModifyStyleEx(0, WS_EX_LAYERED | WS_EX_NOACTIVATE); SetLayeredWindowAttributes(0, 200, LWA_ALPHA); // 子类化按钮为 CIconButton m_btnPrev.SubclassDlgItem(IDC_SEARCH_PREV, this); m_btnNext.SubclassDlgItem(IDC_SEARCH_NEXT, this); m_btnClose.SubclassDlgItem(IDC_SEARCH_CLOSE, this); // 设置图标 m_btnPrev.SetIconDrawFunc(CIconButton::DrawIconArrowLeft); m_btnNext.SetIconDrawFunc(CIconButton::DrawIconArrowRight); m_btnClose.SetIconDrawFunc(CIconButton::DrawIconClose); m_btnClose.SetIsCloseButton(true); // 创建工具提示 m_tooltip.Create(this); m_tooltip.Activate(TRUE); m_tooltip.AddTool(&m_btnPrev, _TR("上一个 (Shift+Enter)")); m_tooltip.AddTool(&m_btnNext, _TR("下一个 (Enter)")); m_tooltip.AddTool(&m_btnClose, _TR("关闭 (Esc)")); // 设置输入框提示文本 m_editSearch.SetCueBanner(L"Search...", TRUE); // 初始化计数文本 m_staticCount.SetWindowText(_T("")); return TRUE; } BOOL CSearchBarDlg::PreTranslateMessage(MSG* pMsg) { if (m_tooltip.GetSafeHwnd()) { m_tooltip.RelayEvent(pMsg); } if (pMsg->message == WM_KEYDOWN) { if (pMsg->wParam == VK_ESCAPE) { Hide(); return TRUE; } if (pMsg->wParam == VK_RETURN) { if (GetKeyState(VK_SHIFT) & 0x8000) { GotoPrev(); } else { GotoNext(); } return TRUE; } // F3 = 下一个, Shift+F3 = 上一个 if (pMsg->wParam == VK_F3) { if (GetKeyState(VK_SHIFT) & 0x8000) { GotoPrev(); } else { GotoNext(); } return TRUE; } } return __super::PreTranslateMessage(pMsg); } BOOL CSearchBarDlg::OnEraseBkgnd(CDC* pDC) { CRect rect; GetClientRect(&rect); pDC->FillSolidRect(rect, RGB(40, 40, 40)); return TRUE; } HBRUSH CSearchBarDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = __super::OnCtlColor(pDC, pWnd, nCtlColor); // 输入框深色背景 if (nCtlColor == CTLCOLOR_EDIT && pWnd->GetDlgCtrlID() == IDC_SEARCH_EDIT) { pDC->SetTextColor(RGB(240, 240, 240)); pDC->SetBkColor(RGB(60, 60, 60)); return m_brushEdit; } // 静态文本(计数)- 使用实色背景避免重绘问题 if (nCtlColor == CTLCOLOR_STATIC) { pDC->SetTextColor(RGB(180, 180, 180)); pDC->SetBkColor(RGB(40, 40, 40)); return m_brushStatic; } return hbr; } void CSearchBarDlg::Show() { if (!GetSafeHwnd()) { Create(IDD_SEARCH_BAR, m_pParent); } UpdatePosition(); ShowWindow(SW_SHOWNOACTIVATE); SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); // 让输入框获得焦点并选中所有文本 m_editSearch.SetFocus(); m_editSearch.SetSel(0, -1); } void CSearchBarDlg::Hide() { KillTimer(TIMER_SEARCH); // 清理定时器 ShowWindow(SW_HIDE); // 将焦点还给主窗口列表 if (m_pParent) { m_pParent->SetFocus(); } } void CSearchBarDlg::InvalidateCache() { // 杀掉 pending 的搜索定时器,避免竞争条件 KillTimer(TIMER_SEARCH); // 清空搜索缓存 m_strLastSearch.Empty(); m_Results.clear(); m_nCurrentIndex = -1; // 如果搜索栏存在且可见,立即重新搜索并更新界面 if (GetSafeHwnd() && IsWindowVisible()) { CString searchText; m_editSearch.GetWindowText(searchText); if (!searchText.IsEmpty()) { DoSearch(); } else { UpdateCountText(); } } } void CSearchBarDlg::UpdatePosition() { if (!m_pParent || !GetSafeHwnd()) return; CRect rcParent; m_pParent->GetWindowRect(&rcParent); CRect rcThis; GetWindowRect(&rcThis); // 居中显示 int x = rcParent.left + (rcParent.Width() - rcThis.Width()) / 2; int y = rcParent.top + (rcParent.Height() - rcThis.Height()) / 2; SetWindowPos(NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } void CSearchBarDlg::OnEnChangeSearch() { // 延迟搜索:重置定时器,避免每次输入都搜索 KillTimer(TIMER_SEARCH); SetTimer(TIMER_SEARCH, SEARCH_DELAY_MS, NULL); } void CSearchBarDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == TIMER_SEARCH) { KillTimer(TIMER_SEARCH); DoSearch(); } __super::OnTimer(nIDEvent); } void CSearchBarDlg::DoSearch() { CString searchText; m_editSearch.GetWindowText(searchText); // 空文本时清空结果 if (searchText.IsEmpty()) { m_Results.clear(); m_nCurrentIndex = -1; m_strLastSearch.Empty(); UpdateCountText(); return; } // 相同文本不重复搜索(注意:Tab 切换时 m_strLastSearch 会被清空,强制重新搜索) if (searchText == m_strLastSearch && !m_Results.empty()) { return; } m_strLastSearch = searchText; // 大小写不敏感 searchText.MakeLower(); m_Results.clear(); if (!m_pParent) return; // 获取当前选中项的 clientID,用于确定搜索起始位置 uint64_t selectedClientID = 0; POSITION pos = m_pParent->m_CList_Online.GetFirstSelectedItemPosition(); if (pos) { int nSelectedItem = m_pParent->m_CList_Online.GetNextSelectedItem(pos); EnterCriticalSection(&m_pParent->m_cs); context* selCtx = m_pParent->GetContextByListIndex(nSelectedItem); if (selCtx) selectedClientID = selCtx->GetClientID(); LeaveCriticalSection(&m_pParent->m_cs); } // 加锁保护:访问 m_FilteredIndices, m_HostList, context EnterCriticalSection(&m_pParent->m_cs); // 遍历过滤后的列表,存储匹配项的 clientID(而非索引) int count = (int)m_pParent->m_FilteredIndices.size(); for (int i = 0; i < count; i++) { context* ctx = m_pParent->GetContextByListIndex(i); if (!ctx) continue; bool matched = false; // 搜索各列:IP(0), 地理位置(2), 计算机名/备注(3), 操作系统(4), 版本(8) int cols[] = { 0, 2, 3, 4, 8 }; for (int col : cols) { CString colText; // 备注列特殊处理 if (col == 3) { // ONLINELIST_COMPUTER_NAME colText = m_pParent->m_ClientMap->GetClientMapData(ctx->GetClientID(), MAP_NOTE); if (colText.IsEmpty()) { colText = ctx->GetClientData(col); } } else { colText = ctx->GetClientData(col); } colText.MakeLower(); if (colText.Find(searchText) >= 0) { matched = true; break; } } if (matched) { m_Results.push_back(ctx->GetClientID()); } } LeaveCriticalSection(&m_pParent->m_cs); // 定位到最接近当前选中项的结果 m_nCurrentIndex = -1; if (!m_Results.empty()) { m_nCurrentIndex = 0; if (selectedClientID != 0) { for (int i = 0; i < (int)m_Results.size(); i++) { if (m_Results[i] == selectedClientID) { m_nCurrentIndex = i; break; } } } GotoResult(m_nCurrentIndex); } UpdateCountText(); } void CSearchBarDlg::GotoPrev() { if (m_Results.empty()) return; m_nCurrentIndex--; if (m_nCurrentIndex < 0) { m_nCurrentIndex = (int)m_Results.size() - 1; // 循环 } GotoResult(m_nCurrentIndex); UpdateCountText(); } void CSearchBarDlg::GotoNext() { if (m_Results.empty()) return; m_nCurrentIndex++; if (m_nCurrentIndex >= (int)m_Results.size()) { m_nCurrentIndex = 0; // 循环 } GotoResult(m_nCurrentIndex); UpdateCountText(); } int CSearchBarDlg::FindListIndexByClientID(uint64_t clientID) { if (!m_pParent || clientID == 0) return -1; // 加锁查找 EnterCriticalSection(&m_pParent->m_cs); int count = (int)m_pParent->m_FilteredIndices.size(); for (int i = 0; i < count; i++) { context* ctx = m_pParent->GetContextByListIndex(i); if (ctx && ctx->GetClientID() == clientID) { LeaveCriticalSection(&m_pParent->m_cs); return i; } } LeaveCriticalSection(&m_pParent->m_cs); return -1; } void CSearchBarDlg::GotoResult(int index) { if (!m_pParent) return; // 循环查找有效结果(避免递归导致栈溢出) int attempts = 0; int maxAttempts = (int)m_Results.size(); while (attempts < maxAttempts) { if (index < 0 || index >= (int)m_Results.size()) { m_nCurrentIndex = -1; UpdateCountText(); return; } uint64_t clientID = m_Results[index]; int listIndex = FindListIndexByClientID(clientID); if (listIndex >= 0) { // 找到有效结果 m_nCurrentIndex = index; // 取消所有选中 int nItem = -1; while ((nItem = m_pParent->m_CList_Online.GetNextItem(-1, LVNI_SELECTED)) != -1) { m_pParent->m_CList_Online.SetItemState(nItem, 0, LVIS_SELECTED | LVIS_FOCUSED); } // 选中并滚动到目标项 m_pParent->m_CList_Online.SetItemState(listIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); m_pParent->m_CList_Online.EnsureVisible(listIndex, FALSE); return; } // 该主机不在当前列表中,移除并尝试下一个 m_Results.erase(m_Results.begin() + index); if (m_Results.empty()) { m_nCurrentIndex = -1; UpdateCountText(); return; } // 调整索引继续查找 if (index >= (int)m_Results.size()) { index = 0; } attempts++; } // 所有结果都失效 m_Results.clear(); m_nCurrentIndex = -1; UpdateCountText(); } void CSearchBarDlg::UpdateCountText() { CString text; if (m_Results.empty()) { CString searchText; m_editSearch.GetWindowText(searchText); if (!searchText.IsEmpty()) { text = _T("Not found"); } } else { text.Format(_T("%d/%d"), m_nCurrentIndex + 1, (int)m_Results.size()); } m_staticCount.SetWindowText(text); m_staticCount.Invalidate(); m_staticCount.UpdateWindow(); } void CSearchBarDlg::OnBnClickedPrev() { GotoPrev(); } void CSearchBarDlg::OnBnClickedNext() { GotoNext(); } void CSearchBarDlg::OnBnClickedClose() { Hide(); }