1 | // Aseprite |
2 | // Copyright (C) 2018-2021 Igara Studio S.A. |
3 | // Copyright (C) 2001-2016 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/ini_file.h" |
13 | |
14 | #include "app/resource_finder.h" |
15 | #include "base/fs.h" |
16 | #include "base/split_string.h" |
17 | #include "base/string.h" |
18 | #include "cfg/cfg.h" |
19 | |
20 | #ifdef __APPLE__ |
21 | #include "os/logger.h" |
22 | #include "os/system.h" |
23 | #endif |
24 | |
25 | #ifndef _WIN32 |
26 | #include "base/fs.h" |
27 | #endif |
28 | |
29 | #include <cstdlib> |
30 | #include <vector> |
31 | |
32 | namespace app { |
33 | |
34 | using namespace gfx; |
35 | |
36 | static std::string g_configFilename; |
37 | static std::vector<cfg::CfgFile*> g_configs; |
38 | |
39 | ConfigModule::ConfigModule() |
40 | { |
41 | ResourceFinder rf; |
42 | rf.includeUserDir("aseprite.ini" ); |
43 | |
44 | // getFirstOrCreateDefault() will create the Aseprite directory |
45 | // inside the OS configuration folder (~/.config/aseprite/, etc.). |
46 | std::string fn = rf.getFirstOrCreateDefault(); |
47 | |
48 | #ifdef __APPLE__ |
49 | |
50 | // On OS X we migrate from ~/.config/aseprite/* -> "~/Library/Application Support/Aseprite/*" |
51 | if (!base::is_file(fn)) { |
52 | try { |
53 | std::string new_dir = base::get_file_path(fn); |
54 | |
55 | // Now we try to move all old configuration files into the new |
56 | // directory. |
57 | ResourceFinder old_rf; |
58 | old_rf.includeHomeDir(".config/aseprite/aseprite.ini" ); |
59 | std::string old_config_fn = old_rf.defaultFilename(); |
60 | if (base::is_file(old_config_fn)) { |
61 | std::string old_dir = base::get_file_path(old_config_fn); |
62 | for (std::string old_fn : base::list_files(old_dir)) { |
63 | std::string from = base::join_path(old_dir, old_fn); |
64 | std::string to = base::join_path(new_dir, old_fn); |
65 | base::move_file(from, to); |
66 | } |
67 | base::remove_directory(old_dir); |
68 | } |
69 | } |
70 | // Something failed |
71 | catch (const std::exception& ex) { |
72 | std::string err = "Error in configuration migration: " ; |
73 | err += ex.what(); |
74 | |
75 | auto system = os::instance(); |
76 | if (system && system->logger()) |
77 | system->logger()->logError(err.c_str()); |
78 | } |
79 | } |
80 | |
81 | #elif !defined(_WIN32) |
82 | |
83 | // On Linux we migrate the old configuration file name |
84 | // (~/.asepriterc -> ~/.config/aseprite/aseprite.ini) |
85 | { |
86 | ResourceFinder old_rf; |
87 | old_rf.includeHomeDir(".asepriterc" ); |
88 | std::string old_fn = old_rf.defaultFilename(); |
89 | if (base::is_file(old_fn)) |
90 | base::move_file(old_fn, fn); |
91 | } |
92 | |
93 | #endif |
94 | |
95 | set_config_file(fn.c_str()); |
96 | g_configFilename = fn; |
97 | } |
98 | |
99 | ConfigModule::~ConfigModule() |
100 | { |
101 | flush_config_file(); |
102 | |
103 | for (auto cfg : g_configs) |
104 | delete cfg; |
105 | g_configs.clear(); |
106 | } |
107 | |
108 | ////////////////////////////////////////////////////////////////////// |
109 | // Allegro-like API to handle .ini configuration files |
110 | |
111 | void push_config_state() |
112 | { |
113 | g_configs.push_back(new cfg::CfgFile()); |
114 | } |
115 | |
116 | void pop_config_state() |
117 | { |
118 | ASSERT(!g_configs.empty()); |
119 | |
120 | delete g_configs.back(); |
121 | g_configs.erase(--g_configs.end()); |
122 | } |
123 | |
124 | void flush_config_file() |
125 | { |
126 | ASSERT(!g_configs.empty()); |
127 | |
128 | g_configs.back()->save(); |
129 | } |
130 | |
131 | void set_config_file(const char* filename) |
132 | { |
133 | if (g_configs.empty()) |
134 | g_configs.push_back(new cfg::CfgFile()); |
135 | |
136 | g_configs.back()->load(filename); |
137 | } |
138 | |
139 | std::string main_config_filename() |
140 | { |
141 | return g_configFilename; |
142 | } |
143 | |
144 | const char* get_config_string(const char* section, const char* name, const char* value) |
145 | { |
146 | return g_configs.back()->getValue(section, name, value); |
147 | } |
148 | |
149 | void set_config_string(const char* section, const char* name, const char* value) |
150 | { |
151 | g_configs.back()->setValue(section, name, value); |
152 | } |
153 | |
154 | int get_config_int(const char* section, const char* name, int value) |
155 | { |
156 | return g_configs.back()->getIntValue(section, name, value); |
157 | } |
158 | |
159 | void set_config_int(const char* section, const char* name, int value) |
160 | { |
161 | g_configs.back()->setIntValue(section, name, value); |
162 | } |
163 | |
164 | float get_config_float(const char* section, const char* name, float value) |
165 | { |
166 | return (float)g_configs.back()->getDoubleValue(section, name, (float)value); |
167 | } |
168 | |
169 | void set_config_float(const char* section, const char* name, float value) |
170 | { |
171 | g_configs.back()->setDoubleValue(section, name, (float)value); |
172 | } |
173 | |
174 | double get_config_double(const char* section, const char* name, double value) |
175 | { |
176 | return g_configs.back()->getDoubleValue(section, name, value); |
177 | } |
178 | |
179 | void set_config_double(const char* section, const char* name, double value) |
180 | { |
181 | g_configs.back()->setDoubleValue(section, name, value); |
182 | } |
183 | |
184 | bool get_config_bool(const char* section, const char* name, bool value) |
185 | { |
186 | return g_configs.back()->getBoolValue(section, name, value); |
187 | } |
188 | |
189 | void set_config_bool(const char* section, const char* name, bool value) |
190 | { |
191 | g_configs.back()->setBoolValue(section, name, value); |
192 | } |
193 | |
194 | Point get_config_point(const char* section, const char* name, const Point& point) |
195 | { |
196 | Point point2(point); |
197 | const char* value = get_config_string(section, name, "" ); |
198 | if (value) { |
199 | std::vector<std::string> parts; |
200 | base::split_string(value, parts, " " ); |
201 | if (parts.size() == 2) { |
202 | point2.x = strtol(parts[0].c_str(), NULL, 10); |
203 | point2.y = strtol(parts[1].c_str(), NULL, 10); |
204 | } |
205 | } |
206 | return point2; |
207 | } |
208 | |
209 | void set_config_point(const char* section, const char* name, const Point& point) |
210 | { |
211 | char buf[128]; |
212 | sprintf(buf, "%d %d" , point.x, point.y); |
213 | set_config_string(section, name, buf); |
214 | } |
215 | |
216 | Size get_config_size(const char* section, const char* name, const Size& size) |
217 | { |
218 | Size size2(size); |
219 | const char* value = get_config_string(section, name, "" ); |
220 | if (value) { |
221 | std::vector<std::string> parts; |
222 | base::split_string(value, parts, " " ); |
223 | if (parts.size() == 2) { |
224 | size2.w = strtol(parts[0].c_str(), NULL, 10); |
225 | size2.h = strtol(parts[1].c_str(), NULL, 10); |
226 | } |
227 | } |
228 | return size2; |
229 | } |
230 | |
231 | void set_config_size(const char* section, const char* name, const Size& size) |
232 | { |
233 | char buf[128]; |
234 | sprintf(buf, "%d %d" , size.w, size.h); |
235 | set_config_string(section, name, buf); |
236 | } |
237 | |
238 | Rect get_config_rect(const char* section, const char* name, const Rect& rect) |
239 | { |
240 | Rect rect2(rect); |
241 | const char* value = get_config_string(section, name, "" ); |
242 | if (value) { |
243 | std::vector<std::string> parts; |
244 | base::split_string(value, parts, " " ); |
245 | if (parts.size() == 4) { |
246 | rect2.x = strtol(parts[0].c_str(), NULL, 10); |
247 | rect2.y = strtol(parts[1].c_str(), NULL, 10); |
248 | rect2.w = strtol(parts[2].c_str(), NULL, 10); |
249 | rect2.h = strtol(parts[3].c_str(), NULL, 10); |
250 | } |
251 | } |
252 | return rect2; |
253 | } |
254 | |
255 | void set_config_rect(const char* section, const char* name, const Rect& rect) |
256 | { |
257 | char buf[128]; |
258 | sprintf(buf, "%d %d %d %d" , rect.x, rect.y, rect.w, rect.h); |
259 | set_config_string(section, name, buf); |
260 | } |
261 | |
262 | app::Color get_config_color(const char* section, const char* name, const app::Color& value) |
263 | { |
264 | return app::Color::fromString(get_config_string(section, name, value.toString().c_str())); |
265 | } |
266 | |
267 | void set_config_color(const char* section, const char* name, const app::Color& value) |
268 | { |
269 | set_config_string(section, name, value.toString().c_str()); |
270 | } |
271 | |
272 | void del_config_value(const char* section, const char* name) |
273 | { |
274 | g_configs.back()->deleteValue(section, name); |
275 | } |
276 | |
277 | void del_config_section(const char* section) |
278 | { |
279 | g_configs.back()->deleteSection(section); |
280 | } |
281 | |
282 | base::paths enum_config_keys(const char* section) |
283 | { |
284 | base::paths keys; |
285 | g_configs.back()->getAllKeys(section, keys); |
286 | return keys; |
287 | } |
288 | |
289 | } // namespace app |
290 | |