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