ARB
AWT_config_manager.cxx
Go to the documentation of this file.
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 {
36 
37  // 'Edit' subwindow
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 
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 
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 
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 
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_modal_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_modal_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 
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 
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 
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 
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 
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 
694 
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);
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 
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 
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 
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 {
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) {
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) {
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) {
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 
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) {
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 
970  mapping(new ConfigMapping),
971  parse_error(NULp)
972 {
973  init_from_awars(cfg_def->get_mapping());
974 }
975 
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 
1012  mapping->get_entries(to_array);
1013 }
1014 
1015 // ------------------------------
1016 // AWT_config_definition
1017 
1019  : config_mapping(new ConfigMapping)
1020 {}
1021 
1023  : config_mapping(new ConfigMapping)
1024 {
1025  add(mdef);
1026 }
1027 
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 }
1039  while (mdef->awar_name && mdef->config_name) {
1040  add(mdef->awar_name, mdef->config_name);
1041  mdef++;
1042  }
1043 }
1044 
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 
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) {
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 
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)
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)
static void field_content_changed_cb(AW_root *, AWT_configuration *config)
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 delete_cb(AW_window *, AWT_configuration *config)
size_t size() const
Definition: arb_strarray.h:85
GBDATA * GB_child(GBDATA *father)
Definition: adquery.cxx:322
return string(buffer, length)
static void erase_comment_cb(AW_window *, AW_awar *awar_comment)
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
GB_ERROR Save(const char *filename, const string &cfg_name, const string &comment)
const char * get_id() const
#define awt_assert(cond)
Definition: awt.hxx:28
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
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)
static char * store_generated_config_cb(const ConfigSetupCallback *setup_cb)
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
static void comment_changed_cb(AW_root *, AWT_configuration *config)
char *(* ConfigModifyCallback)(const char *key, const char *value, AW_CL cl_user)
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
static void generate_config_from_mapping_cb(AWT_config_definition &cdef, const AWT_config_mapping_def *mapping)
#define ARRAY_ELEMS(array)
Definition: arb_defs.h:19
string get_config(const string &cfgname)
GB_ERROR parseError() const
static void load_or_reset_generated_config_cb(const char *stored_string, const ConfigSetupCallback *setup_cb)
#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
static void selected_field_changed_cb(AW_root *, AWT_configuration *config)
static void refresh_config_sellist_cb(AW_root *, AWT_configuration *config, AW_selection_list *sel)
const char * read_char_pntr() const
Definition: AW_awar.cxx:168
void save_comments(const AWT_config &comments, AWT_configuration *config)
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
GB_CSTR GB_read_key_pntr(GBDATA *gbd)
Definition: arbdb.cxx:1656
void get_entries(class ConstStrArray &to_array)
Definition: ConfigMapping.h:73
static void encode_escapes(std::string &s, const char *to_escape)
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
#define NO_CONFIG_SELECTED
void remove_from_configs(const string &config, string &existing_configs)
const char * mnemonic
static void save_cb(AW_window *, AWT_configuration *config)
static GB_ERROR decode_escapes(std::string &s)
char * read_as_string() const
static void get_existing_configs(ConfigDefinition &configDef, ConstStrArray &cfgs)
GB_ERROR reset_to_default()
Definition: AW_awar.cxx:214
void warn_unknown_awar(const string &awar_name)
AWT_config(const char *cfgStr)
static void edit_cb(AW_window *aww, 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
#define HEADERLEN
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 destroy_AWT_configuration(AWT_configuration *c, AW_window *)
static void store_cb(AW_window *, AWT_configuration *config)
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)
bool is_prefined(const string &cfgname)
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 void current_changed_cb(AW_root *, AWT_configuration *config)
long AW_CL
Definition: cb.h:21
GB_ERROR Load(const char *filename, const string &cfg_name, string &found_comment)
void get_entries(class ConstStrArray &to_array)
static AW_window * create_config_manager_window(AW_root *, AWT_configuration *config, AW_window *aww)
GB_ERROR parseFrom(const std::string &configString)
static void delete_field_cb(AW_window *, AWT_configuration *config)
void AWT_modify_managed_configs(AW_default default_file, const char *id, ConfigModifyCallback mod_cb, AW_CL cl_user)
string get_config_dbpath(const string &cfgname) const
bool operator<(const ConfigDefinition &other) const
void set_entry(const char *entry, const char *value)
string get_awar_value(ConfigAwar a) const
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
char * aw_modal_file_selection(const char *title, const char *dir, const char *def_name, const char *suffix)
Definition: AW_modal.cxx:310
#define NULp
Definition: cxxforward.h:116
static void reset_cb(AW_window *, AWT_configuration *config)
static void keep_changed_fields_cb(AW_window *, AWT_configuration *config)
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)
#define HEADER
GBDATA * GB_nextChild(GBDATA *child)
Definition: adquery.cxx:326
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
char * config_string() const
static void restore_cb(AW_window *, AWT_configuration *config)
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:287
static void load_cb(AW_window *, AWT_configuration *config)
#define max(a, b)
Definition: f2c.h:154
GB_write_int const char s
Definition: AW_awar.cxx:154