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