source: tags/arb-6.0/NTREE/NT_main.cxx

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