+/*****************************************************************************\
+ * Project: CppLib: C++ library for Windows/UNIX platfroms *
+ * File: fileconf.h - file based implementation of Config *
+ *---------------------------------------------------------------------------*
+ * Language: C++ *
+ * Platfrom: Any *
+ *---------------------------------------------------------------------------*
+ * Classes: *
+ *---------------------------------------------------------------------------*
+ * Author: Vadim Zeitlin zeitlin@dptmaths.ens-cachan.fr> *
+ * adapted from earlier class by VZ & Karsten Ballüder *
+ * History: *
+ * 27.04.98 created *
+\*****************************************************************************/
+
+#ifndef _FILECONF_H
+#define _FILECONF_H
+
+// ----------------------------------------------------------------------------
+// wxFileConfig
+// ----------------------------------------------------------------------------
+
+/*
+ wxFileConfig derives from base Config and implements file based config class,
+ i.e. it uses ASCII disk files to store the information. These files are
+ alternatively called INI, .conf or .rc in the documentation. They are
+ organized in groups or sections, which can nest (i.e. a group contains
+ subgroups, which contain their own subgroups &c). Each group has some
+ number of entries, which are "key = value" pairs. More precisely, the format
+ is:
+
+ # comments are allowed after either ';' or '#' (Win/UNIX standard)
+
+ # blank lines (as above) are ignored
+
+ # global entries are members of special (no name) top group
+ written_for = Windows
+ platform = Linux
+
+ # the start of the group 'Foo'
+ [Foo] # may put comments like this also
+ # following 3 lines are entries
+ key = value
+ another_key = " strings with spaces in the beginning should be quoted, \
+ otherwise the spaces are lost"
+ last_key = but you don't have to put " normally (nor quote them, like here)
+
+ # subgroup of the group 'Foo'
+ # (order is not important, only the name is: separator is '/', as in paths)
+ [Foo/Bar]
+ # entries prefixed with "!" are immutable, i.e. can't be changed if they are
+ # set in the system-wide config file
+ !special_key = value
+ bar_entry = whatever
+
+ [Foo/Bar/Fubar] # depth is (theoretically :-) unlimited
+ # may have the same name as key in another section
+ bar_entry = whatever not
+
+ You have {read/write/delete}Entry functions (guess what they do) and also
+ setCurrentPath to select current group. enum{Subgroups/Entries} allow you
+ to get all entries in the config file (in the current group). Finally,
+ flush() writes immediately all changed entries to disk (otherwise it would
+ be done automatically in dtor)
+
+ wxFileConfig manages not less than 2 config files for each program: global
+ and local (or system and user if you prefer). Entries are read from both of
+ them and the local entries override the global ones unless the latter is
+ immutable (prefixed with '!') in which case a warning message is generated
+ and local value is ignored. Of course, the changes are always written to local
+ file only.
+
+ @@@@ describe environment variable expansion
+*/
+
+class wxFileConfig : public wxConfig
+{
+public:
+ // construct the "standard" full name for global (system-wide) and
+ // local (user-specific) config files from the base file name.
+ //
+ // the following are the filenames returned by this functions:
+ // global local
+ // Unix /etc/file.ext ~/.file
+ // Win %windir%\file.ext %USERPROFILE%\file.ext
+ //
+ // where file is the basename of szFile, ext is it's extension
+ // or .conf (Unix) or .ini (Win) if it has none
+ static wxString GetGlobalFileName(const char *szFile);
+ static wxString GetLocalFileName(const char *szFile);
+
+ // ctor & dtor
+ // if strGlobal is empty, only local config file is used
+ wxFileConfig(const wxString& strLocal,
+ const wxString& strGlobal = "");
+ // dtor will save unsaved data
+ virtual ~wxFileConfig();
+
+ // implement inherited pure virtual functions
+ virtual void SetPath(const wxString& strPath);
+ virtual const wxString& GetPath() const { return m_strPath; }
+
+ virtual bool GetFirstGroup(wxString& str, long& lIndex);
+ virtual bool GetNextGroup (wxString& str, long& lIndex);
+ virtual bool GetFirstEntry(wxString& str, long& lIndex);
+ virtual bool GetNextEntry (wxString& str, long& lIndex);
+
+ virtual bool Read(wxString *pstr, const char *szKey,
+ const char *szDefault = 0) const;
+ virtual const char *Read(const char *szKey,
+ const char *szDefault = 0) const;
+ virtual bool Read(long *pl, const char *szKey, long lDefault) const;
+ virtual bool Write(const char *szKey, const char *szValue);
+ virtual bool Write(const char *szKey, long lValue);
+ virtual bool Flush(bool bCurrentOnly = FALSE);
+
+ virtual bool DeleteEntry(const char *szKey, bool bGroupIfEmptyAlso);
+ virtual bool DeleteGroup(const char *szKey);
+ virtual bool DeleteAll();
+
+public:
+ // fwd decl
+ class ConfigGroup;
+ class ConfigEntry;
+
+ // we store all lines of the local config file as a linked list in memory
+ class LineList
+ {
+ public:
+ // ctor
+ LineList(const wxString& str, LineList *pNext = NULL) : m_strLine(str)
+ { SetNext(pNext); }
+
+ //
+ LineList *Next() const { return m_pNext; }
+ void SetNext(LineList *pNext) { m_pNext = pNext; }
+
+ //
+ void SetText(const wxString& str) { m_strLine = str; }
+ const wxString& Text() const { return m_strLine; }
+
+ private:
+ wxString m_strLine; // line contents
+ LineList *m_pNext; // next node
+ };
+
+ // functions to work with this list
+ LineList *LineListAppend(const wxString& str);
+ LineList *LineListInsert(const wxString& str,
+ LineList *pLine); // NULL => Append()
+ bool LineListIsEmpty();
+
+private:
+ // put the object in the initial state
+ void Init();
+
+ // parse the whole file
+ void Parse(wxTextFile& file, bool bLocal);
+
+ // the same as SetPath("/")
+ void SetRootPath();
+
+ // member variables
+ // ----------------
+ LineList *m_linesHead, // head of the linked list
+ *m_linesTail; // tail
+
+ wxString m_strLocalFile, // local file name passed to ctor
+ m_strGlobalFile; // global
+ wxString m_strPath; // current path (not '/' terminated)
+
+ ConfigGroup *m_pRootGroup, // the top (unnamed) group
+ *m_pCurrentGroup; // the current group
+
+//protected: --- if wxFileConfig::ConfigEntry is not public, functions in
+// ConfigGroup such as Find/AddEntry can't return "ConfigEntry *"
+public:
+ WX_DEFINE_ARRAY(ConfigEntry *, ArrayEntries);
+ WX_DEFINE_ARRAY(ConfigGroup *, ArrayGroups);
+
+ class ConfigEntry
+ {
+ private:
+ ConfigGroup *m_pParent; // group that contains us
+ wxString m_strName, // entry name
+ m_strValue; // value
+ bool m_bDirty, // changed since last read?
+ m_bImmutable; // can be overriden locally?
+ int m_nLine; // used if m_pLine == NULL only
+ LineList *m_pLine; // pointer to our line in the linked list
+ // or NULL if it was found in global file
+
+ public:
+ ConfigEntry(ConfigGroup *pParent, const wxString& strName, int nLine);
+
+ // simple accessors
+ const wxString& Name() const { return m_strName; }
+ const wxString& Value() const { return m_strValue; }
+ ConfigGroup *Group() const { return m_pParent; }
+ bool IsDirty() const { return m_bDirty; }
+ bool IsImmutable() const { return m_bImmutable; }
+ bool IsLocal() const { return m_pLine != 0; }
+ int Line() const { return m_nLine; }
+ LineList *GetLine() const { return m_pLine; }
+
+ // modify entry attributes
+ void SetValue(const wxString& strValue, bool bUser = TRUE);
+ void SetDirty();
+ void SetLine(LineList *pLine);
+ };
+
+protected:
+ class ConfigGroup
+ {
+ private:
+ wxFileConfig *m_pConfig; // config object we belong to
+ ConfigGroup *m_pParent; // parent group (NULL for root group)
+ ArrayEntries m_aEntries; // entries in this group
+ ArrayGroups m_aSubgroups; // subgroups
+ wxString m_strName; // group's name
+ bool m_bDirty; // if FALSE => all subgroups are not dirty
+ LineList *m_pLine; // pointer to our line in the linked list
+ int m_nLastEntry, // last here means "last added"
+ m_nLastGroup; //
+
+ public:
+ // ctor
+ ConfigGroup(ConfigGroup *pParent, const wxString& strName, wxFileConfig *);
+
+ // dtor deletes all entries and subgroups also
+ ~ConfigGroup();
+
+ // simple accessors
+ const wxString& Name() const { return m_strName; }
+ ConfigGroup *Parent() const { return m_pParent; }
+ wxFileConfig *Config() const { return m_pConfig; }
+ bool IsDirty() const { return m_bDirty; }
+
+ bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
+ const ArrayEntries& Entries() const { return m_aEntries; }
+ const ArrayGroups& Groups() const { return m_aSubgroups; }
+
+ // find entry/subgroup (NULL if not found)
+ ConfigGroup *FindSubgroup(const char *szName) const;
+ ConfigEntry *FindEntry (const char *szName) const;
+
+ // delete entry/subgroup, return FALSE if doesn't exist
+ bool DeleteSubgroup(const char *szName);
+ bool DeleteEntry(const char *szName);
+
+ // create new entry/subgroup returning pointer to newly created element
+ ConfigGroup *AddSubgroup(const wxString& strName);
+ ConfigEntry *AddEntry (const wxString& strName, int nLine = NOT_FOUND);
+
+ // will also recursively set parent's dirty flag
+ void SetDirty();
+ void SetLine(LineList *pLine);
+
+ wxString GetFullName() const;
+
+ // get the last line belonging to an entry/subgroup of this group
+ LineList *GetGroupLine();
+ LineList *GetLastEntryLine();
+ LineList *GetLastGroupLine();
+ };
+};
+
+#endif //_FILECONF_H
+