source: tags/ms_ra2q56/NTREE/NT_main.cxx

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