1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/fileconf.cpp
3 // Purpose: implementation of wxFileConfig derivation of wxConfig
4 // Author: Vadim Zeitlin
6 // Created: 07.04.98 (adapted from appconf.cpp)
8 // Copyright: (c) 1997 Karsten Ballueder & Vadim Zeitlin
9 // Ballueder@usa.net <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
24 #if wxUSE_CONFIG && wxUSE_FILECONFIG
27 #include "wx/dynarray.h"
28 #include "wx/string.h"
32 #include "wx/utils.h" // for wxGetHomeDir
34 #include "wx/stream.h"
35 #endif // wxUSE_STREAMS
39 #include "wx/textfile.h"
40 #include "wx/memtext.h"
41 #include "wx/config.h"
42 #include "wx/fileconf.h"
43 #include "wx/filefn.h"
45 #include "wx/base64.h"
47 #include "wx/stdpaths.h"
49 #if defined(__WXMSW__)
50 #include "wx/msw/private.h"
60 // ----------------------------------------------------------------------------
62 // ----------------------------------------------------------------------------
63 #define CONST_CAST ((wxFileConfig *)this)->
65 // ----------------------------------------------------------------------------
67 // ----------------------------------------------------------------------------
73 #define FILECONF_TRACE_MASK _T("fileconf")
75 // ----------------------------------------------------------------------------
76 // global functions declarations
77 // ----------------------------------------------------------------------------
79 // compare functions for sorting the arrays
80 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
81 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
84 static wxString
FilterInValue(const wxString
& str
);
85 static wxString
FilterOutValue(const wxString
& str
);
87 static wxString
FilterInEntryName(const wxString
& str
);
88 static wxString
FilterOutEntryName(const wxString
& str
);
90 // get the name to use in wxFileConfig ctor
91 static wxString
GetAppName(const wxString
& appname
);
93 // ============================================================================
95 // ============================================================================
97 // ----------------------------------------------------------------------------
98 // "template" array types
99 // ----------------------------------------------------------------------------
101 #ifdef WXMAKINGDLL_BASE
102 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
,
104 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
,
107 WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
108 WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
);
111 // ----------------------------------------------------------------------------
112 // wxFileConfigLineList
113 // ----------------------------------------------------------------------------
115 // we store all lines of the local config file as a linked list in memory
116 class wxFileConfigLineList
119 void SetNext(wxFileConfigLineList
*pNext
) { m_pNext
= pNext
; }
120 void SetPrev(wxFileConfigLineList
*pPrev
) { m_pPrev
= pPrev
; }
123 wxFileConfigLineList(const wxString
& str
,
124 wxFileConfigLineList
*pNext
= NULL
) : m_strLine(str
)
125 { SetNext(pNext
); SetPrev(NULL
); }
127 // next/prev nodes in the linked list
128 wxFileConfigLineList
*Next() const { return m_pNext
; }
129 wxFileConfigLineList
*Prev() const { return m_pPrev
; }
131 // get/change lines text
132 void SetText(const wxString
& str
) { m_strLine
= str
; }
133 const wxString
& Text() const { return m_strLine
; }
136 wxString m_strLine
; // line contents
137 wxFileConfigLineList
*m_pNext
, // next node
138 *m_pPrev
; // previous one
140 wxDECLARE_NO_COPY_CLASS(wxFileConfigLineList
);
143 // ----------------------------------------------------------------------------
144 // wxFileConfigEntry: a name/value pair
145 // ----------------------------------------------------------------------------
147 class wxFileConfigEntry
150 wxFileConfigGroup
*m_pParent
; // group that contains us
152 wxString m_strName
, // entry name
154 bool m_bImmutable
:1, // can be overriden locally?
155 m_bHasValue
:1; // set after first call to SetValue()
157 int m_nLine
; // used if m_pLine == NULL only
159 // pointer to our line in the linked list or NULL if it was found in global
160 // file (which we don't modify)
161 wxFileConfigLineList
*m_pLine
;
164 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
165 const wxString
& strName
, int nLine
);
168 const wxString
& Name() const { return m_strName
; }
169 const wxString
& Value() const { return m_strValue
; }
170 wxFileConfigGroup
*Group() const { return m_pParent
; }
171 bool IsImmutable() const { return m_bImmutable
; }
172 bool IsLocal() const { return m_pLine
!= 0; }
173 int Line() const { return m_nLine
; }
174 wxFileConfigLineList
*
175 GetLine() const { return m_pLine
; }
177 // modify entry attributes
178 void SetValue(const wxString
& strValue
, bool bUser
= true);
179 void SetLine(wxFileConfigLineList
*pLine
);
181 wxDECLARE_NO_COPY_CLASS(wxFileConfigEntry
);
184 // ----------------------------------------------------------------------------
185 // wxFileConfigGroup: container of entries and other groups
186 // ----------------------------------------------------------------------------
188 class wxFileConfigGroup
191 wxFileConfig
*m_pConfig
; // config object we belong to
192 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
193 ArrayEntries m_aEntries
; // entries in this group
194 ArrayGroups m_aSubgroups
; // subgroups
195 wxString m_strName
; // group's name
196 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
197 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
198 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
200 // DeleteSubgroupByName helper
201 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
204 void UpdateGroupAndSubgroupsLines();
208 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
210 // dtor deletes all entries and subgroups also
211 ~wxFileConfigGroup();
214 const wxString
& Name() const { return m_strName
; }
215 wxFileConfigGroup
*Parent() const { return m_pParent
; }
216 wxFileConfig
*Config() const { return m_pConfig
; }
218 const ArrayEntries
& Entries() const { return m_aEntries
; }
219 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
220 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
222 // find entry/subgroup (NULL if not found)
223 wxFileConfigGroup
*FindSubgroup(const wxString
& name
) const;
224 wxFileConfigEntry
*FindEntry (const wxString
& name
) const;
226 // delete entry/subgroup, return false if doesn't exist
227 bool DeleteSubgroupByName(const wxString
& name
);
228 bool DeleteEntry(const wxString
& name
);
230 // create new entry/subgroup returning pointer to newly created element
231 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
232 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
234 void SetLine(wxFileConfigLineList
*pLine
);
236 // rename: no checks are done to ensure that the name is unique!
237 void Rename(const wxString
& newName
);
240 wxString
GetFullName() const;
242 // get the last line belonging to an entry/subgroup of this group
243 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
244 // may be NULL for "/" only
245 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
246 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
248 // called by entries/subgroups when they're created/deleted
249 void SetLastEntry(wxFileConfigEntry
*pEntry
);
250 void SetLastGroup(wxFileConfigGroup
*pGroup
)
251 { m_pLastGroup
= pGroup
; }
253 wxDECLARE_NO_COPY_CLASS(wxFileConfigGroup
);
256 // ============================================================================
258 // ============================================================================
260 // ----------------------------------------------------------------------------
262 // ----------------------------------------------------------------------------
264 // this function modifies in place the given wxFileName object if it doesn't
265 // already have an extension
267 // note that it's slightly misnamed under Mac as there it doesn't add an
268 // extension but modifies the file name instead, so you shouldn't suppose that
269 // fn.HasExt() is true after it returns
270 static void AddConfFileExtIfNeeded(wxFileName
& fn
)
274 #if defined( __WXMAC__ )
275 fn
.SetName(fn
.GetName() + wxT(" Preferences"));
276 #elif defined( __UNIX__ )
277 fn
.SetExt(wxT("conf"));
279 fn
.SetExt(wxT("ini"));
284 wxString
wxFileConfig::GetGlobalDir()
286 return wxStandardPaths::Get().GetConfigDir();
289 wxString
wxFileConfig::GetLocalDir(int style
)
293 wxStandardPathsBase
& stdp
= wxStandardPaths::Get();
295 // it so happens that user data directory is a subdirectory of user config
296 // directory on all supported platforms, which explains why we use it here
297 return style
& wxCONFIG_USE_SUBDIR
? stdp
.GetUserDataDir()
298 : stdp
.GetUserConfigDir();
301 wxFileName
wxFileConfig::GetGlobalFile(const wxString
& szFile
)
303 wxFileName
fn(GetGlobalDir(), szFile
);
305 AddConfFileExtIfNeeded(fn
);
310 wxFileName
wxFileConfig::GetLocalFile(const wxString
& szFile
, int style
)
312 wxFileName
fn(GetLocalDir(style
), szFile
);
314 #if defined( __UNIX__ ) && !defined( __WXMAC__ )
315 if ( !(style
& wxCONFIG_USE_SUBDIR
) )
317 // dot-files under Unix start with, well, a dot (but OTOH they usually
318 // don't have any specific extension)
319 fn
.SetName(wxT('.') + fn
.GetName());
321 else // we do append ".conf" extension to config files in subdirectories
322 #endif // defined( __UNIX__ ) && !defined( __WXMAC__ )
324 AddConfFileExtIfNeeded(fn
);
330 // ----------------------------------------------------------------------------
332 // ----------------------------------------------------------------------------
333 IMPLEMENT_ABSTRACT_CLASS(wxFileConfig
, wxConfigBase
)
335 void wxFileConfig::Init()
338 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
343 // It's not an error if (one of the) file(s) doesn't exist.
345 // parse the global file
346 if ( m_fnGlobalFile
.IsOk() && m_fnGlobalFile
.FileExists() )
348 wxTextFile
fileGlobal(m_fnGlobalFile
.GetFullPath());
350 if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) )
352 Parse(fileGlobal
, false /* global */);
357 wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile
.GetFullPath().c_str());
361 // parse the local file
362 if ( m_fnLocalFile
.IsOk() && m_fnLocalFile
.FileExists() )
364 wxTextFile
fileLocal(m_fnLocalFile
.GetFullPath());
365 if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) )
367 Parse(fileLocal
, true /* local */);
372 const wxString path
= m_fnLocalFile
.GetFullPath();
373 wxLogWarning(_("can't open user configuration file '%s'."),
376 if ( m_fnLocalFile
.FileExists() )
378 wxLogWarning(_("Changes won't be saved to avoid overwriting the existing file \"%s\""),
380 m_fnLocalFile
.Clear();
388 // constructor supports creation of wxFileConfig objects of any type
389 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
390 const wxString
& strLocal
, const wxString
& strGlobal
,
392 const wxMBConv
& conv
)
393 : wxConfigBase(::GetAppName(appName
), vendorName
,
396 m_fnLocalFile(strLocal
),
397 m_fnGlobalFile(strGlobal
),
400 // Make up names for files if empty
401 if ( !m_fnLocalFile
.IsOk() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
402 m_fnLocalFile
= GetLocalFile(GetAppName(), style
);
404 if ( !m_fnGlobalFile
.IsOk() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
405 m_fnGlobalFile
= GetGlobalFile(GetAppName());
407 // Check if styles are not supplied, but filenames are, in which case
408 // add the correct styles.
409 if ( m_fnLocalFile
.IsOk() )
410 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
412 if ( m_fnGlobalFile
.IsOk() )
413 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
415 // if the path is not absolute, prepend the standard directory to it
416 // unless explicitly asked not to
417 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
419 if ( m_fnLocalFile
.IsOk() )
420 m_fnLocalFile
.MakeAbsolute(GetLocalDir(style
));
422 if ( m_fnGlobalFile
.IsOk() )
423 m_fnGlobalFile
.MakeAbsolute(GetGlobalDir());
433 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, const wxMBConv
& conv
)
434 : m_conv(conv
.Clone())
436 // always local_file when this constructor is called (?)
437 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
440 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
445 // read the entire stream contents in memory
447 static const size_t chunkLen
= 1024;
449 wxMemoryBuffer
buf(chunkLen
);
452 inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
);
453 buf
.UngetAppendBuf(inStream
.LastRead());
455 const wxStreamError err
= inStream
.GetLastError();
457 if ( err
!= wxSTREAM_NO_ERROR
&& err
!= wxSTREAM_EOF
)
459 wxLogError(_("Error reading config options."));
463 while ( !inStream
.Eof() );
467 cbuf
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen() + 1, &len
);
468 if ( !len
&& buf
.GetDataLen() )
470 wxLogError(_("Failed to read config options."));
472 #else // !wxUSE_UNICODE
473 // no need for conversion
474 cbuf
= wxCharBuffer::CreateNonOwned((char *)buf
.GetData(), buf
.GetDataLen());
475 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
478 // now break it into lines
479 wxMemoryText memText
;
480 for ( const wxChar
*s
= cbuf
; ; ++s
)
483 while ( *e
!= '\0' && *e
!= '\n' && *e
!= '\r' )
486 // notice that we throw away the original EOL kind here, maybe we
487 // should preserve it?
489 memText
.AddLine(wxString(s
, e
));
494 // skip the second EOL byte if it's a DOS one
495 if ( *e
== '\r' && e
[1] == '\n' )
501 // Finally we can parse it all.
502 Parse(memText
, true /* local */);
508 #endif // wxUSE_STREAMS
510 void wxFileConfig::CleanUp()
514 wxFileConfigLineList
*pCur
= m_linesHead
;
515 while ( pCur
!= NULL
) {
516 wxFileConfigLineList
*pNext
= pCur
->Next();
522 wxFileConfig::~wxFileConfig()
531 // ----------------------------------------------------------------------------
532 // parse a config file
533 // ----------------------------------------------------------------------------
535 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
538 size_t nLineCount
= buffer
.GetLineCount();
540 for ( size_t n
= 0; n
< nLineCount
; n
++ )
542 wxString strLine
= buffer
[n
];
543 // FIXME-UTF8: rewrite using iterators, without this buffer
544 wxWxCharBuffer
buf(strLine
.c_str());
545 const wxChar
*pStart
;
548 // add the line to linked list
550 LineListAppend(strLine
);
553 // skip leading spaces
554 for ( pStart
= buf
; wxIsspace(*pStart
); pStart
++ )
557 // skip blank/comment lines
558 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
561 if ( *pStart
== wxT('[') ) { // a new group
564 while ( *++pEnd
!= wxT(']') ) {
565 if ( *pEnd
== wxT('\\') ) {
566 // the next char is escaped, so skip it even if it is ']'
570 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
571 // we reached the end of line, break out of the loop
576 if ( *pEnd
!= wxT(']') ) {
577 wxLogError(_("file '%s': unexpected character %c at line %d."),
578 buffer
.GetName(), *pEnd
, n
+ 1);
579 continue; // skip this line
582 // group name here is always considered as abs path
585 strGroup
<< wxCONFIG_PATH_SEPARATOR
586 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
588 // will create it if doesn't yet exist
593 if ( m_pCurrentGroup
->Parent() )
594 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
595 m_pCurrentGroup
->SetLine(m_linesTail
);
598 // check that there is nothing except comments left on this line
600 while ( *++pEnd
!= wxT('\0') && bCont
) {
609 // ignore whitespace ('\n' impossible here)
613 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
614 buffer
.GetName(), n
+ 1, pEnd
);
621 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
622 if ( *pEnd
== wxT('\\') ) {
623 // next character may be space or not - still take it because it's
624 // quoted (unless there is nothing)
627 // the error message will be given below anyhow
635 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
638 while ( wxIsspace(*pEnd
) )
641 if ( *pEnd
++ != wxT('=') ) {
642 wxLogError(_("file '%s', line %d: '=' expected."),
643 buffer
.GetName(), n
+ 1);
646 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
648 if ( pEntry
== NULL
) {
650 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
653 if ( bLocal
&& pEntry
->IsImmutable() ) {
654 // immutable keys can't be changed by user
655 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
656 buffer
.GetName(), n
+ 1, strKey
.c_str());
659 // the condition below catches the cases (a) and (b) but not (c):
660 // (a) global key found second time in global file
661 // (b) key found second (or more) time in local file
662 // (c) key from global file now found in local one
663 // which is exactly what we want.
664 else if ( !bLocal
|| pEntry
->IsLocal() ) {
665 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
666 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
672 pEntry
->SetLine(m_linesTail
);
675 while ( wxIsspace(*pEnd
) )
678 wxString value
= pEnd
;
679 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
680 value
= FilterInValue(value
);
682 pEntry
->SetValue(value
, false);
688 // ----------------------------------------------------------------------------
690 // ----------------------------------------------------------------------------
692 void wxFileConfig::SetRootPath()
695 m_pCurrentGroup
= m_pRootGroup
;
699 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
701 wxArrayString aParts
;
703 if ( strPath
.empty() ) {
708 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
710 wxSplitPath(aParts
, strPath
);
713 // relative path, combine with current one
714 wxString strFullPath
= m_strPath
;
715 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
716 wxSplitPath(aParts
, strFullPath
);
719 // change current group
721 m_pCurrentGroup
= m_pRootGroup
;
722 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
723 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
724 if ( pNextGroup
== NULL
)
726 if ( !createMissingComponents
)
729 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
732 m_pCurrentGroup
= pNextGroup
;
735 // recombine path parts in one variable
737 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
738 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
744 void wxFileConfig::SetPath(const wxString
& strPath
)
746 DoSetPath(strPath
, true /* create missing path components */);
749 // ----------------------------------------------------------------------------
751 // ----------------------------------------------------------------------------
753 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
756 return GetNextGroup(str
, lIndex
);
759 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
761 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) {
762 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
769 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
772 return GetNextEntry(str
, lIndex
);
775 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
777 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) {
778 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
785 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
787 size_t n
= m_pCurrentGroup
->Entries().GetCount();
789 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
790 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
791 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
792 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
793 n
+= GetNumberOfEntries(true);
794 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
801 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
803 size_t n
= m_pCurrentGroup
->Groups().GetCount();
805 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
806 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
807 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
808 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
809 n
+= GetNumberOfGroups(true);
810 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
817 // ----------------------------------------------------------------------------
818 // tests for existence
819 // ----------------------------------------------------------------------------
821 bool wxFileConfig::HasGroup(const wxString
& strName
) const
823 // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
824 // but there is no group with empty name so treat this separately
825 if ( strName
.empty() )
828 const wxString pathOld
= GetPath();
830 wxFileConfig
*self
= const_cast<wxFileConfig
*>(this);
832 rc
= self
->DoSetPath(strName
, false /* don't create missing components */);
834 self
->SetPath(pathOld
);
839 bool wxFileConfig::HasEntry(const wxString
& entry
) const
841 // path is the part before the last "/"
842 wxString path
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
);
844 // except in the special case of "/keyname" when there is nothing before "/"
845 if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR
)
847 path
= wxCONFIG_PATH_SEPARATOR
;
850 // change to the path of the entry if necessary and remember the old path
851 // to restore it later
853 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
857 if ( pathOld
.empty() )
858 pathOld
= wxCONFIG_PATH_SEPARATOR
;
860 if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) )
866 // check if the entry exists in this group
867 const bool exists
= m_pCurrentGroup
->FindEntry(
868 entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
;
870 // restore the old path if we changed it above
871 if ( !pathOld
.empty() )
873 self
->SetPath(pathOld
);
879 // ----------------------------------------------------------------------------
881 // ----------------------------------------------------------------------------
883 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
885 wxConfigPathChanger
path(this, key
);
887 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
888 if (pEntry
== NULL
) {
892 *pStr
= pEntry
->Value();
897 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
900 if ( !Read(key
, &str
) )
903 // extra spaces shouldn't prevent us from reading numeric values
906 return str
.ToLong(pl
);
911 bool wxFileConfig::DoReadBinary(const wxString
& key
, wxMemoryBuffer
* buf
) const
913 wxCHECK_MSG( buf
, false, _T("NULL buffer") );
916 if ( !Read(key
, &str
) )
919 *buf
= wxBase64Decode(str
);
923 #endif // wxUSE_BASE64
925 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
927 wxConfigPathChanger
path(this, key
);
928 wxString strName
= path
.Name();
930 wxLogTrace( FILECONF_TRACE_MASK
,
931 _T(" Writing String '%s' = '%s' to Group '%s'"),
936 if ( strName
.empty() )
938 // setting the value of a group is an error
940 wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") );
942 // ... except if it's empty in which case it's a way to force it's creation
944 wxLogTrace( FILECONF_TRACE_MASK
,
945 _T(" Creating group %s"),
946 m_pCurrentGroup
->Name().c_str() );
950 // this will add a line for this group if it didn't have it before (or
951 // do nothing for the root but it's ok as it always exists anyhow)
952 (void)m_pCurrentGroup
->GetGroupLine();
956 // writing an entry check that the name is reasonable
957 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
959 wxLogError( _("Config entry name cannot start with '%c'."),
960 wxCONFIG_IMMUTABLE_PREFIX
);
964 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
968 wxLogTrace( FILECONF_TRACE_MASK
,
969 _T(" Adding Entry %s"),
971 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
974 wxLogTrace( FILECONF_TRACE_MASK
,
975 _T(" Setting value %s"),
977 pEntry
->SetValue(szValue
);
985 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
987 return Write(key
, wxString::Format(_T("%ld"), lValue
));
992 bool wxFileConfig::DoWriteBinary(const wxString
& key
, const wxMemoryBuffer
& buf
)
994 return Write(key
, wxBase64Encode(buf
));
997 #endif // wxUSE_BASE64
999 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
1001 if ( !IsDirty() || !m_fnLocalFile
.GetFullPath() )
1004 // set the umask if needed
1005 wxCHANGE_UMASK(m_umask
);
1007 wxTempFile
file(m_fnLocalFile
.GetFullPath());
1009 if ( !file
.IsOpened() )
1011 wxLogError(_("can't open user configuration file."));
1015 // write all strings to file
1017 filetext
.reserve(4096);
1018 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1020 filetext
<< p
->Text() << wxTextFile::GetEOL();
1023 if ( !file
.Write(filetext
, *m_conv
) )
1025 wxLogError(_("can't write user configuration file."));
1029 if ( !file
.Commit() )
1031 wxLogError(_("Failed to update user configuration file."));
1038 #if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
1039 m_fnLocalFile
.MacSetTypeAndCreator('TEXT', 'ttxt');
1047 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
)
1049 // save unconditionally, even if not dirty
1050 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1052 wxString line
= p
->Text();
1053 line
+= wxTextFile::GetEOL();
1055 wxCharBuffer
buf(line
.mb_str(conv
));
1056 if ( !os
.Write(buf
, strlen(buf
)) )
1058 wxLogError(_("Error saving user configuration data."));
1069 #endif // wxUSE_STREAMS
1071 // ----------------------------------------------------------------------------
1072 // renaming groups/entries
1073 // ----------------------------------------------------------------------------
1075 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1076 const wxString
& newName
)
1078 wxASSERT_MSG( oldName
.find(wxCONFIG_PATH_SEPARATOR
) == wxString::npos
,
1079 _T("RenameEntry(): paths are not supported") );
1081 // check that the entry exists
1082 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1086 // check that the new entry doesn't already exist
1087 if ( m_pCurrentGroup
->FindEntry(newName
) )
1090 // delete the old entry, create the new one
1091 wxString value
= oldEntry
->Value();
1092 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1097 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1098 newEntry
->SetValue(value
);
1103 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1104 const wxString
& newName
)
1106 // check that the group exists
1107 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1111 // check that the new group doesn't already exist
1112 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1115 group
->Rename(newName
);
1122 // ----------------------------------------------------------------------------
1123 // delete groups/entries
1124 // ----------------------------------------------------------------------------
1126 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1128 wxConfigPathChanger
path(this, key
);
1130 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1135 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1136 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1137 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1138 SetPath(wxT("..")); // changes m_pCurrentGroup!
1139 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1141 //else: never delete the root group
1147 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1149 wxConfigPathChanger
path(this, RemoveTrailingSeparator(key
));
1151 if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) )
1154 path
.UpdateIfDeleted();
1161 bool wxFileConfig::DeleteAll()
1165 if ( m_fnLocalFile
.IsOk() )
1167 if ( m_fnLocalFile
.FileExists() &&
1168 !wxRemoveFile(m_fnLocalFile
.GetFullPath()) )
1170 wxLogSysError(_("can't delete user configuration file '%s'"),
1171 m_fnLocalFile
.GetFullPath().c_str());
1181 // ----------------------------------------------------------------------------
1182 // linked list functions
1183 // ----------------------------------------------------------------------------
1185 // append a new line to the end of the list
1187 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1189 wxLogTrace( FILECONF_TRACE_MASK
,
1190 _T(" ** Adding Line '%s'"),
1192 wxLogTrace( FILECONF_TRACE_MASK
,
1194 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1196 wxLogTrace( FILECONF_TRACE_MASK
,
1198 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1201 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1203 if ( m_linesTail
== NULL
)
1206 m_linesHead
= pLine
;
1211 m_linesTail
->SetNext(pLine
);
1212 pLine
->SetPrev(m_linesTail
);
1215 m_linesTail
= pLine
;
1217 wxLogTrace( FILECONF_TRACE_MASK
,
1219 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1221 wxLogTrace( FILECONF_TRACE_MASK
,
1223 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1229 // insert a new line after the given one or in the very beginning if !pLine
1230 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1231 wxFileConfigLineList
*pLine
)
1233 wxLogTrace( FILECONF_TRACE_MASK
,
1234 _T(" ** Inserting Line '%s' after '%s'"),
1236 ((pLine
) ? (const wxChar
*)pLine
->Text().c_str()
1238 wxLogTrace( FILECONF_TRACE_MASK
,
1240 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1242 wxLogTrace( FILECONF_TRACE_MASK
,
1244 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1247 if ( pLine
== m_linesTail
)
1248 return LineListAppend(str
);
1250 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1251 if ( pLine
== NULL
)
1253 // prepend to the list
1254 pNewLine
->SetNext(m_linesHead
);
1255 m_linesHead
->SetPrev(pNewLine
);
1256 m_linesHead
= pNewLine
;
1260 // insert before pLine
1261 wxFileConfigLineList
*pNext
= pLine
->Next();
1262 pNewLine
->SetNext(pNext
);
1263 pNewLine
->SetPrev(pLine
);
1264 pNext
->SetPrev(pNewLine
);
1265 pLine
->SetNext(pNewLine
);
1268 wxLogTrace( FILECONF_TRACE_MASK
,
1270 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1272 wxLogTrace( FILECONF_TRACE_MASK
,
1274 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1280 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1282 wxLogTrace( FILECONF_TRACE_MASK
,
1283 _T(" ** Removing Line '%s'"),
1284 pLine
->Text().c_str() );
1285 wxLogTrace( FILECONF_TRACE_MASK
,
1287 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1289 wxLogTrace( FILECONF_TRACE_MASK
,
1291 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1294 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1295 *pNext
= pLine
->Next();
1299 if ( pPrev
== NULL
)
1300 m_linesHead
= pNext
;
1302 pPrev
->SetNext(pNext
);
1306 if ( pNext
== NULL
)
1307 m_linesTail
= pPrev
;
1309 pNext
->SetPrev(pPrev
);
1311 wxLogTrace( FILECONF_TRACE_MASK
,
1313 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1315 wxLogTrace( FILECONF_TRACE_MASK
,
1317 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1323 bool wxFileConfig::LineListIsEmpty()
1325 return m_linesHead
== NULL
;
1328 // ============================================================================
1329 // wxFileConfig::wxFileConfigGroup
1330 // ============================================================================
1332 // ----------------------------------------------------------------------------
1334 // ----------------------------------------------------------------------------
1337 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1338 const wxString
& strName
,
1339 wxFileConfig
*pConfig
)
1340 : m_aEntries(CompareEntries
),
1341 m_aSubgroups(CompareGroups
),
1344 m_pConfig
= pConfig
;
1345 m_pParent
= pParent
;
1348 m_pLastEntry
= NULL
;
1349 m_pLastGroup
= NULL
;
1352 // dtor deletes all children
1353 wxFileConfigGroup::~wxFileConfigGroup()
1356 size_t n
, nCount
= m_aEntries
.GetCount();
1357 for ( n
= 0; n
< nCount
; n
++ )
1358 delete m_aEntries
[n
];
1361 nCount
= m_aSubgroups
.GetCount();
1362 for ( n
= 0; n
< nCount
; n
++ )
1363 delete m_aSubgroups
[n
];
1366 // ----------------------------------------------------------------------------
1368 // ----------------------------------------------------------------------------
1370 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1372 // for a normal (i.e. not root) group this method shouldn't be called twice
1373 // unless we are resetting the line
1374 wxASSERT_MSG( !m_pParent
|| !m_pLine
|| !pLine
,
1375 _T("changing line for a non-root group?") );
1381 This is a bit complicated, so let me explain it in details. All lines that
1382 were read from the local file (the only one we will ever modify) are stored
1383 in a (doubly) linked list. Our problem is to know at which position in this
1384 list should we insert the new entries/subgroups. To solve it we keep three
1385 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1387 m_pLine points to the line containing "[group_name]"
1388 m_pLastEntry points to the last entry of this group in the local file.
1389 m_pLastGroup subgroup
1391 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1392 from the local file, the corresponding variable is set. However, if the group
1393 was read from the global file and then modified or created by the application
1394 these variables are still NULL and we need to create the corresponding lines.
1395 See the following functions (and comments preceding them) for the details of
1398 Also, when our last entry/group are deleted we need to find the new last
1399 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1400 of lines until it either founds an entry/subgroup (and this is the new last
1401 element) or the m_pLine of the group, in which case there are no more entries
1402 (or subgroups) left and m_pLast<element> becomes NULL.
1404 NB: This last problem could be avoided for entries if we added new entries
1405 immediately after m_pLine, but in this case the entries would appear
1406 backwards in the config file (OTOH, it's not that important) and as we
1407 would still need to do it for the subgroups the code wouldn't have been
1408 significantly less complicated.
1411 // Return the line which contains "[our name]". If we're still not in the list,
1412 // add our line to it immediately after the last line of our parent group if we
1413 // have it or in the very beginning if we're the root group.
1414 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1416 wxLogTrace( FILECONF_TRACE_MASK
,
1417 _T(" GetGroupLine() for Group '%s'"),
1422 wxLogTrace( FILECONF_TRACE_MASK
,
1423 _T(" Getting Line item pointer") );
1425 wxFileConfigGroup
*pParent
= Parent();
1427 // this group wasn't present in local config file, add it now
1430 wxLogTrace( FILECONF_TRACE_MASK
,
1431 _T(" checking parent '%s'"),
1432 pParent
->Name().c_str() );
1434 wxString strFullName
;
1436 // add 1 to the name because we don't want to start with '/'
1437 strFullName
<< wxT("[")
1438 << FilterOutEntryName(GetFullName().c_str() + 1)
1440 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1441 pParent
->GetLastGroupLine());
1442 pParent
->SetLastGroup(this); // we're surely after all the others
1444 //else: this is the root group and so we return NULL because we don't
1445 // have any group line
1451 // Return the last line belonging to the subgroups of this group (after which
1452 // we can add a new subgroup), if we don't have any subgroups or entries our
1453 // last line is the group line (m_pLine) itself.
1454 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1456 // if we have any subgroups, our last line is the last line of the last
1460 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1462 wxASSERT_MSG( pLine
, _T("last group must have !NULL associated line") );
1467 // no subgroups, so the last line is the line of thelast entry (if any)
1468 return GetLastEntryLine();
1471 // return the last line belonging to the entries of this group (after which
1472 // we can add a new entry), if we don't have any entries we will add the new
1473 // one immediately after the group line itself.
1474 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1476 wxLogTrace( FILECONF_TRACE_MASK
,
1477 _T(" GetLastEntryLine() for Group '%s'"),
1482 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1484 wxASSERT_MSG( pLine
, _T("last entry must have !NULL associated line") );
1489 // no entries: insert after the group header, if any
1490 return GetGroupLine();
1493 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry
*pEntry
)
1495 m_pLastEntry
= pEntry
;
1499 // the only situation in which a group without its own line can have
1500 // an entry is when the first entry is added to the initially empty
1501 // root pseudo-group
1502 wxASSERT_MSG( !m_pParent
, _T("unexpected for non root group") );
1504 // let the group know that it does have a line in the file now
1505 m_pLine
= pEntry
->GetLine();
1509 // ----------------------------------------------------------------------------
1511 // ----------------------------------------------------------------------------
1513 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
1515 // update the line of this group
1516 wxFileConfigLineList
*line
= GetGroupLine();
1517 wxCHECK_RET( line
, _T("a non root group must have a corresponding line!") );
1519 // +1: skip the leading '/'
1520 line
->SetText(wxString::Format(_T("[%s]"), GetFullName().c_str() + 1));
1523 // also update all subgroups as they have this groups name in their lines
1524 const size_t nCount
= m_aSubgroups
.GetCount();
1525 for ( size_t n
= 0; n
< nCount
; n
++ )
1527 m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines();
1531 void wxFileConfigGroup::Rename(const wxString
& newName
)
1533 wxCHECK_RET( m_pParent
, _T("the root group can't be renamed") );
1535 if ( newName
== m_strName
)
1538 // we need to remove the group from the parent and it back under the new
1539 // name to keep the parents array of subgroups alphabetically sorted
1540 m_pParent
->m_aSubgroups
.Remove(this);
1542 m_strName
= newName
;
1544 m_pParent
->m_aSubgroups
.Add(this);
1546 // update the group lines recursively
1547 UpdateGroupAndSubgroupsLines();
1550 wxString
wxFileConfigGroup::GetFullName() const
1554 fullname
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1559 // ----------------------------------------------------------------------------
1561 // ----------------------------------------------------------------------------
1563 // use binary search because the array is sorted
1565 wxFileConfigGroup::FindEntry(const wxString
& name
) const
1569 hi
= m_aEntries
.GetCount();
1571 wxFileConfigEntry
*pEntry
;
1575 pEntry
= m_aEntries
[i
];
1577 #if wxCONFIG_CASE_SENSITIVE
1578 res
= pEntry
->Name().compare(name
);
1580 res
= pEntry
->Name().CmpNoCase(name
);
1595 wxFileConfigGroup::FindSubgroup(const wxString
& name
) const
1599 hi
= m_aSubgroups
.GetCount();
1601 wxFileConfigGroup
*pGroup
;
1605 pGroup
= m_aSubgroups
[i
];
1607 #if wxCONFIG_CASE_SENSITIVE
1608 res
= pGroup
->Name().compare(name
);
1610 res
= pGroup
->Name().CmpNoCase(name
);
1624 // ----------------------------------------------------------------------------
1625 // create a new item
1626 // ----------------------------------------------------------------------------
1628 // create a new entry and add it to the current group
1629 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1631 wxASSERT( FindEntry(strName
) == 0 );
1633 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1635 m_aEntries
.Add(pEntry
);
1639 // create a new group and add it to the current group
1640 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1642 wxASSERT( FindSubgroup(strName
) == 0 );
1644 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1646 m_aSubgroups
.Add(pGroup
);
1650 // ----------------------------------------------------------------------------
1652 // ----------------------------------------------------------------------------
1655 The delete operations are _very_ slow if we delete the last item of this
1656 group (see comments before GetXXXLineXXX functions for more details),
1657 so it's much better to start with the first entry/group if we want to
1658 delete several of them.
1661 bool wxFileConfigGroup::DeleteSubgroupByName(const wxString
& name
)
1663 wxFileConfigGroup
* const pGroup
= FindSubgroup(name
);
1665 return pGroup
? DeleteSubgroup(pGroup
) : false;
1668 // Delete the subgroup and remove all references to it from
1669 // other data structures.
1670 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1672 wxCHECK_MSG( pGroup
, false, _T("deleting non existing group?") );
1674 wxLogTrace( FILECONF_TRACE_MASK
,
1675 _T("Deleting group '%s' from '%s'"),
1676 pGroup
->Name().c_str(),
1679 wxLogTrace( FILECONF_TRACE_MASK
,
1680 _T(" (m_pLine) = prev: %p, this %p, next %p"),
1681 m_pLine
? static_cast<void*>(m_pLine
->Prev()) : 0,
1682 static_cast<void*>(m_pLine
),
1683 m_pLine
? static_cast<void*>(m_pLine
->Next()) : 0 );
1684 wxLogTrace( FILECONF_TRACE_MASK
,
1686 m_pLine
? (const wxChar
*)m_pLine
->Text().c_str()
1689 // delete all entries...
1690 size_t nCount
= pGroup
->m_aEntries
.GetCount();
1692 wxLogTrace(FILECONF_TRACE_MASK
,
1693 _T("Removing %lu entries"), (unsigned long)nCount
);
1695 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1697 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1701 wxLogTrace( FILECONF_TRACE_MASK
,
1703 pLine
->Text().c_str() );
1704 m_pConfig
->LineListRemove(pLine
);
1708 // ...and subgroups of this subgroup
1709 nCount
= pGroup
->m_aSubgroups
.GetCount();
1711 wxLogTrace( FILECONF_TRACE_MASK
,
1712 _T("Removing %lu subgroups"), (unsigned long)nCount
);
1714 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1716 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1719 // and then finally the group itself
1720 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1723 wxLogTrace( FILECONF_TRACE_MASK
,
1724 _T(" Removing line for group '%s' : '%s'"),
1725 pGroup
->Name().c_str(),
1726 pLine
->Text().c_str() );
1727 wxLogTrace( FILECONF_TRACE_MASK
,
1728 _T(" Removing from group '%s' : '%s'"),
1730 ((m_pLine
) ? (const wxChar
*)m_pLine
->Text().c_str()
1733 // notice that we may do this test inside the previous "if"
1734 // because the last entry's line is surely !NULL
1735 if ( pGroup
== m_pLastGroup
)
1737 wxLogTrace( FILECONF_TRACE_MASK
,
1738 _T(" Removing last group") );
1740 // our last entry is being deleted, so find the last one which
1741 // stays by going back until we find a subgroup or reach the
1743 const size_t nSubgroups
= m_aSubgroups
.GetCount();
1745 m_pLastGroup
= NULL
;
1746 for ( wxFileConfigLineList
*pl
= pLine
->Prev();
1747 pl
&& !m_pLastGroup
;
1750 // does this line belong to our subgroup?
1751 for ( size_t n
= 0; n
< nSubgroups
; n
++ )
1753 // do _not_ call GetGroupLine! we don't want to add it to
1754 // the local file if it's not already there
1755 if ( m_aSubgroups
[n
]->m_pLine
== pl
)
1757 m_pLastGroup
= m_aSubgroups
[n
];
1762 if ( pl
== m_pLine
)
1767 m_pConfig
->LineListRemove(pLine
);
1771 wxLogTrace( FILECONF_TRACE_MASK
,
1772 _T(" No line entry for Group '%s'?"),
1773 pGroup
->Name().c_str() );
1776 m_aSubgroups
.Remove(pGroup
);
1782 bool wxFileConfigGroup::DeleteEntry(const wxString
& name
)
1784 wxFileConfigEntry
*pEntry
= FindEntry(name
);
1787 // entry doesn't exist, nothing to do
1791 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1792 if ( pLine
!= NULL
) {
1793 // notice that we may do this test inside the previous "if" because the
1794 // last entry's line is surely !NULL
1795 if ( pEntry
== m_pLastEntry
) {
1796 // our last entry is being deleted - find the last one which stays
1797 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1799 // find the previous entry (if any)
1800 wxFileConfigEntry
*pNewLast
= NULL
;
1801 const wxFileConfigLineList
* const
1802 pNewLastLine
= m_pLastEntry
->GetLine()->Prev();
1803 const size_t nEntries
= m_aEntries
.GetCount();
1804 for ( size_t n
= 0; n
< nEntries
; n
++ ) {
1805 if ( m_aEntries
[n
]->GetLine() == pNewLastLine
) {
1806 pNewLast
= m_aEntries
[n
];
1811 // pNewLast can be NULL here -- it's ok and can happen if we have no
1813 m_pLastEntry
= pNewLast
;
1816 m_pConfig
->LineListRemove(pLine
);
1819 m_aEntries
.Remove(pEntry
);
1825 // ============================================================================
1826 // wxFileConfig::wxFileConfigEntry
1827 // ============================================================================
1829 // ----------------------------------------------------------------------------
1831 // ----------------------------------------------------------------------------
1832 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1833 const wxString
& strName
,
1835 : m_strName(strName
)
1837 wxASSERT( !strName
.empty() );
1839 m_pParent
= pParent
;
1843 m_bHasValue
= false;
1845 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1847 m_strName
.erase(0, 1); // remove first character
1850 // ----------------------------------------------------------------------------
1852 // ----------------------------------------------------------------------------
1854 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1856 if ( m_pLine
!= NULL
) {
1857 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1858 Name().c_str(), m_pParent
->GetFullName().c_str());
1862 Group()->SetLastEntry(this);
1865 // second parameter is false if we read the value from file and prevents the
1866 // entry from being marked as 'dirty'
1867 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1869 if ( bUser
&& IsImmutable() )
1871 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1876 // do nothing if it's the same value: but don't test for it if m_bHasValue
1877 // hadn't been set yet or we'd never write empty values to the file
1878 if ( m_bHasValue
&& strValue
== m_strValue
)
1882 m_strValue
= strValue
;
1886 wxString strValFiltered
;
1888 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1890 strValFiltered
= strValue
;
1893 strValFiltered
= FilterOutValue(strValue
);
1897 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1901 // entry was read from the local config file, just modify the line
1902 m_pLine
->SetText(strLine
);
1904 else // this entry didn't exist in the local file
1906 // add a new line to the file: note that line returned by
1907 // GetLastEntryLine() may be NULL if we're in the root group and it
1908 // doesn't have any entries yet, but this is ok as passing NULL
1909 // line to LineListInsert() means to prepend new line to the list
1910 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1911 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1913 Group()->SetLastEntry(this);
1918 // ============================================================================
1920 // ============================================================================
1922 // ----------------------------------------------------------------------------
1923 // compare functions for array sorting
1924 // ----------------------------------------------------------------------------
1926 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1928 #if wxCONFIG_CASE_SENSITIVE
1929 return p1
->Name().compare(p2
->Name());
1931 return p1
->Name().CmpNoCase(p2
->Name());
1935 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1937 #if wxCONFIG_CASE_SENSITIVE
1938 return p1
->Name().compare(p2
->Name());
1940 return p1
->Name().CmpNoCase(p2
->Name());
1944 // ----------------------------------------------------------------------------
1946 // ----------------------------------------------------------------------------
1948 // undo FilterOutValue
1949 static wxString
FilterInValue(const wxString
& str
)
1955 strResult
.reserve(str
.length());
1957 wxString::const_iterator i
= str
.begin();
1958 const bool bQuoted
= *i
== '"';
1962 for ( const wxString::const_iterator end
= str
.end(); i
!= end
; ++i
)
1964 if ( *i
== wxT('\\') )
1968 wxLogWarning(_("trailing backslash ignored in '%s'"), str
.c_str());
1972 switch ( (*i
).GetValue() )
1975 strResult
+= wxT('\n');
1979 strResult
+= wxT('\r');
1983 strResult
+= wxT('\t');
1987 strResult
+= wxT('\\');
1991 strResult
+= wxT('"');
1995 else // not a backslash
1997 if ( *i
!= wxT('"') || !bQuoted
)
2001 else if ( i
!= end
- 1 )
2003 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2004 i
- str
.begin(), str
.c_str());
2006 //else: it's the last quote of a quoted string, ok
2013 // quote the string before writing it to file
2014 static wxString
FilterOutValue(const wxString
& str
)
2020 strResult
.Alloc(str
.Len());
2022 // quoting is necessary to preserve spaces in the beginning of the string
2023 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2026 strResult
+= wxT('"');
2029 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2030 switch ( str
[n
].GetValue() ) {
2052 //else: fall through
2055 strResult
+= str
[n
];
2056 continue; // nothing special to do
2059 // we get here only for special characters
2060 strResult
<< wxT('\\') << c
;
2064 strResult
+= wxT('"');
2069 // undo FilterOutEntryName
2070 static wxString
FilterInEntryName(const wxString
& str
)
2073 strResult
.Alloc(str
.Len());
2075 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2076 if ( *pc
== wxT('\\') ) {
2077 // we need to test it here or we'd skip past the NUL in the loop line
2078 if ( *++pc
== _T('\0') )
2088 // sanitize entry or group name: insert '\\' before any special characters
2089 static wxString
FilterOutEntryName(const wxString
& str
)
2092 strResult
.Alloc(str
.Len());
2094 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2095 const wxChar c
= *pc
;
2097 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2098 // which will probably never have special meaning and with which we can't
2099 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2101 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2102 // should *not* be quoted
2105 ((unsigned char)c
< 127) &&
2107 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) )
2109 strResult
+= wxT('\\');
2118 // we can't put ?: in the ctor initializer list because it confuses some
2119 // broken compilers (Borland C++)
2120 static wxString
GetAppName(const wxString
& appName
)
2122 if ( !appName
&& wxTheApp
)
2123 return wxTheApp
->GetAppName();
2128 #endif // wxUSE_CONFIG