2025-11-27 16:46:48 +09:00

553 lines
12 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;
}