1 ///////////////////////////////////////////////////////////////////////////////
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 license
11 ///////////////////////////////////////////////////////////////////////////////
14 #pragma implementation "fileconf.h"
17 // ----------------------------------------------------------------------------
19 // ----------------------------------------------------------------------------
21 #include "wx/wxprec.h"
30 #include "wx/string.h"
35 #include "wx/dynarray.h"
38 #include "wx/textfile.h"
39 #include "wx/memtext.h"
40 #include "wx/config.h"
41 #include "wx/fileconf.h"
44 #include "wx/stream.h"
45 #endif // wxUSE_STREAMS
47 #include "wx/utils.h" // for wxGetHomeDir
49 #if defined(__WXMAC__)
50 #include "wx/mac/private.h" // includes mac headers
53 #if defined(__WXMSW__)
54 #include "wx/msw/private.h"
64 // headers needed for umask()
66 #include <sys/types.h>
70 // ----------------------------------------------------------------------------
72 // ----------------------------------------------------------------------------
73 #define CONST_CAST ((wxFileConfig *)this)->
75 // ----------------------------------------------------------------------------
77 // ----------------------------------------------------------------------------
83 // ----------------------------------------------------------------------------
84 // global functions declarations
85 // ----------------------------------------------------------------------------
87 // compare functions for sorting the arrays
88 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
89 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
92 static wxString
FilterInValue(const wxString
& str
);
93 static wxString
FilterOutValue(const wxString
& str
);
95 static wxString
FilterInEntryName(const wxString
& str
);
96 static wxString
FilterOutEntryName(const wxString
& str
);
98 // get the name to use in wxFileConfig ctor
99 static wxString
GetAppName(const wxString
& appname
);
101 // ============================================================================
103 // ============================================================================
105 // ----------------------------------------------------------------------------
106 // "template" array types
107 // ----------------------------------------------------------------------------
109 WX_DEFINE_SORTED_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
110 WX_DEFINE_SORTED_EXPORTED_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_bDirty
:1, // changed since last read?
156 m_bImmutable
:1, // can be overriden locally?
157 m_bHasValue
:1; // set after first call to SetValue()
159 int m_nLine
; // used if m_pLine == NULL only
161 // pointer to our line in the linked list or NULL if it was found in global
162 // file (which we don't modify)
163 wxFileConfigLineList
*m_pLine
;
166 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
167 const wxString
& strName
, int nLine
);
170 const wxString
& Name() const { return m_strName
; }
171 const wxString
& Value() const { return m_strValue
; }
172 wxFileConfigGroup
*Group() const { return m_pParent
; }
173 bool IsDirty() const { return m_bDirty
; }
174 bool IsImmutable() const { return m_bImmutable
; }
175 bool IsLocal() const { return m_pLine
!= 0; }
176 int Line() const { return m_nLine
; }
177 wxFileConfigLineList
*
178 GetLine() const { return m_pLine
; }
180 // modify entry attributes
181 void SetValue(const wxString
& strValue
, bool bUser
= TRUE
);
183 void SetLine(wxFileConfigLineList
*pLine
);
185 DECLARE_NO_COPY_CLASS(wxFileConfigEntry
)
188 // ----------------------------------------------------------------------------
189 // wxFileConfigGroup: container of entries and other groups
190 // ----------------------------------------------------------------------------
192 class wxFileConfigGroup
195 wxFileConfig
*m_pConfig
; // config object we belong to
196 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
197 ArrayEntries m_aEntries
; // entries in this group
198 ArrayGroups m_aSubgroups
; // subgroups
199 wxString m_strName
; // group's name
200 bool m_bDirty
; // if FALSE => all subgroups are not dirty
201 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
202 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
203 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
205 // DeleteSubgroupByName helper
206 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
210 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
212 // dtor deletes all entries and subgroups also
213 ~wxFileConfigGroup();
216 const wxString
& Name() const { return m_strName
; }
217 wxFileConfigGroup
*Parent() const { return m_pParent
; }
218 wxFileConfig
*Config() const { return m_pConfig
; }
219 bool IsDirty() const { return m_bDirty
; }
221 const ArrayEntries
& Entries() const { return m_aEntries
; }
222 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
223 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
225 // find entry/subgroup (NULL if not found)
226 wxFileConfigGroup
*FindSubgroup(const wxChar
*szName
) const;
227 wxFileConfigEntry
*FindEntry (const wxChar
*szName
) const;
229 // delete entry/subgroup, return FALSE if doesn't exist
230 bool DeleteSubgroupByName(const wxChar
*szName
);
231 bool DeleteEntry(const wxChar
*szName
);
233 // create new entry/subgroup returning pointer to newly created element
234 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
235 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
237 // will also recursively set parent's dirty flag
239 void SetLine(wxFileConfigLineList
*pLine
);
241 // rename: no checks are done to ensure that the name is unique!
242 void Rename(const wxString
& newName
);
245 wxString
GetFullName() const;
247 // get the last line belonging to an entry/subgroup of this group
248 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
249 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
250 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
252 // called by entries/subgroups when they're created/deleted
253 void SetLastEntry(wxFileConfigEntry
*pEntry
) { m_pLastEntry
= pEntry
; }
254 void SetLastGroup(wxFileConfigGroup
*pGroup
) { m_pLastGroup
= pGroup
; }
256 DECLARE_NO_COPY_CLASS(wxFileConfigGroup
)
259 // ============================================================================
261 // ============================================================================
263 // ----------------------------------------------------------------------------
265 // ----------------------------------------------------------------------------
266 wxString
wxFileConfig::GetGlobalDir()
270 #ifdef __VMS__ // Note if __VMS is defined __UNIX is also defined
271 strDir
= wxT("sys$manager:");
272 #elif defined(__WXMAC__)
273 strDir
= wxMacFindFolder( (short) kOnSystemDisk
, kPreferencesFolderType
, kDontCreateFolder
) ;
274 #elif defined( __UNIX__ )
275 strDir
= wxT("/etc/");
276 #elif defined(__WXPM__)
277 ULONG aulSysInfo
[QSV_MAX
] = {0};
281 rc
= DosQuerySysInfo( 1L, QSV_MAX
, (PVOID
)aulSysInfo
, sizeof(ULONG
)*QSV_MAX
);
284 drive
= aulSysInfo
[QSV_BOOT_DRIVE
- 1];
285 strDir
.Printf(wxT("%c:\\OS2\\"), 'A'+drive
-1);
287 #elif defined(__WXSTUBS__)
288 wxASSERT_MSG( FALSE
, wxT("TODO") ) ;
289 #elif defined(__DOS__)
290 // There's no such thing as global cfg dir in MS-DOS, let's return
291 // current directory (FIXME_MGL?)
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 return GetGlobalDir() ;
312 wxGetHomeDir(&strDir
);
316 if (strDir
.Last() != wxT(']'))
318 if (strDir
.Last() != wxT('/')) strDir
<< wxT('/');
320 if (strDir
.Last() != wxT('\\')) strDir
<< wxT('\\');
327 wxString
wxFileConfig::GetGlobalFileName(const wxChar
*szFile
)
329 wxString str
= GetGlobalDir();
332 if ( wxStrchr(szFile
, wxT('.')) == NULL
)
333 #if defined( __WXMAC__ )
334 str
<< " Preferences";
335 #elif defined( __UNIX__ )
344 wxString
wxFileConfig::GetLocalFileName(const wxChar
*szFile
)
347 // On VMS I saw the problem that the home directory was appended
348 // twice for the configuration file. Does that also happen for
350 wxString str
= wxT( '.' );
352 wxString str
= GetLocalDir();
355 #if defined( __UNIX__ ) && !defined( __VMS ) && !defined( __WXMAC__ )
361 #if defined(__WINDOWS__) || defined(__DOS__)
362 if ( wxStrchr(szFile
, wxT('.')) == NULL
)
367 str
<< " Preferences";
373 // ----------------------------------------------------------------------------
375 // ----------------------------------------------------------------------------
377 void wxFileConfig::Init()
380 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxT(""), this);
385 // It's not an error if (one of the) file(s) doesn't exist.
387 // parse the global file
388 if ( !m_strGlobalFile
.IsEmpty() && wxFile::Exists(m_strGlobalFile
) )
390 wxTextFile
fileGlobal(m_strGlobalFile
);
392 #if defined(__WXGTK20__) && wxUSE_UNICODE
393 if ( fileGlobal
.Open( wxConvUTF8
) )
395 if ( fileGlobal
.Open() )
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
.IsEmpty() && wxFile::Exists(m_strLocalFile
) )
410 wxTextFile
fileLocal(m_strLocalFile
);
411 #if defined(__WXGTK20__) && wxUSE_UNICODE
412 if ( fileLocal
.Open( wxConvUTF8
) )
414 if ( fileLocal
.Open() )
417 Parse(fileLocal
, TRUE
/* local */);
422 wxLogWarning(_("can't open user configuration file '%s'."), m_strLocalFile
.c_str() );
427 // constructor supports creation of wxFileConfig objects of any type
428 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
429 const wxString
& strLocal
, const wxString
& strGlobal
,
431 : wxConfigBase(::GetAppName(appName
), vendorName
,
434 m_strLocalFile(strLocal
), m_strGlobalFile(strGlobal
)
436 // Make up names for files if empty
437 if ( m_strLocalFile
.IsEmpty() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
438 m_strLocalFile
= GetLocalFileName(GetAppName());
440 if ( m_strGlobalFile
.IsEmpty() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
441 m_strGlobalFile
= GetGlobalFileName(GetAppName());
443 // Check if styles are not supplied, but filenames are, in which case
444 // add the correct styles.
445 if ( !m_strLocalFile
.IsEmpty() )
446 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
448 if ( !m_strGlobalFile
.IsEmpty() )
449 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
451 // if the path is not absolute, prepend the standard directory to it
452 // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
453 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
455 if ( !m_strLocalFile
.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile
) )
457 wxString strLocal
= m_strLocalFile
;
458 m_strLocalFile
= GetLocalDir();
459 m_strLocalFile
<< strLocal
;
462 if ( !m_strGlobalFile
.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile
) )
464 wxString strGlobal
= m_strGlobalFile
;
465 m_strGlobalFile
= GetGlobalDir();
466 m_strGlobalFile
<< strGlobal
;
477 wxFileConfig::wxFileConfig(wxInputStream
&inStream
)
479 // always local_file when this constructor is called (?)
480 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
483 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxT(""), this);
488 // translate everything to the current (platform-dependent) line
489 // termination character
495 while ( !inStream
.Read(buf
, WXSIZEOF(buf
)).Eof() )
496 strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead());
498 strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead());
500 strTrans
= wxTextBuffer::Translate(strTmp
);
503 wxMemoryText memText
;
505 // Now we can add the text to the memory text. To do this we extract line
506 // by line from the translated string, until we've reached the end.
508 // VZ: all this is horribly inefficient, we should do the translation on
509 // the fly in one pass saving both memory and time (TODO)
511 const wxChar
*pEOL
= wxTextBuffer::GetEOL(wxTextBuffer::typeDefault
);
512 const size_t EOLLen
= wxStrlen(pEOL
);
514 int posLineStart
= strTrans
.Find(pEOL
);
515 while ( posLineStart
!= -1 )
517 wxString
line(strTrans
.Left(posLineStart
));
519 memText
.AddLine(line
);
521 strTrans
= strTrans
.Mid(posLineStart
+ EOLLen
);
523 posLineStart
= strTrans
.Find(pEOL
);
526 // also add whatever we have left in the translated string.
527 memText
.AddLine(strTrans
);
529 // Finally we can parse it all.
530 Parse(memText
, TRUE
/* local */);
535 #endif // wxUSE_STREAMS
537 void wxFileConfig::CleanUp()
541 wxFileConfigLineList
*pCur
= m_linesHead
;
542 while ( pCur
!= NULL
) {
543 wxFileConfigLineList
*pNext
= pCur
->Next();
549 wxFileConfig::~wxFileConfig()
556 // ----------------------------------------------------------------------------
557 // parse a config file
558 // ----------------------------------------------------------------------------
560 void wxFileConfig::Parse(wxTextBuffer
& buffer
, bool bLocal
)
562 const wxChar
*pStart
;
566 size_t nLineCount
= buffer
.GetLineCount();
568 for ( size_t n
= 0; n
< nLineCount
; n
++ )
572 // add the line to linked list
574 LineListAppend(strLine
);
576 // skip leading spaces
577 for ( pStart
= strLine
; wxIsspace(*pStart
); pStart
++ )
580 // skip blank/comment lines
581 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
584 if ( *pStart
== wxT('[') ) { // a new group
587 while ( *++pEnd
!= wxT(']') ) {
588 if ( *pEnd
== wxT('\\') ) {
589 // the next char is escaped, so skip it even if it is ']'
593 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
594 // we reached the end of line, break out of the loop
599 if ( *pEnd
!= wxT(']') ) {
600 wxLogError(_("file '%s': unexpected character %c at line %d."),
601 buffer
.GetName(), *pEnd
, n
+ 1);
602 continue; // skip this line
605 // group name here is always considered as abs path
608 strGroup
<< wxCONFIG_PATH_SEPARATOR
609 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
611 // will create it if doesn't yet exist
615 m_pCurrentGroup
->SetLine(m_linesTail
);
617 // check that there is nothing except comments left on this line
619 while ( *++pEnd
!= wxT('\0') && bCont
) {
628 // ignore whitespace ('\n' impossible here)
632 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
633 buffer
.GetName(), n
+ 1, pEnd
);
639 const wxChar
*pEnd
= pStart
;
640 while ( *pEnd
&& *pEnd
!= wxT('=') && !wxIsspace(*pEnd
) ) {
641 if ( *pEnd
== wxT('\\') ) {
642 // next character may be space or not - still take it because it's
643 // quoted (unless there is nothing)
646 // the error message will be given below anyhow
654 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
)));
657 while ( wxIsspace(*pEnd
) )
660 if ( *pEnd
++ != wxT('=') ) {
661 wxLogError(_("file '%s', line %d: '=' expected."),
662 buffer
.GetName(), n
+ 1);
665 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
667 if ( pEntry
== NULL
) {
669 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
672 pEntry
->SetLine(m_linesTail
);
675 if ( bLocal
&& pEntry
->IsImmutable() ) {
676 // immutable keys can't be changed by user
677 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
678 buffer
.GetName(), n
+ 1, strKey
.c_str());
681 // the condition below catches the cases (a) and (b) but not (c):
682 // (a) global key found second time in global file
683 // (b) key found second (or more) time in local file
684 // (c) key from global file now found in local one
685 // which is exactly what we want.
686 else if ( !bLocal
|| pEntry
->IsLocal() ) {
687 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
688 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
691 pEntry
->SetLine(m_linesTail
);
696 while ( wxIsspace(*pEnd
) )
699 wxString value
= pEnd
;
700 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
701 value
= FilterInValue(value
);
703 pEntry
->SetValue(value
, FALSE
);
709 // ----------------------------------------------------------------------------
711 // ----------------------------------------------------------------------------
713 void wxFileConfig::SetRootPath()
716 m_pCurrentGroup
= m_pRootGroup
;
719 void wxFileConfig::SetPath(const wxString
& strPath
)
721 wxArrayString aParts
;
723 if ( strPath
.IsEmpty() ) {
728 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
730 wxSplitPath(aParts
, strPath
);
733 // relative path, combine with current one
734 wxString strFullPath
= m_strPath
;
735 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
736 wxSplitPath(aParts
, strFullPath
);
739 // change current group
741 m_pCurrentGroup
= m_pRootGroup
;
742 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
743 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
744 if ( pNextGroup
== NULL
)
745 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
746 m_pCurrentGroup
= pNextGroup
;
749 // recombine path parts in one variable
751 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
752 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
756 // ----------------------------------------------------------------------------
758 // ----------------------------------------------------------------------------
760 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
763 return GetNextGroup(str
, lIndex
);
766 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
768 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().Count() ) {
769 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
776 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
779 return GetNextEntry(str
, lIndex
);
782 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
784 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().Count() ) {
785 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
792 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
794 size_t n
= m_pCurrentGroup
->Entries().Count();
796 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
797 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
798 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
799 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
800 n
+= GetNumberOfEntries(TRUE
);
801 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
808 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
810 size_t n
= m_pCurrentGroup
->Groups().Count();
812 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
813 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
814 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
815 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
816 n
+= GetNumberOfGroups(TRUE
);
817 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
824 // ----------------------------------------------------------------------------
825 // tests for existence
826 // ----------------------------------------------------------------------------
828 bool wxFileConfig::HasGroup(const wxString
& strName
) const
830 wxConfigPathChanger
path(this, strName
);
832 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
->FindSubgroup(path
.Name());
833 return pGroup
!= NULL
;
836 bool wxFileConfig::HasEntry(const wxString
& strName
) const
838 wxConfigPathChanger
path(this, strName
);
840 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
841 return pEntry
!= NULL
;
844 // ----------------------------------------------------------------------------
846 // ----------------------------------------------------------------------------
848 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
850 wxConfigPathChanger
path(this, key
);
852 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
853 if (pEntry
== NULL
) {
857 *pStr
= pEntry
->Value();
862 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
865 if ( !Read(key
, & str
) )
869 return str
.ToLong(pl
) ;
872 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
874 wxConfigPathChanger
path(this, key
);
875 wxString strName
= path
.Name();
877 wxLogTrace( _T("wxFileConfig"),
878 _T(" Writing String '%s' = '%s' to Group '%s'"),
883 if ( strName
.IsEmpty() )
885 // setting the value of a group is an error
887 wxASSERT_MSG( wxIsEmpty(szValue
), wxT("can't set value of a group!") );
889 // ... except if it's empty in which case it's a way to force it's creation
891 wxLogTrace( _T("wxFileConfig"),
892 _T(" Creating group %s"),
893 m_pCurrentGroup
->Name().c_str() );
895 m_pCurrentGroup
->SetDirty();
897 // this will add a line for this group if it didn't have it before
899 (void)m_pCurrentGroup
->GetGroupLine();
904 // check that the name is reasonable
906 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
908 wxLogError( _("Config entry name cannot start with '%c'."),
909 wxCONFIG_IMMUTABLE_PREFIX
);
913 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
917 wxLogTrace( _T("wxFileConfig"),
918 _T(" Adding Entry %s"),
920 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
923 wxLogTrace( _T("wxFileConfig"),
924 _T(" Setting value %s"),
926 pEntry
->SetValue(szValue
);
932 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
934 return Write(key
, wxString::Format(_T("%ld"), lValue
));
937 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
939 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() || !m_strLocalFile
)
943 // set the umask if needed
947 umaskOld
= umask((mode_t
)m_umask
);
951 wxTempFile
file(m_strLocalFile
);
953 if ( !file
.IsOpened() )
955 wxLogError(_("can't open user configuration file."));
959 // write all strings to file
960 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
962 wxString line
= p
->Text();
963 line
+= wxTextFile::GetEOL();
965 wxCharBuffer buf
= wxConvLocal
.cWX2MB( line
);
966 if ( !file
.Write( (const char*)buf
, strlen( (const char*) buf
) ) )
968 if ( !file
.Write( line
.c_str(), line
.Len() ) )
971 wxLogError(_("can't write user configuration file."));
976 bool ret
= file
.Commit();
978 #if defined(__WXMAC__)
983 wxMacFilename2FSSpec( m_strLocalFile
, &spec
) ;
985 if ( FSpGetFInfo( &spec
, &finfo
) == noErr
)
987 finfo
.fdType
= 'TEXT' ;
988 finfo
.fdCreator
= 'ttxt' ;
989 FSpSetFInfo( &spec
, &finfo
) ;
995 // restore the old umask if we changed it
998 (void)umask(umaskOld
);
1005 // ----------------------------------------------------------------------------
1006 // renaming groups/entries
1007 // ----------------------------------------------------------------------------
1009 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1010 const wxString
& newName
)
1012 // check that the entry exists
1013 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1017 // check that the new entry doesn't already exist
1018 if ( m_pCurrentGroup
->FindEntry(newName
) )
1021 // delete the old entry, create the new one
1022 wxString value
= oldEntry
->Value();
1023 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1026 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1027 newEntry
->SetValue(value
);
1032 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1033 const wxString
& newName
)
1035 // check that the group exists
1036 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1040 // check that the new group doesn't already exist
1041 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1044 group
->Rename(newName
);
1049 // ----------------------------------------------------------------------------
1050 // delete groups/entries
1051 // ----------------------------------------------------------------------------
1053 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1055 wxConfigPathChanger
path(this, key
);
1057 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1060 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1061 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1062 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1063 SetPath(wxT("..")); // changes m_pCurrentGroup!
1064 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1066 //else: never delete the root group
1072 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1074 wxConfigPathChanger
path(this, key
);
1076 return m_pCurrentGroup
->DeleteSubgroupByName(path
.Name());
1079 bool wxFileConfig::DeleteAll()
1083 if ( wxRemove(m_strLocalFile
) == -1 )
1084 wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile
.c_str());
1086 m_strLocalFile
= m_strGlobalFile
= wxT("");
1092 // ----------------------------------------------------------------------------
1093 // linked list functions
1094 // ----------------------------------------------------------------------------
1096 // append a new line to the end of the list
1098 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1100 wxLogTrace( _T("wxFileConfig"),
1101 _T(" ** Adding Line '%s'"),
1103 wxLogTrace( _T("wxFileConfig"),
1105 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1106 wxLogTrace( _T("wxFileConfig"),
1108 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1110 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1112 if ( m_linesTail
== NULL
)
1115 m_linesHead
= pLine
;
1120 m_linesTail
->SetNext(pLine
);
1121 pLine
->SetPrev(m_linesTail
);
1124 m_linesTail
= pLine
;
1126 wxLogTrace( _T("wxFileConfig"),
1128 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1129 wxLogTrace( _T("wxFileConfig"),
1131 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1136 // insert a new line after the given one or in the very beginning if !pLine
1138 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1139 wxFileConfigLineList
*pLine
)
1141 wxLogTrace( _T("wxFileConfig"),
1142 _T(" ** Inserting Line '%s' after '%s'"),
1144 ((pLine
) ? pLine
->Text().c_str() : wxEmptyString
) );
1145 wxLogTrace( _T("wxFileConfig"),
1147 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1148 wxLogTrace( _T("wxFileConfig"),
1150 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1152 if ( pLine
== m_linesTail
)
1153 return LineListAppend(str
);
1155 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1156 if ( pLine
== NULL
)
1158 // prepend to the list
1159 pNewLine
->SetNext(m_linesHead
);
1160 m_linesHead
->SetPrev(pNewLine
);
1161 m_linesHead
= pNewLine
;
1165 // insert before pLine
1166 wxFileConfigLineList
*pNext
= pLine
->Next();
1167 pNewLine
->SetNext(pNext
);
1168 pNewLine
->SetPrev(pLine
);
1169 pNext
->SetPrev(pNewLine
);
1170 pLine
->SetNext(pNewLine
);
1173 wxLogTrace( _T("wxFileConfig"),
1175 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1176 wxLogTrace( _T("wxFileConfig"),
1178 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1183 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1185 wxLogTrace( _T("wxFileConfig"),
1186 _T(" ** Removing Line '%s'"),
1187 pLine
->Text().c_str() );
1188 wxLogTrace( _T("wxFileConfig"),
1190 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1191 wxLogTrace( _T("wxFileConfig"),
1193 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1195 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1196 *pNext
= pLine
->Next();
1200 if ( pPrev
== NULL
)
1201 m_linesHead
= pNext
;
1203 pPrev
->SetNext(pNext
);
1207 if ( pNext
== NULL
)
1208 m_linesTail
= pPrev
;
1210 pNext
->SetPrev(pPrev
);
1212 wxLogTrace( _T("wxFileConfig"),
1214 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1215 wxLogTrace( _T("wxFileConfig"),
1217 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1222 bool wxFileConfig::LineListIsEmpty()
1224 return m_linesHead
== NULL
;
1227 // ============================================================================
1228 // wxFileConfig::wxFileConfigGroup
1229 // ============================================================================
1231 // ----------------------------------------------------------------------------
1233 // ----------------------------------------------------------------------------
1236 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1237 const wxString
& strName
,
1238 wxFileConfig
*pConfig
)
1239 : m_aEntries(CompareEntries
),
1240 m_aSubgroups(CompareGroups
),
1243 m_pConfig
= pConfig
;
1244 m_pParent
= pParent
;
1248 m_pLastEntry
= NULL
;
1249 m_pLastGroup
= NULL
;
1252 // dtor deletes all children
1253 wxFileConfigGroup::~wxFileConfigGroup()
1256 size_t n
, nCount
= m_aEntries
.Count();
1257 for ( n
= 0; n
< nCount
; n
++ )
1258 delete m_aEntries
[n
];
1261 nCount
= m_aSubgroups
.Count();
1262 for ( n
= 0; n
< nCount
; n
++ )
1263 delete m_aSubgroups
[n
];
1266 // ----------------------------------------------------------------------------
1268 // ----------------------------------------------------------------------------
1270 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1272 wxASSERT( m_pLine
== 0 ); // shouldn't be called twice
1277 This is a bit complicated, so let me explain it in details. All lines that
1278 were read from the local file (the only one we will ever modify) are stored
1279 in a (doubly) linked list. Our problem is to know at which position in this
1280 list should we insert the new entries/subgroups. To solve it we keep three
1281 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1283 m_pLine points to the line containing "[group_name]"
1284 m_pLastEntry points to the last entry of this group in the local file.
1285 m_pLastGroup subgroup
1287 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1288 from the local file, the corresponding variable is set. However, if the group
1289 was read from the global file and then modified or created by the application
1290 these variables are still NULL and we need to create the corresponding lines.
1291 See the following functions (and comments preceding them) for the details of
1294 Also, when our last entry/group are deleted we need to find the new last
1295 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1296 of lines until it either founds an entry/subgroup (and this is the new last
1297 element) or the m_pLine of the group, in which case there are no more entries
1298 (or subgroups) left and m_pLast<element> becomes NULL.
1300 NB: This last problem could be avoided for entries if we added new entries
1301 immediately after m_pLine, but in this case the entries would appear
1302 backwards in the config file (OTOH, it's not that important) and as we
1303 would still need to do it for the subgroups the code wouldn't have been
1304 significantly less complicated.
1307 // Return the line which contains "[our name]". If we're still not in the list,
1308 // add our line to it immediately after the last line of our parent group if we
1309 // have it or in the very beginning if we're the root group.
1310 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1312 wxLogTrace( _T("wxFileConfig"),
1313 _T(" GetGroupLine() for Group '%s'"),
1318 wxLogTrace( _T("wxFileConfig"),
1319 _T(" Getting Line item pointer") );
1321 wxFileConfigGroup
*pParent
= Parent();
1323 // this group wasn't present in local config file, add it now
1327 wxLogTrace( _T("wxFileConfig"),
1328 _T(" checking parent '%s'"),
1329 pParent
->Name().c_str() );
1331 wxString strFullName
;
1333 strFullName
<< wxT("[") // +1: no '/'
1334 << FilterOutEntryName(GetFullName().c_str() + 1)
1336 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1337 pParent
->GetLastGroupLine());
1338 pParent
->SetLastGroup(this); // we're surely after all the others
1342 // we return NULL, so that LineListInsert() will insert us in the
1350 // Return the last line belonging to the subgroups of this group (after which
1351 // we can add a new subgroup), if we don't have any subgroups or entries our
1352 // last line is the group line (m_pLine) itself.
1353 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1355 // if we have any subgroups, our last line is
1356 // the last line of the last subgroup
1358 if ( m_pLastGroup
!= 0 )
1360 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1362 wxASSERT( pLine
!= 0 ); // last group must have !NULL associated line
1366 // no subgroups, so the last line is the line of thelast entry (if any)
1368 return GetLastEntryLine();
1371 // return the last line belonging to the entries of this group (after which
1372 // we can add a new entry), if we don't have any entries we will add the new
1373 // one immediately after the group line itself.
1374 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1376 wxLogTrace( _T("wxFileConfig"),
1377 _T(" GetLastEntryLine() for Group '%s'"),
1380 if ( m_pLastEntry
!= 0 )
1382 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1384 wxASSERT( pLine
!= 0 ); // last entry must have !NULL associated line
1388 // no entries: insert after the group header
1390 return GetGroupLine();
1393 // ----------------------------------------------------------------------------
1395 // ----------------------------------------------------------------------------
1397 void wxFileConfigGroup::Rename(const wxString
& newName
)
1399 m_strName
= newName
;
1401 wxFileConfigLineList
*line
= GetGroupLine();
1402 wxString strFullName
;
1403 strFullName
<< wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
1404 line
->SetText(strFullName
);
1409 wxString
wxFileConfigGroup::GetFullName() const
1412 return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1417 // ----------------------------------------------------------------------------
1419 // ----------------------------------------------------------------------------
1421 // use binary search because the array is sorted
1423 wxFileConfigGroup::FindEntry(const wxChar
*szName
) const
1427 hi
= m_aEntries
.Count();
1429 wxFileConfigEntry
*pEntry
;
1433 pEntry
= m_aEntries
[i
];
1435 #if wxCONFIG_CASE_SENSITIVE
1436 res
= wxStrcmp(pEntry
->Name(), szName
);
1438 res
= wxStricmp(pEntry
->Name(), szName
);
1453 wxFileConfigGroup::FindSubgroup(const wxChar
*szName
) const
1457 hi
= m_aSubgroups
.Count();
1459 wxFileConfigGroup
*pGroup
;
1463 pGroup
= m_aSubgroups
[i
];
1465 #if wxCONFIG_CASE_SENSITIVE
1466 res
= wxStrcmp(pGroup
->Name(), szName
);
1468 res
= wxStricmp(pGroup
->Name(), szName
);
1482 // ----------------------------------------------------------------------------
1483 // create a new item
1484 // ----------------------------------------------------------------------------
1486 // create a new entry and add it to the current group
1487 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1489 wxASSERT( FindEntry(strName
) == 0 );
1491 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1493 m_aEntries
.Add(pEntry
);
1497 // create a new group and add it to the current group
1498 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1500 wxASSERT( FindSubgroup(strName
) == 0 );
1502 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1504 m_aSubgroups
.Add(pGroup
);
1508 // ----------------------------------------------------------------------------
1510 // ----------------------------------------------------------------------------
1513 The delete operations are _very_ slow if we delete the last item of this
1514 group (see comments before GetXXXLineXXX functions for more details),
1515 so it's much better to start with the first entry/group if we want to
1516 delete several of them.
1519 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar
*szName
)
1521 return DeleteSubgroup(FindSubgroup(szName
));
1524 // Delete the subgroup and remove all references to it from
1525 // other data structures.
1526 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1528 wxLogTrace( _T("wxFileConfig"),
1529 _T("Deleting group '%s' from '%s'"),
1530 pGroup
->Name().c_str(),
1533 wxLogTrace( _T("wxFileConfig"),
1534 _T(" (m_pLine) = prev: %p, this %p, next %p"),
1535 ((m_pLine
) ? m_pLine
->Prev() : 0),
1537 ((m_pLine
) ? m_pLine
->Next() : 0) );
1538 wxLogTrace( _T("wxFileConfig"),
1540 ((m_pLine
) ? m_pLine
->Text().c_str() : wxEmptyString
) );
1542 wxCHECK_MSG( pGroup
!= 0, FALSE
, _T("deleting non existing group?") );
1544 // delete all entries
1546 size_t nCount
= pGroup
->m_aEntries
.Count();
1548 wxLogTrace(_T("wxFileConfig"),
1549 _T("Removing %lu Entries"),
1550 (unsigned long)nCount
);
1552 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1554 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1558 wxLogTrace( _T("wxFileConfig"),
1560 pLine
->Text().c_str() );
1561 m_pConfig
->LineListRemove(pLine
);
1565 // and subgroups of this subgroup
1567 nCount
= pGroup
->m_aSubgroups
.Count();
1569 wxLogTrace( _T("wxFileConfig"),
1570 _T("Removing %lu SubGroups"),
1571 (unsigned long)nCount
);
1573 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1575 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1578 // finally the group itself
1580 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1584 wxLogTrace( _T("wxFileConfig"),
1585 _T(" Removing line entry for Group '%s' : '%s'"),
1586 pGroup
->Name().c_str(),
1587 pLine
->Text().c_str() );
1588 wxLogTrace( _T("wxFileConfig"),
1589 _T(" Removing from Group '%s' : '%s'"),
1591 ((m_pLine
) ? m_pLine
->Text().c_str() : wxEmptyString
) );
1593 // notice that we may do this test inside the previous "if"
1594 // because the last entry's line is surely !NULL
1596 if ( pGroup
== m_pLastGroup
)
1598 wxLogTrace( _T("wxFileConfig"),
1599 _T(" ------- Removing last group -------") );
1601 // our last entry is being deleted, so find the last one which stays.
1602 // go back until we find a subgroup or reach the group's line, unless
1603 // we are the root group, which we'll notice shortly.
1605 wxFileConfigGroup
*pNewLast
= 0;
1606 size_t nSubgroups
= m_aSubgroups
.Count();
1607 wxFileConfigLineList
*pl
;
1609 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() )
1611 // is it our subgroup?
1613 for ( size_t n
= 0; (pNewLast
== 0) && (n
< nSubgroups
); n
++ )
1615 // do _not_ call GetGroupLine! we don't want to add it to the local
1616 // file if it's not already there
1618 if ( m_aSubgroups
[n
]->m_pLine
== m_pLine
)
1619 pNewLast
= m_aSubgroups
[n
];
1622 if ( pNewLast
!= 0 ) // found?
1626 if ( pl
== m_pLine
|| m_pParent
== 0 )
1628 wxLogTrace( _T("wxFileConfig"),
1629 _T(" ------- No previous group found -------") );
1631 wxASSERT_MSG( !pNewLast
|| m_pLine
== 0,
1632 _T("how comes it has the same line as we?") );
1634 // we've reached the group line without finding any subgroups,
1635 // or realised we removed the last group from the root.
1641 wxLogTrace( _T("wxFileConfig"),
1642 _T(" ------- Last Group set to '%s' -------"),
1643 pNewLast
->Name().c_str() );
1645 m_pLastGroup
= pNewLast
;
1649 m_pConfig
->LineListRemove(pLine
);
1653 wxLogTrace( _T("wxFileConfig"),
1654 _T(" No line entry for Group '%s'?"),
1655 pGroup
->Name().c_str() );
1660 m_aSubgroups
.Remove(pGroup
);
1666 bool wxFileConfigGroup::DeleteEntry(const wxChar
*szName
)
1668 wxFileConfigEntry
*pEntry
= FindEntry(szName
);
1669 wxCHECK( pEntry
!= NULL
, FALSE
); // deleting non existing item?
1671 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1672 if ( pLine
!= NULL
) {
1673 // notice that we may do this test inside the previous "if" because the
1674 // last entry's line is surely !NULL
1675 if ( pEntry
== m_pLastEntry
) {
1676 // our last entry is being deleted - find the last one which stays
1677 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1679 // go back until we find another entry or reach the group's line
1680 wxFileConfigEntry
*pNewLast
= NULL
;
1681 size_t n
, nEntries
= m_aEntries
.Count();
1682 wxFileConfigLineList
*pl
;
1683 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1684 // is it our subgroup?
1685 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1686 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1687 pNewLast
= m_aEntries
[n
];
1690 if ( pNewLast
!= NULL
) // found?
1694 if ( pl
== m_pLine
) {
1695 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1697 // we've reached the group line without finding any subgroups
1698 m_pLastEntry
= NULL
;
1701 m_pLastEntry
= pNewLast
;
1704 m_pConfig
->LineListRemove(pLine
);
1707 // we must be written back for the changes to be saved
1710 m_aEntries
.Remove(pEntry
);
1716 // ----------------------------------------------------------------------------
1718 // ----------------------------------------------------------------------------
1719 void wxFileConfigGroup::SetDirty()
1722 if ( Parent() != NULL
) // propagate upwards
1723 Parent()->SetDirty();
1726 // ============================================================================
1727 // wxFileConfig::wxFileConfigEntry
1728 // ============================================================================
1730 // ----------------------------------------------------------------------------
1732 // ----------------------------------------------------------------------------
1733 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1734 const wxString
& strName
,
1736 : m_strName(strName
)
1738 wxASSERT( !strName
.IsEmpty() );
1740 m_pParent
= pParent
;
1745 m_bHasValue
= FALSE
;
1747 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1749 m_strName
.erase(0, 1); // remove first character
1752 // ----------------------------------------------------------------------------
1754 // ----------------------------------------------------------------------------
1756 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1758 if ( m_pLine
!= NULL
) {
1759 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1760 Name().c_str(), m_pParent
->GetFullName().c_str());
1764 Group()->SetLastEntry(this);
1767 // second parameter is FALSE if we read the value from file and prevents the
1768 // entry from being marked as 'dirty'
1769 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1771 if ( bUser
&& IsImmutable() )
1773 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1778 // do nothing if it's the same value: but don't test for it
1779 // if m_bHasValue hadn't been set yet or we'd never write
1780 // empty values to the file
1782 if ( m_bHasValue
&& strValue
== m_strValue
)
1786 m_strValue
= strValue
;
1790 wxString strValFiltered
;
1792 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1794 strValFiltered
= strValue
;
1797 strValFiltered
= FilterOutValue(strValue
);
1801 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1805 // entry was read from the local config file, just modify the line
1806 m_pLine
->SetText(strLine
);
1809 // add a new line to the file
1810 wxASSERT( m_nLine
== wxNOT_FOUND
); // consistency check
1812 m_pLine
= Group()->Config()->LineListInsert(strLine
,
1813 Group()->GetLastEntryLine());
1814 Group()->SetLastEntry(this);
1821 void wxFileConfigEntry::SetDirty()
1824 Group()->SetDirty();
1827 // ============================================================================
1829 // ============================================================================
1831 // ----------------------------------------------------------------------------
1832 // compare functions for array sorting
1833 // ----------------------------------------------------------------------------
1835 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1837 #if wxCONFIG_CASE_SENSITIVE
1838 return wxStrcmp(p1
->Name(), p2
->Name());
1840 return wxStricmp(p1
->Name(), p2
->Name());
1844 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1846 #if wxCONFIG_CASE_SENSITIVE
1847 return wxStrcmp(p1
->Name(), p2
->Name());
1849 return wxStricmp(p1
->Name(), p2
->Name());
1853 // ----------------------------------------------------------------------------
1855 // ----------------------------------------------------------------------------
1857 // undo FilterOutValue
1858 static wxString
FilterInValue(const wxString
& str
)
1861 strResult
.Alloc(str
.Len());
1863 bool bQuoted
= !str
.IsEmpty() && str
[0] == '"';
1865 for ( size_t n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1866 if ( str
[n
] == wxT('\\') ) {
1867 switch ( str
[++n
] ) {
1869 strResult
+= wxT('\n');
1873 strResult
+= wxT('\r');
1877 strResult
+= wxT('\t');
1881 strResult
+= wxT('\\');
1885 strResult
+= wxT('"');
1890 if ( str
[n
] != wxT('"') || !bQuoted
)
1891 strResult
+= str
[n
];
1892 else if ( n
!= str
.Len() - 1 ) {
1893 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1896 //else: it's the last quote of a quoted string, ok
1903 // quote the string before writing it to file
1904 static wxString
FilterOutValue(const wxString
& str
)
1910 strResult
.Alloc(str
.Len());
1912 // quoting is necessary to preserve spaces in the beginning of the string
1913 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
1916 strResult
+= wxT('"');
1919 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
1942 //else: fall through
1945 strResult
+= str
[n
];
1946 continue; // nothing special to do
1949 // we get here only for special characters
1950 strResult
<< wxT('\\') << c
;
1954 strResult
+= wxT('"');
1959 // undo FilterOutEntryName
1960 static wxString
FilterInEntryName(const wxString
& str
)
1963 strResult
.Alloc(str
.Len());
1965 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
1966 if ( *pc
== wxT('\\') )
1975 // sanitize entry or group name: insert '\\' before any special characters
1976 static wxString
FilterOutEntryName(const wxString
& str
)
1979 strResult
.Alloc(str
.Len());
1981 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
1984 // we explicitly allow some of "safe" chars and 8bit ASCII characters
1985 // which will probably never have special meaning
1986 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
1987 // should *not* be quoted
1988 if ( !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) && ((c
& 0x80) == 0) )
1989 strResult
+= wxT('\\');
1997 // we can't put ?: in the ctor initializer list because it confuses some
1998 // broken compilers (Borland C++)
1999 static wxString
GetAppName(const wxString
& appName
)
2001 if ( !appName
&& wxTheApp
)
2002 return wxTheApp
->GetAppName();
2007 #endif // wxUSE_CONFIG