Introduce Property<T> to simplify preferences system
[xonotic/netradiant.git] / radiant / preferencedictionary.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #if !defined( INCLUDED_PREFERENCEDICTIONARY_H )
23 #define INCLUDED_PREFERENCEDICTIONARY_H
24
25 #include "preferencesystem.h"
26 #include "xml/ixml.h"
27 #include "stream/stringstream.h"
28 #include "generic/callback.h"
29 #include "versionlib.h"
30 #include <map>
31
32 class PreferenceDictionary : public PreferenceSystem
33 {
34 class PreferenceEntry
35 {
36 Property<const char *> m_cb;
37 public:
38 PreferenceEntry( const Property<const char *>& cb )
39         : m_cb( cb ){
40 }
41 void importString( const char* string ){
42         m_cb.set( string );
43 }
44 void exportString( const Callback<void(const char *)> & importer ){
45         m_cb.get( importer );
46 }
47 };
48
49 typedef std::map<CopiedString, PreferenceEntry> PreferenceEntries;
50 PreferenceEntries m_preferences;
51
52 typedef std::map<CopiedString, CopiedString> PreferenceCache;
53 PreferenceCache m_cache;
54
55 public:
56 typedef PreferenceEntries::iterator iterator;
57
58 iterator begin(){
59         return m_preferences.begin();
60 }
61 iterator end(){
62         return m_preferences.end();
63 }
64 iterator find( const char* name ){
65         return m_preferences.find( name );
66 }
67
68 void registerPreference( const char* name, const Property<const char *>& cb ){
69         m_preferences.insert( PreferenceEntries::value_type( name, PreferenceEntry( cb ) ) );
70         PreferenceCache::iterator i = m_cache.find( name );
71         if ( i != m_cache.end() ) {
72                 cb.set( i->second.c_str() );
73                 m_cache.erase( i );
74         }
75 }
76
77 void importPref( const char* name, const char* value ){
78         PreferenceEntries::iterator i = m_preferences.find( name );
79         if ( i != m_preferences.end() ) {
80                 ( *i ).second.importString( value );
81         }
82         else
83         {
84                 m_cache.erase( name );
85                 m_cache.insert( PreferenceCache::value_type( name, value ) );
86         }
87 }
88 };
89
90 inline void XMLPreference_importString( XMLImporter& importer, const char* value ){
91         importer.write( value, string_length( value ) );
92 }
93 typedef ReferenceCaller<XMLImporter, void(const char*), XMLPreference_importString> XMLPreferenceImportStringCaller;
94
95 class XMLPreferenceDictionaryExporter : public XMLExporter
96 {
97 class XMLQPrefElement : public XMLElement
98 {
99 const char* m_version;
100 public:
101 XMLQPrefElement( const char* version ) : m_version( version ){
102 }
103 const char* name() const {
104         return "qpref";
105 }
106 const char* attribute( const char* name ) const {
107         if ( string_equal( name, "version" ) ) {
108                 return m_version;
109         }
110         return "";
111 }
112 void forEachAttribute( XMLAttrVisitor& visitor ) const {
113         visitor.visit( "version", m_version );
114 }
115 };
116
117 class XMLPreferenceElement : public XMLElement
118 {
119 const char* m_name;
120 public:
121 XMLPreferenceElement( const char* name )
122         : m_name( name ){
123 }
124 const char* name() const {
125         return "epair";
126 }
127 const char* attribute( const char* name ) const {
128         if ( string_equal( name, "name" ) ) {
129                 return m_name;
130         }
131         return "";
132 }
133 void forEachAttribute( XMLAttrVisitor& visitor ) const {
134         visitor.visit( "name", m_name );
135 }
136 };
137
138 typedef PreferenceDictionary PreferenceEntries;
139 PreferenceEntries& m_preferences;
140 const char* m_version;
141 public:
142 XMLPreferenceDictionaryExporter( PreferenceDictionary& preferences, const char* version )
143         : m_preferences( preferences ), m_version( version ){
144 }
145
146 void exportXML( XMLImporter& importer ){
147         importer.write( "\n", 1 );
148
149         XMLQPrefElement qpref_element( m_version );
150         importer.pushElement( qpref_element );
151         importer.write( "\n", 1 );
152
153         for ( PreferenceEntries::iterator i = m_preferences.begin(); i != m_preferences.end(); ++i )
154         {
155                 XMLPreferenceElement epair_element( ( *i ).first.c_str() );
156
157                 importer.pushElement( epair_element );
158
159                 ( *i ).second.exportString( XMLPreferenceImportStringCaller( importer ) );
160
161                 importer.popElement( epair_element.name() );
162                 importer.write( "\n", 1 );
163         }
164
165         importer.popElement( qpref_element.name() );
166         importer.write( "\n", 1 );
167 }
168 };
169
170 class XMLPreferenceDictionaryImporter : public XMLImporter
171 {
172 struct xml_state_t
173 {
174         enum ETag
175         {
176                 tag_qpref,
177                 tag_qpref_ignore,
178                 tag_epair,
179                 tag_epair_ignore
180         };
181
182         xml_state_t( ETag tag )
183                 : m_tag( tag ){
184         }
185
186         ETag m_tag;
187         CopiedString m_name;
188         StringOutputStream m_ostream;
189 };
190
191 typedef std::vector<xml_state_t> xml_stack_t;
192 xml_stack_t m_xml_stack;
193
194 typedef PreferenceDictionary PreferenceEntries;
195 PreferenceEntries& m_preferences;
196 Version m_version;
197 public:
198 XMLPreferenceDictionaryImporter( PreferenceDictionary& preferences, const char* version )
199         : m_preferences( preferences ), m_version( version_parse( version ) ){
200 }
201
202 void pushElement( const XMLElement& element ){
203         if ( m_xml_stack.empty() ) {
204                 if ( string_equal( element.name(), "qpref" ) ) {
205                         Version dataVersion( version_parse( element.attribute( "version" ) ) );
206                         if ( !version_compatible( m_version, dataVersion ) ) {
207                                 globalOutputStream() << "qpref import: data version " << dataVersion << " is not compatible with code version " << m_version << "\n";
208                                 m_xml_stack.push_back( xml_state_t::tag_qpref_ignore );
209                         }
210                         else
211                         {
212                                 globalOutputStream() << "qpref import: data version " << dataVersion << " is compatible with code version " << m_version << "\n";
213                                 m_xml_stack.push_back( xml_state_t::tag_qpref );
214                         }
215                 }
216                 else
217                 {
218                         // not valid
219                 }
220         }
221         else
222         {
223                 switch ( m_xml_stack.back().m_tag )
224                 {
225                 case xml_state_t::tag_qpref:
226                         if ( string_equal( element.name(), "epair" ) ) {
227                                 m_xml_stack.push_back( xml_state_t::tag_epair );
228                                 m_xml_stack.back().m_name = element.attribute( "name" );
229                         }
230                         else
231                         {
232                                 // not valid
233                         }
234                         break;
235                 case xml_state_t::tag_qpref_ignore:
236                         if ( string_equal( element.name(), "epair" ) ) {
237                                 m_xml_stack.push_back( xml_state_t::tag_epair_ignore );
238                         }
239                         else
240                         {
241                                 // not valid
242                         }
243                         break;
244                 case xml_state_t::tag_epair:
245                 case xml_state_t::tag_epair_ignore:
246                         // not valid
247                         break;
248                 }
249         }
250
251 }
252 void popElement( const char* name ){
253         if ( m_xml_stack.back().m_tag == xml_state_t::tag_epair ) {
254                 m_preferences.importPref( m_xml_stack.back().m_name.c_str(), m_xml_stack.back().m_ostream.c_str() );
255         }
256         m_xml_stack.pop_back();
257 }
258 std::size_t write( const char* buffer, std::size_t length ){
259         return m_xml_stack.back().m_ostream.write( buffer, length );
260 }
261 };
262
263 #endif