source: branches/profile/NTREE/NT_main.cxx

Last change on this file was 12754, checked in by westram, 10 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.8 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : NT_main.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "NT_local.h"
12
13#include <mg_merge.hxx>
14#include <awti_import.hxx>
15#include <insdel.h>
16
17#include <awt.hxx>
18
19#include <aw_advice.hxx>
20#include <aw_question.hxx>
21#include <aw_awars.hxx>
22#include <aw_edit.hxx>
23#include <aw_file.hxx>
24#include <aw_msg.hxx>
25#include <aw_window.hxx>
26#include <aw_root.hxx>
27
28#include <arbdbt.h>
29#include <adGene.h>
30
31#include <arb_version.h>
32#include <arb_progress.h>
33#include <arb_file.h>
34#include <awt_sel_boxes.hxx>
35#include <awt_TreeAwars.hxx>
36#include <macros.hxx>
37#include <signal.h>
38
39using namespace std;
40
41AW_HEADER_MAIN
42
43NT_global GLOBAL; 
44
45// NT_format_all_alignments may be called after any operation which causes
46// unformatted alignments (e.g importing sequences)
47//
48// It tests all alignments whether they need to be formatted
49// and asks the user if they should be formatted.
50
51GB_ERROR NT_format_all_alignments(GBDATA *gb_main) {
52    GB_ERROR err = 0;
53    GB_push_transaction(gb_main);
54    GB_push_my_security(gb_main);
55
56    int ali_count = GBT_count_alignments(gb_main);
57    if (ali_count) {
58        arb_progress progress("Formatting alignments", ali_count);
59        err = GBT_check_data(gb_main, 0);
60
61        AW_repeated_question question;
62        question.add_help("prompt/format_alignments.hlp");
63
64        GBDATA *gb_presets = GBT_get_presets(gb_main);
65        for (GBDATA *gb_ali = GB_entry(gb_presets, "alignment");
66             gb_ali && !err;
67             gb_ali        = GB_nextEntry(gb_ali))
68        {
69            GBDATA *gb_aligned = GB_search(gb_ali, "aligned", GB_INT);
70
71            if (GB_read_int(gb_aligned) == 0) { // sequences in alignment are not formatted
72                enum FormatAction {
73                    FA_ASK_USER   = 0, // ask user
74                    FA_FORMAT_ALL = 1, // format automatically w/o asking
75                    FA_SKIP_ALL   = 2, // skip automatically w/o asking
76                };
77                FormatAction  format_action = FA_ASK_USER;
78                GBDATA       *gb_ali_name   = GB_entry(gb_ali, "alignment_name");
79                const char   *ali_name      = GB_read_char_pntr(gb_ali_name);
80
81                {
82                    bool    is_ali_genom   = strcmp(ali_name, GENOM_ALIGNMENT) == 0;
83                    GBDATA *gb_auto_format = GB_entry(gb_ali, "auto_format");
84
85                    if (gb_auto_format) {
86                        format_action = FormatAction(GB_read_int(gb_auto_format));
87                        if (is_ali_genom) {
88                            if (format_action != FA_SKIP_ALL) {
89                                format_action = FA_SKIP_ALL; // always skip ali_genom
90                                err           = GB_write_int(gb_auto_format, FA_SKIP_ALL);
91                            }
92                        }
93                    }
94                    else if (is_ali_genom) {
95                        format_action = FA_SKIP_ALL;
96                        err           = GBT_write_int(gb_ali, "auto_format", FA_SKIP_ALL);  // always skip
97                    }
98                }
99
100                bool perform_format = false;
101                if (!err) {
102                    switch (format_action) {
103                        case FA_FORMAT_ALL: perform_format = true; break;
104                        case FA_SKIP_ALL:   perform_format = false; break;
105                        default: {
106                            char *qtext  = GBS_global_string_copy("Alignment '%s' is not formatted. Format?", ali_name);
107                            int   answer = question.get_answer("format_alignments", qtext, "Format,Skip,Always format,Always skip", "all", false);
108
109                            switch (answer) {
110                                case 2:
111                                    err = GBT_write_int(gb_ali, "auto_format", FA_FORMAT_ALL);
112                                    // fall-through
113                                case 0:
114                                    perform_format = true;
115                                    break;
116
117                                case 3:
118                                    err = GBT_write_int(gb_ali, "auto_format", FA_SKIP_ALL);
119                                    break;
120                            }
121
122                            free(qtext);
123                            break;
124                        }
125                    }
126                }
127                if (!err && perform_format) {
128                    GB_push_my_security(gb_main);
129                    err = ARB_format_alignment(gb_main, ali_name);
130                    GB_pop_my_security(gb_main);
131                }
132            }
133            progress.inc_and_check_user_abort(err);
134        }
135    }
136
137    GB_pop_my_security(gb_main);
138
139    return GB_end_transaction(gb_main, err);
140}
141
142
143// --------------------------------------------------------------------------------
144
145
146static GB_ERROR nt_check_database_consistency() {
147    // called once on ARB_NTREE startup
148    arb_progress cons_progress("Checking consistency");
149
150    GB_ERROR err  = NT_format_all_alignments(GLOBAL.gb_main);
151    if (!err) err = NT_repair_DB(GLOBAL.gb_main);
152
153    return err;
154}
155
156__ATTR__USERESULT static GB_ERROR startup_mainwindow_and_dbserver(AW_root *aw_root, const char *autorun_macro) {
157    AWT_initTreeAwarRegistry(GLOBAL.gb_main);
158
159    GB_ERROR error = configure_macro_recording(aw_root, "ARB_NT", GLOBAL.gb_main); // @@@ problematic if called from startup-importer
160    if (!error) {
161        NT_create_main_window(aw_root);
162        if (GB_is_server(GLOBAL.gb_main)) {
163            error = nt_check_database_consistency();
164            if (!error) NT_repair_userland_problems();
165        }
166    }
167
168    if (!error && autorun_macro) execute_macro(aw_root, autorun_macro); // @@@ triggering execution here is ok, but its a bad place to pass 'autorun_macro'. Should be handled more generally
169
170    return error;
171}
172
173static ARB_ERROR load_and_startup_main_window(AW_root *aw_root, const char *autorun_macro) {
174    char *db_server = aw_root->awar(AWAR_DB_PATH)->read_string();
175    GLOBAL.gb_main  = GBT_open(db_server, "rw");
176
177    ARB_ERROR error;
178    if (!GLOBAL.gb_main) {
179        error = GB_await_error();
180    }
181    else {
182        aw_root->awar(AWAR_DB_PATH)->write_string(db_server);
183
184#define MAXNAMELEN 35
185        int len = strlen(db_server);
186        if (len>MAXNAMELEN) {
187            char *nameOnly = strrchr(db_server, '/');
188            if (nameOnly) {
189                nameOnly++;
190                len -= (nameOnly-db_server);
191                memmove(db_server, nameOnly, len+1);
192                if (len>MAXNAMELEN) {
193                    strcpy(db_server+MAXNAMELEN-3, "...");
194                }
195            }
196        }
197#if defined(DEBUG)
198        AWT_announce_db_to_browser(GLOBAL.gb_main, GBS_global_string("ARB database (%s)", db_server));
199#endif // DEBUG
200
201        GB_ERROR problem = startup_mainwindow_and_dbserver(aw_root, autorun_macro);
202        aw_message_if(problem); // no need to terminate ARB
203    }
204
205    free(db_server);
206    return error;
207}
208
209static void nt_delete_database(AW_window *aww) {
210    AW_root *aw_root   = aww->get_root();
211    char    *db_server = aw_root->awar(AWAR_DB_PATH)->read_string();
212
213    if (strlen(db_server)) {
214        if (aw_ask_sure(NULL, GBS_global_string("Are you sure to delete database %s\nNote: there is no way to undelete it afterwards", db_server))) {
215            GB_ERROR error = GB_delete_database(db_server);
216            if (error) {
217                aw_message(error);
218            }
219            else {
220                aw_root->awar(AWAR_DB_FILTER)->touch();
221            }
222        }
223    }
224    else {
225        aw_message("No database selected");
226    }
227    free(db_server);
228}
229
230static void start_main_window_after_import(AW_root *aw_root) {
231    GLOBAL.aw_root = aw_root;
232
233    GBDATA *gb_imported = AWTI_take_imported_DB_and_cleanup_importer();
234    nt_assert(gb_imported == GLOBAL.gb_main); // import-DB should already be used as main-DB
235    GLOBAL.gb_main      = gb_imported;
236
237    aw_message_if(startup_mainwindow_and_dbserver(aw_root, NULL));
238}
239
240static void nt_intro_start_existing(AW_window *aw_intro) {
241    aw_intro->hide();
242    ARB_ERROR error = load_and_startup_main_window(aw_intro->get_root(), NULL);
243    nt_assert(contradicted(error, got_macro_ability(aw_intro->get_root())));
244    if (error) {
245        aw_intro->show();
246        aw_popup_ok(error.deliver());
247    }
248    else {
249        error.expect_no_error();
250    }
251}
252
253static void nt_intro_start_merge(AW_window *aw_intro) {
254    AW_root    *aw_root    = aw_intro->get_root();
255    const char *dir        = aw_root->awar("tmp/nt/arbdb/directory")->read_char_pntr();
256    char       *merge_args = GBS_global_string_copy("'%s' '%s'", dir, dir);
257
258    NT_restart(aw_root, merge_args); //  call arb_ntree as merge-tool on exit
259}
260
261static void nt_intro_start_import(AW_window *aw_intro) {
262    aw_intro->hide();
263
264    AW_root *aw_root = aw_intro->get_root();
265    aw_root->awar_string(AWAR_DB_PATH)->write_string("noname.arb");
266    aw_root->awar_int(AWAR_READ_GENOM_DB, IMP_PLAIN_SEQUENCE); // Default toggle  in window  "Create&import" is Non-Genom
267
268    nt_assert(!GLOBAL.gb_main);
269    AWTI_open_import_window(aw_root, "", true, 0, makeRootCallback(start_main_window_after_import));
270    GLOBAL.gb_main = AWTI_peek_imported_DB();
271
272    nt_assert(got_macro_ability(aw_root));
273}
274
275static AW_window *nt_create_intro_window(AW_root *awr) {
276    AW_window_simple *aws = new AW_window_simple;
277    aws->init(awr, "ARB_INTRO", "ARB INTRO");
278    aws->load_xfig("arb_intro.fig");
279
280    aws->callback(NT_exit, EXIT_SUCCESS);
281    aws->at("close");
282    aws->create_button("EXIT", "Exit", "x");
283
284    aws->at("help");
285    aws->callback(makeHelpCallback("arb_intro.hlp"));
286    aws->create_button("HELP", "HELP", "H");
287
288    AW_create_standard_fileselection(aws, "tmp/nt/arbdb");
289
290    aws->button_length(0);
291
292    aws->at("logo");
293    aws->create_button(0, "#logo.xpm");
294
295    aws->at("version");
296    aws->create_button(0, GBS_global_string("Version " ARB_VERSION), 0); // version
297
298    aws->at("copyright");
299    aws->create_button(0, GBS_global_string("(C) 1993-" ARB_BUILD_YEAR), 0);
300
301    aws->at("old");
302    aws->callback(nt_intro_start_existing);
303    aws->create_autosize_button("OPEN_SELECTED", "OPEN SELECTED", "O");
304
305    aws->at("del");
306    aws->callback(nt_delete_database);
307    aws->create_autosize_button("DELETE_SELECTED", "DELETE SELECTED");
308
309    aws->at("new_complex");
310    aws->callback(nt_intro_start_import);
311    aws->create_autosize_button("CREATE_AND_IMPORT", "CREATE AND IMPORT", "I");
312
313    aws->at("merge");
314    aws->callback(nt_intro_start_merge);
315    aws->create_autosize_button("MERGE_TWO_DATABASES", "MERGE TWO ARB DATABASES", "O");
316
317    aws->at("expert");
318    aws->create_toggle(AWAR_EXPERT);
319
320    return aws;
321}
322
323static void AWAR_DB_PATH_changed_cb(AW_root *awr) {
324    static bool avoid_recursion = false;
325
326    if (!avoid_recursion) {
327        LocallyModify<bool> flag(avoid_recursion, true);
328
329        char *value  = awr->awar(AWAR_DB_PATH)->read_string();
330        char *lslash = strrchr(value, '/');
331
332        char *name = lslash ? lslash+1 : value;
333#if defined(DEBUG)
334        printf("writing '%s' to AWAR_DB_NAME\n", name);
335#endif // DEBUG
336        awr->awar(AWAR_DB_NAME)->write_string(name);
337
338        if (lslash) {               // update value of directory
339            lslash[0] = 0;
340            awr->awar(AWAR_DB"directory")->write_string(value);
341            lslash[0] = '/';
342        }
343
344        free(value);
345    }
346}
347
348class NtreeCommandLine : virtual Noncopyable {
349    int               arg_count;
350    char const*const* args;
351
352    bool help_requested;
353    bool do_import;
354   
355    const char *macro_name;
356
357public:
358    NtreeCommandLine(int argc_, char const*const* argv_)
359        : arg_count(argc_-1),
360          args(argv_+1),
361          help_requested(false),
362          do_import(false),
363          macro_name(NULL)
364    {}
365
366    void shift() { ++args; --arg_count; }
367
368    int free_args() const { return arg_count; }
369    const char *get_arg(int num) const { return num<arg_count ? args[num] : NULL; }
370
371    bool wants_help() const { return help_requested; }
372    bool wants_import() const { return do_import; }
373    bool wants_merge() const { return arg_count == 2; }
374
375    const char *autorun_macro() const { return macro_name; }
376
377    void print_help(FILE *out) const {
378        fprintf(out,
379                "\n"
380                "arb_ntree version " ARB_VERSION_DETAILED "\n"
381                "(C) 1993-" ARB_BUILD_YEAR " Lehrstuhl fuer Mikrobiologie - TU Muenchen\n"
382                "http://www.arb-home.de/\n"
383                "(version build by: " ARB_BUILD_USER "@" ARB_BUILD_HOST ")\n"
384                "\n"
385                "Possible usage:\n"
386                "  arb_ntree               => start ARB (intro)\n"
387                "  arb_ntree DB            => start ARB with DB\n"
388                "  arb_ntree DB1 DB2       => merge from DB1 into DB2\n"
389                "  arb_ntree --import FILE => import FILE into new database (FILE may be a filemask)\n"
390                "\n"
391                "Additional arguments possible with command lines above:\n"
392                "  --execute macroname     => execute macro 'macroname' after startup\n"
393                "\n"
394                "Each DB argument may be one of the following:\n"
395                "  database.arb            -> use existing or new database\n"
396                \":\"                     -> connect to database of a running instance of ARB\n"
397                "  directory               -> prompt for databases inside directory\n"
398                "  filemask                -> also prompt for DB, but more specific (e.g. \"prot*.arb\")\n"
399                "\n"
400            );
401    }
402
403    ARB_ERROR parse() {
404        ARB_ERROR error;
405
406        while (!error && arg_count>0 && args[0][0] == '-') {
407            const char *opt = args[0]+1;
408            if (opt[0] == '-') ++opt; // accept '-' or '--'
409            if (strcmp(opt, "help") == 0 || strcmp(opt, "h") == 0) { help_requested = true; }
410            else if (strcmp(opt, "execute") == 0) { shift(); macro_name = *args; }
411            else if (strcmp(opt, "import") == 0) { do_import = true; }
412
413            // bunch of test switches to provoke various ways to terminate (see also http://bugs.arb-home.de/ticket/538)
414            else if (strcmp(opt, "crash") == 0) { GBK_terminate("crash requested"); }
415            else if (strcmp(opt, "trap") == 0) { arb_assert(0); }
416            else if (strcmp(opt, "sighup") == 0) { raise(SIGHUP); }
417            else if (strcmp(opt, "sigsegv") == 0) { raise(SIGSEGV); }
418            else if (strcmp(opt, "sigterm") == 0) { raise(SIGTERM); }
419            else if (strcmp(opt, "fail") == 0) { exit(EXIT_FAILURE); }
420            else if (strcmp(opt, "exit") == 0) { exit(EXIT_SUCCESS); }
421            // end of test switches ----------------------------------------
422
423            else error = GBS_global_string("Unknown switch '%s'", args[0]);
424            shift();
425        }
426        // non-switch arguments remain in arg_count/args
427        if (!error) {
428            if (do_import && arg_count != 1) error = "expected exactly one file-name or file-mask behind --import";
429            else if (arg_count>2)            error = "too many stray arguments given (max. 2 accepted)";
430        }
431        return error;
432    }
433};
434
435enum ArgType { // args that might be specified on command line (for DB or FILE; see above)
436    RUNNING_DB,
437    DIRECTORY,
438    EXISTING_DB,
439    NEW_DB,
440    FILEMASK,
441    EXISTING_FILE,
442    UNKNOWN_ARG,
443};
444
445inline bool has_arb_suffix(const char *arg) {
446    const char *suffix = strstr(arg, ".arb");
447    if (suffix) {
448        return strcmp(suffix, ".arb") == 0;
449    }
450    return false;
451}
452
453static ArgType detectArgType(const char *arg) {
454    if (strcmp(arg, ":") == 0) return RUNNING_DB;
455    if (GB_is_directory(arg)) return DIRECTORY;
456    if (strpbrk(arg, "*?") != 0) return FILEMASK;
457    if (has_arb_suffix(arg)) {
458        return GB_is_regularfile(arg) ? EXISTING_DB : NEW_DB;
459    }
460
461    GB_ERROR load_file_err = GBT_check_arb_file(arg);
462    if (!load_file_err) return EXISTING_DB;
463
464    return GB_is_regularfile(arg) ? EXISTING_FILE : UNKNOWN_ARG;
465}
466
467enum RunMode { NORMAL, IMPORT, MERGE, BROWSE };
468
469#define ABORTED_BY_USER "aborted by user"
470
471static ARB_ERROR check_argument_for_mode(const char *database, char *&browser_startdir, RunMode& mode) {
472    // Check whether 'database' is a
473    // - ARB database
474    // - directory name
475    // - file to import
476    //
477    // Modify 'mode' occordingly.
478    // Set 'browser_startdir'
479
480    ARB_ERROR error;
481    if (mode != IMPORT) {
482        if (!database) mode = BROWSE;
483        else {
484            GB_ERROR load_file_err = GBT_check_arb_file(database);
485            if (load_file_err) {
486                char *full_path = AW_unfold_path("PWD", database);
487
488                enum Action { LOAD_DB, CONVERT_DB, BROWSE_DB, EXIT, NOIDEA };
489                Action action = NOIDEA;
490                if (GB_is_directory(full_path)) {
491                    action = BROWSE_DB;
492                }
493                else {
494                    if (!GB_is_regularfile(full_path)) {
495                        const char *msg = GBS_global_string("'%s' is neither a known option nor a legal file- or directory-name.\n(Error: %s)",
496                                                            full_path, load_file_err);
497
498                        int ans = aw_question(NULL, msg, "Browser,Exit");
499                        action  = ans ? EXIT : BROWSE_DB;
500                    }
501                    else {
502                        const char *msg = GBS_global_string("Your file is not an original arb file\n(%s)", load_file_err);
503                        action          = (Action)aw_question(NULL, msg, "Continue (dangerous),Start Converter,Browser,Exit");
504                    }
505                }
506
507                switch (action) {
508                    case CONVERT_DB:    mode = IMPORT; break;
509                    case LOAD_DB:       break;
510                    case NOIDEA:        nt_assert(0);
511                    case EXIT:          error = ABORTED_BY_USER; break;
512                    case BROWSE_DB: {
513                        char *dir = nulldup(full_path);
514                        while (dir && !GB_is_directory(dir)) freeset(dir, AW_extract_directory(dir));
515
516                        if (dir) {
517                            nt_assert(GB_is_directory(dir));
518                            reassign(browser_startdir, dir);
519                            mode = BROWSE;
520                        }
521                        free(dir);
522                        break;
523                    }
524               
525                }
526                free(full_path);
527            }
528        }
529    }
530   
531    return error;
532}
533
534class SelectedDatabase : virtual Noncopyable {
535    GBDATA*&  gb_main;
536    char     *name;
537    ArgType   type;
538    char     *role;
539
540    void fix_name() {
541        if (type != RUNNING_DB && name[0]) {
542            const char *changed;
543
544            if (type == NEW_DB) {
545                changed = GB_unfold_in_directory(GB_getcwd(), name);
546            }
547            else {
548                changed = GB_canonical_path(name);
549            }
550
551            if (strcmp(name, changed) != 0) {
552                reselect_file(changed);
553            }
554        }
555    }
556
557public:
558    SelectedDatabase(GBDATA*& gb_main_, const char *name_, const char *role_)
559        : gb_main(gb_main_),
560          name(strdup(name_)),
561          type(detectArgType(name)),
562          role(strdup(role_))
563    {
564        fix_name();
565    }
566    ~SelectedDatabase() {
567        free(role);
568        free(name);
569    }
570
571    ArgType arg_type() const { return type; }
572
573    bool needs_to_prompt() const {
574        return type == DIRECTORY || type == FILEMASK || type == UNKNOWN_ARG;
575    }
576
577    const char *get_fullname() const { return name; }
578    const char *get_nameonly() const {
579        const char *slash = strrchr(name, '/');
580        return slash ? slash+1 : name;
581    }
582    const char *get_role() const { return role; }
583    const char *get_description() const { return GBS_global_string("%s (%s)", get_role(), get_nameonly()); }
584
585    ArgType get_type() const { return type; }
586
587    const char *get_dir() const {
588        const char *dir = NULL;
589        switch (type) {
590            case DIRECTORY:
591                dir = get_fullname();
592                break;
593
594            case NEW_DB:
595            case FILEMASK:
596            case EXISTING_FILE:
597            case EXISTING_DB: {
598                char *dir_copy;
599                GB_split_full_path(name, &dir_copy, NULL, NULL, NULL);
600
601                static SmartCharPtr dir_store;
602                dir_store = dir_copy;
603
604                dir = dir_copy;
605                break;
606            }
607            case UNKNOWN_ARG:
608            case RUNNING_DB:
609                dir = ".";
610                break;
611        }
612        return dir;
613    }
614
615    const char *get_mask() const {
616        const char *mask;
617        switch (type) {
618            case FILEMASK: {
619                char *mask_copy;
620                GB_split_full_path(name, NULL, &mask_copy, NULL, NULL);
621
622                static SmartCharPtr mask_store;
623                mask_store = mask_copy;
624
625                mask = mask_copy;
626                break;
627            }
628            default:
629                mask = ".arb";
630                break;
631        }
632        return mask;
633    }
634
635    void reselect_file(const char *file) {
636        freedup(name, file);
637        type = detectArgType(name);
638        fix_name();
639    }
640    void reselect_from_awar(AW_root *aw_root, const char *awar_name) {
641        if (awar_name) {
642            const char *file = aw_root->awar(awar_name)->read_char_pntr();
643            if (file) reselect_file(file);
644        }
645    }
646
647    GB_ERROR open_db_for_merge(bool is_source_db);
648    void close_db() { if (gb_main) GB_close(gb_main); }
649};
650
651GB_ERROR SelectedDatabase::open_db_for_merge(bool is_source_db) {
652    GB_ERROR    error    = NULL;
653    const char *openMode = "rw";
654
655    switch (get_type()) {
656        case DIRECTORY:
657        case FILEMASK:
658            GBK_terminate("Program logic error (should have been prompted for explicit DB name)");
659            break;
660
661        case UNKNOWN_ARG:
662        case EXISTING_FILE:
663            error = GBS_global_string("'%s' is no arb-database", get_fullname());
664            break;
665
666        case NEW_DB:
667            if (is_source_db) {
668                error = GBS_global_string("'%s' has to exist", get_fullname());
669                break;
670            }
671            openMode = "crw"; // allow to create DB
672            break;
673
674        case EXISTING_DB:
675        case RUNNING_DB:
676            break;
677    }
678
679    if (!error) {
680        gb_main             = GBT_open(get_fullname(), openMode);
681        if (!gb_main) error = GB_await_error();
682        else {
683            IF_DEBUG(AWT_announce_db_to_browser(gb_main, get_description()));
684        }
685    }
686
687    return error;
688}
689
690struct merge_scheme : virtual Noncopyable {
691    SelectedDatabase *src;
692    SelectedDatabase *dst;
693
694    char *awar_src;
695    char *awar_dst;
696
697    GB_ERROR error;
698
699    merge_scheme(SelectedDatabase *src_, SelectedDatabase *dst_)
700        : src(src_),
701          dst(dst_),
702          awar_src(NULL),
703          awar_dst(NULL),
704          error(NULL)
705    {}
706    ~merge_scheme() {
707        if (error) {
708            src->close_db();
709            dst->close_db();
710        }
711        delete src;
712        delete dst;
713        free(awar_src);
714        free(awar_dst);
715    }
716
717
718    void open_dbs() {
719        if (!error) error = src->open_db_for_merge(true);
720        if (!error) error = dst->open_db_for_merge(false);
721    }
722
723    void fix_dst(AW_root *aw_root) { dst->reselect_from_awar(aw_root, awar_dst); }
724    void fix_src(AW_root *aw_root) { src->reselect_from_awar(aw_root, awar_src); }
725
726    bool knows_dbs() const { return !src->needs_to_prompt() && !dst->needs_to_prompt(); }
727};
728
729static bool merge_tool_running_as_client = true; // default to safe state (true avoids call of 'arb_clean' at exit)
730static void exit_from_merge(const char *restart_args) {
731    if (merge_tool_running_as_client) { // there is a main ARB running
732        if (restart_args) NT_start(restart_args, true);
733        exit(EXIT_SUCCESS); // exit w/o killing clients (as NT_exit() does)
734    }
735    else {
736        NT_restart(AW_root::SINGLETON, restart_args ? restart_args : "");
737        nt_assert(0);
738    }
739}
740
741static void merge_startup_abort_cb(AW_window*) {
742    fputs("Error: merge aborted by user\n", stderr);
743    exit_from_merge(NULL);
744}
745
746static AW_window *merge_startup_error_window(AW_root *aw_root, GB_ERROR error) {
747    AW_window_simple *aw_msg = new AW_window_simple;
748
749    aw_msg->init(aw_root, "arb_merge_error", "ARB merge error");
750    aw_msg->recalc_size_atShow(AW_RESIZE_DEFAULT); // force size recalc (ignores user size)
751
752    aw_msg->at(10, 10);
753    aw_msg->auto_space(10, 10);
754
755    aw_msg->create_autosize_button(NULL, error, "", 2);
756    aw_msg->at_newline();
757
758    aw_msg->callback(merge_startup_abort_cb);
759    aw_msg->create_autosize_button("OK", "Ok", "O", 2);
760
761    aw_msg->window_fit();
762
763    return aw_msg;
764}
765static AW_window *startup_merge_main_window(AW_root *aw_root, merge_scheme *ms) {
766    ms->fix_src(aw_root);
767    ms->fix_dst(aw_root);
768
769    if (ms->knows_dbs()) {
770        ms->open_dbs();
771    }
772    else {
773        ms->error = "Need two database to merge";
774    }
775
776    AW_window *aw_result;
777    if (!ms->error) {
778        MERGE_create_db_file_awars(aw_root, AW_ROOT_DEFAULT, ms->src->get_fullname(), ms->dst->get_fullname());
779        bool dst_is_new = ms->dst->arg_type() == NEW_DB;
780        aw_result       = MERGE_create_main_window(aw_root, dst_is_new, exit_from_merge);
781
782        nt_assert(got_macro_ability(aw_root));
783    }
784    else {
785        aw_result = merge_startup_error_window(aw_root, ms->error);
786    }
787    delete ms; // was allocated in startup_gui()
788    return aw_result;
789}
790
791static AW_window *startup_merge_prompting_for_nonexplicit_dst_db(AW_root *aw_root, merge_scheme *ms) {
792    AW_window *aw_result;
793    if (ms->dst->needs_to_prompt()) {
794        aw_result = awt_create_load_box(aw_root, "Select", ms->dst->get_role(),
795                                        ms->dst->get_dir(), ms->dst->get_mask(),
796                                        &(ms->awar_dst),
797                                        AW_window::makeWindowReplacer(makeCreateWindowCallback(startup_merge_main_window, ms)),
798                                        makeWindowCallback(merge_startup_abort_cb), "Cancel");
799    }
800    else {
801        aw_result = startup_merge_main_window(aw_root, ms);
802    }
803    return aw_result;
804}
805
806static AW_window *startup_merge_prompting_for_nonexplicit_dbs(AW_root *aw_root, merge_scheme *ms) {
807    // if src_spec or dst_spec needs to be prompted for -> startup prompters with callbacks bound to continue
808    // otherwise just startup merge
809
810    AW_window *aw_result;
811    if (ms->src->needs_to_prompt()) {
812        aw_result = awt_create_load_box(aw_root, "Select", ms->src->get_role(),
813                                        ms->src->get_dir(), ms->src->get_mask(),
814                                        &(ms->awar_src),
815                                        AW_window::makeWindowReplacer(makeCreateWindowCallback(startup_merge_prompting_for_nonexplicit_dst_db, ms)),
816                                        makeWindowCallback(merge_startup_abort_cb), "Cancel");
817    }
818    else {
819        aw_result = startup_merge_prompting_for_nonexplicit_dst_db(aw_root, ms);
820    }
821    return aw_result;
822}
823
824static void startup_gui(NtreeCommandLine& cl, ARB_ERROR& error) {
825    {
826        char *message = strdup(GB_path_in_ARBLIB("message"));
827        char *stamp   = strdup(GB_path_in_arbprop("msgtime"));
828        if (GB_time_of_file(message)>GB_time_of_file(stamp)) {
829            AW_edit(message);
830            aw_message_if(GBK_system(GBS_global_string("touch %s", stamp)));
831        }
832        free(stamp);
833        free(message);
834    }
835
836    // create some early awars
837    // Note: normally you don't like to add your awar-init-function here, but into nt_create_all_awars()
838
839    AW_root* aw_root = GLOBAL.aw_root;
840
841    AW_create_fileselection_awars(aw_root, AWAR_DB, "", ".arb", "noname.arb");
842    aw_root->awar_string(AWAR_DB_TYPE, "b");
843    aw_root->awar_string(AWAR_DB_NAME, "noname.arb");
844    aw_root->awar(AWAR_DB_PATH)->add_callback(AWAR_DB_PATH_changed_cb);
845
846    aw_root->awar_int(AWAR_EXPERT, 0);
847
848    AWT_install_cb_guards();
849
850    ARB_declare_global_awars(aw_root, AW_ROOT_DEFAULT);
851
852    aw_root->setUserActionTracker(new NullTracker); // no macro recording inside prompters that may popup
853    if (!error) {
854        nt_assert(!cl.wants_help());
855               
856        RunMode mode = NORMAL;
857
858        if      (cl.wants_merge())  mode = MERGE;
859        else if (cl.wants_import()) mode = IMPORT;
860
861        if (mode == MERGE) {
862            MERGE_create_all_awars(aw_root, AW_ROOT_DEFAULT);
863
864            merge_scheme *ms = new merge_scheme( // freed in startup_merge_main_window()
865                new SelectedDatabase(GLOBAL_gb_src, cl.get_arg(0), "Source DB for merge"),
866                new SelectedDatabase(GLOBAL_gb_dst, cl.get_arg(1), "Destination DB for merge"));
867
868            merge_tool_running_as_client = ms->src->arg_type() == RUNNING_DB || ms->dst->arg_type() == RUNNING_DB;
869
870            if (ms->src->arg_type() == RUNNING_DB && ms->dst->arg_type() == RUNNING_DB) {
871                error = "You cannot merge from running to running DB";
872            }
873            else {
874                aw_root->setUserActionTracker(new NullTracker); // no macro recording during startup of merge tool (file prompts)
875                AW_window *aww = startup_merge_prompting_for_nonexplicit_dbs(aw_root, ms);
876
877                nt_assert(contradicted(aww, error));
878
879                if (!error) {
880                    aww->show();
881                    aw_root->main_loop();
882                    nt_assert(0);
883                }
884            }
885        }
886        else {
887            const char *database         = NULL;
888            char       *browser_startdir = strdup(".");
889
890            if (cl.free_args() > 0) database = cl.get_arg(0);
891
892            error = check_argument_for_mode(database, browser_startdir, mode);
893            if (!error) {
894                if (mode == IMPORT) {
895                    aw_root->awar_int(AWAR_READ_GENOM_DB, IMP_PLAIN_SEQUENCE);
896
897                    AWTI_open_import_window(aw_root, database, true, 0, makeRootCallback(start_main_window_after_import));
898                    nt_assert(!GLOBAL.gb_main);
899                    GLOBAL.gb_main = AWTI_peek_imported_DB();
900
901                    nt_assert(got_macro_ability(aw_root));
902                    aw_root->main_loop();
903                }
904                else if (mode == NORMAL) {
905                    aw_root->awar(AWAR_DB_PATH)->write_string(database);
906                    error = load_and_startup_main_window(aw_root, cl.autorun_macro());
907                    if (!error) {
908                        nt_assert(got_macro_ability(aw_root));
909                        aw_root->main_loop();
910                    }
911                }
912                else if (mode == BROWSE) {
913                    aw_root->awar(AWAR_DB"directory")->write_string(browser_startdir);
914                    char *latest = GB_find_latest_file(browser_startdir, "/\\.(arb|a[0-9]{2})$/");
915                    if (latest) {
916                        int l = strlen(latest);
917                        strcpy(latest+l-3, "arb");
918                        aw_root->awar(AWAR_DB_PATH)->write_string(latest);
919                        free(latest);
920                    }
921                    nt_create_intro_window(aw_root)->show();
922                    aw_root->setUserActionTracker(new NullTracker); // no macro recording inside intro window
923                    aw_root->main_loop();
924                }
925            }
926            free(browser_startdir);
927        }
928    }
929
930    if (error) {
931        const char *msg = error.preserve();
932        if (strcmp(msg, ABORTED_BY_USER) != 0) aw_popup_ok(msg);
933    }
934}
935
936int ARB_main(int argc, char *argv[]) {
937    {
938        // i really dont want 'arb_ntree --help' to startup the GUI
939        // hack: parse CL twice
940        NtreeCommandLine cl(argc, argv);
941        bool             cl_ok = !cl.parse().deliver();
942        if (cl_ok) {
943            if (cl.wants_help()) {
944                cl.print_help(stderr);
945                return EXIT_SUCCESS;
946            }
947        }
948    }
949
950    aw_initstatus();
951    GB_set_verbose();
952
953    GB_shell shell;
954    AW_root *aw_root = AWT_create_root("ntree.arb", "ARB_NT", need_macro_ability(), &argc, &argv);
955
956    GLOBAL.aw_root = aw_root;
957
958    NtreeCommandLine cl(argc, argv);
959    ARB_ERROR        error = cl.parse();
960
961    if (!error) {
962        if (cl.wants_help()) {
963            cl.print_help(stderr);
964        }
965        else {
966            startup_gui(cl, error);
967        }
968    }
969
970    int exitcode = EXIT_SUCCESS;
971    if (error) {
972        exitcode = EXIT_FAILURE;
973        fprintf(stderr, "arb_ntree: Error: %s\n", error.deliver());
974    }
975    else {
976        error.expect_no_error();
977    }
978
979    delete aw_root;
980
981    return exitcode;
982}
983
Note: See TracBrowser for help on using the repository browser.