15 * You should have received a copy of the GNU General Public License |
15 * You should have received a copy of the GNU General Public License |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 */ |
17 */ |
18 |
18 |
19 #include <errno.h> |
19 #include <errno.h> |
20 #include <time.h> |
|
21 #include <QDir> |
20 #include <QDir> |
22 #include <QTextStream> |
21 #include <QTextStream> |
|
22 #include <QSettings> |
23 #include "common.h" |
23 #include "common.h" |
24 #include "config.h" |
24 #include "config.h" |
25 #include "misc.h" |
25 #include "misc.h" |
26 #include "gui.h" |
26 #include "gui.h" |
|
27 #include "file.h" |
27 |
28 |
28 config* g_configPointers[MAX_CONFIG]; |
29 config* g_configPointers[MAX_CONFIG]; |
29 static ushort g_cfgPointerCursor = 0; |
30 static ushort g_cfgPointerCursor = 0; |
30 |
31 |
31 // ============================================================================= |
32 // ============================================================================= |
|
33 static QSettings* getSettingsObject() { |
|
34 #ifdef PORTABLE |
|
35 # ifdef _WIN32 |
|
36 # define EXTENSION ".ini" |
|
37 # else |
|
38 # define EXTENSION ".cfg" |
|
39 # endif // _WIN32 |
|
40 return new QSettings (str (APPNAME).toLower() + EXTENSION, QSettings::IniFormat); |
|
41 #else |
|
42 return new QSettings; |
|
43 #endif // PORTABLE |
|
44 } |
|
45 |
|
46 // ============================================================================= |
32 // Load the configuration from file |
47 // Load the configuration from file |
33 bool config::load() { |
48 bool config::load() { |
34 print ("config::load: Loading configuration file...\n"); |
49 QSettings* settings = getSettingsObject(); |
35 print ("config::load: Path to configuration is %1\n", filepath()); |
50 print ("config::load: Loading configuration file from %1...\n", settings->fileName()); |
36 |
|
37 // Locale must be disabled for atof |
|
38 setlocale (LC_NUMERIC, "C"); |
|
39 |
|
40 File f (filepath(), File::Read); |
|
41 int ln = 0; |
|
42 |
|
43 if (!f) |
|
44 return false; |
|
45 |
|
46 // Read the values. |
|
47 for (str line : f) { |
|
48 ln++; |
|
49 |
|
50 if (line.isEmpty() || line[0] == '#') |
|
51 continue; // Empty line or comment. |
|
52 |
|
53 // Find the equals sign. |
|
54 int equals = line.indexOf ('='); |
|
55 |
|
56 if (equals == -1) { |
|
57 fprint (stderr, "couldn't find `=` sign in entry `%1`\n", line); |
|
58 continue; |
|
59 } |
|
60 |
|
61 str entry = line.left (equals); |
|
62 str valstring = line.right (line.length() - equals - 1); |
|
63 |
|
64 // Find the config entry for this. |
|
65 config* cfg = null; |
|
66 |
|
67 for (config* i : g_configPointers) { |
|
68 if (!i) |
|
69 break; |
|
70 |
|
71 if (entry == i->name) |
|
72 cfg = i; |
|
73 } |
|
74 |
|
75 if (!cfg) { |
|
76 fprint (stderr, "unknown config `%1`\n", entry); |
|
77 continue; |
|
78 } |
|
79 |
|
80 switch (cfg->getType()) { |
|
81 case Type_int: |
|
82 static_cast<intconfig*> (cfg)->value = valstring.toInt(); |
|
83 break; |
|
84 |
|
85 case Type_str: |
|
86 static_cast<strconfig*> (cfg)->value = valstring; |
|
87 break; |
|
88 |
|
89 case Type_float: |
|
90 static_cast<floatconfig*> (cfg)->value = valstring.toFloat(); |
|
91 break; |
|
92 |
|
93 case Type_bool: { |
|
94 bool& val = static_cast<boolconfig*> (cfg)->value; |
|
95 |
|
96 if (valstring.toUpper() == "TRUE" || valstring == "1") |
|
97 val = true; |
|
98 elif (valstring.toUpper() == "FALSE" || valstring == "0") |
|
99 val = false; |
|
100 |
|
101 break; |
|
102 } |
|
103 |
|
104 case Type_keyseq: |
|
105 static_cast<keyseqconfig*> (cfg)->value = keyseq::fromString (valstring); |
|
106 break; |
|
107 |
|
108 default: |
|
109 break; |
|
110 } |
|
111 } |
|
112 |
|
113 f.close(); |
|
114 return true; |
|
115 } |
|
116 |
|
117 extern_cfg (str, io_ldpath); |
|
118 |
|
119 // ============================================================================= |
|
120 // Save the configuration to disk |
|
121 bool config::save() { |
|
122 // The function will write floats, disable the locale now so that they |
|
123 // are written properly. |
|
124 setlocale (LC_NUMERIC, "C"); |
|
125 |
|
126 // If the directory doesn't exist, create it now. |
|
127 if (QDir (dirpath()).exists() == false) { |
|
128 fprint (stderr, "Creating config path %1...\n", dirpath()); |
|
129 |
|
130 if (!QDir().mkpath (dirpath())) { |
|
131 critical ("Failed to create the configuration directory. Configuration cannot be saved!\n"); |
|
132 return false; |
|
133 } |
|
134 } |
|
135 |
|
136 File f (filepath(), File::Write); |
|
137 print ("writing cfg to %1\n", filepath()); |
|
138 |
|
139 if (!f) { |
|
140 critical (fmt (QObject::tr ("Cannot save configuration, cannot open %1 for writing: %2\n"), |
|
141 filepath(), strerror (errno))); |
|
142 return false; |
|
143 } |
|
144 |
|
145 fprint (f, "# Configuration file for " APPNAME "\n"); |
|
146 str valstring; |
|
147 |
51 |
148 for (config* cfg : g_configPointers) { |
52 for (config* cfg : g_configPointers) { |
149 if (!cfg) |
53 if (!cfg) |
150 break; |
54 break; |
151 |
55 |
152 if (cfg->isDefault()) |
56 cfg->loadFromConfig (settings); |
153 continue; |
57 } |
154 |
58 |
155 switch (cfg->getType()) { |
59 settings->deleteLater(); |
156 case Type_int: |
60 return true; |
157 valstring = fmt ("%1", static_cast<intconfig*> (cfg)->value); |
61 } |
|
62 |
|
63 // ============================================================================= |
|
64 void intconfig::loadFromConfig (const QSettings* cfg) { |
|
65 QVariant val = cfg->value (name, str::number (defval)); |
|
66 value = val.toInt(); |
|
67 } |
|
68 |
|
69 void floatconfig::loadFromConfig (const QSettings* cfg) { |
|
70 QVariant val = cfg->value (name, str::number (defval)); |
|
71 value = val.toFloat(); |
|
72 } |
|
73 |
|
74 void strconfig::loadFromConfig (const QSettings* cfg) { |
|
75 QVariant val = cfg->value (name, defval); |
|
76 value = val.toString(); |
|
77 } |
|
78 |
|
79 void boolconfig::loadFromConfig (const QSettings* cfg) { |
|
80 QVariant val = cfg->value (name, str::number (defval)); |
|
81 value = val.toBool(); |
|
82 } |
|
83 |
|
84 void keyseqconfig::loadFromConfig (const QSettings* cfg) { |
|
85 QVariant val = cfg->value (name, defval.toString()); |
|
86 value = keyseq (val.toString()); |
|
87 } |
|
88 |
|
89 // ============================================================================= |
|
90 // TODO: make virtual |
|
91 str config::toString() const { |
|
92 switch (getType()) { |
|
93 case Type_int: |
|
94 return fmt ("%1", static_cast<const intconfig*> (this)->value); |
|
95 break; |
|
96 |
|
97 case Type_str: |
|
98 return static_cast<const strconfig*> (this)->value; |
|
99 break; |
|
100 |
|
101 case Type_float: |
|
102 return fmt ("%1", static_cast<const floatconfig*> (this)->value); |
|
103 break; |
|
104 |
|
105 case Type_bool: |
|
106 return (static_cast<const boolconfig*> (this)->value) ? "true" : "false"; |
|
107 break; |
|
108 |
|
109 case Type_keyseq: |
|
110 return static_cast<const keyseqconfig*> (this)->value.toString(); |
|
111 break; |
|
112 |
|
113 default: |
|
114 break; |
|
115 } |
|
116 |
|
117 return ""; |
|
118 } |
|
119 |
|
120 // ============================================================================= |
|
121 // Save the configuration to disk |
|
122 bool config::save() { |
|
123 QSettings* settings = getSettingsObject(); |
|
124 print ("Saving configuration to %1...\n", settings->fileName()); |
|
125 |
|
126 for (config* cfg : g_configPointers) { |
|
127 if (!cfg) |
158 break; |
128 break; |
159 |
129 |
160 case Type_str: |
130 settings->setValue (cfg->name, cfg->toString()); |
161 valstring = static_cast<strconfig*> (cfg)->value; |
|
162 break; |
|
163 |
|
164 case Type_float: |
|
165 valstring = fmt ("%1", static_cast<floatconfig*> (cfg)->value); |
|
166 break; |
|
167 |
|
168 case Type_bool: |
|
169 valstring = (static_cast<boolconfig*> (cfg)->value) ? "true" : "false"; |
|
170 break; |
|
171 |
|
172 case Type_keyseq: |
|
173 valstring = static_cast<keyseqconfig*> (cfg)->value.toString(); |
|
174 break; |
|
175 |
|
176 default: |
|
177 break; |
|
178 } |
|
179 |
|
180 // Write the entry now. |
|
181 fprint (f, "%1=%2\n", cfg->name, valstring); |
|
182 } |
131 } |
183 |
132 |
184 f.close(); |
133 settings->sync(); |
|
134 settings->deleteLater(); |
185 return true; |
135 return true; |
186 } |
136 } |
187 |
137 |
188 // ============================================================================= |
138 // ============================================================================= |
189 void config::reset() { |
139 void config::reset() { |
194 cfg->resetValue(); |
144 cfg->resetValue(); |
195 } |
145 } |
196 } |
146 } |
197 |
147 |
198 // ============================================================================= |
148 // ============================================================================= |
199 str config::filepath() { |
149 str config::filepath (str file) { |
200 str path = fmt ("%1%2.cfg", dirpath(), str (APPNAME).toLower()); |
150 return config::dirpath() + DIRSLASH + file; |
201 return path; |
|
202 } |
151 } |
203 |
152 |
204 // ============================================================================= |
153 // ============================================================================= |
205 str config::dirpath() { |
154 str config::dirpath() { |
206 #ifndef _WIN32 |
155 QSettings* cfg = getSettingsObject(); |
207 return fmt ("%1" DIRSLASH ".%2" DIRSLASH, |
156 return dirname (cfg->fileName()); |
208 QDir::homePath(), str (APPNAME).toLower()); |
|
209 #else |
|
210 return fmt ("%1" DIRSLASH APPNAME DIRSLASH, QDir::homePath()); |
|
211 #endif // _WIN32 |
|
212 } |
157 } |
213 |
158 |
|
159 // ============================================================================= |
|
160 str config::defaultString() const { |
|
161 str defstring = m_defstring; |
|
162 |
|
163 // String types inevitably get extra quotes in their default string due to |
|
164 // preprocessing stuff. We can only remove them now... |
|
165 if (getType() == Type_str) { |
|
166 defstring.remove (0, 1); |
|
167 defstring.chop (1); |
|
168 } |
|
169 |
|
170 return defstring; |
|
171 } |
|
172 |
|
173 // ============================================================================= |
214 void addConfig (config* ptr) { |
174 void addConfig (config* ptr) { |
215 if (g_cfgPointerCursor == 0) |
175 if (g_cfgPointerCursor == 0) |
216 memset (g_configPointers, 0, sizeof g_configPointers); |
176 memset (g_configPointers, 0, sizeof g_configPointers); |
217 |
177 |
218 assert (g_cfgPointerCursor < MAX_CONFIG); |
178 assert (g_cfgPointerCursor < MAX_CONFIG); |