1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/fileconf.cpp
3 // Purpose: implementation of wxFileConfig derivation of wxConfig
4 // Author: Vadim Zeitlin
6 // Created: 07.04.98 (adapted from appconf.cpp)
8 // Copyright: (c) 1997 Karsten Ballüder & Vadim Zeitlin
9 // Ballueder@usa.net <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
24 #if wxUSE_CONFIG && wxUSE_FILECONFIG
27 #include "wx/dynarray.h"
28 #include "wx/string.h"
32 #include "wx/utils.h" // for wxGetHomeDir
34 #include "wx/stream.h"
35 #endif // wxUSE_STREAMS
39 #include "wx/textfile.h"
40 #include "wx/memtext.h"
41 #include "wx/config.h"
42 #include "wx/fileconf.h"
43 #include "wx/filefn.h"
45 #include "wx/base64.h"
47 #include "wx/stdpaths.h"
49 #if defined(__WXMAC__)
50 #include "wx/mac/private.h" // includes mac headers
53 #if defined(__WXMSW__)
54 #include "wx/msw/private.h"
64 // ----------------------------------------------------------------------------
66 // ----------------------------------------------------------------------------
67 #define CONST_CAST ((wxFileConfig *)this)->
69 // ----------------------------------------------------------------------------
71 // ----------------------------------------------------------------------------
77 #define FILECONF_TRACE_MASK _T("fileconf")
79 // ----------------------------------------------------------------------------
80 // global functions declarations
81 // ----------------------------------------------------------------------------
83 // compare functions for sorting the arrays
84 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
85 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
88 static wxString
FilterInValue(const wxString
& str
);
89 static wxString
FilterOutValue(const wxString
& str
);
91 static wxString
FilterInEntryName(const wxString
& str
);
92 static wxString
FilterOutEntryName(const wxString
& str
);
94 // get the name to use in wxFileConfig ctor
95 static wxString
GetAppName(const wxString
& appname
);
97 // ============================================================================
99 // ============================================================================
101 // ----------------------------------------------------------------------------
102 // "template" array types
103 // ----------------------------------------------------------------------------
105 #ifdef WXMAKINGDLL_BASE
106 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
,
108 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
,
111 WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
112 WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
);
115 // ----------------------------------------------------------------------------
116 // wxFileConfigLineList
117 // ----------------------------------------------------------------------------
119 // we store all lines of the local config file as a linked list in memory
120 class wxFileConfigLineList
123 void SetNext(wxFileConfigLineList
*pNext
) { m_pNext
= pNext
; }
124 void SetPrev(wxFileConfigLineList
*pPrev
) { m_pPrev
= pPrev
; }
127 wxFileConfigLineList(const wxString
& str
,
128 wxFileConfigLineList
*pNext
= NULL
) : m_strLine(str
)
129 { SetNext(pNext
); SetPrev(NULL
); }
131 // next/prev nodes in the linked list
132 wxFileConfigLineList
*Next() const { return m_pNext
; }
133 wxFileConfigLineList
*Prev() const { return m_pPrev
; }
135 // get/change lines text
136 void SetText(const wxString
& str
) { m_strLine
= str
; }
137 const wxString
& Text() const { return m_strLine
; }
140 wxString m_strLine
; // line contents
141 wxFileConfigLineList
*m_pNext
, // next node
142 *m_pPrev
; // previous one
144 DECLARE_NO_COPY_CLASS(wxFileConfigLineList
)
147 // ----------------------------------------------------------------------------
148 // wxFileConfigEntry: a name/value pair
149 // ----------------------------------------------------------------------------
151 class wxFileConfigEntry
154 wxFileConfigGroup
*m_pParent
; // group that contains us
156 wxString m_strName
, // entry name
158 bool m_bImmutable
:1, // can be overriden locally?
159 m_bHasValue
:1; // set after first call to SetValue()
161 int m_nLine
; // used if m_pLine == NULL only
163 // pointer to our line in the linked list or NULL if it was found in global
164 // file (which we don't modify)
165 wxFileConfigLineList
*m_pLine
;
168 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
169 const wxString
& strName
, int nLine
);
172 const wxString
& Name() const { return m_strName
; }
173 const wxString
& Value() const { return m_strValue
; }
174 wxFileConfigGroup
*Group() const { return m_pParent
; }
175 bool IsImmutable() const { return m_bImmutable
; }
176 bool IsLocal() const { return m_pLine
!= 0; }
177 int Line() const { return m_nLine
; }
178 wxFileConfigLineList
*
179 GetLine() const { return m_pLine
; }
181 // modify entry attributes
182 void SetValue(const wxString
& strValue
, bool bUser
= true);
183 void SetLine(wxFileConfigLineList
*pLine
);
185 DECLARE_NO_COPY_CLASS(wxFileConfigEntry
)
188 // ----------------------------------------------------------------------------
189 // wxFileConfigGroup: container of entries and other groups
190 // ----------------------------------------------------------------------------
192 class wxFileConfigGroup
195 wxFileConfig
*m_pConfig
; // config object we belong to
196 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
197 ArrayEntries m_aEntries
; // entries in this group
198 ArrayGroups m_aSubgroups
; // subgroups
199 wxString m_strName
; // group's name
200 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
201 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
202 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
204 // DeleteSubgroupByName helper
205 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
208 void UpdateGroupAndSubgroupsLines();
212 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
214 // dtor deletes all entries and subgroups also
215 ~wxFileConfigGroup();
218 const wxString
& Name() const { return m_strName
; }
219 wxFileConfigGroup
*Parent() const { return m_pParent
; }
220 wxFileConfig
*Config() const { return m_pConfig
; }
222 const ArrayEntries
& Entries() const { return m_aEntries
; }
223 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
224 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
226 // find entry/subgroup (NULL if not found)
227 wxFileConfigGroup
*FindSubgroup(const wxString
& name
) const;
228 wxFileConfigEntry
*FindEntry (const wxString
& name
) const;
230 // delete entry/subgroup, return false if doesn't exist
231 bool DeleteSubgroupByName(const wxString
& name
);
232 bool DeleteEntry(const wxString
& name
);
234 // create new entry/subgroup returning pointer to newly created element
235 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
236 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
238 void SetLine(wxFileConfigLineList
*pLine
);
240 // rename: no checks are done to ensure that the name is unique!
241 void Rename(const wxString
& newName
);
244 wxString
GetFullName() const;
246 // get the last line belonging to an entry/subgroup of this group
247 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
248 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
249 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
251 // called by entries/subgroups when they're created/deleted
252 void SetLastEntry(wxFileConfigEntry
*pEntry
);
253 void SetLastGroup(wxFileConfigGroup
*pGroup
)
254 { m_pLastGroup
= pGroup
; }
256 DECLARE_NO_COPY_CLASS(wxFileConfigGroup
)
259 // ============================================================================
261 // ============================================================================
263 // ----------------------------------------------------------------------------
265 // ----------------------------------------------------------------------------
267 // this function modifies in place the given wxFileName object if it doesn't
268 // already have an extension
270 // note that it's slightly misnamed under Mac as there it doesn't add an
271 // extension but modifies the file name instead, so you shouldn't suppose that
272 // fn.HasExt() is true after it returns
273 static void AddConfFileExtIfNeeded(wxFileName
& fn
)
277 #if defined( __WXMAC__ )
278 fn
.SetName(fn
.GetName() + wxT(" Preferences"));
279 #elif defined( __UNIX__ )
280 fn
.SetExt(wxT(".conf"));
282 fn
.SetExt(wxT(".ini"));
287 wxString
wxFileConfig::GetGlobalDir()
289 return wxStandardPaths::Get().GetConfigDir();
292 wxString
wxFileConfig::GetLocalDir(int style
)
296 wxStandardPathsBase
& stdp
= wxStandardPaths::Get();
298 // it so happens that user data directory is a subdirectory of user config
299 // directory on all supported platforms, which explains why we use it here
300 return style
& wxCONFIG_USE_SUBDIR
? stdp
.GetUserDataDir()
301 : stdp
.GetUserConfigDir();
304 wxFileName
wxFileConfig::GetGlobalFile(const wxString
& szFile
)
306 wxFileName
fn(GetGlobalDir(), szFile
);
308 AddConfFileExtIfNeeded(fn
);
313 wxFileName
wxFileConfig::GetLocalFile(const wxString
& szFile
, int style
)
315 wxFileName
fn(GetLocalDir(style
), szFile
);
318 if ( !(style
& wxCONFIG_USE_SUBDIR
) )
320 // dot-files under Unix start with, well, a dot (but OTOH they usually
321 // don't have any specific extension)
322 fn
.SetName(wxT('.') + fn
.GetName());
324 else // we do append ".conf" extension to config files in subdirectories
327 AddConfFileExtIfNeeded(fn
);
333 // ----------------------------------------------------------------------------
335 // ----------------------------------------------------------------------------
336 IMPLEMENT_ABSTRACT_CLASS(wxFileConfig
, wxConfigBase
)
338 void wxFileConfig::Init()
341 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
346 // It's not an error if (one of the) file(s) doesn't exist.
348 // parse the global file
349 if ( m_fnGlobalFile
.IsOk() && m_fnGlobalFile
.FileExists() )
351 wxTextFile
fileGlobal(m_fnGlobalFile
.GetFullPath());
353 if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) )
355 Parse(fileGlobal
, false /* global */);
360 wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile
.GetFullPath().c_str());
364 // parse the local file
365 if ( m_fnLocalFile
.IsOk() && m_fnLocalFile
.FileExists() )
367 wxTextFile
fileLocal(m_fnLocalFile
.GetFullPath());
368 if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) )
370 Parse(fileLocal
, true /* local */);
375 wxLogWarning(_("can't open user configuration file '%s'."), m_fnLocalFile
.GetFullPath().c_str() );
382 // constructor supports creation of wxFileConfig objects of any type
383 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
384 const wxString
& strLocal
, const wxString
& strGlobal
,
386 const wxMBConv
& conv
)
387 : wxConfigBase(::GetAppName(appName
), vendorName
,
390 m_fnLocalFile(strLocal
),
391 m_fnGlobalFile(strGlobal
),
394 // Make up names for files if empty
395 if ( !m_fnLocalFile
.IsOk() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
396 m_fnLocalFile
= GetLocalFile(GetAppName(), style
);
398 if ( !m_fnGlobalFile
.IsOk() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
399 m_fnGlobalFile
= GetGlobalFile(GetAppName());
401 // Check if styles are not supplied, but filenames are, in which case
402 // add the correct styles.
403 if ( m_fnLocalFile
.IsOk() )
404 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
406 if ( m_fnGlobalFile
.IsOk() )
407 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
409 // if the path is not absolute, prepend the standard directory to it
410 // unless explicitly asked not to
411 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
413 if ( m_fnLocalFile
.IsOk() )
414 m_fnLocalFile
.MakeAbsolute(GetLocalDir(style
));
416 if ( m_fnGlobalFile
.IsOk() )
417 m_fnGlobalFile
.MakeAbsolute(GetGlobalDir());
427 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, const wxMBConv
& conv
)
428 : m_conv(conv
.Clone())
430 // always local_file when this constructor is called (?)
431 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
434 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
439 // read the entire stream contents in memory
441 static const size_t chunkLen
= 1024;
443 wxMemoryBuffer
buf(chunkLen
);
446 inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
);
447 buf
.UngetAppendBuf(inStream
.LastRead());
449 const wxStreamError err
= inStream
.GetLastError();
451 if ( err
!= wxSTREAM_NO_ERROR
&& err
!= wxSTREAM_EOF
)
453 wxLogError(_("Error reading config options."));
457 while ( !inStream
.Eof() );
461 cbuf
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen(), &len
);
462 if ( !len
&& buf
.GetDataLen() )
464 wxLogError(_("Failed to read config options."));
466 #else // !wxUSE_UNICODE
467 // no need for conversion
468 cbuf
= wxCharBuffer::CreateNonOwned((char *)buf
.GetData());
469 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
472 // now break it into lines
473 wxMemoryText memText
;
474 for ( const wxChar
*s
= cbuf
; ; ++s
)
477 while ( *e
!= '\0' && *e
!= '\n' && *e
!= '\r' )
480 // notice that we throw away the original EOL kind here, maybe we
481 // should preserve it?
482 memText
.AddLine(wxString(s
, e
));
487 // skip the second EOL byte if it's a DOS one
488 if ( *e
== '\r' && e
[1] == '\n' )
494 // Finally we can parse it all.
495 Parse(memText
, true /* local */);
501 #endif // wxUSE_STREAMS
503 void wxFileConfig::CleanUp()
507 wxFileConfigLineList
*pCur
= m_linesHead
;
508 while ( pCur
!= NULL
) {
509 wxFileConfigLineList
*pNext
= pCur
->Next();
515 wxFileConfig::~wxFileConfig()
524 // ----------------------------------------------------------------------------
525 // parse a config file
526 // ----------------------------------------------------------------------------
528 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
531 size_t nLineCount
= buffer
.GetLineCount();
533 for ( size_t n
= 0; n
< nLineCount
; n
++ )
535 wxString strLine
= buffer
[n
];
536 // FIXME-UTF8: rewrite using iterators, without this buffer
537 wxWxCharBuffer
buf(strLine
.c_str());
538 const wxChar
*pStart
;
541 // add the line to linked list
544 LineListAppend(strLine
);
546 // let the root group have its start line as well
549 m_pCurrentGroup
->SetLine(m_linesTail
);
554 // skip leading spaces
555 for ( pStart
= buf
; wxIsspace(*pStart
); pStart
++ )
558 // skip blank/comment lines
559 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
562 if ( *pStart
== wxT('[') ) { // a new group
565 while ( *++pEnd
!= wxT(']') ) {
566 if ( *pEnd
== wxT('\\') ) {
567 // the next char is escaped, so skip it even if it is ']'
571 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
572 // we reached the end of line, break out of the loop
577 if ( *pEnd
!= wxT(']') ) {
578 wxLogError(_("file '%s': unexpected character %c at line %d."),
579 buffer
.GetName(), *pEnd
, n
+ 1);
580 continue; // skip this line
583 // group name here is always considered as abs path
586 strGroup
<< wxCONFIG_PATH_SEPARATOR
587 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
589 // will create it if doesn't yet exist
594 if ( m_pCurrentGroup
->Parent() )
595 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
596 m_pCurrentGroup
->SetLine(m_linesTail
);
599 // check that there is nothing except comments left on this line
601 while ( *++pEnd
!= wxT('\0') && bCont
) {
610 // ignore whitespace ('\n' impossible here)
614 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
615 buffer
.GetName(), n
+ 1, pEnd
);
622 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
623 if ( *pEnd
== wxT('\\') ) {
624 // next character may be space or not - still take it because it's
625 // quoted (unless there is nothing)
628 // the error message will be given below anyhow
636 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
639 while ( wxIsspace(*pEnd
) )
642 if ( *pEnd
++ != wxT('=') ) {
643 wxLogError(_("file '%s', line %d: '=' expected."),
644 buffer
.GetName(), n
+ 1);
647 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
649 if ( pEntry
== NULL
) {
651 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
654 if ( bLocal
&& pEntry
->IsImmutable() ) {
655 // immutable keys can't be changed by user
656 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
657 buffer
.GetName(), n
+ 1, strKey
.c_str());
660 // the condition below catches the cases (a) and (b) but not (c):
661 // (a) global key found second time in global file
662 // (b) key found second (or more) time in local file
663 // (c) key from global file now found in local one
664 // which is exactly what we want.
665 else if ( !bLocal
|| pEntry
->IsLocal() ) {
666 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
667 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
673 pEntry
->SetLine(m_linesTail
);
676 while ( wxIsspace(*pEnd
) )
679 wxString value
= pEnd
;
680 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
681 value
= FilterInValue(value
);
683 pEntry
->SetValue(value
, false);
689 // ----------------------------------------------------------------------------
691 // ----------------------------------------------------------------------------
693 void wxFileConfig::SetRootPath()
696 m_pCurrentGroup
= m_pRootGroup
;
700 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
702 wxArrayString aParts
;
704 if ( strPath
.empty() ) {
709 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
711 wxSplitPath(aParts
, strPath
);
714 // relative path, combine with current one
715 wxString strFullPath
= m_strPath
;
716 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
717 wxSplitPath(aParts
, strFullPath
);
720 // change current group
722 m_pCurrentGroup
= m_pRootGroup
;
723 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
724 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
725 if ( pNextGroup
== NULL
)
727 if ( !createMissingComponents
)
730 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
733 m_pCurrentGroup
= pNextGroup
;
736 // recombine path parts in one variable
738 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
739 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
745 void wxFileConfig::SetPath(const wxString
& strPath
)
747 DoSetPath(strPath
, true /* create missing path components */);
750 // ----------------------------------------------------------------------------
752 // ----------------------------------------------------------------------------
754 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
757 return GetNextGroup(str
, lIndex
);
760 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
762 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) {
763 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
770 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
773 return GetNextEntry(str
, lIndex
);
776 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
778 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) {
779 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
786 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
788 size_t n
= m_pCurrentGroup
->Entries().GetCount();
790 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
791 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
792 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
793 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
794 n
+= GetNumberOfEntries(true);
795 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
802 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
804 size_t n
= m_pCurrentGroup
->Groups().GetCount();
806 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
807 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
808 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
809 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
810 n
+= GetNumberOfGroups(true);
811 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
818 // ----------------------------------------------------------------------------
819 // tests for existence
820 // ----------------------------------------------------------------------------
822 bool wxFileConfig::HasGroup(const wxString
& strName
) const
824 // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
825 // but there is no group with empty name so treat this separately
826 if ( strName
.empty() )
829 const wxString pathOld
= GetPath();
831 wxFileConfig
*self
= wx_const_cast(wxFileConfig
*, this);
833 rc
= self
->DoSetPath(strName
, false /* don't create missing components */);
835 self
->SetPath(pathOld
);
840 bool wxFileConfig::HasEntry(const wxString
& entry
) const
842 // path is the part before the last "/"
843 wxString path
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
);
845 // except in the special case of "/keyname" when there is nothing before "/"
846 if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR
)
848 path
= wxCONFIG_PATH_SEPARATOR
;
851 // change to the path of the entry if necessary and remember the old path
852 // to restore it later
854 wxFileConfig
* const self
= wx_const_cast(wxFileConfig
*, this);
858 if ( pathOld
.empty() )
859 pathOld
= wxCONFIG_PATH_SEPARATOR
;
861 if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) )
867 // check if the entry exists in this group
868 const bool exists
= m_pCurrentGroup
->FindEntry(
869 entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
;
871 // restore the old path if we changed it above
872 if ( !pathOld
.empty() )
874 self
->SetPath(pathOld
);
880 // ----------------------------------------------------------------------------
882 // ----------------------------------------------------------------------------
884 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
886 wxConfigPathChanger
path(this, key
);
888 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
889 if (pEntry
== NULL
) {
893 *pStr
= pEntry
->Value();
898 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
901 if ( !Read(key
, &str
) )
904 // extra spaces shouldn't prevent us from reading numeric values
907 return str
.ToLong(pl
);
912 bool wxFileConfig::DoReadBinary(const wxString
& key
, wxMemoryBuffer
* buf
) const
914 wxCHECK_MSG( buf
, false, _T("NULL buffer") );
917 if ( !Read(key
, &str
) )
920 *buf
= wxBase64Decode(str
.ToAscii());
924 #endif // wxUSE_BASE64
926 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
928 wxConfigPathChanger
path(this, key
);
929 wxString strName
= path
.Name();
931 wxLogTrace( FILECONF_TRACE_MASK
,
932 _T(" Writing String '%s' = '%s' to Group '%s'"),
937 if ( strName
.empty() )
939 // setting the value of a group is an error
941 wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") );
943 // ... except if it's empty in which case it's a way to force it's creation
945 wxLogTrace( FILECONF_TRACE_MASK
,
946 _T(" Creating group %s"),
947 m_pCurrentGroup
->Name().c_str() );
951 // this will add a line for this group if it didn't have it before
953 (void)m_pCurrentGroup
->GetGroupLine();
957 // writing an entry check that the name is reasonable
958 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
960 wxLogError( _("Config entry name cannot start with '%c'."),
961 wxCONFIG_IMMUTABLE_PREFIX
);
965 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
969 wxLogTrace( FILECONF_TRACE_MASK
,
970 _T(" Adding Entry %s"),
972 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
975 wxLogTrace( FILECONF_TRACE_MASK
,
976 _T(" Setting value %s"),
978 pEntry
->SetValue(szValue
);
986 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
988 return Write(key
, wxString::Format(_T("%ld"), lValue
));
993 bool wxFileConfig::DoWriteBinary(const wxString
& key
, const wxMemoryBuffer
& buf
)
995 return Write(key
, wxBase64Encode(buf
));
998 #endif // wxUSE_BASE64
1000 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
1002 if ( !IsDirty() || !m_fnLocalFile
.GetFullPath() )
1005 // set the umask if needed
1006 wxCHANGE_UMASK(m_umask
);
1008 wxTempFile
file(m_fnLocalFile
.GetFullPath());
1010 if ( !file
.IsOpened() )
1012 wxLogError(_("can't open user configuration file."));
1016 // write all strings to file
1018 filetext
.reserve(4096);
1019 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1021 filetext
<< p
->Text() << wxTextFile::GetEOL();
1024 if ( !file
.Write(filetext
, *m_conv
) )
1026 wxLogError(_("can't write user configuration file."));
1030 if ( !file
.Commit() )
1032 wxLogError(_("Failed to update user configuration file."));
1039 #if defined(__WXMAC__)
1040 m_fnLocalFile
.MacSetTypeAndCreator('TEXT', 'ttxt');
1048 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
)
1050 // save unconditionally, even if not dirty
1051 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1053 wxString line
= p
->Text();
1054 line
+= wxTextFile::GetEOL();
1056 wxCharBuffer
buf(line
.mb_str(conv
));
1057 if ( !os
.Write(buf
, strlen(buf
)) )
1059 wxLogError(_("Error saving user configuration data."));
1070 #endif // wxUSE_STREAMS
1072 // ----------------------------------------------------------------------------
1073 // renaming groups/entries
1074 // ----------------------------------------------------------------------------
1076 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1077 const wxString
& newName
)
1079 wxASSERT_MSG( oldName
.find(wxCONFIG_PATH_SEPARATOR
) == wxString::npos
,
1080 _T("RenameEntry(): paths are not supported") );
1082 // check that the entry exists
1083 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1087 // check that the new entry doesn't already exist
1088 if ( m_pCurrentGroup
->FindEntry(newName
) )
1091 // delete the old entry, create the new one
1092 wxString value
= oldEntry
->Value();
1093 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1098 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1099 newEntry
->SetValue(value
);
1104 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1105 const wxString
& newName
)
1107 // check that the group exists
1108 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1112 // check that the new group doesn't already exist
1113 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1116 group
->Rename(newName
);
1123 // ----------------------------------------------------------------------------
1124 // delete groups/entries
1125 // ----------------------------------------------------------------------------
1127 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1129 wxConfigPathChanger
path(this, key
);
1131 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1136 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1137 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1138 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1139 SetPath(wxT("..")); // changes m_pCurrentGroup!
1140 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1142 //else: never delete the root group
1148 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1150 wxConfigPathChanger
path(this, RemoveTrailingSeparator(key
));
1152 if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) )
1155 path
.UpdateIfDeleted();
1162 bool wxFileConfig::DeleteAll()
1166 if ( m_fnLocalFile
.IsOk() )
1168 if ( m_fnLocalFile
.FileExists() &&
1169 !wxRemoveFile(m_fnLocalFile
.GetFullPath()) )
1171 wxLogSysError(_("can't delete user configuration file '%s'"),
1172 m_fnLocalFile
.GetFullPath().c_str());
1182 // ----------------------------------------------------------------------------
1183 // linked list functions
1184 // ----------------------------------------------------------------------------
1186 // append a new line to the end of the list
1188 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1190 wxLogTrace( FILECONF_TRACE_MASK
,
1191 _T(" ** Adding Line '%s'"),
1193 wxLogTrace( FILECONF_TRACE_MASK
,
1195 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1197 wxLogTrace( FILECONF_TRACE_MASK
,
1199 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1202 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1204 if ( m_linesTail
== NULL
)
1207 m_linesHead
= pLine
;
1212 m_linesTail
->SetNext(pLine
);
1213 pLine
->SetPrev(m_linesTail
);
1216 m_linesTail
= pLine
;
1218 wxLogTrace( FILECONF_TRACE_MASK
,
1220 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1222 wxLogTrace( FILECONF_TRACE_MASK
,
1224 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1230 // insert a new line after the given one or in the very beginning if !pLine
1231 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1232 wxFileConfigLineList
*pLine
)
1234 wxLogTrace( FILECONF_TRACE_MASK
,
1235 _T(" ** Inserting Line '%s' after '%s'"),
1237 ((pLine
) ? (const wxChar
*)pLine
->Text().c_str()
1239 wxLogTrace( FILECONF_TRACE_MASK
,
1241 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1243 wxLogTrace( FILECONF_TRACE_MASK
,
1245 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1248 if ( pLine
== m_linesTail
)
1249 return LineListAppend(str
);
1251 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1252 if ( pLine
== NULL
)
1254 // prepend to the list
1255 pNewLine
->SetNext(m_linesHead
);
1256 m_linesHead
->SetPrev(pNewLine
);
1257 m_linesHead
= pNewLine
;
1261 // insert before pLine
1262 wxFileConfigLineList
*pNext
= pLine
->Next();
1263 pNewLine
->SetNext(pNext
);
1264 pNewLine
->SetPrev(pLine
);
1265 pNext
->SetPrev(pNewLine
);
1266 pLine
->SetNext(pNewLine
);
1269 wxLogTrace( FILECONF_TRACE_MASK
,
1271 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1273 wxLogTrace( FILECONF_TRACE_MASK
,
1275 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1281 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1283 wxLogTrace( FILECONF_TRACE_MASK
,
1284 _T(" ** Removing Line '%s'"),
1285 pLine
->Text().c_str() );
1286 wxLogTrace( FILECONF_TRACE_MASK
,
1288 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1290 wxLogTrace( FILECONF_TRACE_MASK
,
1292 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1295 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1296 *pNext
= pLine
->Next();
1300 if ( pPrev
== NULL
)
1301 m_linesHead
= pNext
;
1303 pPrev
->SetNext(pNext
);
1307 if ( pNext
== NULL
)
1308 m_linesTail
= pPrev
;
1310 pNext
->SetPrev(pPrev
);
1312 if ( m_pRootGroup
->GetGroupLine() == pLine
)
1313 m_pRootGroup
->SetLine(m_linesHead
);
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 _T("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 _T(" GetGroupLine() for Group '%s'"),
1426 wxLogTrace( FILECONF_TRACE_MASK
,
1427 _T(" 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 _T(" 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
, _T("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 _T(" GetLastEntryLine() for Group '%s'"),
1486 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1488 wxASSERT_MSG( pLine
, _T("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
, _T("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
, _T("a non root group must have a corresponding line!") );
1523 // +1: skip the leading '/'
1524 line
->SetText(wxString::Format(_T("[%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
, _T("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, _T("deleting non existing group?") );
1678 wxLogTrace( FILECONF_TRACE_MASK
,
1679 _T("Deleting group '%s' from '%s'"),
1680 pGroup
->Name().c_str(),
1683 wxLogTrace( FILECONF_TRACE_MASK
,
1684 _T(" (m_pLine) = prev: %p, this %p, next %p"),
1685 m_pLine
? wx_static_cast(void*, m_pLine
->Prev()) : 0,
1686 wx_static_cast(void*, m_pLine
),
1687 m_pLine
? wx_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 _T("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 _T("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 _T(" Removing line for group '%s' : '%s'"),
1729 pGroup
->Name().c_str(),
1730 pLine
->Text().c_str() );
1731 wxLogTrace( FILECONF_TRACE_MASK
,
1732 _T(" 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 _T(" 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 _T(" 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 // go back until we find another entry or reach the group's line
1804 wxFileConfigEntry
*pNewLast
= NULL
;
1805 size_t n
, nEntries
= m_aEntries
.GetCount();
1806 wxFileConfigLineList
*pl
;
1807 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1808 // is it our subgroup?
1809 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1810 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1811 pNewLast
= m_aEntries
[n
];
1814 if ( pNewLast
!= NULL
) // found?
1818 if ( pl
== m_pLine
) {
1819 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1821 // we've reached the group line without finding any subgroups
1822 m_pLastEntry
= NULL
;
1825 m_pLastEntry
= pNewLast
;
1828 m_pConfig
->LineListRemove(pLine
);
1831 m_aEntries
.Remove(pEntry
);
1837 // ============================================================================
1838 // wxFileConfig::wxFileConfigEntry
1839 // ============================================================================
1841 // ----------------------------------------------------------------------------
1843 // ----------------------------------------------------------------------------
1844 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1845 const wxString
& strName
,
1847 : m_strName(strName
)
1849 wxASSERT( !strName
.empty() );
1851 m_pParent
= pParent
;
1855 m_bHasValue
= false;
1857 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1859 m_strName
.erase(0, 1); // remove first character
1862 // ----------------------------------------------------------------------------
1864 // ----------------------------------------------------------------------------
1866 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1868 if ( m_pLine
!= NULL
) {
1869 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1870 Name().c_str(), m_pParent
->GetFullName().c_str());
1874 Group()->SetLastEntry(this);
1877 // second parameter is false if we read the value from file and prevents the
1878 // entry from being marked as 'dirty'
1879 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1881 if ( bUser
&& IsImmutable() )
1883 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1888 // do nothing if it's the same value: but don't test for it if m_bHasValue
1889 // hadn't been set yet or we'd never write empty values to the file
1890 if ( m_bHasValue
&& strValue
== m_strValue
)
1894 m_strValue
= strValue
;
1898 wxString strValFiltered
;
1900 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1902 strValFiltered
= strValue
;
1905 strValFiltered
= FilterOutValue(strValue
);
1909 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1913 // entry was read from the local config file, just modify the line
1914 m_pLine
->SetText(strLine
);
1916 else // this entry didn't exist in the local file
1918 // add a new line to the file: note that line returned by
1919 // GetLastEntryLine() may be NULL if we're in the root group and it
1920 // doesn't have any entries yet, but this is ok as passing NULL
1921 // line to LineListInsert() means to prepend new line to the list
1922 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1923 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1925 Group()->SetLastEntry(this);
1930 // ============================================================================
1932 // ============================================================================
1934 // ----------------------------------------------------------------------------
1935 // compare functions for array sorting
1936 // ----------------------------------------------------------------------------
1938 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1940 #if wxCONFIG_CASE_SENSITIVE
1941 return p1
->Name().compare(p2
->Name());
1943 return p1
->Name().CmpNoCase(p2
->Name());
1947 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1949 #if wxCONFIG_CASE_SENSITIVE
1950 return p1
->Name().compare(p2
->Name());
1952 return p1
->Name().CmpNoCase(p2
->Name());
1956 // ----------------------------------------------------------------------------
1958 // ----------------------------------------------------------------------------
1960 // undo FilterOutValue
1961 static wxString
FilterInValue(const wxString
& str
)
1967 strResult
.reserve(str
.length());
1969 wxString::const_iterator i
= str
.begin();
1970 const bool bQuoted
= *i
== '"';
1974 for ( const wxString::const_iterator end
= str
.end(); i
!= end
; ++i
)
1976 if ( *i
== wxT('\\') )
1980 wxLogWarning(_("trailing backslash ignored in '%s'"), str
.c_str());
1984 switch ( (*i
).GetValue() )
1987 strResult
+= wxT('\n');
1991 strResult
+= wxT('\r');
1995 strResult
+= wxT('\t');
1999 strResult
+= wxT('\\');
2003 strResult
+= wxT('"');
2007 else // not a backslash
2009 if ( *i
!= wxT('"') || !bQuoted
)
2013 else if ( i
!= end
- 1 )
2015 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2016 i
- str
.begin(), str
.c_str());
2018 //else: it's the last quote of a quoted string, ok
2025 // quote the string before writing it to file
2026 static wxString
FilterOutValue(const wxString
& str
)
2032 strResult
.Alloc(str
.Len());
2034 // quoting is necessary to preserve spaces in the beginning of the string
2035 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2038 strResult
+= wxT('"');
2041 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2042 switch ( str
[n
].GetValue() ) {
2064 //else: fall through
2067 strResult
+= str
[n
];
2068 continue; // nothing special to do
2071 // we get here only for special characters
2072 strResult
<< wxT('\\') << c
;
2076 strResult
+= wxT('"');
2081 // undo FilterOutEntryName
2082 static wxString
FilterInEntryName(const wxString
& str
)
2085 strResult
.Alloc(str
.Len());
2087 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2088 if ( *pc
== wxT('\\') ) {
2089 // we need to test it here or we'd skip past the NUL in the loop line
2090 if ( *++pc
== _T('\0') )
2100 // sanitize entry or group name: insert '\\' before any special characters
2101 static wxString
FilterOutEntryName(const wxString
& str
)
2104 strResult
.Alloc(str
.Len());
2106 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2107 const wxChar c
= *pc
;
2109 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2110 // which will probably never have special meaning and with which we can't
2111 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2113 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2114 // should *not* be quoted
2117 ((unsigned char)c
< 127) &&
2119 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) )
2121 strResult
+= wxT('\\');
2130 // we can't put ?: in the ctor initializer list because it confuses some
2131 // broken compilers (Borland C++)
2132 static wxString
GetAppName(const wxString
& appName
)
2134 if ( !appName
&& wxTheApp
)
2135 return wxTheApp
->GetAppName();
2140 #endif // wxUSE_CONFIG