source: branches/profile/SL/TREEDISP/TreeCallbacks.cxx

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