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 ( fileGlobal
.Open(m_conv
/*ignored in ANSI build*/) )
394 Parse(fileGlobal
, FALSE
/* global */);
399 wxLogWarning(_("can't open global configuration file '%s'."), m_strGlobalFile
.c_str());
403 // parse the local file
404 if ( !m_strLocalFile
.IsEmpty() && wxFile::Exists(m_strLocalFile
) )
406 wxTextFile
fileLocal(m_strLocalFile
);
407 if ( fileLocal
.Open(m_conv
/*ignored in ANSI build*/) )
409 Parse(fileLocal
, TRUE
/* local */);
414 wxLogWarning(_("can't open user configuration file '%s'."), m_strLocalFile
.c_str() );
419 // constructor supports creation of wxFileConfig objects of any type
420 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
421 const wxString
& strLocal
, const wxString
& strGlobal
,
422 long style
, wxMBConv
& conv
)
423 : wxConfigBase(::GetAppName(appName
), vendorName
,
426 m_strLocalFile(strLocal
), m_strGlobalFile(strGlobal
),
429 // Make up names for files if empty
430 if ( m_strLocalFile
.IsEmpty() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
431 m_strLocalFile
= GetLocalFileName(GetAppName());
433 if ( m_strGlobalFile
.IsEmpty() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
434 m_strGlobalFile
= GetGlobalFileName(GetAppName());
436 // Check if styles are not supplied, but filenames are, in which case
437 // add the correct styles.
438 if ( !m_strLocalFile
.IsEmpty() )
439 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
441 if ( !m_strGlobalFile
.IsEmpty() )
442 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
444 // if the path is not absolute, prepend the standard directory to it
445 // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
446 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
448 if ( !m_strLocalFile
.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile
) )
450 wxString strLocal
= m_strLocalFile
;
451 m_strLocalFile
= GetLocalDir();
452 m_strLocalFile
<< strLocal
;
455 if ( !m_strGlobalFile
.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile
) )
457 wxString strGlobal
= m_strGlobalFile
;
458 m_strGlobalFile
= GetGlobalDir();
459 m_strGlobalFile
<< strGlobal
;
470 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, wxMBConv
& conv
)
473 // always local_file when this constructor is called (?)
474 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
477 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxT(""), this);
482 // translate everything to the current (platform-dependent) line
483 // termination character
489 while ( !inStream
.Read(buf
, WXSIZEOF(buf
)).Eof() )
490 strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead());
492 strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead());
494 strTrans
= wxTextBuffer::Translate(strTmp
);
497 wxMemoryText memText
;
499 // Now we can add the text to the memory text. To do this we extract line
500 // by line from the translated string, until we've reached the end.
502 // VZ: all this is horribly inefficient, we should do the translation on
503 // the fly in one pass saving both memory and time (TODO)
505 const wxChar
*pEOL
= wxTextBuffer::GetEOL(wxTextBuffer::typeDefault
);
506 const size_t EOLLen
= wxStrlen(pEOL
);
508 int posLineStart
= strTrans
.Find(pEOL
);
509 while ( posLineStart
!= -1 )
511 wxString
line(strTrans
.Left(posLineStart
));
513 memText
.AddLine(line
);
515 strTrans
= strTrans
.Mid(posLineStart
+ EOLLen
);
517 posLineStart
= strTrans
.Find(pEOL
);
520 // also add whatever we have left in the translated string.
521 memText
.AddLine(strTrans
);
523 // Finally we can parse it all.
524 Parse(memText
, TRUE
/* local */);
529 #endif // wxUSE_STREAMS
531 void wxFileConfig::CleanUp()
535 wxFileConfigLineList
*pCur
= m_linesHead
;
536 while ( pCur
!= NULL
) {
537 wxFileConfigLineList
*pNext
= pCur
->Next();
543 wxFileConfig::~wxFileConfig()
550 // ----------------------------------------------------------------------------
551 // parse a config file
552 // ----------------------------------------------------------------------------
554 void wxFileConfig::Parse(wxTextBuffer
& buffer
, bool bLocal
)
556 const wxChar
*pStart
;
560 size_t nLineCount
= buffer
.GetLineCount();
562 for ( size_t n
= 0; n
< nLineCount
; n
++ )
566 // add the line to linked list
568 LineListAppend(strLine
);
570 // skip leading spaces
571 for ( pStart
= strLine
; wxIsspace(*pStart
); pStart
++ )
574 // skip blank/comment lines
575 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
578 if ( *pStart
== wxT('[') ) { // a new group
581 while ( *++pEnd
!= wxT(']') ) {
582 if ( *pEnd
== wxT('\\') ) {
583 // the next char is escaped, so skip it even if it is ']'
587 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
588 // we reached the end of line, break out of the loop
593 if ( *pEnd
!= wxT(']') ) {
594 wxLogError(_("file '%s': unexpected character %c at line %d."),
595 buffer
.GetName(), *pEnd
, n
+ 1);
596 continue; // skip this line
599 // group name here is always considered as abs path
602 strGroup
<< wxCONFIG_PATH_SEPARATOR
603 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
605 // will create it if doesn't yet exist
609 m_pCurrentGroup
->SetLine(m_linesTail
);
611 // check that there is nothing except comments left on this line
613 while ( *++pEnd
!= wxT('\0') && bCont
) {
622 // ignore whitespace ('\n' impossible here)
626 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
627 buffer
.GetName(), n
+ 1, pEnd
);
633 const wxChar
*pEnd
= pStart
;
634 while ( *pEnd
&& *pEnd
!= wxT('=') && !wxIsspace(*pEnd
) ) {
635 if ( *pEnd
== wxT('\\') ) {
636 // next character may be space or not - still take it because it's
637 // quoted (unless there is nothing)
640 // the error message will be given below anyhow
648 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
)));
651 while ( wxIsspace(*pEnd
) )
654 if ( *pEnd
++ != wxT('=') ) {
655 wxLogError(_("file '%s', line %d: '=' expected."),
656 buffer
.GetName(), n
+ 1);
659 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
661 if ( pEntry
== NULL
) {
663 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
666 pEntry
->SetLine(m_linesTail
);
669 if ( bLocal
&& pEntry
->IsImmutable() ) {
670 // immutable keys can't be changed by user
671 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
672 buffer
.GetName(), n
+ 1, strKey
.c_str());
675 // the condition below catches the cases (a) and (b) but not (c):
676 // (a) global key found second time in global file
677 // (b) key found second (or more) time in local file
678 // (c) key from global file now found in local one
679 // which is exactly what we want.
680 else if ( !bLocal
|| pEntry
->IsLocal() ) {
681 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
682 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
685 pEntry
->SetLine(m_linesTail
);
690 while ( wxIsspace(*pEnd
) )
693 wxString value
= pEnd
;
694 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
695 value
= FilterInValue(value
);
697 pEntry
->SetValue(value
, FALSE
);
703 // ----------------------------------------------------------------------------
705 // ----------------------------------------------------------------------------
707 void wxFileConfig::SetRootPath()
710 m_pCurrentGroup
= m_pRootGroup
;
713 void wxFileConfig::SetPath(const wxString
& strPath
)
715 wxArrayString aParts
;
717 if ( strPath
.IsEmpty() ) {
722 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
724 wxSplitPath(aParts
, strPath
);
727 // relative path, combine with current one
728 wxString strFullPath
= m_strPath
;
729 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
730 wxSplitPath(aParts
, strFullPath
);
733 // change current group
735 m_pCurrentGroup
= m_pRootGroup
;
736 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
737 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
738 if ( pNextGroup
== NULL
)
739 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
740 m_pCurrentGroup
= pNextGroup
;
743 // recombine path parts in one variable
745 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
746 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
750 // ----------------------------------------------------------------------------
752 // ----------------------------------------------------------------------------
754 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
757 return GetNextGroup(str
, lIndex
);
760 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
762 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().Count() ) {
763 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
770 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
773 return GetNextEntry(str
, lIndex
);
776 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
778 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().Count() ) {
779 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
786 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
788 size_t n
= m_pCurrentGroup
->Entries().Count();
790 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
791 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
792 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
793 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
794 n
+= GetNumberOfEntries(TRUE
);
795 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
802 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
804 size_t n
= m_pCurrentGroup
->Groups().Count();
806 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
807 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
808 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
809 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
810 n
+= GetNumberOfGroups(TRUE
);
811 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
818 // ----------------------------------------------------------------------------
819 // tests for existence
820 // ----------------------------------------------------------------------------
822 bool wxFileConfig::HasGroup(const wxString
& strName
) const
824 wxConfigPathChanger
path(this, strName
);
826 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
->FindSubgroup(path
.Name());
827 return pGroup
!= NULL
;
830 bool wxFileConfig::HasEntry(const wxString
& strName
) const
832 wxConfigPathChanger
path(this, strName
);
834 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
835 return pEntry
!= NULL
;
838 // ----------------------------------------------------------------------------
840 // ----------------------------------------------------------------------------
842 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
844 wxConfigPathChanger
path(this, key
);
846 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
847 if (pEntry
== NULL
) {
851 *pStr
= pEntry
->Value();
856 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
859 if ( !Read(key
, & str
) )
863 return str
.ToLong(pl
) ;
866 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
868 wxConfigPathChanger
path(this, key
);
869 wxString strName
= path
.Name();
871 wxLogTrace( _T("wxFileConfig"),
872 _T(" Writing String '%s' = '%s' to Group '%s'"),
877 if ( strName
.IsEmpty() )
879 // setting the value of a group is an error
881 wxASSERT_MSG( wxIsEmpty(szValue
), wxT("can't set value of a group!") );
883 // ... except if it's empty in which case it's a way to force it's creation
885 wxLogTrace( _T("wxFileConfig"),
886 _T(" Creating group %s"),
887 m_pCurrentGroup
->Name().c_str() );
889 m_pCurrentGroup
->SetDirty();
891 // this will add a line for this group if it didn't have it before
893 (void)m_pCurrentGroup
->GetGroupLine();
898 // check that the name is reasonable
900 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
)
902 wxLogError( _("Config entry name cannot start with '%c'."),
903 wxCONFIG_IMMUTABLE_PREFIX
);
907 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
911 wxLogTrace( _T("wxFileConfig"),
912 _T(" Adding Entry %s"),
914 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
917 wxLogTrace( _T("wxFileConfig"),
918 _T(" Setting value %s"),
920 pEntry
->SetValue(szValue
);
926 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
928 return Write(key
, wxString::Format(_T("%ld"), lValue
));
931 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
933 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() || !m_strLocalFile
)
937 // set the umask if needed
941 umaskOld
= umask((mode_t
)m_umask
);
945 wxTempFile
file(m_strLocalFile
);
947 if ( !file
.IsOpened() )
949 wxLogError(_("can't open user configuration file."));
953 // write all strings to file
954 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
956 wxString line
= p
->Text();
957 line
+= wxTextFile::GetEOL();
958 if ( !file
.Write(line
, m_conv
) )
960 wxLogError(_("can't write user configuration file."));
965 bool ret
= file
.Commit();
967 #if defined(__WXMAC__)
972 wxMacFilename2FSSpec( m_strLocalFile
, &spec
) ;
974 if ( FSpGetFInfo( &spec
, &finfo
) == noErr
)
976 finfo
.fdType
= 'TEXT' ;
977 finfo
.fdCreator
= 'ttxt' ;
978 FSpSetFInfo( &spec
, &finfo
) ;
984 // restore the old umask if we changed it
987 (void)umask(umaskOld
);
994 // ----------------------------------------------------------------------------
995 // renaming groups/entries
996 // ----------------------------------------------------------------------------
998 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
999 const wxString
& newName
)
1001 // check that the entry exists
1002 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1006 // check that the new entry doesn't already exist
1007 if ( m_pCurrentGroup
->FindEntry(newName
) )
1010 // delete the old entry, create the new one
1011 wxString value
= oldEntry
->Value();
1012 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1015 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1016 newEntry
->SetValue(value
);
1021 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1022 const wxString
& newName
)
1024 // check that the group exists
1025 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1029 // check that the new group doesn't already exist
1030 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1033 group
->Rename(newName
);
1038 // ----------------------------------------------------------------------------
1039 // delete groups/entries
1040 // ----------------------------------------------------------------------------
1042 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1044 wxConfigPathChanger
path(this, key
);
1046 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1049 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1050 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1051 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1052 SetPath(wxT("..")); // changes m_pCurrentGroup!
1053 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1055 //else: never delete the root group
1061 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1063 wxConfigPathChanger
path(this, key
);
1065 return m_pCurrentGroup
->DeleteSubgroupByName(path
.Name());
1068 bool wxFileConfig::DeleteAll()
1072 if ( wxRemove(m_strLocalFile
) == -1 )
1073 wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile
.c_str());
1075 m_strLocalFile
= m_strGlobalFile
= wxT("");
1081 // ----------------------------------------------------------------------------
1082 // linked list functions
1083 // ----------------------------------------------------------------------------
1085 // append a new line to the end of the list
1087 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1089 wxLogTrace( _T("wxFileConfig"),
1090 _T(" ** Adding Line '%s'"),
1092 wxLogTrace( _T("wxFileConfig"),
1094 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1095 wxLogTrace( _T("wxFileConfig"),
1097 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1099 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1101 if ( m_linesTail
== NULL
)
1104 m_linesHead
= pLine
;
1109 m_linesTail
->SetNext(pLine
);
1110 pLine
->SetPrev(m_linesTail
);
1113 m_linesTail
= pLine
;
1115 wxLogTrace( _T("wxFileConfig"),
1117 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1118 wxLogTrace( _T("wxFileConfig"),
1120 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1125 // insert a new line after the given one or in the very beginning if !pLine
1127 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1128 wxFileConfigLineList
*pLine
)
1130 wxLogTrace( _T("wxFileConfig"),
1131 _T(" ** Inserting Line '%s' after '%s'"),
1133 ((pLine
) ? pLine
->Text().c_str() : wxEmptyString
) );
1134 wxLogTrace( _T("wxFileConfig"),
1136 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1137 wxLogTrace( _T("wxFileConfig"),
1139 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1141 if ( pLine
== m_linesTail
)
1142 return LineListAppend(str
);
1144 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1145 if ( pLine
== NULL
)
1147 // prepend to the list
1148 pNewLine
->SetNext(m_linesHead
);
1149 m_linesHead
->SetPrev(pNewLine
);
1150 m_linesHead
= pNewLine
;
1154 // insert before pLine
1155 wxFileConfigLineList
*pNext
= pLine
->Next();
1156 pNewLine
->SetNext(pNext
);
1157 pNewLine
->SetPrev(pLine
);
1158 pNext
->SetPrev(pNewLine
);
1159 pLine
->SetNext(pNewLine
);
1162 wxLogTrace( _T("wxFileConfig"),
1164 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1165 wxLogTrace( _T("wxFileConfig"),
1167 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1172 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1174 wxLogTrace( _T("wxFileConfig"),
1175 _T(" ** Removing Line '%s'"),
1176 pLine
->Text().c_str() );
1177 wxLogTrace( _T("wxFileConfig"),
1179 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1180 wxLogTrace( _T("wxFileConfig"),
1182 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1184 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1185 *pNext
= pLine
->Next();
1189 if ( pPrev
== NULL
)
1190 m_linesHead
= pNext
;
1192 pPrev
->SetNext(pNext
);
1196 if ( pNext
== NULL
)
1197 m_linesTail
= pPrev
;
1199 pNext
->SetPrev(pPrev
);
1201 wxLogTrace( _T("wxFileConfig"),
1203 ((m_linesHead
) ? m_linesHead
->Text().c_str() : wxEmptyString
) );
1204 wxLogTrace( _T("wxFileConfig"),
1206 ((m_linesTail
) ? m_linesTail
->Text().c_str() : wxEmptyString
) );
1211 bool wxFileConfig::LineListIsEmpty()
1213 return m_linesHead
== NULL
;
1216 // ============================================================================
1217 // wxFileConfig::wxFileConfigGroup
1218 // ============================================================================
1220 // ----------------------------------------------------------------------------
1222 // ----------------------------------------------------------------------------
1225 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1226 const wxString
& strName
,
1227 wxFileConfig
*pConfig
)
1228 : m_aEntries(CompareEntries
),
1229 m_aSubgroups(CompareGroups
),
1232 m_pConfig
= pConfig
;
1233 m_pParent
= pParent
;
1237 m_pLastEntry
= NULL
;
1238 m_pLastGroup
= NULL
;
1241 // dtor deletes all children
1242 wxFileConfigGroup::~wxFileConfigGroup()
1245 size_t n
, nCount
= m_aEntries
.Count();
1246 for ( n
= 0; n
< nCount
; n
++ )
1247 delete m_aEntries
[n
];
1250 nCount
= m_aSubgroups
.Count();
1251 for ( n
= 0; n
< nCount
; n
++ )
1252 delete m_aSubgroups
[n
];
1255 // ----------------------------------------------------------------------------
1257 // ----------------------------------------------------------------------------
1259 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1261 wxASSERT( m_pLine
== 0 ); // shouldn't be called twice
1266 This is a bit complicated, so let me explain it in details. All lines that
1267 were read from the local file (the only one we will ever modify) are stored
1268 in a (doubly) linked list. Our problem is to know at which position in this
1269 list should we insert the new entries/subgroups. To solve it we keep three
1270 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1272 m_pLine points to the line containing "[group_name]"
1273 m_pLastEntry points to the last entry of this group in the local file.
1274 m_pLastGroup subgroup
1276 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1277 from the local file, the corresponding variable is set. However, if the group
1278 was read from the global file and then modified or created by the application
1279 these variables are still NULL and we need to create the corresponding lines.
1280 See the following functions (and comments preceding them) for the details of
1283 Also, when our last entry/group are deleted we need to find the new last
1284 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1285 of lines until it either founds an entry/subgroup (and this is the new last
1286 element) or the m_pLine of the group, in which case there are no more entries
1287 (or subgroups) left and m_pLast<element> becomes NULL.
1289 NB: This last problem could be avoided for entries if we added new entries
1290 immediately after m_pLine, but in this case the entries would appear
1291 backwards in the config file (OTOH, it's not that important) and as we
1292 would still need to do it for the subgroups the code wouldn't have been
1293 significantly less complicated.
1296 // Return the line which contains "[our name]". If we're still not in the list,
1297 // add our line to it immediately after the last line of our parent group if we
1298 // have it or in the very beginning if we're the root group.
1299 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1301 wxLogTrace( _T("wxFileConfig"),
1302 _T(" GetGroupLine() for Group '%s'"),
1307 wxLogTrace( _T("wxFileConfig"),
1308 _T(" Getting Line item pointer") );
1310 wxFileConfigGroup
*pParent
= Parent();
1312 // this group wasn't present in local config file, add it now
1316 wxLogTrace( _T("wxFileConfig"),
1317 _T(" checking parent '%s'"),
1318 pParent
->Name().c_str() );
1320 wxString strFullName
;
1322 strFullName
<< wxT("[") // +1: no '/'
1323 << FilterOutEntryName(GetFullName().c_str() + 1)
1325 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1326 pParent
->GetLastGroupLine());
1327 pParent
->SetLastGroup(this); // we're surely after all the others
1331 // we return NULL, so that LineListInsert() will insert us in the
1339 // Return the last line belonging to the subgroups of this group (after which
1340 // we can add a new subgroup), if we don't have any subgroups or entries our
1341 // last line is the group line (m_pLine) itself.
1342 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1344 // if we have any subgroups, our last line is
1345 // the last line of the last subgroup
1347 if ( m_pLastGroup
!= 0 )
1349 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1351 wxASSERT( pLine
!= 0 ); // last group must have !NULL associated line
1355 // no subgroups, so the last line is the line of thelast entry (if any)
1357 return GetLastEntryLine();
1360 // return the last line belonging to the entries of this group (after which
1361 // we can add a new entry), if we don't have any entries we will add the new
1362 // one immediately after the group line itself.
1363 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1365 wxLogTrace( _T("wxFileConfig"),
1366 _T(" GetLastEntryLine() for Group '%s'"),
1369 if ( m_pLastEntry
!= 0 )
1371 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1373 wxASSERT( pLine
!= 0 ); // last entry must have !NULL associated line
1377 // no entries: insert after the group header
1379 return GetGroupLine();
1382 // ----------------------------------------------------------------------------
1384 // ----------------------------------------------------------------------------
1386 void wxFileConfigGroup::Rename(const wxString
& newName
)
1388 m_strName
= newName
;
1390 wxFileConfigLineList
*line
= GetGroupLine();
1391 wxString strFullName
;
1392 strFullName
<< wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
1393 line
->SetText(strFullName
);
1398 wxString
wxFileConfigGroup::GetFullName() const
1401 return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1406 // ----------------------------------------------------------------------------
1408 // ----------------------------------------------------------------------------
1410 // use binary search because the array is sorted
1412 wxFileConfigGroup::FindEntry(const wxChar
*szName
) const
1416 hi
= m_aEntries
.Count();
1418 wxFileConfigEntry
*pEntry
;
1422 pEntry
= m_aEntries
[i
];
1424 #if wxCONFIG_CASE_SENSITIVE
1425 res
= wxStrcmp(pEntry
->Name(), szName
);
1427 res
= wxStricmp(pEntry
->Name(), szName
);
1442 wxFileConfigGroup::FindSubgroup(const wxChar
*szName
) const
1446 hi
= m_aSubgroups
.Count();
1448 wxFileConfigGroup
*pGroup
;
1452 pGroup
= m_aSubgroups
[i
];
1454 #if wxCONFIG_CASE_SENSITIVE
1455 res
= wxStrcmp(pGroup
->Name(), szName
);
1457 res
= wxStricmp(pGroup
->Name(), szName
);
1471 // ----------------------------------------------------------------------------
1472 // create a new item
1473 // ----------------------------------------------------------------------------
1475 // create a new entry and add it to the current group
1476 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1478 wxASSERT( FindEntry(strName
) == 0 );
1480 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1482 m_aEntries
.Add(pEntry
);
1486 // create a new group and add it to the current group
1487 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1489 wxASSERT( FindSubgroup(strName
) == 0 );
1491 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1493 m_aSubgroups
.Add(pGroup
);
1497 // ----------------------------------------------------------------------------
1499 // ----------------------------------------------------------------------------
1502 The delete operations are _very_ slow if we delete the last item of this
1503 group (see comments before GetXXXLineXXX functions for more details),
1504 so it's much better to start with the first entry/group if we want to
1505 delete several of them.
1508 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar
*szName
)
1510 wxFileConfigGroup
* const pGroup
= FindSubgroup(szName
);
1512 return pGroup
? DeleteSubgroup(pGroup
) : FALSE
;
1515 // Delete the subgroup and remove all references to it from
1516 // other data structures.
1517 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1519 wxCHECK_MSG( pGroup
, FALSE
, _T("deleting non existing group?") );
1521 wxLogTrace( _T("wxFileConfig"),
1522 _T("Deleting group '%s' from '%s'"),
1523 pGroup
->Name().c_str(),
1526 wxLogTrace( _T("wxFileConfig"),
1527 _T(" (m_pLine) = prev: %p, this %p, next %p"),
1528 ((m_pLine
) ? m_pLine
->Prev() : 0),
1530 ((m_pLine
) ? m_pLine
->Next() : 0) );
1531 wxLogTrace( _T("wxFileConfig"),
1533 ((m_pLine
) ? m_pLine
->Text().c_str() : wxEmptyString
) );
1535 // delete all entries
1536 size_t nCount
= pGroup
->m_aEntries
.Count();
1538 wxLogTrace(_T("wxFileConfig"),
1539 _T("Removing %lu Entries"),
1540 (unsigned long)nCount
);
1542 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1544 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1548 wxLogTrace( _T("wxFileConfig"),
1550 pLine
->Text().c_str() );
1551 m_pConfig
->LineListRemove(pLine
);
1555 // and subgroups of this subgroup
1557 nCount
= pGroup
->m_aSubgroups
.Count();
1559 wxLogTrace( _T("wxFileConfig"),
1560 _T("Removing %lu SubGroups"),
1561 (unsigned long)nCount
);
1563 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1565 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1568 // finally the group itself
1570 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1574 wxLogTrace( _T("wxFileConfig"),
1575 _T(" Removing line entry for Group '%s' : '%s'"),
1576 pGroup
->Name().c_str(),
1577 pLine
->Text().c_str() );
1578 wxLogTrace( _T("wxFileConfig"),
1579 _T(" Removing from Group '%s' : '%s'"),
1581 ((m_pLine
) ? m_pLine
->Text().c_str() : wxEmptyString
) );
1583 // notice that we may do this test inside the previous "if"
1584 // because the last entry's line is surely !NULL
1586 if ( pGroup
== m_pLastGroup
)
1588 wxLogTrace( _T("wxFileConfig"),
1589 _T(" ------- Removing last group -------") );
1591 // our last entry is being deleted, so find the last one which stays.
1592 // go back until we find a subgroup or reach the group's line, unless
1593 // we are the root group, which we'll notice shortly.
1595 wxFileConfigGroup
*pNewLast
= 0;
1596 size_t nSubgroups
= m_aSubgroups
.Count();
1597 wxFileConfigLineList
*pl
;
1599 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() )
1601 // is it our subgroup?
1603 for ( size_t n
= 0; (pNewLast
== 0) && (n
< nSubgroups
); n
++ )
1605 // do _not_ call GetGroupLine! we don't want to add it to the local
1606 // file if it's not already there
1608 if ( m_aSubgroups
[n
]->m_pLine
== m_pLine
)
1609 pNewLast
= m_aSubgroups
[n
];
1612 if ( pNewLast
!= 0 ) // found?
1616 if ( pl
== m_pLine
|| m_pParent
== 0 )
1618 wxLogTrace( _T("wxFileConfig"),
1619 _T(" ------- No previous group found -------") );
1621 wxASSERT_MSG( !pNewLast
|| m_pLine
== 0,
1622 _T("how comes it has the same line as we?") );
1624 // we've reached the group line without finding any subgroups,
1625 // or realised we removed the last group from the root.
1631 wxLogTrace( _T("wxFileConfig"),
1632 _T(" ------- Last Group set to '%s' -------"),
1633 pNewLast
->Name().c_str() );
1635 m_pLastGroup
= pNewLast
;
1639 m_pConfig
->LineListRemove(pLine
);
1643 wxLogTrace( _T("wxFileConfig"),
1644 _T(" No line entry for Group '%s'?"),
1645 pGroup
->Name().c_str() );
1650 m_aSubgroups
.Remove(pGroup
);
1656 bool wxFileConfigGroup::DeleteEntry(const wxChar
*szName
)
1658 wxFileConfigEntry
*pEntry
= FindEntry(szName
);
1659 wxCHECK( pEntry
!= NULL
, FALSE
); // deleting non existing item?
1661 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1662 if ( pLine
!= NULL
) {
1663 // notice that we may do this test inside the previous "if" because the
1664 // last entry's line is surely !NULL
1665 if ( pEntry
== m_pLastEntry
) {
1666 // our last entry is being deleted - find the last one which stays
1667 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1669 // go back until we find another entry or reach the group's line
1670 wxFileConfigEntry
*pNewLast
= NULL
;
1671 size_t n
, nEntries
= m_aEntries
.Count();
1672 wxFileConfigLineList
*pl
;
1673 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1674 // is it our subgroup?
1675 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1676 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1677 pNewLast
= m_aEntries
[n
];
1680 if ( pNewLast
!= NULL
) // found?
1684 if ( pl
== m_pLine
) {
1685 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1687 // we've reached the group line without finding any subgroups
1688 m_pLastEntry
= NULL
;
1691 m_pLastEntry
= pNewLast
;
1694 m_pConfig
->LineListRemove(pLine
);
1697 // we must be written back for the changes to be saved
1700 m_aEntries
.Remove(pEntry
);
1706 // ----------------------------------------------------------------------------
1708 // ----------------------------------------------------------------------------
1709 void wxFileConfigGroup::SetDirty()
1712 if ( Parent() != NULL
) // propagate upwards
1713 Parent()->SetDirty();
1716 // ============================================================================
1717 // wxFileConfig::wxFileConfigEntry
1718 // ============================================================================
1720 // ----------------------------------------------------------------------------
1722 // ----------------------------------------------------------------------------
1723 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1724 const wxString
& strName
,
1726 : m_strName(strName
)
1728 wxASSERT( !strName
.IsEmpty() );
1730 m_pParent
= pParent
;
1735 m_bHasValue
= FALSE
;
1737 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1739 m_strName
.erase(0, 1); // remove first character
1742 // ----------------------------------------------------------------------------
1744 // ----------------------------------------------------------------------------
1746 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1748 if ( m_pLine
!= NULL
) {
1749 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1750 Name().c_str(), m_pParent
->GetFullName().c_str());
1754 Group()->SetLastEntry(this);
1757 // second parameter is FALSE if we read the value from file and prevents the
1758 // entry from being marked as 'dirty'
1759 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1761 if ( bUser
&& IsImmutable() )
1763 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1768 // do nothing if it's the same value: but don't test for it
1769 // if m_bHasValue hadn't been set yet or we'd never write
1770 // empty values to the file
1772 if ( m_bHasValue
&& strValue
== m_strValue
)
1776 m_strValue
= strValue
;
1780 wxString strValFiltered
;
1782 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1784 strValFiltered
= strValue
;
1787 strValFiltered
= FilterOutValue(strValue
);
1791 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1795 // entry was read from the local config file, just modify the line
1796 m_pLine
->SetText(strLine
);
1799 // add a new line to the file
1800 wxASSERT( m_nLine
== wxNOT_FOUND
); // consistency check
1802 m_pLine
= Group()->Config()->LineListInsert(strLine
,
1803 Group()->GetLastEntryLine());
1804 Group()->SetLastEntry(this);
1811 void wxFileConfigEntry::SetDirty()
1814 Group()->SetDirty();
1817 // ============================================================================
1819 // ============================================================================
1821 // ----------------------------------------------------------------------------
1822 // compare functions for array sorting
1823 // ----------------------------------------------------------------------------
1825 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1827 #if wxCONFIG_CASE_SENSITIVE
1828 return wxStrcmp(p1
->Name(), p2
->Name());
1830 return wxStricmp(p1
->Name(), p2
->Name());
1834 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1836 #if wxCONFIG_CASE_SENSITIVE
1837 return wxStrcmp(p1
->Name(), p2
->Name());
1839 return wxStricmp(p1
->Name(), p2
->Name());
1843 // ----------------------------------------------------------------------------
1845 // ----------------------------------------------------------------------------
1847 // undo FilterOutValue
1848 static wxString
FilterInValue(const wxString
& str
)
1851 strResult
.Alloc(str
.Len());
1853 bool bQuoted
= !str
.IsEmpty() && str
[0] == '"';
1855 for ( size_t n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1856 if ( str
[n
] == wxT('\\') ) {
1857 switch ( str
[++n
] ) {
1859 strResult
+= wxT('\n');
1863 strResult
+= wxT('\r');
1867 strResult
+= wxT('\t');
1871 strResult
+= wxT('\\');
1875 strResult
+= wxT('"');
1880 if ( str
[n
] != wxT('"') || !bQuoted
)
1881 strResult
+= str
[n
];
1882 else if ( n
!= str
.Len() - 1 ) {
1883 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1886 //else: it's the last quote of a quoted string, ok
1893 // quote the string before writing it to file
1894 static wxString
FilterOutValue(const wxString
& str
)
1900 strResult
.Alloc(str
.Len());
1902 // quoting is necessary to preserve spaces in the beginning of the string
1903 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
1906 strResult
+= wxT('"');
1909 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
1932 //else: fall through
1935 strResult
+= str
[n
];
1936 continue; // nothing special to do
1939 // we get here only for special characters
1940 strResult
<< wxT('\\') << c
;
1944 strResult
+= wxT('"');
1949 // undo FilterOutEntryName
1950 static wxString
FilterInEntryName(const wxString
& str
)
1953 strResult
.Alloc(str
.Len());
1955 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
1956 if ( *pc
== wxT('\\') )
1965 // sanitize entry or group name: insert '\\' before any special characters
1966 static wxString
FilterOutEntryName(const wxString
& str
)
1969 strResult
.Alloc(str
.Len());
1971 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
1974 // we explicitly allow some of "safe" chars and 8bit ASCII characters
1975 // which will probably never have special meaning
1976 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
1977 // should *not* be quoted
1978 if ( !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) && ((c
& 0x80) == 0) )
1979 strResult
+= wxT('\\');
1987 // we can't put ?: in the ctor initializer list because it confuses some
1988 // broken compilers (Borland C++)
1989 static wxString
GetAppName(const wxString
& appName
)
1991 if ( !appName
&& wxTheApp
)
1992 return wxTheApp
->GetAppName();
1997 #endif // wxUSE_CONFIG