windows-nt/Source/XPSP1/NT/admin/pchealth/sysinfo/msconfig/exe/undolog.h
2020-09-26 16:20:57 +08:00

408 lines
11 KiB
C++

//=============================================================================
// The CUndoLog is used to implement a log of undo entries to allow MSConfig
// to reverse any changes it may have made. Each tab object is responsible for
// writing a string when it makes changes - this string can be used to undo
// the changes the tab made.
//
// The undo log file will look like this:
//
// ["timestamp" tabname "description" <SHOW|FINAL>]
// tab specific string - any line starting with a "[" will have a
// backslash preceding it
//
// The entries will be arranged in chronological order (most recent first).
// The "description" field will be the only one shown to the user - so it
// will be the only one which needs to be localized. The tab is responsible
// for supplying this text.
//=============================================================================
#pragma once
#include "pagebase.h"
//-----------------------------------------------------------------------------
// This class encapsulates an undo entry (instance of this class will be
// saved in a list).
//-----------------------------------------------------------------------------
class CUndoLogEntry
{
public:
enum UndoEntryState { SHOW, FINAL, UNDONE };
CUndoLogEntry() : m_state(SHOW) {};
CUndoLogEntry(const CString & strTab, const CString & strDescription, const CString & strEntry, const COleDateTime & timestamp)
: m_strTab(strTab),
m_strDescription(strDescription),
m_strEntry(strEntry),
m_timestamp(timestamp),
m_state(SHOW)
{};
CUndoLogEntry(const CString & strTab, const CString & strDescription, const CString & strEntry)
: m_strTab(strTab),
m_strDescription(strDescription),
m_strEntry(strEntry),
m_timestamp(COleDateTime::GetCurrentTime()),
m_state(SHOW)
{};
private:
CUndoLogEntry(const CString & strTab, const CString & strDescription, const CString & strEntry, const COleDateTime & timestamp, UndoEntryState state)
: m_strTab(strTab),
m_strDescription(strDescription),
m_strEntry(strEntry),
m_timestamp(timestamp),
m_state(state)
{};
public:
static CUndoLogEntry * ReadFromFile(CStdioFile & infile)
{
CString strTab, strDescription, strEntry;
UndoEntryState state;
COleDateTime timestamp;
CString strLine;
if (!infile.ReadString(strLine))
return NULL;
strLine.TrimLeft(_T("[\""));
CString strTimestamp = strLine.SpanExcluding(_T("\""));
strLine = strLine.Mid(strTimestamp.GetLength());
timestamp.ParseDateTime(strTimestamp);
strLine.TrimLeft(_T(" \""));
strTab = strLine.SpanExcluding(_T(" "));
strLine = strLine.Mid(strTab.GetLength());
strLine.TrimLeft(_T(" \""));
strDescription = strLine.SpanExcluding(_T("\""));
strLine = strLine.Mid(strDescription.GetLength());
strLine.TrimLeft(_T(" \""));
CString strFinal = strLine.SpanExcluding(_T("]"));
if (strFinal.CompareNoCase(_T("final")) == 0)
state = FINAL;
else if (strFinal.CompareNoCase(_T("show")) == 0)
state = SHOW;
else
state = UNDONE;
strLine.Empty();
for (;;)
{
if (!infile.ReadString(strLine))
break;
if (strLine.IsEmpty())
continue;
if (strLine[0] == _T('['))
{
// We read the first line of the next entry. Back up in the file (including the
// newline and CR characters).
infile.Seek(-1 * (strLine.GetLength() + 2) * sizeof(TCHAR), CFile::current);
break;
}
if (strLine[0] == _T('\\'))
strLine = strLine.Mid(1);
strEntry += strLine + _T("\n");
}
return new CUndoLogEntry(strTab, strDescription, strEntry, timestamp, state);
}
BOOL WriteToFile(CStdioFile & outfile)
{
ASSERT(!m_strTab.IsEmpty());
CString strLine;
strLine = _T("[\"") + m_timestamp.Format() + _T("\" ");
strLine += m_strTab + _T(" \"");
strLine += m_strDescription + _T("\" ");
switch (m_state)
{
case FINAL:
strLine += _T("FINAL");
break;
case UNDONE:
strLine += _T("UNDONE");
break;
case SHOW:
default:
strLine += _T("SHOW");
break;
}
strLine += _T("]\n");
outfile.WriteString(strLine); // TBD - catch the exception
CString strWorking(m_strEntry);
while (!strWorking.IsEmpty())
{
strLine = strWorking.SpanExcluding(_T("\n\r"));
strWorking = strWorking.Mid(strLine.GetLength());
strWorking.TrimLeft(_T("\n\r"));
if (!strLine.IsEmpty() && strLine[0] == _T('['))
strLine = _T("\\") + strLine;
strLine += _T("\n");
outfile.WriteString(strLine);
}
return TRUE;
}
~CUndoLogEntry() {};
CString m_strTab;
CString m_strDescription;
CString m_strEntry;
COleDateTime m_timestamp;
UndoEntryState m_state;
};
//-----------------------------------------------------------------------------
// This class implements the undo log.
//-----------------------------------------------------------------------------
class CUndoLog
{
public:
CUndoLog() : m_fChanges(FALSE), m_pmapTabs(NULL)
{
}
~CUndoLog()
{
while (!m_entrylist.IsEmpty())
{
CUndoLogEntry * pEntry = (CUndoLogEntry *)m_entrylist.RemoveHead();
if (pEntry)
delete pEntry;
}
};
//-------------------------------------------------------------------------
// These functions will load the undo log from a file, or save to a file.
// Note - saving to a file will overwrite the contents of the file with
// the contents of the undo log.
//-------------------------------------------------------------------------
BOOL LoadFromFile(LPCTSTR szFilename)
{
ASSERT(szFilename);
if (szFilename == NULL)
return FALSE;
CStdioFile logfile;
if (logfile.Open(szFilename, CFile::modeRead | CFile::typeText))
{
CUndoLogEntry * pEntry;
while (pEntry = CUndoLogEntry::ReadFromFile(logfile))
m_entrylist.AddTail((void *) pEntry);
logfile.Close();
return TRUE;
}
return FALSE;
}
BOOL SaveToFile(LPCTSTR szFilename)
{
ASSERT(szFilename);
if (szFilename == NULL)
return FALSE;
if (!m_fChanges)
return TRUE;
CStdioFile logfile;
if (logfile.Open(szFilename, CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive | CFile::typeText))
{
for (POSITION pos = m_entrylist.GetHeadPosition(); pos != NULL;)
{
CUndoLogEntry * pEntry = (CUndoLogEntry *)m_entrylist.GetNext(pos);
if (pEntry != NULL)
pEntry->WriteToFile(logfile);
}
logfile.Close();
return TRUE;
}
return FALSE;
}
//-------------------------------------------------------------------------
// How many undo entries are in this log?
//-------------------------------------------------------------------------
int GetUndoEntryCount()
{
int iCount = 0;
for (POSITION pos = m_entrylist.GetHeadPosition(); pos != NULL;)
{
CUndoLogEntry * pEntry = (CUndoLogEntry *)m_entrylist.GetNext(pos);
if (pEntry != NULL && pEntry->m_state == CUndoLogEntry::SHOW)
iCount += 1;
}
return iCount;
}
//-------------------------------------------------------------------------
// Get information about a specific entry (returns FALSE for bad index).
//-------------------------------------------------------------------------
BOOL GetUndoEntryInfo(int iIndex, CString & strDescription, COleDateTime & timestamp)
{
CUndoLogEntry * pEntry = GetEntryByIndex(iIndex);
if (pEntry != NULL)
{
strDescription = pEntry->m_strDescription;
timestamp = pEntry->m_timestamp;
return TRUE;
}
return FALSE;
}
//-------------------------------------------------------------------------
// Get the entry data (to pass to a tab to undo). FALSE for bad index.
//-------------------------------------------------------------------------
BOOL GetUndoEntry(int iIndex, CString * pstrTab, CString * pstrEntry)
{
CUndoLogEntry * pEntry = GetEntryByIndex(iIndex);
if (pEntry != NULL)
{
if (pstrTab) *pstrTab = pEntry->m_strTab;
if (pstrEntry) *pstrEntry = pEntry->m_strEntry;
return TRUE;
}
return FALSE;
}
//-------------------------------------------------------------------------
// Mark an entry as final (stays in the file, but marked so it won't
// appear in the undo log). FALSE for bad index.
//-------------------------------------------------------------------------
BOOL MarkUndoEntryFinal(int iIndex)
{
CUndoLogEntry * pEntry = GetEntryByIndex(iIndex);
if (pEntry != NULL)
{
pEntry->m_state = CUndoLogEntry::FINAL;
m_fChanges = TRUE;
return TRUE;
}
return FALSE;
}
//-------------------------------------------------------------------------
// Delete all entries in the log that are older than
// timestampOlderThanThis. The entries will be gone, purged from the file.
//-------------------------------------------------------------------------
BOOL DeleteOldUndoEntries(const COleDateTime & timestampOlderThanThis)
{
m_fChanges = TRUE;
return FALSE;
}
//-------------------------------------------------------------------------
// Create a new undo entry, using the current time, and add it to the
// end of the undo log. Shouldn't return FALSE unless there is no memory.
//-------------------------------------------------------------------------
BOOL AddUndoEntry(const CString & strTab, const CString & strDescription, const CString & strEntry)
{
CUndoLogEntry * pEntry = new CUndoLogEntry(strTab, strDescription, strEntry);
if (pEntry == NULL)
return FALSE;
m_entrylist.AddHead((void *)pEntry);
m_fChanges = TRUE;
return TRUE;
}
//-------------------------------------------------------------------------
// Called to undo the effects of one of the entries. This function will
// need to find the appropriate tab and call its undo function.
//-------------------------------------------------------------------------
BOOL UndoEntry(int iIndex)
{
CUndoLogEntry * pEntry = GetEntryByIndex(iIndex);
if (!pEntry)
return FALSE;
if (pEntry->m_state != CUndoLogEntry::SHOW)
return FALSE;
if (!m_pmapTabs)
return FALSE;
CPageBase * pPage;
if (!m_pmapTabs->Lookup(pEntry->m_strTab, (void * &)pPage) || !pPage)
return FALSE;
// if (!pPage->Undo(pEntry->m_strEntry))
// return FALSE;
pEntry->m_state = CUndoLogEntry::UNDONE;
m_fChanges = TRUE;
return TRUE;
}
//-------------------------------------------------------------------------
// Sets a pointer to the map from tab name to pointer.
//-------------------------------------------------------------------------
void SetTabMap(CMapStringToPtr * pmap)
{
m_pmapTabs = pmap;
}
private:
CUndoLogEntry * GetEntryByIndex(int iIndex)
{
CUndoLogEntry * pEntry = NULL;
POSITION pos = m_entrylist.GetHeadPosition();
do
{
if (pos == NULL)
return NULL;
pEntry = (CUndoLogEntry *)m_entrylist.GetNext(pos);
if (pEntry != NULL && pEntry->m_state == CUndoLogEntry::SHOW)
iIndex -= 1;
} while (iIndex >= 0);
return pEntry;
}
private:
//-------------------------------------------------------------------------
// Member variables.
//-------------------------------------------------------------------------
CMapStringToPtr * m_pmapTabs; // map from tab name to CPageBase pointers
CPtrList m_entrylist; // list of CUndoLogEntry pointers
BOOL m_fChanges; // was the log changed?
};