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 Ballueder & 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(__WXMSW__)
50 #include "wx/msw/private.h"
60 // ----------------------------------------------------------------------------
62 // ----------------------------------------------------------------------------
68 #define FILECONF_TRACE_MASK wxT("fileconf")
70 // ----------------------------------------------------------------------------
71 // global functions declarations
72 // ----------------------------------------------------------------------------
74 // compare functions for sorting the arrays
75 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
76 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
79 static wxString
FilterInValue(const wxString
& str
);
80 static wxString
FilterOutValue(const wxString
& str
);
82 static wxString
FilterInEntryName(const wxString
& str
);
83 static wxString
FilterOutEntryName(const wxString
& str
);
85 // get the name to use in wxFileConfig ctor
86 static wxString
GetAppName(const wxString
& appname
);
88 // ============================================================================
90 // ============================================================================
92 // ----------------------------------------------------------------------------
93 // "template" array types
94 // ----------------------------------------------------------------------------
96 #ifdef WXMAKINGDLL_BASE
97 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
,
99 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
,
102 WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
103 WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
);
106 // ----------------------------------------------------------------------------
107 // wxFileConfigLineList
108 // ----------------------------------------------------------------------------
110 // we store all lines of the local config file as a linked list in memory
111 class wxFileConfigLineList
114 void SetNext(wxFileConfigLineList
*pNext
) { m_pNext
= pNext
; }
115 void SetPrev(wxFileConfigLineList
*pPrev
) { m_pPrev
= pPrev
; }
118 wxFileConfigLineList(const wxString
& str
,
119 wxFileConfigLineList
*pNext
= NULL
) : m_strLine(str
)
120 { SetNext(pNext
); SetPrev(NULL
); }
122 // next/prev nodes in the linked list
123 wxFileConfigLineList
*Next() const { return m_pNext
; }
124 wxFileConfigLineList
*Prev() const { return m_pPrev
; }
126 // get/change lines text
127 void SetText(const wxString
& str
) { m_strLine
= str
; }
128 const wxString
& Text() const { return m_strLine
; }
131 wxString m_strLine
; // line contents
132 wxFileConfigLineList
*m_pNext
, // next node
133 *m_pPrev
; // previous one
135 wxDECLARE_NO_COPY_CLASS(wxFileConfigLineList
);
138 // ----------------------------------------------------------------------------
139 // wxFileConfigEntry: a name/value pair
140 // ----------------------------------------------------------------------------
142 class wxFileConfigEntry
145 wxFileConfigGroup
*m_pParent
; // group that contains us
147 wxString m_strName
, // entry name
149 bool m_bImmutable
:1, // can be overriden locally?
150 m_bHasValue
:1; // set after first call to SetValue()
152 int m_nLine
; // used if m_pLine == NULL only
154 // pointer to our line in the linked list or NULL if it was found in global
155 // file (which we don't modify)
156 wxFileConfigLineList
*m_pLine
;
159 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
160 const wxString
& strName
, int nLine
);
163 const wxString
& Name() const { return m_strName
; }
164 const wxString
& Value() const { return m_strValue
; }
165 wxFileConfigGroup
*Group() const { return m_pParent
; }
166 bool IsImmutable() const { return m_bImmutable
; }
167 bool IsLocal() const { return m_pLine
!= 0; }
168 int Line() const { return m_nLine
; }
169 wxFileConfigLineList
*
170 GetLine() const { return m_pLine
; }
172 // modify entry attributes
173 void SetValue(const wxString
& strValue
, bool bUser
= true);
174 void SetLine(wxFileConfigLineList
*pLine
);
176 wxDECLARE_NO_COPY_CLASS(wxFileConfigEntry
);
179 // ----------------------------------------------------------------------------
180 // wxFileConfigGroup: container of entries and other groups
181 // ----------------------------------------------------------------------------
183 class wxFileConfigGroup
186 wxFileConfig
*m_pConfig
; // config object we belong to
187 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
188 ArrayEntries m_aEntries
; // entries in this group
189 ArrayGroups m_aSubgroups
; // subgroups
190 wxString m_strName
; // group's name
191 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
192 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
193 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
195 // DeleteSubgroupByName helper
196 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
199 void UpdateGroupAndSubgroupsLines();
203 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
205 // dtor deletes all entries and subgroups also
206 ~wxFileConfigGroup();
209 const wxString
& Name() const { return m_strName
; }
210 wxFileConfigGroup
*Parent() const { return m_pParent
; }
211 wxFileConfig
*Config() const { return m_pConfig
; }
213 const ArrayEntries
& Entries() const { return m_aEntries
; }
214 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
215 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
217 // find entry/subgroup (NULL if not found)
218 wxFileConfigGroup
*FindSubgroup(const wxString
& name
) const;
219 wxFileConfigEntry
*FindEntry (const wxString
& name
) const;
221 // delete entry/subgroup, return false if doesn't exist
222 bool DeleteSubgroupByName(const wxString
& name
);
223 bool DeleteEntry(const wxString
& name
);
225 // create new entry/subgroup returning pointer to newly created element
226 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
227 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
229 void SetLine(wxFileConfigLineList
*pLine
);
231 // rename: no checks are done to ensure that the name is unique!
232 void Rename(const wxString
& newName
);
235 wxString
GetFullName() const;
237 // get the last line belonging to an entry/subgroup of this group
238 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
239 // may be NULL for "/" only
240 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
241 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
243 // called by entries/subgroups when they're created/deleted
244 void SetLastEntry(wxFileConfigEntry
*pEntry
);
245 void SetLastGroup(wxFileConfigGroup
*pGroup
)
246 { m_pLastGroup
= pGroup
; }
248 wxDECLARE_NO_COPY_CLASS(wxFileConfigGroup
);
251 // ============================================================================
253 // ============================================================================
255 // ----------------------------------------------------------------------------
257 // ----------------------------------------------------------------------------
259 // this function modifies in place the given wxFileName object if it doesn't
260 // already have an extension
262 // note that it's slightly misnamed under Mac as there it doesn't add an
263 // extension but modifies the file name instead, so you shouldn't suppose that
264 // fn.HasExt() is true after it returns
265 static void AddConfFileExtIfNeeded(wxFileName
& fn
)
269 #if defined( __WXMAC__ )
270 fn
.SetName(fn
.GetName() + wxT(" Preferences"));
271 #elif defined( __UNIX__ )
272 fn
.SetExt(wxT("conf"));
274 fn
.SetExt(wxT("ini"));
279 wxString
wxFileConfig::GetGlobalDir()
281 return wxStandardPaths::Get().GetConfigDir();
284 wxString
wxFileConfig::GetLocalDir(int style
)
288 wxStandardPathsBase
& stdp
= wxStandardPaths::Get();
290 // it so happens that user data directory is a subdirectory of user config
291 // directory on all supported platforms, which explains why we use it here
292 return style
& wxCONFIG_USE_SUBDIR
? stdp
.GetUserDataDir()
293 : stdp
.GetUserConfigDir();
296 wxFileName
wxFileConfig::GetGlobalFile(const wxString
& szFile
)
298 wxFileName
fn(GetGlobalDir(), szFile
);
300 AddConfFileExtIfNeeded(fn
);
305 wxFileName
wxFileConfig::GetLocalFile(const wxString
& szFile
, int style
)
307 wxFileName
fn(GetLocalDir(style
), szFile
);
309 #if defined( __UNIX__ ) && !defined( __WXMAC__ )
310 if ( !(style
& wxCONFIG_USE_SUBDIR
) )
312 // dot-files under Unix start with, well, a dot (but OTOH they usually
313 // don't have any specific extension)
314 fn
.SetName(wxT('.') + fn
.GetName());
316 else // we do append ".conf" extension to config files in subdirectories
317 #endif // defined( __UNIX__ ) && !defined( __WXMAC__ )
319 AddConfFileExtIfNeeded(fn
);
325 // ----------------------------------------------------------------------------
327 // ----------------------------------------------------------------------------
328 IMPLEMENT_ABSTRACT_CLASS(wxFileConfig
, wxConfigBase
)
330 void wxFileConfig::Init()
333 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
338 // It's not an error if (one of the) file(s) doesn't exist.
340 // parse the global file
341 if ( m_fnGlobalFile
.IsOk() && m_fnGlobalFile
.FileExists() )
343 wxTextFile
fileGlobal(m_fnGlobalFile
.GetFullPath());
345 if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) )
347 Parse(fileGlobal
, false /* global */);
352 wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile
.GetFullPath().c_str());
356 // parse the local file
357 if ( m_fnLocalFile
.IsOk() && m_fnLocalFile
.FileExists() )
359 wxTextFile
fileLocal(m_fnLocalFile
.GetFullPath());
360 if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) )
362 Parse(fileLocal
, true /* local */);
367 const wxString path
= m_fnLocalFile
.GetFullPath();
368 wxLogWarning(_("can't open user configuration file '%s'."),
371 if ( m_fnLocalFile
.FileExists() )
373 wxLogWarning(_("Changes won't be saved to avoid overwriting the existing file \"%s\""),
375 m_fnLocalFile
.Clear();
383 // constructor supports creation of wxFileConfig objects of any type
384 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
385 const wxString
& strLocal
, const wxString
& strGlobal
,
387 const wxMBConv
& conv
)
388 : wxConfigBase(::GetAppName(appName
), vendorName
,
391 m_fnLocalFile(strLocal
),
392 m_fnGlobalFile(strGlobal
),
395 // Make up names for files if empty
396 if ( !m_fnLocalFile
.IsOk() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
397 m_fnLocalFile
= GetLocalFile(GetAppName(), style
);
399 if ( !m_fnGlobalFile
.IsOk() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
400 m_fnGlobalFile
= GetGlobalFile(GetAppName());
402 // Check if styles are not supplied, but filenames are, in which case
403 // add the correct styles.
404 if ( m_fnLocalFile
.IsOk() )
405 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
407 if ( m_fnGlobalFile
.IsOk() )
408 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
410 // if the path is not absolute, prepend the standard directory to it
411 // unless explicitly asked not to
412 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
414 if ( m_fnLocalFile
.IsOk() )
415 m_fnLocalFile
.MakeAbsolute(GetLocalDir(style
));
417 if ( m_fnGlobalFile
.IsOk() )
418 m_fnGlobalFile
.MakeAbsolute(GetGlobalDir());
428 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, const wxMBConv
& conv
)
429 : m_conv(conv
.Clone())
431 // always local_file when this constructor is called (?)
432 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
435 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
440 // read the entire stream contents in memory
442 static const size_t chunkLen
= 1024;
444 wxMemoryBuffer
buf(chunkLen
);
447 inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
);
448 buf
.UngetAppendBuf(inStream
.LastRead());
450 const wxStreamError err
= inStream
.GetLastError();
452 if ( err
!= wxSTREAM_NO_ERROR
&& err
!= wxSTREAM_EOF
)
454 wxLogError(_("Error reading config options."));
458 while ( !inStream
.Eof() );
462 cbuf
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen() + 1, &len
);
463 if ( !len
&& buf
.GetDataLen() )
465 wxLogError(_("Failed to read config options."));
467 #else // !wxUSE_UNICODE
468 // no need for conversion
469 cbuf
= wxCharBuffer::CreateNonOwned((char *)buf
.GetData(), buf
.GetDataLen());
470 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
473 // now break it into lines
474 wxMemoryText memText
;
475 for ( const wxChar
*s
= cbuf
; ; ++s
)
478 while ( *e
!= '\0' && *e
!= '\n' && *e
!= '\r' )
481 // notice that we throw away the original EOL kind here, maybe we
482 // should preserve it?
484 memText
.AddLine(wxString(s
, e
));
489 // skip the second EOL byte if it's a DOS one
490 if ( *e
== '\r' && e
[1] == '\n' )
496 // Finally we can parse it all.
497 Parse(memText
, true /* local */);
503 #endif // wxUSE_STREAMS
505 void wxFileConfig::CleanUp()
509 wxFileConfigLineList
*pCur
= m_linesHead
;
510 while ( pCur
!= NULL
) {
511 wxFileConfigLineList
*pNext
= pCur
->Next();
517 wxFileConfig::~wxFileConfig()
526 // ----------------------------------------------------------------------------
527 // parse a config file
528 // ----------------------------------------------------------------------------
530 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
533 size_t nLineCount
= buffer
.GetLineCount();
535 for ( size_t n
= 0; n
< nLineCount
; n
++ )
537 wxString strLine
= buffer
[n
];
538 // FIXME-UTF8: rewrite using iterators, without this buffer
539 wxWxCharBuffer
buf(strLine
.c_str());
540 const wxChar
*pStart
;
543 // add the line to linked list
545 LineListAppend(strLine
);
548 // skip leading spaces
549 for ( pStart
= buf
; wxIsspace(*pStart
); pStart
++ )
552 // skip blank/comment lines
553 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
556 if ( *pStart
== wxT('[') ) { // a new group
559 while ( *++pEnd
!= wxT(']') ) {
560 if ( *pEnd
== wxT('\\') ) {
561 // the next char is escaped, so skip it even if it is ']'
565 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
566 // we reached the end of line, break out of the loop
571 if ( *pEnd
!= wxT(']') ) {
572 wxLogError(_("file '%s': unexpected character %c at line %d."),
573 buffer
.GetName(), *pEnd
, n
+ 1);
574 continue; // skip this line
577 // group name here is always considered as abs path
580 strGroup
<< wxCONFIG_PATH_SEPARATOR
581 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
583 // will create it if doesn't yet exist
588 if ( m_pCurrentGroup
->Parent() )
589 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
590 m_pCurrentGroup
->SetLine(m_linesTail
);
593 // check that there is nothing except comments left on this line
595 while ( *++pEnd
!= wxT('\0') && bCont
) {
604 // ignore whitespace ('\n' impossible here)
608 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
609 buffer
.GetName(), n
+ 1, pEnd
);
616 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
617 if ( *pEnd
== wxT('\\') ) {
618 // next character may be space or not - still take it because it's
619 // quoted (unless there is nothing)
622 // the error message will be given below anyhow
630 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
633 while ( wxIsspace(*pEnd
) )
636 if ( *pEnd
++ != wxT('=') ) {
637 wxLogError(_("file '%s', line %d: '=' expected."),
638 buffer
.GetName(), n
+ 1);
641 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
643 if ( pEntry
== NULL
) {
645 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
648 if ( bLocal
&& pEntry
->IsImmutable() ) {
649 // immutable keys can't be changed by user
650 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
651 buffer
.GetName(), n
+ 1, strKey
.c_str());
654 // the condition below catches the cases (a) and (b) but not (c):
655 // (a) global key found second time in global file
656 // (b) key found second (or more) time in local file
657 // (c) key from global file now found in local one
658 // which is exactly what we want.
659 else if ( !bLocal
|| pEntry
->IsLocal() ) {
660 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
661 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
667 pEntry
->SetLine(m_linesTail
);
670 while ( wxIsspace(*pEnd
) )
673 wxString value
= pEnd
;
674 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
675 value
= FilterInValue(value
);
677 pEntry
->SetValue(value
, false);
683 // ----------------------------------------------------------------------------
685 // ----------------------------------------------------------------------------
687 void wxFileConfig::SetRootPath()
690 m_pCurrentGroup
= m_pRootGroup
;
694 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
696 wxArrayString aParts
;
698 if ( strPath
.empty() ) {
703 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
705 wxSplitPath(aParts
, strPath
);
708 // relative path, combine with current one
709 wxString strFullPath
= m_strPath
;
710 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
711 wxSplitPath(aParts
, strFullPath
);
714 // change current group
716 m_pCurrentGroup
= m_pRootGroup
;
717 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
718 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
719 if ( pNextGroup
== NULL
)
721 if ( !createMissingComponents
)
724 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
727 m_pCurrentGroup
= pNextGroup
;
730 // recombine path parts in one variable
732 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
733 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
739 void wxFileConfig::SetPath(const wxString
& strPath
)
741 DoSetPath(strPath
, true /* create missing path components */);
744 const wxString
& wxFileConfig::GetPath() const
749 // ----------------------------------------------------------------------------
751 // ----------------------------------------------------------------------------
753 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
756 return GetNextGroup(str
, lIndex
);
759 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
761 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) {
762 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
769 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
772 return GetNextEntry(str
, lIndex
);
775 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
777 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) {
778 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
785 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
787 size_t n
= m_pCurrentGroup
->Entries().GetCount();
789 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
791 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
792 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
793 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
794 self
->m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
795 n
+= GetNumberOfEntries(true);
796 self
->m_pCurrentGroup
= pOldCurrentGroup
;
803 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
805 size_t n
= m_pCurrentGroup
->Groups().GetCount();
807 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
809 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
810 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
811 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
812 self
->m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
813 n
+= GetNumberOfGroups(true);
814 self
->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
= 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
= 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, wxT("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 wxT(" 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 wxT(" 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 wxT(" Adding Entry %s"),
975 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
978 wxLogTrace( FILECONF_TRACE_MASK
,
979 wxT(" Setting value %s"),
981 pEntry
->SetValue(szValue
);
989 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
991 return Write(key
, wxString::Format(wxT("%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( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
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 wxT("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 wxT(" ** 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 wxT(" ** 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 wxT(" ** 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 wxLogTrace( FILECONF_TRACE_MASK
,
1317 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1319 wxLogTrace( FILECONF_TRACE_MASK
,
1321 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1327 bool wxFileConfig::LineListIsEmpty()
1329 return m_linesHead
== NULL
;
1332 // ============================================================================
1333 // wxFileConfig::wxFileConfigGroup
1334 // ============================================================================
1336 // ----------------------------------------------------------------------------
1338 // ----------------------------------------------------------------------------
1341 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1342 const wxString
& strName
,
1343 wxFileConfig
*pConfig
)
1344 : m_aEntries(CompareEntries
),
1345 m_aSubgroups(CompareGroups
),
1348 m_pConfig
= pConfig
;
1349 m_pParent
= pParent
;
1352 m_pLastEntry
= NULL
;
1353 m_pLastGroup
= NULL
;
1356 // dtor deletes all children
1357 wxFileConfigGroup::~wxFileConfigGroup()
1360 size_t n
, nCount
= m_aEntries
.GetCount();
1361 for ( n
= 0; n
< nCount
; n
++ )
1362 delete m_aEntries
[n
];
1365 nCount
= m_aSubgroups
.GetCount();
1366 for ( n
= 0; n
< nCount
; n
++ )
1367 delete m_aSubgroups
[n
];
1370 // ----------------------------------------------------------------------------
1372 // ----------------------------------------------------------------------------
1374 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1376 // for a normal (i.e. not root) group this method shouldn't be called twice
1377 // unless we are resetting the line
1378 wxASSERT_MSG( !m_pParent
|| !m_pLine
|| !pLine
,
1379 wxT("changing line for a non-root group?") );
1385 This is a bit complicated, so let me explain it in details. All lines that
1386 were read from the local file (the only one we will ever modify) are stored
1387 in a (doubly) linked list. Our problem is to know at which position in this
1388 list should we insert the new entries/subgroups. To solve it we keep three
1389 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1391 m_pLine points to the line containing "[group_name]"
1392 m_pLastEntry points to the last entry of this group in the local file.
1393 m_pLastGroup subgroup
1395 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1396 from the local file, the corresponding variable is set. However, if the group
1397 was read from the global file and then modified or created by the application
1398 these variables are still NULL and we need to create the corresponding lines.
1399 See the following functions (and comments preceding them) for the details of
1402 Also, when our last entry/group are deleted we need to find the new last
1403 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1404 of lines until it either founds an entry/subgroup (and this is the new last
1405 element) or the m_pLine of the group, in which case there are no more entries
1406 (or subgroups) left and m_pLast<element> becomes NULL.
1408 NB: This last problem could be avoided for entries if we added new entries
1409 immediately after m_pLine, but in this case the entries would appear
1410 backwards in the config file (OTOH, it's not that important) and as we
1411 would still need to do it for the subgroups the code wouldn't have been
1412 significantly less complicated.
1415 // Return the line which contains "[our name]". If we're still not in the list,
1416 // add our line to it immediately after the last line of our parent group if we
1417 // have it or in the very beginning if we're the root group.
1418 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1420 wxLogTrace( FILECONF_TRACE_MASK
,
1421 wxT(" GetGroupLine() for Group '%s'"),
1426 wxLogTrace( FILECONF_TRACE_MASK
,
1427 wxT(" Getting Line item pointer") );
1429 wxFileConfigGroup
*pParent
= Parent();
1431 // this group wasn't present in local config file, add it now
1434 wxLogTrace( FILECONF_TRACE_MASK
,
1435 wxT(" checking parent '%s'"),
1436 pParent
->Name().c_str() );
1438 wxString strFullName
;
1440 // add 1 to the name because we don't want to start with '/'
1441 strFullName
<< wxT("[")
1442 << FilterOutEntryName(GetFullName().c_str() + 1)
1444 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1445 pParent
->GetLastGroupLine());
1446 pParent
->SetLastGroup(this); // we're surely after all the others
1448 //else: this is the root group and so we return NULL because we don't
1449 // have any group line
1455 // Return the last line belonging to the subgroups of this group (after which
1456 // we can add a new subgroup), if we don't have any subgroups or entries our
1457 // last line is the group line (m_pLine) itself.
1458 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1460 // if we have any subgroups, our last line is the last line of the last
1464 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1466 wxASSERT_MSG( pLine
, wxT("last group must have !NULL associated line") );
1471 // no subgroups, so the last line is the line of thelast entry (if any)
1472 return GetLastEntryLine();
1475 // return the last line belonging to the entries of this group (after which
1476 // we can add a new entry), if we don't have any entries we will add the new
1477 // one immediately after the group line itself.
1478 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1480 wxLogTrace( FILECONF_TRACE_MASK
,
1481 wxT(" GetLastEntryLine() for Group '%s'"),
1486 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1488 wxASSERT_MSG( pLine
, wxT("last entry must have !NULL associated line") );
1493 // no entries: insert after the group header, if any
1494 return GetGroupLine();
1497 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry
*pEntry
)
1499 m_pLastEntry
= pEntry
;
1503 // the only situation in which a group without its own line can have
1504 // an entry is when the first entry is added to the initially empty
1505 // root pseudo-group
1506 wxASSERT_MSG( !m_pParent
, wxT("unexpected for non root group") );
1508 // let the group know that it does have a line in the file now
1509 m_pLine
= pEntry
->GetLine();
1513 // ----------------------------------------------------------------------------
1515 // ----------------------------------------------------------------------------
1517 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
1519 // update the line of this group
1520 wxFileConfigLineList
*line
= GetGroupLine();
1521 wxCHECK_RET( line
, wxT("a non root group must have a corresponding line!") );
1523 // +1: skip the leading '/'
1524 line
->SetText(wxString::Format(wxT("[%s]"), GetFullName().c_str() + 1));
1527 // also update all subgroups as they have this groups name in their lines
1528 const size_t nCount
= m_aSubgroups
.GetCount();
1529 for ( size_t n
= 0; n
< nCount
; n
++ )
1531 m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines();
1535 void wxFileConfigGroup::Rename(const wxString
& newName
)
1537 wxCHECK_RET( m_pParent
, wxT("the root group can't be renamed") );
1539 if ( newName
== m_strName
)
1542 // we need to remove the group from the parent and it back under the new
1543 // name to keep the parents array of subgroups alphabetically sorted
1544 m_pParent
->m_aSubgroups
.Remove(this);
1546 m_strName
= newName
;
1548 m_pParent
->m_aSubgroups
.Add(this);
1550 // update the group lines recursively
1551 UpdateGroupAndSubgroupsLines();
1554 wxString
wxFileConfigGroup::GetFullName() const
1558 fullname
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1563 // ----------------------------------------------------------------------------
1565 // ----------------------------------------------------------------------------
1567 // use binary search because the array is sorted
1569 wxFileConfigGroup::FindEntry(const wxString
& name
) const
1573 hi
= m_aEntries
.GetCount();
1575 wxFileConfigEntry
*pEntry
;
1579 pEntry
= m_aEntries
[i
];
1581 #if wxCONFIG_CASE_SENSITIVE
1582 res
= pEntry
->Name().compare(name
);
1584 res
= pEntry
->Name().CmpNoCase(name
);
1599 wxFileConfigGroup::FindSubgroup(const wxString
& name
) const
1603 hi
= m_aSubgroups
.GetCount();
1605 wxFileConfigGroup
*pGroup
;
1609 pGroup
= m_aSubgroups
[i
];
1611 #if wxCONFIG_CASE_SENSITIVE
1612 res
= pGroup
->Name().compare(name
);
1614 res
= pGroup
->Name().CmpNoCase(name
);
1628 // ----------------------------------------------------------------------------
1629 // create a new item
1630 // ----------------------------------------------------------------------------
1632 // create a new entry and add it to the current group
1633 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1635 wxASSERT( FindEntry(strName
) == 0 );
1637 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1639 m_aEntries
.Add(pEntry
);
1643 // create a new group and add it to the current group
1644 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1646 wxASSERT( FindSubgroup(strName
) == 0 );
1648 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1650 m_aSubgroups
.Add(pGroup
);
1654 // ----------------------------------------------------------------------------
1656 // ----------------------------------------------------------------------------
1659 The delete operations are _very_ slow if we delete the last item of this
1660 group (see comments before GetXXXLineXXX functions for more details),
1661 so it's much better to start with the first entry/group if we want to
1662 delete several of them.
1665 bool wxFileConfigGroup::DeleteSubgroupByName(const wxString
& name
)
1667 wxFileConfigGroup
* const pGroup
= FindSubgroup(name
);
1669 return pGroup
? DeleteSubgroup(pGroup
) : false;
1672 // Delete the subgroup and remove all references to it from
1673 // other data structures.
1674 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1676 wxCHECK_MSG( pGroup
, false, wxT("deleting non existing group?") );
1678 wxLogTrace( FILECONF_TRACE_MASK
,
1679 wxT("Deleting group '%s' from '%s'"),
1680 pGroup
->Name().c_str(),
1683 wxLogTrace( FILECONF_TRACE_MASK
,
1684 wxT(" (m_pLine) = prev: %p, this %p, next %p"),
1685 m_pLine
? static_cast<void*>(m_pLine
->Prev()) : 0,
1686 static_cast<void*>(m_pLine
),
1687 m_pLine
? static_cast<void*>(m_pLine
->Next()) : 0 );
1688 wxLogTrace( FILECONF_TRACE_MASK
,
1690 m_pLine
? (const wxChar
*)m_pLine
->Text().c_str()
1693 // delete all entries...
1694 size_t nCount
= pGroup
->m_aEntries
.GetCount();
1696 wxLogTrace(FILECONF_TRACE_MASK
,
1697 wxT("Removing %lu entries"), (unsigned long)nCount
);
1699 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1701 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1705 wxLogTrace( FILECONF_TRACE_MASK
,
1707 pLine
->Text().c_str() );
1708 m_pConfig
->LineListRemove(pLine
);
1712 // ...and subgroups of this subgroup
1713 nCount
= pGroup
->m_aSubgroups
.GetCount();
1715 wxLogTrace( FILECONF_TRACE_MASK
,
1716 wxT("Removing %lu subgroups"), (unsigned long)nCount
);
1718 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1720 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1723 // and then finally the group itself
1724 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1727 wxLogTrace( FILECONF_TRACE_MASK
,
1728 wxT(" Removing line for group '%s' : '%s'"),
1729 pGroup
->Name().c_str(),
1730 pLine
->Text().c_str() );
1731 wxLogTrace( FILECONF_TRACE_MASK
,
1732 wxT(" Removing from group '%s' : '%s'"),
1734 ((m_pLine
) ? (const wxChar
*)m_pLine
->Text().c_str()
1737 // notice that we may do this test inside the previous "if"
1738 // because the last entry's line is surely !NULL
1739 if ( pGroup
== m_pLastGroup
)
1741 wxLogTrace( FILECONF_TRACE_MASK
,
1742 wxT(" Removing last group") );
1744 // our last entry is being deleted, so find the last one which
1745 // stays by going back until we find a subgroup or reach the
1747 const size_t nSubgroups
= m_aSubgroups
.GetCount();
1749 m_pLastGroup
= NULL
;
1750 for ( wxFileConfigLineList
*pl
= pLine
->Prev();
1751 pl
&& !m_pLastGroup
;
1754 // does this line belong to our subgroup?
1755 for ( size_t n
= 0; n
< nSubgroups
; n
++ )
1757 // do _not_ call GetGroupLine! we don't want to add it to
1758 // the local file if it's not already there
1759 if ( m_aSubgroups
[n
]->m_pLine
== pl
)
1761 m_pLastGroup
= m_aSubgroups
[n
];
1766 if ( pl
== m_pLine
)
1771 m_pConfig
->LineListRemove(pLine
);
1775 wxLogTrace( FILECONF_TRACE_MASK
,
1776 wxT(" No line entry for Group '%s'?"),
1777 pGroup
->Name().c_str() );
1780 m_aSubgroups
.Remove(pGroup
);
1786 bool wxFileConfigGroup::DeleteEntry(const wxString
& name
)
1788 wxFileConfigEntry
*pEntry
= FindEntry(name
);
1791 // entry doesn't exist, nothing to do
1795 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1796 if ( pLine
!= NULL
) {
1797 // notice that we may do this test inside the previous "if" because the
1798 // last entry's line is surely !NULL
1799 if ( pEntry
== m_pLastEntry
) {
1800 // our last entry is being deleted - find the last one which stays
1801 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1803 // find the previous entry (if any)
1804 wxFileConfigEntry
*pNewLast
= NULL
;
1805 const wxFileConfigLineList
* const
1806 pNewLastLine
= m_pLastEntry
->GetLine()->Prev();
1807 const size_t nEntries
= m_aEntries
.GetCount();
1808 for ( size_t n
= 0; n
< nEntries
; n
++ ) {
1809 if ( m_aEntries
[n
]->GetLine() == pNewLastLine
) {
1810 pNewLast
= m_aEntries
[n
];
1815 // pNewLast can be NULL here -- it's ok and can happen if we have no
1817 m_pLastEntry
= pNewLast
;
1820 m_pConfig
->LineListRemove(pLine
);
1823 m_aEntries
.Remove(pEntry
);
1829 // ============================================================================
1830 // wxFileConfig::wxFileConfigEntry
1831 // ============================================================================
1833 // ----------------------------------------------------------------------------
1835 // ----------------------------------------------------------------------------
1836 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1837 const wxString
& strName
,
1839 : m_strName(strName
)
1841 wxASSERT( !strName
.empty() );
1843 m_pParent
= pParent
;
1847 m_bHasValue
= false;
1849 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1851 m_strName
.erase(0, 1); // remove first character
1854 // ----------------------------------------------------------------------------
1856 // ----------------------------------------------------------------------------
1858 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1860 if ( m_pLine
!= NULL
) {
1861 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1862 Name().c_str(), m_pParent
->GetFullName().c_str());
1866 Group()->SetLastEntry(this);
1869 // second parameter is false if we read the value from file and prevents the
1870 // entry from being marked as 'dirty'
1871 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1873 if ( bUser
&& IsImmutable() )
1875 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1880 // do nothing if it's the same value: but don't test for it if m_bHasValue
1881 // hadn't been set yet or we'd never write empty values to the file
1882 if ( m_bHasValue
&& strValue
== m_strValue
)
1886 m_strValue
= strValue
;
1890 wxString strValFiltered
;
1892 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1894 strValFiltered
= strValue
;
1897 strValFiltered
= FilterOutValue(strValue
);
1901 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1905 // entry was read from the local config file, just modify the line
1906 m_pLine
->SetText(strLine
);
1908 else // this entry didn't exist in the local file
1910 // add a new line to the file: note that line returned by
1911 // GetLastEntryLine() may be NULL if we're in the root group and it
1912 // doesn't have any entries yet, but this is ok as passing NULL
1913 // line to LineListInsert() means to prepend new line to the list
1914 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1915 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1917 Group()->SetLastEntry(this);
1922 // ============================================================================
1924 // ============================================================================
1926 // ----------------------------------------------------------------------------
1927 // compare functions for array sorting
1928 // ----------------------------------------------------------------------------
1930 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1932 #if wxCONFIG_CASE_SENSITIVE
1933 return p1
->Name().compare(p2
->Name());
1935 return p1
->Name().CmpNoCase(p2
->Name());
1939 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1941 #if wxCONFIG_CASE_SENSITIVE
1942 return p1
->Name().compare(p2
->Name());
1944 return p1
->Name().CmpNoCase(p2
->Name());
1948 // ----------------------------------------------------------------------------
1950 // ----------------------------------------------------------------------------
1952 // undo FilterOutValue
1953 static wxString
FilterInValue(const wxString
& str
)
1959 strResult
.reserve(str
.length());
1961 wxString::const_iterator i
= str
.begin();
1962 const bool bQuoted
= *i
== '"';
1966 for ( const wxString::const_iterator end
= str
.end(); i
!= end
; ++i
)
1968 if ( *i
== wxT('\\') )
1972 wxLogWarning(_("trailing backslash ignored in '%s'"), str
.c_str());
1976 switch ( (*i
).GetValue() )
1979 strResult
+= wxT('\n');
1983 strResult
+= wxT('\r');
1987 strResult
+= wxT('\t');
1991 strResult
+= wxT('\\');
1995 strResult
+= wxT('"');
1999 else // not a backslash
2001 if ( *i
!= wxT('"') || !bQuoted
)
2005 else if ( i
!= end
- 1 )
2007 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2008 i
- str
.begin(), str
.c_str());
2010 //else: it's the last quote of a quoted string, ok
2017 // quote the string before writing it to file
2018 static wxString
FilterOutValue(const wxString
& str
)
2024 strResult
.Alloc(str
.Len());
2026 // quoting is necessary to preserve spaces in the beginning of the string
2027 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2030 strResult
+= wxT('"');
2033 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2034 switch ( str
[n
].GetValue() ) {
2056 //else: fall through
2059 strResult
+= str
[n
];
2060 continue; // nothing special to do
2063 // we get here only for special characters
2064 strResult
<< wxT('\\') << c
;
2068 strResult
+= wxT('"');
2073 // undo FilterOutEntryName
2074 static wxString
FilterInEntryName(const wxString
& str
)
2077 strResult
.Alloc(str
.Len());
2079 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2080 if ( *pc
== wxT('\\') ) {
2081 // we need to test it here or we'd skip past the NUL in the loop line
2082 if ( *++pc
== wxT('\0') )
2092 // sanitize entry or group name: insert '\\' before any special characters
2093 static wxString
FilterOutEntryName(const wxString
& str
)
2096 strResult
.Alloc(str
.Len());
2098 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2099 const wxChar c
= *pc
;
2101 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2102 // which will probably never have special meaning and with which we can't
2103 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2105 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2106 // should *not* be quoted
2109 ((unsigned char)c
< 127) &&
2111 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) )
2113 strResult
+= wxT('\\');
2122 // we can't put ?: in the ctor initializer list because it confuses some
2123 // broken compilers (Borland C++)
2124 static wxString
GetAppName(const wxString
& appName
)
2126 if ( !appName
&& wxTheApp
)
2127 return wxTheApp
->GetAppName();
2132 #endif // wxUSE_CONFIG