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 // may be NULL for "/" only
249 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
250 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
252 // called by entries/subgroups when they're created/deleted
253 void SetLastEntry(wxFileConfigEntry
*pEntry
);
254 void SetLastGroup(wxFileConfigGroup
*pGroup
)
255 { m_pLastGroup
= pGroup
; }
257 DECLARE_NO_COPY_CLASS(wxFileConfigGroup
)
260 // ============================================================================
262 // ============================================================================
264 // ----------------------------------------------------------------------------
266 // ----------------------------------------------------------------------------
268 // this function modifies in place the given wxFileName object if it doesn't
269 // already have an extension
271 // note that it's slightly misnamed under Mac as there it doesn't add an
272 // extension but modifies the file name instead, so you shouldn't suppose that
273 // fn.HasExt() is true after it returns
274 static void AddConfFileExtIfNeeded(wxFileName
& fn
)
278 #if defined( __WXMAC__ )
279 fn
.SetName(fn
.GetName() + wxT(" Preferences"));
280 #elif defined( __UNIX__ )
281 fn
.SetExt(wxT(".conf"));
283 fn
.SetExt(wxT(".ini"));
288 wxString
wxFileConfig::GetGlobalDir()
290 return wxStandardPaths::Get().GetConfigDir();
293 wxString
wxFileConfig::GetLocalDir(int style
)
297 wxStandardPathsBase
& stdp
= wxStandardPaths::Get();
299 // it so happens that user data directory is a subdirectory of user config
300 // directory on all supported platforms, which explains why we use it here
301 return style
& wxCONFIG_USE_SUBDIR
? stdp
.GetUserDataDir()
302 : stdp
.GetUserConfigDir();
305 wxFileName
wxFileConfig::GetGlobalFile(const wxString
& szFile
)
307 wxFileName
fn(GetGlobalDir(), szFile
);
309 AddConfFileExtIfNeeded(fn
);
314 wxFileName
wxFileConfig::GetLocalFile(const wxString
& szFile
, int style
)
316 wxFileName
fn(GetLocalDir(style
), szFile
);
319 if ( !(style
& wxCONFIG_USE_SUBDIR
) )
321 // dot-files under Unix start with, well, a dot (but OTOH they usually
322 // don't have any specific extension)
323 fn
.SetName(wxT('.') + fn
.GetName());
325 else // we do append ".conf" extension to config files in subdirectories
328 AddConfFileExtIfNeeded(fn
);
334 // ----------------------------------------------------------------------------
336 // ----------------------------------------------------------------------------
337 IMPLEMENT_ABSTRACT_CLASS(wxFileConfig
, wxConfigBase
)
339 void wxFileConfig::Init()
342 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
347 // It's not an error if (one of the) file(s) doesn't exist.
349 // parse the global file
350 if ( m_fnGlobalFile
.IsOk() && m_fnGlobalFile
.FileExists() )
352 wxTextFile
fileGlobal(m_fnGlobalFile
.GetFullPath());
354 if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) )
356 Parse(fileGlobal
, false /* global */);
361 wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile
.GetFullPath().c_str());
365 // parse the local file
366 if ( m_fnLocalFile
.IsOk() && m_fnLocalFile
.FileExists() )
368 wxTextFile
fileLocal(m_fnLocalFile
.GetFullPath());
369 if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) )
371 Parse(fileLocal
, true /* local */);
376 const wxString path
= m_fnLocalFile
.GetFullPath();
377 wxLogWarning(_("can't open user configuration file '%s'."),
380 if ( m_fnLocalFile
.FileExists() )
382 wxLogWarning(_("Changes won't be saved to avoid overwriting the existing file \"%s\""),
384 m_fnLocalFile
.Clear();
392 // constructor supports creation of wxFileConfig objects of any type
393 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
394 const wxString
& strLocal
, const wxString
& strGlobal
,
396 const wxMBConv
& conv
)
397 : wxConfigBase(::GetAppName(appName
), vendorName
,
400 m_fnLocalFile(strLocal
),
401 m_fnGlobalFile(strGlobal
),
404 // Make up names for files if empty
405 if ( !m_fnLocalFile
.IsOk() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
406 m_fnLocalFile
= GetLocalFile(GetAppName(), style
);
408 if ( !m_fnGlobalFile
.IsOk() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
409 m_fnGlobalFile
= GetGlobalFile(GetAppName());
411 // Check if styles are not supplied, but filenames are, in which case
412 // add the correct styles.
413 if ( m_fnLocalFile
.IsOk() )
414 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
416 if ( m_fnGlobalFile
.IsOk() )
417 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
419 // if the path is not absolute, prepend the standard directory to it
420 // unless explicitly asked not to
421 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
423 if ( m_fnLocalFile
.IsOk() )
424 m_fnLocalFile
.MakeAbsolute(GetLocalDir(style
));
426 if ( m_fnGlobalFile
.IsOk() )
427 m_fnGlobalFile
.MakeAbsolute(GetGlobalDir());
437 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, const wxMBConv
& conv
)
438 : m_conv(conv
.Clone())
440 // always local_file when this constructor is called (?)
441 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
444 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
449 // read the entire stream contents in memory
451 static const size_t chunkLen
= 1024;
453 wxMemoryBuffer
buf(chunkLen
);
456 inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
);
457 buf
.UngetAppendBuf(inStream
.LastRead());
459 const wxStreamError err
= inStream
.GetLastError();
461 if ( err
!= wxSTREAM_NO_ERROR
&& err
!= wxSTREAM_EOF
)
463 wxLogError(_("Error reading config options."));
467 while ( !inStream
.Eof() );
471 cbuf
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen() + 1, &len
);
472 if ( !len
&& buf
.GetDataLen() )
474 wxLogError(_("Failed to read config options."));
476 #else // !wxUSE_UNICODE
477 // no need for conversion
478 cbuf
= wxCharBuffer::CreateNonOwned((char *)buf
.GetData());
479 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
482 // now break it into lines
483 wxMemoryText memText
;
484 for ( const wxChar
*s
= cbuf
; ; ++s
)
487 while ( *e
!= '\0' && *e
!= '\n' && *e
!= '\r' )
490 // notice that we throw away the original EOL kind here, maybe we
491 // should preserve it?
493 memText
.AddLine(wxString(s
, e
));
498 // skip the second EOL byte if it's a DOS one
499 if ( *e
== '\r' && e
[1] == '\n' )
505 // Finally we can parse it all.
506 Parse(memText
, true /* local */);
512 #endif // wxUSE_STREAMS
514 void wxFileConfig::CleanUp()
518 wxFileConfigLineList
*pCur
= m_linesHead
;
519 while ( pCur
!= NULL
) {
520 wxFileConfigLineList
*pNext
= pCur
->Next();
526 wxFileConfig::~wxFileConfig()
535 // ----------------------------------------------------------------------------
536 // parse a config file
537 // ----------------------------------------------------------------------------
539 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
542 size_t nLineCount
= buffer
.GetLineCount();
544 for ( size_t n
= 0; n
< nLineCount
; n
++ )
546 wxString strLine
= buffer
[n
];
547 // FIXME-UTF8: rewrite using iterators, without this buffer
548 wxWxCharBuffer
buf(strLine
.c_str());
549 const wxChar
*pStart
;
552 // add the line to linked list
554 LineListAppend(strLine
);
557 // skip leading spaces
558 for ( pStart
= buf
; wxIsspace(*pStart
); pStart
++ )
561 // skip blank/comment lines
562 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
565 if ( *pStart
== wxT('[') ) { // a new group
568 while ( *++pEnd
!= wxT(']') ) {
569 if ( *pEnd
== wxT('\\') ) {
570 // the next char is escaped, so skip it even if it is ']'
574 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
575 // we reached the end of line, break out of the loop
580 if ( *pEnd
!= wxT(']') ) {
581 wxLogError(_("file '%s': unexpected character %c at line %d."),
582 buffer
.GetName(), *pEnd
, n
+ 1);
583 continue; // skip this line
586 // group name here is always considered as abs path
589 strGroup
<< wxCONFIG_PATH_SEPARATOR
590 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
592 // will create it if doesn't yet exist
597 if ( m_pCurrentGroup
->Parent() )
598 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
599 m_pCurrentGroup
->SetLine(m_linesTail
);
602 // check that there is nothing except comments left on this line
604 while ( *++pEnd
!= wxT('\0') && bCont
) {
613 // ignore whitespace ('\n' impossible here)
617 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
618 buffer
.GetName(), n
+ 1, pEnd
);
625 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
626 if ( *pEnd
== wxT('\\') ) {
627 // next character may be space or not - still take it because it's
628 // quoted (unless there is nothing)
631 // the error message will be given below anyhow
639 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
642 while ( wxIsspace(*pEnd
) )
645 if ( *pEnd
++ != wxT('=') ) {
646 wxLogError(_("file '%s', line %d: '=' expected."),
647 buffer
.GetName(), n
+ 1);
650 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
652 if ( pEntry
== NULL
) {
654 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
657 if ( bLocal
&& pEntry
->IsImmutable() ) {
658 // immutable keys can't be changed by user
659 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
660 buffer
.GetName(), n
+ 1, strKey
.c_str());
663 // the condition below catches the cases (a) and (b) but not (c):
664 // (a) global key found second time in global file
665 // (b) key found second (or more) time in local file
666 // (c) key from global file now found in local one
667 // which is exactly what we want.
668 else if ( !bLocal
|| pEntry
->IsLocal() ) {
669 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
670 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
676 pEntry
->SetLine(m_linesTail
);
679 while ( wxIsspace(*pEnd
) )
682 wxString value
= pEnd
;
683 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
684 value
= FilterInValue(value
);
686 pEntry
->SetValue(value
, false);
692 // ----------------------------------------------------------------------------
694 // ----------------------------------------------------------------------------
696 void wxFileConfig::SetRootPath()
699 m_pCurrentGroup
= m_pRootGroup
;
703 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
705 wxArrayString aParts
;
707 if ( strPath
.empty() ) {
712 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
714 wxSplitPath(aParts
, strPath
);
717 // relative path, combine with current one
718 wxString strFullPath
= m_strPath
;
719 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
720 wxSplitPath(aParts
, strFullPath
);
723 // change current group
725 m_pCurrentGroup
= m_pRootGroup
;
726 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
727 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
728 if ( pNextGroup
== NULL
)
730 if ( !createMissingComponents
)
733 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
736 m_pCurrentGroup
= pNextGroup
;
739 // recombine path parts in one variable
741 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
742 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
748 void wxFileConfig::SetPath(const wxString
& strPath
)
750 DoSetPath(strPath
, true /* create missing path components */);
753 // ----------------------------------------------------------------------------
755 // ----------------------------------------------------------------------------
757 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
760 return GetNextGroup(str
, lIndex
);
763 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
765 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) {
766 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
773 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
776 return GetNextEntry(str
, lIndex
);
779 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
781 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) {
782 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
789 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
791 size_t n
= m_pCurrentGroup
->Entries().GetCount();
793 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
794 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
795 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
796 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
797 n
+= GetNumberOfEntries(true);
798 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
805 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
807 size_t n
= m_pCurrentGroup
->Groups().GetCount();
809 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
810 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
811 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
812 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
813 n
+= GetNumberOfGroups(true);
814 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
821 // ----------------------------------------------------------------------------
822 // tests for existence
823 // ----------------------------------------------------------------------------
825 bool wxFileConfig::HasGroup(const wxString
& strName
) const
827 // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
828 // but there is no group with empty name so treat this separately
829 if ( strName
.empty() )
832 const wxString pathOld
= GetPath();
834 wxFileConfig
*self
= wx_const_cast(wxFileConfig
*, this);
836 rc
= self
->DoSetPath(strName
, false /* don't create missing components */);
838 self
->SetPath(pathOld
);
843 bool wxFileConfig::HasEntry(const wxString
& entry
) const
845 // path is the part before the last "/"
846 wxString path
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
);
848 // except in the special case of "/keyname" when there is nothing before "/"
849 if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR
)
851 path
= wxCONFIG_PATH_SEPARATOR
;
854 // change to the path of the entry if necessary and remember the old path
855 // to restore it later
857 wxFileConfig
* const self
= wx_const_cast(wxFileConfig
*, this);
861 if ( pathOld
.empty() )
862 pathOld
= wxCONFIG_PATH_SEPARATOR
;
864 if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) )
870 // check if the entry exists in this group
871 const bool exists
= m_pCurrentGroup
->FindEntry(
872 entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
;
874 // restore the old path if we changed it above
875 if ( !pathOld
.empty() )
877 self
->SetPath(pathOld
);
883 // ----------------------------------------------------------------------------
885 // ----------------------------------------------------------------------------
887 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
889 wxConfigPathChanger
path(this, key
);
891 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
892 if (pEntry
== NULL
) {
896 *pStr
= pEntry
->Value();
901 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
904 if ( !Read(key
, &str
) )
907 // extra spaces shouldn't prevent us from reading numeric values
910 return str
.ToLong(pl
);
915 bool wxFileConfig::DoReadBinary(const wxString
& key
, wxMemoryBuffer
* buf
) const
917 wxCHECK_MSG( buf
, false, _T("NULL buffer") );
920 if ( !Read(key
, &str
) )
923 *buf
= wxBase64Decode(str
);
927 #endif // wxUSE_BASE64
929 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
931 wxConfigPathChanger
path(this, key
);
932 wxString strName
= path
.Name();
934 wxLogTrace( FILECONF_TRACE_MASK
,
935 _T(" Writing String '%s' = '%s' to Group '%s'"),
940 if ( strName
.empty() )
942 // setting the value of a group is an error
944 wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") );
946 // ... except if it's empty in which case it's a way to force it's creation
948 wxLogTrace( FILECONF_TRACE_MASK
,
949 _T(" Creating group %s"),
950 m_pCurrentGroup
->Name().c_str() );
954 // this will add a line for this group if it didn't have it before (or
955 // do nothing for the root but it's ok as it always exists anyhow)
956 (void)m_pCurrentGroup
->GetGroupLine();
960 // writing an entry check that the name is reasonable
961 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
963 wxLogError( _("Config entry name cannot start with '%c'."),
964 wxCONFIG_IMMUTABLE_PREFIX
);
968 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
972 wxLogTrace( FILECONF_TRACE_MASK
,
973 _T(" Adding Entry %s"),
975 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
978 wxLogTrace( FILECONF_TRACE_MASK
,
979 _T(" Setting value %s"),
981 pEntry
->SetValue(szValue
);
989 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
991 return Write(key
, wxString::Format(_T("%ld"), lValue
));
996 bool wxFileConfig::DoWriteBinary(const wxString
& key
, const wxMemoryBuffer
& buf
)
998 return Write(key
, wxBase64Encode(buf
));
1001 #endif // wxUSE_BASE64
1003 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
1005 if ( !IsDirty() || !m_fnLocalFile
.GetFullPath() )
1008 // set the umask if needed
1009 wxCHANGE_UMASK(m_umask
);
1011 wxTempFile
file(m_fnLocalFile
.GetFullPath());
1013 if ( !file
.IsOpened() )
1015 wxLogError(_("can't open user configuration file."));
1019 // write all strings to file
1021 filetext
.reserve(4096);
1022 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1024 filetext
<< p
->Text() << wxTextFile::GetEOL();
1027 if ( !file
.Write(filetext
, *m_conv
) )
1029 wxLogError(_("can't write user configuration file."));
1033 if ( !file
.Commit() )
1035 wxLogError(_("Failed to update user configuration file."));
1042 #if defined(__WXMAC__)
1043 m_fnLocalFile
.MacSetTypeAndCreator('TEXT', 'ttxt');
1051 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
)
1053 // save unconditionally, even if not dirty
1054 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1056 wxString line
= p
->Text();
1057 line
+= wxTextFile::GetEOL();
1059 wxCharBuffer
buf(line
.mb_str(conv
));
1060 if ( !os
.Write(buf
, strlen(buf
)) )
1062 wxLogError(_("Error saving user configuration data."));
1073 #endif // wxUSE_STREAMS
1075 // ----------------------------------------------------------------------------
1076 // renaming groups/entries
1077 // ----------------------------------------------------------------------------
1079 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1080 const wxString
& newName
)
1082 wxASSERT_MSG( oldName
.find(wxCONFIG_PATH_SEPARATOR
) == wxString::npos
,
1083 _T("RenameEntry(): paths are not supported") );
1085 // check that the entry exists
1086 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1090 // check that the new entry doesn't already exist
1091 if ( m_pCurrentGroup
->FindEntry(newName
) )
1094 // delete the old entry, create the new one
1095 wxString value
= oldEntry
->Value();
1096 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1101 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1102 newEntry
->SetValue(value
);
1107 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1108 const wxString
& newName
)
1110 // check that the group exists
1111 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1115 // check that the new group doesn't already exist
1116 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1119 group
->Rename(newName
);
1126 // ----------------------------------------------------------------------------
1127 // delete groups/entries
1128 // ----------------------------------------------------------------------------
1130 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1132 wxConfigPathChanger
path(this, key
);
1134 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1139 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1140 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1141 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1142 SetPath(wxT("..")); // changes m_pCurrentGroup!
1143 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1145 //else: never delete the root group
1151 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1153 wxConfigPathChanger
path(this, RemoveTrailingSeparator(key
));
1155 if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) )
1158 path
.UpdateIfDeleted();
1165 bool wxFileConfig::DeleteAll()
1169 if ( m_fnLocalFile
.IsOk() )
1171 if ( m_fnLocalFile
.FileExists() &&
1172 !wxRemoveFile(m_fnLocalFile
.GetFullPath()) )
1174 wxLogSysError(_("can't delete user configuration file '%s'"),
1175 m_fnLocalFile
.GetFullPath().c_str());
1185 // ----------------------------------------------------------------------------
1186 // linked list functions
1187 // ----------------------------------------------------------------------------
1189 // append a new line to the end of the list
1191 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1193 wxLogTrace( FILECONF_TRACE_MASK
,
1194 _T(" ** Adding Line '%s'"),
1196 wxLogTrace( FILECONF_TRACE_MASK
,
1198 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1200 wxLogTrace( FILECONF_TRACE_MASK
,
1202 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1205 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1207 if ( m_linesTail
== NULL
)
1210 m_linesHead
= pLine
;
1215 m_linesTail
->SetNext(pLine
);
1216 pLine
->SetPrev(m_linesTail
);
1219 m_linesTail
= pLine
;
1221 wxLogTrace( FILECONF_TRACE_MASK
,
1223 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1225 wxLogTrace( FILECONF_TRACE_MASK
,
1227 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1233 // insert a new line after the given one or in the very beginning if !pLine
1234 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1235 wxFileConfigLineList
*pLine
)
1237 wxLogTrace( FILECONF_TRACE_MASK
,
1238 _T(" ** Inserting Line '%s' after '%s'"),
1240 ((pLine
) ? (const wxChar
*)pLine
->Text().c_str()
1242 wxLogTrace( FILECONF_TRACE_MASK
,
1244 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1246 wxLogTrace( FILECONF_TRACE_MASK
,
1248 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1251 if ( pLine
== m_linesTail
)
1252 return LineListAppend(str
);
1254 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1255 if ( pLine
== NULL
)
1257 // prepend to the list
1258 pNewLine
->SetNext(m_linesHead
);
1259 m_linesHead
->SetPrev(pNewLine
);
1260 m_linesHead
= pNewLine
;
1264 // insert before pLine
1265 wxFileConfigLineList
*pNext
= pLine
->Next();
1266 pNewLine
->SetNext(pNext
);
1267 pNewLine
->SetPrev(pLine
);
1268 pNext
->SetPrev(pNewLine
);
1269 pLine
->SetNext(pNewLine
);
1272 wxLogTrace( FILECONF_TRACE_MASK
,
1274 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1276 wxLogTrace( FILECONF_TRACE_MASK
,
1278 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1284 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1286 wxLogTrace( FILECONF_TRACE_MASK
,
1287 _T(" ** Removing Line '%s'"),
1288 pLine
->Text().c_str() );
1289 wxLogTrace( FILECONF_TRACE_MASK
,
1291 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1293 wxLogTrace( FILECONF_TRACE_MASK
,
1295 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1298 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1299 *pNext
= pLine
->Next();
1303 if ( pPrev
== NULL
)
1304 m_linesHead
= pNext
;
1306 pPrev
->SetNext(pNext
);
1310 if ( pNext
== NULL
)
1311 m_linesTail
= pPrev
;
1313 pNext
->SetPrev(pPrev
);
1315 if ( m_pRootGroup
->GetGroupLine() == pLine
)
1316 m_pRootGroup
->SetLine(m_linesHead
);
1318 wxLogTrace( FILECONF_TRACE_MASK
,
1320 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1322 wxLogTrace( FILECONF_TRACE_MASK
,
1324 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1330 bool wxFileConfig::LineListIsEmpty()
1332 return m_linesHead
== NULL
;
1335 // ============================================================================
1336 // wxFileConfig::wxFileConfigGroup
1337 // ============================================================================
1339 // ----------------------------------------------------------------------------
1341 // ----------------------------------------------------------------------------
1344 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1345 const wxString
& strName
,
1346 wxFileConfig
*pConfig
)
1347 : m_aEntries(CompareEntries
),
1348 m_aSubgroups(CompareGroups
),
1351 m_pConfig
= pConfig
;
1352 m_pParent
= pParent
;
1355 m_pLastEntry
= NULL
;
1356 m_pLastGroup
= NULL
;
1359 // dtor deletes all children
1360 wxFileConfigGroup::~wxFileConfigGroup()
1363 size_t n
, nCount
= m_aEntries
.GetCount();
1364 for ( n
= 0; n
< nCount
; n
++ )
1365 delete m_aEntries
[n
];
1368 nCount
= m_aSubgroups
.GetCount();
1369 for ( n
= 0; n
< nCount
; n
++ )
1370 delete m_aSubgroups
[n
];
1373 // ----------------------------------------------------------------------------
1375 // ----------------------------------------------------------------------------
1377 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1379 // for a normal (i.e. not root) group this method shouldn't be called twice
1380 // unless we are resetting the line
1381 wxASSERT_MSG( !m_pParent
|| !m_pLine
|| !pLine
,
1382 _T("changing line for a non-root group?") );
1388 This is a bit complicated, so let me explain it in details. All lines that
1389 were read from the local file (the only one we will ever modify) are stored
1390 in a (doubly) linked list. Our problem is to know at which position in this
1391 list should we insert the new entries/subgroups. To solve it we keep three
1392 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1394 m_pLine points to the line containing "[group_name]"
1395 m_pLastEntry points to the last entry of this group in the local file.
1396 m_pLastGroup subgroup
1398 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1399 from the local file, the corresponding variable is set. However, if the group
1400 was read from the global file and then modified or created by the application
1401 these variables are still NULL and we need to create the corresponding lines.
1402 See the following functions (and comments preceding them) for the details of
1405 Also, when our last entry/group are deleted we need to find the new last
1406 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1407 of lines until it either founds an entry/subgroup (and this is the new last
1408 element) or the m_pLine of the group, in which case there are no more entries
1409 (or subgroups) left and m_pLast<element> becomes NULL.
1411 NB: This last problem could be avoided for entries if we added new entries
1412 immediately after m_pLine, but in this case the entries would appear
1413 backwards in the config file (OTOH, it's not that important) and as we
1414 would still need to do it for the subgroups the code wouldn't have been
1415 significantly less complicated.
1418 // Return the line which contains "[our name]". If we're still not in the list,
1419 // add our line to it immediately after the last line of our parent group if we
1420 // have it or in the very beginning if we're the root group.
1421 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1423 wxLogTrace( FILECONF_TRACE_MASK
,
1424 _T(" GetGroupLine() for Group '%s'"),
1429 wxLogTrace( FILECONF_TRACE_MASK
,
1430 _T(" Getting Line item pointer") );
1432 wxFileConfigGroup
*pParent
= Parent();
1434 // this group wasn't present in local config file, add it now
1437 wxLogTrace( FILECONF_TRACE_MASK
,
1438 _T(" checking parent '%s'"),
1439 pParent
->Name().c_str() );
1441 wxString strFullName
;
1443 // add 1 to the name because we don't want to start with '/'
1444 strFullName
<< wxT("[")
1445 << FilterOutEntryName(GetFullName().c_str() + 1)
1447 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1448 pParent
->GetLastGroupLine());
1449 pParent
->SetLastGroup(this); // we're surely after all the others
1451 //else: this is the root group and so we return NULL because we don't
1452 // have any group line
1458 // Return the last line belonging to the subgroups of this group (after which
1459 // we can add a new subgroup), if we don't have any subgroups or entries our
1460 // last line is the group line (m_pLine) itself.
1461 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1463 // if we have any subgroups, our last line is the last line of the last
1467 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1469 wxASSERT_MSG( pLine
, _T("last group must have !NULL associated line") );
1474 // no subgroups, so the last line is the line of thelast entry (if any)
1475 return GetLastEntryLine();
1478 // return the last line belonging to the entries of this group (after which
1479 // we can add a new entry), if we don't have any entries we will add the new
1480 // one immediately after the group line itself.
1481 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1483 wxLogTrace( FILECONF_TRACE_MASK
,
1484 _T(" GetLastEntryLine() for Group '%s'"),
1489 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1491 wxASSERT_MSG( pLine
, _T("last entry must have !NULL associated line") );
1496 // no entries: insert after the group header, if any
1497 return GetGroupLine();
1500 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry
*pEntry
)
1502 m_pLastEntry
= pEntry
;
1506 // the only situation in which a group without its own line can have
1507 // an entry is when the first entry is added to the initially empty
1508 // root pseudo-group
1509 wxASSERT_MSG( !m_pParent
, _T("unexpected for non root group") );
1511 // let the group know that it does have a line in the file now
1512 m_pLine
= pEntry
->GetLine();
1516 // ----------------------------------------------------------------------------
1518 // ----------------------------------------------------------------------------
1520 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
1522 // update the line of this group
1523 wxFileConfigLineList
*line
= GetGroupLine();
1524 wxCHECK_RET( line
, _T("a non root group must have a corresponding line!") );
1526 // +1: skip the leading '/'
1527 line
->SetText(wxString::Format(_T("[%s]"), GetFullName().c_str() + 1));
1530 // also update all subgroups as they have this groups name in their lines
1531 const size_t nCount
= m_aSubgroups
.GetCount();
1532 for ( size_t n
= 0; n
< nCount
; n
++ )
1534 m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines();
1538 void wxFileConfigGroup::Rename(const wxString
& newName
)
1540 wxCHECK_RET( m_pParent
, _T("the root group can't be renamed") );
1542 if ( newName
== m_strName
)
1545 // we need to remove the group from the parent and it back under the new
1546 // name to keep the parents array of subgroups alphabetically sorted
1547 m_pParent
->m_aSubgroups
.Remove(this);
1549 m_strName
= newName
;
1551 m_pParent
->m_aSubgroups
.Add(this);
1553 // update the group lines recursively
1554 UpdateGroupAndSubgroupsLines();
1557 wxString
wxFileConfigGroup::GetFullName() const
1561 fullname
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1566 // ----------------------------------------------------------------------------
1568 // ----------------------------------------------------------------------------
1570 // use binary search because the array is sorted
1572 wxFileConfigGroup::FindEntry(const wxString
& name
) const
1576 hi
= m_aEntries
.GetCount();
1578 wxFileConfigEntry
*pEntry
;
1582 pEntry
= m_aEntries
[i
];
1584 #if wxCONFIG_CASE_SENSITIVE
1585 res
= pEntry
->Name().compare(name
);
1587 res
= pEntry
->Name().CmpNoCase(name
);
1602 wxFileConfigGroup::FindSubgroup(const wxString
& name
) const
1606 hi
= m_aSubgroups
.GetCount();
1608 wxFileConfigGroup
*pGroup
;
1612 pGroup
= m_aSubgroups
[i
];
1614 #if wxCONFIG_CASE_SENSITIVE
1615 res
= pGroup
->Name().compare(name
);
1617 res
= pGroup
->Name().CmpNoCase(name
);
1631 // ----------------------------------------------------------------------------
1632 // create a new item
1633 // ----------------------------------------------------------------------------
1635 // create a new entry and add it to the current group
1636 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1638 wxASSERT( FindEntry(strName
) == 0 );
1640 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1642 m_aEntries
.Add(pEntry
);
1646 // create a new group and add it to the current group
1647 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1649 wxASSERT( FindSubgroup(strName
) == 0 );
1651 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1653 m_aSubgroups
.Add(pGroup
);
1657 // ----------------------------------------------------------------------------
1659 // ----------------------------------------------------------------------------
1662 The delete operations are _very_ slow if we delete the last item of this
1663 group (see comments before GetXXXLineXXX functions for more details),
1664 so it's much better to start with the first entry/group if we want to
1665 delete several of them.
1668 bool wxFileConfigGroup::DeleteSubgroupByName(const wxString
& name
)
1670 wxFileConfigGroup
* const pGroup
= FindSubgroup(name
);
1672 return pGroup
? DeleteSubgroup(pGroup
) : false;
1675 // Delete the subgroup and remove all references to it from
1676 // other data structures.
1677 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1679 wxCHECK_MSG( pGroup
, false, _T("deleting non existing group?") );
1681 wxLogTrace( FILECONF_TRACE_MASK
,
1682 _T("Deleting group '%s' from '%s'"),
1683 pGroup
->Name().c_str(),
1686 wxLogTrace( FILECONF_TRACE_MASK
,
1687 _T(" (m_pLine) = prev: %p, this %p, next %p"),
1688 m_pLine
? wx_static_cast(void*, m_pLine
->Prev()) : 0,
1689 wx_static_cast(void*, m_pLine
),
1690 m_pLine
? wx_static_cast(void*, m_pLine
->Next()) : 0 );
1691 wxLogTrace( FILECONF_TRACE_MASK
,
1693 m_pLine
? (const wxChar
*)m_pLine
->Text().c_str()
1696 // delete all entries...
1697 size_t nCount
= pGroup
->m_aEntries
.GetCount();
1699 wxLogTrace(FILECONF_TRACE_MASK
,
1700 _T("Removing %lu entries"), (unsigned long)nCount
);
1702 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1704 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1708 wxLogTrace( FILECONF_TRACE_MASK
,
1710 pLine
->Text().c_str() );
1711 m_pConfig
->LineListRemove(pLine
);
1715 // ...and subgroups of this subgroup
1716 nCount
= pGroup
->m_aSubgroups
.GetCount();
1718 wxLogTrace( FILECONF_TRACE_MASK
,
1719 _T("Removing %lu subgroups"), (unsigned long)nCount
);
1721 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1723 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1726 // and then finally the group itself
1727 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1730 wxLogTrace( FILECONF_TRACE_MASK
,
1731 _T(" Removing line for group '%s' : '%s'"),
1732 pGroup
->Name().c_str(),
1733 pLine
->Text().c_str() );
1734 wxLogTrace( FILECONF_TRACE_MASK
,
1735 _T(" Removing from group '%s' : '%s'"),
1737 ((m_pLine
) ? (const wxChar
*)m_pLine
->Text().c_str()
1740 // notice that we may do this test inside the previous "if"
1741 // because the last entry's line is surely !NULL
1742 if ( pGroup
== m_pLastGroup
)
1744 wxLogTrace( FILECONF_TRACE_MASK
,
1745 _T(" Removing last group") );
1747 // our last entry is being deleted, so find the last one which
1748 // stays by going back until we find a subgroup or reach the
1750 const size_t nSubgroups
= m_aSubgroups
.GetCount();
1752 m_pLastGroup
= NULL
;
1753 for ( wxFileConfigLineList
*pl
= pLine
->Prev();
1754 pl
&& !m_pLastGroup
;
1757 // does this line belong to our subgroup?
1758 for ( size_t n
= 0; n
< nSubgroups
; n
++ )
1760 // do _not_ call GetGroupLine! we don't want to add it to
1761 // the local file if it's not already there
1762 if ( m_aSubgroups
[n
]->m_pLine
== pl
)
1764 m_pLastGroup
= m_aSubgroups
[n
];
1769 if ( pl
== m_pLine
)
1774 m_pConfig
->LineListRemove(pLine
);
1778 wxLogTrace( FILECONF_TRACE_MASK
,
1779 _T(" No line entry for Group '%s'?"),
1780 pGroup
->Name().c_str() );
1783 m_aSubgroups
.Remove(pGroup
);
1789 bool wxFileConfigGroup::DeleteEntry(const wxString
& name
)
1791 wxFileConfigEntry
*pEntry
= FindEntry(name
);
1794 // entry doesn't exist, nothing to do
1798 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1799 if ( pLine
!= NULL
) {
1800 // notice that we may do this test inside the previous "if" because the
1801 // last entry's line is surely !NULL
1802 if ( pEntry
== m_pLastEntry
) {
1803 // our last entry is being deleted - find the last one which stays
1804 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1806 // find the previous entry (if any)
1807 wxFileConfigEntry
*pNewLast
= NULL
;
1808 const wxFileConfigLineList
* const
1809 pNewLastLine
= m_pLastEntry
->GetLine()->Prev();
1810 const size_t nEntries
= m_aEntries
.GetCount();
1811 for ( size_t n
= 0; n
< nEntries
; n
++ ) {
1812 if ( m_aEntries
[n
]->GetLine() == pNewLastLine
) {
1813 pNewLast
= m_aEntries
[n
];
1818 // pNewLast can be NULL here -- it's ok and can happen if we have no
1820 m_pLastEntry
= pNewLast
;
1823 m_pConfig
->LineListRemove(pLine
);
1826 m_aEntries
.Remove(pEntry
);
1832 // ============================================================================
1833 // wxFileConfig::wxFileConfigEntry
1834 // ============================================================================
1836 // ----------------------------------------------------------------------------
1838 // ----------------------------------------------------------------------------
1839 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1840 const wxString
& strName
,
1842 : m_strName(strName
)
1844 wxASSERT( !strName
.empty() );
1846 m_pParent
= pParent
;
1850 m_bHasValue
= false;
1852 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1854 m_strName
.erase(0, 1); // remove first character
1857 // ----------------------------------------------------------------------------
1859 // ----------------------------------------------------------------------------
1861 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1863 if ( m_pLine
!= NULL
) {
1864 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1865 Name().c_str(), m_pParent
->GetFullName().c_str());
1869 Group()->SetLastEntry(this);
1872 // second parameter is false if we read the value from file and prevents the
1873 // entry from being marked as 'dirty'
1874 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1876 if ( bUser
&& IsImmutable() )
1878 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1883 // do nothing if it's the same value: but don't test for it if m_bHasValue
1884 // hadn't been set yet or we'd never write empty values to the file
1885 if ( m_bHasValue
&& strValue
== m_strValue
)
1889 m_strValue
= strValue
;
1893 wxString strValFiltered
;
1895 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1897 strValFiltered
= strValue
;
1900 strValFiltered
= FilterOutValue(strValue
);
1904 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1908 // entry was read from the local config file, just modify the line
1909 m_pLine
->SetText(strLine
);
1911 else // this entry didn't exist in the local file
1913 // add a new line to the file: note that line returned by
1914 // GetLastEntryLine() may be NULL if we're in the root group and it
1915 // doesn't have any entries yet, but this is ok as passing NULL
1916 // line to LineListInsert() means to prepend new line to the list
1917 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1918 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1920 Group()->SetLastEntry(this);
1925 // ============================================================================
1927 // ============================================================================
1929 // ----------------------------------------------------------------------------
1930 // compare functions for array sorting
1931 // ----------------------------------------------------------------------------
1933 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1935 #if wxCONFIG_CASE_SENSITIVE
1936 return p1
->Name().compare(p2
->Name());
1938 return p1
->Name().CmpNoCase(p2
->Name());
1942 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1944 #if wxCONFIG_CASE_SENSITIVE
1945 return p1
->Name().compare(p2
->Name());
1947 return p1
->Name().CmpNoCase(p2
->Name());
1951 // ----------------------------------------------------------------------------
1953 // ----------------------------------------------------------------------------
1955 // undo FilterOutValue
1956 static wxString
FilterInValue(const wxString
& str
)
1962 strResult
.reserve(str
.length());
1964 wxString::const_iterator i
= str
.begin();
1965 const bool bQuoted
= *i
== '"';
1969 for ( const wxString::const_iterator end
= str
.end(); i
!= end
; ++i
)
1971 if ( *i
== wxT('\\') )
1975 wxLogWarning(_("trailing backslash ignored in '%s'"), str
.c_str());
1979 switch ( (*i
).GetValue() )
1982 strResult
+= wxT('\n');
1986 strResult
+= wxT('\r');
1990 strResult
+= wxT('\t');
1994 strResult
+= wxT('\\');
1998 strResult
+= wxT('"');
2002 else // not a backslash
2004 if ( *i
!= wxT('"') || !bQuoted
)
2008 else if ( i
!= end
- 1 )
2010 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2011 i
- str
.begin(), str
.c_str());
2013 //else: it's the last quote of a quoted string, ok
2020 // quote the string before writing it to file
2021 static wxString
FilterOutValue(const wxString
& str
)
2027 strResult
.Alloc(str
.Len());
2029 // quoting is necessary to preserve spaces in the beginning of the string
2030 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2033 strResult
+= wxT('"');
2036 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2037 switch ( str
[n
].GetValue() ) {
2059 //else: fall through
2062 strResult
+= str
[n
];
2063 continue; // nothing special to do
2066 // we get here only for special characters
2067 strResult
<< wxT('\\') << c
;
2071 strResult
+= wxT('"');
2076 // undo FilterOutEntryName
2077 static wxString
FilterInEntryName(const wxString
& str
)
2080 strResult
.Alloc(str
.Len());
2082 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2083 if ( *pc
== wxT('\\') ) {
2084 // we need to test it here or we'd skip past the NUL in the loop line
2085 if ( *++pc
== _T('\0') )
2095 // sanitize entry or group name: insert '\\' before any special characters
2096 static wxString
FilterOutEntryName(const wxString
& str
)
2099 strResult
.Alloc(str
.Len());
2101 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2102 const wxChar c
= *pc
;
2104 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2105 // which will probably never have special meaning and with which we can't
2106 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2108 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2109 // should *not* be quoted
2112 ((unsigned char)c
< 127) &&
2114 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) )
2116 strResult
+= wxT('\\');
2125 // we can't put ?: in the ctor initializer list because it confuses some
2126 // broken compilers (Borland C++)
2127 static wxString
GetAppName(const wxString
& appName
)
2129 if ( !appName
&& wxTheApp
)
2130 return wxTheApp
->GetAppName();
2135 #endif // wxUSE_CONFIG