source: tags/ms_r17q1/NTREE/NT_main.cxx

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