source: tags/ms_r18q1/AWTI/AWTI_edit.cxx

Last change on this file was 16763, checked in by westram, 6 years ago
File size: 19.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AWTI_edit.cxx                                     //
4//   Purpose   : test and modify import/export filters             //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in October 2017   //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "awti_edit.hxx"
12#include "awti_exp_local.hxx"
13#include "awti_imp_local.hxx"
14
15#include <seqio.hxx>
16#include <db_scanner.hxx>
17
18#include <awt_prompt.hxx>
19
20#include <aw_root.hxx>
21#include <aw_edit.hxx>
22#include <aw_msg.hxx>
23#include <aw_awar_defs.hxx>
24#include <aw_select.hxx>
25
26#include <BufferedFileReader.h>
27#include <FileWatch.h>
28
29#include <arb_strbuf.h>
30#include <arb_progress.h>
31#include <arb_misc.h>
32
33
34enum FormatType { FT_IMPORT, FT_EXPORT };
35enum FormatAction { EDIT, COPY, RENAME, DELETE };
36
37static const char *action_name[] = {
38    "edit",
39    "copy",
40    "rename",
41    "delete",
42};
43
44class FormatTester : virtual Noncopyable {
45    FormatType  type;
46    AW_window  *aws;             // filter test window
47    AW_awar    *awar_filtername; // contains full path of selected (=tested) filter
48
49    const char *get_awarname() const { return type == FT_IMPORT ? AWAR_IMPORT_FORMATNAME : AWAR_EXPORT_FORMATNAME; }
50    const char *ieport()       const { return type == FT_IMPORT ? "import" : "export"; }
51    const char *get_suffix()   const { return type == FT_IMPORT ? "ift" : "eft"; }
52
53    const char *get_selected_filter() const { return awar_filtername->read_char_pntr(); }
54
55    char *user_filter_fullname(const char *name) {
56        char       *name_suf = ARB_strdup(GB_append_suffix(name, get_suffix()));
57        const char *user_dir = GB_path_in_arbprop("filter");
58        char       *fullpath = ARB_strdup(GB_concat_path(user_dir, name_suf));
59        free(name_suf);
60        return fullpath;
61    }
62
63    // management button callbacks:
64    GB_ERROR nameEntered_handler(FormatAction action, const char *targetName) {
65        GB_ERROR    error          = NULp;
66        char       *fullTargetName = user_filter_fullname(targetName);
67        const char *fullSourceName = get_selected_filter();
68
69        if (strcmp(fullSourceName, fullTargetName) == 0) {
70            error = "source and destination are the same";
71        }
72        else if (action == RENAME) {
73            error = GB_safe_rename_file(fullSourceName, fullTargetName);
74        }
75        else {
76            awti_assert(action == COPY);
77            error = GB_safe_copy_file(fullSourceName, fullTargetName);
78        }
79        if (!error) {
80            awar_filtername->write_string(fullTargetName);
81        }
82        free(fullTargetName);
83        return error;
84    }
85    static GB_ERROR nameEntered_wrapper(const char *targetName, FormatAction action, FormatTester *const tester) {
86        return tester->nameEntered_handler(action, targetName);
87    }
88    void handle_copyRename(FormatAction action) {
89        char *Action = strdup(action_name[action]);
90        Action[0]    = toupper(Action[0]);
91
92        char *title  = GBS_global_string_copy("%s %s format", Action, ieport());
93        char *prompt = GBS_global_string_copy("Enter name of %s format:", ieport());
94
95        char *nameOnly;
96        GB_split_full_path(get_selected_filter(), NULp, NULp, &nameOnly, NULp);
97        if (!nameOnly) nameOnly = ARB_strdup("");
98
99        AWT_activate_prompt(title, prompt, nameOnly, Action, makeResultHandler(nameEntered_wrapper, action, this));
100
101        free(nameOnly);
102        free(prompt);
103        free(title);
104    }
105
106public:
107    FormatTester(FormatType type_, AW_window *aws_) :
108        type(type_),
109        aws(aws_)
110    {
111        awar_filtername = aws->get_root()->awar(get_awarname());
112        awti_assert(awar_filtername);
113    }
114
115    void create_common_gui(const char *helpfile);
116
117    void handle_action_cb(FormatAction action) {
118        char *filter_name = awar_filtername->read_string(); // use copy (may disappear)
119        if (!GB_is_regularfile(filter_name)) {
120            aw_message(GBS_global_string("Please select the filter to %s", action_name[action]));
121        }
122        else {
123            if (action == COPY || action == RENAME) {
124                handle_copyRename(action);
125            }
126            else {
127                bool is_writeable = GB_is_writeablefile(filter_name);
128                if (action == EDIT) {
129                    if (!is_writeable) {
130                        aw_message("The filter is write-protected (use COPY to create a local copy)");
131                    }
132                    else {
133                        AW_edit(filter_name);
134                    }
135                }
136                else if (action == DELETE) {
137                    if (!is_writeable) {
138                        aw_message("The filter is write-protected");
139                    }
140                    else {
141                        awar_filtername->write_string(""); // deselect
142                        if (GB_unlink(filter_name)<0) {
143                            awar_filtername->write_string(filter_name); // re-select
144                            aw_message(GB_await_error());
145                        }
146                    }
147                }
148                else {
149                    awti_assert(0); // unhandled FormatAction
150                }
151            }
152        }
153        free(filter_name);
154    }
155};
156
157typedef SmartPtr<FormatTester> FormatTesterPtr;
158
159static void action_cb_wrapper(AW_window*, FormatTester *const tester, FormatAction action) {
160    tester->handle_action_cb(action);
161}
162
163void FormatTester::create_common_gui(const char *helpfile) {
164    aws->at("close");
165    aws->callback(AW_POPDOWN);
166    aws->create_button("CLOSE", "CLOSE", "C");
167
168    aws->at("help");
169    aws->callback(makeHelpCallback(helpfile));
170    aws->create_button("HELP", "HELP", "H");
171
172    aws->at("filter");
173    aws->create_button(NULp, awar_filtername->awar_name, NULp, "+");
174
175    aws->at("buttons");
176
177    aws->auto_space(5, 5);
178
179    aws->callback(makeWindowCallback(action_cb_wrapper, this, EDIT));   aws->create_button("EDIT",   "EDIT",   "E");
180    aws->callback(makeWindowCallback(action_cb_wrapper, this, COPY));   aws->create_button("COPY",   "COPY",   "C");
181    aws->callback(makeWindowCallback(action_cb_wrapper, this, RENAME)); aws->create_button("RENAME", "RENAME", "R");
182    aws->callback(makeWindowCallback(action_cb_wrapper, this, DELETE)); aws->create_button("DELETE", "DELETE", "D");
183}
184
185static char *get_file_content_for_viewer(const char *filename) {
186    char *result = NULp;
187    FILE *in     = fopen(filename, "rt");
188    if (in) {
189        BufferedFileReader reader(filename, in);
190        GBS_strstruct      view(10000);
191
192        string line;
193        while (reader.getLine(line)) {
194            view.ncat(line.c_str(), line.size());
195            view.put('\n');
196        }
197
198        result = view.release();
199    }
200    else {
201        result = ARB_strdup(GB_IO_error("reading", filename));
202    }
203    return result;
204}
205
206
207#define TESTER_AWAR_PREFIX          "tmp/tester/"
208#define AWAR_TESTER_LINK_EX2IMPORT  TESTER_AWAR_PREFIX "link"
209#define AWAR_TESTER_EXPORT_RESULT   TESTER_AWAR_PREFIX "result"
210#define AWAR_TESTER_IMPORT_FILENAME TESTER_AWAR_PREFIX "filename"
211#define AWAR_TESTER_IMPORT_STATUS   TESTER_AWAR_PREFIX "status"
212#define AWAR_TESTER_IMPORT_SPECIES  TESTER_AWAR_PREFIX "species"
213
214static bool test_import_active = false;
215static bool test_export_active = false;
216
217static void disable_test_import(AW_window*) {
218    test_import_active = false;
219}
220static void disable_test_export(AW_window *aww) {
221    aww->get_root()->awar(AWAR_TESTER_LINK_EX2IMPORT)->write_int(0); // avoid confusion in export-test
222    test_export_active = false;
223}
224
225static void create_tester_awars(AW_root *awr) {
226    static bool initialized = false;
227    if (!initialized) {
228        awr->awar_string(AWAR_TESTER_EXPORT_RESULT,   "<undefined>");
229        awr->awar_string(AWAR_TESTER_IMPORT_STATUS,   "<undefined>");
230        awr->awar_string(AWAR_TESTER_IMPORT_FILENAME, "");
231        awr->awar_string(AWAR_TESTER_IMPORT_SPECIES,  "");
232        awr->awar_int   (AWAR_TESTER_LINK_EX2IMPORT,  0);
233        initialized = true;
234    }
235}
236
237static void update_exportTest_result_cb(AW_root *awr, GBDATA *gb_main, adfiltercbstruct *acbs) {
238    if (test_export_active) {
239        GB_transaction ta(gb_main);
240
241        AW_awar *awar_result      = awr->awar(AWAR_TESTER_EXPORT_RESULT);
242        char    *selected_species = awr->awar(AWAR_SPECIES_NAME)->read_string();
243        bool     have_species     = GBT_find_species(gb_main, selected_species);
244
245        if (have_species) {
246            AP_filter *filter = awt_get_filter(acbs);
247
248            int   cut_stop_codon = awr->awar(AWAR_EXPORT_CUTSTOP)->read_int();
249            int   compress       = awr->awar(AWAR_EXPORT_COMPRESS)->read_int();
250            int   linked         = awr->awar(AWAR_TESTER_LINK_EX2IMPORT)->read_int();
251            char *formname       = awr->awar(AWAR_EXPORT_FORMATNAME)->read_string();
252            char *db_name        = awr->awar(AWAR_DB_NAME)->read_string();
253            char *outname        = GB_create_tempfile("test_export");
254            char *real_outname   = NULp;
255
256            arb_suppress_progress here;
257
258            GB_ERROR error = SEQIO::export_by_format(gb_main, SEQIO::EBF_ONE, selected_species, filter, cut_stop_codon,
259                                                     compress, db_name, formname, outname, 0, &real_outname);
260
261            if (error) {
262                awar_result->write_string(GBS_global_string("Error during export:\n%s", error));
263            }
264            else {
265                char *content = get_file_content_for_viewer(real_outname);
266                awar_result->write_string(content);
267                free(content);
268
269                if (linked) {
270                    awr->awar(AWAR_TESTER_IMPORT_FILENAME)->write_string(real_outname);
271                }
272                else {
273                    GB_unlink_or_warn(real_outname, NULp);
274                }
275            }
276
277            free(real_outname);
278            free(outname);
279            free(selected_species);
280            free(db_name);
281            free(formname);
282        }
283        else {
284            awar_result->write_string("<no species selected>");
285        }
286    }
287}
288
289static void exportImportLinkChanged_cb(AW_root *awr, GBDATA *gb_main, adfiltercbstruct *acbs) {
290    if (awr->awar(AWAR_TESTER_LINK_EX2IMPORT)->read_int()) { // link activated
291        update_exportTest_result_cb(awr, gb_main, acbs); // run export test again (will trigger AWAR_TESTER_IMPORT_FILENAME on success)
292    }
293    else { // link de-activated
294        awr->awar(AWAR_IMPORT_FILENAME)->touch(); // reset AWAR_TESTER_IMPORT_FILENAME to "unlinked" value
295    }
296}
297
298struct ImportTestData : virtual Noncopyable {
299    AW_selection_list *species_sellist;
300    DbScanner         *field_scanner;
301    GBDATA            *gb_main_lastImport;
302
303    ImportTestData() :
304        species_sellist(NULp),
305        field_scanner(NULp),
306        gb_main_lastImport(NULp)
307    {
308    }
309    ~ImportTestData() {
310        if (gb_main_lastImport) {
311            GB_close(gb_main_lastImport);
312        }
313    }
314
315    void forgetLastImport() {
316        if (gb_main_lastImport) {
317            GB_close(gb_main_lastImport);
318            gb_main_lastImport = NULp;
319        }
320        species_sellist->clear();
321        species_sellist->insert_default("", "");
322        species_sellist->update();
323    }
324    void setNewImport(GBDATA *gb_new_main) {
325        gb_main_lastImport = gb_new_main;
326        field_scanner->RemapToDatabase(gb_main_lastImport);
327    }
328
329    void refill_species_list() {
330        GB_transaction ta(gb_main_lastImport);
331        for (GBDATA *gb_species = GBT_first_species(gb_main_lastImport);
332             gb_species;
333             gb_species = GBT_next_species(gb_species))
334        {
335            const char *name = GBT_get_name(gb_species);
336            if (name) {
337                species_sellist->insert(name, name);
338            }
339            else {
340                species_sellist->insert("<unnamed species>", "");
341            }
342        }
343        species_sellist->insert_default("", "");
344        species_sellist->update();
345    }
346
347    void remap_scanner(AW_root *awr);
348
349    void countSpeciesAndData(const char *aliName, long& speciesCount, long& dataCount) {
350        speciesCount = 0;
351        dataCount    = 0;
352
353        GB_transaction ta(gb_main_lastImport);
354        for (GBDATA *gb_species = GBT_first_species(gb_main_lastImport);
355             gb_species;
356             gb_species = GBT_next_species(gb_species))
357        {
358            ++speciesCount;
359
360            GBDATA     *gb_seq = GBT_find_sequence(gb_species, aliName);
361            const char *seq    = GB_read_char_pntr(gb_seq);
362            for (int p = 0; seq[p]; ++p) {
363                dataCount += !!isalpha(seq[p]);
364            }
365        }
366    }
367
368};
369
370static void neverCalledDummy_cb(AW_root*) { awti_assert(0); }
371
372static void rerun_importTest_cb(AW_root *awr, ImportTestData *tdata) {
373    if (test_import_active) {
374        ArbImporter importer(makeRootCallback(neverCalledDummy_cb));
375
376        arb_suppress_progress here;
377
378        tdata->forgetLastImport();
379
380        char     *mask           = awr->awar(AWAR_TESTER_IMPORT_FILENAME)->read_string(); // will use result of export (if linked)
381        GB_ERROR  error          = importer.import_data(awr, mask, true);
382        AW_awar  *awar_source    = awr->awar(AWAR_TESTER_IMPORT_STATUS);
383        AW_awar  *awar_selected  = awr->awar(AWAR_TESTER_IMPORT_SPECIES);
384        bool      touch_selected = true;
385
386        if (error) {
387            awar_source->write_string(error);
388        }
389        else {
390            tdata->setNewImport(importer.takeImportDB());
391            tdata->refill_species_list();
392
393            const char *aliName = awr->awar(AWAR_IMPORT_ALI)->read_char_pntr();
394            long        speciesCount, dataCount;
395            tdata->countSpeciesAndData(aliName, speciesCount, dataCount);
396
397            const char *status = GBS_global_string("Species imported: %li\n"
398                                                   "Base count: %s",
399                                                   speciesCount,
400                                                   GBS_readable_size(dataCount, "bp"));
401            awar_source->write_string(status);
402
403            {
404                GB_transaction ta(tdata->gb_main_lastImport);
405                char *previously_selected = awar_selected->read_string();
406                if (!GBT_find_species(tdata->gb_main_lastImport, previously_selected)) {
407                    GBDATA *gb_first = GBT_first_species(tdata->gb_main_lastImport);
408                    if (gb_first) {
409                        awar_selected->write_string(GBT_get_name(gb_first));
410                        touch_selected = false;
411                    }
412                }
413                free(previously_selected);
414            }
415        }
416        if (touch_selected) awar_selected->touch(); // remaps species fields
417        free(mask);
418    }
419}
420
421void ImportTestData::remap_scanner(AW_root *awr) {
422    if (gb_main_lastImport) {
423        GB_transaction ta(gb_main_lastImport);
424        const ItemSelector&  selector   = field_scanner->get_selector();
425        GBDATA              *gb_species = selector.get_selected_item(gb_main_lastImport, awr);
426        field_scanner->Map(gb_species, selector.change_key_path);
427    }
428}
429static void remap_scanner_cb(AW_root *awr, ImportTestData *tdata) { tdata->remap_scanner(awr); }
430
431static GBDATA* get_selected_imported_species(GBDATA *gb_main, AW_root *aw_root) {
432    char   *species_name = aw_root->awar(AWAR_TESTER_IMPORT_SPECIES)->read_string();
433    GBDATA *gb_species   = NULp;
434    if (species_name[0]) {
435        gb_species = GBT_find_species(gb_main, species_name);
436    }
437    free(species_name);
438    return gb_species;
439}
440static const ItemSelector& get_importedSpecies_selector() {
441    static struct MutableItemSelector spec_sel = SPECIES_get_selector(); // copy species selector
442    spec_sel.get_selected_item = get_selected_imported_species;
443    return spec_sel;
444}
445
446static void update_import_filename_cb(AW_root *awr) {
447    const char *currImportSource = awr->awar(AWAR_IMPORT_FILENAME)->read_char_pntr();
448    awr->awar(AWAR_TESTER_IMPORT_FILENAME)->write_string(currImportSource);
449}
450
451static void import_file_changed_cb(const char *, ChangeReason, ImportTestData *tdata) {
452    rerun_importTest_cb(AW_root::SINGLETON, tdata);
453}
454
455void AWTI_activate_import_test_window(AW_window *awp) {
456    static AW_window_simple *aws = NULp;
457
458    test_import_active = true;
459
460    AW_root *awr = awp->get_root();
461    if (!aws) {
462        create_tester_awars(awr);
463
464        aws = new AW_window_simple;
465        aws->init(awr, "ARB_IMPORT_TEST", "Test import filter");
466        aws->load_xfig("awt/import_test.fig");
467
468        static FormatTesterPtr tester = new FormatTester(FT_IMPORT, aws);
469
470        tester->create_common_gui("import_test.hlp");
471
472        aws->at("stat");
473        aws->create_text_field(AWAR_TESTER_IMPORT_STATUS);
474
475        static SmartPtr<ImportTestData> tdataPtr = new ImportTestData; // (cleaned up on exit)
476        ImportTestData *const           tdata    = &*tdataPtr;
477
478        aws->at("spec");
479        tdata->species_sellist = aws->create_selection_list(AWAR_TESTER_IMPORT_SPECIES, true);
480
481        tdata->field_scanner = DbScanner::create(NULp, "test_import", aws, "fields", NULp, NULp, DB_SCANNER, NULp, get_importedSpecies_selector());
482
483        RootCallback test_import_cb = makeRootCallback(rerun_importTest_cb, tdata);
484        awr->awar(AWAR_IMPORT_FORMATNAME)->add_callback(test_import_cb);
485        awr->awar(AWAR_IMPORT_ALI)       ->add_callback(test_import_cb);
486
487        awr->awar(AWAR_IMPORT_FILENAME)->add_callback(update_import_filename_cb);
488        static SmartPtr<FileWatch> fwatch = new FileWatch(AWAR_TESTER_IMPORT_FILENAME, makeFileChangedCallback(import_file_changed_cb, tdata));
489
490        awr->awar(AWAR_TESTER_IMPORT_SPECIES)->add_callback(makeRootCallback(remap_scanner_cb, tdata));
491
492        aws->on_hide(disable_test_import);
493    }
494
495    awr->awar(AWAR_IMPORT_FORMATNAME)->touch(); // trigger callback once (needed after re-opening window)
496    aws->activate();
497}
498
499void AWTI_activate_export_test_window(AW_window *awp, GBDATA *gb_main, adfiltercbstruct *acbs) {
500    static AW_window_simple *aws = NULp;
501
502    test_export_active = true;
503
504    AW_root *awr = awp->get_root();
505    if (!aws) {
506        create_tester_awars(awr);
507
508        aws = new AW_window_simple;
509        aws->init(awr, "ARB_EXPORT_TEST", "Test export filter");
510        aws->load_xfig("awt/export_test.fig");
511
512        static FormatTesterPtr tester = new FormatTester(FT_EXPORT, aws);
513
514        tester->create_common_gui("export_test.hlp");
515
516        aws->label("Link to import");
517        aws->create_toggle(AWAR_TESTER_LINK_EX2IMPORT);
518
519        aws->at("res");
520        aws->create_text_field(AWAR_TESTER_EXPORT_RESULT);
521
522        RootCallback test_export_cb = makeRootCallback(update_exportTest_result_cb, gb_main, acbs);
523        awr->awar(AWAR_SPECIES_NAME)          ->add_callback(test_export_cb);
524        awr->awar(AWAR_EXPORT_FORMATNAME)     ->add_callback(test_export_cb);
525        awr->awar(AWAR_EXPORT_CUTSTOP)        ->add_callback(test_export_cb);
526        awr->awar(AWAR_EXPORT_COMPRESS)       ->add_callback(test_export_cb);
527        awr->awar(AWAR_TESTER_LINK_EX2IMPORT) ->add_callback(makeRootCallback(exportImportLinkChanged_cb, gb_main, acbs));
528        awr->awar(acbs->def_filter)           ->add_callback(test_export_cb); // filter string
529
530        aws->on_hide(disable_test_export);
531    }
532
533    awr->awar(AWAR_EXPORT_FORMATNAME)->touch(); // trigger callback once (needed after re-opening window)
534    aws->activate();
535}
536
537
Note: See TracBrowser for help on using the repository browser.