Feature: Add log search bar with prev/next/clear for m_CList_Message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,7 @@
|
||||
#include "common/file_upload.h"
|
||||
#include "SplashDlg.h"
|
||||
#include "SearchBarDlg.h"
|
||||
#include "LogSearchBar.h"
|
||||
#include <ServerServiceWrapper.h>
|
||||
#include "CDlgFileSend.h"
|
||||
#include "CClientListDlg.h"
|
||||
@@ -689,6 +690,10 @@ CMy2015RemoteDlg::~CMy2015RemoteDlg()
|
||||
m_pSearchBar->DestroyWindow();
|
||||
SAFE_DELETE(m_pSearchBar);
|
||||
}
|
||||
if (m_pLogSearchBar) {
|
||||
m_pLogSearchBar->DestroyWindow();
|
||||
SAFE_DELETE(m_pLogSearchBar);
|
||||
}
|
||||
}
|
||||
|
||||
// DLL 请求限流成员函数实现 (根据配置限制请求频率)
|
||||
@@ -932,6 +937,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
||||
ON_COMMAND(ID_MSGLOG_DELETE, &CMy2015RemoteDlg::OnMsglogDelete)
|
||||
ON_COMMAND(ID_MSGLOG_COPY, &CMy2015RemoteDlg::OnMsglogCopy)
|
||||
ON_COMMAND(ID_MSGLOG_CLEAR, &CMy2015RemoteDlg::OnMsglogClear)
|
||||
ON_COMMAND(ID_MSGLOG_SEARCH, &CMy2015RemoteDlg::OnMsglogSearch)
|
||||
ON_COMMAND(ID_ONLINE_ADD_WATCH, &CMy2015RemoteDlg::OnOnlineAddWatch)
|
||||
ON_COMMAND(ID_ONLINE_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnOnlineLoginNotify)
|
||||
ON_NOTIFY(NM_CUSTOMDRAW, IDC_ONLINE, &CMy2015RemoteDlg::OnNMCustomdrawOnline)
|
||||
@@ -1429,6 +1435,9 @@ VOID CMy2015RemoteDlg::InitControl()
|
||||
m_nSplitPos = max(60, min(m_nSplitPos, 600));
|
||||
m_SplitterBar.Create(this);
|
||||
|
||||
m_pLogSearchBar = new CLogSearchBar();
|
||||
m_pLogSearchBar->Create(this, &m_CList_Message);
|
||||
|
||||
// 不在这里调 ApplyThumbnailSettings —— 调用方在 LoadThumbnailSettingsFromCfg
|
||||
// 之后统一 Apply(避免"先用默认值 Apply 一次,再读 INI 后再 Apply 一次"的双绘)。
|
||||
}
|
||||
@@ -1670,6 +1679,23 @@ LRESULT CMy2015RemoteDlg::OnShowMessage(WPARAM wParam, LPARAM lParam)
|
||||
// 消息日志最大条数,达到上限时删除最旧的记录
|
||||
#define MAX_MESSAGE_COUNT 1000
|
||||
|
||||
namespace {
|
||||
bool MsgHasKW(const CString& s, const CString* kw, int n) {
|
||||
for (int i = 0; i < n; i++)
|
||||
if (s.Find(kw[i]) >= 0) return true;
|
||||
return false;
|
||||
}
|
||||
int MsgColorLevel(const CString& type, const CString& msg) {
|
||||
const CString redKW[] = { _L("严禁"), _L("禁止") };
|
||||
const CString orangeKW[] = { _L("失败"), _L("警告") };
|
||||
const CString greenKW[] = { _L("成功"), _L("请") };
|
||||
if (MsgHasKW(type, redKW, _countof(redKW)) || MsgHasKW(msg, redKW, _countof(redKW))) return 1;
|
||||
if (MsgHasKW(type, orangeKW, _countof(orangeKW)) || MsgHasKW(msg, orangeKW, _countof(orangeKW))) return 2;
|
||||
if (MsgHasKW(msg, greenKW, _countof(greenKW))) return 3;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
VOID CMy2015RemoteDlg::ShowMessage(CString strType, CString strMsg)
|
||||
{
|
||||
AUTO_TICK(200, "");
|
||||
@@ -1682,9 +1708,10 @@ VOID CMy2015RemoteDlg::ShowMessage(CString strType, CString strMsg)
|
||||
m_CList_Message.DeleteItem(count - 1);
|
||||
}
|
||||
|
||||
m_CList_Message.InsertItem(0, strType); //向控件中设置数据
|
||||
m_CList_Message.InsertItem(0, strType);
|
||||
m_CList_Message.SetItemText(0, 1, strTime);
|
||||
m_CList_Message.SetItemText(0, 2, strMsg);
|
||||
m_CList_Message.SetItemData(0, (DWORD_PTR)MsgColorLevel(strType, strMsg));
|
||||
|
||||
CString strStatusMsg;
|
||||
|
||||
@@ -1759,9 +1786,12 @@ LRESULT CMy2015RemoteDlg::OnShowErrMessage(WPARAM wParam, LPARAM lParam)
|
||||
m_CList_Message.DeleteItem(count - 1);
|
||||
}
|
||||
|
||||
m_CList_Message.InsertItem(0, title ? _L(*title) : _TR("操作错误"));
|
||||
CString strTitle = title ? _L(*title) : _TR("操作错误");
|
||||
CString strText = text ? _L(*text) : _TR("内部错误");
|
||||
m_CList_Message.InsertItem(0, strTitle);
|
||||
m_CList_Message.SetItemText(0, 1, strTime);
|
||||
m_CList_Message.SetItemText(0, 2, text ? _L(*text) : _TR("内部错误"));
|
||||
m_CList_Message.SetItemText(0, 2, strText);
|
||||
m_CList_Message.SetItemData(0, (DWORD_PTR)MsgColorLevel(strTitle, strText));
|
||||
if(title)delete title;
|
||||
if(text)delete text;
|
||||
|
||||
@@ -3226,6 +3256,7 @@ void CMy2015RemoteDlg::OnSize(UINT nType, int cx, int cy)
|
||||
|
||||
BOOL hideLog = THIS_CFG.GetInt("settings", "HideMsg", 0) == 1;
|
||||
const int SPLITTER_H = 6;
|
||||
const int LOG_SEARCH_H = 26; // 日志搜索条高度
|
||||
// 日志区有效高度 = m_nSplitPos(不含分割条),分割条紧贴日志区上方
|
||||
int splitPos = hideLog ? 0 : m_nSplitPos;
|
||||
|
||||
@@ -3252,6 +3283,22 @@ void CMy2015RemoteDlg::OnSize(UINT nType, int cx, int cy)
|
||||
}
|
||||
LeaveCriticalSection(&m_cs);
|
||||
|
||||
if (m_pLogSearchBar && m_pLogSearchBar->GetSafeHwnd()) {
|
||||
if (hideLog || !m_bLogSearchVisible) {
|
||||
m_pLogSearchBar->ShowWindow(SW_HIDE);
|
||||
} else {
|
||||
m_pLogSearchBar->ShowWindow(SW_SHOW);
|
||||
// 右对齐,最多 380px;左侧留出 180px 给分组信息
|
||||
int barW = min(260, max(60, cx - 182));
|
||||
int barX = cx - 2 - barW;
|
||||
// 叠在在线列表底部右角:紧贴分割条上方,向上偏移一个条高
|
||||
int barY = cy - 20 - splitPos - SPLITTER_H - LOG_SEARCH_H + 5;
|
||||
m_pLogSearchBar->MoveWindow(barX, barY, barW, LOG_SEARCH_H);
|
||||
// 保证搜索条在列表控件上层(BringWindowToTop 仅影响 Z-order,不影响焦点)
|
||||
m_pLogSearchBar->BringWindowToTop();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_SplitterBar.m_hWnd != NULL) {
|
||||
if (hideLog) {
|
||||
m_SplitterBar.ShowWindow(SW_HIDE);
|
||||
@@ -9196,11 +9243,41 @@ void CMy2015RemoteDlg::OnNMCustomdrawMessage(NMHDR* pNMHDR, LRESULT* pResult)
|
||||
|
||||
case CDDS_ITEMPREPAINT: {
|
||||
int nRow = static_cast<int>(pLVCD->nmcd.dwItemSpec);
|
||||
int nLastRow = m_CList_Message.GetItemCount() - 1;
|
||||
if (nRow == nLastRow && nLastRow >= 0) {
|
||||
pLVCD->clrText = RGB(255, 0, 0);
|
||||
|
||||
// 搜索命中行深蓝高亮(不依赖 CDIS_SELECTED,直接读搜索栏当前命中行号)
|
||||
if (m_pLogSearchBar) {
|
||||
int nHit = m_pLogSearchBar->GetHighlightedRow();
|
||||
if (nHit >= 0 && nRow == nHit) {
|
||||
pLVCD->clrTextBk = RGB(0, 80, 180);
|
||||
pLVCD->clrText = RGB(255, 255, 255);
|
||||
*pResult = CDRF_NEWFONT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 关键字着色:必须同时设 clrText + clrTextBk,否则系统预填的蓝色会泄漏
|
||||
int colorLevel = (int)m_CList_Message.GetItemData(nRow);
|
||||
COLORREF bkDefault = ::GetSysColor(COLOR_WINDOW);
|
||||
if (colorLevel == 1) {
|
||||
pLVCD->clrText = RGB(220, 0, 0);
|
||||
pLVCD->clrTextBk = bkDefault;
|
||||
*pResult = CDRF_NEWFONT;
|
||||
return;
|
||||
}
|
||||
if (colorLevel == 2) {
|
||||
pLVCD->clrText = RGB(200, 100, 0);
|
||||
pLVCD->clrTextBk = bkDefault;
|
||||
*pResult = CDRF_NEWFONT;
|
||||
return;
|
||||
}
|
||||
if (colorLevel == 3) {
|
||||
pLVCD->clrText = RGB(0, 140, 0);
|
||||
pLVCD->clrTextBk = bkDefault;
|
||||
*pResult = CDRF_NEWFONT;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9213,6 +9290,9 @@ void CMy2015RemoteDlg::OnRClickMessage(NMHDR* pNMHDR, LRESULT* pResult)
|
||||
menu.AppendMenu(MF_STRING, ID_MSGLOG_DELETE, _TR("删除选中"));
|
||||
menu.AppendMenu(MF_STRING, ID_MSGLOG_COPY, _TR("复制选中"));
|
||||
menu.AppendMenu(MF_STRING, ID_MSGLOG_CLEAR, _TR("清空日志"));
|
||||
menu.AppendMenu(MF_SEPARATOR);
|
||||
menu.AppendMenu(MF_STRING, ID_MSGLOG_SEARCH,
|
||||
m_bLogSearchVisible ? _TR("隐藏搜索") : _TR("搜索日志"));
|
||||
|
||||
// 没有选中项时禁用"删除选中"
|
||||
if (m_CList_Message.GetSelectedCount() == 0) {
|
||||
@@ -9237,7 +9317,7 @@ void CMy2015RemoteDlg::OnMsglogDelete()
|
||||
while (pos) {
|
||||
selected.push_back(m_CList_Message.GetNextSelectedItem(pos));
|
||||
}
|
||||
// 倒序删除
|
||||
// 倒序删除,避免索引变化
|
||||
for (auto it = selected.rbegin(); it != selected.rend(); ++it) {
|
||||
m_CList_Message.DeleteItem(*it);
|
||||
}
|
||||
@@ -9280,6 +9360,14 @@ void CMy2015RemoteDlg::OnMsglogClear()
|
||||
m_CList_Message.DeleteAllItems();
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnMsglogSearch()
|
||||
{
|
||||
m_bLogSearchVisible = !m_bLogSearchVisible;
|
||||
// 复用 OnSize 里的布局逻辑
|
||||
CRect rc;
|
||||
GetClientRect(&rc);
|
||||
OnSize(SIZE_RESTORED, rc.Width(), rc.Height());
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnOnlineAddWatch()
|
||||
{
|
||||
|
||||
@@ -75,6 +75,7 @@ class CSplashDlg; // 前向声明
|
||||
class CClientListDlg;
|
||||
class CLicenseDlg;
|
||||
class CSearchBarDlg;
|
||||
class CLogSearchBar;
|
||||
|
||||
#include "pwd_gen.h"
|
||||
|
||||
@@ -140,7 +141,9 @@ public:
|
||||
_ClientList *m_ClientMap = nullptr;
|
||||
CClientListDlg* m_pClientListDlg = nullptr;
|
||||
CLicenseDlg* m_pLicenseDlg = nullptr;
|
||||
CSearchBarDlg* m_pSearchBar = nullptr; // 搜索工具栏
|
||||
CSearchBarDlg* m_pSearchBar = nullptr;
|
||||
CLogSearchBar* m_pLogSearchBar = nullptr;
|
||||
bool m_bLogSearchVisible = false; // 日志搜索条是否显示(右键菜单切换)
|
||||
BOOL m_bEnableFileV2 = FALSE; // V2 文件传输开关
|
||||
|
||||
// 构造
|
||||
@@ -560,6 +563,7 @@ public:
|
||||
afx_msg void OnMsglogDelete();
|
||||
afx_msg void OnMsglogCopy();
|
||||
afx_msg void OnMsglogClear();
|
||||
afx_msg void OnMsglogSearch();
|
||||
afx_msg void OnOnlineAddWatch();
|
||||
afx_msg void OnNMCustomdrawOnline(NMHDR* pNMHDR, LRESULT* pResult);
|
||||
afx_msg void OnOnlineRunAsAdmin();
|
||||
|
||||
@@ -291,6 +291,7 @@
|
||||
<ClInclude Include="BuildDlg.h" />
|
||||
<ClInclude Include="CClientListDlg.h" />
|
||||
<ClInclude Include="CClientLog.h" />
|
||||
<ClInclude Include="LogSearchBar.h" />
|
||||
<ClInclude Include="CDlgFileSend.h" />
|
||||
<ClInclude Include="CDrawingBoard.h" />
|
||||
<ClInclude Include="CGridDialog.h" />
|
||||
@@ -373,6 +374,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\client\Audio.cpp" />
|
||||
<ClCompile Include="CClientLog.cpp" />
|
||||
<ClCompile Include="LogSearchBar.cpp" />
|
||||
<ClCompile Include="msvc_compat.c">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||
|
||||
229
server/2015Remote/LogSearchBar.cpp
Normal file
229
server/2015Remote/LogSearchBar.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
// LogSearchBar.cpp - 日志列表搜索条实现
|
||||
#include "stdafx.h"
|
||||
#include "LogSearchBar.h"
|
||||
#include "LangManager.h"
|
||||
|
||||
BEGIN_MESSAGE_MAP(CLogSearchBar, CWnd)
|
||||
ON_WM_CREATE()
|
||||
ON_WM_SIZE()
|
||||
ON_WM_ERASEBKGND()
|
||||
ON_WM_TIMER()
|
||||
ON_EN_CHANGE(IDC_LOG_SEARCH_EDIT, OnEnChangeSearch)
|
||||
ON_BN_CLICKED(IDC_LOG_SEARCH_PREV, OnBnClickedPrev)
|
||||
ON_BN_CLICKED(IDC_LOG_SEARCH_NEXT, OnBnClickedNext)
|
||||
ON_BN_CLICKED(IDC_LOG_SEARCH_CLEAR, OnBnClickedClear)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
CLogSearchBar::CLogSearchBar()
|
||||
{
|
||||
}
|
||||
|
||||
CLogSearchBar::~CLogSearchBar()
|
||||
{
|
||||
}
|
||||
|
||||
BOOL CLogSearchBar::Create(CWnd* pParent, CListCtrl* pList)
|
||||
{
|
||||
m_pList = pList;
|
||||
LPCTSTR cls = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
|
||||
::LoadCursor(NULL, IDC_ARROW));
|
||||
return CWnd::Create(cls, _T(""), WS_CHILD | WS_CLIPCHILDREN,
|
||||
CRect(0, 0, 1, 1), pParent, 0);
|
||||
}
|
||||
|
||||
int CLogSearchBar::OnCreate(LPCREATESTRUCT lpcs)
|
||||
{
|
||||
if (CWnd::OnCreate(lpcs) == -1) return -1;
|
||||
|
||||
// 搜索输入框
|
||||
m_editSearch.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
|
||||
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_EDIT);
|
||||
m_editSearch.SetCueBanner(L"搜索日志...", TRUE);
|
||||
|
||||
// 清除按钮
|
||||
m_btnClear.Create(_T("X"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT,
|
||||
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_CLEAR);
|
||||
|
||||
// 上一个 / 下一个按钮(系统样式,与浅色背景融合)
|
||||
m_btnPrev.Create(_T("▲"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT,
|
||||
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_PREV);
|
||||
|
||||
m_btnNext.Create(_T("▼"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT,
|
||||
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_NEXT);
|
||||
|
||||
// 结果计数
|
||||
m_staticCount.Create(_T(""), WS_CHILD | WS_VISIBLE | SS_CENTER | SS_CENTERIMAGE,
|
||||
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_COUNT);
|
||||
|
||||
// 统一字体:跟父窗口(主对话框)保持一致
|
||||
CFont* pFont = GetParent()->GetFont();
|
||||
if (pFont) {
|
||||
m_staticCount.SetFont(pFont);
|
||||
m_editSearch.SetFont(pFont);
|
||||
m_btnClear.SetFont(pFont);
|
||||
m_btnPrev.SetFont(pFont);
|
||||
m_btnNext.SetFont(pFont);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CLogSearchBar::LayoutControls(int cx, int cy)
|
||||
{
|
||||
if (!m_editSearch.GetSafeHwnd()) return;
|
||||
const int m = 2;
|
||||
const int h = cy - 2 * m;
|
||||
if (h <= 0) return;
|
||||
const int btnW = h;
|
||||
const int cntW = 62;
|
||||
// 搜索框填满剩余空间(barW 由外部控制总宽,这里保证控件铺满)
|
||||
const int editW = max(20, cx - 2 * m - 3 * btnW - 2 * m - cntW);
|
||||
|
||||
int x = m;
|
||||
m_staticCount.MoveWindow(x, m, cntW, h);
|
||||
x += cntW + m;
|
||||
m_editSearch.MoveWindow(x, m, editW, h);
|
||||
x += editW + m;
|
||||
m_btnClear.MoveWindow(x, m, btnW, h);
|
||||
x += btnW;
|
||||
m_btnPrev.MoveWindow(x, m, btnW, h);
|
||||
x += btnW;
|
||||
m_btnNext.MoveWindow(x, m, btnW, h);
|
||||
}
|
||||
|
||||
void CLogSearchBar::OnSize(UINT nType, int cx, int cy)
|
||||
{
|
||||
CWnd::OnSize(nType, cx, cy);
|
||||
LayoutControls(cx, cy);
|
||||
}
|
||||
|
||||
BOOL CLogSearchBar::OnEraseBkgnd(CDC* pDC)
|
||||
{
|
||||
CRect rc;
|
||||
GetClientRect(&rc);
|
||||
pDC->FillSolidRect(rc, ::GetSysColor(COLOR_3DFACE));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CLogSearchBar::OnEnChangeSearch()
|
||||
{
|
||||
KillTimer(TIMER_SEARCH);
|
||||
SetTimer(TIMER_SEARCH, SEARCH_DELAY_MS, NULL);
|
||||
}
|
||||
|
||||
void CLogSearchBar::OnTimer(UINT_PTR nIDEvent)
|
||||
{
|
||||
if (nIDEvent == TIMER_SEARCH) {
|
||||
KillTimer(TIMER_SEARCH);
|
||||
DoSearch();
|
||||
}
|
||||
CWnd::OnTimer(nIDEvent);
|
||||
}
|
||||
|
||||
void CLogSearchBar::DoSearch()
|
||||
{
|
||||
if (!m_pList || !m_pList->GetSafeHwnd()) return;
|
||||
|
||||
CString kw;
|
||||
m_editSearch.GetWindowText(kw);
|
||||
|
||||
m_Results.clear();
|
||||
m_nCurrentIndex = -1;
|
||||
|
||||
if (kw.IsEmpty()) {
|
||||
UpdateCountText();
|
||||
return;
|
||||
}
|
||||
|
||||
CString kwLow = kw;
|
||||
kwLow.MakeLower();
|
||||
|
||||
int nCols = m_pList->GetHeaderCtrl() ? m_pList->GetHeaderCtrl()->GetItemCount() : 1;
|
||||
int nItems = m_pList->GetItemCount();
|
||||
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
for (int c = 0; c < nCols; c++) {
|
||||
CString cell = m_pList->GetItemText(i, c);
|
||||
cell.MakeLower();
|
||||
if (cell.Find(kwLow) >= 0) {
|
||||
m_Results.push_back(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_Results.empty()) {
|
||||
m_nCurrentIndex = 0;
|
||||
GotoMatch(m_nCurrentIndex);
|
||||
}
|
||||
UpdateCountText();
|
||||
}
|
||||
|
||||
void CLogSearchBar::GotoMatch(int idx)
|
||||
{
|
||||
if (!m_pList || idx < 0 || idx >= (int)m_Results.size()) return;
|
||||
int item = m_Results[idx];
|
||||
|
||||
// 取消所有选中
|
||||
int n = -1;
|
||||
while ((n = m_pList->GetNextItem(-1, LVNI_SELECTED)) != -1)
|
||||
m_pList->SetItemState(n, 0, LVIS_SELECTED | LVIS_FOCUSED);
|
||||
|
||||
m_pList->SetItemState(item, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
|
||||
m_pList->EnsureVisible(item, FALSE);
|
||||
}
|
||||
|
||||
void CLogSearchBar::UpdateCountText()
|
||||
{
|
||||
CString text;
|
||||
if (m_Results.empty()) {
|
||||
CString kw;
|
||||
m_editSearch.GetWindowText(kw);
|
||||
text = kw.IsEmpty() ? _TR("") : _TR("无结果");
|
||||
} else {
|
||||
text.Format(_T("%d/%d"), m_nCurrentIndex + 1, (int)m_Results.size());
|
||||
}
|
||||
m_staticCount.SetWindowText(text);
|
||||
}
|
||||
|
||||
void CLogSearchBar::InvalidateSearch()
|
||||
{
|
||||
KillTimer(TIMER_SEARCH);
|
||||
m_Results.clear();
|
||||
m_nCurrentIndex = -1;
|
||||
UpdateCountText();
|
||||
|
||||
if (m_editSearch.GetSafeHwnd()) {
|
||||
CString kw;
|
||||
m_editSearch.GetWindowText(kw);
|
||||
if (!kw.IsEmpty())
|
||||
SetTimer(TIMER_SEARCH, SEARCH_DELAY_MS, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void CLogSearchBar::OnBnClickedPrev()
|
||||
{
|
||||
if (m_Results.empty()) return;
|
||||
m_nCurrentIndex--;
|
||||
if (m_nCurrentIndex < 0)
|
||||
m_nCurrentIndex = (int)m_Results.size() - 1;
|
||||
GotoMatch(m_nCurrentIndex);
|
||||
UpdateCountText();
|
||||
}
|
||||
|
||||
void CLogSearchBar::OnBnClickedNext()
|
||||
{
|
||||
if (m_Results.empty()) return;
|
||||
m_nCurrentIndex++;
|
||||
if (m_nCurrentIndex >= (int)m_Results.size())
|
||||
m_nCurrentIndex = 0;
|
||||
GotoMatch(m_nCurrentIndex);
|
||||
UpdateCountText();
|
||||
}
|
||||
|
||||
void CLogSearchBar::OnBnClickedClear()
|
||||
{
|
||||
m_editSearch.SetWindowText(_T(""));
|
||||
m_editSearch.SetFocus();
|
||||
// 触发 EN_CHANGE → 定时器 → DoSearch 清空结果
|
||||
}
|
||||
57
server/2015Remote/LogSearchBar.h
Normal file
57
server/2015Remote/LogSearchBar.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// LogSearchBar.h - 日志列表搜索条(嵌入式,固定在右侧)
|
||||
#pragma once
|
||||
#include <afxwin.h>
|
||||
#include <afxcmn.h>
|
||||
#include <vector>
|
||||
|
||||
#define IDC_LOG_SEARCH_EDIT 5001
|
||||
#define IDC_LOG_SEARCH_PREV 5002
|
||||
#define IDC_LOG_SEARCH_NEXT 5003
|
||||
#define IDC_LOG_SEARCH_COUNT 5004
|
||||
#define IDC_LOG_SEARCH_CLEAR 5005
|
||||
|
||||
class CLogSearchBar : public CWnd
|
||||
{
|
||||
public:
|
||||
CLogSearchBar();
|
||||
virtual ~CLogSearchBar();
|
||||
|
||||
// pParent: 主对话框; pList: 要搜索的 CListCtrl(m_CList_Message)
|
||||
BOOL Create(CWnd* pParent, CListCtrl* pList);
|
||||
void InvalidateSearch();
|
||||
|
||||
// 当前命中行的列表索引,无命中返回 -1(纯内存读)
|
||||
int GetHighlightedRow() const {
|
||||
if (m_nCurrentIndex < 0 || m_nCurrentIndex >= (int)m_Results.size()) return -1;
|
||||
return m_Results[m_nCurrentIndex];
|
||||
}
|
||||
|
||||
protected:
|
||||
CListCtrl* m_pList = nullptr;
|
||||
CEdit m_editSearch;
|
||||
CButton m_btnPrev;
|
||||
CButton m_btnNext;
|
||||
CButton m_btnClear;
|
||||
CStatic m_staticCount;
|
||||
|
||||
std::vector<int> m_Results; // 匹配行索引
|
||||
int m_nCurrentIndex = -1;
|
||||
|
||||
void DoSearch();
|
||||
void GotoMatch(int idx);
|
||||
void UpdateCountText();
|
||||
void LayoutControls(int cx, int cy);
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
afx_msg int OnCreate(LPCREATESTRUCT lpcs);
|
||||
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
|
||||
afx_msg void OnTimer(UINT_PTR nIDEvent);
|
||||
afx_msg void OnEnChangeSearch();
|
||||
afx_msg void OnBnClickedPrev();
|
||||
afx_msg void OnBnClickedNext();
|
||||
afx_msg void OnBnClickedClear();
|
||||
|
||||
static const UINT TIMER_SEARCH = 1;
|
||||
static const UINT SEARCH_DELAY_MS = 400;
|
||||
};
|
||||
@@ -1958,3 +1958,10 @@ FRPC Զ
|
||||
客户端日志=Client Log
|
||||
封禁使用=Forbidden Client
|
||||
确定封禁选定的被控程序吗?=Are you sure to forbidden the selected clients?
|
||||
无结果=Not found
|
||||
严禁=Prohibited
|
||||
禁止=Forbidden
|
||||
失败=Failed
|
||||
搜索日志=Search Logs
|
||||
隐藏搜索=Hide Search
|
||||
请=Please
|
||||
|
||||
@@ -1949,3 +1949,10 @@ FRPC Զ
|
||||
客户端日志=客户端日志
|
||||
封禁使用=封禁使用
|
||||
确定封禁选定的被控程序吗?=确定封禁选定的被控程序吗?
|
||||
无结果=无结果
|
||||
严禁=严禁
|
||||
禁止=禁止
|
||||
失败=失败
|
||||
搜索日志=搜索日志
|
||||
隐藏搜索=隐藏搜索
|
||||
请=请
|
||||
|
||||
@@ -990,6 +990,7 @@
|
||||
#define ID_MSGLOG_CLEAR 33041
|
||||
#define ID_CANCEL_SHARE 33042
|
||||
#define ID_MSGLOG_COPY 33043
|
||||
#define ID_MSGLOG_SEARCH 33078
|
||||
#define ID_WEB_REMOTE_CONTROL 33044
|
||||
#define ID_TOOL_PLUGIN_SETTINGS 33045
|
||||
#define ID_33046 33046
|
||||
@@ -1031,7 +1032,7 @@
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 398
|
||||
#define _APS_NEXT_COMMAND_VALUE 33077
|
||||
#define _APS_NEXT_COMMAND_VALUE 33079
|
||||
#define _APS_NEXT_CONTROL_VALUE 2542
|
||||
#define _APS_NEXT_SYMED_VALUE 105
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user