| 1 | // ================================================================ // | 
|---|
| 2 | //                                                                  // | 
|---|
| 3 | //   File      : AWT_sel_boxes.cxx                                  // | 
|---|
| 4 | //   Purpose   :                                                    // | 
|---|
| 5 | //                                                                  // | 
|---|
| 6 | //   Institute of Microbiology (Technical University Munich)        // | 
|---|
| 7 | //   http://www.arb-home.de/                                        // | 
|---|
| 8 | //                                                                  // | 
|---|
| 9 | // ================================================================ // | 
|---|
| 10 |  | 
|---|
| 11 | #include "awt.hxx" | 
|---|
| 12 | #include "awt_sel_boxes.hxx" | 
|---|
| 13 |  | 
|---|
| 14 | #include <item_sel_list.h> | 
|---|
| 15 |  | 
|---|
| 16 | #include <aw_awars.hxx> | 
|---|
| 17 | #include <aw_file.hxx> | 
|---|
| 18 | #include <aw_msg.hxx> | 
|---|
| 19 | #include <aw_root.hxx> | 
|---|
| 20 | #include <aw_edit.hxx> | 
|---|
| 21 |  | 
|---|
| 22 | #include <ad_config.h> | 
|---|
| 23 | #include <ad_cb.h> | 
|---|
| 24 |  | 
|---|
| 25 | #include <arbdbt.h> | 
|---|
| 26 | #include <arb_strbuf.h> | 
|---|
| 27 | #include <arb_strarray.h> | 
|---|
| 28 | #include <arb_file.h> | 
|---|
| 29 | #include <arb_global_defs.h> | 
|---|
| 30 | #include <arb_stdstring.h> | 
|---|
| 31 |  | 
|---|
| 32 | #include "awt_modules.hxx" | 
|---|
| 33 | #include <BufferedFileReader.h> | 
|---|
| 34 |  | 
|---|
| 35 | #include <list> | 
|---|
| 36 | #include <map> | 
|---|
| 37 | #include <set> | 
|---|
| 38 |  | 
|---|
| 39 | using namespace std; | 
|---|
| 40 |  | 
|---|
| 41 | typedef map<string, AW_window*> WinMap; | 
|---|
| 42 |  | 
|---|
| 43 | class SelectionListSpec { | 
|---|
| 44 | string        awar_name; | 
|---|
| 45 | static WinMap window_map; // contains popup windows of all selection list popups | 
|---|
| 46 |  | 
|---|
| 47 | virtual AW_DB_selection *create(AW_selection_list *sellist) const = 0; | 
|---|
| 48 | AW_DB_selection *init(AW_selection_list *sellist) const { | 
|---|
| 49 | AW_DB_selection *sel = create(sellist); | 
|---|
| 50 | sel->refresh(); | 
|---|
| 51 | return sel; | 
|---|
| 52 | } | 
|---|
| 53 |  | 
|---|
| 54 | public: | 
|---|
| 55 | SelectionListSpec(const char *awar_name_) | 
|---|
| 56 | : awar_name(awar_name_) | 
|---|
| 57 | {} | 
|---|
| 58 | virtual ~SelectionListSpec() {} | 
|---|
| 59 |  | 
|---|
| 60 | virtual const char *get_macro_id() const = 0; | 
|---|
| 61 | virtual const char *get_title() const    = 0; | 
|---|
| 62 |  | 
|---|
| 63 | const char *get_awar_name() const { return awar_name.c_str(); } | 
|---|
| 64 |  | 
|---|
| 65 | AW_DB_selection *create_list(AW_window *aws, bool fallback2default) const { | 
|---|
| 66 | return init(aws->create_selection_list(get_awar_name(), 40, 4, fallback2default)); | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | void popup() const { | 
|---|
| 70 | WinMap::iterator  found  = window_map.find(awar_name); | 
|---|
| 71 | if (found == window_map.end()) { | 
|---|
| 72 | AW_window_simple *aws = new AW_window_simple; | 
|---|
| 73 | aws->init(AW_root::SINGLETON, get_macro_id(), get_title()); | 
|---|
| 74 | aws->load_xfig("select_simple.fig"); | 
|---|
| 75 |  | 
|---|
| 76 | aws->at("selection"); | 
|---|
| 77 | AW_root::SINGLETON->awar(get_awar_name())->add_callback(makeRootCallback(awt_auto_popdown_cb, aws)); | 
|---|
| 78 | create_list(aws, true); | 
|---|
| 79 |  | 
|---|
| 80 | aws->at("button"); | 
|---|
| 81 | aws->callback(AW_POPDOWN); | 
|---|
| 82 | aws->create_button("CLOSE", "CLOSE", "C"); | 
|---|
| 83 |  | 
|---|
| 84 | aws->window_fit(); | 
|---|
| 85 |  | 
|---|
| 86 | window_map[awar_name] = aws; | 
|---|
| 87 | aws->activate(); | 
|---|
| 88 | } | 
|---|
| 89 | else { | 
|---|
| 90 | found->second->activate(); | 
|---|
| 91 | } | 
|---|
| 92 | } | 
|---|
| 93 |  | 
|---|
| 94 | void createButton(AW_window *aws) const; | 
|---|
| 95 | }; | 
|---|
| 96 | WinMap SelectionListSpec::window_map; | 
|---|
| 97 |  | 
|---|
| 98 | static void popup_SelectionListSpec_cb(UNFIXED, const SelectionListSpec *spec) { | 
|---|
| 99 | spec->popup(); | 
|---|
| 100 | } | 
|---|
| 101 |  | 
|---|
| 102 | void SelectionListSpec::createButton(AW_window *aws) const { | 
|---|
| 103 | // WARNING: this is bound to callback (do not free) | 
|---|
| 104 | aws->callback(makeWindowCallback(popup_SelectionListSpec_cb, this)); | 
|---|
| 105 | aws->create_button(get_macro_id(), get_awar_name()); | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 | // -------------------------------------- | 
|---|
| 109 | //      selection boxes on alignments | 
|---|
| 110 |  | 
|---|
| 111 | class ALI_selection : public AW_DB_selection { // derived from a Noncopyable | 
|---|
| 112 | char *ali_type_match;                           // filter for wanted alignments (GBS_string_eval command) | 
|---|
| 113 | public: | 
|---|
| 114 | ALI_selection(AW_selection_list *sellist_, GBDATA *gb_presets, const char *ali_type_match_) | 
|---|
| 115 | : AW_DB_selection(sellist_, gb_presets), | 
|---|
| 116 | ali_type_match(nulldup(ali_type_match_)) | 
|---|
| 117 | {} | 
|---|
| 118 |  | 
|---|
| 119 | void fill() OVERRIDE { | 
|---|
| 120 | GBDATA         *gb_presets = get_gbd(); | 
|---|
| 121 | GB_transaction  ta(gb_presets); | 
|---|
| 122 |  | 
|---|
| 123 | for (GBDATA *gb_alignment = GB_entry(gb_presets, "alignment"); | 
|---|
| 124 | gb_alignment; | 
|---|
| 125 | gb_alignment = GB_nextEntry(gb_alignment)) | 
|---|
| 126 | { | 
|---|
| 127 | char *alignment_type = GBT_read_string(gb_alignment, "alignment_type"); | 
|---|
| 128 | char *alignment_name = GBT_read_string(gb_alignment, "alignment_name"); | 
|---|
| 129 | char *str            = GBS_string_eval(alignment_type, ali_type_match); | 
|---|
| 130 |  | 
|---|
| 131 | if (!*str) insert(alignment_name, alignment_name); | 
|---|
| 132 | free(str); | 
|---|
| 133 | free(alignment_type); | 
|---|
| 134 | free(alignment_name); | 
|---|
| 135 | } | 
|---|
| 136 | insert_default(DISPLAY_NONE, NO_ALI_SELECTED); | 
|---|
| 137 | } | 
|---|
| 138 |  | 
|---|
| 139 | void reconfigure(const char *new_ali_type_match) { | 
|---|
| 140 | freedup(ali_type_match, new_ali_type_match); | 
|---|
| 141 | refresh(); | 
|---|
| 142 | } | 
|---|
| 143 | }; | 
|---|
| 144 |  | 
|---|
| 145 | class ALI_sellst_spec : public SelectionListSpec, virtual Noncopyable { | 
|---|
| 146 | GBDATA *gb_main; | 
|---|
| 147 | string  ali_type_match; | 
|---|
| 148 |  | 
|---|
| 149 | AW_DB_selection *create(AW_selection_list *sellist) const { | 
|---|
| 150 | GB_transaction ta(gb_main); | 
|---|
| 151 | return new ALI_selection(sellist, GBT_get_presets(gb_main), ali_type_match.c_str()); | 
|---|
| 152 | } | 
|---|
| 153 |  | 
|---|
| 154 | public: | 
|---|
| 155 | ALI_sellst_spec(const char *awar_name_, GBDATA *gb_main_, const char *ali_type_match_) | 
|---|
| 156 | : SelectionListSpec(awar_name_), | 
|---|
| 157 | gb_main(gb_main_), | 
|---|
| 158 | ali_type_match(ali_type_match_) | 
|---|
| 159 | {} | 
|---|
| 160 |  | 
|---|
| 161 | const char *get_macro_id() const { return "SELECT_ALI"; } | 
|---|
| 162 | const char *get_title() const { return "Select alignment"; } | 
|---|
| 163 | }; | 
|---|
| 164 |  | 
|---|
| 165 | AW_DB_selection *awt_create_ALI_selection_list(GBDATA *gb_main, AW_window *aws, const char *varname, const char *ali_type_match) { | 
|---|
| 166 | // Create selection lists on alignments | 
|---|
| 167 | // | 
|---|
| 168 | // if 'ali_type_match' is set, then only insert alignments, | 
|---|
| 169 | // where 'ali_type_match' GBS_string_eval's the alignment type | 
|---|
| 170 |  | 
|---|
| 171 | ALI_sellst_spec spec(varname, gb_main, ali_type_match); | 
|---|
| 172 | return spec.create_list(aws, true); | 
|---|
| 173 | } | 
|---|
| 174 |  | 
|---|
| 175 | void awt_create_ALI_selection_button(GBDATA *gb_main, AW_window *aws, const char *varname, const char *ali_type_match) { | 
|---|
| 176 | (new ALI_sellst_spec(varname, gb_main, ali_type_match))->createButton(aws); // do not free (bound to callback) | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | void awt_reconfigure_ALI_selection_list(AW_DB_selection *dbsel, const char *ali_type_match) { | 
|---|
| 180 | ALI_selection *alisel = dynamic_cast<ALI_selection*>(dbsel); | 
|---|
| 181 | alisel->reconfigure(ali_type_match); | 
|---|
| 182 | } | 
|---|
| 183 |  | 
|---|
| 184 | // --------------------------------- | 
|---|
| 185 | //      selection boxes on trees | 
|---|
| 186 |  | 
|---|
| 187 | struct AWT_tree_selection: public AW_DB_selection { | 
|---|
| 188 | AWT_tree_selection(AW_selection_list *sellist_, GBDATA *gb_tree_data) | 
|---|
| 189 | : AW_DB_selection(sellist_, gb_tree_data) | 
|---|
| 190 | {} | 
|---|
| 191 |  | 
|---|
| 192 | void fill() OVERRIDE { | 
|---|
| 193 | GBDATA         *gb_main = get_gb_main(); | 
|---|
| 194 | GB_transaction  ta(gb_main); | 
|---|
| 195 |  | 
|---|
| 196 | ConstStrArray tree_names; | 
|---|
| 197 | GBT_get_tree_names(tree_names, gb_main, true); | 
|---|
| 198 |  | 
|---|
| 199 | if (!tree_names.empty()) { | 
|---|
| 200 | int maxTreeNameLen = 0; | 
|---|
| 201 | for (int i = 0; tree_names[i]; ++i) { | 
|---|
| 202 | const char *tree = tree_names[i]; | 
|---|
| 203 | int         len  = strlen(tree); | 
|---|
| 204 | if (len>maxTreeNameLen) maxTreeNameLen = len; | 
|---|
| 205 | } | 
|---|
| 206 | for (int i = 0; tree_names[i]; ++i) { | 
|---|
| 207 | const char *tree = tree_names[i]; | 
|---|
| 208 | const char *info = GBT_tree_info_string(gb_main, tree, maxTreeNameLen); | 
|---|
| 209 | if (info) { | 
|---|
| 210 | insert(info, tree); | 
|---|
| 211 | } | 
|---|
| 212 | else { | 
|---|
| 213 | aw_message(GB_await_error()); | 
|---|
| 214 | insert(tree, tree); | 
|---|
| 215 | } | 
|---|
| 216 | } | 
|---|
| 217 | } | 
|---|
| 218 | insert_default(DISPLAY_NONE, NO_TREE_SELECTED); | 
|---|
| 219 | } | 
|---|
| 220 | }; | 
|---|
| 221 |  | 
|---|
| 222 | AW_DB_selection *awt_create_TREE_selection_list(GBDATA *gb_main, AW_window *aws, const char *varname, bool fallback2default) { | 
|---|
| 223 | GBDATA *gb_tree_data; | 
|---|
| 224 | { | 
|---|
| 225 | GB_transaction ta(gb_main); | 
|---|
| 226 | gb_tree_data = GBT_get_tree_data(gb_main); | 
|---|
| 227 | } | 
|---|
| 228 | AW_selection_list  *sellist = aws->create_selection_list(varname, 40, 4, fallback2default); | 
|---|
| 229 | AWT_tree_selection *treesel = new AWT_tree_selection(sellist, gb_tree_data); // owned by nobody | 
|---|
| 230 | treesel->refresh(); | 
|---|
| 231 | return treesel; | 
|---|
| 232 | } | 
|---|
| 233 |  | 
|---|
| 234 |  | 
|---|
| 235 | // -------------------------------------- | 
|---|
| 236 | //      selection boxes on pt-servers | 
|---|
| 237 |  | 
|---|
| 238 | #define PT_SERVERNAME_LENGTH        23              // that's for buttons | 
|---|
| 239 | #define PT_SERVERNAME_SELLIST_WIDTH 30              // this for lists | 
|---|
| 240 |  | 
|---|
| 241 | class PT_selection : public AW_selection { | 
|---|
| 242 | typedef list<PT_selection*> PT_selections; | 
|---|
| 243 |  | 
|---|
| 244 | static PT_selections ptserver_selections; | 
|---|
| 245 | public: | 
|---|
| 246 | PT_selection(AW_selection_list *sellist_); | 
|---|
| 247 |  | 
|---|
| 248 | void fill() OVERRIDE; | 
|---|
| 249 |  | 
|---|
| 250 | static void refresh_all(); | 
|---|
| 251 | }; | 
|---|
| 252 |  | 
|---|
| 253 | PT_selection::PT_selections PT_selection::ptserver_selections; | 
|---|
| 254 |  | 
|---|
| 255 | void PT_selection::fill() { | 
|---|
| 256 | const char * const *pt_servers = GBS_get_arb_tcp_entries("ARB_PT_SERVER*"); | 
|---|
| 257 |  | 
|---|
| 258 | int count = 0; | 
|---|
| 259 | while (pt_servers[count]) count++; | 
|---|
| 260 |  | 
|---|
| 261 | for (int i=0; i<count; i++) { | 
|---|
| 262 | char *choice = GBS_ptserver_id_to_choice(i, 1); | 
|---|
| 263 | if (!choice) { | 
|---|
| 264 | aw_message(GB_await_error()); | 
|---|
| 265 | break; | 
|---|
| 266 | } | 
|---|
| 267 | insert(choice, (long)i); | 
|---|
| 268 | free(choice); | 
|---|
| 269 | } | 
|---|
| 270 |  | 
|---|
| 271 | insert_default("-undefined-", (long)-1); | 
|---|
| 272 | } | 
|---|
| 273 |  | 
|---|
| 274 | void PT_selection::refresh_all() { | 
|---|
| 275 | PT_selections::iterator end = ptserver_selections.end(); | 
|---|
| 276 | for (PT_selections::iterator pts_sel = ptserver_selections.begin(); pts_sel != end; ++pts_sel) { | 
|---|
| 277 | (*pts_sel)->refresh(); | 
|---|
| 278 | } | 
|---|
| 279 | } | 
|---|
| 280 |  | 
|---|
| 281 | static void ptserverlog_changed_cb(const char *) { PT_selection::refresh_all(); } | 
|---|
| 282 | static void arbtcpdat_changed_cb(const char*)    { PT_selection::refresh_all(); } | 
|---|
| 283 |  | 
|---|
| 284 | PT_selection::PT_selection(AW_selection_list *sellist_) | 
|---|
| 285 | : AW_selection(sellist_) | 
|---|
| 286 | { | 
|---|
| 287 | if (ptserver_selections.empty()) { | 
|---|
| 288 | // first pt server selection list -> install log tracker | 
|---|
| 289 | AW_add_inotification(GBS_ptserver_logname(), makeFileChangedCallback(ptserverlog_changed_cb)); | 
|---|
| 290 | } | 
|---|
| 291 | ptserver_selections.push_back(this); | 
|---|
| 292 | } | 
|---|
| 293 |  | 
|---|
| 294 |  | 
|---|
| 295 | void awt_edit_arbtcpdat_cb(AW_window *) { | 
|---|
| 296 | char *filename = GB_arbtcpdat_path(); | 
|---|
| 297 | AW_edit_notified(filename, makeFileChangedCallback(arbtcpdat_changed_cb)); | 
|---|
| 298 | free(filename); | 
|---|
| 299 | } | 
|---|
| 300 |  | 
|---|
| 301 | void awt_auto_popdown_cb(AW_root*, AW_window_simple *aw_popup) { | 
|---|
| 302 | /*! auto pops-down a simple selection window when the awar bound | 
|---|
| 303 | * to the selection list gets changed. | 
|---|
| 304 | */ | 
|---|
| 305 | aw_popup->hide(); | 
|---|
| 306 | } | 
|---|
| 307 |  | 
|---|
| 308 | static char *readable_pt_servername(int index, int maxlength) { | 
|---|
| 309 | char *fullname = GBS_ptserver_id_to_choice(index, 0); | 
|---|
| 310 | if (!fullname) { | 
|---|
| 311 | #ifdef DEBUG | 
|---|
| 312 | printf("awar given to ptserver-selection does not contain a valid index\n"); | 
|---|
| 313 | #endif | 
|---|
| 314 | GB_clear_error(); | 
|---|
| 315 | return ARB_strdup("-undefined-"); | 
|---|
| 316 | } | 
|---|
| 317 |  | 
|---|
| 318 | int len = strlen(fullname); | 
|---|
| 319 | if (len <= maxlength) { | 
|---|
| 320 | return fullname; | 
|---|
| 321 | } | 
|---|
| 322 |  | 
|---|
| 323 | int remove  = len-maxlength; | 
|---|
| 324 | fullname[0] = '.'; | 
|---|
| 325 | fullname[1] = '.'; | 
|---|
| 326 | strcpy(fullname+2, fullname+2+remove); | 
|---|
| 327 |  | 
|---|
| 328 | return fullname; | 
|---|
| 329 | } | 
|---|
| 330 |  | 
|---|
| 331 | static void update_ptserver_button(AW_root *, AW_awar *awar_ptserver, AW_awar *awar_buttontext_name) { | 
|---|
| 332 | char *readable_name = readable_pt_servername(awar_ptserver->read_int(), PT_SERVERNAME_LENGTH); | 
|---|
| 333 | awar_buttontext_name->write_string(readable_name); | 
|---|
| 334 | free(readable_name); | 
|---|
| 335 | } | 
|---|
| 336 |  | 
|---|
| 337 | static AW_window *create_PTSERVER_selection_window(AW_root *aw_root, const char *varname) { | 
|---|
| 338 | AW_window_simple *aw_popup = new AW_window_simple; | 
|---|
| 339 |  | 
|---|
| 340 | aw_popup->init(aw_root, "SELECT_PT_SERVER", "Select a PT-Server"); | 
|---|
| 341 | aw_popup->auto_space(10, 10); | 
|---|
| 342 |  | 
|---|
| 343 | const char *CLOSE_ID = "CLOSE"; | 
|---|
| 344 |  | 
|---|
| 345 | aw_popup->at_newline(); | 
|---|
| 346 | aw_root->awar(varname)->add_callback(makeRootCallback(awt_auto_popdown_cb, aw_popup)); | 
|---|
| 347 | AW_selection_list *sellist = aw_popup->create_selection_list(varname, PT_SERVERNAME_SELLIST_WIDTH, 20, true); | 
|---|
| 348 |  | 
|---|
| 349 | aw_popup->at_newline(); | 
|---|
| 350 | aw_popup->callback(AW_POPDOWN); | 
|---|
| 351 | aw_popup->create_button(CLOSE_ID, "CLOSE", "C"); | 
|---|
| 352 |  | 
|---|
| 353 | aw_popup->window_fit(); | 
|---|
| 354 | aw_popup->recalc_pos_atShow(AW_REPOS_TO_MOUSE); | 
|---|
| 355 |  | 
|---|
| 356 | (new PT_selection(sellist))->refresh(); | 
|---|
| 357 |  | 
|---|
| 358 | return aw_popup; | 
|---|
| 359 | } | 
|---|
| 360 |  | 
|---|
| 361 | void awt_create_PTSERVER_selection_button(AW_window *aws, const char *varname) { | 
|---|
| 362 | AW_root *aw_root              = aws->get_root(); | 
|---|
| 363 | char    *awar_buttontext_name = GBS_global_string_copy("/tmp/%s_BUTTON", varname); | 
|---|
| 364 | AW_awar *awar_ptserver        = aw_root->awar(varname); | 
|---|
| 365 | int      ptserver_index       = awar_ptserver->read_int(); | 
|---|
| 366 |  | 
|---|
| 367 | if (ptserver_index<0) { // fix invalid pt_server indices | 
|---|
| 368 | ptserver_index = 0; | 
|---|
| 369 | awar_ptserver->write_int(ptserver_index); | 
|---|
| 370 | } | 
|---|
| 371 |  | 
|---|
| 372 | char *readable_name = readable_pt_servername(ptserver_index, PT_SERVERNAME_LENGTH); | 
|---|
| 373 |  | 
|---|
| 374 | awt_assert(!GB_have_error()); | 
|---|
| 375 |  | 
|---|
| 376 | AW_awar *awar_buttontext = aw_root->awar_string(awar_buttontext_name, readable_name, AW_ROOT_DEFAULT); | 
|---|
| 377 | awar_ptserver->add_callback(makeRootCallback(update_ptserver_button, awar_ptserver, awar_buttontext)); | 
|---|
| 378 |  | 
|---|
| 379 | int old_button_length = aws->get_button_length(); | 
|---|
| 380 |  | 
|---|
| 381 | aws->button_length(PT_SERVERNAME_LENGTH+1); | 
|---|
| 382 | aws->callback(makeCreateWindowCallback(create_PTSERVER_selection_window, awar_ptserver->awar_name)); | 
|---|
| 383 | aws->create_button("CURR_PT_SERVER", awar_buttontext_name); | 
|---|
| 384 |  | 
|---|
| 385 | aws->button_length(old_button_length); | 
|---|
| 386 |  | 
|---|
| 387 | free(readable_name); | 
|---|
| 388 | free(awar_buttontext_name); | 
|---|
| 389 | } | 
|---|
| 390 | void awt_create_PTSERVER_selection_list(AW_window *aws, const char *varname) { | 
|---|
| 391 | (new PT_selection(aws->create_selection_list(varname, true)))->refresh(); | 
|---|
| 392 | } | 
|---|
| 393 |  | 
|---|
| 394 | // ------------------------------------------------- | 
|---|
| 395 | //      selection boxes on editor configurations | 
|---|
| 396 |  | 
|---|
| 397 | struct AWT_configuration_selection : public AW_DB_selection { | 
|---|
| 398 | AWT_configuration_selection(AW_selection_list *sellist_, GBDATA *gb_configuration_data) | 
|---|
| 399 | : AW_DB_selection(sellist_, gb_configuration_data) | 
|---|
| 400 | {} | 
|---|
| 401 |  | 
|---|
| 402 | int getConfigInfo(const char *name, string& comment) { | 
|---|
| 403 | // returns number of species in config + sets comment | 
|---|
| 404 | GB_ERROR   error; | 
|---|
| 405 | GBT_config cfg(get_gb_main(), name, error); | 
|---|
| 406 |  | 
|---|
| 407 | int count = 0; | 
|---|
| 408 | if (!error) { | 
|---|
| 409 | const char *cmt = cfg.get_comment(); | 
|---|
| 410 | comment         = null2empty(cmt); | 
|---|
| 411 | for (int area = 0; area<2; ++area) { | 
|---|
| 412 | GBT_config_parser parser(cfg, area); | 
|---|
| 413 | while (1) { | 
|---|
| 414 | const GBT_config_item& item = parser.nextItem(error); | 
|---|
| 415 | if (error || item.type == CI_END_OF_CONFIG) break; | 
|---|
| 416 | if (item.type == CI_SPECIES) ++count; | 
|---|
| 417 | } | 
|---|
| 418 | } | 
|---|
| 419 | } | 
|---|
| 420 | else { | 
|---|
| 421 | comment = ""; | 
|---|
| 422 | } | 
|---|
| 423 | return count; | 
|---|
| 424 | } | 
|---|
| 425 |  | 
|---|
| 426 | void fill() OVERRIDE { | 
|---|
| 427 | ConstStrArray config; | 
|---|
| 428 | GBT_get_configuration_names(config, get_gb_main()); | 
|---|
| 429 |  | 
|---|
| 430 | if (!config.empty()) { | 
|---|
| 431 | int     maxlen   = 0; | 
|---|
| 432 | int     maxcount = 0; | 
|---|
| 433 | int    *count    = new int[config.size()]; | 
|---|
| 434 | string *comment  = new string[config.size()]; | 
|---|
| 435 |  | 
|---|
| 436 | for (int c = 0; config[c]; ++c) { | 
|---|
| 437 | maxlen   = max(maxlen, int(strlen(config[c]))); | 
|---|
| 438 | count[c] = getConfigInfo(config[c], comment[c]); | 
|---|
| 439 | maxcount = max(maxcount, count[c]); | 
|---|
| 440 | } | 
|---|
| 441 | int maxdigits = calc_digits(maxcount); | 
|---|
| 442 | for (int c = 0; config[c]; ++c) { | 
|---|
| 443 | int digits = calc_digits(count[c]); | 
|---|
| 444 | insert(GBS_global_string("%-*s %*s(%i) %s", maxlen, config[c], (maxdigits-digits), "", count[c], comment[c].c_str()), config[c]); | 
|---|
| 445 | } | 
|---|
| 446 | delete [] comment; | 
|---|
| 447 | delete [] count; | 
|---|
| 448 | } | 
|---|
| 449 | insert_default(DISPLAY_NONE, NO_CONFIG_SELECTED); | 
|---|
| 450 | } | 
|---|
| 451 | }; | 
|---|
| 452 |  | 
|---|
| 453 | AW_DB_selection *awt_create_CONFIG_selection_list(GBDATA *gb_main, AW_window *aws, const char *varname, bool fallback2default) { | 
|---|
| 454 | GBDATA *gb_configuration_data; | 
|---|
| 455 | { | 
|---|
| 456 | GB_transaction ta(gb_main); | 
|---|
| 457 | gb_configuration_data = GB_search(gb_main, CONFIG_DATA_PATH, GB_CREATE_CONTAINER); | 
|---|
| 458 | } | 
|---|
| 459 | AW_selection_list           *sellist = aws->create_selection_list(varname, 40, 15, fallback2default); | 
|---|
| 460 | AWT_configuration_selection *confSel = new AWT_configuration_selection(sellist, gb_configuration_data); | 
|---|
| 461 | confSel->refresh(); | 
|---|
| 462 | return confSel; | 
|---|
| 463 | } | 
|---|
| 464 |  | 
|---|
| 465 | // ---------------------- | 
|---|
| 466 | //      SAI selection | 
|---|
| 467 |  | 
|---|
| 468 |  | 
|---|
| 469 | static char *get_SAI_description(GBDATA *gb_extended) { | 
|---|
| 470 | const char *name     = GBT_get_name_or_description(gb_extended); | 
|---|
| 471 | GBDATA     *gb_group = GB_entry(gb_extended, "sai_group"); | 
|---|
| 472 |  | 
|---|
| 473 | if (gb_group) { | 
|---|
| 474 | const char *group = GB_read_char_pntr(gb_group); | 
|---|
| 475 | return GBS_global_string_copy("[%s] %s", group, name); | 
|---|
| 476 | } | 
|---|
| 477 | return ARB_strdup(name); | 
|---|
| 478 | } | 
|---|
| 479 |  | 
|---|
| 480 | const SaiSelectionlistFilterCallback& awt_std_SAI_filter_cb() { | 
|---|
| 481 | static SaiSelectionlistFilterCallback std_SAI_filter_cb = makeSaiSelectionlistFilterCallback(get_SAI_description); | 
|---|
| 482 | return std_SAI_filter_cb; | 
|---|
| 483 | } | 
|---|
| 484 |  | 
|---|
| 485 | class SAI_selection : public AW_DB_selection { // derived from a Noncopyable | 
|---|
| 486 | SaiSelectionlistFilterCallback filter_cb; | 
|---|
| 487 |  | 
|---|
| 488 | public: | 
|---|
| 489 |  | 
|---|
| 490 | SAI_selection(AW_selection_list *sellist_, GBDATA *gb_sai_data, const SaiSelectionlistFilterCallback& fcb) | 
|---|
| 491 | : AW_DB_selection(sellist_, gb_sai_data), | 
|---|
| 492 | filter_cb(fcb) | 
|---|
| 493 | {} | 
|---|
| 494 |  | 
|---|
| 495 | void fill() OVERRIDE; | 
|---|
| 496 | }; | 
|---|
| 497 |  | 
|---|
| 498 | void SAI_selection::fill() { | 
|---|
| 499 | AW_selection_list *sel = get_sellist(); | 
|---|
| 500 | sel->clear(); | 
|---|
| 501 |  | 
|---|
| 502 | GBDATA         *gb_main = get_gb_main(); | 
|---|
| 503 | GB_transaction  ta(gb_main); | 
|---|
| 504 |  | 
|---|
| 505 | for (GBDATA *gb_extended = GBT_first_SAI(gb_main); | 
|---|
| 506 | gb_extended; | 
|---|
| 507 | gb_extended = GBT_next_SAI(gb_extended)) | 
|---|
| 508 | { | 
|---|
| 509 | char *res = filter_cb(gb_extended); | 
|---|
| 510 | if (res) { | 
|---|
| 511 | sel->insert(res, GBT_get_name_or_description(gb_extended)); | 
|---|
| 512 | free(res); | 
|---|
| 513 | } | 
|---|
| 514 | } | 
|---|
| 515 | sel->sort(false, false); | 
|---|
| 516 |  | 
|---|
| 517 | sel->insert_default(DISPLAY_NONE, ""); | 
|---|
| 518 | sel->update(); | 
|---|
| 519 | } | 
|---|
| 520 |  | 
|---|
| 521 | class SAI_sellst_spec : public SelectionListSpec, virtual Noncopyable { | 
|---|
| 522 | GBDATA                         *gb_main; | 
|---|
| 523 | SaiSelectionlistFilterCallback  filter_cb; | 
|---|
| 524 |  | 
|---|
| 525 | AW_DB_selection *create(AW_selection_list *sellist) const { | 
|---|
| 526 | GB_transaction ta(gb_main); | 
|---|
| 527 | return new SAI_selection(sellist, GBT_get_SAI_data(gb_main), filter_cb); | 
|---|
| 528 | } | 
|---|
| 529 |  | 
|---|
| 530 | public: | 
|---|
| 531 | SAI_sellst_spec(const char *awar_name_, GBDATA *gb_main_, const SaiSelectionlistFilterCallback& fcb) | 
|---|
| 532 | : SelectionListSpec(awar_name_), | 
|---|
| 533 | gb_main(gb_main_), | 
|---|
| 534 | filter_cb(fcb) | 
|---|
| 535 | { | 
|---|
| 536 | // Warning: do not use different filters for same awar! (wont work as expected) // @@@ add assertion against | 
|---|
| 537 | } | 
|---|
| 538 |  | 
|---|
| 539 | const char *get_macro_id() const { return "SELECT_SAI"; } | 
|---|
| 540 | const char *get_title() const { return "Select SAI"; } | 
|---|
| 541 | }; | 
|---|
| 542 |  | 
|---|
| 543 | void awt_popup_SAI_selection_list(AW_window *, const char *awar_name, GBDATA *gb_main) { | 
|---|
| 544 | SAI_sellst_spec spec(awar_name, gb_main, awt_std_SAI_filter_cb()); | 
|---|
| 545 | spec.popup(); | 
|---|
| 546 | } | 
|---|
| 547 |  | 
|---|
| 548 | AW_DB_selection *awt_create_SAI_selection_list(GBDATA *gb_main, AW_window *aws, const char *varname, bool fallback2default, const SaiSelectionlistFilterCallback& fcb) { | 
|---|
| 549 | /* Selection list for SAIs | 
|---|
| 550 | * only shows those SAIs for which fcb returns a string (string must be a heap copy) | 
|---|
| 551 | */ | 
|---|
| 552 | SAI_sellst_spec spec(varname, gb_main, fcb); | 
|---|
| 553 | return spec.create_list(aws, fallback2default); | 
|---|
| 554 | } | 
|---|
| 555 |  | 
|---|
| 556 | void awt_create_SAI_selection_button(GBDATA *gb_main, AW_window *aws, const char *varname, const SaiSelectionlistFilterCallback& fcb) { | 
|---|
| 557 | (new SAI_sellst_spec(varname, gb_main, fcb))->createButton(aws); // do not free (bound to callback) | 
|---|
| 558 | } | 
|---|
| 559 |  | 
|---|
| 560 | // -------------------------------------------------- | 
|---|
| 561 | //      save/load selection content to/from file | 
|---|
| 562 |  | 
|---|
| 563 | static GB_ERROR standard_list2file(const CharPtrArray& display, const CharPtrArray& value, StrArray& line) { | 
|---|
| 564 | GB_ERROR error = NULp; | 
|---|
| 565 | for (size_t i = 0; i<display.size() && !error; ++i) { | 
|---|
| 566 | const char *disp = display[i]; | 
|---|
| 567 |  | 
|---|
| 568 | if (disp[0] == '#') { // would interpret as comment when loaded | 
|---|
| 569 | error = "Invalid character '#' at start of displayed text (won't load)"; | 
|---|
| 570 | } | 
|---|
| 571 | else { | 
|---|
| 572 | if (strchr(disp, ',')) { | 
|---|
| 573 | // would be interpreted as separator between display and value on load | 
|---|
| 574 | error = "Invalid character ',' in displayed text (won't load correctly)"; | 
|---|
| 575 | } | 
|---|
| 576 | else { | 
|---|
| 577 | awt_assert(!strchr(disp, '\n')); | 
|---|
| 578 |  | 
|---|
| 579 | const char *val = value[i]; | 
|---|
| 580 | if (strcmp(disp, val) == 0) { | 
|---|
| 581 | line.put(ARB_strdup(disp)); | 
|---|
| 582 | } | 
|---|
| 583 | else { | 
|---|
| 584 | char *escaped = GBS_escape_string(val, "\n", '\\'); | 
|---|
| 585 | line.put(GBS_global_string_copy("%s,%s", disp, escaped)); | 
|---|
| 586 | free(escaped); | 
|---|
| 587 | } | 
|---|
| 588 | } | 
|---|
| 589 | } | 
|---|
| 590 | } | 
|---|
| 591 | return NULp; | 
|---|
| 592 | } | 
|---|
| 593 |  | 
|---|
| 594 |  | 
|---|
| 595 |  | 
|---|
| 596 | static GB_ERROR standard_file2list(const CharPtrArray& line, StrArray& display, StrArray& value) { | 
|---|
| 597 | for (size_t i = 0; i<line.size(); ++i) { | 
|---|
| 598 | if (line[i][0] == '#') continue; // ignore comments | 
|---|
| 599 |  | 
|---|
| 600 | const char *comma = strchr(line[i], ','); | 
|---|
| 601 | if (comma) { | 
|---|
| 602 | display.put(ARB_strpartdup(line[i], comma-1)); | 
|---|
| 603 |  | 
|---|
| 604 | comma++; | 
|---|
| 605 | const char *rest      = comma+strspn(comma, " \t"); | 
|---|
| 606 | char       *unescaped = GBS_unescape_string(rest, "\n", '\\'); | 
|---|
| 607 | value.put(unescaped); | 
|---|
| 608 | } | 
|---|
| 609 | else { | 
|---|
| 610 | display.put(ARB_strdup(line[i])); | 
|---|
| 611 | value.put(ARB_strdup(line[i])); | 
|---|
| 612 | } | 
|---|
| 613 | } | 
|---|
| 614 |  | 
|---|
| 615 | return NULp; | 
|---|
| 616 | } | 
|---|
| 617 |  | 
|---|
| 618 | StorableSelectionList::StorableSelectionList(const TypedSelectionList& tsl_) | 
|---|
| 619 | : tsl(tsl_), | 
|---|
| 620 | list2file(standard_list2file), | 
|---|
| 621 | file2list(standard_file2list) | 
|---|
| 622 | {} | 
|---|
| 623 |  | 
|---|
| 624 | inline char *get_shared_sellist_awar_base(const TypedSelectionList& typedsellst) { | 
|---|
| 625 | return GBS_global_string_copy("tmp/sellist/%s", typedsellst.get_shared_id()); | 
|---|
| 626 | } | 
|---|
| 627 | inline char *get_shared_sellist_awar_name(const TypedSelectionList& typedsellst, const char *name) { | 
|---|
| 628 | char *base      = get_shared_sellist_awar_base(typedsellst); | 
|---|
| 629 | char *awar_name = GBS_global_string_copy("%s/%s", base, name); | 
|---|
| 630 | free(base); | 
|---|
| 631 | return awar_name; | 
|---|
| 632 | } | 
|---|
| 633 |  | 
|---|
| 634 | GB_ERROR StorableSelectionList::save(const char *filename, long number_of_lines) const { | 
|---|
| 635 | // number_of_lines == 0 -> save all lines (otherwise truncate after 'number_of_lines') | 
|---|
| 636 |  | 
|---|
| 637 | StrArray           display, values; | 
|---|
| 638 | AW_selection_list *sellist = tsl.get_sellist(); | 
|---|
| 639 |  | 
|---|
| 640 | sellist->to_array(display, false); | 
|---|
| 641 | sellist->to_array(values, true); | 
|---|
| 642 |  | 
|---|
| 643 | awt_assert(display.size() == values.size()); | 
|---|
| 644 |  | 
|---|
| 645 | if (number_of_lines>0) { // limit number of lines? | 
|---|
| 646 | display.resize(number_of_lines); | 
|---|
| 647 | values.resize(number_of_lines); | 
|---|
| 648 | } | 
|---|
| 649 |  | 
|---|
| 650 | GB_ERROR error = NULp; | 
|---|
| 651 | if (display.size()<1) { | 
|---|
| 652 | error = "List is empty (did not save)"; | 
|---|
| 653 | } | 
|---|
| 654 | else { | 
|---|
| 655 | StrArray line; | 
|---|
| 656 | error = list2file(display, values, line); | 
|---|
| 657 | if (!error) { | 
|---|
| 658 | if (line.size()<1) { | 
|---|
| 659 | error = "list>file conversion produced nothing (internal error)"; | 
|---|
| 660 | } | 
|---|
| 661 | else { | 
|---|
| 662 | FILE *out = fopen(filename, "wt"); | 
|---|
| 663 | if (!out) { | 
|---|
| 664 | error = GB_IO_error("writing", filename); | 
|---|
| 665 | } | 
|---|
| 666 | else { | 
|---|
| 667 | const char *warning = NULp; | 
|---|
| 668 | for (size_t i = 0; i<line.size(); ++i) { | 
|---|
| 669 | if (!warning && strchr(line[i], '\n')) { | 
|---|
| 670 | warning = "Warning: Saved content contains LFs (loading will be impossible)"; | 
|---|
| 671 | } | 
|---|
| 672 | fputs(line[i], out); | 
|---|
| 673 | fputc('\n', out); | 
|---|
| 674 | } | 
|---|
| 675 | fclose(out); | 
|---|
| 676 | error = warning; | 
|---|
| 677 | } | 
|---|
| 678 | } | 
|---|
| 679 | } | 
|---|
| 680 | } | 
|---|
| 681 |  | 
|---|
| 682 | return error; | 
|---|
| 683 | } | 
|---|
| 684 |  | 
|---|
| 685 | GB_ERROR StorableSelectionList::load(const char *filemask, bool append) const { | 
|---|
| 686 | GB_ERROR error = NULp; | 
|---|
| 687 | StrArray fnames; | 
|---|
| 688 |  | 
|---|
| 689 | if (GB_is_directory(filemask)) { | 
|---|
| 690 | error = GBS_global_string("refusing to load all files from directory\n" | 
|---|
| 691 | "'%s'\n" | 
|---|
| 692 | "(one possibility is to enter '*.%s')'", | 
|---|
| 693 | filemask, get_filter()); | 
|---|
| 694 | } | 
|---|
| 695 | else { | 
|---|
| 696 | GBS_read_dir(fnames, filemask, NULp); | 
|---|
| 697 | error = GB_incur_error_if(fnames.empty()); | 
|---|
| 698 | } | 
|---|
| 699 |  | 
|---|
| 700 | StrArray lines; | 
|---|
| 701 | for (int f = 0; fnames[f] && !error; ++f) { | 
|---|
| 702 | FILE *in = fopen(fnames[f], "rb"); | 
|---|
| 703 | if (!in) { | 
|---|
| 704 | error = GB_IO_error("reading", fnames[f]); | 
|---|
| 705 | } | 
|---|
| 706 | else { | 
|---|
| 707 | BufferedFileReader file(fnames[f], in); | 
|---|
| 708 | string line; | 
|---|
| 709 | while (file.getLine(line)) { | 
|---|
| 710 | if (!line.empty()) lines.put(ARB_stringdup(line)); | 
|---|
| 711 | } | 
|---|
| 712 | } | 
|---|
| 713 | } | 
|---|
| 714 |  | 
|---|
| 715 | AW_selection_list *sellist = tsl.get_sellist(); | 
|---|
| 716 | if (!append) sellist->clear(); | 
|---|
| 717 |  | 
|---|
| 718 | if (!error) { | 
|---|
| 719 | StrArray displayed, values; | 
|---|
| 720 | error = file2list(lines, displayed, values); | 
|---|
| 721 | if (!error) { | 
|---|
| 722 | int dsize = displayed.size(); | 
|---|
| 723 | int vsize = values.size(); | 
|---|
| 724 |  | 
|---|
| 725 | if (dsize != vsize) { | 
|---|
| 726 | error = GBS_global_string("Error in translation (value/display mismatch: %i!=%i)", vsize, dsize); | 
|---|
| 727 | } | 
|---|
| 728 | else { | 
|---|
| 729 | for (int i = 0; i<dsize; ++i) { | 
|---|
| 730 | sellist->insert(displayed[i], values[i]); | 
|---|
| 731 | } | 
|---|
| 732 | sellist->insert_default("", ""); | 
|---|
| 733 | } | 
|---|
| 734 | } | 
|---|
| 735 | } | 
|---|
| 736 |  | 
|---|
| 737 | if (error) { | 
|---|
| 738 | sellist->insert_default(GBS_global_string("Error: %s", error), ""); | 
|---|
| 739 | } | 
|---|
| 740 | sellist->update(); | 
|---|
| 741 |  | 
|---|
| 742 | return error; | 
|---|
| 743 | } | 
|---|
| 744 |  | 
|---|
| 745 | static void save_list_cb(AW_window *aww, const StorableSelectionList *storabsellist) { | 
|---|
| 746 | const TypedSelectionList& typedsellist = storabsellist->get_typedsellist(); | 
|---|
| 747 |  | 
|---|
| 748 | char    *awar_prefix = get_shared_sellist_awar_base(typedsellist); | 
|---|
| 749 | char    *bline_anz   = get_shared_sellist_awar_name(typedsellist, "line_anz"); | 
|---|
| 750 | AW_root *aw_root     = aww->get_root(); | 
|---|
| 751 | char    *filename    = AW_get_selected_fullname(aw_root, awar_prefix); | 
|---|
| 752 |  | 
|---|
| 753 | long     lineLimit = aw_root->awar(bline_anz)->read_int(); | 
|---|
| 754 | GB_ERROR error     = storabsellist->save(filename, lineLimit); | 
|---|
| 755 |  | 
|---|
| 756 | if (!error) AW_refresh_fileselection(aw_root, awar_prefix); | 
|---|
| 757 | aww->hide_or_notify(error); | 
|---|
| 758 |  | 
|---|
| 759 | free(filename); | 
|---|
| 760 | free(bline_anz); | 
|---|
| 761 | free(awar_prefix); | 
|---|
| 762 | } | 
|---|
| 763 |  | 
|---|
| 764 | static void load_list_cb(AW_window *aww, const StorableSelectionList *storabsellist) { | 
|---|
| 765 | const TypedSelectionList& typedsellist = storabsellist->get_typedsellist(); | 
|---|
| 766 |  | 
|---|
| 767 | AW_root  *aw_root     = aww->get_root(); | 
|---|
| 768 | char     *awar_prefix = get_shared_sellist_awar_base(typedsellist); | 
|---|
| 769 | char     *awar_append = get_shared_sellist_awar_name(typedsellist, "append"); | 
|---|
| 770 | bool      append      = aw_root->awar(awar_append)->read_int(); | 
|---|
| 771 | char     *filename    = AW_get_selected_fullname(aw_root, awar_prefix); | 
|---|
| 772 | GB_ERROR  error       = storabsellist->load(filename, append); | 
|---|
| 773 |  | 
|---|
| 774 | aww->hide_or_notify(error); | 
|---|
| 775 |  | 
|---|
| 776 | free(filename); | 
|---|
| 777 | free(awar_append); | 
|---|
| 778 | free(awar_prefix); | 
|---|
| 779 | } | 
|---|
| 780 |  | 
|---|
| 781 | AW_window *create_save_box_for_selection_lists(AW_root *aw_root, const StorableSelectionList *storabsellist) { | 
|---|
| 782 | const TypedSelectionList& typedsellist = storabsellist->get_typedsellist(); | 
|---|
| 783 |  | 
|---|
| 784 | char *awar_base     = get_shared_sellist_awar_base(typedsellist); | 
|---|
| 785 | char *awar_line_anz = get_shared_sellist_awar_name(typedsellist, "line_anz"); | 
|---|
| 786 | { | 
|---|
| 787 | char *def_name = GBS_string_2_key(typedsellist.whats_contained()); | 
|---|
| 788 | AW_create_fileselection_awars(aw_root, awar_base, ".", storabsellist->get_filter(), def_name); | 
|---|
| 789 | free(def_name); | 
|---|
| 790 | aw_root->awar_int(awar_line_anz, 0, AW_ROOT_DEFAULT); | 
|---|
| 791 | } | 
|---|
| 792 |  | 
|---|
| 793 | AW_window_simple *aws = new AW_window_simple; | 
|---|
| 794 |  | 
|---|
| 795 | char *window_id    = GBS_global_string_copy("SAVE_SELECTION_BOX_%s", typedsellist.get_unique_id()); | 
|---|
| 796 | char *window_title = GBS_global_string_copy("Save %s", typedsellist.whats_contained()); | 
|---|
| 797 |  | 
|---|
| 798 | aws->init(aw_root, window_id, window_title); | 
|---|
| 799 | aws->load_xfig("sl_s_box.fig"); | 
|---|
| 800 |  | 
|---|
| 801 | aws->button_length(10); | 
|---|
| 802 |  | 
|---|
| 803 | aws->at("cancel"); | 
|---|
| 804 | aws->callback(AW_POPDOWN); | 
|---|
| 805 | aws->create_button("CANCEL", "CANCEL", "C"); | 
|---|
| 806 |  | 
|---|
| 807 | aws->at("save"); | 
|---|
| 808 | aws->highlight(); | 
|---|
| 809 | aws->callback(makeWindowCallback(save_list_cb, storabsellist)); | 
|---|
| 810 | aws->create_button("SAVE", "SAVE", "S"); | 
|---|
| 811 |  | 
|---|
| 812 | aws->at("nlines"); | 
|---|
| 813 | aws->create_option_menu(awar_line_anz, true); | 
|---|
| 814 | aws->insert_default_option("all",   "a", 0); | 
|---|
| 815 | aws->insert_option        ("10",    "",  10); | 
|---|
| 816 | aws->insert_option        ("50",    "",  50); | 
|---|
| 817 | aws->insert_option        ("100",   "",  100); | 
|---|
| 818 | aws->insert_option        ("500",   "",  500); | 
|---|
| 819 | aws->insert_option        ("1000",  "",  1000); | 
|---|
| 820 | aws->insert_option        ("5000",  "",  5000); | 
|---|
| 821 | aws->insert_option        ("10000", "",  10000); | 
|---|
| 822 | aws->update_option_menu(); | 
|---|
| 823 |  | 
|---|
| 824 | AW_create_standard_fileselection(aws, awar_base); | 
|---|
| 825 |  | 
|---|
| 826 | free(window_title); | 
|---|
| 827 | free(window_id); | 
|---|
| 828 | free(awar_line_anz); | 
|---|
| 829 | free(awar_base); | 
|---|
| 830 |  | 
|---|
| 831 | aws->recalc_pos_atShow(AW_REPOS_TO_MOUSE); | 
|---|
| 832 |  | 
|---|
| 833 | return aws; | 
|---|
| 834 | } | 
|---|
| 835 |  | 
|---|
| 836 | AW_window *create_load_box_for_selection_lists(AW_root *aw_root, const StorableSelectionList *storabsellist) { | 
|---|
| 837 | const TypedSelectionList& typedsellist = storabsellist->get_typedsellist(); | 
|---|
| 838 |  | 
|---|
| 839 | char *awar_base_name = get_shared_sellist_awar_base(typedsellist); | 
|---|
| 840 | char *awar_append    = get_shared_sellist_awar_name(typedsellist, "append"); | 
|---|
| 841 |  | 
|---|
| 842 | AW_create_fileselection_awars(aw_root, awar_base_name, ".", storabsellist->get_filter(), ""); | 
|---|
| 843 | aw_root->awar_int(awar_append, 1); // append is default ( = old behavior) | 
|---|
| 844 |  | 
|---|
| 845 | AW_window_simple *aws = new AW_window_simple; | 
|---|
| 846 |  | 
|---|
| 847 | char *window_id    = GBS_global_string_copy("LOAD_SELECTION_BOX_%s", typedsellist.get_unique_id()); | 
|---|
| 848 | char *window_title = GBS_global_string_copy("Load %s", typedsellist.whats_contained()); | 
|---|
| 849 |  | 
|---|
| 850 | aws->init(aw_root, window_id, window_title); | 
|---|
| 851 | aws->load_xfig("sl_l_box.fig"); | 
|---|
| 852 |  | 
|---|
| 853 | aws->at("cancel"); | 
|---|
| 854 | aws->callback(AW_POPDOWN); | 
|---|
| 855 | aws->create_button("CANCEL", "CANCEL", "C"); | 
|---|
| 856 |  | 
|---|
| 857 | aws->at("load"); | 
|---|
| 858 | aws->highlight(); | 
|---|
| 859 | aws->callback(makeWindowCallback(load_list_cb, storabsellist)); | 
|---|
| 860 | aws->create_button("LOAD", "LOAD", "L"); | 
|---|
| 861 |  | 
|---|
| 862 | aws->at("append"); | 
|---|
| 863 | aws->label("Append?"); | 
|---|
| 864 | aws->create_toggle(awar_append); | 
|---|
| 865 |  | 
|---|
| 866 | AW_create_fileselection(aws, awar_base_name, "", "PWD", ANY_DIR, true); | 
|---|
| 867 |  | 
|---|
| 868 | aws->recalc_pos_atShow(AW_REPOS_TO_MOUSE); | 
|---|
| 869 |  | 
|---|
| 870 | free(window_title); | 
|---|
| 871 | free(window_id); | 
|---|
| 872 | free(awar_append); | 
|---|
| 873 | free(awar_base_name); | 
|---|
| 874 |  | 
|---|
| 875 | return aws; | 
|---|
| 876 | } | 
|---|
| 877 |  | 
|---|
| 878 | void awt_clear_selection_list_cb(AW_window *, AW_selection_list *sellist) { | 
|---|
| 879 | sellist->clear(); | 
|---|
| 880 | sellist->insert_default("", ""); | 
|---|
| 881 | sellist->update(); | 
|---|
| 882 | } | 
|---|
| 883 |  | 
|---|
| 884 | void create_print_box_for_selection_lists(AW_window *aw_window, const TypedSelectionList *typedsellist) { | 
|---|
| 885 | char *data = typedsellist->get_sellist()->get_content_as_string(0); | 
|---|
| 886 | AWT_create_ascii_print_window(aw_window->get_root(), data, typedsellist->whats_contained()); | 
|---|
| 887 | free(data); | 
|---|
| 888 | } | 
|---|
| 889 |  | 
|---|
| 890 | AW_window *awt_create_load_box(AW_root     *aw_root, | 
|---|
| 891 | const char  *action, | 
|---|
| 892 | const char  *what, | 
|---|
| 893 | const char  *default_directory, | 
|---|
| 894 | const char  *file_extension, | 
|---|
| 895 | char       **set_file_name_awar, | 
|---|
| 896 | const WindowCallback& ok_cb, | 
|---|
| 897 | const WindowCallback& close_cb, | 
|---|
| 898 | const char *close_button_text) | 
|---|
| 899 | { | 
|---|
| 900 | /* general purpose file selection box | 
|---|
| 901 | * | 
|---|
| 902 | * 'action' describes what is intended to be done (e.g. "Load"). | 
|---|
| 903 | * used for window title and button. | 
|---|
| 904 | * | 
|---|
| 905 | * 'what' describes what is going to be loaded (e.g. "destination database") | 
|---|
| 906 | * It is also used to create the awars for the filebox, i.e. same description for multiple | 
|---|
| 907 | * fileboxes makes them share the awars. | 
|---|
| 908 | * | 
|---|
| 909 | * if 'set_file_name_awar' is non-NULp, it'll be set to a heap-copy of the awar-name | 
|---|
| 910 | * containing the full selected filename. | 
|---|
| 911 | * | 
|---|
| 912 | * 'default_directory' specifies the directory opened in the filebox | 
|---|
| 913 | * | 
|---|
| 914 | * 'file_extension' specifies the filter to be used (which files are shown) | 
|---|
| 915 | * | 
|---|
| 916 | * You have to provide an 'ok_cb', which will be called when 'OK' is pressed. | 
|---|
| 917 | * Optionally you may pass a 'close_cb' which will be called when 'CLOSE' is pressed. | 
|---|
| 918 | * If not given, AW_POPDOWN will be called. | 
|---|
| 919 | * | 
|---|
| 920 | * Both callbacks will be called as callbacks of the load-box-window. | 
|---|
| 921 | * The load-box does not popdown, the callback has to do that. | 
|---|
| 922 | * | 
|---|
| 923 | * Optionally you may also pass the button text for the 'CLOSE'-button (e.g. 'EXIT' or 'Abort') | 
|---|
| 924 | */ | 
|---|
| 925 |  | 
|---|
| 926 |  | 
|---|
| 927 | char *what_key  = GBS_string_2_key(what); | 
|---|
| 928 | char *base_name = GBS_global_string_copy("tmp/load_box_%s", what_key); | 
|---|
| 929 |  | 
|---|
| 930 | AW_create_fileselection_awars(aw_root, base_name, default_directory, file_extension, ""); | 
|---|
| 931 |  | 
|---|
| 932 | if (set_file_name_awar) { | 
|---|
| 933 | *set_file_name_awar = GBS_global_string_copy("%s/file_name", base_name); | 
|---|
| 934 | } | 
|---|
| 935 |  | 
|---|
| 936 | AW_window_simple *aws = new AW_window_simple; | 
|---|
| 937 | { | 
|---|
| 938 | char title[100]; | 
|---|
| 939 | sprintf(title, "%s %s", action, what); | 
|---|
| 940 | aws->init(aw_root, title, title); | 
|---|
| 941 | aws->load_xfig("load_box.fig"); | 
|---|
| 942 | } | 
|---|
| 943 |  | 
|---|
| 944 | aws->at("close"); | 
|---|
| 945 | aws->callback(close_cb); | 
|---|
| 946 | if (close_button_text) { | 
|---|
| 947 | aws->create_button("CLOSE", close_button_text, ""); | 
|---|
| 948 | } | 
|---|
| 949 | else { | 
|---|
| 950 | aws->create_button("CLOSE", "CLOSE", "C"); | 
|---|
| 951 | } | 
|---|
| 952 |  | 
|---|
| 953 | #if 0 | 
|---|
| 954 | // @@@ allow to pass helpfile | 
|---|
| 955 | aws->at("help"); | 
|---|
| 956 | aws->callback(makeHelpCallback("")); | 
|---|
| 957 | aws->create_button("HELP", "HELP"); | 
|---|
| 958 | #endif | 
|---|
| 959 |  | 
|---|
| 960 | aws->at("go"); | 
|---|
| 961 | aws->callback(ok_cb); | 
|---|
| 962 | aws->create_autosize_button("GO", action); | 
|---|
| 963 |  | 
|---|
| 964 | AW_create_standard_fileselection(aws, base_name); | 
|---|
| 965 | free(base_name); | 
|---|
| 966 | free(what_key); | 
|---|
| 967 | aws->recalc_pos_atShow(AW_REPOS_TO_MOUSE); | 
|---|
| 968 |  | 
|---|
| 969 | return aws; | 
|---|
| 970 | } | 
|---|
| 971 |  | 
|---|
| 972 | // -------------------------------------------------------------------------------- | 
|---|
| 973 |  | 
|---|
| 974 | #define SUBSET_NOELEM_DISPLAY "<none>" | 
|---|
| 975 |  | 
|---|
| 976 | class AW_subset_selection : public AW_selection { // only works with string-type selections | 
|---|
| 977 | AW_selection_list& parent_sellist; | 
|---|
| 978 |  | 
|---|
| 979 | SubsetChangedCb subChanged_cb; | 
|---|
| 980 | AW_CL           cl_user; | 
|---|
| 981 |  | 
|---|
| 982 | static void finish_fill_box(AW_selection_list *parent_sellist, AW_selection_list *sub_sellist) { | 
|---|
| 983 | sub_sellist->insert_default(parent_sellist->get_default_display(), *parent_sellist->get_default_value()); | 
|---|
| 984 | sub_sellist->update(); | 
|---|
| 985 | } | 
|---|
| 986 |  | 
|---|
| 987 | static AW_selection_list *create_box(AW_window *aww, AW_selection_list& parent_sellist) { | 
|---|
| 988 | const char *parent_awar_name = parent_sellist.get_awar_name(); | 
|---|
| 989 | awt_assert(parent_awar_name[0] != '/'); | 
|---|
| 990 | awt_assert(parent_sellist.get_awar_type() == GB_STRING); // only impl for strings | 
|---|
| 991 |  | 
|---|
| 992 | AW_root *aw_root   = aww->get_root(); | 
|---|
| 993 | char    *awar_name = GBS_global_string_copy("tmp/subsel/%s", parent_awar_name); | 
|---|
| 994 |  | 
|---|
| 995 | aw_root->awar_string(awar_name); | 
|---|
| 996 |  | 
|---|
| 997 | AW_selection_list *sub_sellist = aww->create_selection_list(awar_name, true); | 
|---|
| 998 | finish_fill_box(&parent_sellist, sub_sellist); | 
|---|
| 999 |  | 
|---|
| 1000 | free(awar_name); | 
|---|
| 1001 |  | 
|---|
| 1002 | return sub_sellist; | 
|---|
| 1003 | } | 
|---|
| 1004 |  | 
|---|
| 1005 | void callChangedCallback(bool interactive_change) { if (subChanged_cb) subChanged_cb(this, interactive_change, cl_user); } | 
|---|
| 1006 |  | 
|---|
| 1007 | public: | 
|---|
| 1008 | AW_subset_selection(AW_window *aww, AW_selection_list& parent_sellist_, SubsetChangedCb subChanged_cb_, AW_CL cl_user_) | 
|---|
| 1009 | : AW_selection(create_box(aww, parent_sellist_)), | 
|---|
| 1010 | parent_sellist(parent_sellist_), | 
|---|
| 1011 | subChanged_cb(subChanged_cb_), | 
|---|
| 1012 | cl_user(cl_user_) | 
|---|
| 1013 | { | 
|---|
| 1014 | callChangedCallback(false); | 
|---|
| 1015 | } | 
|---|
| 1016 |  | 
|---|
| 1017 | AW_selection_list *get_parent_sellist() const { return &parent_sellist; } | 
|---|
| 1018 |  | 
|---|
| 1019 | const char *default_select_value() const { return parent_sellist.get_default_value()->get_string(); } | 
|---|
| 1020 | const char *default_select_display() const { return parent_sellist.get_default_display(); } | 
|---|
| 1021 |  | 
|---|
| 1022 | void fill() OVERRIDE { awt_assert(0); } // unused | 
|---|
| 1023 |  | 
|---|
| 1024 | void collect_subset_cb(awt_collect_mode what) { | 
|---|
| 1025 | AW_selection_list *subset_list = get_sellist(); | 
|---|
| 1026 | AW_selection_list *whole_list  = get_parent_sellist(); | 
|---|
| 1027 |  | 
|---|
| 1028 | switch(what) { | 
|---|
| 1029 | case ACM_FILL: | 
|---|
| 1030 | for (AW_selection_list_iterator listEntry(whole_list); listEntry; ++listEntry) { | 
|---|
| 1031 | if (subset_list->get_index_of(*listEntry.get_value()) == -1) { // only add not already existing elements | 
|---|
| 1032 | subset_list->insert(listEntry.get_displayed(), *listEntry.get_value()); | 
|---|
| 1033 | } | 
|---|
| 1034 | } | 
|---|
| 1035 | finish_fill_box(whole_list, subset_list); | 
|---|
| 1036 | break; | 
|---|
| 1037 |  | 
|---|
| 1038 | case ACM_ADD: { | 
|---|
| 1039 | if (!whole_list->default_is_selected()) { | 
|---|
| 1040 | AW_scalar selected  = whole_list->get_awar_value(); | 
|---|
| 1041 | int       src_index = whole_list->get_index_of(selected); | 
|---|
| 1042 |  | 
|---|
| 1043 | if (subset_list->get_index_of(selected) == -1) { // not yet in subset_list | 
|---|
| 1044 | AW_selection_list_iterator entry(whole_list, src_index); | 
|---|
| 1045 | subset_list->insert(entry.get_displayed(), *entry.get_value()); | 
|---|
| 1046 | subset_list->update(); | 
|---|
| 1047 | } | 
|---|
| 1048 |  | 
|---|
| 1049 | subset_list->set_awar_value(selected);      // position right side to newly added or already existing alignment | 
|---|
| 1050 | whole_list->select_element_at(src_index+1); // go down 1 position on left side | 
|---|
| 1051 | } | 
|---|
| 1052 |  | 
|---|
| 1053 | break; | 
|---|
| 1054 | } | 
|---|
| 1055 | case ACM_REMOVE: { | 
|---|
| 1056 | if (!subset_list->default_is_selected()) { | 
|---|
| 1057 | AW_scalar selected     = subset_list->get_awar_value(); | 
|---|
| 1058 | int       old_position = subset_list->get_index_of(selected); | 
|---|
| 1059 |  | 
|---|
| 1060 | subset_list->delete_element_at(old_position); | 
|---|
| 1061 | finish_fill_box(whole_list, subset_list); | 
|---|
| 1062 |  | 
|---|
| 1063 | subset_list->select_element_at(old_position); | 
|---|
| 1064 | whole_list->set_awar_value(selected); // set left selection to deleted alignment | 
|---|
| 1065 | } | 
|---|
| 1066 | break; | 
|---|
| 1067 | } | 
|---|
| 1068 | case ACM_EMPTY: | 
|---|
| 1069 | subset_list->clear(); | 
|---|
| 1070 | finish_fill_box(whole_list, subset_list); | 
|---|
| 1071 | break; | 
|---|
| 1072 | } | 
|---|
| 1073 | callChangedCallback(true); | 
|---|
| 1074 | } | 
|---|
| 1075 | void reorder_subset_cb(awt_reorder_mode dest) { | 
|---|
| 1076 | AW_selection_list *subset_list = get_sellist(); | 
|---|
| 1077 |  | 
|---|
| 1078 | if (!subset_list->default_is_selected()) { | 
|---|
| 1079 | AW_scalar selected = subset_list->get_awar_value(); | 
|---|
| 1080 |  | 
|---|
| 1081 | StrArray listContent; | 
|---|
| 1082 | subset_list->to_array(listContent, true); | 
|---|
| 1083 |  | 
|---|
| 1084 | int old_pos = listContent.index_of(selected.get_string()); | 
|---|
| 1085 | if (old_pos >= 0) { | 
|---|
| 1086 | int new_pos = 0; | 
|---|
| 1087 | switch (dest) { | 
|---|
| 1088 | case ARM_TOP:    new_pos= 0;         break; | 
|---|
| 1089 | case ARM_UP:     new_pos= old_pos-1; break; | 
|---|
| 1090 | case ARM_DOWN:   new_pos= old_pos+1; break; | 
|---|
| 1091 | case ARM_BOTTOM: new_pos= -1;        break; | 
|---|
| 1092 | } | 
|---|
| 1093 | if (old_pos != new_pos) { | 
|---|
| 1094 | listContent.move(old_pos, new_pos); | 
|---|
| 1095 | subset_list->init_from_array(listContent, subset_list->get_default_display(), subset_list->get_default_value()->get_string()); | 
|---|
| 1096 | } | 
|---|
| 1097 | } | 
|---|
| 1098 | } | 
|---|
| 1099 | callChangedCallback(true); | 
|---|
| 1100 | } | 
|---|
| 1101 |  | 
|---|
| 1102 | void delete_entries_missing_in_parent() { | 
|---|
| 1103 | // check subset for entries missing in parent, | 
|---|
| 1104 | // delete these and update | 
|---|
| 1105 | typedef std::set<const char*, charpLess> Entries; | 
|---|
| 1106 |  | 
|---|
| 1107 | bool    deleted = false; | 
|---|
| 1108 | Entries pEntry; | 
|---|
| 1109 | { | 
|---|
| 1110 | AW_selection_list_iterator pIter(&parent_sellist); | 
|---|
| 1111 | while (pIter) { | 
|---|
| 1112 | pEntry.insert(pIter.get_value()->get_string()); | 
|---|
| 1113 | ++pIter; | 
|---|
| 1114 | } | 
|---|
| 1115 | } | 
|---|
| 1116 |  | 
|---|
| 1117 | AW_selection_list *subsel = get_sellist(); | 
|---|
| 1118 | int                size   = subsel->size(); | 
|---|
| 1119 |  | 
|---|
| 1120 | for (int i = 0; i<size; ++i) { | 
|---|
| 1121 | if (pEntry.find(subsel->get_value_at(i)->get_string()) == pEntry.end()) { // entry missing in parent list | 
|---|
| 1122 | subsel->delete_element_at(i); | 
|---|
| 1123 | deleted = true; | 
|---|
| 1124 | --i; --size; | 
|---|
| 1125 | } | 
|---|
| 1126 | } | 
|---|
| 1127 |  | 
|---|
| 1128 | if (deleted) { | 
|---|
| 1129 | subsel->update(); | 
|---|
| 1130 | callChangedCallback(false); | 
|---|
| 1131 | } | 
|---|
| 1132 | } | 
|---|
| 1133 |  | 
|---|
| 1134 | void fill_entries_matching_values(const CharPtrArray& values) { | 
|---|
| 1135 | AW_selection_list *subset_list = get_sellist(); | 
|---|
| 1136 | subset_list->clear(); | 
|---|
| 1137 |  | 
|---|
| 1138 | for (size_t e = 0; e<values.size(); ++e) { | 
|---|
| 1139 | const char *value = values[e]; | 
|---|
| 1140 |  | 
|---|
| 1141 | AW_selection_list_iterator pIter(&parent_sellist); | 
|---|
| 1142 | while (pIter) { | 
|---|
| 1143 | if (strcmp(pIter.get_value()->get_string(), value) == 0) { | 
|---|
| 1144 | subset_list->insert(pIter.get_displayed(), *pIter.get_value()); | 
|---|
| 1145 | break; | 
|---|
| 1146 | } | 
|---|
| 1147 | ++pIter; | 
|---|
| 1148 | } | 
|---|
| 1149 | } | 
|---|
| 1150 |  | 
|---|
| 1151 | finish_fill_box(&parent_sellist, subset_list); | 
|---|
| 1152 | callChangedCallback(false); | 
|---|
| 1153 | } | 
|---|
| 1154 | }; | 
|---|
| 1155 |  | 
|---|
| 1156 | static void collect_subset_cb(AW_window *, awt_collect_mode what, AW_subset_selection *subsel) { subsel->collect_subset_cb(what); } | 
|---|
| 1157 | static void reorder_subset_cb(AW_window *, awt_reorder_mode dest, AW_subset_selection *subsel) { subsel->reorder_subset_cb(dest); } | 
|---|
| 1158 |  | 
|---|
| 1159 | static void correct_subselection_cb(AW_selection_list *IF_ASSERTION_USED(parent_sel), AW_CL cl_subsel) { | 
|---|
| 1160 | AW_subset_selection *subsel = (AW_subset_selection*)cl_subsel; | 
|---|
| 1161 | aw_assert(subsel->get_parent_sellist() == parent_sel); | 
|---|
| 1162 | subsel->delete_entries_missing_in_parent(); | 
|---|
| 1163 | } | 
|---|
| 1164 |  | 
|---|
| 1165 | AW_selection *awt_create_subset_selection_list(AW_window *aww, AW_selection_list *parent_selection, const char *at_box, const char *at_add, const char *at_sort, bool autocorrect_subselection, SubsetChangedCb subChanged_cb, AW_CL cl_user) { | 
|---|
| 1166 | // at_sort==NULp => skip sort buttons | 
|---|
| 1167 | awt_assert(parent_selection); | 
|---|
| 1168 |  | 
|---|
| 1169 | aww->at(at_box); | 
|---|
| 1170 | int x_list = aww->get_at_xposition(); | 
|---|
| 1171 |  | 
|---|
| 1172 | AW_subset_selection *subsel = new AW_subset_selection(aww, *parent_selection, subChanged_cb, cl_user); | 
|---|
| 1173 |  | 
|---|
| 1174 | int old_button_length = aww->get_button_length(); | 
|---|
| 1175 | aww->button_length(0); | 
|---|
| 1176 |  | 
|---|
| 1177 | aww->at(at_add); | 
|---|
| 1178 | int x_buttons = aww->get_at_xposition(); | 
|---|
| 1179 |  | 
|---|
| 1180 | bool move_rightwards = x_list>x_buttons; | 
|---|
| 1181 | awt_create_collect_buttons(aww, move_rightwards, collect_subset_cb, subsel); | 
|---|
| 1182 |  | 
|---|
| 1183 | if (at_sort) { | 
|---|
| 1184 | aww->at(at_sort); | 
|---|
| 1185 | awt_create_order_buttons(aww, reorder_subset_cb, subsel); | 
|---|
| 1186 | } | 
|---|
| 1187 |  | 
|---|
| 1188 | if (autocorrect_subselection) parent_selection->set_update_callback(correct_subselection_cb, AW_CL(subsel)); | 
|---|
| 1189 |  | 
|---|
| 1190 | aww->button_length(old_button_length); | 
|---|
| 1191 |  | 
|---|
| 1192 | return subsel; | 
|---|
| 1193 | } | 
|---|
| 1194 |  | 
|---|
| 1195 | void awt_set_subset_selection_content(AW_selection *subset_sel_, const CharPtrArray& values) { | 
|---|
| 1196 | /*! sets content of a subset-selection-list | 
|---|
| 1197 | * @param subset_sel_ selection list created by awt_create_subset_selection_list() | 
|---|
| 1198 | * @param values      e.g. retrieved using subset_sel_->get_values() | 
|---|
| 1199 | */ | 
|---|
| 1200 | AW_subset_selection *subset_sel = dynamic_cast<AW_subset_selection*>(subset_sel_); | 
|---|
| 1201 | if (subset_sel) subset_sel->fill_entries_matching_values(values); | 
|---|
| 1202 | } | 
|---|
| 1203 |  | 
|---|
| 1204 | 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) { | 
|---|
| 1205 | /*! create selection_list and input_field on awar 'awar_name' | 
|---|
| 1206 | * @param aww window where to create gui elements | 
|---|
| 1207 | * @param at_box position of selection_list | 
|---|
| 1208 | * @param at_field position of input_field | 
|---|
| 1209 | */ | 
|---|
| 1210 |  | 
|---|
| 1211 | aww->at(at_field); | 
|---|
| 1212 | aww->create_input_field(awar_name); | 
|---|
| 1213 | aww->at(at_box); | 
|---|
| 1214 | return aww->create_selection_list(awar_name, false); | 
|---|
| 1215 | } | 
|---|
| 1216 |  | 
|---|
| 1217 |  | 
|---|