source: branches/port5/AWT/AWT_config_manager.cxx

Last change on this file was 6121, checked in by westram, 15 years ago
  • fixed macro ids for merge species window
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.4 KB
Line 
1//  ==================================================================== //
2//                                                                       //
3//    File      : AWT_config_manager.cxx                                 //
4//    Purpose   :                                                        //
5//                                                                       //
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.hxx"
16#include "awt_config_manager.hxx"
17
18#include <map>
19#include <string>
20#include <cstdlib>
21#include <cstring>
22
23using namespace std;
24
25//  --------------------------------
26//      class AWT_configuration
27//  --------------------------------
28class AWT_configuration {
29private:
30    string id;
31
32    AWT_store_config_to_string  store;
33    AWT_load_config_from_string load;
34    AW_CL                       client1; // client data
35    AW_CL                       client2;
36
37    AW_window  *last_client_aww;
38    AW_default  default_file;
39
40public:
41    AWT_configuration(AW_window *aww, AW_default default_file_, const char *id_, AWT_store_config_to_string store_,
42                      AWT_load_config_from_string load_, AW_CL cl1, AW_CL cl2)
43    {
44        id              = id_;
45        store           = store_;
46        load            = load_;
47        client1         = cl1;
48        client2         = cl2;
49        last_client_aww = aww;
50        default_file    = default_file_;
51    }
52    virtual ~AWT_configuration() {}
53
54    bool operator<(const AWT_configuration& other) const { return id<other.id; }
55    string get_awar_name(const string& subname) const { return string("general_configs/")+id+'/'+subname; }
56    string get_awar_value(const string& subname, const char *default_value = "") const {
57        AW_root *aw_root   = last_client_aww->get_root();
58        string   awar_name = get_awar_name(subname);
59        char    *value     = aw_root->awar_string(awar_name.c_str(), default_value, default_file)->read_string();
60        string   result    = value;
61        free(value);
62        return result;
63    }
64    void set_awar_value(const string& subname, const string& new_value) const {
65        AW_root *aw_root   = last_client_aww->get_root();
66        aw_root->awar_string(get_awar_name(subname).c_str(), "")->write_string(new_value.c_str());
67    }
68
69    char *Store() const { return store(last_client_aww, client1, client2); }
70    void Restore(const string& s) const { return load(last_client_aww, s.c_str(), client1, client2); }
71};
72
73//  ---------------------------------------------------------------------------------
74//      void remove_from_configs(const string& config, string& existing_configs)
75//  ---------------------------------------------------------------------------------
76void remove_from_configs(const string& config, string& existing_configs) {
77    size_t start = -1U;
78    printf("erasing '%s' from '%s'\n", config.c_str(), existing_configs.c_str());
79
80    while (1) {
81        start = existing_configs.find(config, start+1);
82        if (start == string::npos) break; // not found
83        if (start == 0 || existing_configs[start-1] == ';') { // config starts with string
84            size_t stop = start+config.length();
85            if (stop != existing_configs.length()) {
86                if (stop>existing_configs.length()) break; // not found
87                if (existing_configs[stop] != ';') continue; // name continues
88            }
89            existing_configs.erase(start, stop-start+1);
90            if (existing_configs[existing_configs.length()-1] == ';') {
91                existing_configs.erase(existing_configs.length()-1);
92            }
93            remove_from_configs(config, existing_configs);
94            break;
95        }
96    }
97#if defined(DEBUG)
98    printf("result: '%s'\n", existing_configs.c_str());
99#endif // DEBUG
100}
101// ---------------------------------------------------------
102//      static char *correct_key_name(const char *name)
103// ---------------------------------------------------------
104static char *correct_key_name(const char *name) {
105    char *corrected = GBS_string_2_key(name);
106
107    if (strcmp(corrected, "__") == 0) freedup(corrected, "");
108    return corrected;
109}
110
111//  ------------------------------------------------------------------------------
112//      static void AWT_start_config_manager(AW_window *aww, AW_CL cl_config)
113//  ------------------------------------------------------------------------------
114static void AWT_start_config_manager(AW_window *aww, AW_CL cl_config)
115{
116    AWT_configuration *config           = (AWT_configuration*)cl_config;
117    string             existing_configs = config->get_awar_value("existing");
118    config->get_awar_value("current"); // create!
119    bool               reopen           = false;
120    char              *title            = GBS_global_string_copy("Configurations for '%s'", aww->window_name);
121
122    const char *buttons = "RESTORE,STORE,DELETE,CLOSE,HELP";
123    char       *result  = aw_string_selection2awar(title, "Enter a new or select an existing config",
124                                                   config->get_awar_name("current").c_str(), existing_configs.c_str(),
125                                                   buttons, correct_key_name);
126    int         button  = aw_string_selection_button();
127
128    if (button >= 0 && button <= 2) { // RESTORE, STORE and DELETE
129        if (!result || !result[0]) { // did user specify a config-name ?
130            aw_message("Please enter or select a config");
131        }
132        else {
133            string awar_name = string("cfg_")+result;
134           
135            switch (button) {
136                case 0: {           // RESTORE
137                    config->Restore(config->get_awar_value(awar_name));
138                    config->set_awar_value("current", result);
139                    break;
140                }
141                case 1: {               // STORE
142                    remove_from_configs(result, existing_configs); // remove existing config
143
144                    if (existing_configs.length()) existing_configs = string(result)+';'+existing_configs;
145                    else existing_configs                           = result;
146
147                    char *config_string = config->Store();
148                    config->set_awar_value(awar_name, config_string);
149                    free(config_string);
150                    config->set_awar_value("current", result);
151                    config->set_awar_value("existing", existing_configs);
152                    reopen = true;
153                    break;
154                }
155                case 2: {               // DELETE
156                    remove_from_configs(result, existing_configs); // remove existing config
157                    config->set_awar_value("current", "");
158                    config->set_awar_value("existing", existing_configs);
159
160                    // config is not really deleted from properties
161                    reopen = true;
162                    break;
163                }
164            }
165        }
166    }
167    else {
168        if (button == 4) { // HELP
169            AW_POPUP_HELP(aww, (AW_CL)"configurations.hlp");
170        }
171    }
172
173    free(title);
174    free(result);
175
176    //     if (reopen) AWT_start_config_manager(aww, cl_config); // crashes!
177}
178
179void AWT_insert_config_manager(AW_window *aww, AW_default default_file_, const char *id, AWT_store_config_to_string store_cb,
180                               AWT_load_config_from_string load_cb, AW_CL cl1, AW_CL cl2, const char *macro_id)
181{
182    AWT_configuration *config = new AWT_configuration(aww, default_file_, id, store_cb, load_cb, cl1, cl2);
183    // config will not be freed!!!
184
185    aww->button_length(0); // -> autodetect size by size of graphic
186    aww->callback(AWT_start_config_manager, (AW_CL)config);
187    aww->create_button(macro_id ? macro_id : "SAVELOAD_CONFIG", "#conf_save.xpm");
188}
189
190static GB_ERROR decode_escapes(string& s) {
191    string::iterator f = s.begin();
192    string::iterator t = s.begin();
193
194    for (; f != s.end(); ++f, ++t) {
195        if (*f == '\\') {
196            ++f;
197            if (f == s.end()) return GBS_global_string("Trailing \\ in '%s'", s.c_str());
198            switch (*f) {
199                case 'n':
200                    *t = '\n';
201                    break;
202                case 'r':
203                    *t = '\r';
204                    break;
205                case 't':
206                    *t = '\t';
207                    break;
208                default:
209                    *t = *f;
210                    break;
211            }
212        }
213        else {
214            *t = *f;
215        }
216    }
217
218    s.erase(t, f);
219
220    return 0;
221}
222
223static void encode_escapes(string& s, const char *to_escape) {
224    string neu;
225    neu.reserve(s.length()*2+1);
226
227    for (string::iterator p = s.begin(); p != s.end(); ++p) {
228        if (*p == '\\' || strchr(to_escape, *p) != 0) {
229            neu = neu+'\\'+*p;
230        }
231        else if (*p == '\n') { neu = neu+"\\n"; }
232        else if (*p == '\r') { neu = neu+"\\r"; }
233        else if (*p == '\t') { neu = neu+"\\t"; }
234        else { neu = neu+*p; }
235    }
236    s = neu;
237}
238
239typedef map<string, string> config_map;
240struct AWT_config_mapping {
241    config_map cmap;
242
243    config_map::iterator entry(const string &e) { return cmap.find(e); }
244
245    config_map::iterator begin() { return cmap.begin(); }
246    config_map::const_iterator end() const { return cmap.end(); }
247    config_map::iterator end() { return cmap.end(); }
248};
249
250// ---------------------------------------
251//      class AWT_config
252// ---------------------------------------
253
254AWT_config::AWT_config(const char *config_char_ptr)
255    : mapping(new AWT_config_mapping)
256    , parse_error(0)
257{
258    // parse string in format "key1='value1';key2='value2'"..
259    // and put values into a map.
260    // assumes that keys are unique
261
262    string      configString(config_char_ptr);
263    config_map& cmap  = mapping->cmap;
264    size_t      pos   = 0;
265
266    while (!parse_error) {
267        size_t equal = configString.find('=', pos);
268        if (equal == string::npos) break;
269
270        if (configString[equal+1] != '\'') {
271            parse_error = "expected quote \"'\"";
272            break;
273        }
274        size_t start = equal+2;
275        size_t end   = configString.find('\'', start);
276        while (end != string::npos) {
277            if (configString[end-1] != '\\') break;
278            end = configString.find('\'', end+1);
279        }
280        if (end == string::npos) {
281            parse_error = "could not find matching quote \"'\"";
282            break;
283        }
284
285        string config_name = configString.substr(pos, equal-pos);
286        string value       = configString.substr(start, end-start);
287
288        parse_error = decode_escapes(value);
289        if (!parse_error) {
290            cmap[config_name] = value;
291        }
292
293        pos = end+2;            // skip ';'
294    }
295}
296AWT_config::AWT_config(const AWT_config_mapping *cfgname_2_awar, AW_root *root)
297    : mapping(new AWT_config_mapping)
298    , parse_error(0)
299{
300    const config_map& awarmap  = cfgname_2_awar->cmap;
301    config_map& valuemap = mapping->cmap;
302
303    for (config_map::const_iterator c = awarmap.begin(); c != awarmap.end(); ++c) {
304        const string& key(c->first);
305        const string& awar_name(c->second);
306       
307        char *awar_value = root->awar(awar_name.c_str())->read_as_string();
308        valuemap[key]    = awar_value;
309        free(awar_value);
310    }
311
312    awt_assert(valuemap.size() == awarmap.size());
313}
314
315AWT_config::~AWT_config() {
316    delete mapping;
317}
318
319bool AWT_config::has_entry(const char *entry) const {
320    awt_assert(!parse_error);
321    return mapping->entry(entry) != mapping->end();
322}
323const char *AWT_config::get_entry(const char *entry) const {
324    awt_assert(!parse_error);
325    config_map::iterator found = mapping->entry(entry);
326    return (found == mapping->end()) ? 0 : found->second.c_str();
327}
328void AWT_config::set_entry(const char *entry, const char *value) {
329    awt_assert(!parse_error);
330    mapping->cmap[entry] = value;
331}
332void AWT_config::delete_entry(const char *entry) {
333    awt_assert(!parse_error);
334    mapping->cmap.erase(entry);
335}
336
337char *AWT_config::config_string() const {
338    awt_assert(!parse_error);
339    string result;
340    for (config_map::iterator e = mapping->begin(); e != mapping->end(); ++e) {
341        const string& config_name(e->first);
342        string        value(e->second);
343
344        encode_escapes(value, "\'");
345        string entry = config_name+"='"+value+'\'';
346        if (result.empty()) {
347            result = entry;
348        }
349        else {
350            result = result+';'+entry;
351        }
352    }
353    return strdup(result.c_str());
354}
355GB_ERROR AWT_config::write_to_awars(const AWT_config_mapping *cfgname_2_awar, AW_root *root) const {
356    GB_ERROR        error = 0;
357    GB_transaction *ta    = 0;
358    awt_assert(!parse_error);
359    for (config_map::iterator e = mapping->begin(); !error && e != mapping->end(); ++e) {
360        const string& config_name(e->first);
361        const string& value(e->second);
362
363        config_map::const_iterator found = cfgname_2_awar->cmap.find(config_name);
364        if (found == cfgname_2_awar->end()) {
365            error = GBS_global_string("config contains unmapped entry '%s'", config_name.c_str());
366        }
367        else {
368            const string&  awar_name(found->second);
369            AW_awar       *awar = root->awar(awar_name.c_str());
370            if (!ta) {
371                ta = new GB_transaction((GBDATA*)awar->gb_var); // do all awar changes in 1 transaction
372            }
373            awar->write_as_string(value.c_str());
374        }
375    }
376    if (ta) delete ta; // close transaction
377    return error;
378}
379
380//  ------------------------------------
381//      class AWT_config_definition
382//  ------------------------------------
383
384AWT_config_definition::AWT_config_definition(AW_root *aw_root)
385    : root(aw_root) , config_mapping(new AWT_config_mapping) {}
386
387AWT_config_definition::AWT_config_definition(AW_root *aw_root, AWT_config_mapping_def *mdef)
388    : root(aw_root) , config_mapping(new AWT_config_mapping)
389{
390    add(mdef);
391}
392
393AWT_config_definition::~AWT_config_definition() {
394    delete config_mapping;
395}
396
397void AWT_config_definition::add(const char *awar_name, const char *config_name) {
398    config_mapping->cmap[config_name] = awar_name;
399}
400void AWT_config_definition::add(const char *awar_name, const char *config_name, int counter) {
401    add(awar_name, GBS_global_string("%s%i", config_name, counter));
402}
403void AWT_config_definition::add(AWT_config_mapping_def *mdef) {
404    while (mdef->awar_name && mdef->config_name) {
405        add(mdef->awar_name, mdef->config_name);
406        mdef++;
407    }
408}
409
410char *AWT_config_definition::read() const {
411    // creates a string from awar values
412   
413    AWT_config current_state(config_mapping, root);
414    return current_state.config_string();
415}
416void AWT_config_definition::write(const char *config_char_ptr) const {
417    // write values from string to awars
418    // if the string contains unknown settings, they are silently ignored
419
420    AWT_config wanted_state(config_char_ptr);
421    GB_ERROR   error  = wanted_state.parseError();
422    if (!error) error = wanted_state.write_to_awars(config_mapping, root);
423    if (error) aw_message(GBS_global_string("Error restoring configuration (%s)", error));
424}
425
Note: See TracBrowser for help on using the repository browser.