1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/fileconf.cpp
3 // Purpose: implementation of wxFileConfig derivation of wxConfig
4 // Author: Vadim Zeitlin
6 // Created: 07.04.98 (adapted from appconf.cpp)
8 // Copyright: (c) 1997 Karsten Ballüder & Vadim Zeitlin
9 // Ballueder@usa.net <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
24 #if wxUSE_CONFIG && wxUSE_FILECONFIG
27 #include "wx/dynarray.h"
28 #include "wx/string.h"
32 #include "wx/utils.h" // for wxGetHomeDir
34 #include "wx/stream.h"
35 #endif // wxUSE_STREAMS
39 #include "wx/textfile.h"
40 #include "wx/memtext.h"
41 #include "wx/config.h"
42 #include "wx/fileconf.h"
43 #include "wx/filefn.h"
45 #include "wx/base64.h"
47 #include "wx/stdpaths.h"
49 #if defined(__WXMAC__)
50 #include "wx/mac/private.h" // includes mac headers
53 #if defined(__WXMSW__)
54 #include "wx/msw/private.h"
64 // ----------------------------------------------------------------------------
66 // ----------------------------------------------------------------------------
67 #define CONST_CAST ((wxFileConfig *)this)->
69 // ----------------------------------------------------------------------------
71 // ----------------------------------------------------------------------------
77 #define FILECONF_TRACE_MASK _T("fileconf")
79 // ----------------------------------------------------------------------------
80 // global functions declarations
81 // ----------------------------------------------------------------------------
83 // compare functions for sorting the arrays
84 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
85 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
88 static wxString
FilterInValue(const wxString
& str
);
89 static wxString
FilterOutValue(const wxString
& str
);
91 static wxString
FilterInEntryName(const wxString
& str
);
92 static wxString
FilterOutEntryName(const wxString
& str
);
94 // get the name to use in wxFileConfig ctor
95 static wxString
GetAppName(const wxString
& appname
);
97 // ============================================================================
99 // ============================================================================
101 // ----------------------------------------------------------------------------
102 // "template" array types
103 // ----------------------------------------------------------------------------
105 #ifdef WXMAKINGDLL_BASE
106 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
,
108 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
,
111 WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
112 WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
);
115 // ----------------------------------------------------------------------------
116 // wxFileConfigLineList
117 // ----------------------------------------------------------------------------
119 // we store all lines of the local config file as a linked list in memory
120 class wxFileConfigLineList
123 void SetNext(wxFileConfigLineList
*pNext
) { m_pNext
= pNext
; }
124 void SetPrev(wxFileConfigLineList
*pPrev
) { m_pPrev
= pPrev
; }
127 wxFileConfigLineList(const wxString
& str
,
128 wxFileConfigLineList
*pNext
= NULL
) : m_strLine(str
)
129 { SetNext(pNext
); SetPrev(NULL
); }
131 // next/prev nodes in the linked list
132 wxFileConfigLineList
*Next() const { return m_pNext
; }
133 wxFileConfigLineList
*Prev() const { return m_pPrev
; }
135 // get/change lines text
136 void SetText(const wxString
& str
) { m_strLine
= str
; }
137 const wxString
& Text() const { return m_strLine
; }
140 wxString m_strLine
; // line contents
141 wxFileConfigLineList
*m_pNext
, // next node
142 *m_pPrev
; // previous one
144 DECLARE_NO_COPY_CLASS(wxFileConfigLineList
)
147 // ----------------------------------------------------------------------------
148 // wxFileConfigEntry: a name/value pair
149 // ----------------------------------------------------------------------------
151 class wxFileConfigEntry
154 wxFileConfigGroup
*m_pParent
; // group that contains us
156 wxString m_strName
, // entry name
158 bool m_bImmutable
:1, // can be overriden locally?
159 m_bHasValue
:1; // set after first call to SetValue()
161 int m_nLine
; // used if m_pLine == NULL only
163 // pointer to our line in the linked list or NULL if it was found in global
164 // file (which we don't modify)
165 wxFileConfigLineList
*m_pLine
;
168 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
169 const wxString
& strName
, int nLine
);
172 const wxString
& Name() const { return m_strName
; }
173 const wxString
& Value() const { return m_strValue
; }
174 wxFileConfigGroup
*Group() const { return m_pParent
; }
175 bool IsImmutable() const { return m_bImmutable
; }
176 bool IsLocal() const { return m_pLine
!= 0; }
177 int Line() const { return m_nLine
; }
178 wxFileConfigLineList
*
179 GetLine() const { return m_pLine
; }
181 // modify entry attributes
182 void SetValue(const wxString
& strValue
, bool bUser
= true);
183 void SetLine(wxFileConfigLineList
*pLine
);
185 DECLARE_NO_COPY_CLASS(wxFileConfigEntry
)
188 // ----------------------------------------------------------------------------
189 // wxFileConfigGroup: container of entries and other groups
190 // ----------------------------------------------------------------------------
192 class wxFileConfigGroup
195 wxFileConfig
*m_pConfig
; // config object we belong to
196 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
197 ArrayEntries m_aEntries
; // entries in this group
198 ArrayGroups m_aSubgroups
; // subgroups
199 wxString m_strName
; // group's name
200 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
201 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
202 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
204 // DeleteSubgroupByName helper
205 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
208 void UpdateGroupAndSubgroupsLines();
212 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
214 // dtor deletes all entries and subgroups also
215 ~wxFileConfigGroup();
218 const wxString
& Name() const { return m_strName
; }
219 wxFileConfigGroup
*Parent() const { return m_pParent
; }
220 wxFileConfig
*Config() const { return m_pConfig
; }
222 const ArrayEntries
& Entries() const { return m_aEntries
; }
223 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
224 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
226 // find entry/subgroup (NULL if not found)
227 wxFileConfigGroup
*FindSubgroup(const wxString
& name
) const;
228 wxFileConfigEntry
*FindEntry (const wxString
& name
) const;
230 // delete entry/subgroup, return false if doesn't exist
231 bool DeleteSubgroupByName(const wxString
& name
);
232 bool DeleteEntry(const wxString
& name
);
234 // create new entry/subgroup returning pointer to newly created element
235 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
236 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
238 void SetLine(wxFileConfigLineList
*pLine
);
240 // rename: no checks are done to ensure that the name is unique!
241 void Rename(const wxString
& newName
);
244 wxString
GetFullName() const;
246 // get the last line belonging to an entry/subgroup of this group
247 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
248 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
249 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
251 // called by entries/subgroups when they're created/deleted
252 void SetLastEntry(wxFileConfigEntry
*pEntry
);
253 void SetLastGroup(wxFileConfigGroup
*pGroup
)
254 { m_pLastGroup
= pGroup
; }
256 DECLARE_NO_COPY_CLASS(wxFileConfigGroup
)
259 // ============================================================================
261 // ============================================================================
263 // ----------------------------------------------------------------------------
265 // ----------------------------------------------------------------------------
267 // this function modifies in place the given wxFileName object if it doesn't
268 // already have an extension
270 // note that it's slightly misnamed under Mac as there it doesn't add an
271 // extension but modifies the file name instead, so you shouldn't suppose that
272 // fn.HasExt() is true after it returns
273 static void AddConfFileExtIfNeeded(wxFileName
& fn
)
277 #if defined( __WXMAC__ )
278 fn
.SetName(fn
.GetName() + wxT(" Preferences"));
279 #elif defined( __UNIX__ )
280 fn
.SetExt(wxT(".conf"));
282 fn
.SetExt(wxT(".ini"));
287 wxString
wxFileConfig::GetGlobalDir()
289 return wxStandardPaths::Get().GetConfigDir();
292 wxString
wxFileConfig::GetLocalDir(int style
)
296 wxStandardPathsBase
& stdp
= wxStandardPaths::Get();
298 // it so happens that user data directory is a subdirectory of user config
299 // directory on all supported platforms, which explains why we use it here
300 return style
& wxCONFIG_USE_SUBDIR
? stdp
.GetUserDataDir()
301 : stdp
.GetUserConfigDir();
304 wxFileName
wxFileConfig::GetGlobalFile(const wxString
& szFile
)
306 wxFileName
fn(GetGlobalDir(), szFile
);
308 AddConfFileExtIfNeeded(fn
);
313 wxFileName
wxFileConfig::GetLocalFile(const wxString
& szFile
, int style
)
315 wxFileName
fn(GetLocalDir(style
), szFile
);
318 if ( !(style
& wxCONFIG_USE_SUBDIR
) )
320 // dot-files under Unix start with, well, a dot (but OTOH they usually
321 // don't have any specific extension)
322 fn
.SetName(wxT('.') + fn
.GetName());
324 else // we do append ".conf" extension to config files in subdirectories
327 AddConfFileExtIfNeeded(fn
);
333 // ----------------------------------------------------------------------------
335 // ----------------------------------------------------------------------------
336 IMPLEMENT_ABSTRACT_CLASS(wxFileConfig
, wxConfigBase
)
338 void wxFileConfig::Init()
341 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
346 // It's not an error if (one of the) file(s) doesn't exist.
348 // parse the global file
349 if ( m_fnGlobalFile
.IsOk() && m_fnGlobalFile
.FileExists() )
351 wxTextFile
fileGlobal(m_fnGlobalFile
.GetFullPath());
353 if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) )
355 Parse(fileGlobal
, false /* global */);
360 wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile
.GetFullPath().c_str());
364 // parse the local file
365 if ( m_fnLocalFile
.IsOk() && m_fnLocalFile
.FileExists() )
367 wxTextFile
fileLocal(m_fnLocalFile
.GetFullPath());
368 if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) )
370 Parse(fileLocal
, true /* local */);
375 const wxString path
= m_fnLocalFile
.GetFullPath();
376 wxLogWarning(_("can't open user configuration file '%s'."),
379 if ( m_fnLocalFile
.FileExists() )
381 wxLogWarning(_("Changes won't be saved to avoid overwriting the existing file \"%s\""),
383 m_fnLocalFile
.Clear();
391 // constructor supports creation of wxFileConfig objects of any type
392 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
393 const wxString
& strLocal
, const wxString
& strGlobal
,
395 const wxMBConv
& conv
)
396 : wxConfigBase(::GetAppName(appName
), vendorName
,
399 m_fnLocalFile(strLocal
),
400 m_fnGlobalFile(strGlobal
),
403 // Make up names for files if empty
404 if ( !m_fnLocalFile
.IsOk() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
405 m_fnLocalFile
= GetLocalFile(GetAppName(), style
);
407 if ( !m_fnGlobalFile
.IsOk() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
408 m_fnGlobalFile
= GetGlobalFile(GetAppName());
410 // Check if styles are not supplied, but filenames are, in which case
411 // add the correct styles.
412 if ( m_fnLocalFile
.IsOk() )
413 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
415 if ( m_fnGlobalFile
.IsOk() )
416 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
418 // if the path is not absolute, prepend the standard directory to it
419 // unless explicitly asked not to
420 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
422 if ( m_fnLocalFile
.IsOk() )
423 m_fnLocalFile
.MakeAbsolute(GetLocalDir(style
));
425 if ( m_fnGlobalFile
.IsOk() )
426 m_fnGlobalFile
.MakeAbsolute(GetGlobalDir());
436 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, const wxMBConv
& conv
)
437 : m_conv(conv
.Clone())
439 // always local_file when this constructor is called (?)
440 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
443 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
448 // read the entire stream contents in memory
450 static const size_t chunkLen
= 1024;
452 wxMemoryBuffer
buf(chunkLen
);
455 inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
);
456 buf
.UngetAppendBuf(inStream
.LastRead());
458 const wxStreamError err
= inStream
.GetLastError();
460 if ( err
!= wxSTREAM_NO_ERROR
&& err
!= wxSTREAM_EOF
)
462 wxLogError(_("Error reading config options."));
466 while ( !inStream
.Eof() );
470 cbuf
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen() + 1, &len
);
471 if ( !len
&& buf
.GetDataLen() )
473 wxLogError(_("Failed to read config options."));
475 #else // !wxUSE_UNICODE
476 // no need for conversion
477 cbuf
= wxCharBuffer::CreateNonOwned((char *)buf
.GetData());
478 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
481 // now break it into lines
482 wxMemoryText memText
;
483 for ( const wxChar
*s
= cbuf
; ; ++s
)
486 while ( *e
!= '\0' && *e
!= '\n' && *e
!= '\r' )
489 // notice that we throw away the original EOL kind here, maybe we
490 // should preserve it?
492 memText
.AddLine(wxString(s
, e
));
497 // skip the second EOL byte if it's a DOS one
498 if ( *e
== '\r' && e
[1] == '\n' )
504 // Finally we can parse it all.
505 Parse(memText
, true /* local */);
511 #endif // wxUSE_STREAMS
513 void wxFileConfig::CleanUp()
517 wxFileConfigLineList
*pCur
= m_linesHead
;
518 while ( pCur
!= NULL
) {
519 wxFileConfigLineList
*pNext
= pCur
->Next();
525 wxFileConfig::~wxFileConfig()
534 // ----------------------------------------------------------------------------
535 // parse a config file
536 // ----------------------------------------------------------------------------
538 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
541 size_t nLineCount
= buffer
.GetLineCount();
543 for ( size_t n
= 0; n
< nLineCount
; n
++ )
545 wxString strLine
= buffer
[n
];
546 // FIXME-UTF8: rewrite using iterators, without this buffer
547 wxWxCharBuffer
buf(strLine
.c_str());
548 const wxChar
*pStart
;
551 // add the line to linked list
554 LineListAppend(strLine
);
556 // let the root group have its start line as well
559 m_pCurrentGroup
->SetLine(m_linesTail
);
564 // skip leading spaces
565 for ( pStart
= buf
; wxIsspace(*pStart
); pStart
++ )
568 // skip blank/comment lines
569 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
572 if ( *pStart
== wxT('[') ) { // a new group
575 while ( *++pEnd
!= wxT(']') ) {
576 if ( *pEnd
== wxT('\\') ) {
577 // the next char is escaped, so skip it even if it is ']'
581 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
582 // we reached the end of line, break out of the loop
587 if ( *pEnd
!= wxT(']') ) {
588 wxLogError(_("file '%s': unexpected character %c at line %d."),
589 buffer
.GetName(), *pEnd
, n
+ 1);
590 continue; // skip this line
593 // group name here is always considered as abs path
596 strGroup
<< wxCONFIG_PATH_SEPARATOR
597 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
599 // will create it if doesn't yet exist
604 if ( m_pCurrentGroup
->Parent() )
605 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
606 m_pCurrentGroup
->SetLine(m_linesTail
);
609 // check that there is nothing except comments left on this line
611 while ( *++pEnd
!= wxT('\0') && bCont
) {
620 // ignore whitespace ('\n' impossible here)
624 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
625 buffer
.GetName(), n
+ 1, pEnd
);
632 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
633 if ( *pEnd
== wxT('\\') ) {
634 // next character may be space or not - still take it because it's
635 // quoted (unless there is nothing)
638 // the error message will be given below anyhow
646 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
649 while ( wxIsspace(*pEnd
) )
652 if ( *pEnd
++ != wxT('=') ) {
653 wxLogError(_("file '%s', line %d: '=' expected."),
654 buffer
.GetName(), n
+ 1);
657 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
659 if ( pEntry
== NULL
) {
661 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
664 if ( bLocal
&& pEntry
->IsImmutable() ) {
665 // immutable keys can't be changed by user
666 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
667 buffer
.GetName(), n
+ 1, strKey
.c_str());
670 // the condition below catches the cases (a) and (b) but not (c):
671 // (a) global key found second time in global file
672 // (b) key found second (or more) time in local file
673 // (c) key from global file now found in local one
674 // which is exactly what we want.
675 else if ( !bLocal
|| pEntry
->IsLocal() ) {
676 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
677 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
683 pEntry
->SetLine(m_linesTail
);
686 while ( wxIsspace(*pEnd
) )
689 wxString value
= pEnd
;
690 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
691 value
= FilterInValue(value
);
693 pEntry
->SetValue(value
, false);
699 // ----------------------------------------------------------------------------
701 // ----------------------------------------------------------------------------
703 void wxFileConfig::SetRootPath()
706 m_pCurrentGroup
= m_pRootGroup
;
710 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
712 wxArrayString aParts
;
714 if ( strPath
.empty() ) {
719 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
721 wxSplitPath(aParts
, strPath
);
724 // relative path, combine with current one
725 wxString strFullPath
= m_strPath
;
726 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
727 wxSplitPath(aParts
, strFullPath
);
730 // change current group
732 m_pCurrentGroup
= m_pRootGroup
;
733 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
734 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
735 if ( pNextGroup
== NULL
)
737 if ( !createMissingComponents
)
740 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
743 m_pCurrentGroup
= pNextGroup
;
746 // recombine path parts in one variable
748 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
749 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
755 void wxFileConfig::SetPath(const wxString
& strPath
)
757 DoSetPath(strPath
, true /* create missing path components */);
760 // ----------------------------------------------------------------------------
762 // ----------------------------------------------------------------------------
764 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
767 return GetNextGroup(str
, lIndex
);
770 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
772 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) {
773 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
780 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
783 return GetNextEntry(str
, lIndex
);
786 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
788 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) {
789 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
796 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
798 size_t n
= m_pCurrentGroup
->Entries().GetCount();
800 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
801 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
802 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
803 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
804 n
+= GetNumberOfEntries(true);
805 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
812 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
814 size_t n
= m_pCurrentGroup
->Groups().GetCount();
816 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
817 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
818 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
819 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
820 n
+= GetNumberOfGroups(true);
821 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
828 // ----------------------------------------------------------------------------
829 // tests for existence
830 // ----------------------------------------------------------------------------
832 bool wxFileConfig::HasGroup(const wxString
& strName
) const
834 // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
835 // but there is no group with empty name so treat this separately
836 if ( strName
.empty() )
839 const wxString pathOld
= GetPath();
841 wxFileConfig
*self
= wx_const_cast(wxFileConfig
*, this);
843 rc
= self
->DoSetPath(strName
, false /* don't create missing components */);
845 self
->SetPath(pathOld
);
850 bool wxFileConfig::HasEntry(const wxString
& entry
) const
852 // path is the part before the last "/"
853 wxString path
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
);
855 // except in the special case of "/keyname" when there is nothing before "/"
856 if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR
)
858 path
= wxCONFIG_PATH_SEPARATOR
;
861 // change to the path of the entry if necessary and remember the old path
862 // to restore it later
864 wxFileConfig
* const self
= wx_const_cast(wxFileConfig
*, this);
868 if ( pathOld
.empty() )
869 pathOld
= wxCONFIG_PATH_SEPARATOR
;
871 if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) )
877 // check if the entry exists in this group
878 const bool exists
= m_pCurrentGroup
->FindEntry(
879 entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
;
881 // restore the old path if we changed it above
882 if ( !pathOld
.empty() )
884 self
->SetPath(pathOld
);
890 // ----------------------------------------------------------------------------
892 // ----------------------------------------------------------------------------
894 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
896 wxConfigPathChanger
path(this, key
);
898 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
899 if (pEntry
== NULL
) {
903 *pStr
= pEntry
->Value();
908 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
911 if ( !Read(key
, &str
) )
914 // extra spaces shouldn't prevent us from reading numeric values
917 return str
.ToLong(pl
);
922 bool wxFileConfig::DoReadBinary(const wxString
& key
, wxMemoryBuffer
* buf
) const
924 wxCHECK_MSG( buf
, false, _T("NULL buffer") );
927 if ( !Read(key
, &str
) )
930 *buf
= wxBase64Decode(str
.ToAscii());
934 #endif // wxUSE_BASE64
936 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
938 wxConfigPathChanger
path(this, key
);
939 wxString strName
= path
.Name();
941 wxLogTrace( FILECONF_TRACE_MASK
,
942 _T(" Writing String '%s' = '%s' to Group '%s'"),
947 if ( strName
.empty() )
949 // setting the value of a group is an error
951 wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") );
953 // ... except if it's empty in which case it's a way to force it's creation
955 wxLogTrace( FILECONF_TRACE_MASK
,
956 _T(" Creating group %s"),
957 m_pCurrentGroup
->Name().c_str() );
961 // this will add a line for this group if it didn't have it before
963 (void)m_pCurrentGroup
->GetGroupLine();
967 // writing an entry check that the name is reasonable
968 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
970 wxLogError( _("Config entry name cannot start with '%c'."),
971 wxCONFIG_IMMUTABLE_PREFIX
);
975 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
979 wxLogTrace( FILECONF_TRACE_MASK
,
980 _T(" Adding Entry %s"),
982 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
985 wxLogTrace( FILECONF_TRACE_MASK
,
986 _T(" Setting value %s"),
988 pEntry
->SetValue(szValue
);
996 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
998 return Write(key
, wxString::Format(_T("%ld"), lValue
));
1003 bool wxFileConfig::DoWriteBinary(const wxString
& key
, const wxMemoryBuffer
& buf
)
1005 return Write(key
, wxBase64Encode(buf
));
1008 #endif // wxUSE_BASE64
1010 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
1012 if ( !IsDirty() || !m_fnLocalFile
.GetFullPath() )
1015 // set the umask if needed
1016 wxCHANGE_UMASK(m_umask
);
1018 wxTempFile
file(m_fnLocalFile
.GetFullPath());
1020 if ( !file
.IsOpened() )
1022 wxLogError(_("can't open user configuration file."));
1026 // write all strings to file
1028 filetext
.reserve(4096);
1029 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1031 filetext
<< p
->Text() << wxTextFile::GetEOL();
1034 if ( !file
.Write(filetext
, *m_conv
) )
1036 wxLogError(_("can't write user configuration file."));
1040 if ( !file
.Commit() )
1042 wxLogError(_("Failed to update user configuration file."));
1049 #if defined(__WXMAC__)
1050 m_fnLocalFile
.MacSetTypeAndCreator('TEXT', 'ttxt');
1058 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
)
1060 // save unconditionally, even if not dirty
1061 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1063 wxString line
= p
->Text();
1064 line
+= wxTextFile::GetEOL();
1066 wxCharBuffer
buf(line
.mb_str(conv
));
1067 if ( !os
.Write(buf
, strlen(buf
)) )
1069 wxLogError(_("Error saving user configuration data."));
1080 #endif // wxUSE_STREAMS
1082 // ----------------------------------------------------------------------------
1083 // renaming groups/entries
1084 // ----------------------------------------------------------------------------
1086 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1087 const wxString
& newName
)
1089 wxASSERT_MSG( oldName
.find(wxCONFIG_PATH_SEPARATOR
) == wxString::npos
,
1090 _T("RenameEntry(): paths are not supported") );
1092 // check that the entry exists
1093 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1097 // check that the new entry doesn't already exist
1098 if ( m_pCurrentGroup
->FindEntry(newName
) )
1101 // delete the old entry, create the new one
1102 wxString value
= oldEntry
->Value();
1103 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1108 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1109 newEntry
->SetValue(value
);
1114 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1115 const wxString
& newName
)
1117 // check that the group exists
1118 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1122 // check that the new group doesn't already exist
1123 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1126 group
->Rename(newName
);
1133 // ----------------------------------------------------------------------------
1134 // delete groups/entries
1135 // ----------------------------------------------------------------------------
1137 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1139 wxConfigPathChanger
path(this, key
);
1141 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1146 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1147 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1148 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1149 SetPath(wxT("..")); // changes m_pCurrentGroup!
1150 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1152 //else: never delete the root group
1158 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1160 wxConfigPathChanger
path(this, RemoveTrailingSeparator(key
));
1162 if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) )
1165 path
.UpdateIfDeleted();
1172 bool wxFileConfig::DeleteAll()
1176 if ( m_fnLocalFile
.IsOk() )
1178 if ( m_fnLocalFile
.FileExists() &&
1179 !wxRemoveFile(m_fnLocalFile
.GetFullPath()) )
1181 wxLogSysError(_("can't delete user configuration file '%s'"),
1182 m_fnLocalFile
.GetFullPath().c_str());
1192 // ----------------------------------------------------------------------------
1193 // linked list functions
1194 // ----------------------------------------------------------------------------
1196 // append a new line to the end of the list
1198 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1200 wxLogTrace( FILECONF_TRACE_MASK
,
1201 _T(" ** Adding Line '%s'"),
1203 wxLogTrace( FILECONF_TRACE_MASK
,
1205 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1207 wxLogTrace( FILECONF_TRACE_MASK
,
1209 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1212 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1214 if ( m_linesTail
== NULL
)
1217 m_linesHead
= pLine
;
1222 m_linesTail
->SetNext(pLine
);
1223 pLine
->SetPrev(m_linesTail
);
1226 m_linesTail
= pLine
;
1228 wxLogTrace( FILECONF_TRACE_MASK
,
1230 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1232 wxLogTrace( FILECONF_TRACE_MASK
,
1234 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1240 // insert a new line after the given one or in the very beginning if !pLine
1241 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1242 wxFileConfigLineList
*pLine
)
1244 wxLogTrace( FILECONF_TRACE_MASK
,
1245 _T(" ** Inserting Line '%s' after '%s'"),
1247 ((pLine
) ? (const wxChar
*)pLine
->Text().c_str()
1249 wxLogTrace( FILECONF_TRACE_MASK
,
1251 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1253 wxLogTrace( FILECONF_TRACE_MASK
,
1255 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1258 if ( pLine
== m_linesTail
)
1259 return LineListAppend(str
);
1261 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1262 if ( pLine
== NULL
)
1264 // prepend to the list
1265 pNewLine
->SetNext(m_linesHead
);
1266 m_linesHead
->SetPrev(pNewLine
);
1267 m_linesHead
= pNewLine
;
1271 // insert before pLine
1272 wxFileConfigLineList
*pNext
= pLine
->Next();
1273 pNewLine
->SetNext(pNext
);
1274 pNewLine
->SetPrev(pLine
);
1275 pNext
->SetPrev(pNewLine
);
1276 pLine
->SetNext(pNewLine
);
1279 wxLogTrace( FILECONF_TRACE_MASK
,
1281 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1283 wxLogTrace( FILECONF_TRACE_MASK
,
1285 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1291 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1293 wxLogTrace( FILECONF_TRACE_MASK
,
1294 _T(" ** Removing Line '%s'"),
1295 pLine
->Text().c_str() );
1296 wxLogTrace( FILECONF_TRACE_MASK
,
1298 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1300 wxLogTrace( FILECONF_TRACE_MASK
,
1302 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1305 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1306 *pNext
= pLine
->Next();
1310 if ( pPrev
== NULL
)
1311 m_linesHead
= pNext
;
1313 pPrev
->SetNext(pNext
);
1317 if ( pNext
== NULL
)
1318 m_linesTail
= pPrev
;
1320 pNext
->SetPrev(pPrev
);
1322 if ( m_pRootGroup
->GetGroupLine() == pLine
)
1323 m_pRootGroup
->SetLine(m_linesHead
);
1325 wxLogTrace( FILECONF_TRACE_MASK
,
1327 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1329 wxLogTrace( FILECONF_TRACE_MASK
,
1331 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1337 bool wxFileConfig::LineListIsEmpty()
1339 return m_linesHead
== NULL
;
1342 // ============================================================================
1343 // wxFileConfig::wxFileConfigGroup
1344 // ============================================================================
1346 // ----------------------------------------------------------------------------
1348 // ----------------------------------------------------------------------------
1351 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1352 const wxString
& strName
,
1353 wxFileConfig
*pConfig
)
1354 : m_aEntries(CompareEntries
),
1355 m_aSubgroups(CompareGroups
),
1358 m_pConfig
= pConfig
;
1359 m_pParent
= pParent
;
1362 m_pLastEntry
= NULL
;
1363 m_pLastGroup
= NULL
;
1366 // dtor deletes all children
1367 wxFileConfigGroup::~wxFileConfigGroup()
1370 size_t n
, nCount
= m_aEntries
.GetCount();
1371 for ( n
= 0; n
< nCount
; n
++ )
1372 delete m_aEntries
[n
];
1375 nCount
= m_aSubgroups
.GetCount();
1376 for ( n
= 0; n
< nCount
; n
++ )
1377 delete m_aSubgroups
[n
];
1380 // ----------------------------------------------------------------------------
1382 // ----------------------------------------------------------------------------
1384 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1386 // for a normal (i.e. not root) group this method shouldn't be called twice
1387 // unless we are resetting the line
1388 wxASSERT_MSG( !m_pParent
|| !m_pLine
|| !pLine
,
1389 _T("changing line for a non-root group?") );
1395 This is a bit complicated, so let me explain it in details. All lines that
1396 were read from the local file (the only one we will ever modify) are stored
1397 in a (doubly) linked list. Our problem is to know at which position in this
1398 list should we insert the new entries/subgroups. To solve it we keep three
1399 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1401 m_pLine points to the line containing "[group_name]"
1402 m_pLastEntry points to the last entry of this group in the local file.
1403 m_pLastGroup subgroup
1405 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1406 from the local file, the corresponding variable is set. However, if the group
1407 was read from the global file and then modified or created by the application
1408 these variables are still NULL and we need to create the corresponding lines.
1409 See the following functions (and comments preceding them) for the details of
1412 Also, when our last entry/group are deleted we need to find the new last
1413 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1414 of lines until it either founds an entry/subgroup (and this is the new last
1415 element) or the m_pLine of the group, in which case there are no more entries
1416 (or subgroups) left and m_pLast<element> becomes NULL.
1418 NB: This last problem could be avoided for entries if we added new entries
1419 immediately after m_pLine, but in this case the entries would appear
1420 backwards in the config file (OTOH, it's not that important) and as we
1421 would still need to do it for the subgroups the code wouldn't have been
1422 significantly less complicated.
1425 // Return the line which contains "[our name]". If we're still not in the list,
1426 // add our line to it immediately after the last line of our parent group if we
1427 // have it or in the very beginning if we're the root group.
1428 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1430 wxLogTrace( FILECONF_TRACE_MASK
,
1431 _T(" GetGroupLine() for Group '%s'"),
1436 wxLogTrace( FILECONF_TRACE_MASK
,
1437 _T(" Getting Line item pointer") );
1439 wxFileConfigGroup
*pParent
= Parent();
1441 // this group wasn't present in local config file, add it now
1444 wxLogTrace( FILECONF_TRACE_MASK
,
1445 _T(" checking parent '%s'"),
1446 pParent
->Name().c_str() );
1448 wxString strFullName
;
1450 // add 1 to the name because we don't want to start with '/'
1451 strFullName
<< wxT("[")
1452 << FilterOutEntryName(GetFullName().c_str() + 1)
1454 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1455 pParent
->GetLastGroupLine());
1456 pParent
->SetLastGroup(this); // we're surely after all the others
1458 //else: this is the root group and so we return NULL because we don't
1459 // have any group line
1465 // Return the last line belonging to the subgroups of this group (after which
1466 // we can add a new subgroup), if we don't have any subgroups or entries our
1467 // last line is the group line (m_pLine) itself.
1468 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1470 // if we have any subgroups, our last line is the last line of the last
1474 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1476 wxASSERT_MSG( pLine
, _T("last group must have !NULL associated line") );
1481 // no subgroups, so the last line is the line of thelast entry (if any)
1482 return GetLastEntryLine();
1485 // return the last line belonging to the entries of this group (after which
1486 // we can add a new entry), if we don't have any entries we will add the new
1487 // one immediately after the group line itself.
1488 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1490 wxLogTrace( FILECONF_TRACE_MASK
,
1491 _T(" GetLastEntryLine() for Group '%s'"),
1496 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1498 wxASSERT_MSG( pLine
, _T("last entry must have !NULL associated line") );
1503 // no entries: insert after the group header, if any
1504 return GetGroupLine();
1507 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry
*pEntry
)
1509 m_pLastEntry
= pEntry
;
1513 // the only situation in which a group without its own line can have
1514 // an entry is when the first entry is added to the initially empty
1515 // root pseudo-group
1516 wxASSERT_MSG( !m_pParent
, _T("unexpected for non root group") );
1518 // let the group know that it does have a line in the file now
1519 m_pLine
= pEntry
->GetLine();
1523 // ----------------------------------------------------------------------------
1525 // ----------------------------------------------------------------------------
1527 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
1529 // update the line of this group
1530 wxFileConfigLineList
*line
= GetGroupLine();
1531 wxCHECK_RET( line
, _T("a non root group must have a corresponding line!") );
1533 // +1: skip the leading '/'
1534 line
->SetText(wxString::Format(_T("[%s]"), GetFullName().c_str() + 1));
1537 // also update all subgroups as they have this groups name in their lines
1538 const size_t nCount
= m_aSubgroups
.GetCount();
1539 for ( size_t n
= 0; n
< nCount
; n
++ )
1541 m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines();
1545 void wxFileConfigGroup::Rename(const wxString
& newName
)
1547 wxCHECK_RET( m_pParent
, _T("the root group can't be renamed") );
1549 if ( newName
== m_strName
)
1552 // we need to remove the group from the parent and it back under the new
1553 // name to keep the parents array of subgroups alphabetically sorted
1554 m_pParent
->m_aSubgroups
.Remove(this);
1556 m_strName
= newName
;
1558 m_pParent
->m_aSubgroups
.Add(this);
1560 // update the group lines recursively
1561 UpdateGroupAndSubgroupsLines();
1564 wxString
wxFileConfigGroup::GetFullName() const
1568 fullname
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1573 // ----------------------------------------------------------------------------
1575 // ----------------------------------------------------------------------------
1577 // use binary search because the array is sorted
1579 wxFileConfigGroup::FindEntry(const wxString
& name
) const
1583 hi
= m_aEntries
.GetCount();
1585 wxFileConfigEntry
*pEntry
;
1589 pEntry
= m_aEntries
[i
];
1591 #if wxCONFIG_CASE_SENSITIVE
1592 res
= pEntry
->Name().compare(name
);
1594 res
= pEntry
->Name().CmpNoCase(name
);
1609 wxFileConfigGroup::FindSubgroup(const wxString
& name
) const
1613 hi
= m_aSubgroups
.GetCount();
1615 wxFileConfigGroup
*pGroup
;
1619 pGroup
= m_aSubgroups
[i
];
1621 #if wxCONFIG_CASE_SENSITIVE
1622 res
= pGroup
->Name().compare(name
);
1624 res
= pGroup
->Name().CmpNoCase(name
);
1638 // ----------------------------------------------------------------------------
1639 // create a new item
1640 // ----------------------------------------------------------------------------
1642 // create a new entry and add it to the current group
1643 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1645 wxASSERT( FindEntry(strName
) == 0 );
1647 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1649 m_aEntries
.Add(pEntry
);
1653 // create a new group and add it to the current group
1654 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1656 wxASSERT( FindSubgroup(strName
) == 0 );
1658 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1660 m_aSubgroups
.Add(pGroup
);
1664 // ----------------------------------------------------------------------------
1666 // ----------------------------------------------------------------------------
1669 The delete operations are _very_ slow if we delete the last item of this
1670 group (see comments before GetXXXLineXXX functions for more details),
1671 so it's much better to start with the first entry/group if we want to
1672 delete several of them.
1675 bool wxFileConfigGroup::DeleteSubgroupByName(const wxString
& name
)
1677 wxFileConfigGroup
* const pGroup
= FindSubgroup(name
);
1679 return pGroup
? DeleteSubgroup(pGroup
) : false;
1682 // Delete the subgroup and remove all references to it from
1683 // other data structures.
1684 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1686 wxCHECK_MSG( pGroup
, false, _T("deleting non existing group?") );
1688 wxLogTrace( FILECONF_TRACE_MASK
,
1689 _T("Deleting group '%s' from '%s'"),
1690 pGroup
->Name().c_str(),
1693 wxLogTrace( FILECONF_TRACE_MASK
,
1694 _T(" (m_pLine) = prev: %p, this %p, next %p"),
1695 m_pLine
? wx_static_cast(void*, m_pLine
->Prev()) : 0,
1696 wx_static_cast(void*, m_pLine
),
1697 m_pLine
? wx_static_cast(void*, m_pLine
->Next()) : 0 );
1698 wxLogTrace( FILECONF_TRACE_MASK
,
1700 m_pLine
? (const wxChar
*)m_pLine
->Text().c_str()
1703 // delete all entries...
1704 size_t nCount
= pGroup
->m_aEntries
.GetCount();
1706 wxLogTrace(FILECONF_TRACE_MASK
,
1707 _T("Removing %lu entries"), (unsigned long)nCount
);
1709 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1711 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1715 wxLogTrace( FILECONF_TRACE_MASK
,
1717 pLine
->Text().c_str() );
1718 m_pConfig
->LineListRemove(pLine
);
1722 // ...and subgroups of this subgroup
1723 nCount
= pGroup
->m_aSubgroups
.GetCount();
1725 wxLogTrace( FILECONF_TRACE_MASK
,
1726 _T("Removing %lu subgroups"), (unsigned long)nCount
);
1728 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1730 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1733 // and then finally the group itself
1734 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1737 wxLogTrace( FILECONF_TRACE_MASK
,
1738 _T(" Removing line for group '%s' : '%s'"),
1739 pGroup
->Name().c_str(),
1740 pLine
->Text().c_str() );
1741 wxLogTrace( FILECONF_TRACE_MASK
,
1742 _T(" Removing from group '%s' : '%s'"),
1744 ((m_pLine
) ? (const wxChar
*)m_pLine
->Text().c_str()
1747 // notice that we may do this test inside the previous "if"
1748 // because the last entry's line is surely !NULL
1749 if ( pGroup
== m_pLastGroup
)
1751 wxLogTrace( FILECONF_TRACE_MASK
,
1752 _T(" Removing last group") );
1754 // our last entry is being deleted, so find the last one which
1755 // stays by going back until we find a subgroup or reach the
1757 const size_t nSubgroups
= m_aSubgroups
.GetCount();
1759 m_pLastGroup
= NULL
;
1760 for ( wxFileConfigLineList
*pl
= pLine
->Prev();
1761 pl
&& !m_pLastGroup
;
1764 // does this line belong to our subgroup?
1765 for ( size_t n
= 0; n
< nSubgroups
; n
++ )
1767 // do _not_ call GetGroupLine! we don't want to add it to
1768 // the local file if it's not already there
1769 if ( m_aSubgroups
[n
]->m_pLine
== pl
)
1771 m_pLastGroup
= m_aSubgroups
[n
];
1776 if ( pl
== m_pLine
)
1781 m_pConfig
->LineListRemove(pLine
);
1785 wxLogTrace( FILECONF_TRACE_MASK
,
1786 _T(" No line entry for Group '%s'?"),
1787 pGroup
->Name().c_str() );
1790 m_aSubgroups
.Remove(pGroup
);
1796 bool wxFileConfigGroup::DeleteEntry(const wxString
& name
)
1798 wxFileConfigEntry
*pEntry
= FindEntry(name
);
1801 // entry doesn't exist, nothing to do
1805 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1806 if ( pLine
!= NULL
) {
1807 // notice that we may do this test inside the previous "if" because the
1808 // last entry's line is surely !NULL
1809 if ( pEntry
== m_pLastEntry
) {
1810 // our last entry is being deleted - find the last one which stays
1811 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1813 // go back until we find another entry or reach the group's line
1814 wxFileConfigEntry
*pNewLast
= NULL
;
1815 size_t n
, nEntries
= m_aEntries
.GetCount();
1816 wxFileConfigLineList
*pl
;
1817 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1818 // is it our subgroup?
1819 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1820 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1821 pNewLast
= m_aEntries
[n
];
1824 if ( pNewLast
!= NULL
) // found?
1828 if ( pl
== m_pLine
) {
1829 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1831 // we've reached the group line without finding any subgroups
1832 m_pLastEntry
= NULL
;
1835 m_pLastEntry
= pNewLast
;
1838 m_pConfig
->LineListRemove(pLine
);
1841 m_aEntries
.Remove(pEntry
);
1847 // ============================================================================
1848 // wxFileConfig::wxFileConfigEntry
1849 // ============================================================================
1851 // ----------------------------------------------------------------------------
1853 // ----------------------------------------------------------------------------
1854 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1855 const wxString
& strName
,
1857 : m_strName(strName
)
1859 wxASSERT( !strName
.empty() );
1861 m_pParent
= pParent
;
1865 m_bHasValue
= false;
1867 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1869 m_strName
.erase(0, 1); // remove first character
1872 // ----------------------------------------------------------------------------
1874 // ----------------------------------------------------------------------------
1876 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1878 if ( m_pLine
!= NULL
) {
1879 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1880 Name().c_str(), m_pParent
->GetFullName().c_str());
1884 Group()->SetLastEntry(this);
1887 // second parameter is false if we read the value from file and prevents the
1888 // entry from being marked as 'dirty'
1889 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1891 if ( bUser
&& IsImmutable() )
1893 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1898 // do nothing if it's the same value: but don't test for it if m_bHasValue
1899 // hadn't been set yet or we'd never write empty values to the file
1900 if ( m_bHasValue
&& strValue
== m_strValue
)
1904 m_strValue
= strValue
;
1908 wxString strValFiltered
;
1910 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1912 strValFiltered
= strValue
;
1915 strValFiltered
= FilterOutValue(strValue
);
1919 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1923 // entry was read from the local config file, just modify the line
1924 m_pLine
->SetText(strLine
);
1926 else // this entry didn't exist in the local file
1928 // add a new line to the file: note that line returned by
1929 // GetLastEntryLine() may be NULL if we're in the root group and it
1930 // doesn't have any entries yet, but this is ok as passing NULL
1931 // line to LineListInsert() means to prepend new line to the list
1932 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1933 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1935 Group()->SetLastEntry(this);
1940 // ============================================================================
1942 // ============================================================================
1944 // ----------------------------------------------------------------------------
1945 // compare functions for array sorting
1946 // ----------------------------------------------------------------------------
1948 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1950 #if wxCONFIG_CASE_SENSITIVE
1951 return p1
->Name().compare(p2
->Name());
1953 return p1
->Name().CmpNoCase(p2
->Name());
1957 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1959 #if wxCONFIG_CASE_SENSITIVE
1960 return p1
->Name().compare(p2
->Name());
1962 return p1
->Name().CmpNoCase(p2
->Name());
1966 // ----------------------------------------------------------------------------
1968 // ----------------------------------------------------------------------------
1970 // undo FilterOutValue
1971 static wxString
FilterInValue(const wxString
& str
)
1977 strResult
.reserve(str
.length());
1979 wxString::const_iterator i
= str
.begin();
1980 const bool bQuoted
= *i
== '"';
1984 for ( const wxString::const_iterator end
= str
.end(); i
!= end
; ++i
)
1986 if ( *i
== wxT('\\') )
1990 wxLogWarning(_("trailing backslash ignored in '%s'"), str
.c_str());
1994 switch ( (*i
).GetValue() )
1997 strResult
+= wxT('\n');
2001 strResult
+= wxT('\r');
2005 strResult
+= wxT('\t');
2009 strResult
+= wxT('\\');
2013 strResult
+= wxT('"');
2017 else // not a backslash
2019 if ( *i
!= wxT('"') || !bQuoted
)
2023 else if ( i
!= end
- 1 )
2025 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2026 i
- str
.begin(), str
.c_str());
2028 //else: it's the last quote of a quoted string, ok
2035 // quote the string before writing it to file
2036 static wxString
FilterOutValue(const wxString
& str
)
2042 strResult
.Alloc(str
.Len());
2044 // quoting is necessary to preserve spaces in the beginning of the string
2045 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2048 strResult
+= wxT('"');
2051 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2052 switch ( str
[n
].GetValue() ) {
2074 //else: fall through
2077 strResult
+= str
[n
];
2078 continue; // nothing special to do
2081 // we get here only for special characters
2082 strResult
<< wxT('\\') << c
;
2086 strResult
+= wxT('"');
2091 // undo FilterOutEntryName
2092 static wxString
FilterInEntryName(const wxString
& str
)
2095 strResult
.Alloc(str
.Len());
2097 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2098 if ( *pc
== wxT('\\') ) {
2099 // we need to test it here or we'd skip past the NUL in the loop line
2100 if ( *++pc
== _T('\0') )
2110 // sanitize entry or group name: insert '\\' before any special characters
2111 static wxString
FilterOutEntryName(const wxString
& str
)
2114 strResult
.Alloc(str
.Len());
2116 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2117 const wxChar c
= *pc
;
2119 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2120 // which will probably never have special meaning and with which we can't
2121 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2123 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2124 // should *not* be quoted
2127 ((unsigned char)c
< 127) &&
2129 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) )
2131 strResult
+= wxT('\\');
2140 // we can't put ?: in the ctor initializer list because it confuses some
2141 // broken compilers (Borland C++)
2142 static wxString
GetAppName(const wxString
& appName
)
2144 if ( !appName
&& wxTheApp
)
2145 return wxTheApp
->GetAppName();
2150 #endif // wxUSE_CONFIG