added HasEntry/Group function to wxConfig, corrected entry/group deletion
bug (were not deleted from file) in wxFileConfig git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@183 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -109,6 +109,15 @@ public:
|
|||||||
virtual bool GetFirstEntry(wxString& str, long& lIndex) = 0;
|
virtual bool GetFirstEntry(wxString& str, long& lIndex) = 0;
|
||||||
virtual bool GetNextEntry (wxString& str, long& lIndex) = 0;
|
virtual bool GetNextEntry (wxString& str, long& lIndex) = 0;
|
||||||
|
|
||||||
|
// tests of existence
|
||||||
|
// returns TRUE if the group by this name exists
|
||||||
|
virtual bool HasGroup(const wxString& strName) const = 0;
|
||||||
|
// same as above, but for an entry
|
||||||
|
virtual bool HasEntry(const wxString& strName) const = 0;
|
||||||
|
// returns TRUE if either a group or an entry with a given name exist
|
||||||
|
bool Exists(const wxString& strName) const
|
||||||
|
{ return HasGroup(strName) || HasEntry(strName); }
|
||||||
|
|
||||||
// key access: returns TRUE if value was really read, FALSE if default used
|
// key access: returns TRUE if value was really read, FALSE if default used
|
||||||
// (and if the key is not found the default value is returned.)
|
// (and if the key is not found the default value is returned.)
|
||||||
// read a string from the key
|
// read a string from the key
|
||||||
@@ -119,7 +128,7 @@ public:
|
|||||||
virtual const char *Read(const char *szKey,
|
virtual const char *Read(const char *szKey,
|
||||||
const char *szDefault = NULL) const;
|
const char *szDefault = NULL) const;
|
||||||
// the same for longs
|
// the same for longs
|
||||||
long Read(const char *szKey, long lDefault) const
|
virtual long Read(const char *szKey, long lDefault) const
|
||||||
{ long l; Read(&l, szKey, lDefault); return l; }
|
{ long l; Read(&l, szKey, lDefault); return l; }
|
||||||
// and another version: returns true if default value is returned
|
// and another version: returns true if default value is returned
|
||||||
virtual bool Read(long *pl, const char *szKey, long lDefault = 0) const = 0;
|
virtual bool Read(long *pl, const char *szKey, long lDefault = 0) const = 0;
|
||||||
|
@@ -34,12 +34,12 @@
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
wxFileConfig derives from base Config and implements file based config class,
|
wxFileConfig derives from base Config and implements file based config class,
|
||||||
i.e. it uses ASCII disk files to store the information. These files are
|
i.e. it uses ASCII disk files to store the information. These files are
|
||||||
alternatively called INI, .conf or .rc in the documentation. They are
|
alternatively called INI, .conf or .rc in the documentation. They are
|
||||||
organized in groups or sections, which can nest (i.e. a group contains
|
organized in groups or sections, which can nest (i.e. a group contains
|
||||||
subgroups, which contain their own subgroups &c). Each group has some
|
subgroups, which contain their own subgroups &c). Each group has some
|
||||||
number of entries, which are "key = value" pairs. More precisely, the format
|
number of entries, which are "key = value" pairs. More precisely, the format
|
||||||
is:
|
is:
|
||||||
|
|
||||||
# comments are allowed after either ';' or '#' (Win/UNIX standard)
|
# comments are allowed after either ';' or '#' (Win/UNIX standard)
|
||||||
@@ -118,11 +118,16 @@ public:
|
|||||||
virtual bool GetFirstEntry(wxString& str, long& lIndex);
|
virtual bool GetFirstEntry(wxString& str, long& lIndex);
|
||||||
virtual bool GetNextEntry (wxString& str, long& lIndex);
|
virtual bool GetNextEntry (wxString& str, long& lIndex);
|
||||||
|
|
||||||
|
virtual bool HasGroup(const wxString& strName) const;
|
||||||
|
virtual bool HasEntry(const wxString& strName) const;
|
||||||
|
|
||||||
virtual bool Read(wxString *pstr, const char *szKey,
|
virtual bool Read(wxString *pstr, const char *szKey,
|
||||||
const char *szDefault = 0) const;
|
const char *szDefault = 0) const;
|
||||||
virtual const char *Read(const char *szKey,
|
virtual const char *Read(const char *szKey,
|
||||||
const char *szDefault = 0) const;
|
const char *szDefault = 0) const;
|
||||||
virtual bool Read(long *pl, const char *szKey, long lDefault) const;
|
virtual bool Read(long *pl, const char *szKey, long lDefault) const;
|
||||||
|
virtual long Read(const char *szKey, long lDefault) const
|
||||||
|
{ return wxConfig::Read(szKey, lDefault); }
|
||||||
virtual bool Write(const char *szKey, const char *szValue);
|
virtual bool Write(const char *szKey, const char *szValue);
|
||||||
virtual bool Write(const char *szKey, long lValue);
|
virtual bool Write(const char *szKey, long lValue);
|
||||||
virtual bool Flush(bool bCurrentOnly = FALSE);
|
virtual bool Flush(bool bCurrentOnly = FALSE);
|
||||||
@@ -141,12 +146,14 @@ public:
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// ctor
|
// ctor
|
||||||
LineList(const wxString& str, LineList *pNext = NULL) : m_strLine(str)
|
LineList(const wxString& str, LineList *pNext = NULL) : m_strLine(str)
|
||||||
{ SetNext(pNext); }
|
{ SetNext(pNext); SetPrev(NULL); }
|
||||||
|
|
||||||
//
|
//
|
||||||
LineList *Next() const { return m_pNext; }
|
LineList *Next() const { return m_pNext; }
|
||||||
|
LineList *Prev() const { return m_pPrev; }
|
||||||
void SetNext(LineList *pNext) { m_pNext = pNext; }
|
void SetNext(LineList *pNext) { m_pNext = pNext; }
|
||||||
|
void SetPrev(LineList *pPrev) { m_pPrev = pPrev; }
|
||||||
|
|
||||||
//
|
//
|
||||||
void SetText(const wxString& str) { m_strLine = str; }
|
void SetText(const wxString& str) { m_strLine = str; }
|
||||||
@@ -154,13 +161,15 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
wxString m_strLine; // line contents
|
wxString m_strLine; // line contents
|
||||||
LineList *m_pNext; // next node
|
LineList *m_pNext, // next node
|
||||||
|
*m_pPrev; // previous one
|
||||||
};
|
};
|
||||||
|
|
||||||
// functions to work with this list
|
// functions to work with this list
|
||||||
LineList *LineListAppend(const wxString& str);
|
LineList *LineListAppend(const wxString& str);
|
||||||
LineList *LineListInsert(const wxString& str,
|
LineList *LineListInsert(const wxString& str,
|
||||||
LineList *pLine); // NULL => Append()
|
LineList *pLine); // NULL => Prepend()
|
||||||
|
void LineListRemove(LineList *pLine);
|
||||||
bool LineListIsEmpty();
|
bool LineListIsEmpty();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -268,7 +277,7 @@ protected:
|
|||||||
// will also recursively set parent's dirty flag
|
// will also recursively set parent's dirty flag
|
||||||
void SetDirty();
|
void SetDirty();
|
||||||
void SetLine(LineList *pLine);
|
void SetLine(LineList *pLine);
|
||||||
|
|
||||||
// the new entries in this subgroup will be inserted after the last subgroup
|
// the new entries in this subgroup will be inserted after the last subgroup
|
||||||
// or, if there is none, after the last entry
|
// or, if there is none, after the last entry
|
||||||
void SetLastEntry(ConfigEntry *pLastEntry) { m_pLastEntry = pLastEntry; }
|
void SetLastEntry(ConfigEntry *pLastEntry) { m_pLastEntry = pLastEntry; }
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
// Name: fileconf.cpp
|
// Name: fileconf.cpp
|
||||||
// Purpose: implementation of wxFileConfig derivation of wxConfig
|
// Purpose: implementation of wxFileConfig derivation of wxConfig
|
||||||
// Author: Vadim Zeitlin
|
// Author: Vadim Zeitlin
|
||||||
// Modified by:
|
// Modified by:
|
||||||
// Created: 07.04.98 (adapted from appconf.cpp)
|
// Created: 07.04.98 (adapted from appconf.cpp)
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) 1997 Karsten Ball<6C>der & Vadim Zeitlin
|
// Copyright: (c) 1997 Karsten Ball<6C>der & Vadim Zeitlin
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
// is 'c' a valid character in group name?
|
// is 'c' a valid character in group name?
|
||||||
// NB: APPCONF_IMMUTABLE_PREFIX and APPCONF_PATH_SEPARATOR must be valid chars,
|
// NB: APPCONF_IMMUTABLE_PREFIX and APPCONF_PATH_SEPARATOR must be valid chars,
|
||||||
// but _not_ ']' (group name delimiter)
|
// but _not_ ']' (group name delimiter)
|
||||||
inline bool IsValid(char c) { return isalnum(c) || strchr("_/-!.*%", c); }
|
inline bool IsValid(char c) { return isalnum(c) || strchr("@_/-!.*%", c); }
|
||||||
|
|
||||||
// filter strings
|
// filter strings
|
||||||
static wxString FilterIn(const wxString& str);
|
static wxString FilterIn(const wxString& str);
|
||||||
@@ -131,7 +131,7 @@ wxString wxFileConfig::GetLocalFileName(const char *szFile)
|
|||||||
|
|
||||||
void wxFileConfig::Init()
|
void wxFileConfig::Init()
|
||||||
{
|
{
|
||||||
m_pCurrentGroup =
|
m_pCurrentGroup =
|
||||||
m_pRootGroup = new ConfigGroup(NULL, "", this);
|
m_pRootGroup = new ConfigGroup(NULL, "", this);
|
||||||
|
|
||||||
m_linesHead =
|
m_linesHead =
|
||||||
@@ -191,7 +191,7 @@ void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
|
|||||||
{
|
{
|
||||||
const char *pStart;
|
const char *pStart;
|
||||||
const char *pEnd;
|
const char *pEnd;
|
||||||
|
|
||||||
for ( uint n = 0; n < file.GetLineCount(); n++ ) {
|
for ( uint n = 0; n < file.GetLineCount(); n++ ) {
|
||||||
// add the line to linked list
|
// add the line to linked list
|
||||||
if ( bLocal )
|
if ( bLocal )
|
||||||
@@ -238,12 +238,12 @@ void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
|
|||||||
case ';':
|
case ';':
|
||||||
bCont = FALSE;
|
bCont = FALSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ' ':
|
case ' ':
|
||||||
case '\t':
|
case '\t':
|
||||||
// ignore whitespace ('\n' impossible here)
|
// ignore whitespace ('\n' impossible here)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
wxLogWarning("file '%s', line %d: '%s' ignored after group header.",
|
wxLogWarning("file '%s', line %d: '%s' ignored after group header.",
|
||||||
file.GetName(), n + 1, pEnd);
|
file.GetName(), n + 1, pEnd);
|
||||||
@@ -259,7 +259,7 @@ void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
|
|||||||
wxString strKey(pStart, pEnd);
|
wxString strKey(pStart, pEnd);
|
||||||
|
|
||||||
// skip whitespace
|
// skip whitespace
|
||||||
while ( isspace(*pEnd) )
|
while ( isspace(*pEnd) )
|
||||||
pEnd++;
|
pEnd++;
|
||||||
|
|
||||||
if ( *pEnd++ != '=' ) {
|
if ( *pEnd++ != '=' ) {
|
||||||
@@ -288,7 +288,7 @@ void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
|
|||||||
// (c) key from global file now found in local one
|
// (c) key from global file now found in local one
|
||||||
// which is exactly what we want.
|
// which is exactly what we want.
|
||||||
else if ( !bLocal || pEntry->IsLocal() ) {
|
else if ( !bLocal || pEntry->IsLocal() ) {
|
||||||
wxLogWarning("file '%s', line %d: key '%s' was first found at line %d.",
|
wxLogWarning("file '%s', line %d: key '%s' was first found at line %d.",
|
||||||
file.GetName(), n + 1, strKey.c_str(), pEntry->Line());
|
file.GetName(), n + 1, strKey.c_str(), pEntry->Line());
|
||||||
|
|
||||||
if ( bLocal )
|
if ( bLocal )
|
||||||
@@ -297,7 +297,7 @@ void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skip whitespace
|
// skip whitespace
|
||||||
while ( isspace(*pEnd) )
|
while ( isspace(*pEnd) )
|
||||||
pEnd++;
|
pEnd++;
|
||||||
|
|
||||||
wxString strValue;
|
wxString strValue;
|
||||||
@@ -325,8 +325,10 @@ void wxFileConfig::SetPath(const wxString& strPath)
|
|||||||
{
|
{
|
||||||
wxArrayString aParts;
|
wxArrayString aParts;
|
||||||
|
|
||||||
if ( strPath.IsEmpty() )
|
if ( strPath.IsEmpty() ) {
|
||||||
|
SetRootPath();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( strPath[0] == APPCONF_PATH_SEPARATOR ) {
|
if ( strPath[0] == APPCONF_PATH_SEPARATOR ) {
|
||||||
// absolute path
|
// absolute path
|
||||||
@@ -392,6 +394,26 @@ bool wxFileConfig::GetNextEntry (wxString& str, long& lIndex)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// tests for existence
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool wxFileConfig::HasGroup(const wxString& strName) const
|
||||||
|
{
|
||||||
|
PathChanger path(this, strName);
|
||||||
|
|
||||||
|
ConfigGroup *pGroup = m_pCurrentGroup->FindSubgroup(path.Name());
|
||||||
|
return pGroup != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxFileConfig::HasEntry(const wxString& strName) const
|
||||||
|
{
|
||||||
|
PathChanger path(this, strName);
|
||||||
|
|
||||||
|
ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
|
||||||
|
return pEntry != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// read/write values
|
// read/write values
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -540,6 +562,7 @@ wxFileConfig::LineList *wxFileConfig::LineListAppend(const wxString& str)
|
|||||||
else {
|
else {
|
||||||
// adjust pointers
|
// adjust pointers
|
||||||
m_linesTail->SetNext(pLine);
|
m_linesTail->SetNext(pLine);
|
||||||
|
pLine->SetPrev(m_linesTail);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_linesTail = pLine;
|
m_linesTail = pLine;
|
||||||
@@ -547,26 +570,49 @@ wxFileConfig::LineList *wxFileConfig::LineListAppend(const wxString& str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// insert a new line after the given one or in the very beginning if !pLine
|
// insert a new line after the given one or in the very beginning if !pLine
|
||||||
wxFileConfig::LineList *wxFileConfig::LineListInsert(const wxString& str,
|
wxFileConfig::LineList *wxFileConfig::LineListInsert(const wxString& str,
|
||||||
LineList *pLine)
|
LineList *pLine)
|
||||||
{
|
{
|
||||||
if ( pLine == m_linesTail )
|
if ( pLine == m_linesTail )
|
||||||
return LineListAppend(str);
|
return LineListAppend(str);
|
||||||
|
|
||||||
LineList *pNewLine;
|
LineList *pNewLine = new LineList(str);
|
||||||
|
|
||||||
if ( pLine == NULL ) {
|
if ( pLine == NULL ) {
|
||||||
pNewLine = new LineList(str, m_linesHead);
|
// prepend to the list
|
||||||
|
pNewLine->SetNext(m_linesHead);
|
||||||
|
m_linesHead->SetPrev(pNewLine);
|
||||||
m_linesHead = pNewLine;
|
m_linesHead = pNewLine;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pNewLine = new LineList(str, pLine->Next());
|
// insert before pLine
|
||||||
|
LineList *pNext = pLine->Next();
|
||||||
|
pNewLine->SetNext(pNext);
|
||||||
|
pNewLine->SetPrev(pLine);
|
||||||
|
pNext->SetPrev(pNewLine);
|
||||||
pLine->SetNext(pNewLine);
|
pLine->SetNext(pNewLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pNewLine;
|
return pNewLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxFileConfig::LineListRemove(LineList *pLine)
|
||||||
|
{
|
||||||
|
LineList *pPrev = pLine->Prev(),
|
||||||
|
*pNext = pLine->Next();
|
||||||
|
if ( pPrev == NULL ) {
|
||||||
|
// deleting the first entry
|
||||||
|
m_linesHead = pNext;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// not the first entry
|
||||||
|
pPrev->SetNext(pNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
pNext->SetPrev(pPrev);
|
||||||
|
|
||||||
|
delete pLine;
|
||||||
|
}
|
||||||
|
|
||||||
bool wxFileConfig::LineListIsEmpty()
|
bool wxFileConfig::LineListIsEmpty()
|
||||||
{
|
{
|
||||||
return m_linesHead == NULL;
|
return m_linesHead == NULL;
|
||||||
@@ -627,7 +673,7 @@ wxFileConfig::LineList *wxFileConfig::ConfigGroup::GetGroupLine()
|
|||||||
if ( Parent() != NULL ) {
|
if ( Parent() != NULL ) {
|
||||||
wxString strFullName;
|
wxString strFullName;
|
||||||
strFullName << "[" << GetFullName().c_str() + 1 << "]"; // +1: no '/'
|
strFullName << "[" << GetFullName().c_str() + 1 << "]"; // +1: no '/'
|
||||||
m_pLine = m_pConfig->LineListInsert(strFullName,
|
m_pLine = m_pConfig->LineListInsert(strFullName,
|
||||||
Parent()->GetLastGroupLine());
|
Parent()->GetLastGroupLine());
|
||||||
Parent()->SetLastGroup(this);
|
Parent()->SetLastGroup(this);
|
||||||
}
|
}
|
||||||
@@ -755,7 +801,21 @@ bool wxFileConfig::ConfigGroup::DeleteSubgroup(const char *szName)
|
|||||||
if ( n == nCount )
|
if ( n == nCount )
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
delete m_aSubgroups[n];
|
nCount = m_aEntries.Count();
|
||||||
|
for ( n = 0; n < nCount; n++ ) {
|
||||||
|
LineList *pLine = m_aEntries[n]->GetLine();
|
||||||
|
if ( pLine != NULL )
|
||||||
|
m_pConfig->LineListRemove(pLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigGroup *pGroup = m_aSubgroups[n];
|
||||||
|
LineList *pLine = pGroup->m_pLine;
|
||||||
|
if ( pLine != NULL )
|
||||||
|
m_pConfig->LineListRemove(pLine);
|
||||||
|
delete pGroup;
|
||||||
|
|
||||||
|
SetDirty();
|
||||||
|
|
||||||
m_aSubgroups.Remove(n);
|
m_aSubgroups.Remove(n);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -771,13 +831,20 @@ bool wxFileConfig::ConfigGroup::DeleteEntry(const char *szName)
|
|||||||
if ( n == nCount )
|
if ( n == nCount )
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
delete m_aEntries[n];
|
ConfigEntry *pEntry = m_aEntries[n];
|
||||||
|
LineList *pLine = pEntry->GetLine();
|
||||||
|
if ( pLine != NULL )
|
||||||
|
m_pConfig->LineListRemove(pLine);
|
||||||
|
delete pEntry;
|
||||||
|
|
||||||
|
SetDirty();
|
||||||
|
|
||||||
m_aEntries.Remove(n);
|
m_aEntries.Remove(n);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
void wxFileConfig::ConfigGroup::SetDirty()
|
void wxFileConfig::ConfigGroup::SetDirty()
|
||||||
{
|
{
|
||||||
@@ -793,7 +860,7 @@ void wxFileConfig::ConfigGroup::SetDirty()
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// ctor
|
// ctor
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
wxFileConfig::ConfigEntry::ConfigEntry(wxFileConfig::ConfigGroup *pParent,
|
wxFileConfig::ConfigEntry::ConfigEntry(wxFileConfig::ConfigGroup *pParent,
|
||||||
const wxString& strName,
|
const wxString& strName,
|
||||||
int nLine)
|
int nLine)
|
||||||
: m_strName(strName)
|
: m_strName(strName)
|
||||||
@@ -829,7 +896,7 @@ void wxFileConfig::ConfigEntry::SetLine(LineList *pLine)
|
|||||||
void wxFileConfig::ConfigEntry::SetValue(const wxString& strValue, bool bUser)
|
void wxFileConfig::ConfigEntry::SetValue(const wxString& strValue, bool bUser)
|
||||||
{
|
{
|
||||||
if ( bUser && IsImmutable() ) {
|
if ( bUser && IsImmutable() ) {
|
||||||
wxLogWarning("Attempt to change immutable key '%s' ignored.",
|
wxLogWarning("Attempt to change immutable key '%s' ignored.",
|
||||||
Name().c_str());
|
Name().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -853,7 +920,7 @@ void wxFileConfig::ConfigEntry::SetValue(const wxString& strValue, bool bUser)
|
|||||||
// add a new line to the file
|
// add a new line to the file
|
||||||
wxASSERT( m_nLine == NOT_FOUND ); // consistency check
|
wxASSERT( m_nLine == NOT_FOUND ); // consistency check
|
||||||
|
|
||||||
m_pLine = Group()->Config()->LineListInsert(strLine,
|
m_pLine = Group()->Config()->LineListInsert(strLine,
|
||||||
Group()->GetLastEntryLine());
|
Group()->GetLastEntryLine());
|
||||||
Group()->SetLastEntry(this);
|
Group()->SetLastEntry(this);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user