408 lines
11 KiB
C
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?
|
||
|
};
|