source: tags/ms_r16q4/SL/TREEDISP/TreeCallbacks.cxx

Last change on this file was 15374, checked in by westram, 7 years ago
  • reintegrates 'tree' into 'trunk'
    • implements #578
    • tree display
      • new options: triangle, attach points
      • optimized placement: folded group names, bootstrap values
      • split config window (too big)
  • adds: log:branches/tree@15347:15373
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.2 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : TreeCallbacks.cxx                                 //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "TreeCallbacks.hxx"
12
13#include <aw_color_groups.hxx>
14#include <aw_awars.hxx>
15#include <aw_advice.hxx>
16#include <aw_msg.hxx>
17#include <aw_root.hxx>
18#include <mode_text.h>
19
20#include <cctype>
21
22using namespace AW;
23
24// AISC_MKPT_PROMOTE:#ifndef TREEDISPLAY_HXX
25// AISC_MKPT_PROMOTE:#include <TreeDisplay.hxx>
26// AISC_MKPT_PROMOTE:#endif
27
28void nt_mode_event(UNFIXED, TREE_canvas *ntw, AWT_COMMAND_MODE mode) {
29    const char *text;
30
31    switch (mode) {
32        case AWT_MODE_ZOOM:  text = MODE_TEXT_STANDARD_ZOOMMODE(); break;
33        case AWT_MODE_EMPTY: text = MODE_TEXT_PLACEHOLDER();       break;
34
35        case AWT_MODE_SELECT: text = MODE_TEXT_1BUTTON("SELECT", "select species or open/close group");                  break;
36        case AWT_MODE_INFO:   text = MODE_TEXT_1BUTTON("INFO",   "click for info");                                      break;
37        case AWT_MODE_WWW:    text = MODE_TEXT_1BUTTON("WEB",    "Launch node dependent URL (see <Properties/WWW...>)"); break;
38
39        case AWT_MODE_SWAP:       text= MODE_TEXT_2BUTTONS("SWAP",         "swap child branches",        "flip whole subtree");          break;
40        case AWT_MODE_MARK:       text= MODE_TEXT_2BUTTONS("MARK",         "mark subtree",               "unmark subtree");              break;
41        case AWT_MODE_GROUP:      text= MODE_TEXT_2BUTTONS("GROUP",        "fold/unfold group",          "create/rename/destroy group"); break;
42        case AWT_MODE_NNI:        text= MODE_TEXT_2BUTTONS("OPTI(NNI)",    "once",                       "repeated");                    break;
43        case AWT_MODE_KERNINGHAN: text= MODE_TEXT_2BUTTONS("OPTI(KL)",     "once",                       "repeated");                    break;
44        case AWT_MODE_OPTIMIZE:   text= MODE_TEXT_2BUTTONS("OPTI(NNI&KL)", "once",                       "repeated");                    break;
45        case AWT_MODE_SETROOT:    text= MODE_TEXT_2BUTTONS("REROOT",       "set root to clicked branch", "search optimal root");         break;
46
47        case AWT_MODE_ROTATE: text = MODE_TEXT_1BUTTON_KEYS("ROTATE", "drag branch to rotate",         KEYINFO_ABORT_AND_RESET); break;
48        case AWT_MODE_SPREAD: text = MODE_TEXT_1BUTTON_KEYS("SPREAD", "drag branch to spread subtree", KEYINFO_ABORT_AND_RESET); break;
49
50        case AWT_MODE_LENGTH:    text = MODE_TEXT_2BUTTONS_KEYS("LENGTH",       "drag branch/ruler", "use discrete lengths", KEYINFO_ABORT_AND_RESET); break;
51        case AWT_MODE_MULTIFURC: text = MODE_TEXT_2BUTTONS_KEYS("MULTIFURC",    "drag branch",       "use discrete lengths", KEYINFO_ABORT_AND_RESET); break;
52        case AWT_MODE_LINE:      text = MODE_TEXT_2BUTTONS_KEYS("LINE",         "drag branch/ruler", "whole subtree",        KEYINFO_ABORT_AND_RESET); break;
53        case AWT_MODE_MOVE:      text = MODE_TEXT_2BUTTONS_KEYS("MOVE",         "drag branch/ruler", "move groupinfo only",  KEYINFO_ABORT);           break;
54        case AWT_MODE_LZOOM:     text = MODE_TEXT_2BUTTONS_KEYS("LOGICAL ZOOM", "show only subtree", "go up one step",       KEYINFO_RESET);           break;
55
56        default: text = no_mode_text_defined(); break;
57    }
58
59    td_assert(strlen(text) < AWAR_FOOTER_MAX_LEN); // text too long!
60
61    ntw->awr->awar(AWAR_FOOTER)->write_string(text);
62    ntw->set_mode(mode);
63}
64
65// ---------------------------------------
66//      Basic mark/unmark callbacks :
67
68static void count_mark_all_cb(UNFIXED, AWT_canvas *ntw) {
69    GB_push_transaction(ntw->gb_main);
70
71    GBDATA *gb_species_data = GBT_get_species_data(ntw->gb_main);
72    long    count           = GB_number_of_marked_subentries(gb_species_data);
73
74    GB_pop_transaction(ntw->gb_main);
75
76    char buf[256];
77    switch (count) {
78        case 0: strcpy(buf, "There are NO marked species"); break;
79        case 1: strcpy(buf, "There is 1 marked species"); break;
80        default: sprintf(buf, "There are %li marked species", count); break;
81    }
82    strcat(buf, ". (The number of species is displayed in the top area as well)");
83    aw_message(buf);
84}
85
86static int species_has_alignment(GBDATA *gb_species, void *cd_use) {
87    return GBT_find_sequence(gb_species, (const char*)cd_use) != 0;
88}
89
90static int sequence_is_partial(GBDATA *gb_species, void *cd_partial) {
91    long wanted  = (long)cd_partial;
92    td_assert(wanted == 0 || wanted == 1);
93    int partial = GBT_is_partial(gb_species, 0, false);
94
95    return partial == wanted;
96}
97
98#define MARK_MODE_LOWER_BITS (1|2)
99#define MARK_MODE_UPPER_BITS (4|8|16)
100
101void NT_mark_all_cb(UNFIXED, AWT_canvas *ntw, int mark_mode) {
102    // Bits 0 and 1 of mark_mode:
103    //
104    // mark_mode&3  == 0 -> unmark
105    // mark_mode&3  == 1 -> mark
106    // mark_mode&3  == 2 -> toggle mark
107    //
108    // Bits 2 .. 4 of mark_mode:
109    //
110    // mark_mode&12 == 4 -> affect only full sequences
111    // mark_mode&12 == 8 -> affect only partial sequences
112    // mark_mode&12 == 16 -> affect only species with data in current alignment
113    // else -> affect all sequences
114
115    GB_transaction ta(ntw->gb_main);
116
117    switch (mark_mode&MARK_MODE_UPPER_BITS) {
118        case 0:                 // all sequences
119            GBT_mark_all(ntw->gb_main, mark_mode&MARK_MODE_LOWER_BITS);
120            break;
121        case 4:                 // full sequences only
122            GBT_mark_all_that(ntw->gb_main, mark_mode&MARK_MODE_LOWER_BITS, sequence_is_partial, (void*)0);
123            break;
124        case 8:                 // partial sequences only
125            GBT_mark_all_that(ntw->gb_main, mark_mode&MARK_MODE_LOWER_BITS, sequence_is_partial, (void*)1);
126            break;
127        case 16: {               // species with data in alignment only
128            char *ali = GBT_get_default_alignment(ntw->gb_main);
129            if (ali) GBT_mark_all_that(ntw->gb_main, mark_mode&MARK_MODE_LOWER_BITS, species_has_alignment, (void*)ali);
130            free(ali);
131            break;
132        }
133        default:
134            td_assert(0); // illegal mode
135            break;
136    }
137
138    ntw->refresh();
139}
140
141static void mark_tree_cb(UNFIXED, AWT_canvas *ntw, int mark_mode) {
142    AWT_graphic_tree *gtree = AWT_TREE(ntw);
143    GB_transaction    ta(ntw->gb_main);
144
145    gtree->check_update(ntw->gb_main);
146    AP_tree *tree_root = gtree->get_root_node();
147
148    switch (mark_mode&MARK_MODE_UPPER_BITS) {
149        case 0:                 // all sequences
150            gtree->mark_species_in_tree(tree_root, mark_mode&MARK_MODE_LOWER_BITS);
151            break;
152        case 4:                 // full sequences only
153            gtree->mark_species_in_tree_that(tree_root, mark_mode&MARK_MODE_LOWER_BITS, sequence_is_partial, (void*)0);
154            break;
155        case 8:                 // partial sequences only
156            gtree->mark_species_in_tree_that(tree_root, mark_mode&MARK_MODE_LOWER_BITS, sequence_is_partial, (void*)1);
157            break;
158        case 16: {               // species with data in alignment only
159            char *ali = GBT_get_default_alignment(ntw->gb_main);
160            if (ali) gtree->mark_species_in_tree_that(tree_root, mark_mode&MARK_MODE_LOWER_BITS, species_has_alignment, (void*)ali);
161            free(ali);
162            break;
163        }
164        default:
165            td_assert(0); // illegal mode
166            break;
167    }
168    ntw->refresh();
169}
170
171struct mark_nontree_cb_data {
172    int      mark_mode_upper_bits;
173    char    *ali;               // current alignment (only if mark_mode_upper_bits == 16)
174    GB_HASH *hash;
175};
176
177static int are_not_in_tree(GBDATA *gb_species, void *cb_data) {
178    struct mark_nontree_cb_data *data    = (mark_nontree_cb_data*)cb_data;
179    const char                  *name    = GBT_read_name(gb_species);
180    bool                         mark_me = false;
181
182    if (GBS_read_hash(data->hash, name) == (long)gb_species) { // species is not in tree!
183        switch (data->mark_mode_upper_bits) {
184            case 0:             // all sequences
185                mark_me = true;
186                break;
187            case 4:             // full sequences only
188                mark_me = sequence_is_partial(gb_species, (void*)0);
189                break;
190            case 8:             // partial sequences only
191                mark_me = sequence_is_partial(gb_species, (void*)1);
192                break;
193            case 16:            // species with data in alignment only
194                mark_me = species_has_alignment(gb_species, data->ali);
195                break;
196            default:
197                td_assert(0); // illegal mode
198                break;
199        }
200    }
201
202    return mark_me;
203}
204
205static void mark_nontree_cb(UNFIXED, AWT_canvas *ntw, int mark_mode) {
206    AWT_graphic_tree            *gtree = AWT_TREE(ntw);
207    GB_transaction               ta(ntw->gb_main);
208    struct mark_nontree_cb_data  cd;
209
210    if ((mark_mode&MARK_MODE_LOWER_BITS) == 0) {   // unmark is much faster
211        cd.hash = GBT_create_marked_species_hash(ntw->gb_main); // because it only hashes marked species
212    }
213    else {
214        cd.hash = GBT_create_species_hash(ntw->gb_main); // for mark we have to hash ALL species
215    }
216
217    NT_remove_species_in_tree_from_hash(gtree->get_root_node(), cd.hash);
218
219    cd.mark_mode_upper_bits = mark_mode&MARK_MODE_UPPER_BITS;
220    cd.ali                  = cd.mark_mode_upper_bits == 16 ? GBT_get_default_alignment(ntw->gb_main) : 0;
221
222    GBT_mark_all_that(ntw->gb_main, mark_mode&MARK_MODE_LOWER_BITS, are_not_in_tree, (void*)&cd);
223
224    free(cd.ali);
225
226    ntw->refresh();
227}
228
229static char *create_mark_menu_entry(const char *attrib, const char *entry_template) {
230    char *entry = 0;
231    if (attrib) {
232        bool append = attrib[0] == '-'; // if attrib starts with '-' then append (otherwise prepend)
233        if (append) ++attrib; // skip '-'
234
235        if (append) {
236            char *spaced_attrib = GBS_global_string_copy(" %s", attrib);
237            entry               = GBS_global_string_copy(entry_template, "", spaced_attrib);
238            free(spaced_attrib);
239        }
240        else {
241            char *spaced_attrib = GBS_global_string_copy("%s ", attrib);
242            entry               = GBS_global_string_copy(entry_template, spaced_attrib, "");
243
244            if (islower(entry[0])) entry[0] = toupper(entry[0]); // Caps prepended lowercase 'attrib'
245
246            free(spaced_attrib);
247        }
248    }
249    else {
250        entry = GBS_global_string_copy(entry_template, "", "");
251    }
252    return entry;
253}
254static char *create_mark_menu_id(const char *attrib, const char *id_suffix) {
255    char *id = 0;
256    if (attrib) {
257        id = GBS_global_string_copy("%s_%s", attrib[0] == '-' ? attrib+1 : attrib, id_suffix);
258    }
259    else {
260        id = strdup(id_suffix);
261    }
262    return id;
263}
264
265static void insert_mark_topic(AW_window_menu_modes *awm, AW_active mask, const char *attrib, const char *id_suffix, const char *entry_template,
266                              const char *hotkey, const char *helpfile, const WindowCallback& wcb)
267{
268    char *entry = create_mark_menu_entry(attrib, entry_template);
269    char *id    = create_mark_menu_id(attrib, id_suffix);
270
271    awm->insert_menu_topic(id, entry, hotkey, helpfile, mask, wcb);
272
273    free(id);
274    free(entry);
275}
276
277static void insert_mark_topics(AW_window_menu_modes *awm, AW_active mask, AWT_canvas *ntw, int affect, const char *attrib) {
278    td_assert(affect == (affect&MARK_MODE_UPPER_BITS)); // only bits 2 .. 4 are allowed
279
280    insert_mark_topic(awm, mask, attrib, "mark_all",    "Mark all %sSpecies%s",            "M", "sp_mark.hlp", makeWindowCallback(NT_mark_all_cb, ntw, 1+affect));
281    insert_mark_topic(awm, mask, attrib, "unmark_all",  "Unmark all %sSpecies%s",          "U", "sp_mark.hlp", makeWindowCallback(NT_mark_all_cb, ntw, 0+affect));
282    insert_mark_topic(awm, mask, attrib, "swap_marked", "Invert marks of all %sSpecies%s", "I", "sp_mark.hlp", makeWindowCallback(NT_mark_all_cb, ntw, 2+affect));
283    awm->sep______________();
284
285    char *label = create_mark_menu_entry(attrib, "%sSpecies%s in Tree");
286
287    awm->insert_sub_menu(label, "T");
288    insert_mark_topic(awm, mask, attrib, "mark_tree",        "Mark %sSpecies%s in Tree",            "M", "sp_mark.hlp", makeWindowCallback(mark_tree_cb, ntw, 1+affect));
289    insert_mark_topic(awm, mask, attrib, "unmark_tree",      "Unmark %sSpecies%s in Tree",          "U", "sp_mark.hlp", makeWindowCallback(mark_tree_cb, ntw, 0+affect));
290    insert_mark_topic(awm, mask, attrib, "swap_marked_tree", "Invert marks of %sSpecies%s in Tree", "I", "sp_mark.hlp", makeWindowCallback(mark_tree_cb, ntw, 2+affect));
291    awm->close_sub_menu();
292
293    freeset(label, create_mark_menu_entry(attrib, "%sSpecies%s NOT in Tree"));
294
295    awm->insert_sub_menu(label, "N");
296    insert_mark_topic(awm, mask, attrib, "mark_nontree",        "Mark %sSpecies%s NOT in Tree",            "M", "sp_mark.hlp", makeWindowCallback(mark_nontree_cb, ntw, 1+affect));
297    insert_mark_topic(awm, mask, attrib, "unmark_nontree",      "Unmark %sSpecies%s NOT in Tree",          "U", "sp_mark.hlp", makeWindowCallback(mark_nontree_cb, ntw, 0+affect));
298    insert_mark_topic(awm, mask, attrib, "swap_marked_nontree", "Invert marks of %sSpecies%s NOT in Tree", "I", "sp_mark.hlp", makeWindowCallback(mark_nontree_cb, ntw, 2+affect));
299    awm->close_sub_menu();
300
301    free(label);
302}
303
304void NT_insert_mark_submenus(AW_window_menu_modes *awm, AWT_canvas *ntw, int insert_as_submenu) {
305    if (insert_as_submenu) {
306        awm->insert_sub_menu("Mark species", "M");
307    }
308
309    {
310        awm->insert_menu_topic("count_marked", "Count Marked Species", "C", "sp_count_mrk.hlp", AWM_ALL, makeWindowCallback(count_mark_all_cb, ntw));
311        awm->sep______________();
312        insert_mark_topics(awm, AWM_ALL, ntw, 0, 0);
313        awm->sep______________();
314
315        awm->insert_sub_menu("Complete sequences", "o");
316        insert_mark_topics(awm, AWM_EXP, ntw, 4, "complete");
317        awm->close_sub_menu();
318
319        awm->insert_sub_menu("Partial sequences", "P");
320        insert_mark_topics(awm, AWM_EXP, ntw, 8, "partial");
321        awm->close_sub_menu();
322
323        awm->insert_sub_menu("Current Alignment", "A");
324        insert_mark_topics(awm, AWM_EXP, ntw, 16, "-with data");
325        awm->close_sub_menu();
326    }
327
328    if (insert_as_submenu) {
329        awm->close_sub_menu();
330    }
331}
332
333static void save_changed_tree(AWT_canvas *ntw) {
334    GB_ERROR error = AWT_TREE(ntw)->save(ntw->gb_main, NULL);
335    if (error) aw_message(error);
336    ntw->zoom_reset_and_refresh();
337}
338
339// ----------------------------------------
340//      Automated collapse/expand tree
341
342static void group_and_save_tree(AWT_canvas *ntw, CollapseMode mode, int color_group) {
343    GB_transaction ta(ntw->gb_main);
344
345    AWT_TREE(ntw)->check_update(ntw->gb_main);
346    AWT_TREE(ntw)->group_tree(AWT_TREE(ntw)->get_root_node(), mode, color_group);
347    save_changed_tree(ntw);
348}
349
350static void collapse_all_cb     (UNFIXED, AWT_canvas *ntw) { group_and_save_tree(ntw, COLLAPSE_ALL,      0); }
351static void collapse_terminal_cb(UNFIXED, AWT_canvas *ntw) { group_and_save_tree(ntw, COLLAPSE_TERMINAL, 0); }
352static void expand_all_cb       (UNFIXED, AWT_canvas *ntw) { group_and_save_tree(ntw, EXPAND_ALL,        0); }
353void NT_expand_marked_cb        (UNFIXED, AWT_canvas *ntw) { group_and_save_tree(ntw, EXPAND_MARKED,     0); }
354static void expand_zombies_cb   (UNFIXED, AWT_canvas *ntw) { group_and_save_tree(ntw, EXPAND_ZOMBIES,    0); }
355
356static void expand_color_cb(UNFIXED, AWT_canvas *ntw, int colornum) { group_and_save_tree(ntw, EXPAND_COLOR, colornum); }
357
358static void insert_color_collapse_submenu(AW_window_menu_modes *awm, AWT_canvas *ntree_canvas) {
359#define MAXLABEL 30
360#define MAXENTRY (AW_COLOR_GROUP_NAME_LEN+10)
361
362    td_assert(ntree_canvas != 0);
363
364    awm->insert_sub_menu("Expand color ...", "o");
365
366    char        label_buf[MAXLABEL+1];
367    char        entry_buf[MAXENTRY+1];
368    char hotkey[]       = "x";
369    const char *hotkeys = "AN1234567890BC"+1;
370
371    for (int i = -1; i <= AW_COLOR_GROUPS; ++i) {
372        sprintf(label_buf, "tree_group_not_color_%i", i);
373
374        hotkey[0]                       = hotkeys[i];
375        if (hotkey[0] == ' ') hotkey[0] = 0;
376
377        if (i) {
378            if (i<0) {
379                strcpy(entry_buf, "Any color group");
380            }
381            else {
382                char *color_group_name = AW_get_color_group_name(awm->get_root(), i);
383                sprintf(entry_buf, "%s group '%s'", hotkey, color_group_name);
384                free(color_group_name);
385            }
386        }
387        else {
388            strcpy(entry_buf, "No color group");
389        }
390
391        awm->insert_menu_topic(awm->local_id(label_buf), entry_buf, hotkey, "tree_group.hlp", AWM_ALL, makeWindowCallback(expand_color_cb, ntree_canvas, i));
392    }
393
394    awm->close_sub_menu();
395
396#undef MAXLABEL
397#undef MAXENTRY
398}
399
400void NT_insert_collapse_submenu(AW_window_menu_modes *awm, AWT_canvas *ntw) {
401    awm->insert_sub_menu("Collapse/expand groups",         "d");
402    {
403        const char *grouphelp = "tree_group.hlp";
404        awm->insert_menu_topic(awm->local_id("tree_group_all"),         "Collapse all",      "C", grouphelp, AWM_ALL, makeWindowCallback(collapse_all_cb,      ntw));
405        awm->insert_menu_topic(awm->local_id("tree_group_term_groups"), "Collapse terminal", "t", grouphelp, AWM_ALL, makeWindowCallback(collapse_terminal_cb, ntw));
406        awm->sep______________();
407        awm->insert_menu_topic(awm->local_id("tree_ungroup_all"),       "Expand all",        "E", grouphelp, AWM_ALL, makeWindowCallback(expand_all_cb,        ntw));
408        awm->insert_menu_topic(awm->local_id("tree_group_not_marked"),  "Expand marked",     "m", grouphelp, AWM_ALL, makeWindowCallback(NT_expand_marked_cb,  ntw));
409        awm->insert_menu_topic(awm->local_id("tree_ungroup_zombies"),   "Expand zombies",    "z", grouphelp, AWM_ALL, makeWindowCallback(expand_zombies_cb,    ntw));
410        awm->sep______________();
411        insert_color_collapse_submenu(awm, ntw);
412    }
413    awm->close_sub_menu();
414}
415
416// ------------------------
417//      tree sorting :
418
419GB_ERROR NT_with_displayed_tree_do(AWT_canvas *ntw, bool (*displayed_tree_cb)(TreeNode *tree, GB_ERROR& error)) {
420    // 'displayed_tree_cb' has to return true if tree was changed and needs to be saved
421
422    GB_transaction ta(ntw->gb_main);
423    AWT_TREE(ntw)->check_update(ntw->gb_main);
424
425    GB_ERROR error = NULL;
426    if (displayed_tree_cb(AWT_TREE(ntw)->get_root_node(), error)) {
427        save_changed_tree(ntw);
428    }
429    return error;
430}
431
432void NT_resort_tree_cb(UNFIXED, TREE_canvas *ntw, TreeOrder order) {
433    GB_transaction ta(ntw->gb_main);
434    AWT_TREE(ntw)->check_update(ntw->gb_main);
435    AWT_TREE(ntw)->reorder_tree(order);
436    save_changed_tree(ntw);
437}
438
439void NT_reset_lzoom_cb(UNFIXED, TREE_canvas *ntw) {
440    GB_transaction    ta(ntw->gb_main);
441    AWT_graphic_tree *agt = AWT_TREE(ntw);
442    agt->check_update(ntw->gb_main);
443    agt->set_logical_root_to(agt->get_root_node());
444    ntw->zoom_reset_and_refresh();
445}
446
447void NT_reset_pzoom_cb(UNFIXED, TREE_canvas *ntw) {
448    GB_transaction ta(ntw->gb_main);
449    AWT_TREE(ntw)->check_update(ntw->gb_main);
450    ntw->zoom_reset_and_refresh();
451}
452
453void NT_set_tree_style(UNFIXED, TREE_canvas *ntw, AP_tree_display_style style) {
454    GB_transaction ta(ntw->gb_main);
455    AWT_TREE(ntw)->check_update(ntw->gb_main);
456    AWT_TREE(ntw)->set_tree_style(style, ntw);
457    ntw->zoom_reset_and_refresh();
458    TREE_auto_jump_cb(NULL, ntw, true);
459}
460
461void NT_remove_leafs(UNFIXED, TREE_canvas *ntw, AWT_RemoveType mode) {
462    GB_transaction ta(ntw->gb_main);
463
464    AWT_TREE(ntw)->check_update(ntw->gb_main);
465    AP_tree *root_node = AWT_TREE(ntw)->get_root_node();
466    if (root_node) {
467        AWT_TREE(ntw)->get_tree_root()->remove_leafs(mode);
468
469        root_node = AWT_TREE(ntw)->get_root_node(); // root might have changed -> get again
470        if (root_node) root_node->compute_tree();
471        save_changed_tree(ntw);
472    }
473    else {
474        aw_message("Got no tree");
475    }
476}
477
478void NT_remove_bootstrap(UNFIXED, TREE_canvas *ntw) { // delete all bootstrap values
479    GB_transaction ta(ntw->gb_main);
480
481    AWT_TREE(ntw)->check_update(ntw->gb_main);
482
483    AP_tree *root_node = AWT_TREE(ntw)->get_root_node();
484    if (root_node) {
485        root_node->remove_bootstrap();
486        root_node->compute_tree();
487        save_changed_tree(ntw);
488    }
489    else {
490        aw_message("Got no tree");
491    }
492}
493void NT_toggle_bootstrap100(UNFIXED, TREE_canvas *ntw) { // toggle 100% bootstrap values
494    GB_transaction ta(ntw->gb_main);
495
496    AWT_TREE(ntw)->check_update(ntw->gb_main);
497
498    AP_tree *root_node = AWT_TREE(ntw)->get_root_node();
499    if (root_node) {
500        root_node->toggle_bootstrap100();
501        root_node->compute_tree();
502        save_changed_tree(ntw);
503    }
504    else {
505        aw_message("Got no tree");
506    }
507}
508
509void NT_reset_branchlengths(UNFIXED, TREE_canvas *ntw) { // set all branchlengths to tree_defaults::LENGTH
510    GB_transaction ta(ntw->gb_main);
511    AWT_TREE(ntw)->check_update(ntw->gb_main);
512
513    AP_tree *root_node = AWT_TREE(ntw)->get_root_node();
514    if (root_node) {
515        root_node->reset_branchlengths();
516        root_node->compute_tree();
517        save_changed_tree(ntw);
518    }
519    else {
520        aw_message("Got no tree");
521    }
522}
523
524void NT_multifurcate_tree(AWT_canvas *ntw, const TreeNode::multifurc_limits& below) {
525    GB_transaction ta(ntw->gb_main);
526    AWT_TREE(ntw)->check_update(ntw->gb_main);
527
528    TreeNode *tree = AWT_TREE(ntw)->get_root_node();
529    if (tree) {
530        tree->multifurcate_whole_tree(below);
531        save_changed_tree(ntw);
532    }
533    else {
534        aw_message("Got no tree");
535    }
536}
537
538void NT_move_boot_branch(UNFIXED, TREE_canvas *ntw, int direction) { // copy branchlengths to bootstraps (or vice versa)
539    GB_transaction ta(ntw->gb_main);
540
541    AWT_TREE(ntw)->check_update(ntw->gb_main);
542
543    AP_tree *root_node = AWT_TREE(ntw)->get_root_node();
544    if (root_node) {
545        if (direction == 0) root_node->bootstrap2branchlen();
546        else                root_node->branchlen2bootstrap();
547
548        root_node->compute_tree();
549        save_changed_tree(ntw);
550
551        char *adviceText = GBS_global_string_copy("Please note, that you just overwrote your existing %s.",
552                                                  direction ? "bootstrap values" : "branchlengths");
553        AW_advice(adviceText, AW_ADVICE_TOGGLE_AND_HELP, 0, "tbl_boot2len.hlp");
554        free(adviceText);
555    }
556    else {
557        aw_message("Got no tree");
558    }
559}
560
561void NT_scale_tree(UNFIXED, TREE_canvas *ntw) { // scale branchlengths
562    char *answer = aw_input("Enter scale factor", "Scale branchlengths by factor:", "100");
563    if (answer) {
564        double factor = atof(answer);
565        GB_transaction ta(ntw->gb_main);
566
567        AP_tree *root_node = AWT_TREE(ntw)->get_root_node();
568        if (root_node) {
569            root_node->scale_branchlengths(factor);
570            root_node->compute_tree();
571            save_changed_tree(ntw);
572        }
573        else {
574            aw_message("Got no tree");
575        }
576        free(answer);
577    }
578}
579
580inline AP_tree *common_ancestor(AP_tree *t1, AP_tree *t2) {
581    return DOWNCAST(AP_tree*, t1->ancestor_common_with(t2));
582}
583
584static bool make_node_visible(AWT_canvas *ntw, AP_tree *node) {
585    bool changed = false;
586    while (node) {
587        if (node->gr.grouped) {
588            node->gr.grouped = 0;
589            changed          = true;
590        }
591        node = node->get_father();
592    }
593    if (changed) {
594        AWT_TREE(ntw)->get_root_node()->compute_tree();
595        GB_ERROR error = AWT_TREE(ntw)->save(ntw->gb_main, NULL);
596        if (error) {
597            aw_message(error);
598            return false;
599        }
600        ntw->recalc_size(true);
601    }
602    return true;
603}
604
605void NT_jump_cb(UNFIXED, TREE_canvas *ntw, AP_tree_jump_type jumpType) {
606    if (jumpType == AP_DONT_JUMP) return;
607
608    AW_window        *aww   = ntw->aww;
609    AWT_graphic_tree *gtree = AWT_TREE(ntw);
610
611    GB_transaction ta(ntw->gb_main);
612    if (gtree) gtree->check_update(ntw->gb_main);
613
614    const char *name     = aww->get_root()->awar(AWAR_SPECIES_NAME)->read_char_pntr();
615    char       *msg      = NULL;
616    bool        verboose = jumpType & AP_JUMP_BE_VERBOOSE;
617
618    if (name[0]) {
619        AP_tree *found   = NULL;
620        bool     is_tree = is_tree_style(gtree->get_tree_style());
621
622        if (is_tree) {
623            if (gtree && gtree->get_logical_root()) {
624                found = gtree->get_logical_root()->findLeafNamed(name);
625                if (!found && gtree->is_logically_zoomed()) {
626                    found = gtree->get_root_node()->findLeafNamed(name);
627                    if (found) { // species is invisible because it is outside logically zoomed tree
628                        if (jumpType & AP_JUMP_UNFOLD_GROUPS) {
629                            gtree->set_logical_root_to(common_ancestor(found, gtree->get_logical_root()));
630                            ntw->recalc_size(true);
631                        }
632                        else {
633                            if (verboose) msg = GBS_global_string_copy("Species '%s' is outside logical zoomed subtree", name);
634
635                            found = NULL;
636                        }
637                    }
638                }
639
640                if (found) {
641                    if (jumpType&AP_JUMP_UNFOLD_GROUPS) {
642                        if (!make_node_visible(ntw, found)) found = NULL;
643                    }
644                    else if (found->is_inside_folded_group()) {
645                        found = NULL;
646                    }
647                }
648            }
649        }
650
651        if (found || !is_tree) {
652            bool is_IRS  = gtree->get_tree_style() == AP_TREE_IRS;
653            bool repeat  = is_IRS;
654            bool do_jump = true;
655
656            while (do_jump) {
657                do_jump = false;
658
659                AW_device_size *device = aww->get_size_device(AW_MIDDLE_AREA);
660                device->set_filter(AW_SIZE|AW_SIZE_UNSCALED);
661                device->reset();
662                ntw->init_device(device);
663                ntw->gfx->show(device);
664
665                const AW_screen_area& screen = device->get_area_size();
666
667                const Position& cursor = gtree->get_cursor();
668                if (are_distinct(Origin, cursor)) {
669                    Position S = device->transform(cursor);
670
671                    int scroll_x = 0;
672                    int scroll_y = 0;
673
674                    bool do_vcenter = jumpType & AP_JUMP_FORCE_VCENTER;
675                    bool do_hcenter = jumpType & AP_JUMP_FORCE_HCENTER;
676
677                    if (!do_vcenter) {
678                        if (is_IRS) {
679                            // attempt to center IRS tree vertically often fails (too complicated to predict)
680                            // => force into center-half of screen to reduce error rate
681                            int border = screen.b/10;
682                            do_vcenter = S.ypos()<border || S.ypos()>(screen.b-border);
683                        }
684                        else {
685                            do_vcenter = S.ypos()<0.0 || S.ypos()>screen.b; // center if outside viewport
686                        }
687                    }
688
689                    if (do_vcenter) {
690                        scroll_y = (int)(S.ypos() - screen.b*(is_IRS ? .6 : .5)); // position a bit below vertical center for IRS tree
691
692                        if (!scroll_y && (jumpType & AP_JUMP_ALLOW_HCENTER)) { // allow horizontal centering if vertical has no effect
693                            do_hcenter = true;
694                        }
695                    }
696
697                    if (do_hcenter) {
698                        scroll_x = (int) (S.xpos() - screen.r * (is_tree ? .5 : .02));
699                    }
700                    else { // keep visible
701                        if (S.xpos()<0.0) {
702                            double relPos = 0;
703                            switch (gtree->get_tree_style()) {
704                                case AP_TREE_NORMAL:
705                                case AP_TREE_IRS:      relPos = .1; break;
706                                case AP_TREE_RADIAL:   relPos = .5; break;
707                                case AP_LIST_NDS:
708                                case AP_LIST_SIMPLE:   relPos = .02; break;
709                            }
710                            scroll_x = (int)(S.xpos() - screen.r * relPos);
711                        }
712                        else if (S.xpos()>screen.r) {
713                            scroll_x = (int)(S.xpos() - screen.r * .5);
714                        }
715                    }
716
717                    if (scroll_x || scroll_y) ntw->scroll(scroll_x, scroll_y);
718                    if (repeat) {
719                        // reposition jump in IRS tree (reduces jump failure rate)
720                        repeat  = false;
721                        do_jump = true;
722                    }
723                }
724                else {
725                    td_assert(!is_tree);
726                    if (verboose) msg = GBS_global_string_copy("Species '%s' is no member of this list", name);
727                }
728            }
729        }
730
731        if (!found && is_tree && verboose && !msg) {
732            msg = GBS_global_string_copy("Species '%s' is no member of this %s", name, gtree->get_tree_style() == AP_LIST_NDS ? "list" : "tree");
733        }
734    }
735    else if (verboose) {
736        msg = strdup("No species selected");
737    }
738
739    ntw->refresh(); // always do refresh to show change of selected species
740
741    if (msg) {
742        td_assert(verboose);
743        aw_message(msg);
744        free(msg);
745    }
746}
747
748void TREE_auto_jump_cb(UNFIXED, TREE_canvas *ntw, bool tree_change) {
749    /*! jump to species when tree/treemode/species changes
750     * @param tree_change == true -> tree or treemode has changed; false -> species has changed
751     */
752
753    const char        *awar_name = tree_change ? AWAR_DTREE_AUTO_JUMP_TREE : AWAR_DTREE_AUTO_JUMP;
754    AP_tree_jump_type  jump_type = AP_tree_jump_type(ntw->aww->get_root()->awar(awar_name)->read_int());
755
756
757    if (jump_type == AP_DONT_JUMP) {
758        ntw->refresh();
759    }
760    else {
761        NT_jump_cb(NULL, ntw, jump_type);
762    }
763}
764
765inline const char *plural(int val) {
766    return "s"+(val == 1);
767}
768
769void NT_reload_tree_event(AW_root *, TREE_canvas *ntw, bool unzoom_and_expose) {
770    GB_push_transaction(ntw->gb_main);
771    char     *tree_name = ntw->get_awar_tree()->read_string();
772    GB_ERROR  error     = ntw->gfx->load(ntw->gb_main, tree_name);
773    if (error) {
774        aw_message(error);
775    }
776    else {
777        int zombies, duplicates;
778        DOWNCAST(AWT_graphic_tree*, ntw->gfx)->get_zombies_and_duplicates(zombies, duplicates);
779
780        if (zombies || duplicates) {
781            const char *msg = 0;
782            if (duplicates) {
783                if (zombies) msg = GBS_global_string("%i zombie%s and %i duplicate%s", zombies, plural(zombies), duplicates, plural(duplicates));
784                else msg         = GBS_global_string("%i duplicate%s", duplicates, plural(duplicates));
785            }
786            else {
787                td_assert(zombies);
788                msg = GBS_global_string("%i zombie%s", zombies, plural(zombies));
789            }
790            aw_message(GBS_global_string("%s in '%s'", msg, tree_name));
791        }
792    }
793    free(tree_name);
794    if (unzoom_and_expose) ntw->zoom_reset_and_refresh();
795    GB_pop_transaction(ntw->gb_main);
796}
797
798void TREE_recompute_cb(UNFIXED, AWT_canvas *ntw) {
799    AWT_graphic_tree *gt = DOWNCAST(AWT_graphic_tree*, ntw->gfx);
800
801    AP_tree *troot = gt->get_root_node();
802    if (troot) troot->compute_tree();
803
804    ntw->recalc_size_and_refresh();
805}
806
807void TREE_GC_changed_cb(GcChange whatChanged, AWT_canvas *ntw) {
808    if (whatChanged == GC_COLOR_GROUP_USE_CHANGED) {
809        TREE_recompute_cb(NULL, ntw);
810    }
811    else {
812        AWT_GC_changed_cb(whatChanged, ntw);
813    }
814}
815
816void NT_reinit_treetype(UNFIXED, TREE_canvas *ntw) {
817    AWT_graphic_tree *gt = DOWNCAST(AWT_graphic_tree*, ntw->gfx);
818    gt->set_tree_style(gt->get_tree_style(), ntw);
819    AWT_resize_cb(NULL, ntw);
820    TREE_auto_jump_cb(NULL, ntw, true);
821}
822
823void NT_remove_species_in_tree_from_hash(AP_tree *tree, GB_HASH *hash) {
824    if (!tree) return;
825    if (tree->is_leaf && tree->name) {
826        GBS_write_hash(hash, tree->name, 0); // delete species in hash table
827    }
828    else {
829        NT_remove_species_in_tree_from_hash(tree->get_leftson(), hash);
830        NT_remove_species_in_tree_from_hash(tree->get_rightson(), hash);
831    }
832}
833
Note: See TracBrowser for help on using the repository browser.