1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/fileconf.cpp
3 // Purpose: implementation of wxFileConfig derivation of wxConfig
4 // Author: Vadim Zeitlin
6 // Created: 07.04.98 (adapted from appconf.cpp)
8 // Copyright: (c) 1997 Karsten Ballüder & Vadim Zeitlin
9 // Ballueder@usa.net <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
24 #if wxUSE_CONFIG && wxUSE_FILECONFIG
27 #include "wx/dynarray.h"
28 #include "wx/string.h"
32 #include "wx/utils.h" // for wxGetHomeDir
34 #include "wx/stream.h"
35 #endif // wxUSE_STREAMS
39 #include "wx/textfile.h"
40 #include "wx/memtext.h"
41 #include "wx/config.h"
42 #include "wx/fileconf.h"
43 #include "wx/filefn.h"
45 #if defined(__WXMAC__)
46 #include "wx/mac/private.h" // includes mac headers
47 #include "wx/filename.h" // for MacSetTypeAndCreator
50 #if defined(__WXMSW__)
51 #include "wx/msw/private.h"
61 // ----------------------------------------------------------------------------
63 // ----------------------------------------------------------------------------
64 #define CONST_CAST ((wxFileConfig *)this)->
66 // ----------------------------------------------------------------------------
68 // ----------------------------------------------------------------------------
74 #define FILECONF_TRACE_MASK _T("fileconf")
76 // ----------------------------------------------------------------------------
77 // global functions declarations
78 // ----------------------------------------------------------------------------
80 // compare functions for sorting the arrays
81 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
82 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
85 static wxString
FilterInValue(const wxString
& str
);
86 static wxString
FilterOutValue(const wxString
& str
);
88 static wxString
FilterInEntryName(const wxString
& str
);
89 static wxString
FilterOutEntryName(const wxString
& str
);
91 // get the name to use in wxFileConfig ctor
92 static wxString
GetAppName(const wxString
& appname
);
94 // ============================================================================
96 // ============================================================================
98 // ----------------------------------------------------------------------------
99 // "template" array types
100 // ----------------------------------------------------------------------------
102 #ifdef WXMAKINGDLL_BASE
103 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
,
105 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
,
108 WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
109 WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
);
112 // ----------------------------------------------------------------------------
113 // wxFileConfigLineList
114 // ----------------------------------------------------------------------------
116 // we store all lines of the local config file as a linked list in memory
117 class wxFileConfigLineList
120 void SetNext(wxFileConfigLineList
*pNext
) { m_pNext
= pNext
; }
121 void SetPrev(wxFileConfigLineList
*pPrev
) { m_pPrev
= pPrev
; }
124 wxFileConfigLineList(const wxString
& str
,
125 wxFileConfigLineList
*pNext
= NULL
) : m_strLine(str
)
126 { SetNext(pNext
); SetPrev(NULL
); }
128 // next/prev nodes in the linked list
129 wxFileConfigLineList
*Next() const { return m_pNext
; }
130 wxFileConfigLineList
*Prev() const { return m_pPrev
; }
132 // get/change lines text
133 void SetText(const wxString
& str
) { m_strLine
= str
; }
134 const wxString
& Text() const { return m_strLine
; }
137 wxString m_strLine
; // line contents
138 wxFileConfigLineList
*m_pNext
, // next node
139 *m_pPrev
; // previous one
141 DECLARE_NO_COPY_CLASS(wxFileConfigLineList
)
144 // ----------------------------------------------------------------------------
145 // wxFileConfigEntry: a name/value pair
146 // ----------------------------------------------------------------------------
148 class wxFileConfigEntry
151 wxFileConfigGroup
*m_pParent
; // group that contains us
153 wxString m_strName
, // entry name
155 bool m_bImmutable
:1, // can be overriden locally?
156 m_bHasValue
:1; // set after first call to SetValue()
158 int m_nLine
; // used if m_pLine == NULL only
160 // pointer to our line in the linked list or NULL if it was found in global
161 // file (which we don't modify)
162 wxFileConfigLineList
*m_pLine
;
165 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
166 const wxString
& strName
, int nLine
);
169 const wxString
& Name() const { return m_strName
; }
170 const wxString
& Value() const { return m_strValue
; }
171 wxFileConfigGroup
*Group() const { return m_pParent
; }
172 bool IsImmutable() const { return m_bImmutable
; }
173 bool IsLocal() const { return m_pLine
!= 0; }
174 int Line() const { return m_nLine
; }
175 wxFileConfigLineList
*
176 GetLine() const { return m_pLine
; }
178 // modify entry attributes
179 void SetValue(const wxString
& strValue
, bool bUser
= true);
180 void SetLine(wxFileConfigLineList
*pLine
);
182 DECLARE_NO_COPY_CLASS(wxFileConfigEntry
)
185 // ----------------------------------------------------------------------------
186 // wxFileConfigGroup: container of entries and other groups
187 // ----------------------------------------------------------------------------
189 class wxFileConfigGroup
192 wxFileConfig
*m_pConfig
; // config object we belong to
193 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
194 ArrayEntries m_aEntries
; // entries in this group
195 ArrayGroups m_aSubgroups
; // subgroups
196 wxString m_strName
; // group's name
197 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
198 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
199 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
201 // DeleteSubgroupByName helper
202 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
205 void UpdateGroupAndSubgroupsLines();
209 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
211 // dtor deletes all entries and subgroups also
212 ~wxFileConfigGroup();
215 const wxString
& Name() const { return m_strName
; }
216 wxFileConfigGroup
*Parent() const { return m_pParent
; }
217 wxFileConfig
*Config() const { return m_pConfig
; }
219 const ArrayEntries
& Entries() const { return m_aEntries
; }
220 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
221 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
223 // find entry/subgroup (NULL if not found)
224 wxFileConfigGroup
*FindSubgroup(const wxChar
*szName
) const;
225 wxFileConfigEntry
*FindEntry (const wxChar
*szName
) const;
227 // delete entry/subgroup, return false if doesn't exist
228 bool DeleteSubgroupByName(const wxChar
*szName
);
229 bool DeleteEntry(const wxChar
*szName
);
231 // create new entry/subgroup returning pointer to newly created element
232 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
233 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
235 void SetLine(wxFileConfigLineList
*pLine
);
237 // rename: no checks are done to ensure that the name is unique!
238 void Rename(const wxString
& newName
);
241 wxString
GetFullName() const;
243 // get the last line belonging to an entry/subgroup of this group
244 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
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 DECLARE_NO_COPY_CLASS(wxFileConfigGroup
)
256 // ============================================================================
258 // ============================================================================
260 // ----------------------------------------------------------------------------
262 // ----------------------------------------------------------------------------
263 wxString
wxFileConfig::GetGlobalDir()
267 #ifdef __VMS__ // Note if __VMS is defined __UNIX is also defined
268 strDir
= wxT("sys$manager:");
269 #elif defined(__WXMAC__)
270 strDir
= wxMacFindFolder( (short) kOnSystemDisk
, kPreferencesFolderType
, kDontCreateFolder
) ;
271 #elif defined( __UNIX__ )
272 strDir
= wxT("/etc/");
273 #elif defined(__OS2__)
274 ULONG aulSysInfo
[QSV_MAX
] = {0};
278 rc
= DosQuerySysInfo( 1L, QSV_MAX
, (PVOID
)aulSysInfo
, sizeof(ULONG
)*QSV_MAX
);
281 drive
= aulSysInfo
[QSV_BOOT_DRIVE
- 1];
282 strDir
.Printf(wxT("%c:\\OS2\\"), 'A'+drive
-1);
284 #elif defined(__WXSTUBS__)
285 wxFAIL_MSG( wxT("TODO") );
286 #elif defined(__DOS__)
287 // There's no such thing as global cfg dir in MS-DOS, let's return
288 // current directory (FIXME_MGL?)
290 #elif defined(__WXWINCE__)
291 strDir
= wxT("\\Windows\\");
294 wxChar szWinDir
[MAX_PATH
];
295 ::GetWindowsDirectory(szWinDir
, MAX_PATH
);
299 #endif // Unix/Windows
304 wxString
wxFileConfig::GetLocalDir()
308 #if defined(__WXMAC__) || defined(__DOS__)
309 // no local dir concept on Mac OS 9 or MS-DOS
310 strDir
<< GetGlobalDir() ;
312 wxGetHomeDir(&strDir
);
316 (strDir
.Last() != wxT('/'))
318 && (strDir
.Last() != wxT(']'))
323 if (strDir
.Last() != wxT('\\'))
331 wxString
wxFileConfig::GetGlobalFileName(const wxString
& file
)
333 wxString str
= GetGlobalDir();
336 if ( wxStrchr(file
, wxT('.')) == NULL
)
337 #if defined( __WXMAC__ )
338 str
<< wxT(" Preferences") ;
339 #elif defined( __UNIX__ )
348 wxString
wxFileConfig::GetLocalFileName(const wxString
& file
)
351 // On VMS I saw the problem that the home directory was appended
352 // twice for the configuration file. Does that also happen for
354 wxString str
= wxT( '.' );
356 wxString str
= GetLocalDir();
359 #if defined( __UNIX__ ) && !defined( __VMS ) && !defined( __WXMAC__ )
365 #if defined(__WINDOWS__) || defined(__DOS__)
366 if ( wxStrchr(file
, wxT('.')) == NULL
)
371 str
<< wxT(" Preferences") ;
377 // ----------------------------------------------------------------------------
379 // ----------------------------------------------------------------------------
381 void wxFileConfig::Init()
384 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
389 // It's not an error if (one of the) file(s) doesn't exist.
391 // parse the global file
392 if ( !m_strGlobalFile
.empty() && wxFile::Exists(m_strGlobalFile
) )
394 wxTextFile
fileGlobal(m_strGlobalFile
);
396 if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) )
398 Parse(fileGlobal
, false /* global */);
403 wxLogWarning(_("can't open global configuration file '%s'."), m_strGlobalFile
.c_str());
407 // parse the local file
408 if ( !m_strLocalFile
.empty() && wxFile::Exists(m_strLocalFile
) )
410 wxTextFile
fileLocal(m_strLocalFile
);
411 if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) )
413 Parse(fileLocal
, true /* local */);
418 wxLogWarning(_("can't open user configuration file '%s'."), m_strLocalFile
.c_str() );
425 // constructor supports creation of wxFileConfig objects of any type
426 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
427 const wxString
& strLocal
, const wxString
& strGlobal
,
429 const wxMBConv
& conv
)
430 : wxConfigBase(::GetAppName(appName
), vendorName
,
433 m_strLocalFile(strLocal
), m_strGlobalFile(strGlobal
),
436 // Make up names for files if empty
437 if ( m_strLocalFile
.empty() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
439 m_strLocalFile
= GetLocalFileName(GetAppName());
440 #if defined(__UNIX__) && !defined(__VMS)
441 if ( style
& wxCONFIG_USE_SUBDIR
)
442 m_strLocalFile
<< wxFILE_SEP_PATH
<< GetAppName() << _T(".conf");
446 if ( m_strGlobalFile
.empty() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
447 m_strGlobalFile
= GetGlobalFileName(GetAppName());
449 // Check if styles are not supplied, but filenames are, in which case
450 // add the correct styles.
451 if ( !m_strLocalFile
.empty() )
452 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
454 if ( !m_strGlobalFile
.empty() )
455 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
457 // if the path is not absolute, prepend the standard directory to it
458 // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
459 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
461 if ( !m_strLocalFile
.empty() && !wxIsAbsolutePath(m_strLocalFile
) )
463 const wxString strLocalOrig
= m_strLocalFile
;
464 m_strLocalFile
= GetLocalDir();
465 m_strLocalFile
<< strLocalOrig
;
468 if ( !m_strGlobalFile
.empty() && !wxIsAbsolutePath(m_strGlobalFile
) )
470 const wxString strGlobalOrig
= m_strGlobalFile
;
471 m_strGlobalFile
= GetGlobalDir();
472 m_strGlobalFile
<< strGlobalOrig
;
483 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, const wxMBConv
& conv
)
484 : m_conv(conv
.Clone())
486 // always local_file when this constructor is called (?)
487 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
490 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
495 // read the entire stream contents in memory
498 static const size_t chunkLen
= 1024;
500 wxMemoryBuffer
buf(chunkLen
);
503 inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
);
504 buf
.UngetAppendBuf(inStream
.LastRead());
506 const wxStreamError err
= inStream
.GetLastError();
508 if ( err
!= wxSTREAM_NO_ERROR
&& err
!= wxSTREAM_EOF
)
510 wxLogError(_("Error reading config options."));
514 while ( !inStream
.Eof() );
518 str
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen(), &len
);
519 if ( !len
&& buf
.GetDataLen() )
521 wxLogError(_("Failed to read config options."));
523 #else // !wxUSE_UNICODE
524 // no need for conversion
525 str
.assign((char *)buf
.GetData(), buf
.GetDataLen());
526 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
530 // translate everything to the current (platform-dependent) line
531 // termination character
532 str
= wxTextBuffer::Translate(str
);
534 wxMemoryText memText
;
536 // Now we can add the text to the memory text. To do this we extract line
537 // by line from the translated string, until we've reached the end.
539 // VZ: all this is horribly inefficient, we should do the translation on
540 // the fly in one pass saving both memory and time (TODO)
542 const wxChar
*pEOL
= wxTextBuffer::GetEOL(wxTextBuffer::typeDefault
);
543 const size_t EOLLen
= wxStrlen(pEOL
);
545 int posLineStart
= str
.Find(pEOL
);
546 while ( posLineStart
!= -1 )
548 wxString
line(str
.Left(posLineStart
));
550 memText
.AddLine(line
);
552 str
= str
.Mid(posLineStart
+ EOLLen
);
554 posLineStart
= str
.Find(pEOL
);
557 // also add whatever we have left in the translated string.
559 memText
.AddLine(str
);
561 // Finally we can parse it all.
562 Parse(memText
, true /* local */);
568 #endif // wxUSE_STREAMS
570 void wxFileConfig::CleanUp()
574 wxFileConfigLineList
*pCur
= m_linesHead
;
575 while ( pCur
!= NULL
) {
576 wxFileConfigLineList
*pNext
= pCur
->Next();
582 wxFileConfig::~wxFileConfig()
591 // ----------------------------------------------------------------------------
592 // parse a config file
593 // ----------------------------------------------------------------------------
595 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
597 const wxChar
*pStart
;
601 size_t nLineCount
= buffer
.GetLineCount();
603 for ( size_t n
= 0; n
< nLineCount
; n
++ )
607 // add the line to linked list
610 LineListAppend(strLine
);
612 // let the root group have its start line as well
615 m_pCurrentGroup
->SetLine(m_linesTail
);
620 // skip leading spaces
621 for ( pStart
= strLine
; wxIsspace(*pStart
); pStart
++ )
624 // skip blank/comment lines
625 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
628 if ( *pStart
== wxT('[') ) { // a new group
631 while ( *++pEnd
!= wxT(']') ) {
632 if ( *pEnd
== wxT('\\') ) {
633 // the next char is escaped, so skip it even if it is ']'
637 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
638 // we reached the end of line, break out of the loop
643 if ( *pEnd
!= wxT(']') ) {
644 wxLogError(_("file '%s': unexpected character %c at line %d."),
645 buffer
.GetName(), *pEnd
, n
+ 1);
646 continue; // skip this line
649 // group name here is always considered as abs path
652 strGroup
<< wxCONFIG_PATH_SEPARATOR
653 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
655 // will create it if doesn't yet exist
660 if ( m_pCurrentGroup
->Parent() )
661 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
662 m_pCurrentGroup
->SetLine(m_linesTail
);
665 // check that there is nothing except comments left on this line
667 while ( *++pEnd
!= wxT('\0') && bCont
) {
676 // ignore whitespace ('\n' impossible here)
680 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
681 buffer
.GetName(), n
+ 1, pEnd
);
688 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
689 if ( *pEnd
== wxT('\\') ) {
690 // next character may be space or not - still take it because it's
691 // quoted (unless there is nothing)
694 // the error message will be given below anyhow
702 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
705 while ( wxIsspace(*pEnd
) )
708 if ( *pEnd
++ != wxT('=') ) {
709 wxLogError(_("file '%s', line %d: '=' expected."),
710 buffer
.GetName(), n
+ 1);
713 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
715 if ( pEntry
== NULL
) {
717 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
720 if ( bLocal
&& pEntry
->IsImmutable() ) {
721 // immutable keys can't be changed by user
722 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
723 buffer
.GetName(), n
+ 1, strKey
.c_str());
726 // the condition below catches the cases (a) and (b) but not (c):
727 // (a) global key found second time in global file
728 // (b) key found second (or more) time in local file
729 // (c) key from global file now found in local one
730 // which is exactly what we want.
731 else if ( !bLocal
|| pEntry
->IsLocal() ) {
732 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
733 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
739 pEntry
->SetLine(m_linesTail
);
742 while ( wxIsspace(*pEnd
) )
745 wxString value
= pEnd
;
746 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
747 value
= FilterInValue(value
);
749 pEntry
->SetValue(value
, false);
755 // ----------------------------------------------------------------------------
757 // ----------------------------------------------------------------------------
759 void wxFileConfig::SetRootPath()
762 m_pCurrentGroup
= m_pRootGroup
;
766 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
768 wxArrayString aParts
;
770 if ( strPath
.empty() ) {
775 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
777 wxSplitPath(aParts
, strPath
);
780 // relative path, combine with current one
781 wxString strFullPath
= m_strPath
;
782 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
783 wxSplitPath(aParts
, strFullPath
);
786 // change current group
788 m_pCurrentGroup
= m_pRootGroup
;
789 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
790 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
791 if ( pNextGroup
== NULL
)
793 if ( !createMissingComponents
)
796 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
799 m_pCurrentGroup
= pNextGroup
;
802 // recombine path parts in one variable
804 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
805 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
811 void wxFileConfig::SetPath(const wxString
& strPath
)
813 DoSetPath(strPath
, true /* create missing path components */);
816 // ----------------------------------------------------------------------------
818 // ----------------------------------------------------------------------------
820 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
823 return GetNextGroup(str
, lIndex
);
826 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
828 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().Count() ) {
829 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
836 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
839 return GetNextEntry(str
, lIndex
);
842 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
844 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().Count() ) {
845 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
852 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
854 size_t n
= m_pCurrentGroup
->Entries().Count();
856 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
857 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
858 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
859 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
860 n
+= GetNumberOfEntries(true);
861 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
868 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
870 size_t n
= m_pCurrentGroup
->Groups().Count();
872 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
873 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
874 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
875 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
876 n
+= GetNumberOfGroups(true);
877 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
884 // ----------------------------------------------------------------------------
885 // tests for existence
886 // ----------------------------------------------------------------------------
888 bool wxFileConfig::HasGroup(const wxString
& strName
) const
890 // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
891 // but there is no group with empty name so treat this separately
892 if ( strName
.empty() )
895 const wxString pathOld
= GetPath();
897 wxFileConfig
*self
= wx_const_cast(wxFileConfig
*, this);
899 rc
= self
->DoSetPath(strName
, false /* don't create missing components */);
901 self
->SetPath(pathOld
);
906 bool wxFileConfig::HasEntry(const wxString
& entry
) const
908 // path is the part before the last "/"
909 wxString path
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
);
911 // except in the special case of "/keyname" when there is nothing before "/"
912 if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR
)
914 path
= wxCONFIG_PATH_SEPARATOR
;
917 // change to the path of the entry if necessary and remember the old path
918 // to restore it later
920 wxFileConfig
* const self
= wx_const_cast(wxFileConfig
*, this);
924 if ( pathOld
.empty() )
925 pathOld
= wxCONFIG_PATH_SEPARATOR
;
927 if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) )
933 // check if the entry exists in this group
934 const bool exists
= m_pCurrentGroup
->FindEntry(
935 entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
;
937 // restore the old path if we changed it above
938 if ( !pathOld
.empty() )
940 self
->SetPath(pathOld
);
946 // ----------------------------------------------------------------------------
948 // ----------------------------------------------------------------------------
950 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
952 wxConfigPathChanger
path(this, key
);
954 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
955 if (pEntry
== NULL
) {
959 *pStr
= pEntry
->Value();
964 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
967 if ( !Read(key
, &str
) )
970 // extra spaces shouldn't prevent us from reading numeric values
973 return str
.ToLong(pl
);
976 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
978 wxConfigPathChanger
path(this, key
);
979 wxString strName
= path
.Name();
981 wxLogTrace( FILECONF_TRACE_MASK
,
982 _T(" Writing String '%s' = '%s' to Group '%s'"),
987 if ( strName
.empty() )
989 // setting the value of a group is an error
991 wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") );
993 // ... except if it's empty in which case it's a way to force it's creation
995 wxLogTrace( FILECONF_TRACE_MASK
,
996 _T(" Creating group %s"),
997 m_pCurrentGroup
->Name().c_str() );
1001 // this will add a line for this group if it didn't have it before
1003 (void)m_pCurrentGroup
->GetGroupLine();
1007 // writing an entry check that the name is reasonable
1008 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
1010 wxLogError( _("Config entry name cannot start with '%c'."),
1011 wxCONFIG_IMMUTABLE_PREFIX
);
1015 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
1019 wxLogTrace( FILECONF_TRACE_MASK
,
1020 _T(" Adding Entry %s"),
1022 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
1025 wxLogTrace( FILECONF_TRACE_MASK
,
1026 _T(" Setting value %s"),
1028 pEntry
->SetValue(szValue
);
1036 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
1038 return Write(key
, wxString::Format(_T("%ld"), lValue
));
1041 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
1043 if ( !IsDirty() || !m_strLocalFile
)
1046 // set the umask if needed
1047 wxCHANGE_UMASK(m_umask
);
1049 wxTempFile
file(m_strLocalFile
);
1051 if ( !file
.IsOpened() )
1053 wxLogError(_("can't open user configuration file."));
1057 // write all strings to file
1059 filetext
.reserve(4096);
1060 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1062 filetext
<< p
->Text() << wxTextFile::GetEOL();
1065 if ( !file
.Write(filetext
, *m_conv
) )
1067 wxLogError(_("can't write user configuration file."));
1071 if ( !file
.Commit() )
1073 wxLogError(_("Failed to update user configuration file."));
1080 #if defined(__WXMAC__)
1081 wxFileName(m_strLocalFile
).MacSetTypeAndCreator('TEXT', 'ttxt');
1089 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
)
1091 // save unconditionally, even if not dirty
1092 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1094 wxString line
= p
->Text();
1095 line
+= wxTextFile::GetEOL();
1097 wxCharBuffer
buf(line
.mb_str(conv
));
1098 if ( !os
.Write(buf
, strlen(buf
)) )
1100 wxLogError(_("Error saving user configuration data."));
1111 #endif // wxUSE_STREAMS
1113 // ----------------------------------------------------------------------------
1114 // renaming groups/entries
1115 // ----------------------------------------------------------------------------
1117 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1118 const wxString
& newName
)
1120 wxASSERT_MSG( !wxStrchr(oldName
, wxCONFIG_PATH_SEPARATOR
),
1121 _T("RenameEntry(): paths are not supported") );
1123 // check that the entry exists
1124 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1128 // check that the new entry doesn't already exist
1129 if ( m_pCurrentGroup
->FindEntry(newName
) )
1132 // delete the old entry, create the new one
1133 wxString value
= oldEntry
->Value();
1134 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1139 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1140 newEntry
->SetValue(value
);
1145 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1146 const wxString
& newName
)
1148 // check that the group exists
1149 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1153 // check that the new group doesn't already exist
1154 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1157 group
->Rename(newName
);
1164 // ----------------------------------------------------------------------------
1165 // delete groups/entries
1166 // ----------------------------------------------------------------------------
1168 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1170 wxConfigPathChanger
path(this, key
);
1172 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1177 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1178 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1179 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1180 SetPath(wxT("..")); // changes m_pCurrentGroup!
1181 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1183 //else: never delete the root group
1189 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1191 wxConfigPathChanger
path(this, RemoveTrailingSeparator(key
));
1193 if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) )
1196 path
.UpdateIfDeleted();
1203 bool wxFileConfig::DeleteAll()
1207 if ( !m_strLocalFile
.empty() )
1209 if ( wxFile::Exists(m_strLocalFile
) && wxRemove(m_strLocalFile
) == -1 )
1211 wxLogSysError(_("can't delete user configuration file '%s'"),
1212 m_strLocalFile
.c_str());
1222 // ----------------------------------------------------------------------------
1223 // linked list functions
1224 // ----------------------------------------------------------------------------
1226 // append a new line to the end of the list
1228 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1230 wxLogTrace( FILECONF_TRACE_MASK
,
1231 _T(" ** Adding Line '%s'"),
1233 wxLogTrace( FILECONF_TRACE_MASK
,
1235 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1236 wxLogTrace( FILECONF_TRACE_MASK
,
1238 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1240 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1242 if ( m_linesTail
== NULL
)
1245 m_linesHead
= pLine
;
1250 m_linesTail
->SetNext(pLine
);
1251 pLine
->SetPrev(m_linesTail
);
1254 m_linesTail
= pLine
;
1256 wxLogTrace( FILECONF_TRACE_MASK
,
1258 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1259 wxLogTrace( FILECONF_TRACE_MASK
,
1261 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1266 // insert a new line after the given one or in the very beginning if !pLine
1267 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1268 wxFileConfigLineList
*pLine
)
1270 wxLogTrace( FILECONF_TRACE_MASK
,
1271 _T(" ** Inserting Line '%s' after '%s'"),
1273 ((pLine
) ? pLine
->Text().c_str() : wxEmptyString
) );
1274 wxLogTrace( FILECONF_TRACE_MASK
,
1276 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1277 wxLogTrace( FILECONF_TRACE_MASK
,
1279 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1281 if ( pLine
== m_linesTail
)
1282 return LineListAppend(str
);
1284 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1285 if ( pLine
== NULL
)
1287 // prepend to the list
1288 pNewLine
->SetNext(m_linesHead
);
1289 m_linesHead
->SetPrev(pNewLine
);
1290 m_linesHead
= pNewLine
;
1294 // insert before pLine
1295 wxFileConfigLineList
*pNext
= pLine
->Next();
1296 pNewLine
->SetNext(pNext
);
1297 pNewLine
->SetPrev(pLine
);
1298 pNext
->SetPrev(pNewLine
);
1299 pLine
->SetNext(pNewLine
);
1302 wxLogTrace( FILECONF_TRACE_MASK
,
1304 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1305 wxLogTrace( FILECONF_TRACE_MASK
,
1307 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1312 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1314 wxLogTrace( FILECONF_TRACE_MASK
,
1315 _T(" ** Removing Line '%s'"),
1316 pLine
->Text().c_str() );
1317 wxLogTrace( FILECONF_TRACE_MASK
,
1319 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1320 wxLogTrace( FILECONF_TRACE_MASK
,
1322 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1324 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1325 *pNext
= pLine
->Next();
1329 if ( pPrev
== NULL
)
1330 m_linesHead
= pNext
;
1332 pPrev
->SetNext(pNext
);
1336 if ( pNext
== NULL
)
1337 m_linesTail
= pPrev
;
1339 pNext
->SetPrev(pPrev
);
1341 if ( m_pRootGroup
->GetGroupLine() == pLine
)
1342 m_pRootGroup
->SetLine(m_linesHead
);
1344 wxLogTrace( FILECONF_TRACE_MASK
,
1346 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1347 wxLogTrace( FILECONF_TRACE_MASK
,
1349 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1354 bool wxFileConfig::LineListIsEmpty()
1356 return m_linesHead
== NULL
;
1359 // ============================================================================
1360 // wxFileConfig::wxFileConfigGroup
1361 // ============================================================================
1363 // ----------------------------------------------------------------------------
1365 // ----------------------------------------------------------------------------
1368 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1369 const wxString
& strName
,
1370 wxFileConfig
*pConfig
)
1371 : m_aEntries(CompareEntries
),
1372 m_aSubgroups(CompareGroups
),
1375 m_pConfig
= pConfig
;
1376 m_pParent
= pParent
;
1379 m_pLastEntry
= NULL
;
1380 m_pLastGroup
= NULL
;
1383 // dtor deletes all children
1384 wxFileConfigGroup::~wxFileConfigGroup()
1387 size_t n
, nCount
= m_aEntries
.Count();
1388 for ( n
= 0; n
< nCount
; n
++ )
1389 delete m_aEntries
[n
];
1392 nCount
= m_aSubgroups
.Count();
1393 for ( n
= 0; n
< nCount
; n
++ )
1394 delete m_aSubgroups
[n
];
1397 // ----------------------------------------------------------------------------
1399 // ----------------------------------------------------------------------------
1401 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1403 // for a normal (i.e. not root) group this method shouldn't be called twice
1404 // unless we are resetting the line
1405 wxASSERT_MSG( !m_pParent
|| !m_pLine
|| !pLine
,
1406 _T("changing line for a non-root group?") );
1412 This is a bit complicated, so let me explain it in details. All lines that
1413 were read from the local file (the only one we will ever modify) are stored
1414 in a (doubly) linked list. Our problem is to know at which position in this
1415 list should we insert the new entries/subgroups. To solve it we keep three
1416 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1418 m_pLine points to the line containing "[group_name]"
1419 m_pLastEntry points to the last entry of this group in the local file.
1420 m_pLastGroup subgroup
1422 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1423 from the local file, the corresponding variable is set. However, if the group
1424 was read from the global file and then modified or created by the application
1425 these variables are still NULL and we need to create the corresponding lines.
1426 See the following functions (and comments preceding them) for the details of
1429 Also, when our last entry/group are deleted we need to find the new last
1430 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1431 of lines until it either founds an entry/subgroup (and this is the new last
1432 element) or the m_pLine of the group, in which case there are no more entries
1433 (or subgroups) left and m_pLast<element> becomes NULL.
1435 NB: This last problem could be avoided for entries if we added new entries
1436 immediately after m_pLine, but in this case the entries would appear
1437 backwards in the config file (OTOH, it's not that important) and as we
1438 would still need to do it for the subgroups the code wouldn't have been
1439 significantly less complicated.
1442 // Return the line which contains "[our name]". If we're still not in the list,
1443 // add our line to it immediately after the last line of our parent group if we
1444 // have it or in the very beginning if we're the root group.
1445 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1447 wxLogTrace( FILECONF_TRACE_MASK
,
1448 _T(" GetGroupLine() for Group '%s'"),
1453 wxLogTrace( FILECONF_TRACE_MASK
,
1454 _T(" Getting Line item pointer") );
1456 wxFileConfigGroup
*pParent
= Parent();
1458 // this group wasn't present in local config file, add it now
1461 wxLogTrace( FILECONF_TRACE_MASK
,
1462 _T(" checking parent '%s'"),
1463 pParent
->Name().c_str() );
1465 wxString strFullName
;
1467 // add 1 to the name because we don't want to start with '/'
1468 strFullName
<< wxT("[")
1469 << FilterOutEntryName(GetFullName().c_str() + 1)
1471 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1472 pParent
->GetLastGroupLine());
1473 pParent
->SetLastGroup(this); // we're surely after all the others
1475 //else: this is the root group and so we return NULL because we don't
1476 // have any group line
1482 // Return the last line belonging to the subgroups of this group (after which
1483 // we can add a new subgroup), if we don't have any subgroups or entries our
1484 // last line is the group line (m_pLine) itself.
1485 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1487 // if we have any subgroups, our last line is the last line of the last
1491 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1493 wxASSERT_MSG( pLine
, _T("last group must have !NULL associated line") );
1498 // no subgroups, so the last line is the line of thelast entry (if any)
1499 return GetLastEntryLine();
1502 // return the last line belonging to the entries of this group (after which
1503 // we can add a new entry), if we don't have any entries we will add the new
1504 // one immediately after the group line itself.
1505 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1507 wxLogTrace( FILECONF_TRACE_MASK
,
1508 _T(" GetLastEntryLine() for Group '%s'"),
1513 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1515 wxASSERT_MSG( pLine
, _T("last entry must have !NULL associated line") );
1520 // no entries: insert after the group header, if any
1521 return GetGroupLine();
1524 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry
*pEntry
)
1526 m_pLastEntry
= pEntry
;
1530 // the only situation in which a group without its own line can have
1531 // an entry is when the first entry is added to the initially empty
1532 // root pseudo-group
1533 wxASSERT_MSG( !m_pParent
, _T("unexpected for non root group") );
1535 // let the group know that it does have a line in the file now
1536 m_pLine
= pEntry
->GetLine();
1540 // ----------------------------------------------------------------------------
1542 // ----------------------------------------------------------------------------
1544 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
1546 // update the line of this group
1547 wxFileConfigLineList
*line
= GetGroupLine();
1548 wxCHECK_RET( line
, _T("a non root group must have a corresponding line!") );
1550 // +1: skip the leading '/'
1551 line
->SetText(wxString::Format(_T("[%s]"), GetFullName().c_str() + 1));
1554 // also update all subgroups as they have this groups name in their lines
1555 const size_t nCount
= m_aSubgroups
.Count();
1556 for ( size_t n
= 0; n
< nCount
; n
++ )
1558 m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines();
1562 void wxFileConfigGroup::Rename(const wxString
& newName
)
1564 wxCHECK_RET( m_pParent
, _T("the root group can't be renamed") );
1566 if ( newName
== m_strName
)
1569 // we need to remove the group from the parent and it back under the new
1570 // name to keep the parents array of subgroups alphabetically sorted
1571 m_pParent
->m_aSubgroups
.Remove(this);
1573 m_strName
= newName
;
1575 m_pParent
->m_aSubgroups
.Add(this);
1577 // update the group lines recursively
1578 UpdateGroupAndSubgroupsLines();
1581 wxString
wxFileConfigGroup::GetFullName() const
1585 fullname
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1590 // ----------------------------------------------------------------------------
1592 // ----------------------------------------------------------------------------
1594 // use binary search because the array is sorted
1596 wxFileConfigGroup::FindEntry(const wxChar
*szName
) const
1600 hi
= m_aEntries
.Count();
1602 wxFileConfigEntry
*pEntry
;
1606 pEntry
= m_aEntries
[i
];
1608 #if wxCONFIG_CASE_SENSITIVE
1609 res
= wxStrcmp(pEntry
->Name(), szName
);
1611 res
= wxStricmp(pEntry
->Name(), szName
);
1626 wxFileConfigGroup::FindSubgroup(const wxChar
*szName
) const
1630 hi
= m_aSubgroups
.Count();
1632 wxFileConfigGroup
*pGroup
;
1636 pGroup
= m_aSubgroups
[i
];
1638 #if wxCONFIG_CASE_SENSITIVE
1639 res
= wxStrcmp(pGroup
->Name(), szName
);
1641 res
= wxStricmp(pGroup
->Name(), szName
);
1655 // ----------------------------------------------------------------------------
1656 // create a new item
1657 // ----------------------------------------------------------------------------
1659 // create a new entry and add it to the current group
1660 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1662 wxASSERT( FindEntry(strName
) == 0 );
1664 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1666 m_aEntries
.Add(pEntry
);
1670 // create a new group and add it to the current group
1671 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1673 wxASSERT( FindSubgroup(strName
) == 0 );
1675 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1677 m_aSubgroups
.Add(pGroup
);
1681 // ----------------------------------------------------------------------------
1683 // ----------------------------------------------------------------------------
1686 The delete operations are _very_ slow if we delete the last item of this
1687 group (see comments before GetXXXLineXXX functions for more details),
1688 so it's much better to start with the first entry/group if we want to
1689 delete several of them.
1692 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar
*szName
)
1694 wxFileConfigGroup
* const pGroup
= FindSubgroup(szName
);
1696 return pGroup
? DeleteSubgroup(pGroup
) : false;
1699 // Delete the subgroup and remove all references to it from
1700 // other data structures.
1701 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1703 wxCHECK_MSG( pGroup
, false, _T("deleting non existing group?") );
1705 wxLogTrace( FILECONF_TRACE_MASK
,
1706 _T("Deleting group '%s' from '%s'"),
1707 pGroup
->Name().c_str(),
1710 wxLogTrace( FILECONF_TRACE_MASK
,
1711 _T(" (m_pLine) = prev: %p, this %p, next %p"),
1712 m_pLine
? wx_static_cast(void*, m_pLine
->Prev()) : 0,
1713 wx_static_cast(void*, m_pLine
),
1714 m_pLine
? wx_static_cast(void*, m_pLine
->Next()) : 0 );
1715 wxLogTrace( FILECONF_TRACE_MASK
,
1717 m_pLine
? m_pLine
->Text().c_str() : wxEmptyString
);
1719 // delete all entries...
1720 size_t nCount
= pGroup
->m_aEntries
.Count();
1722 wxLogTrace(FILECONF_TRACE_MASK
,
1723 _T("Removing %lu entries"), (unsigned long)nCount
);
1725 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1727 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1731 wxLogTrace( FILECONF_TRACE_MASK
,
1733 pLine
->Text().c_str() );
1734 m_pConfig
->LineListRemove(pLine
);
1738 // ...and subgroups of this subgroup
1739 nCount
= pGroup
->m_aSubgroups
.Count();
1741 wxLogTrace( FILECONF_TRACE_MASK
,
1742 _T("Removing %lu subgroups"), (unsigned long)nCount
);
1744 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1746 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1749 // and then finally the group itself
1750 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1753 wxLogTrace( FILECONF_TRACE_MASK
,
1754 _T(" Removing line for group '%s' : '%s'"),
1755 pGroup
->Name().c_str(),
1756 pLine
->Text().c_str() );
1757 wxLogTrace( FILECONF_TRACE_MASK
,
1758 _T(" Removing from group '%s' : '%s'"),
1760 ((m_pLine
) ? m_pLine
->Text().c_str() : wxEmptyString
) );
1762 // notice that we may do this test inside the previous "if"
1763 // because the last entry's line is surely !NULL
1764 if ( pGroup
== m_pLastGroup
)
1766 wxLogTrace( FILECONF_TRACE_MASK
,
1767 _T(" Removing last group") );
1769 // our last entry is being deleted, so find the last one which
1770 // stays by going back until we find a subgroup or reach the
1772 const size_t nSubgroups
= m_aSubgroups
.Count();
1774 m_pLastGroup
= NULL
;
1775 for ( wxFileConfigLineList
*pl
= pLine
->Prev();
1776 pl
&& pl
!= m_pLine
&& !m_pLastGroup
;
1779 // does this line belong to our subgroup?
1780 for ( size_t n
= 0; n
< nSubgroups
; n
++ )
1782 // do _not_ call GetGroupLine! we don't want to add it to
1783 // the local file if it's not already there
1784 if ( m_aSubgroups
[n
]->m_pLine
== pl
)
1786 m_pLastGroup
= m_aSubgroups
[n
];
1793 m_pConfig
->LineListRemove(pLine
);
1797 wxLogTrace( FILECONF_TRACE_MASK
,
1798 _T(" No line entry for Group '%s'?"),
1799 pGroup
->Name().c_str() );
1802 m_aSubgroups
.Remove(pGroup
);
1808 bool wxFileConfigGroup::DeleteEntry(const wxChar
*szName
)
1810 wxFileConfigEntry
*pEntry
= FindEntry(szName
);
1813 // entry doesn't exist, nothing to do
1817 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1818 if ( pLine
!= NULL
) {
1819 // notice that we may do this test inside the previous "if" because the
1820 // last entry's line is surely !NULL
1821 if ( pEntry
== m_pLastEntry
) {
1822 // our last entry is being deleted - find the last one which stays
1823 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1825 // go back until we find another entry or reach the group's line
1826 wxFileConfigEntry
*pNewLast
= NULL
;
1827 size_t n
, nEntries
= m_aEntries
.Count();
1828 wxFileConfigLineList
*pl
;
1829 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1830 // is it our subgroup?
1831 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1832 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1833 pNewLast
= m_aEntries
[n
];
1836 if ( pNewLast
!= NULL
) // found?
1840 if ( pl
== m_pLine
) {
1841 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1843 // we've reached the group line without finding any subgroups
1844 m_pLastEntry
= NULL
;
1847 m_pLastEntry
= pNewLast
;
1850 m_pConfig
->LineListRemove(pLine
);
1853 m_aEntries
.Remove(pEntry
);
1859 // ============================================================================
1860 // wxFileConfig::wxFileConfigEntry
1861 // ============================================================================
1863 // ----------------------------------------------------------------------------
1865 // ----------------------------------------------------------------------------
1866 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1867 const wxString
& strName
,
1869 : m_strName(strName
)
1871 wxASSERT( !strName
.empty() );
1873 m_pParent
= pParent
;
1877 m_bHasValue
= false;
1879 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1881 m_strName
.erase(0, 1); // remove first character
1884 // ----------------------------------------------------------------------------
1886 // ----------------------------------------------------------------------------
1888 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1890 if ( m_pLine
!= NULL
) {
1891 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1892 Name().c_str(), m_pParent
->GetFullName().c_str());
1896 Group()->SetLastEntry(this);
1899 // second parameter is false if we read the value from file and prevents the
1900 // entry from being marked as 'dirty'
1901 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1903 if ( bUser
&& IsImmutable() )
1905 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1910 // do nothing if it's the same value: but don't test for it if m_bHasValue
1911 // hadn't been set yet or we'd never write empty values to the file
1912 if ( m_bHasValue
&& strValue
== m_strValue
)
1916 m_strValue
= strValue
;
1920 wxString strValFiltered
;
1922 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1924 strValFiltered
= strValue
;
1927 strValFiltered
= FilterOutValue(strValue
);
1931 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1935 // entry was read from the local config file, just modify the line
1936 m_pLine
->SetText(strLine
);
1938 else // this entry didn't exist in the local file
1940 // add a new line to the file: note that line returned by
1941 // GetLastEntryLine() may be NULL if we're in the root group and it
1942 // doesn't have any entries yet, but this is ok as passing NULL
1943 // line to LineListInsert() means to prepend new line to the list
1944 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1945 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1947 Group()->SetLastEntry(this);
1952 // ============================================================================
1954 // ============================================================================
1956 // ----------------------------------------------------------------------------
1957 // compare functions for array sorting
1958 // ----------------------------------------------------------------------------
1960 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1962 #if wxCONFIG_CASE_SENSITIVE
1963 return wxStrcmp(p1
->Name(), p2
->Name());
1965 return wxStricmp(p1
->Name(), p2
->Name());
1969 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1971 #if wxCONFIG_CASE_SENSITIVE
1972 return wxStrcmp(p1
->Name(), p2
->Name());
1974 return wxStricmp(p1
->Name(), p2
->Name());
1978 // ----------------------------------------------------------------------------
1980 // ----------------------------------------------------------------------------
1982 // undo FilterOutValue
1983 static wxString
FilterInValue(const wxString
& str
)
1986 strResult
.Alloc(str
.Len());
1988 bool bQuoted
= !str
.empty() && str
[0] == '"';
1990 for ( size_t n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1991 if ( str
[n
] == wxT('\\') ) {
1992 switch ( str
[++n
] ) {
1994 strResult
+= wxT('\n');
1998 strResult
+= wxT('\r');
2002 strResult
+= wxT('\t');
2006 strResult
+= wxT('\\');
2010 strResult
+= wxT('"');
2015 if ( str
[n
] != wxT('"') || !bQuoted
)
2016 strResult
+= str
[n
];
2017 else if ( n
!= str
.Len() - 1 ) {
2018 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2021 //else: it's the last quote of a quoted string, ok
2028 // quote the string before writing it to file
2029 static wxString
FilterOutValue(const wxString
& str
)
2035 strResult
.Alloc(str
.Len());
2037 // quoting is necessary to preserve spaces in the beginning of the string
2038 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2041 strResult
+= wxT('"');
2044 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2067 //else: fall through
2070 strResult
+= str
[n
];
2071 continue; // nothing special to do
2074 // we get here only for special characters
2075 strResult
<< wxT('\\') << c
;
2079 strResult
+= wxT('"');
2084 // undo FilterOutEntryName
2085 static wxString
FilterInEntryName(const wxString
& str
)
2088 strResult
.Alloc(str
.Len());
2090 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2091 if ( *pc
== wxT('\\') ) {
2092 // we need to test it here or we'd skip past the NUL in the loop line
2093 if ( *++pc
== _T('\0') )
2103 // sanitize entry or group name: insert '\\' before any special characters
2104 static wxString
FilterOutEntryName(const wxString
& str
)
2107 strResult
.Alloc(str
.Len());
2109 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2110 const wxChar c
= *pc
;
2112 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2113 // which will probably never have special meaning and with which we can't
2114 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2116 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2117 // should *not* be quoted
2120 ((unsigned char)c
< 127) &&
2122 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) )
2124 strResult
+= wxT('\\');
2133 // we can't put ?: in the ctor initializer list because it confuses some
2134 // broken compilers (Borland C++)
2135 static wxString
GetAppName(const wxString
& appName
)
2137 if ( !appName
&& wxTheApp
)
2138 return wxTheApp
->GetAppName();
2143 #endif // wxUSE_CONFIG