source: trunk/SL/TREEDISP/TreeCallbacks.cxx

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