552 lines
24 KiB
C++
552 lines
24 KiB
C++
// ==========================================================================
|
||
// Class Implementation
|
||
// COXHistoryCtrl
|
||
// ==========================================================================
|
||
|
||
// Version: 9.3
|
||
|
||
// This software along with its related components, documentation and files ("The Libraries")
|
||
// is ?1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
|
||
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
|
||
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
|
||
// to obtain this file, or directly from our office. For a copy of the license governing
|
||
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
|
||
|
||
// //////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
#include "stdafx.h"
|
||
#include "OXHistoryCtrl.h"
|
||
|
||
#include <afxdisp.h>
|
||
|
||
// default maximum number of history entries displayed in the control window
|
||
const int OXHISTCTRL_MAXENTRIES=100;
|
||
|
||
|
||
IMPLEMENT_DYNCREATE(COXHistoryCtrl, COXScrollWnd)
|
||
|
||
BEGIN_MESSAGE_MAP(COXHistoryCtrl, COXScrollWnd)
|
||
//{{AFX_MSG_MAP(COXHistoryCtrl)
|
||
ON_WM_PAINT()
|
||
ON_WM_ERASEBKGND()
|
||
//}}AFX_MSG_MAP
|
||
END_MESSAGE_MAP()
|
||
|
||
COXHistoryCtrl::COXHistoryCtrl()
|
||
{
|
||
//setup the critical section variable
|
||
InitializeCriticalSection(&m_criticalSection);
|
||
|
||
//logging variables
|
||
m_sLogFileName=_T("");
|
||
m_bEnableLog=FALSE;
|
||
m_bTimeStampedLog=FALSE;
|
||
m_logDay=0;
|
||
m_logMonth=0;
|
||
m_logYear=0;
|
||
m_bTruncateLogFile=FALSE;
|
||
|
||
//display properties
|
||
m_clrText=::GetSysColor(COLOR_WINDOWTEXT);
|
||
m_clrBack=::GetSysColor(COLOR_WINDOW);
|
||
m_clrLeftOverBack=::GetSysColor(COLOR_WINDOW);
|
||
m_nAlignment=DT_LEFT;
|
||
m_nOffset=0;
|
||
|
||
m_sizeSymbol=CSize(0,0);
|
||
|
||
//max number of entries
|
||
m_nMaxNumEntries=OXHISTCTRL_MAXENTRIES;
|
||
|
||
//max line length
|
||
m_nMaxLengthEntry=0;
|
||
|
||
//datestamp to string format
|
||
SetTimeStampFormat(_T("%X"));
|
||
}
|
||
|
||
COXHistoryCtrl::~COXHistoryCtrl()
|
||
{
|
||
CloseLog();
|
||
|
||
m_arrEntries.RemoveAll();
|
||
|
||
//release the critical section
|
||
DeleteCriticalSection(&m_criticalSection);
|
||
}
|
||
|
||
|
||
void COXHistoryCtrl::EnableLog(BOOL bYesNo)
|
||
{
|
||
m_bEnableLog=bYesNo;
|
||
if(m_bEnableLog)
|
||
OpenLog();
|
||
else
|
||
CloseLog();
|
||
}
|
||
|
||
|
||
BOOL COXHistoryCtrl::AddLine(CString string, BOOL bAddToLog/*=TRUE*/)
|
||
{
|
||
//enter into a critical section
|
||
EnterCriticalSection(&m_criticalSection);
|
||
|
||
//update the log file
|
||
if(bAddToLog && m_bEnableLog)
|
||
WriteToLog(string,TRUE);
|
||
|
||
BOOL bUpdateScroll=FALSE;
|
||
|
||
ASSERT(m_arrEntries.GetCount()<=GetMaxNumEntries());
|
||
|
||
HISTORYENTRY entry(string,m_clrText,m_clrBack,m_nOffset,m_nAlignment);
|
||
m_arrEntries.AddTail(entry);
|
||
|
||
if(m_arrEntries.GetCount()==GetMaxNumEntries()+1)
|
||
{
|
||
entry=m_arrEntries.RemoveHead();
|
||
ASSERT(entry.m_sText.GetLength()<=m_nMaxLengthEntry);
|
||
if(entry.m_sText.GetLength()==m_nMaxLengthEntry)
|
||
{
|
||
UpdateMaxLengthInfo();
|
||
bUpdateScroll=TRUE;
|
||
}
|
||
}
|
||
else
|
||
bUpdateScroll=TRUE;
|
||
|
||
int nLength=string.GetLength();
|
||
if(m_nMaxLengthEntry<nLength)
|
||
{
|
||
m_nMaxLengthEntry=nLength;
|
||
bUpdateScroll=TRUE;
|
||
}
|
||
|
||
if(bUpdateScroll)
|
||
UpdateScrollInfo();
|
||
|
||
ScrollToEnd();
|
||
RedrawWindow();
|
||
|
||
//exit the critical section
|
||
LeaveCriticalSection(&m_criticalSection);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL COXHistoryCtrl::AddStampedLine(CString string,
|
||
BOOL bAddToLog/*=TRUE*/)
|
||
{
|
||
CString sStampedString=GetTimeDateStamp()+_T(" ")+string;
|
||
BOOL bReturn=AddLine(sStampedString,bAddToLog);
|
||
|
||
return bReturn;
|
||
}
|
||
|
||
|
||
BOOL COXHistoryCtrl::AppendToLine(CString string,
|
||
BOOL bAddToLog/*=TRUE*/)
|
||
{
|
||
//enter into a critical section
|
||
EnterCriticalSection(&m_criticalSection);
|
||
|
||
if(m_arrEntries.IsEmpty())
|
||
return FALSE;
|
||
|
||
//update the log file
|
||
if(bAddToLog && m_bEnableLog)
|
||
WriteToLog(string,FALSE);
|
||
|
||
HISTORYENTRY entry=m_arrEntries.GetTail();
|
||
entry.m_sText+=string;
|
||
m_arrEntries.SetAt(m_arrEntries.GetTailPosition(),entry);
|
||
|
||
int nLength=entry.m_sText.GetLength();
|
||
if(m_nMaxLengthEntry<nLength)
|
||
{
|
||
m_nMaxLengthEntry=nLength;
|
||
UpdateScrollInfo();
|
||
}
|
||
|
||
ScrollToEnd();
|
||
RedrawWindow();
|
||
|
||
//exit the critical section
|
||
LeaveCriticalSection(&m_criticalSection);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL COXHistoryCtrl::ClearHistory()
|
||
{
|
||
//enter into a critical section
|
||
EnterCriticalSection(&m_criticalSection);
|
||
|
||
m_arrEntries.RemoveAll();
|
||
|
||
//max line length
|
||
m_nMaxLengthEntry=0;
|
||
|
||
UpdateScrollInfo();
|
||
RedrawWindow();
|
||
|
||
//exit the critical section
|
||
LeaveCriticalSection(&m_criticalSection);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
CString COXHistoryCtrl::GetTimeDateStamp() const
|
||
{
|
||
//get the time/date
|
||
COleDateTime time=COleDateTime::GetCurrentTime();
|
||
return time.Format(GetTimeStampFormat());
|
||
}
|
||
|
||
|
||
int COXHistoryCtrl::OpenLog()
|
||
{
|
||
CloseLog();
|
||
|
||
CString sFileName=m_sLogFileName;
|
||
if(m_bTimeStampedLog)
|
||
{
|
||
//get the time/date
|
||
COleDateTime date=COleDateTime::GetCurrentTime();
|
||
m_logDay=date.GetDay();
|
||
m_logMonth=date.GetMonth();
|
||
m_logYear=date.GetYear();
|
||
CString sStampedLogFileName;
|
||
sStampedLogFileName.Format(_T("%2.2d%2.2d%4.4d"),
|
||
m_logDay,m_logMonth,m_logYear);
|
||
|
||
//find the extension
|
||
int nPos=m_sLogFileName.Find(_T("."));
|
||
if(nPos!=-1)
|
||
sStampedLogFileName+=m_sLogFileName.Mid(nPos);
|
||
m_sTimeStampedLogFileName=sFileName=sStampedLogFileName;
|
||
}
|
||
|
||
BOOL bResult=m_fileLog.Open(sFileName,CFile::modeCreate|CFile::modeWrite|
|
||
(GetTruncateLogFile() ? 0 : CFile::modeNoTruncate));
|
||
if(bResult && !GetTruncateLogFile())
|
||
m_fileLog.SeekToEnd();
|
||
return bResult;
|
||
}
|
||
|
||
|
||
BOOL COXHistoryCtrl::CloseLog()
|
||
{
|
||
if(m_fileLog.m_hFile!=CFile::hFileNull)
|
||
{
|
||
TRY
|
||
{
|
||
m_fileLog.Close();
|
||
}
|
||
CATCH(CFileException,pException)
|
||
{
|
||
UNREFERENCED_PARAMETER(pException);
|
||
return FALSE;
|
||
}
|
||
END_CATCH
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
void COXHistoryCtrl::WriteToLog(CString string, BOOL bNewLine)
|
||
{
|
||
if(!m_bEnableLog)
|
||
return;
|
||
|
||
COleDateTime date=COleDateTime::GetCurrentTime();
|
||
//check to see if the date has changed
|
||
if(m_bTimeStampedLog)
|
||
{
|
||
if(m_logDay!=date.GetDay() || m_logMonth!=date.GetMonth() ||
|
||
m_logYear!=date.GetYear())
|
||
{
|
||
OpenLog();
|
||
}
|
||
else if(m_logDay==date.GetDay() || m_logMonth!=date.GetMonth() ||
|
||
m_logYear!=date.GetYear())
|
||
{
|
||
if(bNewLine && m_fileLog.m_hFile != CFile::hFileNull)
|
||
m_fileLog.Write(_T("\r\n"),2*sizeof(TCHAR));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if(m_fileLog.m_hFile == CFile::hFileNull)
|
||
{
|
||
OpenLog();
|
||
}
|
||
else
|
||
{
|
||
if(bNewLine && m_fileLog.m_hFile != CFile::hFileNull)
|
||
m_fileLog.Write(_T("\r\n"),2*sizeof(TCHAR));
|
||
}
|
||
}
|
||
|
||
if(m_fileLog.m_hFile != CFile::hFileNull)
|
||
m_fileLog.Write(string,string.GetLength()*sizeof(TCHAR));
|
||
}
|
||
|
||
|
||
void COXHistoryCtrl::SetMaxNumEntries(int nMaxNum)
|
||
{
|
||
if(nMaxNum<0)
|
||
return;
|
||
|
||
BOOL bUpdateScroll=FALSE;
|
||
|
||
if(nMaxNum<GetMaxNumEntries())
|
||
{
|
||
int nEntriesToDelete=GetNumEntries()-nMaxNum;
|
||
HISTORYENTRY entry;
|
||
for(int nIndex=0; nIndex<nEntriesToDelete; nIndex++)
|
||
{
|
||
entry=m_arrEntries.RemoveHead();
|
||
ASSERT(entry.m_sText.GetLength()<=m_nMaxLengthEntry);
|
||
if(entry.m_sText.GetLength()==m_nMaxLengthEntry)
|
||
{
|
||
UpdateMaxLengthInfo();
|
||
}
|
||
}
|
||
bUpdateScroll=TRUE;
|
||
}
|
||
|
||
m_nMaxNumEntries=nMaxNum;
|
||
|
||
if(bUpdateScroll)
|
||
UpdateScrollInfo();
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////
|
||
|
||
void COXHistoryCtrl::OnPaint()
|
||
{
|
||
CPaintDC dc(this); // device context for painting
|
||
|
||
// TODO: Add your message handler code here
|
||
|
||
OnPrepareDC(&dc);
|
||
|
||
int nSavedDC=dc.SaveDC();
|
||
ASSERT(nSavedDC!=0);
|
||
|
||
ASSERT((HFONT)m_font!=NULL);
|
||
dc.SelectObject(&m_font);
|
||
|
||
//start from the bottom of the rect and work up
|
||
CRect rect;
|
||
GetClientRect(rect);
|
||
CSize sizeTotal=GetTotalSize();
|
||
if(sizeTotal.cx>rect.Width())
|
||
rect.right=rect.left+sizeTotal.cx;
|
||
if(sizeTotal.cy>rect.Height())
|
||
rect.bottom=rect.top+sizeTotal.cy;
|
||
|
||
CRect rectCopy=rect;
|
||
|
||
|
||
HISTORYENTRY entry;
|
||
POSITION pos=m_arrEntries.GetHeadPosition();
|
||
|
||
int nEntries=GetNumEntries();
|
||
rectCopy.top =0;
|
||
rectCopy.bottom =0;
|
||
for(int nIndex=0; nIndex<nEntries; nIndex++)
|
||
{
|
||
if(pos!=NULL)
|
||
{
|
||
entry=m_arrEntries.GetNext(pos);
|
||
rectCopy.bottom+=m_sizeSymbol.cy;
|
||
|
||
dc.SetTextColor(entry.m_clrText);
|
||
dc.SetBkMode(TRANSPARENT);
|
||
CRect rectItem=rectCopy;
|
||
CBrush brush(entry.m_clrBack);
|
||
dc.FillRect(rectItem,&brush);
|
||
rectItem.left+=entry.m_nOffset;
|
||
dc.DrawText(entry.m_sText,rectItem,
|
||
DT_SINGLELINE|DT_VCENTER|entry.m_nAlignment);
|
||
|
||
rectCopy.top=rectCopy.bottom;
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
if(rectCopy.bottom<rect.bottom)
|
||
{
|
||
rectCopy.bottom=rect.bottom;
|
||
dc.FillSolidRect(rectCopy,m_clrLeftOverBack);
|
||
}
|
||
|
||
VERIFY(dc.RestoreDC(nSavedDC));
|
||
|
||
// Do not call COXScrollWnd::OnPaint() for painting messages
|
||
}
|
||
|
||
BOOL COXHistoryCtrl::Initialize()
|
||
{
|
||
if(!COXScrollWnd::Initialize())
|
||
return FALSE;
|
||
|
||
#ifdef OXSCRLWND_USE_RULER
|
||
DetachRuler();
|
||
#endif // OXSCRLWND_USE_RULER
|
||
|
||
SetScrollSizes(MM_TEXT,CSize(0,0),CSize(0,0),CSize(0,0));
|
||
|
||
// Create a fixed size font
|
||
CFont font;
|
||
font.CreateFont( 0, // nHeight
|
||
0, // nWidth
|
||
0, // nEscapement
|
||
0, // nOrientation
|
||
FW_DONTCARE, // nWeight
|
||
FALSE, // bItalic
|
||
FALSE, // bUnderline
|
||
0, // cStrikeOut
|
||
DEFAULT_CHARSET, // nCharSet
|
||
OUT_DEFAULT_PRECIS, // nOutPrecision
|
||
CLIP_DEFAULT_PRECIS, // nClipPrecision
|
||
DEFAULT_QUALITY, // nQuality
|
||
FIXED_PITCH, // nPitchAndFamily
|
||
NULL); // lpszFacename
|
||
|
||
SetFont(&font, FALSE);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
void COXHistoryCtrl::SetFont(CFont* pFont, BOOL bRedraw/*=TRUE*/)
|
||
{
|
||
ASSERT(pFont!=NULL);
|
||
ASSERT((HFONT)*pFont!=NULL);
|
||
|
||
if((HFONT)m_font!=NULL)
|
||
m_font.DeleteObject();
|
||
|
||
LOGFONT lf;
|
||
VERIFY(pFont->GetLogFont(&lf)!=0);
|
||
VERIFY(m_font.CreateFontIndirect(&lf));
|
||
|
||
UpdateFontInfo();
|
||
UpdateScrollInfo();
|
||
|
||
if(bRedraw)
|
||
RedrawWindow();
|
||
}
|
||
|
||
BOOL COXHistoryCtrl::OnEraseBkgnd(CDC* pDC)
|
||
{
|
||
// TODO: Add your message handler code here and/or call default
|
||
|
||
UNREFERENCED_PARAMETER(pDC);
|
||
return TRUE;
|
||
}
|
||
|
||
void COXHistoryCtrl::UpdateFontInfo()
|
||
{
|
||
//get the symbol height and width
|
||
CDC* pDC=GetDC();
|
||
ASSERT_VALID(pDC);
|
||
|
||
CFont* pOldFont=pDC->SelectObject(&m_font);
|
||
|
||
::GetTextExtentPoint(pDC->GetSafeHdc(),_T("X"),1,&m_sizeSymbol);
|
||
|
||
if(pOldFont!=NULL)
|
||
{
|
||
pDC->SelectObject(pOldFont);
|
||
}
|
||
ReleaseDC(pDC);
|
||
}
|
||
|
||
|
||
void COXHistoryCtrl::UpdateScrollInfo()
|
||
{
|
||
// CPoint ptCurrent=GetDeviceScrollPosition();
|
||
|
||
CRect rect;
|
||
GetClientRect(rect);
|
||
CSize sizeTotal(m_nMaxLengthEntry*m_sizeSymbol.cx,
|
||
GetNumEntries()*m_sizeSymbol.cy+rect.Height()%m_sizeSymbol.cy);
|
||
CSize sizePage(m_sizeSymbol.cx,m_sizeSymbol.cy);
|
||
if(m_sizeSymbol.cx<rect.Width())
|
||
{
|
||
sizePage.cx=(rect.Width()/m_sizeSymbol.cx)*m_sizeSymbol.cx;
|
||
}
|
||
if(m_sizeSymbol.cy<rect.Height())
|
||
{
|
||
sizePage.cy=(rect.Height()/m_sizeSymbol.cy)*m_sizeSymbol.cy;
|
||
}
|
||
CSize sizeLine=m_sizeSymbol;
|
||
|
||
SetScrollSizes(MM_TEXT,sizeTotal,sizePage,sizeLine);
|
||
|
||
// ScrollToDevicePosition(ptCurrent);
|
||
}
|
||
|
||
void COXHistoryCtrl::UpdateMaxLengthInfo()
|
||
{
|
||
m_nMaxLengthEntry=0;
|
||
HISTORYENTRY entry;
|
||
POSITION pos=m_arrEntries.GetHeadPosition();
|
||
while(pos!=NULL)
|
||
{
|
||
entry=m_arrEntries.GetNext(pos);
|
||
int nLength=entry.m_sText.GetLength();
|
||
if(m_nMaxLengthEntry<nLength)
|
||
{
|
||
m_nMaxLengthEntry=nLength;
|
||
}
|
||
}
|
||
}
|
||
|
||
int COXHistoryCtrl::GetVisibleCount()
|
||
{
|
||
ASSERT(::IsWindow(GetSafeHwnd()));
|
||
|
||
CRect rect;
|
||
GetClientRect(rect);
|
||
|
||
int nResult=rect.Height()/m_sizeSymbol.cy;
|
||
nResult=(nResult>GetNumEntries() ? GetNumEntries() : nResult);
|
||
|
||
return nResult;
|
||
}
|
||
|
||
|
||
void COXHistoryCtrl::ScrollToEnd()
|
||
{
|
||
CPoint ptScroll=GetScrollPosition();
|
||
ptScroll.y=GetNumEntries()*m_sizeSymbol.cy;
|
||
ScrollToPosition(ptScroll);
|
||
}
|
||
|
||
|
||
|
||
CString COXHistoryCtrl::GetTimeStampedFileName()
|
||
{
|
||
COleDateTime date=COleDateTime::GetCurrentTime();
|
||
int logDay=date.GetDay();
|
||
int logMonth=date.GetMonth();
|
||
int logYear=date.GetYear();
|
||
m_sTimeStampedLogFileName.Format(_T("%2.2d%2.2d%4.4d"),
|
||
logDay,logMonth,logYear);
|
||
|
||
//find the extension
|
||
int nPos=m_sLogFileName.Find(_T("."));
|
||
if(nPos!=-1)
|
||
m_sTimeStampedLogFileName+=m_sLogFileName.Mid(nPos);
|
||
return m_sTimeStampedLogFileName;
|
||
}
|