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() + 1, &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?
483 memText
.AddLine(wxString(s
, e
));
488 // skip the second EOL byte if it's a DOS one
489 if ( *e
== '\r' && e
[1] == '\n' )
495 // Finally we can parse it all.
496 Parse(memText
, true /* local */);
502 #endif // wxUSE_STREAMS
504 void wxFileConfig::CleanUp()
508 wxFileConfigLineList
*pCur
= m_linesHead
;
509 while ( pCur
!= NULL
) {
510 wxFileConfigLineList
*pNext
= pCur
->Next();
516 wxFileConfig::~wxFileConfig()
525 // ----------------------------------------------------------------------------
526 // parse a config file
527 // ----------------------------------------------------------------------------
529 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
532 size_t nLineCount
= buffer
.GetLineCount();
534 for ( size_t n
= 0; n
< nLineCount
; n
++ )
536 wxString strLine
= buffer
[n
];
537 // FIXME-UTF8: rewrite using iterators, without this buffer
538 wxWxCharBuffer
buf(strLine
.c_str());
539 const wxChar
*pStart
;
542 // add the line to linked list
545 LineListAppend(strLine
);
547 // let the root group have its start line as well
550 m_pCurrentGroup
->SetLine(m_linesTail
);
555 // skip leading spaces
556 for ( pStart
= buf
; wxIsspace(*pStart
); pStart
++ )
559 // skip blank/comment lines
560 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
563 if ( *pStart
== wxT('[') ) { // a new group
566 while ( *++pEnd
!= wxT(']') ) {
567 if ( *pEnd
== wxT('\\') ) {
568 // the next char is escaped, so skip it even if it is ']'
572 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
573 // we reached the end of line, break out of the loop
578 if ( *pEnd
!= wxT(']') ) {
579 wxLogError(_("file '%s': unexpected character %c at line %d."),
580 buffer
.GetName(), *pEnd
, n
+ 1);
581 continue; // skip this line
584 // group name here is always considered as abs path
587 strGroup
<< wxCONFIG_PATH_SEPARATOR
588 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
590 // will create it if doesn't yet exist
595 if ( m_pCurrentGroup
->Parent() )
596 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
597 m_pCurrentGroup
->SetLine(m_linesTail
);
600 // check that there is nothing except comments left on this line
602 while ( *++pEnd
!= wxT('\0') && bCont
) {
611 // ignore whitespace ('\n' impossible here)
615 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
616 buffer
.GetName(), n
+ 1, pEnd
);
623 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
624 if ( *pEnd
== wxT('\\') ) {
625 // next character may be space or not - still take it because it's
626 // quoted (unless there is nothing)
629 // the error message will be given below anyhow
637 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
640 while ( wxIsspace(*pEnd
) )
643 if ( *pEnd
++ != wxT('=') ) {
644 wxLogError(_("file '%s', line %d: '=' expected."),
645 buffer
.GetName(), n
+ 1);
648 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
650 if ( pEntry
== NULL
) {
652 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
655 if ( bLocal
&& pEntry
->IsImmutable() ) {
656 // immutable keys can't be changed by user
657 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
658 buffer
.GetName(), n
+ 1, strKey
.c_str());
661 // the condition below catches the cases (a) and (b) but not (c):
662 // (a) global key found second time in global file
663 // (b) key found second (or more) time in local file
664 // (c) key from global file now found in local one
665 // which is exactly what we want.
666 else if ( !bLocal
|| pEntry
->IsLocal() ) {
667 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
668 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
674 pEntry
->SetLine(m_linesTail
);
677 while ( wxIsspace(*pEnd
) )
680 wxString value
= pEnd
;
681 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
682 value
= FilterInValue(value
);
684 pEntry
->SetValue(value
, false);
690 // ----------------------------------------------------------------------------
692 // ----------------------------------------------------------------------------
694 void wxFileConfig::SetRootPath()
697 m_pCurrentGroup
= m_pRootGroup
;
701 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
703 wxArrayString aParts
;
705 if ( strPath
.empty() ) {
710 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
712 wxSplitPath(aParts
, strPath
);
715 // relative path, combine with current one
716 wxString strFullPath
= m_strPath
;
717 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
718 wxSplitPath(aParts
, strFullPath
);
721 // change current group
723 m_pCurrentGroup
= m_pRootGroup
;
724 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
725 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
726 if ( pNextGroup
== NULL
)
728 if ( !createMissingComponents
)
731 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
734 m_pCurrentGroup
= pNextGroup
;
737 // recombine path parts in one variable
739 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
740 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
746 void wxFileConfig::SetPath(const wxString
& strPath
)
748 DoSetPath(strPath
, true /* create missing path components */);
751 // ----------------------------------------------------------------------------
753 // ----------------------------------------------------------------------------
755 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
758 return GetNextGroup(str
, lIndex
);
761 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
763 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) {
764 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
771 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
774 return GetNextEntry(str
, lIndex
);
777 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
779 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) {
780 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
787 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
789 size_t n
= m_pCurrentGroup
->Entries().GetCount();
791 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
792 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
793 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
794 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
795 n
+= GetNumberOfEntries(true);
796 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
803 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
805 size_t n
= m_pCurrentGroup
->Groups().GetCount();
807 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
808 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
809 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
810 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
811 n
+= GetNumberOfGroups(true);
812 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
819 // ----------------------------------------------------------------------------
820 // tests for existence
821 // ----------------------------------------------------------------------------
823 bool wxFileConfig::HasGroup(const wxString
& strName
) const
825 // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
826 // but there is no group with empty name so treat this separately
827 if ( strName
.empty() )
830 const wxString pathOld
= GetPath();
832 wxFileConfig
*self
= wx_const_cast(wxFileConfig
*, this);
834 rc
= self
->DoSetPath(strName
, false /* don't create missing components */);
836 self
->SetPath(pathOld
);
841 bool wxFileConfig::HasEntry(const wxString
& entry
) const
843 // path is the part before the last "/"
844 wxString path
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
);
846 // except in the special case of "/keyname" when there is nothing before "/"
847 if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR
)
849 path
= wxCONFIG_PATH_SEPARATOR
;
852 // change to the path of the entry if necessary and remember the old path
853 // to restore it later
855 wxFileConfig
* const self
= wx_const_cast(wxFileConfig
*, this);
859 if ( pathOld
.empty() )
860 pathOld
= wxCONFIG_PATH_SEPARATOR
;
862 if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) )
868 // check if the entry exists in this group
869 const bool exists
= m_pCurrentGroup
->FindEntry(
870 entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
;
872 // restore the old path if we changed it above
873 if ( !pathOld
.empty() )
875 self
->SetPath(pathOld
);
881 // ----------------------------------------------------------------------------
883 // ----------------------------------------------------------------------------
885 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
887 wxConfigPathChanger
path(this, key
);
889 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
890 if (pEntry
== NULL
) {
894 *pStr
= pEntry
->Value();
899 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
902 if ( !Read(key
, &str
) )
905 // extra spaces shouldn't prevent us from reading numeric values
908 return str
.ToLong(pl
);
913 bool wxFileConfig::DoReadBinary(const wxString
& key
, wxMemoryBuffer
* buf
) const
915 wxCHECK_MSG( buf
, false, _T("NULL buffer") );
918 if ( !Read(key
, &str
) )
921 *buf
= wxBase64Decode(str
.ToAscii());
925 #endif // wxUSE_BASE64
927 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
929 wxConfigPathChanger
path(this, key
);
930 wxString strName
= path
.Name();
932 wxLogTrace( FILECONF_TRACE_MASK
,
933 _T(" Writing String '%s' = '%s' to Group '%s'"),
938 if ( strName
.empty() )
940 // setting the value of a group is an error
942 wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") );
944 // ... except if it's empty in which case it's a way to force it's creation
946 wxLogTrace( FILECONF_TRACE_MASK
,
947 _T(" Creating group %s"),
948 m_pCurrentGroup
->Name().c_str() );
952 // this will add a line for this group if it didn't have it before
954 (void)m_pCurrentGroup
->GetGroupLine();
958 // writing an entry check that the name is reasonable
959 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
961 wxLogError( _("Config entry name cannot start with '%c'."),
962 wxCONFIG_IMMUTABLE_PREFIX
);
966 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
970 wxLogTrace( FILECONF_TRACE_MASK
,
971 _T(" Adding Entry %s"),
973 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
976 wxLogTrace( FILECONF_TRACE_MASK
,
977 _T(" Setting value %s"),
979 pEntry
->SetValue(szValue
);
987 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
989 return Write(key
, wxString::Format(_T("%ld"), lValue
));
994 bool wxFileConfig::DoWriteBinary(const wxString
& key
, const wxMemoryBuffer
& buf
)
996 return Write(key
, wxBase64Encode(buf
));
999 #endif // wxUSE_BASE64
1001 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
1003 if ( !IsDirty() || !m_fnLocalFile
.GetFullPath() )
1006 // set the umask if needed
1007 wxCHANGE_UMASK(m_umask
);
1009 wxTempFile
file(m_fnLocalFile
.GetFullPath());
1011 if ( !file
.IsOpened() )
1013 wxLogError(_("can't open user configuration file."));
1017 // write all strings to file
1019 filetext
.reserve(4096);
1020 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1022 filetext
<< p
->Text() << wxTextFile::GetEOL();
1025 if ( !file
.Write(filetext
, *m_conv
) )
1027 wxLogError(_("can't write user configuration file."));
1031 if ( !file
.Commit() )
1033 wxLogError(_("Failed to update user configuration file."));
1040 #if defined(__WXMAC__)
1041 m_fnLocalFile
.MacSetTypeAndCreator('TEXT', 'ttxt');
1049 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
)
1051 // save unconditionally, even if not dirty
1052 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1054 wxString line
= p
->Text();
1055 line
+= wxTextFile::GetEOL();
1057 wxCharBuffer
buf(line
.mb_str(conv
));
1058 if ( !os
.Write(buf
, strlen(buf
)) )
1060 wxLogError(_("Error saving user configuration data."));
1071 #endif // wxUSE_STREAMS
1073 // ----------------------------------------------------------------------------
1074 // renaming groups/entries
1075 // ----------------------------------------------------------------------------
1077 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1078 const wxString
& newName
)
1080 wxASSERT_MSG( oldName
.find(wxCONFIG_PATH_SEPARATOR
) == wxString::npos
,
1081 _T("RenameEntry(): paths are not supported") );
1083 // check that the entry exists
1084 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1088 // check that the new entry doesn't already exist
1089 if ( m_pCurrentGroup
->FindEntry(newName
) )
1092 // delete the old entry, create the new one
1093 wxString value
= oldEntry
->Value();
1094 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1099 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1100 newEntry
->SetValue(value
);
1105 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1106 const wxString
& newName
)
1108 // check that the group exists
1109 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1113 // check that the new group doesn't already exist
1114 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1117 group
->Rename(newName
);
1124 // ----------------------------------------------------------------------------
1125 // delete groups/entries
1126 // ----------------------------------------------------------------------------
1128 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1130 wxConfigPathChanger
path(this, key
);
1132 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1137 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1138 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1139 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1140 SetPath(wxT("..")); // changes m_pCurrentGroup!
1141 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1143 //else: never delete the root group
1149 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1151 wxConfigPathChanger
path(this, RemoveTrailingSeparator(key
));
1153 if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) )
1156 path
.UpdateIfDeleted();
1163 bool wxFileConfig::DeleteAll()
1167 if ( m_fnLocalFile
.IsOk() )
1169 if ( m_fnLocalFile
.FileExists() &&
1170 !wxRemoveFile(m_fnLocalFile
.GetFullPath()) )
1172 wxLogSysError(_("can't delete user configuration file '%s'"),
1173 m_fnLocalFile
.GetFullPath().c_str());
1183 // ----------------------------------------------------------------------------
1184 // linked list functions
1185 // ----------------------------------------------------------------------------
1187 // append a new line to the end of the list
1189 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1191 wxLogTrace( FILECONF_TRACE_MASK
,
1192 _T(" ** Adding Line '%s'"),
1194 wxLogTrace( FILECONF_TRACE_MASK
,
1196 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1198 wxLogTrace( FILECONF_TRACE_MASK
,
1200 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1203 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1205 if ( m_linesTail
== NULL
)
1208 m_linesHead
= pLine
;
1213 m_linesTail
->SetNext(pLine
);
1214 pLine
->SetPrev(m_linesTail
);
1217 m_linesTail
= pLine
;
1219 wxLogTrace( FILECONF_TRACE_MASK
,
1221 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1223 wxLogTrace( FILECONF_TRACE_MASK
,
1225 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1231 // insert a new line after the given one or in the very beginning if !pLine
1232 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1233 wxFileConfigLineList
*pLine
)
1235 wxLogTrace( FILECONF_TRACE_MASK
,
1236 _T(" ** Inserting Line '%s' after '%s'"),
1238 ((pLine
) ? (const wxChar
*)pLine
->Text().c_str()
1240 wxLogTrace( FILECONF_TRACE_MASK
,
1242 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1244 wxLogTrace( FILECONF_TRACE_MASK
,
1246 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1249 if ( pLine
== m_linesTail
)
1250 return LineListAppend(str
);
1252 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1253 if ( pLine
== NULL
)
1255 // prepend to the list
1256 pNewLine
->SetNext(m_linesHead
);
1257 m_linesHead
->SetPrev(pNewLine
);
1258 m_linesHead
= pNewLine
;
1262 // insert before pLine
1263 wxFileConfigLineList
*pNext
= pLine
->Next();
1264 pNewLine
->SetNext(pNext
);
1265 pNewLine
->SetPrev(pLine
);
1266 pNext
->SetPrev(pNewLine
);
1267 pLine
->SetNext(pNewLine
);
1270 wxLogTrace( FILECONF_TRACE_MASK
,
1272 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1274 wxLogTrace( FILECONF_TRACE_MASK
,
1276 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1282 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1284 wxLogTrace( FILECONF_TRACE_MASK
,
1285 _T(" ** Removing Line '%s'"),
1286 pLine
->Text().c_str() );
1287 wxLogTrace( FILECONF_TRACE_MASK
,
1289 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1291 wxLogTrace( FILECONF_TRACE_MASK
,
1293 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1296 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1297 *pNext
= pLine
->Next();
1301 if ( pPrev
== NULL
)
1302 m_linesHead
= pNext
;
1304 pPrev
->SetNext(pNext
);
1308 if ( pNext
== NULL
)
1309 m_linesTail
= pPrev
;
1311 pNext
->SetPrev(pPrev
);
1313 if ( m_pRootGroup
->GetGroupLine() == pLine
)
1314 m_pRootGroup
->SetLine(m_linesHead
);
1316 wxLogTrace( FILECONF_TRACE_MASK
,
1318 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1320 wxLogTrace( FILECONF_TRACE_MASK
,
1322 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1328 bool wxFileConfig::LineListIsEmpty()
1330 return m_linesHead
== NULL
;
1333 // ============================================================================
1334 // wxFileConfig::wxFileConfigGroup
1335 // ============================================================================
1337 // ----------------------------------------------------------------------------
1339 // ----------------------------------------------------------------------------
1342 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1343 const wxString
& strName
,
1344 wxFileConfig
*pConfig
)
1345 : m_aEntries(CompareEntries
),
1346 m_aSubgroups(CompareGroups
),
1349 m_pConfig
= pConfig
;
1350 m_pParent
= pParent
;
1353 m_pLastEntry
= NULL
;
1354 m_pLastGroup
= NULL
;
1357 // dtor deletes all children
1358 wxFileConfigGroup::~wxFileConfigGroup()
1361 size_t n
, nCount
= m_aEntries
.GetCount();
1362 for ( n
= 0; n
< nCount
; n
++ )
1363 delete m_aEntries
[n
];
1366 nCount
= m_aSubgroups
.GetCount();
1367 for ( n
= 0; n
< nCount
; n
++ )
1368 delete m_aSubgroups
[n
];
1371 // ----------------------------------------------------------------------------
1373 // ----------------------------------------------------------------------------
1375 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1377 // for a normal (i.e. not root) group this method shouldn't be called twice
1378 // unless we are resetting the line
1379 wxASSERT_MSG( !m_pParent
|| !m_pLine
|| !pLine
,
1380 _T("changing line for a non-root group?") );
1386 This is a bit complicated, so let me explain it in details. All lines that
1387 were read from the local file (the only one we will ever modify) are stored
1388 in a (doubly) linked list. Our problem is to know at which position in this
1389 list should we insert the new entries/subgroups. To solve it we keep three
1390 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1392 m_pLine points to the line containing "[group_name]"
1393 m_pLastEntry points to the last entry of this group in the local file.
1394 m_pLastGroup subgroup
1396 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1397 from the local file, the corresponding variable is set. However, if the group
1398 was read from the global file and then modified or created by the application
1399 these variables are still NULL and we need to create the corresponding lines.
1400 See the following functions (and comments preceding them) for the details of
1403 Also, when our last entry/group are deleted we need to find the new last
1404 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1405 of lines until it either founds an entry/subgroup (and this is the new last
1406 element) or the m_pLine of the group, in which case there are no more entries
1407 (or subgroups) left and m_pLast<element> becomes NULL.
1409 NB: This last problem could be avoided for entries if we added new entries
1410 immediately after m_pLine, but in this case the entries would appear
1411 backwards in the config file (OTOH, it's not that important) and as we
1412 would still need to do it for the subgroups the code wouldn't have been
1413 significantly less complicated.
1416 // Return the line which contains "[our name]". If we're still not in the list,
1417 // add our line to it immediately after the last line of our parent group if we
1418 // have it or in the very beginning if we're the root group.
1419 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1421 wxLogTrace( FILECONF_TRACE_MASK
,
1422 _T(" GetGroupLine() for Group '%s'"),
1427 wxLogTrace( FILECONF_TRACE_MASK
,
1428 _T(" Getting Line item pointer") );
1430 wxFileConfigGroup
*pParent
= Parent();
1432 // this group wasn't present in local config file, add it now
1435 wxLogTrace( FILECONF_TRACE_MASK
,
1436 _T(" checking parent '%s'"),
1437 pParent
->Name().c_str() );
1439 wxString strFullName
;
1441 // add 1 to the name because we don't want to start with '/'
1442 strFullName
<< wxT("[")
1443 << FilterOutEntryName(GetFullName().c_str() + 1)
1445 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1446 pParent
->GetLastGroupLine());
1447 pParent
->SetLastGroup(this); // we're surely after all the others
1449 //else: this is the root group and so we return NULL because we don't
1450 // have any group line
1456 // Return the last line belonging to the subgroups of this group (after which
1457 // we can add a new subgroup), if we don't have any subgroups or entries our
1458 // last line is the group line (m_pLine) itself.
1459 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1461 // if we have any subgroups, our last line is the last line of the last
1465 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1467 wxASSERT_MSG( pLine
, _T("last group must have !NULL associated line") );
1472 // no subgroups, so the last line is the line of thelast entry (if any)
1473 return GetLastEntryLine();
1476 // return the last line belonging to the entries of this group (after which
1477 // we can add a new entry), if we don't have any entries we will add the new
1478 // one immediately after the group line itself.
1479 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1481 wxLogTrace( FILECONF_TRACE_MASK
,
1482 _T(" GetLastEntryLine() for Group '%s'"),
1487 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1489 wxASSERT_MSG( pLine
, _T("last entry must have !NULL associated line") );
1494 // no entries: insert after the group header, if any
1495 return GetGroupLine();
1498 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry
*pEntry
)
1500 m_pLastEntry
= pEntry
;
1504 // the only situation in which a group without its own line can have
1505 // an entry is when the first entry is added to the initially empty
1506 // root pseudo-group
1507 wxASSERT_MSG( !m_pParent
, _T("unexpected for non root group") );
1509 // let the group know that it does have a line in the file now
1510 m_pLine
= pEntry
->GetLine();
1514 // ----------------------------------------------------------------------------
1516 // ----------------------------------------------------------------------------
1518 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
1520 // update the line of this group
1521 wxFileConfigLineList
*line
= GetGroupLine();
1522 wxCHECK_RET( line
, _T("a non root group must have a corresponding line!") );
1524 // +1: skip the leading '/'
1525 line
->SetText(wxString::Format(_T("[%s]"), GetFullName().c_str() + 1));
1528 // also update all subgroups as they have this groups name in their lines
1529 const size_t nCount
= m_aSubgroups
.GetCount();
1530 for ( size_t n
= 0; n
< nCount
; n
++ )
1532 m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines();
1536 void wxFileConfigGroup::Rename(const wxString
& newName
)
1538 wxCHECK_RET( m_pParent
, _T("the root group can't be renamed") );
1540 if ( newName
== m_strName
)
1543 // we need to remove the group from the parent and it back under the new
1544 // name to keep the parents array of subgroups alphabetically sorted
1545 m_pParent
->m_aSubgroups
.Remove(this);
1547 m_strName
= newName
;
1549 m_pParent
->m_aSubgroups
.Add(this);
1551 // update the group lines recursively
1552 UpdateGroupAndSubgroupsLines();
1555 wxString
wxFileConfigGroup::GetFullName() const
1559 fullname
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1564 // ----------------------------------------------------------------------------
1566 // ----------------------------------------------------------------------------
1568 // use binary search because the array is sorted
1570 wxFileConfigGroup::FindEntry(const wxString
& name
) const
1574 hi
= m_aEntries
.GetCount();
1576 wxFileConfigEntry
*pEntry
;
1580 pEntry
= m_aEntries
[i
];
1582 #if wxCONFIG_CASE_SENSITIVE
1583 res
= pEntry
->Name().compare(name
);
1585 res
= pEntry
->Name().CmpNoCase(name
);
1600 wxFileConfigGroup::FindSubgroup(const wxString
& name
) const
1604 hi
= m_aSubgroups
.GetCount();
1606 wxFileConfigGroup
*pGroup
;
1610 pGroup
= m_aSubgroups
[i
];
1612 #if wxCONFIG_CASE_SENSITIVE
1613 res
= pGroup
->Name().compare(name
);
1615 res
= pGroup
->Name().CmpNoCase(name
);
1629 // ----------------------------------------------------------------------------
1630 // create a new item
1631 // ----------------------------------------------------------------------------
1633 // create a new entry and add it to the current group
1634 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1636 wxASSERT( FindEntry(strName
) == 0 );
1638 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1640 m_aEntries
.Add(pEntry
);
1644 // create a new group and add it to the current group
1645 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1647 wxASSERT( FindSubgroup(strName
) == 0 );
1649 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1651 m_aSubgroups
.Add(pGroup
);
1655 // ----------------------------------------------------------------------------
1657 // ----------------------------------------------------------------------------
1660 The delete operations are _very_ slow if we delete the last item of this
1661 group (see comments before GetXXXLineXXX functions for more details),
1662 so it's much better to start with the first entry/group if we want to
1663 delete several of them.
1666 bool wxFileConfigGroup::DeleteSubgroupByName(const wxString
& name
)
1668 wxFileConfigGroup
* const pGroup
= FindSubgroup(name
);
1670 return pGroup
? DeleteSubgroup(pGroup
) : false;
1673 // Delete the subgroup and remove all references to it from
1674 // other data structures.
1675 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1677 wxCHECK_MSG( pGroup
, false, _T("deleting non existing group?") );
1679 wxLogTrace( FILECONF_TRACE_MASK
,
1680 _T("Deleting group '%s' from '%s'"),
1681 pGroup
->Name().c_str(),
1684 wxLogTrace( FILECONF_TRACE_MASK
,
1685 _T(" (m_pLine) = prev: %p, this %p, next %p"),
1686 m_pLine
? wx_static_cast(void*, m_pLine
->Prev()) : 0,
1687 wx_static_cast(void*, m_pLine
),
1688 m_pLine
? wx_static_cast(void*, m_pLine
->Next()) : 0 );
1689 wxLogTrace( FILECONF_TRACE_MASK
,
1691 m_pLine
? (const wxChar
*)m_pLine
->Text().c_str()
1694 // delete all entries...
1695 size_t nCount
= pGroup
->m_aEntries
.GetCount();
1697 wxLogTrace(FILECONF_TRACE_MASK
,
1698 _T("Removing %lu entries"), (unsigned long)nCount
);
1700 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1702 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1706 wxLogTrace( FILECONF_TRACE_MASK
,
1708 pLine
->Text().c_str() );
1709 m_pConfig
->LineListRemove(pLine
);
1713 // ...and subgroups of this subgroup
1714 nCount
= pGroup
->m_aSubgroups
.GetCount();
1716 wxLogTrace( FILECONF_TRACE_MASK
,
1717 _T("Removing %lu subgroups"), (unsigned long)nCount
);
1719 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1721 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1724 // and then finally the group itself
1725 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1728 wxLogTrace( FILECONF_TRACE_MASK
,
1729 _T(" Removing line for group '%s' : '%s'"),
1730 pGroup
->Name().c_str(),
1731 pLine
->Text().c_str() );
1732 wxLogTrace( FILECONF_TRACE_MASK
,
1733 _T(" Removing from group '%s' : '%s'"),
1735 ((m_pLine
) ? (const wxChar
*)m_pLine
->Text().c_str()
1738 // notice that we may do this test inside the previous "if"
1739 // because the last entry's line is surely !NULL
1740 if ( pGroup
== m_pLastGroup
)
1742 wxLogTrace( FILECONF_TRACE_MASK
,
1743 _T(" Removing last group") );
1745 // our last entry is being deleted, so find the last one which
1746 // stays by going back until we find a subgroup or reach the
1748 const size_t nSubgroups
= m_aSubgroups
.GetCount();
1750 m_pLastGroup
= NULL
;
1751 for ( wxFileConfigLineList
*pl
= pLine
->Prev();
1752 pl
&& !m_pLastGroup
;
1755 // does this line belong to our subgroup?
1756 for ( size_t n
= 0; n
< nSubgroups
; n
++ )
1758 // do _not_ call GetGroupLine! we don't want to add it to
1759 // the local file if it's not already there
1760 if ( m_aSubgroups
[n
]->m_pLine
== pl
)
1762 m_pLastGroup
= m_aSubgroups
[n
];
1767 if ( pl
== m_pLine
)
1772 m_pConfig
->LineListRemove(pLine
);
1776 wxLogTrace( FILECONF_TRACE_MASK
,
1777 _T(" No line entry for Group '%s'?"),
1778 pGroup
->Name().c_str() );
1781 m_aSubgroups
.Remove(pGroup
);
1787 bool wxFileConfigGroup::DeleteEntry(const wxString
& name
)
1789 wxFileConfigEntry
*pEntry
= FindEntry(name
);
1792 // entry doesn't exist, nothing to do
1796 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1797 if ( pLine
!= NULL
) {
1798 // notice that we may do this test inside the previous "if" because the
1799 // last entry's line is surely !NULL
1800 if ( pEntry
== m_pLastEntry
) {
1801 // our last entry is being deleted - find the last one which stays
1802 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1804 // go back until we find another entry or reach the group's line
1805 wxFileConfigEntry
*pNewLast
= NULL
;
1806 size_t n
, nEntries
= m_aEntries
.GetCount();
1807 wxFileConfigLineList
*pl
;
1808 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1809 // is it our subgroup?
1810 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1811 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1812 pNewLast
= m_aEntries
[n
];
1815 if ( pNewLast
!= NULL
) // found?
1819 if ( pl
== m_pLine
) {
1820 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1822 // we've reached the group line without finding any subgroups
1823 m_pLastEntry
= NULL
;
1826 m_pLastEntry
= pNewLast
;
1829 m_pConfig
->LineListRemove(pLine
);
1832 m_aEntries
.Remove(pEntry
);
1838 // ============================================================================
1839 // wxFileConfig::wxFileConfigEntry
1840 // ============================================================================
1842 // ----------------------------------------------------------------------------
1844 // ----------------------------------------------------------------------------
1845 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1846 const wxString
& strName
,
1848 : m_strName(strName
)
1850 wxASSERT( !strName
.empty() );
1852 m_pParent
= pParent
;
1856 m_bHasValue
= false;
1858 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1860 m_strName
.erase(0, 1); // remove first character
1863 // ----------------------------------------------------------------------------
1865 // ----------------------------------------------------------------------------
1867 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1869 if ( m_pLine
!= NULL
) {
1870 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1871 Name().c_str(), m_pParent
->GetFullName().c_str());
1875 Group()->SetLastEntry(this);
1878 // second parameter is false if we read the value from file and prevents the
1879 // entry from being marked as 'dirty'
1880 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1882 if ( bUser
&& IsImmutable() )
1884 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1889 // do nothing if it's the same value: but don't test for it if m_bHasValue
1890 // hadn't been set yet or we'd never write empty values to the file
1891 if ( m_bHasValue
&& strValue
== m_strValue
)
1895 m_strValue
= strValue
;
1899 wxString strValFiltered
;
1901 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1903 strValFiltered
= strValue
;
1906 strValFiltered
= FilterOutValue(strValue
);
1910 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1914 // entry was read from the local config file, just modify the line
1915 m_pLine
->SetText(strLine
);
1917 else // this entry didn't exist in the local file
1919 // add a new line to the file: note that line returned by
1920 // GetLastEntryLine() may be NULL if we're in the root group and it
1921 // doesn't have any entries yet, but this is ok as passing NULL
1922 // line to LineListInsert() means to prepend new line to the list
1923 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1924 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1926 Group()->SetLastEntry(this);
1931 // ============================================================================
1933 // ============================================================================
1935 // ----------------------------------------------------------------------------
1936 // compare functions for array sorting
1937 // ----------------------------------------------------------------------------
1939 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1941 #if wxCONFIG_CASE_SENSITIVE
1942 return p1
->Name().compare(p2
->Name());
1944 return p1
->Name().CmpNoCase(p2
->Name());
1948 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1950 #if wxCONFIG_CASE_SENSITIVE
1951 return p1
->Name().compare(p2
->Name());
1953 return p1
->Name().CmpNoCase(p2
->Name());
1957 // ----------------------------------------------------------------------------
1959 // ----------------------------------------------------------------------------
1961 // undo FilterOutValue
1962 static wxString
FilterInValue(const wxString
& str
)
1968 strResult
.reserve(str
.length());
1970 wxString::const_iterator i
= str
.begin();
1971 const bool bQuoted
= *i
== '"';
1975 for ( const wxString::const_iterator end
= str
.end(); i
!= end
; ++i
)
1977 if ( *i
== wxT('\\') )
1981 wxLogWarning(_("trailing backslash ignored in '%s'"), str
.c_str());
1985 switch ( (*i
).GetValue() )
1988 strResult
+= wxT('\n');
1992 strResult
+= wxT('\r');
1996 strResult
+= wxT('\t');
2000 strResult
+= wxT('\\');
2004 strResult
+= wxT('"');
2008 else // not a backslash
2010 if ( *i
!= wxT('"') || !bQuoted
)
2014 else if ( i
!= end
- 1 )
2016 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2017 i
- str
.begin(), str
.c_str());
2019 //else: it's the last quote of a quoted string, ok
2026 // quote the string before writing it to file
2027 static wxString
FilterOutValue(const wxString
& str
)
2033 strResult
.Alloc(str
.Len());
2035 // quoting is necessary to preserve spaces in the beginning of the string
2036 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2039 strResult
+= wxT('"');
2042 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2043 switch ( str
[n
].GetValue() ) {
2065 //else: fall through
2068 strResult
+= str
[n
];
2069 continue; // nothing special to do
2072 // we get here only for special characters
2073 strResult
<< wxT('\\') << c
;
2077 strResult
+= wxT('"');
2082 // undo FilterOutEntryName
2083 static wxString
FilterInEntryName(const wxString
& str
)
2086 strResult
.Alloc(str
.Len());
2088 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2089 if ( *pc
== wxT('\\') ) {
2090 // we need to test it here or we'd skip past the NUL in the loop line
2091 if ( *++pc
== _T('\0') )
2101 // sanitize entry or group name: insert '\\' before any special characters
2102 static wxString
FilterOutEntryName(const wxString
& str
)
2105 strResult
.Alloc(str
.Len());
2107 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2108 const wxChar c
= *pc
;
2110 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2111 // which will probably never have special meaning and with which we can't
2112 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2114 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2115 // should *not* be quoted
2118 ((unsigned char)c
< 127) &&
2120 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) )
2122 strResult
+= wxT('\\');
2131 // we can't put ?: in the ctor initializer list because it confuses some
2132 // broken compilers (Borland C++)
2133 static wxString
GetAppName(const wxString
& appName
)
2135 if ( !appName
&& wxTheApp
)
2136 return wxTheApp
->GetAppName();
2141 #endif // wxUSE_CONFIG