1 | // ==================================================================== // |
---|
2 | // // |
---|
3 | // File : AWT_config_manager.cxx // |
---|
4 | // Purpose : general interface to store/restore // |
---|
5 | // a set of related awars // |
---|
6 | // // |
---|
7 | // Coded by Ralf Westram (coder@reallysoft.de) in January 2002 // |
---|
8 | // Copyright Department of Microbiology (Technical University Munich) // |
---|
9 | // // |
---|
10 | // Visit our web site at: http://www.arb-home.de/ // |
---|
11 | // // |
---|
12 | // // |
---|
13 | // ==================================================================== // |
---|
14 | |
---|
15 | #include "awt_config_manager.hxx" |
---|
16 | #include "awt_sel_boxes.hxx" |
---|
17 | #include "awt.hxx" |
---|
18 | #include <aw_root.hxx> |
---|
19 | #include <aw_question.hxx> |
---|
20 | #include <aw_awar.hxx> |
---|
21 | #include <aw_msg.hxx> |
---|
22 | #include <aw_select.hxx> |
---|
23 | #include <arb_defs.h> |
---|
24 | #include <arb_str.h> |
---|
25 | |
---|
26 | using namespace std; |
---|
27 | |
---|
28 | // -------------------------- |
---|
29 | // AWT_configuration |
---|
30 | |
---|
31 | enum ConfigAwar { |
---|
32 | VISIBLE_COMMENT, |
---|
33 | STORED_COMMENTS, |
---|
34 | EXISTING_CFGS, |
---|
35 | CURRENT_CFG, |
---|
36 | |
---|
37 | // 'Edit' subwindow |
---|
38 | SELECTED_FIELD, |
---|
39 | FIELD_CONTENT, |
---|
40 | |
---|
41 | CONFIG_AWARS, // must be last |
---|
42 | }; |
---|
43 | |
---|
44 | bool is_prefined(const string& cfgname) { return cfgname[0] == '*'; } |
---|
45 | |
---|
46 | class ConfigDefinition : virtual Noncopyable { |
---|
47 | AW_default default_file; |
---|
48 | string id; |
---|
49 | |
---|
50 | AW_awar *std_awar[CONFIG_AWARS]; |
---|
51 | |
---|
52 | public: |
---|
53 | ConfigDefinition(AW_default default_file_, const char *id_) |
---|
54 | : default_file(default_file_), |
---|
55 | id(id_) |
---|
56 | { |
---|
57 | std_awar[VISIBLE_COMMENT] = get_awar("comment", true); |
---|
58 | std_awar[STORED_COMMENTS] = get_awar("comments"); |
---|
59 | std_awar[EXISTING_CFGS] = get_awar("existing"); |
---|
60 | std_awar[CURRENT_CFG] = get_awar("current"); |
---|
61 | std_awar[SELECTED_FIELD] = get_awar("field", true); |
---|
62 | std_awar[FIELD_CONTENT] = get_awar("content", true); |
---|
63 | } |
---|
64 | |
---|
65 | bool operator<(const ConfigDefinition& other) const { return id<other.id; } |
---|
66 | |
---|
67 | AW_default get_db() const { return default_file; } |
---|
68 | const char *get_id() const { return id.c_str(); } |
---|
69 | |
---|
70 | string get_awar_name(const string& subname, bool temp = false) const { |
---|
71 | return string("tmp/general_configs/"+(temp ? 0 : 4))+id+'/'+subname; |
---|
72 | } |
---|
73 | string get_config_dbpath(const string& cfgname) const { |
---|
74 | return get_awar_name(string("cfg_")+cfgname); |
---|
75 | } |
---|
76 | |
---|
77 | AW_awar *get_awar(const string& subname, bool temp = false) const { |
---|
78 | string awar_name = get_awar_name(subname, temp); |
---|
79 | return AW_root::SINGLETON->awar_string(awar_name.c_str(), "", default_file); |
---|
80 | } |
---|
81 | AW_awar *get_awar(ConfigAwar a) const { return std_awar[a]; } |
---|
82 | |
---|
83 | string get_awar_value(ConfigAwar a) const { return get_awar(a)->read_char_pntr(); } |
---|
84 | void set_awar_value(ConfigAwar a, const string& new_value) const { get_awar(a)->write_string(new_value.c_str()); } |
---|
85 | }; |
---|
86 | |
---|
87 | class AWT_configuration : public ConfigDefinition { // derived from Noncopyable |
---|
88 | StoreConfigCallback store; |
---|
89 | RestoreConfigCallback load_or_reset; |
---|
90 | |
---|
91 | const AWT_predefined_config *predefined; |
---|
92 | |
---|
93 | AW_window *aw_edit; |
---|
94 | AW_selection_list *field_selection; |
---|
95 | |
---|
96 | GB_ERROR update_config(const string& cfgname, const AWT_config& config); |
---|
97 | |
---|
98 | public: |
---|
99 | AWT_configuration(AW_default default_file_, const char *id_, const StoreConfigCallback& store_, const RestoreConfigCallback& load_or_reset_, const AWT_predefined_config *predef) |
---|
100 | : ConfigDefinition(default_file_, id_), |
---|
101 | store(store_), |
---|
102 | load_or_reset(load_or_reset_), |
---|
103 | predefined(predef), |
---|
104 | aw_edit(NULp), |
---|
105 | field_selection(NULp) |
---|
106 | { |
---|
107 | } |
---|
108 | |
---|
109 | string get_config(const string& cfgname) { |
---|
110 | if (is_prefined(cfgname)) { |
---|
111 | const AWT_predefined_config *predef = find_predefined(cfgname); |
---|
112 | return predef ? predef->config : ""; |
---|
113 | } |
---|
114 | else { |
---|
115 | GB_transaction ta(get_db()); |
---|
116 | GBDATA *gb_cfg = GB_search(get_db(), get_config_dbpath(cfgname).c_str(), GB_FIND); |
---|
117 | return gb_cfg ? GB_read_char_pntr(gb_cfg) : ""; |
---|
118 | } |
---|
119 | } |
---|
120 | GB_ERROR set_config(const string& cfgname, const string& new_value) { |
---|
121 | GB_ERROR error; |
---|
122 | if (is_prefined(cfgname)) { |
---|
123 | error = "cannot modify predefined config"; |
---|
124 | } |
---|
125 | else { |
---|
126 | GB_transaction ta(get_db()); |
---|
127 | GBDATA *gb_cfg = GB_search(get_db(), get_config_dbpath(cfgname).c_str(), GB_STRING); |
---|
128 | if (!gb_cfg) { |
---|
129 | error = GB_await_error(); |
---|
130 | } |
---|
131 | else { |
---|
132 | error = GB_write_string(gb_cfg, new_value.c_str()); |
---|
133 | get_awar(CURRENT_CFG)->touch(); // force refresh of config editor |
---|
134 | } |
---|
135 | } |
---|
136 | return error; |
---|
137 | } |
---|
138 | |
---|
139 | char *Store() const { return store(); } |
---|
140 | GB_ERROR Restore(const string& s) const { |
---|
141 | GB_ERROR error = NULp; |
---|
142 | |
---|
143 | if (s.empty()) error = "empty/nonexistant config"; |
---|
144 | else load_or_reset(s.c_str()); |
---|
145 | |
---|
146 | return error; |
---|
147 | } |
---|
148 | void Reset() const { |
---|
149 | load_or_reset(NULp); |
---|
150 | } |
---|
151 | |
---|
152 | GB_ERROR Save(const char* filename, const string& cfg_name, const string& comment); // AWAR content -> FILE |
---|
153 | GB_ERROR Load(const char* filename, const string& cfg_name, string& found_comment); // FILE -> AWAR content |
---|
154 | |
---|
155 | bool has_existing(string lookFor) { |
---|
156 | string S(";"); |
---|
157 | string existing = S+ get_awar_value(EXISTING_CFGS) +S; |
---|
158 | string wanted = S+ lookFor +S; |
---|
159 | |
---|
160 | return existing.find(wanted) != string::npos; |
---|
161 | } |
---|
162 | |
---|
163 | void erase_deleted_configs(); |
---|
164 | |
---|
165 | void add_predefined_to(ConstStrArray& cfgs) { |
---|
166 | if (predefined) { |
---|
167 | for (int i = 0; predefined[i].name; ++i) { |
---|
168 | awt_assert(predefined[i].name[0] == '*'); // names have to start with '*' |
---|
169 | cfgs.put(predefined[i].name); |
---|
170 | } |
---|
171 | } |
---|
172 | } |
---|
173 | const AWT_predefined_config *find_predefined(const string& cfgname) { |
---|
174 | awt_assert(is_prefined(cfgname)); |
---|
175 | for (int i = 0; predefined[i].name; ++i) { |
---|
176 | if (cfgname == predefined[i].name) { |
---|
177 | return &predefined[i]; |
---|
178 | } |
---|
179 | } |
---|
180 | return NULp; |
---|
181 | } |
---|
182 | |
---|
183 | void popup_edit_window(AW_window *aw_config); |
---|
184 | void update_field_selection_list(); |
---|
185 | void update_field_content(); |
---|
186 | void store_changed_field_content(); |
---|
187 | void delete_selected_field(); |
---|
188 | void keep_changed_fields(); |
---|
189 | }; |
---|
190 | |
---|
191 | #define HEADER "ARB_CONFIGURATION" |
---|
192 | #define HEADERLEN 17 |
---|
193 | |
---|
194 | GB_ERROR AWT_configuration::Save(const char* filename, const string& cfg_name, const string& comment) { |
---|
195 | awt_assert(strlen(HEADER) == HEADERLEN); |
---|
196 | |
---|
197 | printf("Saving config to '%s'..\n", filename); |
---|
198 | |
---|
199 | FILE *out = fopen(filename, "wt"); |
---|
200 | GB_ERROR error = NULp; |
---|
201 | if (!out) { |
---|
202 | error = GB_IO_error("saving", filename); |
---|
203 | } |
---|
204 | else { |
---|
205 | if (comment.empty()) { |
---|
206 | fprintf(out, HEADER ":%s\n", get_id()); // =same as old format |
---|
207 | } |
---|
208 | else { |
---|
209 | string encoded_comment(comment); |
---|
210 | ConfigMapping::encode_escapes(encoded_comment, ""); |
---|
211 | fprintf(out, HEADER ":%s;%s\n", get_id(), encoded_comment.c_str()); |
---|
212 | } |
---|
213 | |
---|
214 | string content = get_config(cfg_name); |
---|
215 | fputs(content.c_str(), out); |
---|
216 | fclose(out); |
---|
217 | } |
---|
218 | return error; |
---|
219 | } |
---|
220 | |
---|
221 | GB_ERROR AWT_configuration::Load(const char* filename, const string& cfg_name, string& found_comment) { |
---|
222 | GB_ERROR error = NULp; |
---|
223 | |
---|
224 | found_comment = ""; |
---|
225 | printf("Loading config from '%s'..\n", filename); |
---|
226 | |
---|
227 | char *content = GB_read_file(filename); |
---|
228 | if (!content) { |
---|
229 | error = GB_await_error(); |
---|
230 | } |
---|
231 | else { |
---|
232 | if (strncmp(content, HEADER ":", HEADERLEN+1) != 0) { |
---|
233 | error = "Unexpected content (" HEADER " missing)"; |
---|
234 | } |
---|
235 | else { |
---|
236 | char *id_pos = content+HEADERLEN+1; |
---|
237 | char *nl = strchr(id_pos, '\n'); |
---|
238 | |
---|
239 | if (!nl) { |
---|
240 | error = "Unexpected content (no ID)"; |
---|
241 | } |
---|
242 | else { |
---|
243 | *nl++ = 0; |
---|
244 | |
---|
245 | char *comment = strchr(id_pos, ';'); |
---|
246 | if (comment) *comment++ = 0; |
---|
247 | |
---|
248 | if (strcmp(id_pos, get_id()) != 0) { |
---|
249 | error = GBS_global_string("Wrong config type (expected=%s, found=%s)", get_id(), id_pos); |
---|
250 | } |
---|
251 | else { |
---|
252 | error = set_config(cfg_name, nl); |
---|
253 | if (comment && !error) { |
---|
254 | found_comment = comment; |
---|
255 | error = ConfigMapping::decode_escapes(found_comment); |
---|
256 | } |
---|
257 | } |
---|
258 | } |
---|
259 | } |
---|
260 | |
---|
261 | if (error) { |
---|
262 | error = GBS_global_string("Error: %s (while reading %s)", error, filename); |
---|
263 | } |
---|
264 | |
---|
265 | free(content); |
---|
266 | } |
---|
267 | |
---|
268 | return error; |
---|
269 | } |
---|
270 | |
---|
271 | void AWT_configuration::erase_deleted_configs() { |
---|
272 | string cfg_base = get_awar_name("", false); |
---|
273 | GB_ERROR error = NULp; |
---|
274 | { |
---|
275 | GB_transaction ta(get_db()); |
---|
276 | GBDATA *gb_base = GB_search(get_db(), cfg_base.c_str(), GB_FIND); |
---|
277 | if (gb_base) { |
---|
278 | for (GBDATA *gb_cfg = GB_child(gb_base); gb_cfg && !error; ) { |
---|
279 | GBDATA *gb_next = GB_nextChild(gb_cfg); |
---|
280 | const char *key = GB_read_key_pntr(gb_cfg); |
---|
281 | if (key && ARB_strBeginsWith(key, "cfg_")) { |
---|
282 | const char *name = key+4; |
---|
283 | if (!has_existing(name)) { |
---|
284 | error = GB_delete(gb_cfg); |
---|
285 | } |
---|
286 | } |
---|
287 | gb_cfg = gb_next; |
---|
288 | } |
---|
289 | } |
---|
290 | } |
---|
291 | aw_message_if(error); |
---|
292 | } |
---|
293 | |
---|
294 | void remove_from_configs(const string& config, string& existing_configs) { |
---|
295 | ConstStrArray cfgs; |
---|
296 | GBT_split_string(cfgs, existing_configs.c_str(), ';'); |
---|
297 | |
---|
298 | ConstStrArray remaining; |
---|
299 | for (int i = 0; cfgs[i]; ++i) { |
---|
300 | if (strcmp(cfgs[i], config.c_str()) != 0) { |
---|
301 | remaining.put(cfgs[i]); |
---|
302 | } |
---|
303 | } |
---|
304 | |
---|
305 | char *rest = GBT_join_strings(remaining, ';'); |
---|
306 | existing_configs = rest; |
---|
307 | free(rest); |
---|
308 | } |
---|
309 | |
---|
310 | #define NO_CONFIG_SELECTED "<no config selected>" |
---|
311 | |
---|
312 | static void current_changed_cb(AW_root*, AWT_configuration *config) { |
---|
313 | AW_awar *awar_current = config->get_awar(CURRENT_CFG); |
---|
314 | AW_awar *awar_comment = config->get_awar(VISIBLE_COMMENT); |
---|
315 | |
---|
316 | // convert name to key (but allow empty string and strings starting with '*') |
---|
317 | string name; |
---|
318 | { |
---|
319 | const char *entered_name = awar_current->read_char_pntr(); |
---|
320 | if (entered_name[0]) { |
---|
321 | bool isPredefined = is_prefined(entered_name); |
---|
322 | char *asKey = GBS_string_2_key(entered_name); |
---|
323 | name = isPredefined ? string(1, '*')+asKey : asKey; |
---|
324 | free(asKey); |
---|
325 | } |
---|
326 | else { |
---|
327 | name = ""; |
---|
328 | } |
---|
329 | } |
---|
330 | |
---|
331 | awar_current->write_string(name.c_str()); |
---|
332 | |
---|
333 | // refresh comment field |
---|
334 | if (name[0]) { // cfg not empty |
---|
335 | if (config->has_existing(name)) { // load comment of existing config |
---|
336 | string storedComments = config->get_awar_value(STORED_COMMENTS); |
---|
337 | AWT_config comments(storedComments.c_str()); |
---|
338 | |
---|
339 | const char *display; |
---|
340 | if (comments.parseError()) { |
---|
341 | display = GBS_global_string("Error reading config comments:\n%s", comments.parseError()); |
---|
342 | } |
---|
343 | else { |
---|
344 | const char *saved_comment = comments.get_entry(name.c_str()); |
---|
345 | display = null2empty(saved_comment); |
---|
346 | } |
---|
347 | awar_comment->write_string(display); |
---|
348 | } |
---|
349 | else if (is_prefined(name)) { |
---|
350 | const AWT_predefined_config *found = config->find_predefined(name); |
---|
351 | awar_comment->write_string(found ? found->description : NO_CONFIG_SELECTED); |
---|
352 | } |
---|
353 | else { // new config (not stored yet) |
---|
354 | // click <new> and enter name -> clear comment |
---|
355 | // click existing and change name -> reuse existing comment |
---|
356 | if (strcmp(awar_comment->read_char_pntr(), NO_CONFIG_SELECTED) == 0) { |
---|
357 | awar_comment->write_string(""); |
---|
358 | } |
---|
359 | } |
---|
360 | } |
---|
361 | else { // no config selected |
---|
362 | awar_comment->write_string(NO_CONFIG_SELECTED); |
---|
363 | } |
---|
364 | |
---|
365 | // refresh field selection list + content field |
---|
366 | config->update_field_selection_list(); |
---|
367 | } |
---|
368 | |
---|
369 | inline void save_comments(const AWT_config& comments, AWT_configuration *config) { |
---|
370 | char *comments_string = comments.config_string(); |
---|
371 | config->set_awar_value(STORED_COMMENTS, comments_string); |
---|
372 | free(comments_string); |
---|
373 | } |
---|
374 | |
---|
375 | static void comment_changed_cb(AW_root*, AWT_configuration *config) { |
---|
376 | string curr_cfg = config->get_awar_value(CURRENT_CFG); |
---|
377 | if (!curr_cfg.empty()) { |
---|
378 | string changed_comment = config->get_awar_value(VISIBLE_COMMENT); |
---|
379 | if (is_prefined(curr_cfg)) { |
---|
380 | const AWT_predefined_config *found = config->find_predefined(curr_cfg); |
---|
381 | if (found && changed_comment != found->description) { |
---|
382 | aw_message("The description of predefined configs is immutable"); |
---|
383 | config->get_awar(CURRENT_CFG)->touch(); // reload comment |
---|
384 | } |
---|
385 | } |
---|
386 | else if (config->has_existing(curr_cfg)) { |
---|
387 | AWT_config comments(config->get_awar_value(STORED_COMMENTS).c_str()); |
---|
388 | if (comments.parseError()) { |
---|
389 | aw_message(GBS_global_string("Failed to parse config-comments (%s)", comments.parseError())); |
---|
390 | } |
---|
391 | else { |
---|
392 | if (changed_comment.empty()) { |
---|
393 | comments.delete_entry(curr_cfg.c_str()); |
---|
394 | } |
---|
395 | else { |
---|
396 | comments.set_entry(curr_cfg.c_str(), changed_comment.c_str()); |
---|
397 | } |
---|
398 | save_comments(comments, config); |
---|
399 | } |
---|
400 | } |
---|
401 | } |
---|
402 | } |
---|
403 | static void erase_comment_cb(AW_window*, AW_awar *awar_comment) { |
---|
404 | awar_comment->write_string(""); |
---|
405 | } |
---|
406 | |
---|
407 | static void restore_cb(AW_window *, AWT_configuration *config) { |
---|
408 | string cfgName = config->get_awar_value(CURRENT_CFG); |
---|
409 | GB_ERROR error; |
---|
410 | if (cfgName.empty()) { |
---|
411 | error = "Please select config to restore"; |
---|
412 | } |
---|
413 | else { |
---|
414 | error = config->Restore(config->get_config(cfgName)); |
---|
415 | } |
---|
416 | aw_message_if(error); |
---|
417 | } |
---|
418 | static void store_cb(AW_window *, AWT_configuration *config) { |
---|
419 | string cfgName = config->get_awar_value(CURRENT_CFG); |
---|
420 | if (cfgName.empty()) aw_message("Please select or enter name of config to store"); |
---|
421 | else if (is_prefined(cfgName)) aw_message("You can't modify predefined configs"); |
---|
422 | else { |
---|
423 | string existing = config->get_awar_value(EXISTING_CFGS); |
---|
424 | |
---|
425 | AW_awar *awar_comment = config->get_awar(VISIBLE_COMMENT); |
---|
426 | string visibleComment(awar_comment->read_char_pntr()); |
---|
427 | |
---|
428 | remove_from_configs(cfgName, existing); // remove selected from existing configs |
---|
429 | |
---|
430 | existing = existing.empty() ? cfgName : (string(cfgName)+';'+existing); |
---|
431 | { |
---|
432 | char *cfgStr = config->Store(); |
---|
433 | GB_ERROR error = config->set_config(cfgName, cfgStr); |
---|
434 | aw_message_if(error); |
---|
435 | free(cfgStr); |
---|
436 | } |
---|
437 | config->set_awar_value(EXISTING_CFGS, existing); |
---|
438 | awar_comment->rewrite_string(visibleComment.c_str()); // force new config to use last visible comment |
---|
439 | |
---|
440 | config->get_awar(CURRENT_CFG)->touch(); // force refresh of config editor |
---|
441 | } |
---|
442 | } |
---|
443 | static void delete_cb(AW_window *, AWT_configuration *config) { |
---|
444 | string cfgName = config->get_awar_value(CURRENT_CFG); |
---|
445 | if (is_prefined(cfgName)) { |
---|
446 | aw_message("You may not delete predefined configs"); |
---|
447 | } |
---|
448 | else { |
---|
449 | string existing = config->get_awar_value(EXISTING_CFGS); |
---|
450 | remove_from_configs(cfgName, existing); // remove selected from existing configs |
---|
451 | config->set_awar_value(CURRENT_CFG, ""); |
---|
452 | config->set_awar_value(EXISTING_CFGS, existing); |
---|
453 | |
---|
454 | // erase existing comment: |
---|
455 | AWT_config comments(config->get_awar_value(STORED_COMMENTS).c_str()); |
---|
456 | comments.delete_entry(cfgName.c_str()); |
---|
457 | save_comments(comments, config); |
---|
458 | |
---|
459 | config->erase_deleted_configs(); |
---|
460 | } |
---|
461 | } |
---|
462 | static void load_cb(AW_window *, AWT_configuration *config) { |
---|
463 | string cfgName = config->get_awar_value(CURRENT_CFG); |
---|
464 | GB_ERROR error = NULp; |
---|
465 | |
---|
466 | if (cfgName.empty()) error = "Please enter or select target config"; |
---|
467 | else if (is_prefined(cfgName)) error = "You may not load over a predefined config"; |
---|
468 | else { |
---|
469 | char *loadMask = GBS_global_string_copy("%s_*", config->get_id()); |
---|
470 | char *filename = aw_file_selection("Load config from file", "$(ARBCONFIG)", loadMask, ".arbcfg"); |
---|
471 | if (filename) { |
---|
472 | string comment; |
---|
473 | |
---|
474 | error = config->Load(filename, cfgName, comment); |
---|
475 | if (!error) { |
---|
476 | // after successful load restore and store config |
---|
477 | restore_cb(NULp, config); |
---|
478 | store_cb(NULp, config); |
---|
479 | config->set_awar_value(VISIBLE_COMMENT, comment); |
---|
480 | } |
---|
481 | free(filename); |
---|
482 | } |
---|
483 | free(loadMask); |
---|
484 | } |
---|
485 | aw_message_if(error); |
---|
486 | } |
---|
487 | static void save_cb(AW_window *, AWT_configuration *config) { |
---|
488 | string cfgName = config->get_awar_value(CURRENT_CFG); |
---|
489 | GB_ERROR error = NULp; |
---|
490 | |
---|
491 | if (cfgName.empty()) error = "Please select config to save"; |
---|
492 | else { |
---|
493 | char *saveAs = GBS_global_string_copy("%s_%s", |
---|
494 | config->get_id(), |
---|
495 | cfgName.c_str() + (cfgName[0] == '*')); // skip leading '*' |
---|
496 | |
---|
497 | char *filename = aw_file_selection("Save config to file", "$(ARBCONFIG)", saveAs, ".arbcfg"); |
---|
498 | if (filename && filename[0]) { |
---|
499 | restore_cb(NULp, config); |
---|
500 | string comment = config->get_awar_value(VISIBLE_COMMENT); |
---|
501 | error = config->Save(filename, cfgName, comment); |
---|
502 | free(filename); |
---|
503 | } |
---|
504 | free(saveAs); |
---|
505 | } |
---|
506 | aw_message_if(error); |
---|
507 | } |
---|
508 | |
---|
509 | #if defined(DEBUG) |
---|
510 | |
---|
511 | static string esc(const string& str) { |
---|
512 | // escape C string |
---|
513 | |
---|
514 | char *escaped = GBS_string_eval(str.c_str(), "\\\\=\\\\\\\\:\"=\\\\\":\\n=\\\\n:\\t=\\\\t"); |
---|
515 | // unescaped once by compiler and once by SRT interpreter |
---|
516 | // results in SRT: '\=\\:"=\":<lf>=\n:<tab>=\t' |
---|
517 | |
---|
518 | string result(escaped); |
---|
519 | free(escaped); |
---|
520 | return result; |
---|
521 | } |
---|
522 | |
---|
523 | static void dump_cb(AW_window *, AWT_configuration *config) { |
---|
524 | // dump code ready to insert into AWT_predefined_config |
---|
525 | string cfgName = config->get_awar_value(CURRENT_CFG); |
---|
526 | GB_ERROR error = NULp; |
---|
527 | |
---|
528 | if (cfgName.empty()) error = "Please select config to dump"; |
---|
529 | else { |
---|
530 | string comment = esc(config->get_awar_value(VISIBLE_COMMENT)); |
---|
531 | string confStr = esc(config->get_config(cfgName)); |
---|
532 | |
---|
533 | cfgName = esc(cfgName); |
---|
534 | const char *cfg = cfgName.c_str(); |
---|
535 | |
---|
536 | fprintf(stderr, " { \"*%s\", \"%s\", \"%s\" },\n", |
---|
537 | cfg[0] == '*' ? cfg+1 : cfg, |
---|
538 | comment.c_str(), |
---|
539 | confStr.c_str()); |
---|
540 | } |
---|
541 | aw_message_if(error); |
---|
542 | } |
---|
543 | #endif |
---|
544 | |
---|
545 | |
---|
546 | void AWT_configuration::update_field_selection_list() { |
---|
547 | if (field_selection) { |
---|
548 | string cfgName = get_awar_value(CURRENT_CFG); |
---|
549 | char *selected = get_awar(SELECTED_FIELD)->read_string(); |
---|
550 | bool seenSelected = false; |
---|
551 | |
---|
552 | field_selection->clear(); |
---|
553 | if (!cfgName.empty() && has_existing(cfgName)) { |
---|
554 | string configString = get_config(cfgName); |
---|
555 | AWT_config stored(configString.c_str()); |
---|
556 | ConstStrArray entries; |
---|
557 | stored.get_entries(entries); |
---|
558 | |
---|
559 | StrArray entries_with_content; |
---|
560 | size_t maxlen = 0; |
---|
561 | for (size_t e = 0; e<entries.size(); ++e) { |
---|
562 | maxlen = std::max(maxlen, strlen(entries[e])); |
---|
563 | if (strcmp(selected, entries[e]) == 0) seenSelected = true; |
---|
564 | } |
---|
565 | for (size_t e = 0; e<entries.size(); ++e) { |
---|
566 | field_selection->insert(GBS_global_string("%-*s | %s", |
---|
567 | int(maxlen), entries[e], |
---|
568 | stored.get_entry(entries[e])), |
---|
569 | entries[e]); |
---|
570 | } |
---|
571 | } |
---|
572 | field_selection->insert_default("", ""); |
---|
573 | field_selection->update(); |
---|
574 | |
---|
575 | if (!seenSelected) { |
---|
576 | get_awar(SELECTED_FIELD)->write_string(""); |
---|
577 | } |
---|
578 | else { |
---|
579 | get_awar(SELECTED_FIELD)->touch(); |
---|
580 | } |
---|
581 | free(selected); |
---|
582 | } |
---|
583 | } |
---|
584 | |
---|
585 | void AWT_configuration::update_field_content() { |
---|
586 | string cfgName = get_awar_value(CURRENT_CFG); |
---|
587 | string content = "<select a field below>"; |
---|
588 | if (!cfgName.empty() && has_existing(cfgName)) { |
---|
589 | string selected = get_awar_value(SELECTED_FIELD); |
---|
590 | if (!selected.empty()) { |
---|
591 | string configString = get_config(cfgName); |
---|
592 | AWT_config stored(configString.c_str()); |
---|
593 | |
---|
594 | if (stored.has_entry(selected.c_str())) { |
---|
595 | content = stored.get_entry(selected.c_str()); |
---|
596 | } |
---|
597 | else { |
---|
598 | content = GBS_global_string("<field '%s' not stored in config>", selected.c_str()); |
---|
599 | } |
---|
600 | } |
---|
601 | } |
---|
602 | set_awar_value(FIELD_CONTENT, content.c_str()); |
---|
603 | } |
---|
604 | |
---|
605 | GB_ERROR AWT_configuration::update_config(const string& cfgname, const AWT_config& config) { |
---|
606 | char *changedConfigString = config.config_string(); |
---|
607 | GB_ERROR error = set_config(cfgname, changedConfigString); |
---|
608 | free(changedConfigString); |
---|
609 | return error; |
---|
610 | } |
---|
611 | |
---|
612 | void AWT_configuration::store_changed_field_content() { |
---|
613 | string cfgName = get_awar_value(CURRENT_CFG); |
---|
614 | if (!cfgName.empty() && has_existing(cfgName)) { |
---|
615 | string selected = get_awar_value(SELECTED_FIELD); |
---|
616 | if (!selected.empty()) { |
---|
617 | string configString = get_config(cfgName); |
---|
618 | AWT_config stored(configString.c_str()); |
---|
619 | if (stored.has_entry(selected.c_str())) { |
---|
620 | string stored_content = stored.get_entry(selected.c_str()); |
---|
621 | string changed_content = get_awar_value(FIELD_CONTENT); |
---|
622 | |
---|
623 | if (stored_content != changed_content) { |
---|
624 | stored.set_entry(selected.c_str(), changed_content.c_str()); |
---|
625 | aw_message_if(update_config(cfgName, stored)); |
---|
626 | } |
---|
627 | } |
---|
628 | } |
---|
629 | } |
---|
630 | } |
---|
631 | |
---|
632 | void AWT_configuration::delete_selected_field() { |
---|
633 | string cfgName = get_awar_value(CURRENT_CFG); |
---|
634 | if (!cfgName.empty() && has_existing(cfgName)) { |
---|
635 | string selected = get_awar_value(SELECTED_FIELD); |
---|
636 | if (!selected.empty()) { |
---|
637 | string configString = get_config(cfgName); |
---|
638 | AWT_config stored(configString.c_str()); |
---|
639 | if (stored.has_entry(selected.c_str())) { |
---|
640 | stored.delete_entry(selected.c_str()); |
---|
641 | aw_message_if(update_config(cfgName, stored)); |
---|
642 | field_selection->move_selection(1); |
---|
643 | update_field_selection_list(); |
---|
644 | } |
---|
645 | } |
---|
646 | } |
---|
647 | } |
---|
648 | |
---|
649 | void AWT_configuration::keep_changed_fields() { |
---|
650 | string cfgName = get_awar_value(CURRENT_CFG); |
---|
651 | if (!cfgName.empty() && has_existing(cfgName)) { |
---|
652 | string configString = get_config(cfgName); |
---|
653 | AWT_config stored(configString.c_str()); |
---|
654 | |
---|
655 | char *current_state = Store(); |
---|
656 | AWT_config current(current_state); |
---|
657 | |
---|
658 | ConstStrArray entries; |
---|
659 | stored.get_entries(entries); |
---|
660 | int deleted = 0; |
---|
661 | |
---|
662 | for (size_t e = 0; e<entries.size(); ++e) { |
---|
663 | const char *entry = entries[e]; |
---|
664 | const char *stored_content = stored.get_entry(entry); |
---|
665 | |
---|
666 | if (current.has_entry(entry)) { |
---|
667 | const char *current_content = current.get_entry(entry); |
---|
668 | if (strcmp(stored_content, current_content) == 0) { |
---|
669 | stored.delete_entry(entry); |
---|
670 | deleted++; |
---|
671 | } |
---|
672 | } |
---|
673 | else { |
---|
674 | aw_message(GBS_global_string("Entry '%s' is not (or no longer) supported", entry)); |
---|
675 | } |
---|
676 | } |
---|
677 | |
---|
678 | if (deleted) { |
---|
679 | aw_message_if(update_config(cfgName, stored)); |
---|
680 | update_field_selection_list(); |
---|
681 | } |
---|
682 | else { |
---|
683 | aw_message("All entries differ from current state"); |
---|
684 | } |
---|
685 | |
---|
686 | free(current_state); |
---|
687 | } |
---|
688 | } |
---|
689 | |
---|
690 | static void keep_changed_fields_cb(AW_window*, AWT_configuration *config) { config->keep_changed_fields(); } |
---|
691 | static void delete_field_cb(AW_window*, AWT_configuration *config) { config->delete_selected_field(); } |
---|
692 | static void selected_field_changed_cb(AW_root*, AWT_configuration *config) { config->update_field_content(); } |
---|
693 | static void field_content_changed_cb(AW_root*, AWT_configuration *config) { config->store_changed_field_content(); } |
---|
694 | |
---|
695 | void AWT_configuration::popup_edit_window(AW_window *aw_config) { |
---|
696 | if (!aw_edit) { |
---|
697 | AW_root *root = aw_config->get_root(); |
---|
698 | AW_window_simple *aws = new AW_window_simple; |
---|
699 | { |
---|
700 | char *wid = GBS_global_string_copy("%s_edit", aw_config->get_window_id()); |
---|
701 | aws->init(root, wid, "Edit configuration entries"); |
---|
702 | free(wid); |
---|
703 | } |
---|
704 | aws->load_xfig("awt/edit_config.fig"); |
---|
705 | |
---|
706 | aws->at("close"); |
---|
707 | aws->callback(AW_POPDOWN); |
---|
708 | aws->create_button("CLOSE", "CLOSE"); |
---|
709 | |
---|
710 | aws->at("help"); |
---|
711 | aws->callback(makeHelpCallback("prop_configs_edit.hlp")); |
---|
712 | aws->create_button("HELP", "HELP"); |
---|
713 | |
---|
714 | aws->at("content"); |
---|
715 | aws->create_input_field(get_awar(FIELD_CONTENT)->awar_name); |
---|
716 | |
---|
717 | aws->at("name"); |
---|
718 | aws->create_button(NULp, get_awar(CURRENT_CFG)->awar_name, NULp, "+"); |
---|
719 | |
---|
720 | aws->at("entries"); |
---|
721 | field_selection = aws->create_selection_list(get_awar(SELECTED_FIELD)->awar_name, true); |
---|
722 | |
---|
723 | aws->auto_space(0, 3); |
---|
724 | aws->button_length(10); |
---|
725 | aws->at("button"); |
---|
726 | |
---|
727 | int xpos = aws->get_at_xposition(); |
---|
728 | int ypos = aws->get_at_yposition(); |
---|
729 | |
---|
730 | aws->callback(makeWindowCallback(delete_field_cb, this)); |
---|
731 | aws->create_button("DELETE", "Delete\nselected\nentry", "D"); |
---|
732 | |
---|
733 | aws->at_newline(); |
---|
734 | ypos = aws->get_at_yposition(); |
---|
735 | aws->at("button"); |
---|
736 | aws->at(xpos, ypos); |
---|
737 | |
---|
738 | aws->callback(makeWindowCallback(keep_changed_fields_cb, this)); |
---|
739 | aws->create_button("KEEP_CHANGED", "Keep only\nentries\ndiffering\nfrom\ncurrent\nstate", "K"); |
---|
740 | |
---|
741 | aw_edit = aws; |
---|
742 | |
---|
743 | // bind callbacks to awars |
---|
744 | get_awar(SELECTED_FIELD)->add_callback(makeRootCallback(selected_field_changed_cb, this)); |
---|
745 | get_awar(FIELD_CONTENT)->add_callback(makeRootCallback(field_content_changed_cb, this)); |
---|
746 | |
---|
747 | // fill selection list |
---|
748 | update_field_selection_list(); |
---|
749 | } |
---|
750 | |
---|
751 | aw_edit->activate(); |
---|
752 | } |
---|
753 | |
---|
754 | static void edit_cb(AW_window *aww, AWT_configuration *config) { config->popup_edit_window(aww); } |
---|
755 | static void reset_cb(AW_window *, AWT_configuration *config) { config->Reset(); } |
---|
756 | |
---|
757 | static void get_existing_configs(ConfigDefinition& configDef, ConstStrArray& cfgs) { |
---|
758 | string cfgs_str = configDef.get_awar_value(EXISTING_CFGS); |
---|
759 | GBT_split_string(cfgs, cfgs_str.c_str(), ';'); |
---|
760 | } |
---|
761 | |
---|
762 | static void refresh_config_sellist_cb(AW_root*, AWT_configuration *config, AW_selection_list *sel) { |
---|
763 | ConstStrArray cfgs; |
---|
764 | get_existing_configs(*config, cfgs); |
---|
765 | |
---|
766 | config->add_predefined_to(cfgs); |
---|
767 | sel->init_from_array(cfgs, "<new>", ""); |
---|
768 | } |
---|
769 | |
---|
770 | static AW_window *create_config_manager_window(AW_root *, AWT_configuration *config, AW_window *aww) { |
---|
771 | AW_window_simple *aws = new AW_window_simple; |
---|
772 | |
---|
773 | char *title = GBS_global_string_copy("Configurations for '%s'", aww->get_window_title()); |
---|
774 | char *id = GBS_global_string_copy("%s_config", aww->get_window_id()); |
---|
775 | |
---|
776 | aws->init(aww->get_root(), id, title); |
---|
777 | aws->load_xfig("awt/manage_config.fig"); |
---|
778 | |
---|
779 | aws->at("close"); |
---|
780 | aws->callback(AW_POPDOWN); |
---|
781 | aws->create_button("CLOSE", "CLOSE"); |
---|
782 | |
---|
783 | aws->at("help"); |
---|
784 | aws->callback(makeHelpCallback("prop_configs.hlp")); |
---|
785 | aws->create_button("HELP", "HELP"); |
---|
786 | |
---|
787 | // create awars |
---|
788 | AW_awar *awar_existing = config->get_awar(EXISTING_CFGS); |
---|
789 | AW_awar *awar_current = config->get_awar(CURRENT_CFG); |
---|
790 | AW_awar *awar_comment = config->get_awar(VISIBLE_COMMENT); |
---|
791 | |
---|
792 | aws->at("comment"); |
---|
793 | aws->create_text_field(awar_comment->awar_name); |
---|
794 | |
---|
795 | aws->at("clr"); |
---|
796 | aws->callback(makeWindowCallback(erase_comment_cb, awar_comment)); |
---|
797 | aws->create_autosize_button("erase", "Erase", "E"); |
---|
798 | |
---|
799 | awar_current->add_callback(makeRootCallback(current_changed_cb, config)); |
---|
800 | awar_comment->add_callback(makeRootCallback(comment_changed_cb, config)); |
---|
801 | |
---|
802 | AW_selection_list *sel = awt_create_selection_list_with_input_field(aws, awar_current->awar_name, "cfgs", "name"); |
---|
803 | |
---|
804 | awar_existing->add_callback(makeRootCallback(refresh_config_sellist_cb, config, sel)); |
---|
805 | awar_existing->touch(); // fills selection list |
---|
806 | awar_current->touch(); // initialized comment textbox |
---|
807 | |
---|
808 | aws->auto_space(0, 3); |
---|
809 | aws->button_length(10); |
---|
810 | aws->at("button"); |
---|
811 | |
---|
812 | int xpos = aws->get_at_xposition(); |
---|
813 | int ypos = aws->get_at_yposition(); |
---|
814 | |
---|
815 | struct but { |
---|
816 | void (*cb)(AW_window*, AWT_configuration*); |
---|
817 | const char *id; |
---|
818 | const char *label; |
---|
819 | const char *mnemonic; |
---|
820 | } butDef[] = { |
---|
821 | { restore_cb, "RESTORE", "Restore", "R" }, |
---|
822 | { store_cb, "STORE", "Store", "S" }, |
---|
823 | { delete_cb, "DELETE", "Delete", "D" }, |
---|
824 | { load_cb, "LOAD", "Load", "L" }, |
---|
825 | { save_cb, "SAVE", "Save", "v" }, |
---|
826 | { reset_cb, "RESET", "Factory\ndefaults", "F" }, |
---|
827 | { edit_cb, "EDIT", "Edit", "E" }, |
---|
828 | #if defined(DEBUG) |
---|
829 | { dump_cb, "DUMP", "dump\npredef", "U" }, |
---|
830 | #endif |
---|
831 | }; |
---|
832 | const int buttons = ARRAY_ELEMS(butDef); |
---|
833 | for (int b = 0; b<buttons; ++b) { |
---|
834 | const but& B = butDef[b]; |
---|
835 | |
---|
836 | if (b>0) { |
---|
837 | aws->at("button"); |
---|
838 | aws->at(xpos, ypos); |
---|
839 | } |
---|
840 | |
---|
841 | aws->callback(makeWindowCallback(B.cb, config)); |
---|
842 | aws->create_button(B.id, B.label, B.mnemonic); |
---|
843 | |
---|
844 | aws->at_newline(); |
---|
845 | ypos = aws->get_at_yposition(); |
---|
846 | } |
---|
847 | |
---|
848 | free(id); |
---|
849 | free(title); |
---|
850 | |
---|
851 | return aws; |
---|
852 | } |
---|
853 | |
---|
854 | static void destroy_AWT_configuration(AWT_configuration *c, AW_window*) { delete c; } |
---|
855 | |
---|
856 | void AWT_insert_config_manager(AW_window *aww, AW_default default_file_, const char *id, const StoreConfigCallback& store_cb, |
---|
857 | const RestoreConfigCallback& load_or_reset_cb, const char *macro_id, const AWT_predefined_config *predef) |
---|
858 | { |
---|
859 | /*! inserts a config-button into aww |
---|
860 | * @param default_file_ db where configs will be stored (use AW_ROOT_DEFAULT to store in properties) |
---|
861 | * @param id unique id (has to be a key) |
---|
862 | * @param store_cb creates a string from current state |
---|
863 | * @param load_or_reset_cb restores state from string or resets factory defaults if string is NULp |
---|
864 | * @param macro_id custom macro id (normally default (=NULp) will do) |
---|
865 | * @param predef predefined configs (default: none) |
---|
866 | */ |
---|
867 | AWT_configuration * const config = new AWT_configuration(default_file_, id, store_cb, load_or_reset_cb, predef); |
---|
868 | |
---|
869 | int old_button_length = aww->get_button_length(); |
---|
870 | |
---|
871 | aww->button_length(0); // -> autodetect size by size of graphic |
---|
872 | aww->callback(makeCreateWindowCallback(create_config_manager_window, destroy_AWT_configuration, config, aww)); |
---|
873 | aww->create_button(macro_id ? macro_id : "SAVELOAD_CONFIG", "#conf_save.xpm"); |
---|
874 | |
---|
875 | aww->button_length(old_button_length); |
---|
876 | } |
---|
877 | |
---|
878 | static char *store_generated_config_cb(const ConfigSetupCallback *setup_cb) { |
---|
879 | AWT_config_definition cdef; |
---|
880 | (*setup_cb)(cdef); |
---|
881 | |
---|
882 | return cdef.read(); |
---|
883 | } |
---|
884 | static void load_or_reset_generated_config_cb(const char *stored_string, const ConfigSetupCallback *setup_cb) { |
---|
885 | AWT_config_definition cdef; |
---|
886 | (*setup_cb)(cdef); |
---|
887 | |
---|
888 | if (stored_string) cdef.write(stored_string); |
---|
889 | else cdef.reset(); |
---|
890 | } |
---|
891 | void AWT_insert_config_manager(AW_window *aww, AW_default default_file_, const char *id, ConfigSetupCallback setup_cb, const char *macro_id, const AWT_predefined_config *predef) { |
---|
892 | /*! inserts a config-button into aww |
---|
893 | * @param default_file_ db where configs will be stored (use AW_ROOT_DEFAULT to store in properties) |
---|
894 | * @param id unique id (has to be a key) |
---|
895 | * @param setup_cb populates an AWT_config_definition (cl is passed to setup_cb) |
---|
896 | * @param macro_id custom macro id (normally default (=NULp) will do) |
---|
897 | * @param predef predefined configs (default: none) |
---|
898 | */ |
---|
899 | |
---|
900 | ConfigSetupCallback * const setup_cb_copy = new ConfigSetupCallback(setup_cb); // not freed (bound to cb) |
---|
901 | AWT_insert_config_manager(aww, default_file_, id, |
---|
902 | makeStoreConfigCallback(store_generated_config_cb, setup_cb_copy), |
---|
903 | makeRestoreConfigCallback(load_or_reset_generated_config_cb, setup_cb_copy), |
---|
904 | macro_id, predef); |
---|
905 | } |
---|
906 | |
---|
907 | static void generate_config_from_mapping_cb(AWT_config_definition& cdef, const AWT_config_mapping_def *mapping) { |
---|
908 | cdef.add(mapping); |
---|
909 | } |
---|
910 | |
---|
911 | void AWT_insert_config_manager(AW_window *aww, AW_default default_file_, const char *id, const AWT_config_mapping_def *mapping, const char *macro_id, const AWT_predefined_config *predef) { |
---|
912 | /*! inserts a config-button into aww |
---|
913 | * @param default_file_ db where configs will be stored (use AW_ROOT_DEFAULT to store in properties) |
---|
914 | * @param id unique id (has to be a key) |
---|
915 | * @param mapping hardcoded mapping between AWARS and config strings |
---|
916 | * @param macro_id custom macro id (normally default (=NULp) will do) |
---|
917 | * @param predef predefined configs (default: none) |
---|
918 | */ |
---|
919 | AWT_insert_config_manager(aww, default_file_, id, makeConfigSetupCallback(generate_config_from_mapping_cb, mapping), macro_id, predef); |
---|
920 | } |
---|
921 | |
---|
922 | // ------------------- |
---|
923 | // AWT_config |
---|
924 | |
---|
925 | AWT_config::AWT_config(const char *cfgStr) : |
---|
926 | mapping(new ConfigMapping), |
---|
927 | parse_error(NULp) |
---|
928 | { |
---|
929 | parse_error = mapping->parseFrom(cfgStr); |
---|
930 | } |
---|
931 | |
---|
932 | inline void warn_unknown_awar(const string& awar_name) { |
---|
933 | aw_message(GBS_global_string("Warning: unknown awar referenced\n(%s)", awar_name.c_str())); |
---|
934 | } |
---|
935 | |
---|
936 | void AWT_config::init_from_awars(const ConfigMapping& cfgname2awar) { |
---|
937 | config_map& valuemap = *mapping; |
---|
938 | AW_root *aw_root = AW_root::SINGLETON; |
---|
939 | |
---|
940 | int skipped = 0; |
---|
941 | for (config_map::const_iterator c = cfgname2awar.begin(); c != cfgname2awar.end(); ++c) { |
---|
942 | const string& key(c->first); |
---|
943 | const string& awar_name(c->second); |
---|
944 | |
---|
945 | AW_awar *awar = aw_root->awar_no_error(awar_name.c_str()); |
---|
946 | if (awar) { |
---|
947 | char *awar_value = awar->read_as_string(); |
---|
948 | valuemap[key] = awar_value; |
---|
949 | free(awar_value); |
---|
950 | } |
---|
951 | else { |
---|
952 | valuemap.erase(key); |
---|
953 | warn_unknown_awar(awar_name); |
---|
954 | ++skipped; |
---|
955 | } |
---|
956 | } |
---|
957 | |
---|
958 | awt_assert((valuemap.size()+skipped) == cfgname2awar.size()); |
---|
959 | awt_assert(!parse_error); |
---|
960 | } |
---|
961 | |
---|
962 | AWT_config::AWT_config(const ConfigMapping& cfgname2awar) : |
---|
963 | mapping(new ConfigMapping), |
---|
964 | parse_error(NULp) |
---|
965 | { |
---|
966 | init_from_awars(cfgname2awar); |
---|
967 | } |
---|
968 | |
---|
969 | AWT_config::AWT_config(const AWT_config_definition *cfg_def) : |
---|
970 | mapping(new ConfigMapping), |
---|
971 | parse_error(NULp) |
---|
972 | { |
---|
973 | init_from_awars(cfg_def->get_mapping()); |
---|
974 | } |
---|
975 | |
---|
976 | AWT_config::~AWT_config() { |
---|
977 | delete mapping; |
---|
978 | } |
---|
979 | |
---|
980 | void AWT_config::write_to_awars(const ConfigMapping& cfgname2awar, bool warn) const { |
---|
981 | // writes values from config into awars (write-order: alphabetical by config-key) |
---|
982 | |
---|
983 | awt_assert(!parse_error); |
---|
984 | AW_root *aw_root = AW_root::SINGLETON; |
---|
985 | int unmapped = 0; |
---|
986 | for (config_map::const_iterator e = mapping->begin(); e != mapping->end(); ++e) { |
---|
987 | const string& config_name(e->first); |
---|
988 | const string& value(e->second); |
---|
989 | |
---|
990 | config_map::const_iterator found = cfgname2awar.find(config_name); |
---|
991 | if (found == cfgname2awar.end()) { |
---|
992 | if (warn) aw_message(GBS_global_string("config contains unknown entry '%s'", config_name.c_str())); |
---|
993 | unmapped++; |
---|
994 | } |
---|
995 | else { |
---|
996 | const string& awar_name(found->second); |
---|
997 | AW_awar *awar = aw_root->awar(awar_name.c_str()); |
---|
998 | awar->write_as_string(value.c_str()); |
---|
999 | } |
---|
1000 | } |
---|
1001 | |
---|
1002 | if (unmapped && warn) { |
---|
1003 | int mapped = mapping->size()-unmapped; |
---|
1004 | aw_message(GBS_global_string("Not all config entries were valid:\n" |
---|
1005 | "(known/restored: %i, unknown/ignored: %i)\n" |
---|
1006 | "Note: ok for configs shared between multiple windows", |
---|
1007 | mapped, unmapped)); |
---|
1008 | } |
---|
1009 | } |
---|
1010 | |
---|
1011 | void AWT_config::get_entries(ConstStrArray& to_array) { |
---|
1012 | mapping->get_entries(to_array); |
---|
1013 | } |
---|
1014 | |
---|
1015 | // ------------------------------ |
---|
1016 | // AWT_config_definition |
---|
1017 | |
---|
1018 | AWT_config_definition::AWT_config_definition() |
---|
1019 | : config_mapping(new ConfigMapping) |
---|
1020 | {} |
---|
1021 | |
---|
1022 | AWT_config_definition::AWT_config_definition(AWT_config_mapping_def *mdef) |
---|
1023 | : config_mapping(new ConfigMapping) |
---|
1024 | { |
---|
1025 | add(mdef); |
---|
1026 | } |
---|
1027 | |
---|
1028 | AWT_config_definition::~AWT_config_definition() { |
---|
1029 | delete config_mapping; |
---|
1030 | } |
---|
1031 | |
---|
1032 | void AWT_config_definition::add(const char *awar_name, const char *config_name) { |
---|
1033 | (*config_mapping)[config_name] = awar_name; |
---|
1034 | } |
---|
1035 | void AWT_config_definition::add(const char *awar_name, const char *config_name, int counter) { |
---|
1036 | add(awar_name, GBS_global_string("%s%i", config_name, counter)); |
---|
1037 | } |
---|
1038 | void AWT_config_definition::add(const AWT_config_mapping_def *mdef) { |
---|
1039 | while (mdef->awar_name && mdef->config_name) { |
---|
1040 | add(mdef->awar_name, mdef->config_name); |
---|
1041 | mdef++; |
---|
1042 | } |
---|
1043 | } |
---|
1044 | |
---|
1045 | char *AWT_config_definition::read() const { |
---|
1046 | // creates a string from awar values |
---|
1047 | |
---|
1048 | AWT_config current_state(*config_mapping); |
---|
1049 | return current_state.config_string(); |
---|
1050 | } |
---|
1051 | void AWT_config_definition::write(const char *cfgStr) const { |
---|
1052 | // write values from string to awars |
---|
1053 | // if the string contains unknown settings, they are silently ignored |
---|
1054 | |
---|
1055 | awt_assert(cfgStr); |
---|
1056 | |
---|
1057 | AWT_config wanted_state(cfgStr); |
---|
1058 | GB_ERROR error = wanted_state.parseError(); |
---|
1059 | if (!error) { |
---|
1060 | char *old_state = read(); |
---|
1061 | |
---|
1062 | wanted_state.write_to_awars(*config_mapping, true); |
---|
1063 | if (strcmp(old_state, cfgStr) != 0) { // expect that anything gets changed? |
---|
1064 | char *new_state = read(); |
---|
1065 | if (strcmp(new_state, cfgStr) != 0) { |
---|
1066 | bool retry = true; |
---|
1067 | int maxRetries = 10; |
---|
1068 | while (retry && maxRetries--) { |
---|
1069 | printf("Note: repeating config restore (did not set all awars correct)\n"); |
---|
1070 | wanted_state.write_to_awars(*config_mapping, false); |
---|
1071 | char *new_state2 = read(); |
---|
1072 | if (strcmp(new_state, new_state2) != 0) { // retrying had some effect -> repeat |
---|
1073 | reassign(new_state, new_state2); |
---|
1074 | } |
---|
1075 | else { |
---|
1076 | retry = false; |
---|
1077 | free(new_state2); |
---|
1078 | } |
---|
1079 | } |
---|
1080 | if (retry) { |
---|
1081 | error = "Unable to restore everything (might be caused by outdated, now invalid settings)"; |
---|
1082 | } |
---|
1083 | } |
---|
1084 | free(new_state); |
---|
1085 | } |
---|
1086 | free(old_state); |
---|
1087 | } |
---|
1088 | if (error) aw_message(GBS_global_string("Error restoring configuration (%s)", error)); |
---|
1089 | } |
---|
1090 | |
---|
1091 | void AWT_config_definition::reset() const { |
---|
1092 | // reset all awars (stored in config) to factory defaults |
---|
1093 | AW_root *aw_root = AW_root::SINGLETON; |
---|
1094 | for (config_map::const_iterator e = config_mapping->begin(); e != config_mapping->end(); ++e) { |
---|
1095 | const string& awar_name(e->second); |
---|
1096 | AW_awar *awar = aw_root->awar_no_error(awar_name.c_str()); |
---|
1097 | if (awar) { |
---|
1098 | awar->reset_to_default(); |
---|
1099 | } |
---|
1100 | else { |
---|
1101 | warn_unknown_awar(awar_name); |
---|
1102 | } |
---|
1103 | } |
---|
1104 | } |
---|
1105 | |
---|
1106 | // -------------------------------------------------------------------------------- |
---|
1107 | |
---|
1108 | void AWT_modify_managed_configs(AW_default default_file, const char *id, ConfigModifyCallback mod_cb, AW_CL cl_user) { |
---|
1109 | /*! allows to modify (parts of) all stored configs |
---|
1110 | * @param default_file has to be same as used in AWT_insert_config_manager() |
---|
1111 | * @param id ditto |
---|
1112 | * @param mod_cb called with each key/value pair of each stored config. result == NULp -> delete pair; result != NULp -> change or leave unchanged (result has to be a heapcopy!) |
---|
1113 | * @param cl_user forwarded to mod_cb |
---|
1114 | */ |
---|
1115 | |
---|
1116 | ConfigDefinition configDef(default_file, id); |
---|
1117 | |
---|
1118 | ConstStrArray cfgs; |
---|
1119 | get_existing_configs(configDef, cfgs); |
---|
1120 | |
---|
1121 | for (size_t c = 0; c<cfgs.size(); ++c) { |
---|
1122 | GB_transaction ta(configDef.get_db()); |
---|
1123 | GBDATA *gb_cfg = GB_search(configDef.get_db(), configDef.get_config_dbpath(cfgs[c]).c_str(), GB_FIND); |
---|
1124 | GB_ERROR error = NULp; |
---|
1125 | |
---|
1126 | if (gb_cfg) { |
---|
1127 | const char *content = GB_read_char_pntr(gb_cfg); |
---|
1128 | |
---|
1129 | AWT_config cmap(content); |
---|
1130 | error = cmap.parseError(); |
---|
1131 | if (!error) { |
---|
1132 | ConstStrArray entries; |
---|
1133 | cmap.get_entries(entries); |
---|
1134 | |
---|
1135 | bool update = false; |
---|
1136 | for (size_t e = 0; e<entries.size(); ++e) { |
---|
1137 | const char *old_content = cmap.get_entry(entries[e]); |
---|
1138 | char *new_content = mod_cb(entries[e], old_content, cl_user); |
---|
1139 | |
---|
1140 | if (!new_content) { |
---|
1141 | cmap.delete_entry(entries[e]); |
---|
1142 | update = true; |
---|
1143 | } |
---|
1144 | else if (strcmp(old_content, new_content) != 0) { |
---|
1145 | cmap.set_entry(entries[e], new_content); |
---|
1146 | update = true; |
---|
1147 | } |
---|
1148 | free(new_content); |
---|
1149 | } |
---|
1150 | |
---|
1151 | if (update) { |
---|
1152 | char *cs = cmap.config_string(); |
---|
1153 | error = GB_write_string(gb_cfg, cs); |
---|
1154 | free(cs); |
---|
1155 | } |
---|
1156 | } |
---|
1157 | } |
---|
1158 | |
---|
1159 | if (error) { |
---|
1160 | error = GBS_global_string("%s (config='%s')", error, cfgs[c]); |
---|
1161 | aw_message(error); |
---|
1162 | } |
---|
1163 | } |
---|
1164 | } |
---|
1165 | |
---|
1166 | |
---|