source: tags/svn.1.5.4/NTREE/NT_main.cxx

Last change on this file was 8423, checked in by westram, 12 years ago
  • use LocallyModify for several flags
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.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_internal.h"
12#include "ntree.hxx"
13#include "nt_cb.hxx"
14
15#include <mg_merge.hxx>
16#include <awti_import.hxx>
17
18#include <awt.hxx>
19#include <awt_macro.hxx>
20
21#include <aw_advice.hxx>
22#include <aw_question.hxx>
23#include <aw_awars.hxx>
24#include <aw_edit.hxx>
25#include <aw_file.hxx>
26#include <aw_msg.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
36using namespace std;
37
38AW_HEADER_MAIN
39
40#define nt_assert(bed) arb_assert(bed)
41
42#define NT_SERVE_DB_TIMER 50
43#define NT_CHECK_DB_TIMER 200
44
45GBDATA *GLOBAL_gb_main;                             // global gb_main for arb_ntree
46NT_global GLOBAL_NT = { 0, 0, 0, false };
47
48// NT_format_all_alignments may be called after any operation which causes
49// unformatted alignments (e.g importing sequences)
50//
51// It tests all alignments whether they need to be formatted
52// and asks the user if they should be formatted.
53
54GB_ERROR NT_format_all_alignments(GBDATA *gb_main) {
55    GB_ERROR err = 0;
56    GB_push_transaction(gb_main);
57    GB_push_my_security(gb_main);
58
59    int ali_count = GBT_count_alignments(gb_main);
60    if (ali_count) {
61        arb_progress progress("Formatting alignments", ali_count);
62        err = GBT_check_data(gb_main, 0);
63
64        AW_repeated_question  question;
65        GBDATA               *gb_presets = GB_entry(gb_main, "presets");
66
67        question.add_help("prompt/format_alignments.hlp");
68
69        for (GBDATA *gb_ali = GB_entry(gb_presets, "alignment");
70             gb_ali && !err;
71             gb_ali = GB_nextEntry(gb_ali))
72        {
73            GBDATA *gb_aligned = GB_search(gb_ali, "aligned", GB_INT);
74
75            if (GB_read_int(gb_aligned) == 0) { // sequences in alignment are not formatted
76                enum FormatAction {
77                    FA_ASK_USER   = 0, // ask user
78                    FA_FORMAT_ALL = 1, // format automatically w/o asking
79                    FA_SKIP_ALL   = 2, // skip automatically w/o asking
80                };
81                FormatAction  format_action = FA_ASK_USER;
82                GBDATA       *gb_ali_name   = GB_entry(gb_ali, "alignment_name");
83                const char   *ali_name      = GB_read_char_pntr(gb_ali_name);
84
85                {
86                    bool    is_ali_genom   = strcmp(ali_name, GENOM_ALIGNMENT) == 0;
87                    GBDATA *gb_auto_format = GB_entry(gb_ali, "auto_format");
88
89                    if (gb_auto_format) {
90                        format_action = FormatAction(GB_read_int(gb_auto_format));
91                        if (is_ali_genom) {
92                            if (format_action != FA_SKIP_ALL) {
93                                format_action = FA_SKIP_ALL; // always skip ali_genom
94                                err           = GB_write_int(gb_auto_format, FA_SKIP_ALL);
95                            }
96                        }
97                    }
98                    else if (is_ali_genom) {
99                        format_action = FA_SKIP_ALL;
100                        err           = GBT_write_int(gb_ali, "auto_format", FA_SKIP_ALL);  // always skip
101                    }
102                }
103
104                bool perform_format = false;
105                if (!err) {
106                    switch (format_action) {
107                        case FA_FORMAT_ALL: perform_format = true; break;
108                        case FA_SKIP_ALL:   perform_format = false; break;
109                        default: {
110                            char *qtext  = GBS_global_string_copy("Alignment '%s' is not formatted. Format?", ali_name);
111                            int   answer = question.get_answer("format_alignments", qtext, "Format,Skip,Always format,Always skip", "all", false);
112
113                            switch (answer) {
114                                case 2:
115                                    err = GBT_write_int(gb_ali, "auto_format", FA_FORMAT_ALL);
116                                    // fall-through
117                                case 0:
118                                    perform_format = true;
119                                    break;
120
121                                case 3:
122                                    err = GBT_write_int(gb_ali, "auto_format", FA_SKIP_ALL);
123                                    break;
124                            }
125
126                            free(qtext);
127                            break;
128                        }
129                    }
130                }
131                if (!err && perform_format) {
132                    GB_push_my_security(gb_main);
133                    err = GBT_format_alignment(gb_main, ali_name);
134                    GB_pop_my_security(gb_main);
135                }
136            }
137            progress.inc_and_check_user_abort(err);
138        }
139    }
140
141    GB_pop_my_security(gb_main);
142
143    return GB_end_transaction(gb_main, err);
144}
145
146
147// --------------------------------------------------------------------------------
148
149static GB_ERROR nt_check_database_consistency() {
150    // called once on ARB_NTREE startup
151    arb_progress("Checking consistency");
152
153    GB_ERROR err  = NT_format_all_alignments(GLOBAL_gb_main);
154    if (!err) err = NT_repair_DB(GLOBAL_gb_main);
155
156    return err;
157}
158
159
160static void serve_db_interrupt(AW_root *awr) {
161    bool success = GBCMS_accept_calls(GLOBAL_gb_main, false);
162    while (success) {
163        awr->check_for_remote_command((AW_default)GLOBAL_gb_main, AWAR_NT_REMOTE_BASE);
164        success = GBCMS_accept_calls(GLOBAL_gb_main, true);
165    }
166
167    awr->add_timed_callback(NT_SERVE_DB_TIMER, (AW_RCB)serve_db_interrupt, 0, 0);
168}
169
170static void check_db_interrupt(AW_root *awr) {
171    awr->check_for_remote_command((AW_default)GLOBAL_gb_main, AWAR_NT_REMOTE_BASE);
172    awr->add_timed_callback(NT_CHECK_DB_TIMER, (AW_RCB)check_db_interrupt, 0, 0);
173}
174
175static GB_ERROR startup_mainwindow_and_dbserver(AW_root *aw_root, bool install_client_callback, const char *autorun_macro) {
176    GB_ERROR error = NULL;
177    nt_create_main_window(aw_root);
178
179    if (GB_read_clients(GLOBAL_gb_main) == 0) { // server
180        error = GBCMS_open(":", 0, GLOBAL_gb_main);
181        if (error) {
182            error = GBS_global_string("THIS PROGRAM HAS PROBLEMS TO OPEN INTERCLIENT COMMUNICATION:\n"
183                                      "Reason: %s\n"
184                                      "(maybe there is already another server running)\n"
185                                      "You cannot use any EDITOR or other external SOFTWARE from here.\n"
186                                      "Advice: Close ARB again, open a console, type 'arb_clean' and restart arb.\n"
187                                      "Caution: Any unsaved data in an eventually running ARB will be lost.\n",
188                                      error);
189        }
190        else {
191            aw_root->add_timed_callback(NT_SERVE_DB_TIMER, (AW_RCB)serve_db_interrupt, 0, 0);
192            error = nt_check_database_consistency();
193        }
194    }
195    else { // client
196        if (install_client_callback) {
197            aw_root->add_timed_callback(NT_CHECK_DB_TIMER, (AW_RCB)check_db_interrupt, 0, 0);
198        }
199    }
200
201    if (!error && autorun_macro) awt_execute_macro(aw_root, autorun_macro);
202    return error;
203}
204
205static ARB_ERROR load_and_startup_main_window(AW_root *aw_root, const char *autorun_macro) {
206    char *db_server = aw_root->awar(AWAR_DB_PATH)->read_string();
207    GLOBAL_gb_main  = GBT_open(db_server, "rw");
208
209    ARB_ERROR error;
210    if (!GLOBAL_gb_main) {
211        error = GB_await_error();
212    }
213    else {
214        aw_root->awar(AWAR_DB_PATH)->write_string(db_server);
215
216#define MAXNAMELEN 35
217        int len = strlen(db_server);
218        if (len>MAXNAMELEN) {
219            char *nameOnly = strrchr(db_server, '/');
220            if (nameOnly) {
221                nameOnly++;
222                len -= (nameOnly-db_server);
223                memmove(db_server, nameOnly, len+1);
224                if (len>MAXNAMELEN) {
225                    strcpy(db_server+MAXNAMELEN-3, "...");
226                }
227            }
228        }
229#if defined(DEBUG)
230        AWT_announce_db_to_browser(GLOBAL_gb_main, GBS_global_string("ARB database (%s)", db_server));
231#endif // DEBUG
232
233        GB_ERROR problem = startup_mainwindow_and_dbserver(aw_root, true, autorun_macro);
234        aw_message_if(problem); // no need to terminate ARB
235    }
236
237    free(db_server);
238    return error;
239}
240
241static void nt_delete_database(AW_window *aww) {
242    AW_root *aw_root   = aww->get_root();
243    char    *db_server = aw_root->awar(AWAR_DB_PATH)->read_string();
244
245    if (strlen(db_server)) {
246        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))) {
247            GB_ERROR error = 0;
248            error = GB_delete_database(db_server);
249            if (error) {
250                aw_message(error);
251            }
252            else {
253                aw_root->awar(AWAR_DB_FILTER)->touch();
254            }
255        }
256    }
257    else {
258        aw_message("No database selected");
259    }
260    free(db_server);
261}
262
263static void start_main_window_after_import(AW_root *aw_root) {
264    GLOBAL_NT.awr  = aw_root;
265    aw_message_if(startup_mainwindow_and_dbserver(aw_root, false, NULL));
266}
267
268static void nt_intro_start_existing(AW_window *aw_intro) {
269    aw_intro->hide();
270    ARB_ERROR error = load_and_startup_main_window(aw_intro->get_root(), NULL);
271    if (error) {
272        aw_intro->show();
273        aw_popup_ok(error.deliver());
274    }
275    else {
276        error.expect_no_error();
277    }
278}
279
280static void nt_intro_start_merge(AW_window *aw_intro, AW_root *aw_root) {
281    nt_assert(contradicted(aw_intro, aw_root)); // one of them is passed!
282    if (aw_intro) aw_root = aw_intro->get_root();
283    create_MG_main_window(aw_root);
284    if (aw_intro) aw_intro->hide();
285}
286
287static void nt_intro_start_import(AW_window *aw_intro) {
288    aw_intro->hide();
289
290    AW_root *aw_root = aw_intro->get_root();
291    aw_root->awar_string(AWAR_DB_PATH)->write_string("noname.arb");
292    aw_root->awar_int(AWAR_READ_GENOM_DB, IMP_PLAIN_SEQUENCE); // Default toggle  in window  "Create&import" is Non-Genom
293    GLOBAL_gb_main   = open_AWTC_import_window(aw_root, "", true, 0, (AW_RCB)start_main_window_after_import, 0, 0);
294}
295
296static AW_window *nt_create_intro_window(AW_root *awr) {
297    AW_window_simple *aws = new AW_window_simple;
298    aws->init(awr, "ARB_INTRO", "ARB INTRO");
299    aws->load_xfig("arb_intro.fig");
300
301    aws->callback((AW_CB0)exit);
302    aws->at("close");
303    aws->create_button("CANCEL", "CANCEL", "C");
304
305    aws->at("help");
306    aws->callback(AW_POPUP_HELP, (AW_CL)"arb_intro.hlp");
307    aws->create_button("HELP", "HELP", "H");
308
309    AW_create_fileselection(aws, "tmp/nt/arbdb");
310
311    aws->button_length(0);
312
313    aws->at("logo");
314    aws->create_button(0, "#logo.xpm");
315
316    aws->at("version");
317    aws->create_button(0, GBS_global_string("Version " ARB_VERSION), 0); // version
318
319    aws->at("copyright");
320    aws->create_button(0, GBS_global_string("(C) 1993-" ARB_BUILD_YEAR), 0);
321
322    aws->at("old");
323    aws->callback(nt_intro_start_existing);
324    aws->create_autosize_button("OPEN_SELECTED", "OPEN SELECTED", "O");
325
326    aws->at("del");
327    aws->callback(nt_delete_database);
328    aws->create_autosize_button("DELETE_SELECTED", "DELETE SELECTED");
329
330    aws->at("new_complex");
331    aws->callback(nt_intro_start_import);
332    aws->create_autosize_button("CREATE_AND_IMPORT", "CREATE AND IMPORT", "I");
333
334    aws->at("merge");
335    aws->callback((AW_CB1)nt_intro_start_merge, 0);
336    aws->create_autosize_button("MERGE_TWO_DATABASES", "MERGE TWO ARB DATABASES", "O");
337
338    aws->at("expert");
339    aws->create_toggle(AWAR_EXPERT);
340
341    return aws;
342}
343
344static void AWAR_DB_PATH_changed_cb(AW_root *awr) {
345    static bool avoid_recursion = false;
346
347    if (!avoid_recursion) {
348        LocallyModify<bool> flag(avoid_recursion, true);
349
350        char *value  = awr->awar(AWAR_DB_PATH)->read_string();
351        char *lslash = strrchr(value, '/');
352
353        char *name = lslash ? lslash+1 : value;
354#if defined(DEBUG)
355        printf("writing '%s' to AWAR_DB_NAME\n", name);
356#endif // DEBUG
357        awr->awar(AWAR_DB_NAME)->write_string(name);
358
359        if (lslash) {               // update value of directory
360            lslash[0] = 0;
361            awr->awar(AWAR_DB"directory")->write_string(value);
362            lslash[0] = '/';
363        }
364
365        free(value);
366    }
367}
368
369class NtreeCommandLine : virtual Noncopyable {
370    int               arg_count;
371    char const*const* args;
372
373    bool help_requested;
374    bool do_import;
375    bool do_export;
376   
377    const char *macro_name;
378
379public:
380    NtreeCommandLine(int argc_, char const*const* argv_)
381        : arg_count(argc_-1),
382          args(argv_+1),
383          help_requested(false),
384          do_import(false),
385          do_export(false),
386          macro_name(NULL)
387    {}
388
389    void shift() { ++args; --arg_count; }
390
391    int free_args() const { return arg_count; }
392    const char *get_arg(int num) const { return num<arg_count ? args[num] : NULL; }
393
394    bool wants_help() const { return help_requested; }
395    bool wants_export() const { return do_export; }
396    bool wants_import() const { return do_import; }
397    bool wants_merge() const { return arg_count == 2; }
398
399    const char *autorun_macro() const { return macro_name; }
400
401    void print_help(FILE *out) const {
402        fprintf(out,
403                "\n"
404                "arb_ntree version " ARB_VERSION "\n"
405                    "(C) 1993-" ARB_BUILD_YEAR " Lehrstuhl fuer Mikrobiologie - TU Muenchen\n"
406                    "http://www.arb-home.de/\n"
407#if defined(SHOW_WHERE_BUILD)
408                    "(version build by: " ARB_BUILD_USER "@" ARB_BUILD_HOST ")\n"
409#endif // SHOW_WHERE_BUILD
410                    "\n"
411                    "Known command line arguments:\n"
412                    "\n"
413                    "db.arb             => start ARB_NTREE with database db.arb\n"
414                    ":                  => start ARB_NTREE and connect to existing db_server\n"
415                    "db1.arb db2.arb    => merge databases db1.arb and db2.arb\n"
416                    "w/o arguments      => start database browser in current directory\n"
417                    "directory          => start database browser in 'directory'\n"
418                    "-export            => connect to existing ARB server and export database to noname.arb\n"
419                    "-import file       => import 'file' into new database\n"
420                    "\n"
421                    );
422    }
423
424    ARB_ERROR parse() {
425        ARB_ERROR error;
426
427        while (!error && arg_count>0 && args[0][0] == '-') {
428            const char *opt = args[0]+1;
429            if (opt[0] == '-') ++opt; // accept '-' or '--'
430            if (strcmp(opt, "help") == 0 || strcmp(opt, "h") == 0) { help_requested = true; }
431            else if (strcmp(opt, "export") == 0) { do_export = true; }
432            else if (strcmp(opt, "import") == 0) { do_import = true; }
433            else if (strcmp(opt, "execute") == 0) { shift(); macro_name = *args; }
434            else {
435                error = GBS_global_string("Unknown switch '%s'", args[0]);
436            }
437            shift();
438        }
439        // non-switch arguments remain in arg_count/args
440        if (!error) {
441            if (do_import && do_export) error           = "--import can't be used together with --export";
442            else if (do_export && arg_count != 0) error = "superfluous argument behind --export";
443            else if (do_import && arg_count != 1) error = "expected file-name or -mask name behind --import";
444            else if (arg_count>2) error                 = "expected to see up to 2 arguments on command line";
445        }
446        return error;
447    }
448};
449
450enum RunMode { NORMAL, IMPORT, EXPORT, MERGE, BROWSE };
451
452static ARB_ERROR check_argument_for_mode(const char *database, char *&browser_startdir, RunMode& mode) {
453    // Check whether 'database' is a
454    // - ARB database
455    // - directory name
456    // - file to import
457    //
458    // Modify 'mode' occordingly.
459    // Set 'browser_startdir'
460
461    ARB_ERROR error;
462    if (mode != IMPORT) {
463        if (!database) mode = BROWSE;
464        else {
465            GB_ERROR load_file_err = GBT_check_arb_file(database);
466            if (load_file_err) {
467                char *full_path = AW_unfold_path("PWD", database);
468
469                enum Action { LOAD_DB, CONVERT_DB, BROWSE_DB, EXIT, NOIDEA };
470                Action action = NOIDEA;
471                if (GB_is_directory(full_path)) {
472                    action = BROWSE_DB;
473                }
474                else {
475                    if (!GB_is_regularfile(full_path)) {
476                        const char *msg = GBS_global_string("'%s' is neither a known option nor a legal file- or directory-name.\n(Error: %s)",
477                                                            full_path, load_file_err);
478
479                        int ans = aw_question(NULL, msg, "Browser,Exit");
480                        action  = ans ? EXIT : BROWSE_DB;
481                    }
482                    else {
483                        const char *msg = GBS_global_string("Your file is not an original arb file\n(%s)", load_file_err);
484                        action          = (Action)aw_question(NULL, msg, "Continue (dangerous),Start Converter,Browser,Exit");
485                    }
486                }
487
488                switch (action) {
489                    case CONVERT_DB:    mode = IMPORT; break;
490                    case LOAD_DB:       break;
491                    case NOIDEA:        nt_assert(0);
492                    case EXIT:          error = "User abort"; break;
493                    case BROWSE_DB: {
494                        char *dir = nulldup(full_path);
495                        while (dir && !GB_is_directory(dir)) freeset(dir, AW_extract_directory(dir));
496
497                        if (dir) {
498                            nt_assert(GB_is_directory(dir));
499                            reassign(browser_startdir, dir);
500                            mode = BROWSE;
501                        }
502                        free(dir);
503                        break;
504                    }
505               
506                }
507                free(full_path);
508            }
509        }
510    }
511   
512    return error;
513}
514
515int ARB_main(int argc, const char *argv[]) {
516    aw_initstatus();
517    GB_set_verbose();
518
519    GB_shell shell;
520    AW_root *aw_root = AWT_create_root("ntree.arb", "ARB_NT");
521
522    GLOBAL_NT.awr = aw_root;
523
524    {
525        char *message = strdup(GB_path_in_ARBLIB("message"));
526        char *stamp   = strdup(GB_path_in_arbprop("msgtime"));
527        if (GB_time_of_file(message)>GB_time_of_file(stamp)) {
528            AW_edit(message);
529            system(GBS_global_string("touch %s", stamp));
530        }
531        free(stamp);
532        free(message);
533    }
534
535    // create some early awars
536    // Note: normally you don't like to add your awar-init-function here, but into nt_create_all_awars()
537
538    AW_create_fileselection_awars(aw_root, AWAR_DB, "", ".arb", "noname.arb");
539    aw_root->awar_string(AWAR_DB_TYPE, "b");
540    aw_root->awar_string(AWAR_DB_NAME, "noname.arb");
541    aw_root->awar(AWAR_DB_PATH)->add_callback(AWAR_DB_PATH_changed_cb);
542
543    aw_root->awar_int(AWAR_EXPERT, 0);
544
545    init_Advisor(aw_root);
546    AWT_install_cb_guards();
547
548    NtreeCommandLine cl(argc, argv);
549    ARB_ERROR        error = cl.parse();
550
551    ARB_declare_global_awars(aw_root, AW_ROOT_DEFAULT);
552
553    if (!error) {
554        if (cl.wants_help()) {
555            cl.print_help(stderr);
556        }
557        else {
558            RunMode mode = NORMAL;
559
560            if (cl.wants_export())      mode = EXPORT;
561            else if (cl.wants_import()) mode = IMPORT;
562            else if (cl.wants_merge())  mode = MERGE;
563
564            if (mode == EXPORT) {
565                MG_create_all_awars(aw_root, AW_ROOT_DEFAULT, ":", "noname.arb");
566                GLOBAL_gb_merge = GBT_open(":", "rw");
567                if (!GLOBAL_gb_merge) {
568                    error = GB_await_error();
569                }
570                else {
571#if defined(DEBUG)
572                    AWT_announce_db_to_browser(GLOBAL_gb_merge, "Current database (:)");
573#endif // DEBUG
574
575                    GLOBAL_gb_dest = GBT_open("noname.arb", "cw");
576#if defined(DEBUG)
577                    AWT_announce_db_to_browser(GLOBAL_gb_dest, "New database (noname.arb)");
578#endif // DEBUG
579
580                    MG_start_cb2(NULL, aw_root, true, true);
581                    aw_root->main_loop();
582                }
583            }
584            else if (mode == MERGE) {
585                MG_create_all_awars(aw_root, AW_ROOT_DEFAULT, cl.get_arg(0), cl.get_arg(1));
586                nt_intro_start_merge(0, aw_root);
587                aw_root->main_loop();
588                nt_assert(0);
589            }
590            else {
591                const char *database         = NULL;
592                char       *browser_startdir = strdup(".");
593
594                if (cl.free_args() > 0) database = cl.get_arg(0);
595
596                error = check_argument_for_mode(database, browser_startdir, mode); 
597                if (!error) {
598                    if (mode == IMPORT) {
599                        aw_root->awar_int(AWAR_READ_GENOM_DB, IMP_PLAIN_SEQUENCE);
600                        GLOBAL_gb_main = open_AWTC_import_window(aw_root, database, true, 0, (AW_RCB)start_main_window_after_import, 0, 0);
601                        aw_root->main_loop();
602                    }
603                    else if (mode == NORMAL) {
604                        aw_root->awar(AWAR_DB_PATH)->write_string(database);
605                        error = load_and_startup_main_window(aw_root, cl.autorun_macro());
606                        if (!error) aw_root->main_loop();
607                    }
608                    else if (mode == BROWSE) {
609                        aw_root->awar(AWAR_DB"directory")->write_string(browser_startdir);
610                        char *latest = GB_find_latest_file(browser_startdir, "/\\.(arb|a[0-9]{2})$/");
611                        if (latest) {
612                            int l = strlen(latest);
613                            strcpy(latest+l-3, "arb");
614                            aw_root->awar(AWAR_DB_PATH)->write_string(latest);
615                            free(latest);
616                        }
617                        AW_window *iws;
618                        if (GLOBAL_NT.window_creator) {
619                            iws = GLOBAL_NT.window_creator(aw_root, 0);
620                        }
621                        else {
622                            iws = nt_create_intro_window(aw_root);
623                        }
624                        iws->show();
625                        aw_root->main_loop();
626                    }
627                }
628                free(browser_startdir);
629            }
630        }
631    }
632
633    int exitcode = error ? EXIT_FAILURE : EXIT_SUCCESS;
634
635    if (error) aw_popup_ok(error.deliver());
636    else error.expect_no_error();
637
638    delete aw_root;
639
640    return exitcode;
641}
642
Note: See TracBrowser for help on using the repository browser.