/*
* $Header :$
*
* $History: logviewerView.cpp $
*
* ***************** Version 3 *****************
* User: Administrator Date: 12/02/03 Time: 9:20p
* Updated in $/logger/logviewer
* Added pretty icons, version resource, tooltip support, fixed various
* bugs in continuation record support.
*
* ***************** Version 2 *****************
* User: Administrator Date: 11/27/03 Time: 3:17p
* Updated in $/logger/logviewer
* Revamped the LogUpdateThread to post messages to the main thread for
* dealing with the listcontrol.
*
* ***************** Version 1 *****************
* User: Administrator Date: 11/25/03 Time: 5:20p
* Created in $/logger/logviewer
*/
#include "stdafx.h"
#include <atlconv.h>
#include "logviewer.h"
#include "LogData.h"
#include "MainFrm.h"
#include "logviewerDoc.h"
#include "logviewerView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CLogViewerView
IMPLEMENT_DYNCREATE(CLogViewerView, CListView)
BEGIN_MESSAGE_MAP(CLogViewerView, CListView)
//{{AFX_MSG_MAP(CLogViewerView)
ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetDispInfo)
ON_COMMAND(IDC_ERRORS, OnErrors)
ON_COMMAND(IDC_INFORMATION, OnInformation)
ON_COMMAND(IDC_SUCCESS, OnSuccess)
ON_COMMAND(IDC_WARNINGS, OnWarnings)
ON_UPDATE_COMMAND_UI(IDC_ERRORS, OnUpdateErrors)
ON_UPDATE_COMMAND_UI(IDC_INFORMATION, OnUpdateInformation)
ON_UPDATE_COMMAND_UI(IDC_SUCCESS, OnUpdateSuccess)
ON_UPDATE_COMMAND_UI(IDC_WARNINGS, OnUpdateWarnings)
ON_COMMAND(IDC_CLEARSELECTION, OnClearSelection)
ON_UPDATE_COMMAND_UI(IDC_CLEARSELECTION, OnUpdateClearSelection)
ON_COMMAND(IDC_CLEAR, OnClear)
ON_UPDATE_COMMAND_UI(IDC_CLEAR, OnUpdateClear)
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
ON_REGISTERED_MESSAGE(guiAdvise, OnAdvise)
ON_CBN_SELCHANGE(IDC_MODULECOMBO, OnSelChange)
ON_CBN_SELCHANGE(IDC_THREADCOMBO, OnSelChange)
ON_CBN_SELCHANGE(IDC_MACHINECOMBO, OnSelChange)
ON_NOTIFY_RANGE(TTN_NEEDTEXTW, 0, 0xffff, OnToolTipText)
ON_NOTIFY_RANGE(TTN_NEEDTEXTA, 0, 0xffff, OnToolTipText)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CLogViewerView construction/destruction
CLogViewerView::CLogViewerView() : CListView()
{
m_bIsActiveWindow = FALSE;
m_lastTip = (CLogData *) NULL;
memset(m_tipText, 0, sizeof(m_tipText));
m_logFileInstance = (CLogFileInstance *) NULL;
}
CLogViewerView::~CLogViewerView()
{
delete m_logFileInstance;
}
BOOL CLogViewerView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style |= LVS_REPORT | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP;
m_tip.Create(this, TTS_ALWAYSTIP | TTS_BALLOON | TTS_NOPREFIX);
m_tip.SetMaxTipWidth(400);
return CListView::PreCreateWindow(cs);
}
BOOL CLogViewerView::OpenDocument(LPCTSTR szFileName)
{
ASSERT(szFileName);
ASSERT(AfxIsValidString(szFileName));
delete m_logFileInstance;
m_logFileInstance = new CLogFileInstance(this);
ASSERT(m_logFileInstance);
ASSERT_KINDOF(CLogFileInstance, m_logFileInstance);
return m_logFileInstance->Open(szFileName);
}
/////////////////////////////////////////////////////////////////////////////
// CLogViewerView drawing
void CLogViewerView::OnDraw(CDC* /*pDC*/)
{
CLogViewerDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
}
void CLogViewerView::OnInitialUpdate()
{
// We don't call the base class OnInitialUpdate because all it
// does is call our OnUpdate function. No point, we do nothing there....
// CListView::OnInitialUpdate();
CRect rect;
CListCtrl& ctl = GetListCtrl();
ctl.GetClientRect(&rect);
ctl.InsertColumn(LOGVIEWER::COLUMN_IMAGE, _T(""), LVCFMT_LEFT, 20, LOGVIEWER::COLUMN_IMAGE);
ctl.InsertColumn(LOGVIEWER::COLUMN_DATE, _T("Timestamp"), LVCFMT_LEFT, 100, LOGVIEWER::COLUMN_DATE);
ctl.InsertColumn(LOGVIEWER::COLUMN_SEQUENCE, _T("Sequence"), LVCFMT_RIGHT, 100, LOGVIEWER::COLUMN_SEQUENCE);
ctl.InsertColumn(LOGVIEWER::COLUMN_CODE, _T("Code"), LVCFMT_RIGHT, 100, LOGVIEWER::COLUMN_CODE);
ctl.InsertColumn(LOGVIEWER::COLUMN_MODULE, _T("Module"), LVCFMT_LEFT, 100, LOGVIEWER::COLUMN_MODULE);
ctl.InsertColumn(LOGVIEWER::COLUMN_THREAD, _T("Thread"), LVCFMT_RIGHT, 100, LOGVIEWER::COLUMN_THREAD);
ctl.InsertColumn(LOGVIEWER::COLUMN_MACHINE, _T("Machine"), LVCFMT_LEFT, 100, LOGVIEWER::COLUMN_MACHINE);
ctl.InsertColumn(LOGVIEWER::COLUMN_MESSAGE, _T("Message"), LVCFMT_LEFT, rect.Width() - 600, LOGVIEWER::COLUMN_MESSAGE);
ctl.SetExtendedStyle(ctl.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_GRIDLINES | LVS_EX_INFOTIP);
ctl.SetImageList(MAINFRAME->GetImageList(), LVSIL_SMALL);
}
/////////////////////////////////////////////////////////////////////////////
// CLogViewerView diagnostics
#ifdef _DEBUG
void CLogViewerView::AssertValid() const
{
CListView::AssertValid();
}
void CLogViewerView::Dump(CDumpContext& dc) const
{
CListView::Dump(dc);
}
CLogViewerDoc* CLogViewerView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CLogViewerDoc)));
return (CLogViewerDoc *) m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CLogViewerView message handlers
void CLogViewerView::OnUpdate(CView* /*pSender*/, LPARAM /*lHint*/, CObject* /*pHint*/)
{
TRACE("CLogViewerView::OnUpdate\n");
}
void CLogViewerView::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO *pDispInfo = (LV_DISPINFO *) pNMHDR;
LV_ITEM *pItem = &(pDispInfo)->item;
if (pItem->mask & LVIF_TEXT)
{
CLogText *pLog = (CLogText *) pItem->lParam;
ASSERT(pLog);
ASSERT_KINDOF(CLogText, pLog);
pLog->RenderText(*pItem, m_logFileInstance->m_logFilterData);
}
*pResult = 0;
}
void CLogViewerView::OnErrors()
{
if (m_logFileInstance->m_logFilterData.m_iLevel & LOGVIEWER::ERRORS)
m_logFileInstance->m_logFilterData.m_iLevel &= ~LOGVIEWER::ERRORS;
else
m_logFileInstance->m_logFilterData.m_iLevel |= LOGVIEWER::ERRORS;
GetListCtrl().DeleteAllItems();
m_logFileInstance->Reload();
}
void CLogViewerView::OnInformation()
{
if (m_logFileInstance->m_logFilterData.m_iLevel & LOGVIEWER::INFORMATION)
m_logFileInstance->m_logFilterData.m_iLevel &= ~LOGVIEWER::INFORMATION;
else
m_logFileInstance->m_logFilterData.m_iLevel |= LOGVIEWER::INFORMATION;
GetListCtrl().DeleteAllItems();
m_logFileInstance->Reload();
}
void CLogViewerView::OnSuccess()
{
if (m_logFileInstance->m_logFilterData.m_iLevel & LOGVIEWER::SUCCESS)
m_logFileInstance->m_logFilterData.m_iLevel &= ~LOGVIEWER::SUCCESS;
else
m_logFileInstance->m_logFilterData.m_iLevel |= LOGVIEWER::SUCCESS;
GetListCtrl().DeleteAllItems();
m_logFileInstance->Reload();
}
void CLogViewerView::OnWarnings()
{
if (m_logFileInstance->m_logFilterData.m_iLevel & LOGVIEWER::WARNINGS)
m_logFileInstance->m_logFilterData.m_iLevel &= ~LOGVIEWER::WARNINGS;
else
m_logFileInstance->m_logFilterData.m_iLevel |= LOGVIEWER::WARNINGS;
GetListCtrl().DeleteAllItems();
m_logFileInstance->Reload();
}
void CLogViewerView::OnUpdateErrors(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_logFileInstance->m_logFilterData.m_iLevel & LOGVIEWER::ERRORS ? TRUE : FALSE);
}
void CLogViewerView::OnUpdateInformation(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_logFileInstance->m_logFilterData.m_iLevel & LOGVIEWER::INFORMATION ? TRUE : FALSE);
}
void CLogViewerView::OnUpdateSuccess(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_logFileInstance->m_logFilterData.m_iLevel & LOGVIEWER::SUCCESS ? TRUE : FALSE);
}
void CLogViewerView::OnUpdateWarnings(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_logFileInstance->m_logFilterData.m_iLevel & LOGVIEWER::WARNINGS ? TRUE : FALSE);
}
void CLogViewerView::OnClearSelection()
{
CListCtrl& ctl = GetListCtrl();
POSITION pos = ctl.GetFirstSelectedItemPosition();
int index;
while (pos != POSITION(NULL))
{
index = ctl.GetNextSelectedItem(pos);
ctl.SetItemState(index, 0, LVIS_SELECTED);
}
}
void CLogViewerView::OnUpdateClearSelection(CCmdUI* pCmdUI)
{
pCmdUI->Enable(GetListCtrl().GetSelectedCount() ? TRUE : FALSE);
}
void CLogViewerView::OnClear()
{
CLogViewerDoc *pDoc = GetDocument();
ASSERT(pDoc);
ASSERT_KINDOF(CLogViewerDoc, pDoc);
if (AfxMessageBox(IDS_CLEARFILEWARNING, MB_YESNO) == IDYES)
m_logFileInstance->ClearLog();
}
void CLogViewerView::OnUpdateClear(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_logFileInstance->GetSize() ? TRUE : FALSE);
}
void CLogViewerView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
if (bActivate)
// We're being activated so now's the time to populate the
// filter combo boxes on the MainFrame dialog bar...
MAINFRAME->UpdateDlgBar(m_logFileInstance->m_logFilterData);
else
MAINFRAME->UpdateSelections(m_logFileInstance->m_logFilterData);
CListView::OnActivateView(bActivate, pActivateView, pDeactiveView);
m_bIsActiveWindow = bActivate;
}
LRESULT CLogViewerView::OnAdvise(WPARAM wp, LPARAM lp)
{
CListCtrl& ctl = GetListCtrl();
CLogText *pLog;
switch (wp)
{
case adviseAddItem:
pLog = (CLogText *) lp;
ASSERT(pLog);
ASSERT_KINDOF(CLogText, pLog);
if (pLog->IncludeThis(m_logFileInstance->m_logFilterData))
ctl.InsertItem(LVIF_IMAGE | LVIF_PARAM, INT_MAX, LPSTR_TEXTCALLBACK, 0, 0, pLog->Image(), LPARAM(pLog));
break;
case adviseEndUpdate:
if (ctl.GetSelectedCount() == 0)
ctl.EnsureVisible(ctl.GetItemCount() - 1, FALSE);
ctl.SetColumnWidth(LOGVIEWER::COLUMN_DATE, LVSCW_AUTOSIZE_USEHEADER);
ctl.SetColumnWidth(LOGVIEWER::COLUMN_SEQUENCE, LVSCW_AUTOSIZE_USEHEADER);
ctl.SetColumnWidth(LOGVIEWER::COLUMN_CODE, LVSCW_AUTOSIZE_USEHEADER);
ctl.SetColumnWidth(LOGVIEWER::COLUMN_MODULE, LVSCW_AUTOSIZE_USEHEADER);
ctl.SetColumnWidth(LOGVIEWER::COLUMN_THREAD, LVSCW_AUTOSIZE_USEHEADER);
ctl.SetColumnWidth(LOGVIEWER::COLUMN_MACHINE, LVSCW_AUTOSIZE_USEHEADER);
ctl.SetColumnWidth(LOGVIEWER::COLUMN_MESSAGE, LVSCW_AUTOSIZE_USEHEADER);
if (m_bIsActiveWindow)
MAINFRAME->UpdateDlgBar(m_logFileInstance->m_logFilterData);
break;
case adviseClearList:
m_logFileInstance->RemoveAll();
ctl.DeleteAllItems();
break;
}
return 0;
}
void CLogViewerView::OnSelChange()
{
MAINFRAME->UpdateFilter(m_logFileInstance->m_logFilterData);
GetListCtrl().DeleteAllItems();
m_logFileInstance->Reload();
}
CLogData *CLogViewerView::ItemFromPoint(CPoint& point) const
{
LVITEM item;
UINT flags;
CListCtrl& ctl = GetListCtrl();
memset(&item, 0, sizeof(item));
if ((item.iItem = ctl.HitTest(point, &flags)) != -1)
{
item.mask = LVIF_PARAM;
ctl.GetItem(&item);
return (CLogData *) item.lParam;
}
return (CLogData *) NULL;
}
BOOL CLogViewerView::OnToolTipText(UINT /*id*/, NMHDR * pNMHDR, LRESULT * pResult)
{
USES_CONVERSION;
// need to handle both ANSI and UNICODE versions of the message
TOOLTIPTEXTA *pTTTA = (TOOLTIPTEXTA *) pNMHDR;
TOOLTIPTEXTW *pTTTW = (TOOLTIPTEXTW *) pNMHDR;
CLogText *pLog;
CPoint lPoint;
if (pTTTA->hdr.idFrom == 0)
return TRUE;
GetCursorPos(&lPoint);
ScreenToClient(&lPoint);
if ((pLog = ItemFromPoint(lPoint)) != (CLogData *) NULL)
{
ASSERT_VALID(pLog);
ASSERT_KINDOF(CLogText, pLog);
pLog->FormatForTip(m_tipText, sizeof(m_tipText), m_logFileInstance->m_logFilterData);
if (pNMHDR->code == TTN_NEEDTEXTA)
pTTTA->lpszText = m_tipText;
else if (pNMHDR->code == TTN_NEEDTEXTW)
pTTTW->lpszText = A2W(m_tipText);
}
else
m_tip.Pop();
*pResult = 0;
return TRUE; // message was handled
}
BOOL CLogViewerView::PreTranslateMessage(MSG* pMsg)
{
m_tip.RelayEvent(pMsg);
return CListView::PreTranslateMessage(pMsg);
}
void CLogViewerView::OnMouseMove(UINT nFlags, CPoint point)
{
CLogData *pLog = ItemFromPoint(point);
CListView::OnMouseMove(nFlags, point);
if (pLog != m_lastTip || pLog == (CLogData *) NULL)
{
CRect rect;
GetClientRect(&rect);
m_tip.DelTool(this, 1);
m_tip.AddTool(this, LPSTR_TEXTCALLBACK, &rect, 1);
m_tip.Activate(TRUE);
m_tip.SetDelayTime(TTDT_AUTOPOP, INT_MAX);
m_lastTip = pLog;
}
}