1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/fileconf.cpp
3 // Purpose: implementation of wxFileConfig derivation of wxConfig
4 // Author: Vadim Zeitlin
6 // Created: 07.04.98 (adapted from appconf.cpp)
8 // Copyright: (c) 1997 Karsten Ballueder & Vadim Zeitlin
9 // Ballueder@usa.net <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
24 #if wxUSE_CONFIG && wxUSE_FILECONFIG
27 #include "wx/dynarray.h"
28 #include "wx/string.h"
32 #include "wx/utils.h" // for wxGetHomeDir
34 #include "wx/stream.h"
35 #endif // wxUSE_STREAMS
39 #include "wx/textfile.h"
40 #include "wx/memtext.h"
41 #include "wx/config.h"
42 #include "wx/fileconf.h"
43 #include "wx/filefn.h"
45 #include "wx/base64.h"
47 #include "wx/stdpaths.h"
49 #if defined(__WXMSW__)
50 #include "wx/msw/private.h"
60 // ----------------------------------------------------------------------------
62 // ----------------------------------------------------------------------------
68 #define FILECONF_TRACE_MASK wxT("fileconf")
70 // ----------------------------------------------------------------------------
71 // global functions declarations
72 // ----------------------------------------------------------------------------
74 // compare functions for sorting the arrays
75 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
76 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
79 static wxString
FilterInValue(const wxString
& str
);
80 static wxString
FilterOutValue(const wxString
& str
);
82 static wxString
FilterInEntryName(const wxString
& str
);
83 static wxString
FilterOutEntryName(const wxString
& str
);
85 // get the name to use in wxFileConfig ctor
86 static wxString
GetAppName(const wxString
& appname
);
88 // ============================================================================
90 // ============================================================================
92 // ----------------------------------------------------------------------------
93 // "template" array types
94 // ----------------------------------------------------------------------------
96 #ifdef WXMAKINGDLL_BASE
97 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
,
99 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
,
102 WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
103 WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
);
106 // ----------------------------------------------------------------------------
107 // wxFileConfigLineList
108 // ----------------------------------------------------------------------------
110 // we store all lines of the local config file as a linked list in memory
111 class wxFileConfigLineList
114 void SetNext(wxFileConfigLineList
*pNext
) { m_pNext
= pNext
; }
115 void SetPrev(wxFileConfigLineList
*pPrev
) { m_pPrev
= pPrev
; }
118 wxFileConfigLineList(const wxString
& str
,
119 wxFileConfigLineList
*pNext
= NULL
) : m_strLine(str
)
120 { SetNext(pNext
); SetPrev(NULL
); }
122 // next/prev nodes in the linked list
123 wxFileConfigLineList
*Next() const { return m_pNext
; }
124 wxFileConfigLineList
*Prev() const { return m_pPrev
; }
126 // get/change lines text
127 void SetText(const wxString
& str
) { m_strLine
= str
; }
128 const wxString
& Text() const { return m_strLine
; }
131 wxString m_strLine
; // line contents
132 wxFileConfigLineList
*m_pNext
, // next node
133 *m_pPrev
; // previous one
135 wxDECLARE_NO_COPY_CLASS(wxFileConfigLineList
);
138 // ----------------------------------------------------------------------------
139 // wxFileConfigEntry: a name/value pair
140 // ----------------------------------------------------------------------------
142 class wxFileConfigEntry
145 wxFileConfigGroup
*m_pParent
; // group that contains us
147 wxString m_strName
, // entry name
149 bool m_bImmutable
:1, // can be overridden locally?
150 m_bHasValue
:1; // set after first call to SetValue()
152 int m_nLine
; // used if m_pLine == NULL only
154 // pointer to our line in the linked list or NULL if it was found in global
155 // file (which we don't modify)
156 wxFileConfigLineList
*m_pLine
;
159 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
160 const wxString
& strName
, int nLine
);
163 const wxString
& Name() const { return m_strName
; }
164 const wxString
& Value() const { return m_strValue
; }
165 wxFileConfigGroup
*Group() const { return m_pParent
; }
166 bool IsImmutable() const { return m_bImmutable
; }
167 bool IsLocal() const { return m_pLine
!= 0; }
168 int Line() const { return m_nLine
; }
169 wxFileConfigLineList
*
170 GetLine() const { return m_pLine
; }
172 // modify entry attributes
173 void SetValue(const wxString
& strValue
, bool bUser
= true);
174 void SetLine(wxFileConfigLineList
*pLine
);
176 wxDECLARE_NO_COPY_CLASS(wxFileConfigEntry
);
179 // ----------------------------------------------------------------------------
180 // wxFileConfigGroup: container of entries and other groups
181 // ----------------------------------------------------------------------------
183 class wxFileConfigGroup
186 wxFileConfig
*m_pConfig
; // config object we belong to
187 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
188 ArrayEntries m_aEntries
; // entries in this group
189 ArrayGroups m_aSubgroups
; // subgroups
190 wxString m_strName
; // group's name
191 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
192 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
193 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
195 // DeleteSubgroupByName helper
196 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
199 void UpdateGroupAndSubgroupsLines();
203 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
205 // dtor deletes all entries and subgroups also
206 ~wxFileConfigGroup();
209 const wxString
& Name() const { return m_strName
; }
210 wxFileConfigGroup
*Parent() const { return m_pParent
; }
211 wxFileConfig
*Config() const { return m_pConfig
; }
213 const ArrayEntries
& Entries() const { return m_aEntries
; }
214 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
215 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
217 // find entry/subgroup (NULL if not found)
218 wxFileConfigGroup
*FindSubgroup(const wxString
& name
) const;
219 wxFileConfigEntry
*FindEntry (const wxString
& name
) const;
221 // delete entry/subgroup, return false if doesn't exist
222 bool DeleteSubgroupByName(const wxString
& name
);
223 bool DeleteEntry(const wxString
& name
);
225 // create new entry/subgroup returning pointer to newly created element
226 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
227 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
229 void SetLine(wxFileConfigLineList
*pLine
);
231 // rename: no checks are done to ensure that the name is unique!
232 void Rename(const wxString
& newName
);
235 wxString
GetFullName() const;
237 // get the last line belonging to an entry/subgroup of this group
238 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
239 // may be NULL for "/" only
240 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
241 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
243 // called by entries/subgroups when they're created/deleted
244 void SetLastEntry(wxFileConfigEntry
*pEntry
);
245 void SetLastGroup(wxFileConfigGroup
*pGroup
)
246 { m_pLastGroup
= pGroup
; }
248 wxDECLARE_NO_COPY_CLASS(wxFileConfigGroup
);
251 // ============================================================================
253 // ============================================================================
255 // ----------------------------------------------------------------------------
257 // ----------------------------------------------------------------------------
259 // this function modifies in place the given wxFileName object if it doesn't
260 // already have an extension
262 // note that it's slightly misnamed under Mac as there it doesn't add an
263 // extension but modifies the file name instead, so you shouldn't suppose that
264 // fn.HasExt() is true after it returns
265 static void AddConfFileExtIfNeeded(wxFileName
& fn
)
269 #if defined( __WXMAC__ )
270 fn
.SetName(fn
.GetName() + wxT(" Preferences"));
271 #elif defined( __UNIX__ )
272 fn
.SetExt(wxT("conf"));
274 fn
.SetExt(wxT("ini"));
279 wxString
wxFileConfig::GetGlobalDir()
281 return wxStandardPaths::Get().GetConfigDir();
284 wxString
wxFileConfig::GetLocalDir(int style
)
288 wxStandardPathsBase
& stdp
= wxStandardPaths::Get();
290 // it so happens that user data directory is a subdirectory of user config
291 // directory on all supported platforms, which explains why we use it here
292 return style
& wxCONFIG_USE_SUBDIR
? stdp
.GetUserDataDir()
293 : stdp
.GetUserConfigDir();
296 wxFileName
wxFileConfig::GetGlobalFile(const wxString
& szFile
)
298 wxFileName
fn(GetGlobalDir(), szFile
);
300 AddConfFileExtIfNeeded(fn
);
305 wxFileName
wxFileConfig::GetLocalFile(const wxString
& szFile
, int style
)
307 wxFileName
fn(GetLocalDir(style
), szFile
);
309 #if defined( __UNIX__ ) && !defined( __WXMAC__ )
310 if ( !(style
& wxCONFIG_USE_SUBDIR
) )
312 // dot-files under Unix start with, well, a dot (but OTOH they usually
313 // don't have any specific extension)
314 fn
.SetName(wxT('.') + fn
.GetName());
316 else // we do append ".conf" extension to config files in subdirectories
317 #endif // defined( __UNIX__ ) && !defined( __WXMAC__ )
319 AddConfFileExtIfNeeded(fn
);
325 // ----------------------------------------------------------------------------
327 // ----------------------------------------------------------------------------
328 IMPLEMENT_ABSTRACT_CLASS(wxFileConfig
, wxConfigBase
)
330 void wxFileConfig::Init()
333 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
338 // It's not an error if (one of the) file(s) doesn't exist.
340 // parse the global file
341 if ( m_fnGlobalFile
.IsOk() && m_fnGlobalFile
.FileExists() )
343 wxTextFile
fileGlobal(m_fnGlobalFile
.GetFullPath());
345 if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) )
347 Parse(fileGlobal
, false /* global */);
352 wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile
.GetFullPath().c_str());
356 // parse the local file
357 if ( m_fnLocalFile
.IsOk() && m_fnLocalFile
.FileExists() )
359 wxTextFile
fileLocal(m_fnLocalFile
.GetFullPath());
360 if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) )
362 Parse(fileLocal
, true /* local */);
367 const wxString path
= m_fnLocalFile
.GetFullPath();
368 wxLogWarning(_("can't open user configuration file '%s'."),
371 if ( m_fnLocalFile
.FileExists() )
373 wxLogWarning(_("Changes won't be saved to avoid overwriting the existing file \"%s\""),
375 m_fnLocalFile
.Clear();
383 // constructor supports creation of wxFileConfig objects of any type
384 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
385 const wxString
& strLocal
, const wxString
& strGlobal
,
387 const wxMBConv
& conv
)
388 : wxConfigBase(::GetAppName(appName
), vendorName
,
391 m_fnLocalFile(strLocal
),
392 m_fnGlobalFile(strGlobal
),
395 // Make up names for files if empty
396 if ( !m_fnLocalFile
.IsOk() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
397 m_fnLocalFile
= GetLocalFile(GetAppName(), style
);
399 if ( !m_fnGlobalFile
.IsOk() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
400 m_fnGlobalFile
= GetGlobalFile(GetAppName());
402 // Check if styles are not supplied, but filenames are, in which case
403 // add the correct styles.
404 if ( m_fnLocalFile
.IsOk() )
405 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
407 if ( m_fnGlobalFile
.IsOk() )
408 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
410 // if the path is not absolute, prepend the standard directory to it
411 // unless explicitly asked not to
412 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
414 if ( m_fnLocalFile
.IsOk() )
415 m_fnLocalFile
.MakeAbsolute(GetLocalDir(style
));
417 if ( m_fnGlobalFile
.IsOk() )
418 m_fnGlobalFile
.MakeAbsolute(GetGlobalDir());
428 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, const wxMBConv
& conv
)
429 : m_conv(conv
.Clone())
431 // always local_file when this constructor is called (?)
432 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
435 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
440 // read the entire stream contents in memory
442 static const size_t chunkLen
= 1024;
444 wxMemoryBuffer
buf(chunkLen
);
447 inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
);
448 buf
.UngetAppendBuf(inStream
.LastRead());
450 const wxStreamError err
= inStream
.GetLastError();
452 if ( err
!= wxSTREAM_NO_ERROR
&& err
!= wxSTREAM_EOF
)
454 wxLogError(_("Error reading config options."));
458 while ( !inStream
.Eof() );
462 cbuf
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen() + 1, &len
);
463 if ( !len
&& buf
.GetDataLen() )
465 wxLogError(_("Failed to read config options."));
467 #else // !wxUSE_UNICODE
468 // no need for conversion
469 cbuf
= wxCharBuffer::CreateNonOwned((char *)buf
.GetData(), buf
.GetDataLen());
470 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
472 // parse the input contents if there is anything to parse
475 // now break it into lines
476 wxMemoryText memText
;
477 for ( const wxChar
*s
= cbuf
; ; ++s
)
480 while ( *e
!= '\0' && *e
!= '\n' && *e
!= '\r' )
483 // notice that we throw away the original EOL kind here, maybe we
484 // should preserve it?
486 memText
.AddLine(wxString(s
, e
));
491 // skip the second EOL byte if it's a DOS one
492 if ( *e
== '\r' && e
[1] == '\n' )
498 // Finally we can parse it all.
499 Parse(memText
, true /* local */);
506 #endif // wxUSE_STREAMS
508 void wxFileConfig::CleanUp()
512 wxFileConfigLineList
*pCur
= m_linesHead
;
513 while ( pCur
!= NULL
) {
514 wxFileConfigLineList
*pNext
= pCur
->Next();
520 wxFileConfig::~wxFileConfig()
529 // ----------------------------------------------------------------------------
530 // parse a config file
531 // ----------------------------------------------------------------------------
533 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
536 size_t nLineCount
= buffer
.GetLineCount();
538 for ( size_t n
= 0; n
< nLineCount
; n
++ )
540 wxString strLine
= buffer
[n
];
541 // FIXME-UTF8: rewrite using iterators, without this buffer
542 wxWxCharBuffer
buf(strLine
.c_str());
543 const wxChar
*pStart
;
546 // add the line to linked list
548 LineListAppend(strLine
);
551 // skip leading spaces
552 for ( pStart
= buf
; wxIsspace(*pStart
); pStart
++ )
555 // skip blank/comment lines
556 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
559 if ( *pStart
== wxT('[') ) { // a new group
562 while ( *++pEnd
!= wxT(']') ) {
563 if ( *pEnd
== wxT('\\') ) {
564 // the next char is escaped, so skip it even if it is ']'
568 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
569 // we reached the end of line, break out of the loop
574 if ( *pEnd
!= wxT(']') ) {
575 wxLogError(_("file '%s': unexpected character %c at line %d."),
576 buffer
.GetName(), *pEnd
, n
+ 1);
577 continue; // skip this line
580 // group name here is always considered as abs path
583 strGroup
<< wxCONFIG_PATH_SEPARATOR
584 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
586 // will create it if doesn't yet exist
591 if ( m_pCurrentGroup
->Parent() )
592 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
593 m_pCurrentGroup
->SetLine(m_linesTail
);
596 // check that there is nothing except comments left on this line
598 while ( *++pEnd
!= wxT('\0') && bCont
) {
607 // ignore whitespace ('\n' impossible here)
611 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
612 buffer
.GetName(), n
+ 1, pEnd
);
619 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
620 if ( *pEnd
== wxT('\\') ) {
621 // next character may be space or not - still take it because it's
622 // quoted (unless there is nothing)
625 // the error message will be given below anyhow
633 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
636 while ( wxIsspace(*pEnd
) )
639 if ( *pEnd
++ != wxT('=') ) {
640 wxLogError(_("file '%s', line %d: '=' expected."),
641 buffer
.GetName(), n
+ 1);
644 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
646 if ( pEntry
== NULL
) {
648 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
651 if ( bLocal
&& pEntry
->IsImmutable() ) {
652 // immutable keys can't be changed by user
653 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
654 buffer
.GetName(), n
+ 1, strKey
.c_str());
657 // the condition below catches the cases (a) and (b) but not (c):
658 // (a) global key found second time in global file
659 // (b) key found second (or more) time in local file
660 // (c) key from global file now found in local one
661 // which is exactly what we want.
662 else if ( !bLocal
|| pEntry
->IsLocal() ) {
663 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
664 buffer
.GetName(), (int)n
+ 1, strKey
.c_str(), pEntry
->Line());
670 pEntry
->SetLine(m_linesTail
);
673 while ( wxIsspace(*pEnd
) )
676 wxString value
= pEnd
;
677 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
678 value
= FilterInValue(value
);
680 pEntry
->SetValue(value
, false);
686 // ----------------------------------------------------------------------------
688 // ----------------------------------------------------------------------------
690 void wxFileConfig::SetRootPath()
693 m_pCurrentGroup
= m_pRootGroup
;
697 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
699 wxArrayString aParts
;
701 if ( strPath
.empty() ) {
706 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
708 wxSplitPath(aParts
, strPath
);
711 // relative path, combine with current one
712 wxString strFullPath
= m_strPath
;
713 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
714 wxSplitPath(aParts
, strFullPath
);
717 // change current group
719 m_pCurrentGroup
= m_pRootGroup
;
720 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
721 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
722 if ( pNextGroup
== NULL
)
724 if ( !createMissingComponents
)
727 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
730 m_pCurrentGroup
= pNextGroup
;
733 // recombine path parts in one variable
735 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
736 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
742 void wxFileConfig::SetPath(const wxString
& strPath
)
744 DoSetPath(strPath
, true /* create missing path components */);
747 const wxString
& wxFileConfig::GetPath() const
752 // ----------------------------------------------------------------------------
754 // ----------------------------------------------------------------------------
756 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
759 return GetNextGroup(str
, lIndex
);
762 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
764 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) {
765 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
772 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
775 return GetNextEntry(str
, lIndex
);
778 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
780 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) {
781 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
788 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
790 size_t n
= m_pCurrentGroup
->Entries().GetCount();
792 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
794 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
795 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
796 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
797 self
->m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
798 n
+= GetNumberOfEntries(true);
799 self
->m_pCurrentGroup
= pOldCurrentGroup
;
806 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
808 size_t n
= m_pCurrentGroup
->Groups().GetCount();
810 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
812 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
813 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
814 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
815 self
->m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
816 n
+= GetNumberOfGroups(true);
817 self
->m_pCurrentGroup
= pOldCurrentGroup
;
824 // ----------------------------------------------------------------------------
825 // tests for existence
826 // ----------------------------------------------------------------------------
828 bool wxFileConfig::HasGroup(const wxString
& strName
) const
830 // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
831 // but there is no group with empty name so treat this separately
832 if ( strName
.empty() )
835 const wxString pathOld
= GetPath();
837 wxFileConfig
*self
= const_cast<wxFileConfig
*>(this);
839 rc
= self
->DoSetPath(strName
, false /* don't create missing components */);
841 self
->SetPath(pathOld
);
846 bool wxFileConfig::HasEntry(const wxString
& entry
) const
848 // path is the part before the last "/"
849 wxString path
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
);
851 // except in the special case of "/keyname" when there is nothing before "/"
852 if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR
)
854 path
= wxCONFIG_PATH_SEPARATOR
;
857 // change to the path of the entry if necessary and remember the old path
858 // to restore it later
860 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
864 if ( pathOld
.empty() )
865 pathOld
= wxCONFIG_PATH_SEPARATOR
;
867 if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) )
873 // check if the entry exists in this group
874 const bool exists
= m_pCurrentGroup
->FindEntry(
875 entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
;
877 // restore the old path if we changed it above
878 if ( !pathOld
.empty() )
880 self
->SetPath(pathOld
);
886 // ----------------------------------------------------------------------------
888 // ----------------------------------------------------------------------------
890 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
892 wxConfigPathChanger
path(this, key
);
894 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
895 if (pEntry
== NULL
) {
899 *pStr
= pEntry
->Value();
904 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
907 if ( !Read(key
, &str
) )
910 // extra spaces shouldn't prevent us from reading numeric values
913 return str
.ToLong(pl
);
918 bool wxFileConfig::DoReadBinary(const wxString
& key
, wxMemoryBuffer
* buf
) const
920 wxCHECK_MSG( buf
, false, wxT("NULL buffer") );
923 if ( !Read(key
, &str
) )
926 *buf
= wxBase64Decode(str
);
930 #endif // wxUSE_BASE64
932 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
934 wxConfigPathChanger
path(this, key
);
935 wxString strName
= path
.Name();
937 wxLogTrace( FILECONF_TRACE_MASK
,
938 wxT(" Writing String '%s' = '%s' to Group '%s'"),
943 if ( strName
.empty() )
945 // setting the value of a group is an error
947 wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") );
949 // ... except if it's empty in which case it's a way to force it's creation
951 wxLogTrace( FILECONF_TRACE_MASK
,
952 wxT(" Creating group %s"),
953 m_pCurrentGroup
->Name().c_str() );
957 // this will add a line for this group if it didn't have it before (or
958 // do nothing for the root but it's ok as it always exists anyhow)
959 (void)m_pCurrentGroup
->GetGroupLine();
963 // writing an entry check that the name is reasonable
964 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
966 wxLogError( _("Config entry name cannot start with '%c'."),
967 wxCONFIG_IMMUTABLE_PREFIX
);
971 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
975 wxLogTrace( FILECONF_TRACE_MASK
,
976 wxT(" Adding Entry %s"),
978 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
981 wxLogTrace( FILECONF_TRACE_MASK
,
982 wxT(" Setting value %s"),
984 pEntry
->SetValue(szValue
);
992 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
994 return Write(key
, wxString::Format(wxT("%ld"), lValue
));
999 bool wxFileConfig::DoWriteBinary(const wxString
& key
, const wxMemoryBuffer
& buf
)
1001 return Write(key
, wxBase64Encode(buf
));
1004 #endif // wxUSE_BASE64
1006 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
1008 if ( !IsDirty() || !m_fnLocalFile
.GetFullPath() )
1011 // set the umask if needed
1012 wxCHANGE_UMASK(m_umask
);
1014 wxTempFile
file(m_fnLocalFile
.GetFullPath());
1016 if ( !file
.IsOpened() )
1018 wxLogError(_("can't open user configuration file."));
1022 // write all strings to file
1024 filetext
.reserve(4096);
1025 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1027 filetext
<< p
->Text() << wxTextFile::GetEOL();
1030 if ( !file
.Write(filetext
, *m_conv
) )
1032 wxLogError(_("can't write user configuration file."));
1036 if ( !file
.Commit() )
1038 wxLogError(_("Failed to update user configuration file."));
1045 #if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
1046 m_fnLocalFile
.MacSetTypeAndCreator('TEXT', 'ttxt');
1054 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
)
1056 // save unconditionally, even if not dirty
1057 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1059 wxString line
= p
->Text();
1060 line
+= wxTextFile::GetEOL();
1062 wxCharBuffer
buf(line
.mb_str(conv
));
1063 if ( !os
.Write(buf
, strlen(buf
)) )
1065 wxLogError(_("Error saving user configuration data."));
1076 #endif // wxUSE_STREAMS
1078 // ----------------------------------------------------------------------------
1079 // renaming groups/entries
1080 // ----------------------------------------------------------------------------
1082 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1083 const wxString
& newName
)
1085 wxASSERT_MSG( oldName
.find(wxCONFIG_PATH_SEPARATOR
) == wxString::npos
,
1086 wxT("RenameEntry(): paths are not supported") );
1088 // check that the entry exists
1089 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1093 // check that the new entry doesn't already exist
1094 if ( m_pCurrentGroup
->FindEntry(newName
) )
1097 // delete the old entry, create the new one
1098 wxString value
= oldEntry
->Value();
1099 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1104 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1105 newEntry
->SetValue(value
);
1110 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1111 const wxString
& newName
)
1113 // check that the group exists
1114 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1118 // check that the new group doesn't already exist
1119 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1122 group
->Rename(newName
);
1129 // ----------------------------------------------------------------------------
1130 // delete groups/entries
1131 // ----------------------------------------------------------------------------
1133 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1135 wxConfigPathChanger
path(this, key
);
1137 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1142 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1143 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1144 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1145 SetPath(wxT("..")); // changes m_pCurrentGroup!
1146 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1148 //else: never delete the root group
1154 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1156 wxConfigPathChanger
path(this, RemoveTrailingSeparator(key
));
1158 if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) )
1161 path
.UpdateIfDeleted();
1168 bool wxFileConfig::DeleteAll()
1172 if ( m_fnLocalFile
.IsOk() )
1174 if ( m_fnLocalFile
.FileExists() &&
1175 !wxRemoveFile(m_fnLocalFile
.GetFullPath()) )
1177 wxLogSysError(_("can't delete user configuration file '%s'"),
1178 m_fnLocalFile
.GetFullPath().c_str());
1188 // ----------------------------------------------------------------------------
1189 // linked list functions
1190 // ----------------------------------------------------------------------------
1192 // append a new line to the end of the list
1194 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1196 wxLogTrace( FILECONF_TRACE_MASK
,
1197 wxT(" ** Adding Line '%s'"),
1199 wxLogTrace( FILECONF_TRACE_MASK
,
1201 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1203 wxLogTrace( FILECONF_TRACE_MASK
,
1205 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1208 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1210 if ( m_linesTail
== NULL
)
1213 m_linesHead
= pLine
;
1218 m_linesTail
->SetNext(pLine
);
1219 pLine
->SetPrev(m_linesTail
);
1222 m_linesTail
= pLine
;
1224 wxLogTrace( FILECONF_TRACE_MASK
,
1226 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1228 wxLogTrace( FILECONF_TRACE_MASK
,
1230 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1236 // insert a new line after the given one or in the very beginning if !pLine
1237 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1238 wxFileConfigLineList
*pLine
)
1240 wxLogTrace( FILECONF_TRACE_MASK
,
1241 wxT(" ** Inserting Line '%s' after '%s'"),
1243 ((pLine
) ? (const wxChar
*)pLine
->Text().c_str()
1245 wxLogTrace( FILECONF_TRACE_MASK
,
1247 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1249 wxLogTrace( FILECONF_TRACE_MASK
,
1251 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1254 if ( pLine
== m_linesTail
)
1255 return LineListAppend(str
);
1257 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1258 if ( pLine
== NULL
)
1260 // prepend to the list
1261 pNewLine
->SetNext(m_linesHead
);
1262 m_linesHead
->SetPrev(pNewLine
);
1263 m_linesHead
= pNewLine
;
1267 // insert before pLine
1268 wxFileConfigLineList
*pNext
= pLine
->Next();
1269 pNewLine
->SetNext(pNext
);
1270 pNewLine
->SetPrev(pLine
);
1271 pNext
->SetPrev(pNewLine
);
1272 pLine
->SetNext(pNewLine
);
1275 wxLogTrace( FILECONF_TRACE_MASK
,
1277 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1279 wxLogTrace( FILECONF_TRACE_MASK
,
1281 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1287 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1289 wxLogTrace( FILECONF_TRACE_MASK
,
1290 wxT(" ** Removing Line '%s'"),
1291 pLine
->Text().c_str() );
1292 wxLogTrace( FILECONF_TRACE_MASK
,
1294 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1296 wxLogTrace( FILECONF_TRACE_MASK
,
1298 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1301 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1302 *pNext
= pLine
->Next();
1306 if ( pPrev
== NULL
)
1307 m_linesHead
= pNext
;
1309 pPrev
->SetNext(pNext
);
1313 if ( pNext
== NULL
)
1314 m_linesTail
= pPrev
;
1316 pNext
->SetPrev(pPrev
);
1318 wxLogTrace( FILECONF_TRACE_MASK
,
1320 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1322 wxLogTrace( FILECONF_TRACE_MASK
,
1324 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1330 bool wxFileConfig::LineListIsEmpty()
1332 return m_linesHead
== NULL
;
1335 // ============================================================================
1336 // wxFileConfig::wxFileConfigGroup
1337 // ============================================================================
1339 // ----------------------------------------------------------------------------
1341 // ----------------------------------------------------------------------------
1344 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1345 const wxString
& strName
,
1346 wxFileConfig
*pConfig
)
1347 : m_aEntries(CompareEntries
),
1348 m_aSubgroups(CompareGroups
),
1351 m_pConfig
= pConfig
;
1352 m_pParent
= pParent
;
1355 m_pLastEntry
= NULL
;
1356 m_pLastGroup
= NULL
;
1359 // dtor deletes all children
1360 wxFileConfigGroup::~wxFileConfigGroup()
1363 size_t n
, nCount
= m_aEntries
.GetCount();
1364 for ( n
= 0; n
< nCount
; n
++ )
1365 delete m_aEntries
[n
];
1368 nCount
= m_aSubgroups
.GetCount();
1369 for ( n
= 0; n
< nCount
; n
++ )
1370 delete m_aSubgroups
[n
];
1373 // ----------------------------------------------------------------------------
1375 // ----------------------------------------------------------------------------
1377 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1379 // for a normal (i.e. not root) group this method shouldn't be called twice
1380 // unless we are resetting the line
1381 wxASSERT_MSG( !m_pParent
|| !m_pLine
|| !pLine
,
1382 wxT("changing line for a non-root group?") );
1388 This is a bit complicated, so let me explain it in details. All lines that
1389 were read from the local file (the only one we will ever modify) are stored
1390 in a (doubly) linked list. Our problem is to know at which position in this
1391 list should we insert the new entries/subgroups. To solve it we keep three
1392 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1394 m_pLine points to the line containing "[group_name]"
1395 m_pLastEntry points to the last entry of this group in the local file.
1396 m_pLastGroup subgroup
1398 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1399 from the local file, the corresponding variable is set. However, if the group
1400 was read from the global file and then modified or created by the application
1401 these variables are still NULL and we need to create the corresponding lines.
1402 See the following functions (and comments preceding them) for the details of
1405 Also, when our last entry/group are deleted we need to find the new last
1406 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1407 of lines until it either founds an entry/subgroup (and this is the new last
1408 element) or the m_pLine of the group, in which case there are no more entries
1409 (or subgroups) left and m_pLast<element> becomes NULL.
1411 NB: This last problem could be avoided for entries if we added new entries
1412 immediately after m_pLine, but in this case the entries would appear
1413 backwards in the config file (OTOH, it's not that important) and as we
1414 would still need to do it for the subgroups the code wouldn't have been
1415 significantly less complicated.
1418 // Return the line which contains "[our name]". If we're still not in the list,
1419 // add our line to it immediately after the last line of our parent group if we
1420 // have it or in the very beginning if we're the root group.
1421 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1423 wxLogTrace( FILECONF_TRACE_MASK
,
1424 wxT(" GetGroupLine() for Group '%s'"),
1429 wxLogTrace( FILECONF_TRACE_MASK
,
1430 wxT(" Getting Line item pointer") );
1432 wxFileConfigGroup
*pParent
= Parent();
1434 // this group wasn't present in local config file, add it now
1437 wxLogTrace( FILECONF_TRACE_MASK
,
1438 wxT(" checking parent '%s'"),
1439 pParent
->Name().c_str() );
1441 wxString strFullName
;
1443 // add 1 to the name because we don't want to start with '/'
1444 strFullName
<< wxT("[")
1445 << FilterOutEntryName(GetFullName().c_str() + 1)
1447 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1448 pParent
->GetLastGroupLine());
1449 pParent
->SetLastGroup(this); // we're surely after all the others
1451 //else: this is the root group and so we return NULL because we don't
1452 // have any group line
1458 // Return the last line belonging to the subgroups of this group (after which
1459 // we can add a new subgroup), if we don't have any subgroups or entries our
1460 // last line is the group line (m_pLine) itself.
1461 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1463 // if we have any subgroups, our last line is the last line of the last
1467 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1469 wxASSERT_MSG( pLine
, wxT("last group must have !NULL associated line") );
1474 // no subgroups, so the last line is the line of thelast entry (if any)
1475 return GetLastEntryLine();
1478 // return the last line belonging to the entries of this group (after which
1479 // we can add a new entry), if we don't have any entries we will add the new
1480 // one immediately after the group line itself.
1481 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1483 wxLogTrace( FILECONF_TRACE_MASK
,
1484 wxT(" GetLastEntryLine() for Group '%s'"),
1489 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1491 wxASSERT_MSG( pLine
, wxT("last entry must have !NULL associated line") );
1496 // no entries: insert after the group header, if any
1497 return GetGroupLine();
1500 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry
*pEntry
)
1502 m_pLastEntry
= pEntry
;
1506 // the only situation in which a group without its own line can have
1507 // an entry is when the first entry is added to the initially empty
1508 // root pseudo-group
1509 wxASSERT_MSG( !m_pParent
, wxT("unexpected for non root group") );
1511 // let the group know that it does have a line in the file now
1512 m_pLine
= pEntry
->GetLine();
1516 // ----------------------------------------------------------------------------
1518 // ----------------------------------------------------------------------------
1520 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
1522 // update the line of this group
1523 wxFileConfigLineList
*line
= GetGroupLine();
1524 wxCHECK_RET( line
, wxT("a non root group must have a corresponding line!") );
1526 // +1: skip the leading '/'
1527 line
->SetText(wxString::Format(wxT("[%s]"), GetFullName().c_str() + 1));
1530 // also update all subgroups as they have this groups name in their lines
1531 const size_t nCount
= m_aSubgroups
.GetCount();
1532 for ( size_t n
= 0; n
< nCount
; n
++ )
1534 m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines();
1538 void wxFileConfigGroup::Rename(const wxString
& newName
)
1540 wxCHECK_RET( m_pParent
, wxT("the root group can't be renamed") );
1542 if ( newName
== m_strName
)
1545 // we need to remove the group from the parent and it back under the new
1546 // name to keep the parents array of subgroups alphabetically sorted
1547 m_pParent
->m_aSubgroups
.Remove(this);
1549 m_strName
= newName
;
1551 m_pParent
->m_aSubgroups
.Add(this);
1553 // update the group lines recursively
1554 UpdateGroupAndSubgroupsLines();
1557 wxString
wxFileConfigGroup::GetFullName() const
1561 fullname
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1566 // ----------------------------------------------------------------------------
1568 // ----------------------------------------------------------------------------
1570 // use binary search because the array is sorted
1572 wxFileConfigGroup::FindEntry(const wxString
& name
) const
1576 hi
= m_aEntries
.GetCount();
1578 wxFileConfigEntry
*pEntry
;
1582 pEntry
= m_aEntries
[i
];
1584 #if wxCONFIG_CASE_SENSITIVE
1585 res
= pEntry
->Name().compare(name
);
1587 res
= pEntry
->Name().CmpNoCase(name
);
1602 wxFileConfigGroup::FindSubgroup(const wxString
& name
) const
1606 hi
= m_aSubgroups
.GetCount();
1608 wxFileConfigGroup
*pGroup
;
1612 pGroup
= m_aSubgroups
[i
];
1614 #if wxCONFIG_CASE_SENSITIVE
1615 res
= pGroup
->Name().compare(name
);
1617 res
= pGroup
->Name().CmpNoCase(name
);
1631 // ----------------------------------------------------------------------------
1632 // create a new item
1633 // ----------------------------------------------------------------------------
1635 // create a new entry and add it to the current group
1636 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1638 wxASSERT( FindEntry(strName
) == 0 );
1640 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1642 m_aEntries
.Add(pEntry
);
1646 // create a new group and add it to the current group
1647 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1649 wxASSERT( FindSubgroup(strName
) == 0 );
1651 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1653 m_aSubgroups
.Add(pGroup
);
1657 // ----------------------------------------------------------------------------
1659 // ----------------------------------------------------------------------------
1662 The delete operations are _very_ slow if we delete the last item of this
1663 group (see comments before GetXXXLineXXX functions for more details),
1664 so it's much better to start with the first entry/group if we want to
1665 delete several of them.
1668 bool wxFileConfigGroup::DeleteSubgroupByName(const wxString
& name
)
1670 wxFileConfigGroup
* const pGroup
= FindSubgroup(name
);
1672 return pGroup
? DeleteSubgroup(pGroup
) : false;
1675 // Delete the subgroup and remove all references to it from
1676 // other data structures.
1677 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1679 wxCHECK_MSG( pGroup
, false, wxT("deleting non existing group?") );
1681 wxLogTrace( FILECONF_TRACE_MASK
,
1682 wxT("Deleting group '%s' from '%s'"),
1683 pGroup
->Name().c_str(),
1686 wxLogTrace( FILECONF_TRACE_MASK
,
1687 wxT(" (m_pLine) = prev: %p, this %p, next %p"),
1688 m_pLine
? static_cast<void*>(m_pLine
->Prev()) : 0,
1689 static_cast<void*>(m_pLine
),
1690 m_pLine
? static_cast<void*>(m_pLine
->Next()) : 0 );
1691 wxLogTrace( FILECONF_TRACE_MASK
,
1693 m_pLine
? (const wxChar
*)m_pLine
->Text().c_str()
1696 // delete all entries...
1697 size_t nCount
= pGroup
->m_aEntries
.GetCount();
1699 wxLogTrace(FILECONF_TRACE_MASK
,
1700 wxT("Removing %lu entries"), (unsigned long)nCount
);
1702 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1704 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1708 wxLogTrace( FILECONF_TRACE_MASK
,
1710 pLine
->Text().c_str() );
1711 m_pConfig
->LineListRemove(pLine
);
1715 // ...and subgroups of this subgroup
1716 nCount
= pGroup
->m_aSubgroups
.GetCount();
1718 wxLogTrace( FILECONF_TRACE_MASK
,
1719 wxT("Removing %lu subgroups"), (unsigned long)nCount
);
1721 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1723 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1726 // and then finally the group itself
1727 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1730 wxLogTrace( FILECONF_TRACE_MASK
,
1731 wxT(" Removing line for group '%s' : '%s'"),
1732 pGroup
->Name().c_str(),
1733 pLine
->Text().c_str() );
1734 wxLogTrace( FILECONF_TRACE_MASK
,
1735 wxT(" Removing from group '%s' : '%s'"),
1737 ((m_pLine
) ? (const wxChar
*)m_pLine
->Text().c_str()
1740 // notice that we may do this test inside the previous "if"
1741 // because the last entry's line is surely !NULL
1742 if ( pGroup
== m_pLastGroup
)
1744 wxLogTrace( FILECONF_TRACE_MASK
,
1745 wxT(" Removing last group") );
1747 // our last entry is being deleted, so find the last one which
1748 // stays by going back until we find a subgroup or reach the
1750 const size_t nSubgroups
= m_aSubgroups
.GetCount();
1752 m_pLastGroup
= NULL
;
1753 for ( wxFileConfigLineList
*pl
= pLine
->Prev();
1754 pl
&& !m_pLastGroup
;
1757 // does this line belong to our subgroup?
1758 for ( size_t n
= 0; n
< nSubgroups
; n
++ )
1760 // do _not_ call GetGroupLine! we don't want to add it to
1761 // the local file if it's not already there
1762 if ( m_aSubgroups
[n
]->m_pLine
== pl
)
1764 m_pLastGroup
= m_aSubgroups
[n
];
1769 if ( pl
== m_pLine
)
1774 m_pConfig
->LineListRemove(pLine
);
1778 wxLogTrace( FILECONF_TRACE_MASK
,
1779 wxT(" No line entry for Group '%s'?"),
1780 pGroup
->Name().c_str() );
1783 m_aSubgroups
.Remove(pGroup
);
1789 bool wxFileConfigGroup::DeleteEntry(const wxString
& name
)
1791 wxFileConfigEntry
*pEntry
= FindEntry(name
);
1794 // entry doesn't exist, nothing to do
1798 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1799 if ( pLine
!= NULL
) {
1800 // notice that we may do this test inside the previous "if" because the
1801 // last entry's line is surely !NULL
1802 if ( pEntry
== m_pLastEntry
) {
1803 // our last entry is being deleted - find the last one which stays
1804 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1806 // find the previous entry (if any)
1807 wxFileConfigEntry
*pNewLast
= NULL
;
1808 const wxFileConfigLineList
* const
1809 pNewLastLine
= m_pLastEntry
->GetLine()->Prev();
1810 const size_t nEntries
= m_aEntries
.GetCount();
1811 for ( size_t n
= 0; n
< nEntries
; n
++ ) {
1812 if ( m_aEntries
[n
]->GetLine() == pNewLastLine
) {
1813 pNewLast
= m_aEntries
[n
];
1818 // pNewLast can be NULL here -- it's ok and can happen if we have no
1820 m_pLastEntry
= pNewLast
;
1823 m_pConfig
->LineListRemove(pLine
);
1826 m_aEntries
.Remove(pEntry
);
1832 // ============================================================================
1833 // wxFileConfig::wxFileConfigEntry
1834 // ============================================================================
1836 // ----------------------------------------------------------------------------
1838 // ----------------------------------------------------------------------------
1839 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1840 const wxString
& strName
,
1842 : m_strName(strName
)
1844 wxASSERT( !strName
.empty() );
1846 m_pParent
= pParent
;
1850 m_bHasValue
= false;
1852 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1854 m_strName
.erase(0, 1); // remove first character
1857 // ----------------------------------------------------------------------------
1859 // ----------------------------------------------------------------------------
1861 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1863 if ( m_pLine
!= NULL
) {
1864 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1865 Name().c_str(), m_pParent
->GetFullName().c_str());
1869 Group()->SetLastEntry(this);
1872 // second parameter is false if we read the value from file and prevents the
1873 // entry from being marked as 'dirty'
1874 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1876 if ( bUser
&& IsImmutable() )
1878 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1883 // do nothing if it's the same value: but don't test for it if m_bHasValue
1884 // hadn't been set yet or we'd never write empty values to the file
1885 if ( m_bHasValue
&& strValue
== m_strValue
)
1889 m_strValue
= strValue
;
1893 wxString strValFiltered
;
1895 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1897 strValFiltered
= strValue
;
1900 strValFiltered
= FilterOutValue(strValue
);
1904 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1908 // entry was read from the local config file, just modify the line
1909 m_pLine
->SetText(strLine
);
1911 else // this entry didn't exist in the local file
1913 // add a new line to the file: note that line returned by
1914 // GetLastEntryLine() may be NULL if we're in the root group and it
1915 // doesn't have any entries yet, but this is ok as passing NULL
1916 // line to LineListInsert() means to prepend new line to the list
1917 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1918 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1920 Group()->SetLastEntry(this);
1925 // ============================================================================
1927 // ============================================================================
1929 // ----------------------------------------------------------------------------
1930 // compare functions for array sorting
1931 // ----------------------------------------------------------------------------
1933 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1935 #if wxCONFIG_CASE_SENSITIVE
1936 return p1
->Name().compare(p2
->Name());
1938 return p1
->Name().CmpNoCase(p2
->Name());
1942 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1944 #if wxCONFIG_CASE_SENSITIVE
1945 return p1
->Name().compare(p2
->Name());
1947 return p1
->Name().CmpNoCase(p2
->Name());
1951 // ----------------------------------------------------------------------------
1953 // ----------------------------------------------------------------------------
1955 // undo FilterOutValue
1956 static wxString
FilterInValue(const wxString
& str
)
1962 strResult
.reserve(str
.length());
1964 wxString::const_iterator i
= str
.begin();
1965 const bool bQuoted
= *i
== '"';
1969 for ( const wxString::const_iterator end
= str
.end(); i
!= end
; ++i
)
1971 if ( *i
== wxT('\\') )
1975 wxLogWarning(_("trailing backslash ignored in '%s'"), str
.c_str());
1979 switch ( (*i
).GetValue() )
1982 strResult
+= wxT('\n');
1986 strResult
+= wxT('\r');
1990 strResult
+= wxT('\t');
1994 strResult
+= wxT('\\');
1998 strResult
+= wxT('"');
2002 else // not a backslash
2004 if ( *i
!= wxT('"') || !bQuoted
)
2008 else if ( i
!= end
- 1 )
2010 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2011 i
- str
.begin(), str
.c_str());
2013 //else: it's the last quote of a quoted string, ok
2020 // quote the string before writing it to file
2021 static wxString
FilterOutValue(const wxString
& str
)
2027 strResult
.Alloc(str
.Len());
2029 // quoting is necessary to preserve spaces in the beginning of the string
2030 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2033 strResult
+= wxT('"');
2036 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2037 switch ( str
[n
].GetValue() ) {
2059 //else: fall through
2062 strResult
+= str
[n
];
2063 continue; // nothing special to do
2066 // we get here only for special characters
2067 strResult
<< wxT('\\') << c
;
2071 strResult
+= wxT('"');
2076 // undo FilterOutEntryName
2077 static wxString
FilterInEntryName(const wxString
& str
)
2080 strResult
.Alloc(str
.Len());
2082 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2083 if ( *pc
== wxT('\\') ) {
2084 // we need to test it here or we'd skip past the NUL in the loop line
2085 if ( *++pc
== wxT('\0') )
2095 // sanitize entry or group name: insert '\\' before any special characters
2096 static wxString
FilterOutEntryName(const wxString
& str
)
2099 strResult
.Alloc(str
.Len());
2101 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2102 const wxChar c
= *pc
;
2104 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2105 // which will probably never have special meaning and with which we can't
2106 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2108 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2109 // should *not* be quoted
2112 ((unsigned char)c
< 127) &&
2114 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%()"), c
) )
2116 strResult
+= wxT('\\');
2125 // we can't put ?: in the ctor initializer list because it confuses some
2126 // broken compilers (Borland C++)
2127 static wxString
GetAppName(const wxString
& appName
)
2129 if ( !appName
&& wxTheApp
)
2130 return wxTheApp
->GetAppName();
2135 #endif // wxUSE_CONFIG