source: branches/profile/DIST/DI_clusters.cxx

Last change on this file was 12877, checked in by westram, 5 years ago
File size: 38.6 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : DI_clusters.cxx                                   //
4//   Purpose   : Detect clusters of homologous sequences in tree   //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in October 2009   //
7//   Institute of Microbiology (Technical University Munich)       //
8//   http://www.arb-home.de/                                       //
9//                                                                 //
10// =============================================================== //
11
12#include "di_clusters.hxx"
13#include "di_clustertree.hxx"
14#include "di_foundclusters.hxx"
15#include "di_awars.hxx"
16
17#include <AP_filter.hxx>
18#include <AP_seq_protein.hxx>
19#include <AP_seq_dna.hxx>
20
21#include <awt_sel_boxes.hxx>
22
23#include <aw_awars.hxx>
24#include <aw_msg.hxx>
25#include <arb_progress.h>
26#include <aw_root.hxx>
27
28#include <list>
29
30using namespace std;
31
32#define di_assert(cond) arb_assert(cond)
33
34// --------------
35//      awars
36
37#define AWAR_CLUSTER_PREFIX      AWAR_DIST_PREFIX "cluster/"
38#define AWAR_CLUSTER_PREFIX_TEMP "/tmp/" AWAR_DIST_PREFIX
39
40#define AWAR_CLUSTER_MAXDIST   AWAR_CLUSTER_PREFIX "maxdist"
41#define AWAR_CLUSTER_MINSIZE   AWAR_CLUSTER_PREFIX "minsize"
42#define AWAR_CLUSTER_AUTOMARK  AWAR_CLUSTER_PREFIX "automark"
43#define AWAR_CLUSTER_MARKREP   AWAR_CLUSTER_PREFIX "markrep"
44#define AWAR_CLUSTER_SELECTREP AWAR_CLUSTER_PREFIX "selrep"
45#define AWAR_CLUSTER_ORDER     AWAR_CLUSTER_PREFIX "order"
46
47#define AWAR_CLUSTER_GRP_PREFIX     AWAR_CLUSTER_PREFIX "group/"
48#define AWAR_CLUSTER_GRP_PREFIX_TMP "/tmp/" AWAR_CLUSTER_GRP_PREFIX
49
50#define AWAR_CLUSTER_GROUP_WHAT         AWAR_CLUSTER_GRP_PREFIX "all"
51#define AWAR_CLUSTER_GROUP_EXISTING     AWAR_CLUSTER_GRP_PREFIX "existing"
52#define AWAR_CLUSTER_GROUP_NOTFOUND     AWAR_CLUSTER_GRP_PREFIX "notfound"
53#define AWAR_CLUSTER_GROUP_IDENTITY     AWAR_CLUSTER_GRP_PREFIX "identity"
54#define AWAR_CLUSTER_GROUP_PREFIX       AWAR_CLUSTER_GRP_PREFIX "prefix"
55#define AWAR_CLUSTER_GROUP_PREFIX_MATCH AWAR_CLUSTER_GRP_PREFIX "prefix_match"
56#define AWAR_CLUSTER_GROUP_SUFFIX       AWAR_CLUSTER_GRP_PREFIX "suffix"
57#define AWAR_CLUSTER_GROUP_EXAMPLE      AWAR_CLUSTER_GRP_PREFIX_TMP "example"
58
59#define AWAR_CLUSTER_SELECTED      AWAR_CLUSTER_PREFIX_TEMP "selected" // ID of currently selected cluster (or zero)
60#define AWAR_CLUSTER_RESTORE_LABEL AWAR_CLUSTER_PREFIX_TEMP "rlabel" // label of restore button
61
62enum Group_What {
63    GROUP_SELECTED,
64    GROUP_LISTED,
65};
66
67enum Group_Action {
68    GROUP_CREATE,
69    GROUP_DELETE,
70};
71
72enum Group_NotFound {
73    NOTFOUND_ABORT,
74    NOTFOUND_WARN,
75    NOTFOUND_IGNORE,
76};
77
78enum Group_Existing {
79    EXISTING_GROUP_ABORT,
80    EXISTING_GROUP_SKIP,
81    EXISTING_GROUP_OVERWRITE,
82    EXISTING_GROUP_APPEND_ORG,
83};
84
85// ----------------------------------------
86
87enum AffectedClusters { ALL_CLUSTERS, SEL_CLUSTER };
88
89static ClustersData *global_data = 0;
90
91// ------------------------
92
93static void di_forget_global_data(AW_window *) {
94    di_assert(global_data);
95    global_data->free();
96    // do not delete 'global_data' itself, it will be reused when window is opened again
97}
98
99// ------------------------
100//      Update contents
101
102static void update_cluster_sellist() {
103    global_data->update_cluster_selection_list();
104    // @@@ update result info line
105}
106static void update_restore_label(AW_window *aww) {
107    AW_root    *aw_root = aww->get_root();
108    AW_awar    *awar    = aw_root->awar(AWAR_CLUSTER_RESTORE_LABEL);
109    unsigned    size    = global_data->count(STORED_CLUSTERS);
110    const char *label   = size ? GBS_global_string("Stored: %u", size) : "None stored";
111
112    awar->write_string(label);
113}
114static void update_all(AW_window *aww) {
115    update_cluster_sellist();
116    update_restore_label(aww);
117}
118
119static void save_results_recursive(ClusterTree *subtree) {
120    if (subtree->get_state() == CS_IS_CLUSTER) {
121        global_data->add(new Cluster(subtree), SHOWN_CLUSTERS);
122    }
123    if (!subtree->is_leaf) {
124        save_results_recursive(subtree->get_leftson());
125        save_results_recursive(subtree->get_rightson());
126    }
127}
128static void save_results(ClusterTreeRoot *tree) {
129    global_data->clear(SHOWN_CLUSTERS);
130    save_results_recursive(tree->get_root_node());
131    update_cluster_sellist();
132}
133
134static void calculate_clusters(AW_window *aww) {
135    GBDATA   *gb_main = global_data->get_gb_main();
136    GB_ERROR  error   = NULL;
137
138    arb_progress progress("Detecting clusters");
139
140    // calculate ClusterTree
141    ClusterTreeRoot *tree = NULL;
142    {
143        GB_transaction  ta(gb_main);
144        AW_root        *aw_root = aww->get_root();
145
146        {
147            char    *use     = aw_root->awar(AWAR_DIST_ALIGNMENT)->read_string();
148            AliView *aliview = global_data->weighted_filter.create_aliview(use, error);
149
150            if (aliview) {
151                AP_sequence *seq = GBT_is_alignment_protein(gb_main, use)
152                    ? (AP_sequence*)new AP_sequence_protein(aliview)
153                    : new AP_sequence_parsimony(aliview);
154
155                AP_FLOAT maxDistance    = aw_root->awar(AWAR_CLUSTER_MAXDIST)->read_float();
156                unsigned minClusterSize = aw_root->awar(AWAR_CLUSTER_MINSIZE)->read_int();
157
158                tree = new ClusterTreeRoot(aliview, seq, maxDistance/100, minClusterSize);
159
160                delete seq;
161            }
162            free(use);
163        }
164
165        if (!error) {
166            progress.subtitle("Loading tree");
167            {
168                char *tree_name = aw_root->awar(AWAR_DIST_TREE_CURR_NAME)->read_string();
169                error           = tree->loadFromDB(tree_name);
170                free(tree_name);
171            }
172
173            if (!error) error = tree->linkToDB(0, 0);
174        }
175    }
176
177    if (!error) {
178        error = tree->find_clusters();
179        if (!error) save_results(tree);
180    }
181
182    delete tree;
183
184    if (error) aw_message(error);
185}
186
187DECLARE_CBTYPE_FVV_AND_BUILDERS(ClusterCallback, void, ClusterPtr); // generates makeClusterCallback
188
189inline bool is_cluster(ID id) {
190    // 0 is 'no cluster' and -1 is used for list header
191    return id>0;
192}
193
194static int with_affected_clusters_do(AW_root *aw_root, AffectedClusters affected, bool warn_if_none_affected, ClusterCallback cb) {
195    // returns number of affected clusters
196    int affCount = 0;
197    if (affected == SEL_CLUSTER) {
198        AW_awar *awar  = aw_root->awar(AWAR_CLUSTER_SELECTED);
199        ID       selID = awar->read_int();
200
201        if (is_cluster(selID)) {
202            ClusterPtr selCluster = global_data->clusterWithID(selID);
203            cl_assert(!selCluster.isNull());
204            cb(selCluster);
205            affCount++;
206        }
207        else if (warn_if_none_affected) {
208            aw_message("No cluster is selected");
209        }
210    }
211    else {
212        unsigned shown = global_data->count(SHOWN_CLUSTERS);
213        if (shown) {
214            ClusterIDs     clusters(global_data->get_clusterIDs(SHOWN_CLUSTERS)); // intended copy!
215            ClusterIDsIter cli_end = clusters.end();
216            for (ClusterIDsIter cli = clusters.begin(); cli != cli_end; ++cli) {
217                ClusterPtr cluster = global_data->clusterWithID(*cli);
218                cb(cluster);
219                affCount++;
220            }
221        }
222        else if (warn_if_none_affected) {
223            aw_message("There are no clusters in the list");
224        }
225    }
226    return affCount;
227}
228
229// -------------
230//      mark
231
232void Cluster::mark_all_members(ClusterMarkMode mmode) const {
233    DBItemSetIter sp_end = members.end();
234    for (DBItemSetIter sp = members.begin(); sp != sp_end; ++sp) {
235        bool is_rep = *sp == representative;
236        bool mark;
237
238        switch (mmode) {
239            case CMM_ALL:         mark = true;    break;
240            case CMM_ONLY_REP:    mark = is_rep;  break;
241            case CMM_ALL_BUT_REP: mark = !is_rep; break;
242        }
243
244        if (mark) GB_write_flag(*sp, 1);
245    }
246}
247
248static void mark_cluster(ClusterPtr cluster, ClusterMarkMode markRep) {
249    cluster->mark_all_members(markRep);
250}
251static void select_representative(ClusterPtr cluster) {
252    GBDATA         *gb_species = cluster->get_representative();
253    GB_transaction  ta(gb_species);
254    const char     *name       = GBT_get_name(gb_species);
255    global_data->get_aw_root()->awar(AWAR_SPECIES_NAME)->write_string(name);
256}
257
258static void mark_clusters(AW_window *, AffectedClusters affected, bool warn_if_none_affected) {
259    AW_root *aw_root = global_data->get_aw_root();
260    GBDATA  *gb_main = global_data->get_gb_main();
261
262    GB_transaction  ta(gb_main);
263    ClusterMarkMode markRep = (ClusterMarkMode)aw_root->awar(AWAR_CLUSTER_MARKREP)->read_int();
264
265    GBT_mark_all(gb_main, 0); // unmark all
266    with_affected_clusters_do(aw_root, affected, warn_if_none_affected, makeClusterCallback(mark_cluster, markRep));
267    aw_root->awar(AWAR_TREE_REFRESH)->touch();
268}
269
270static void select_cluster_cb(AW_root *aw_root) {
271    GB_transaction ta(global_data->get_gb_main());
272
273    bool auto_mark = aw_root->awar(AWAR_CLUSTER_AUTOMARK)->read_int();
274    if (auto_mark) mark_clusters(NULL, SEL_CLUSTER, false);
275
276    bool selRep = aw_root->awar(AWAR_CLUSTER_SELECTREP)->read_int();
277    if (selRep) {
278        int selected = with_affected_clusters_do(aw_root, SEL_CLUSTER, false, makeClusterCallback(select_representative));
279        if (!selected) aw_root->awar(AWAR_SPECIES_NAME)->write_string(""); // deselect species
280    }
281}
282
283static void select_cluster(ID id) {
284    global_data->get_aw_root()->awar(AWAR_CLUSTER_SELECTED)->write_int(id);
285}
286
287
288// -------------------
289//      Sort order
290
291
292static void sort_order_changed_cb(AW_root *aw_root) {
293    ClusterOrder order = (ClusterOrder)aw_root->awar(AWAR_CLUSTER_ORDER)->read_int();
294    global_data->changeSortOrder(order);
295    update_cluster_sellist();
296}
297
298// --------------
299//      group
300
301class GroupTree;
302typedef map<string, GroupTree*> Species2Tip;
303
304class GroupTree : public ARB_countedTree {
305    unsigned leaf_count;   // total number of leafs in subtree
306    unsigned tagged_count; // tagged leafs
307
308    void update_tag_counters();
309public:
310
311    explicit GroupTree(ARB_seqtree_root *root)
312        : ARB_countedTree(root),
313          leaf_count(0),
314          tagged_count(0)
315    {}
316
317    // ARB_countedTree interface
318    virtual void init_tree() OVERRIDE { update_leaf_counters(); }
319    // ARB_countedTree interface end
320
321    DEFINE_TREE_RELATIVES_ACCESSORS(GroupTree);
322
323    void map_species2tip(Species2Tip& mapping);
324
325    unsigned get_leaf_count() const OVERRIDE { return leaf_count; }
326    unsigned update_leaf_counters();
327
328    void tag_leaf() {
329        di_assert(is_leaf);
330        tagged_count = 1;
331        get_father()->update_tag_counters();
332    }
333    unsigned get_tagged_count() const { return tagged_count; }
334    void clear_tags();
335
336    double tagged_rate() const { return double(get_tagged_count())/get_leaf_count(); }
337};
338
339struct GroupTreeNodeFactory : public RootedTreeNodeFactory {
340    virtual RootedTree *makeNode(TreeRoot *root) const {
341        return new GroupTree(DOWNCAST(ARB_seqtree_root*, root));
342    }
343};
344
345unsigned GroupTree::update_leaf_counters() {
346    if (is_leaf) leaf_count = 1;
347    else leaf_count = get_leftson()->update_leaf_counters() + get_rightson()->update_leaf_counters();
348    return leaf_count;
349}
350
351void GroupTree::clear_tags() {
352    if (!is_leaf && tagged_count) {
353        get_leftson()->clear_tags();
354        get_rightson()->clear_tags();
355    }
356    tagged_count = 0;
357}
358
359void GroupTree::map_species2tip(Species2Tip& mapping) {
360    if (is_leaf) {
361        if (name) mapping[name] = this;
362    }
363    else {
364        get_leftson()->map_species2tip(mapping);
365        get_rightson()->map_species2tip(mapping);
366    }
367}
368
369void GroupTree::update_tag_counters() {
370    di_assert(!is_leaf);
371    GroupTree *node = this;
372    while (node) {
373        node->tagged_count = node->get_leftson()->get_tagged_count() + node->get_rightson()->get_tagged_count();
374        node               = node->get_father();
375    }
376}
377
378struct GroupChanges {
379    unsigned created;
380    unsigned skipped;
381    unsigned overwritten;
382    unsigned deleted;
383    unsigned restored;
384
385    void clear() { created = skipped = overwritten = deleted = restored = 0; }
386    bool exist() const { return created||overwritten||deleted||restored; }
387
388    void show_message() {
389        string msg;
390
391        if (created)     msg += GBS_global_string("%u created  ",     created);
392        if (overwritten) msg += GBS_global_string("%u overwritten  ", overwritten);
393        if (skipped)     msg += GBS_global_string("%u skipped  ",     skipped);
394        if (deleted)     msg += GBS_global_string("%u deleted  ",     deleted);
395        if (restored)    msg += GBS_global_string("%u restored  ",    restored);
396
397        if (!msg.empty()) {
398            msg = string("Group changes: ")+msg;
399            aw_message(msg.c_str());
400        }
401    }
402   
403    GroupChanges() { clear(); }
404};
405
406
407// ---------------------
408//      GroupBuilder
409
410typedef map<GroupTree*, ClusterPtr> Group2Cluster;
411
412class GroupBuilder : virtual Noncopyable {
413    GBDATA           *gb_main;
414    string            tree_name;
415    ARB_seqtree_root *tree_root;
416    Group_Action      action;                       // create or delete ?
417    Species2Tip       species2tip;                  // map speciesName -> leaf
418    ARB_ERROR         error;
419    ClusterPtr        bad_cluster;                  // error occurred here (is set)
420    Group_Existing    existing;
421    unsigned          existing_count;               // counts existing groups
422    Group_NotFound    notfound;
423    double            matchRatio;                   // needed identity of subtree and cluster
424    double            maxDist;                      // max. Distance used for calculation
425    string            cluster_prefix;               // prefix for cluster name
426    string            cluster_suffix_def;           // suffix-definition for cluster name
427    GroupChanges      changes;                      // count tree modifications
428    bool              del_match_prefixes;           // only delete groups, where prefix matches
429
430    GroupTree *find_group_position(GroupTree *subtree, unsigned cluster_size);
431
432public:
433    GroupBuilder(GBDATA *gb_main_, Group_Action action_)
434        : gb_main(gb_main_),
435          tree_root(NULL),
436          action(action_),
437          error(NULL),
438          existing_count(0)
439    {
440        AW_root *awr = global_data->get_aw_root();
441
442        tree_name          = awr->awar(AWAR_DIST_TREE_CURR_NAME)->read_char_pntr();
443        existing           = (Group_Existing)awr->awar(AWAR_CLUSTER_GROUP_EXISTING)->read_int();
444        notfound           = (Group_NotFound)awr->awar(AWAR_CLUSTER_GROUP_NOTFOUND)->read_int();
445        del_match_prefixes = awr->awar(AWAR_CLUSTER_GROUP_PREFIX_MATCH)->read_int();
446        matchRatio         = awr->awar(AWAR_CLUSTER_GROUP_IDENTITY)->read_int()/100.0;
447        maxDist            = awr->awar(AWAR_CLUSTER_MAXDIST)->read_float();
448        cluster_prefix     = awr->awar(AWAR_CLUSTER_GROUP_PREFIX)->read_char_pntr();
449        cluster_suffix_def = awr->awar(AWAR_CLUSTER_GROUP_SUFFIX)->read_char_pntr();
450    }
451    ~GroupBuilder() {
452        delete tree_root;
453    }
454
455    ARB_ERROR get_error() const { return error; }
456    ClusterPtr get_bad_cluster() const { return bad_cluster; }
457    Group_Existing with_existing() const { return existing; }
458    unsigned get_existing_count() const { return existing_count; }
459    double get_max_distance() const { return maxDist; }
460
461    ARB_ERROR save_modified_tree();
462    void      load_tree();
463
464    DEFINE_DOWNCAST_ACCESSORS(GroupTree, get_root_node, tree_root->get_root_node());
465
466    GroupTree *find_best_matching_subtree(ClusterPtr cluster);
467    void update_group(ClusterPtr cluster); // create or delete group for cluster
468    string generate_group_name(ClusterPtr cluster, const GroupTree *group_node);
469
470    bool matches_current_prefix(const char *groupname) const {
471        return strstr(groupname, cluster_prefix.c_str()) == groupname;
472    }
473
474    bool shall_delete_group(const char *name) const {
475        return !del_match_prefixes || matches_current_prefix(name);
476    }
477};
478
479void GroupBuilder::load_tree() {
480    di_assert(!tree_root);
481
482    tree_root = new ARB_seqtree_root(new AliView(gb_main), new GroupTreeNodeFactory, NULL, false);
483    error     = tree_root->loadFromDB(tree_name.c_str());
484
485    if (error) {
486        delete tree_root;
487        tree_root = NULL;
488    }
489    else {
490        changes.clear();
491
492        GroupTree *tree = get_root_node();
493        tree->update_leaf_counters();
494        tree->map_species2tip(species2tip);
495    }
496}
497ARB_ERROR GroupBuilder::save_modified_tree() {
498    di_assert(!error);
499    if (changes.exist()) {
500        di_assert(tree_root);
501        error = tree_root->saveToDB();
502
503        AW_root *awr = global_data->get_aw_root();
504        awr->awar(AWAR_TREE_REFRESH)->touch();
505
506        if (!error) {
507            changes.show_message();
508            changes.clear();
509        }
510    }
511    return error;
512}
513
514GroupTree *GroupBuilder::find_group_position(GroupTree *subtree, unsigned cluster_size) {
515    // searches for best group in subtree matching the cluster
516
517    GroupTree *groupPos = NULL;
518    if (subtree->get_tagged_count() == cluster_size) {
519        groupPos                = find_group_position(subtree->get_leftson(), cluster_size);
520        if (!groupPos) groupPos = find_group_position(subtree->get_rightson(), cluster_size);
521
522        if (!groupPos) {                            // consider 'subtree'
523            if (subtree->tagged_rate() >= matchRatio) {
524                groupPos = subtree;
525            }
526        }
527    }
528    return groupPos;
529}
530
531class HasntCurrentClusterPrefix : public ARB_tree_predicate {
532    const GroupBuilder& builder;
533public:
534    HasntCurrentClusterPrefix(const GroupBuilder& builder_) : builder(builder_) {}
535    bool selects(const ARB_seqtree& tree) const OVERRIDE {
536        const char *groupname        = tree.get_group_name();
537        bool        hasClusterPrefix = groupname && builder.matches_current_prefix(groupname);
538        return !hasClusterPrefix;
539    }
540};
541
542string concatenate_name_parts(const list<string>& namepart) {
543    string concat;
544    for (list<string>::const_iterator p = namepart.begin(); p != namepart.end(); ++p) {
545        if (!p->empty()) concat += '_'+*p;
546    }
547    return concat.erase(0, 1);
548}
549
550struct UseTreeRoot : public ARB_tree_predicate {
551    bool selects(const ARB_seqtree& tree) const OVERRIDE { return tree.is_root_node(); }
552};
553
554string GroupBuilder::generate_group_name(ClusterPtr cluster, const GroupTree *group_node) {
555    list<string> namepart;
556    namepart.push_back(cluster_prefix);
557
558    string  orgname_suffix;
559    if (existing == EXISTING_GROUP_APPEND_ORG) {
560        char *old_name = group_node->name;
561        if (old_name) {
562            char *original = originalGroupName(old_name);
563            if (!original && !matches_current_prefix(old_name)) {
564                original = strdup(old_name); // use existing name as original name
565            }
566            if (original) {
567                orgname_suffix = string(" {was:")+original+"}";
568                free(original);
569            }
570        }
571    }
572
573    string text;
574    for (int i = 0; cluster_suffix_def[i]; ++i) {
575        if (cluster_suffix_def[i] == '%') {
576            ++i;
577            if (cluster_suffix_def[i] == '%') {
578                text += '%';
579            }
580            else {
581                string expanded;
582                switch(cluster_suffix_def[i]) {
583                    case 'p': expanded = cluster->get_upgroup_info(group_node, HasntCurrentClusterPrefix(*this)); break;
584                    case 'P': expanded = cluster->get_upgroup_info(group_node, UseAnyTree()); break;
585                    case 't': expanded = cluster->get_upgroup_info(group_node, UseTreeRoot()); break;
586                    case 'd': expanded = GBS_global_string("~%.3f", cluster->get_mean_distance()); break;
587                    case 'D': expanded = GBS_global_string("<=%.1f%%", get_max_distance()); break;
588                    case 'e': expanded = group_node->name ? group_node->name : ""; break;
589                    case 'o': {
590                        int matchRate = int(group_node->tagged_rate()*100+0.5);
591                        if (matchRate<100) expanded = GBS_global_string("%i%%_of", matchRate);
592                        break;
593                    }
594                    default:
595                        text += '%';
596                        text += cluster_suffix_def[i];
597                        break;
598                }
599
600                if (!expanded.empty()) {
601                    namepart.push_back(text);
602                    text = "";
603                    namepart.push_back(expanded);
604                }
605            }
606        }
607        else {
608            text += cluster_suffix_def[i];
609        }
610    }
611    namepart.push_back(text);
612
613    return concatenate_name_parts(namepart)+orgname_suffix;
614}
615
616GroupTree *GroupBuilder::find_best_matching_subtree(ClusterPtr cluster) {
617    GroupTree *group_node = NULL;
618    if (!error) {
619        if (!tree_root) load_tree();
620        if (!error) {
621            const DBItemSet& members = cluster->get_members();
622
623            // mark cluster members in tree
624            {
625                GB_transaction ta(gb_main);
626                DBItemSetIter sp_end = members.end();
627                for (DBItemSetIter sp = members.begin(); sp != sp_end && !error; ++sp) {
628                    const char *name = GBT_get_name(*sp);
629                    di_assert(name);
630                    if (name) {
631                        Species2Tip::const_iterator found = species2tip.find(name);
632                        if (found == species2tip.end()) {
633                            error = GBS_global_string("Species '%s' is not in '%s'", name, tree_name.c_str());
634                        }
635                        else {
636                            GroupTree *leaf = found->second;
637                            leaf->tag_leaf();
638                        }
639                    }
640                }
641            }
642
643            if (!error) {
644                // top-down search for best matching node
645                group_node = find_group_position(get_root_node(), cluster->get_member_count());
646            }
647        }
648    }
649    return group_node;
650}
651
652void GroupBuilder::update_group(ClusterPtr cluster) {
653    if (!error) {
654        GroupTree *group_node = find_best_matching_subtree(cluster);
655        if (!error) {
656            if (!group_node) { // no matching subtree found
657                switch (notfound) {
658                    case NOTFOUND_WARN:
659                    case NOTFOUND_ABORT: {
660                        const char *msg = GBS_global_string("Could not find matching subtree for cluster '%s'", cluster->get_list_display(NULL));
661                        if (notfound == NOTFOUND_ABORT) error = msg;
662                        else aw_message(msg);
663                        break;
664                    }
665                    case NOTFOUND_IGNORE: break; // silently ignore
666                }
667            }
668            else { // found subtree for group
669                switch (action) {
670                    case GROUP_CREATE: {
671                        char *old_name = group_node->name;
672
673                        if (old_name && existing == EXISTING_GROUP_ABORT) {
674                            error = GBS_global_string("Existing group '%s' is in the way", old_name);
675                        }
676                        else {
677                            if (old_name && existing == EXISTING_GROUP_SKIP) {
678                                changes.skipped++;
679                            }
680                            else {
681                                string new_name = generate_group_name(cluster, group_node);
682
683                                if (old_name) changes.overwritten++; else changes.created++;
684
685                                free(old_name);
686                                group_node->name = strdup(new_name.c_str());
687
688                                // @@@ DRY that.. it's spread everywhere through libs :(
689                                if (!group_node->gb_node) {
690                                    GBDATA *gb_tree = group_node->get_tree_root()->get_gb_tree();
691                                    GB_transaction ta(gb_tree);
692                                    GBDATA *gb_node = GB_create_container(gb_tree, "node");
693                                    if (!gb_node) {
694                                        error = GB_await_error();
695                                    }
696                                    else {
697                                        error = GBT_write_int(gb_node, "id", 0);
698                                    }
699
700                                    if (!error) {
701                                        group_node->gb_node = gb_node;
702                                    }
703                                }
704                                if (group_node->gb_node && !error) {
705                                    GB_transaction ta(group_node->gb_node);
706                                    error = GBT_write_string(group_node->gb_node, "group_name", group_node->name);
707                                }
708
709                                cluster->update_description(group_node); // change list display
710                            }
711                        }
712                        break;
713                    }
714                    case GROUP_DELETE: {
715                        if (group_node->name && shall_delete_group(group_node->name)) {
716                            char *original = originalGroupName(group_node->name);
717
718                            if (original) {
719                                freeset(group_node->name, original); // restore original name
720                                if (group_node->gb_node) {
721                                    error = GBT_write_string(group_node->gb_node, "group_name", group_node->name);
722                                }
723                                changes.restored++;
724                            }
725                            else {
726                                freenull(group_node->name);
727                                group_node->gb_node = NULL; // forget ref to group data (@@@ need to delete group data from DB ? )
728                                changes.deleted++;
729                            }
730
731                            cluster->update_description(group_node); // change list display
732                        }
733                        break;
734                    }
735                }
736            }
737        }
738
739        if (error) bad_cluster = cluster;
740        get_root_node()->clear_tags();
741    }
742}
743
744static void update_example(AW_root *aw_root) {
745    ID     selID = aw_root->awar(AWAR_CLUSTER_SELECTED)->read_int();
746    string value;
747
748    if (is_cluster(selID)) {
749        ClusterPtr selCluster = global_data->clusterWithID(selID);
750        cl_assert(!selCluster.isNull());
751
752        GroupBuilder  builder(global_data->get_gb_main(), GROUP_CREATE);
753        GroupTree    *group_node = builder.find_best_matching_subtree(selCluster);
754
755        GB_ERROR error = builder.get_error().deliver();
756       
757        if (error)           value = GBS_global_string("<error: %s>", error);
758        else if (group_node) value = builder.generate_group_name(selCluster, group_node);
759        else                 value = "<no matching subtree found>";
760    }
761    else value = "<no cluster selected>";
762    aw_root->awar(AWAR_CLUSTER_GROUP_EXAMPLE)->write_string(value.c_str());
763}
764
765static void update_cluster_group(ClusterPtr cluster, GroupBuilder *groupBuilder) {
766    if (!groupBuilder->get_error()) {
767        groupBuilder->update_group(cluster);
768    }
769}
770
771static void accept_proposed_names(ClusterPtr cluster, bool accept) {
772    cluster->accept_proposed(accept);
773}
774
775static void group_clusters(AW_window *, Group_Action action) {
776    AW_root          *aw_root  = global_data->get_aw_root();
777    Group_What        what     = (Group_What)aw_root->awar(AWAR_CLUSTER_GROUP_WHAT)->read_int();
778    AffectedClusters  affected = what == GROUP_LISTED ? ALL_CLUSTERS : SEL_CLUSTER;
779
780    GroupBuilder groupBuilder(global_data->get_gb_main(), action);
781
782    GB_transaction ta(global_data->get_gb_main());
783    with_affected_clusters_do(aw_root, affected, true, makeClusterCallback(update_cluster_group, &groupBuilder));
784
785    ARB_ERROR error = groupBuilder.get_error();
786    if (error) {
787        ClusterPtr bad = groupBuilder.get_bad_cluster();
788        if (!bad.isNull()) {
789            select_cluster(bad->get_ID());
790            aw_message("Problematic cluster has been highlighted");
791        }
792    }
793    else {
794        error = groupBuilder.save_modified_tree();
795    }
796
797    error = ta.close(error);
798
799    bool accept = !error;
800    aw_message_if(error);
801    // careful! the following code will invalidate error, so don't use below
802
803    with_affected_clusters_do(aw_root, affected, false, makeClusterCallback(accept_proposed_names, accept)); // just affects display
804    global_data->update_cluster_selection_list();
805}
806
807static void popup_group_clusters_window(AW_window *aw_clusterList) {
808    static AW_window_simple *aws = 0;
809
810    if (!aws) {
811        AW_root *aw_root = aw_clusterList->get_root();
812
813        aws = new AW_window_simple;
814        aws->init(aw_root, "cluster_groups", "Cluster groups");
815
816        aws->auto_space(10, 10);
817
818        aws->callback((AW_CB0)AW_POPDOWN);
819        aws->create_button("CLOSE", "CLOSE", "C");
820        aws->callback(makeHelpCallback("cluster_group.hlp"));
821        aws->create_button("HELP", "HELP");
822
823        aws->at_newline();
824
825        aws->label("For");
826        aws->create_option_menu(AWAR_CLUSTER_GROUP_WHAT, true);
827        aws->insert_option        ("selected cluster", "s", GROUP_SELECTED);
828        aws->insert_default_option("listed clusters",  "l", GROUP_LISTED);
829        aws->update_option_menu();
830
831        aws->at_newline();
832
833        aws->label("with a min. cluster/subtree identity (%) of");
834        aws->create_input_field(AWAR_CLUSTER_GROUP_IDENTITY, 4);
835
836        aws->at_newline();
837
838        aws->label("-> if no matching subtree found");
839        aws->create_option_menu(AWAR_CLUSTER_GROUP_NOTFOUND, true);
840        aws->insert_default_option("abort",          "a", NOTFOUND_ABORT);
841        aws->insert_option        ("warn",           "w", NOTFOUND_WARN);
842        aws->insert_option        ("ignore",         "i", NOTFOUND_IGNORE);
843        aws->update_option_menu();
844
845        aws->at_newline();
846
847        aws->callback(makeWindowCallback(group_clusters, GROUP_CREATE));
848        aws->create_autosize_button("CREATE_GROUPS", "create groups!");
849
850        aws->label("If group exists");
851        aws->create_option_menu(AWAR_CLUSTER_GROUP_EXISTING, true);
852        aws->insert_default_option("abort",                "a", EXISTING_GROUP_ABORT);
853        aws->insert_option        ("skip",                 "s", EXISTING_GROUP_SKIP);
854        aws->insert_option        ("overwrite (caution!)", "o", EXISTING_GROUP_OVERWRITE);
855        aws->insert_option        ("append original",      "p", EXISTING_GROUP_APPEND_ORG);
856        aws->update_option_menu();
857
858        aws->at_newline();
859
860        aws->callback(makeWindowCallback(group_clusters, GROUP_DELETE));
861        aws->create_autosize_button("DELETE_GROUPS", "delete groups!");
862
863        aws->create_text_toggle(AWAR_CLUSTER_GROUP_PREFIX_MATCH, "(all)", "(where prefix matches)", 30);
864
865        aws->at_newline(); aws->label("Name prefix:"); aws->create_input_field(AWAR_CLUSTER_GROUP_PREFIX, 20);
866        aws->at_newline(); aws->label("Name suffix:"); aws->create_input_field(AWAR_CLUSTER_GROUP_SUFFIX, 20);
867
868        aws->at_newline();
869        aws->button_length(60);
870        aws->label("=>"); 
871        aws->create_button(NULL, AWAR_CLUSTER_GROUP_EXAMPLE);
872
873        aws->window_fit();
874    }
875
876    aws->activate();
877}
878
879// ---------------
880//      delete
881
882static void delete_selected_cluster(ClusterPtr cluster) {
883    int pos    = global_data->get_pos(cluster, SHOWN_CLUSTERS);
884    int nextId = global_data->idAtPos(pos+1, SHOWN_CLUSTERS);
885    select_cluster(nextId);
886    global_data->remove(cluster, SHOWN_CLUSTERS);
887}
888static void delete_clusters(AW_window *aww, AffectedClusters affected) {
889    switch (affected) {
890        case SEL_CLUSTER:
891            with_affected_clusters_do(aww->get_root(), affected, true, makeClusterCallback(delete_selected_cluster));
892            break;
893        case ALL_CLUSTERS:
894            select_cluster(0);
895            global_data->clear(SHOWN_CLUSTERS);
896            break;
897    }
898
899    update_cluster_sellist();
900}
901
902// ----------------------
903//      store/restore
904
905static void store_selected_cluster(ClusterPtr cluster) {
906    int pos    = global_data->get_pos(cluster, SHOWN_CLUSTERS);
907    int nextId = global_data->idAtPos(pos+1, SHOWN_CLUSTERS);
908
909    select_cluster(nextId);
910    global_data->store(cluster->get_ID());
911}
912static void store_clusters(AW_window *aww, AffectedClusters affected) {
913    switch (affected) {
914        case SEL_CLUSTER:
915            with_affected_clusters_do(aww->get_root(), affected, true, makeClusterCallback(store_selected_cluster));
916            break;
917        case ALL_CLUSTERS:
918            select_cluster(0);
919            global_data->store_all();
920            break;
921    }
922
923    update_all(aww);
924}
925
926
927static void restore_clusters(AW_window *aww) {
928    global_data->restore_all();
929    update_all(aww);
930}
931static void swap_clusters(AW_window *aww) {
932    global_data->swap_all();
933    select_cluster(0);
934    update_all(aww);
935}
936
937// ---------------------------------
938//      cluster detection window
939
940AW_window *DI_create_cluster_detection_window(AW_root *aw_root, WeightedFilter *weightedFilter) {
941    static AW_window_simple *aws = 0;
942    if (!aws) {
943        cl_assert(!global_data);
944        global_data = new ClustersData(*weightedFilter);
945
946        aws = new AW_window_simple;
947        aws->init(aw_root, "DETECT_CLUSTERS", "Detect clusters in tree");
948        aws->load_xfig("di_clusters.fig");
949
950        aws->on_hide(di_forget_global_data);
951
952        // -------------------
953        //      upper area
954
955        aws->at("close");
956        aws->callback(AW_POPDOWN);
957        aws->create_button("CLOSE", "CLOSE");
958
959        aws->at("help");
960        aws->callback(makeHelpCallback("di_clusters.hlp"));
961        aws->create_button("HELP", "HELP");
962
963        aws->at("max_dist");
964        aws->d_callback(makeWindowCallback(calculate_clusters));
965        aws->create_input_field(AWAR_CLUSTER_MAXDIST, 12);
966
967        aws->at("min_size");
968        aws->d_callback(makeWindowCallback(calculate_clusters));
969        aws->create_input_field(AWAR_CLUSTER_MINSIZE, 5);
970
971        aws->at("calculate");
972        aws->callback(calculate_clusters);
973        aws->create_autosize_button("CALC", "Detect clusters");
974
975        aws->button_length(20);
976        aws->at("tree_name");
977        aws->create_button(NULL, AWAR_DIST_TREE_CURR_NAME);
978
979        // -------------------
980        //      lower area
981
982        aws->button_length(10);
983
984        // column 1
985
986        aws->at("select_rep"); aws->create_toggle(AWAR_CLUSTER_SELECTREP);
987        aws->at("mark"); aws->callback(makeWindowCallback(mark_clusters, SEL_CLUSTER, true)); aws->create_button("MARK", "Mark");
988        aws->at("auto_mark"); aws->create_toggle(AWAR_CLUSTER_AUTOMARK);
989
990        aws->at("mark_what");
991        aws->create_option_menu(AWAR_CLUSTER_MARKREP, true);
992        aws->insert_default_option("cluster w/o repr.",   "d", CMM_ALL_BUT_REP);
993        aws->insert_option        ("whole cluster",       "b", CMM_ALL);
994        aws->insert_option        ("only representative", "s", CMM_ONLY_REP);
995        aws->update_option_menu();
996
997        aws->at("mark_all"); aws->callback(makeWindowCallback(mark_clusters, ALL_CLUSTERS, true)); aws->create_button("MARKALL", "Mark all");
998
999        // column 2
1000
1001        aws->button_length(18);
1002
1003        aws->at("group"); aws->callback(popup_group_clusters_window); aws->create_button("GROUP", "Cluster groups..");
1004
1005        aws->at("sort");
1006        aws->create_option_menu(AWAR_CLUSTER_ORDER, true);
1007        aws->insert_default_option("by mean distance",  "d", SORT_BY_MEANDIST);
1008        aws->insert_option        ("by min bases used", "b", SORT_BY_MIN_BASES);
1009        aws->insert_option        ("by size",           "s", SORT_BY_CLUSTERSIZE);
1010        aws->insert_option        ("by tree position",  "p", SORT_BY_TREEPOSITION);
1011        aws->insert_option        ("by min distance",   "i", SORT_BY_MIN_DIST);
1012        aws->insert_option        ("by max distance",   "x", SORT_BY_MAX_DIST);
1013        aws->insert_option        ("reverse",           "r", SORT_REVERSE);
1014        aws->update_option_menu();
1015
1016        // store/restore
1017
1018        aws->at("store_all"); aws->callback(makeWindowCallback(store_clusters, ALL_CLUSTERS)); aws->create_button("STOREALL", "Store all");
1019        aws->at("store");     aws->callback(makeWindowCallback(store_clusters, SEL_CLUSTER));  aws->create_button("STORESEL", "Store selected");
1020        aws->at("restore");   aws->callback(restore_clusters);                                 aws->create_button("RESTORE",  AWAR_CLUSTER_RESTORE_LABEL);
1021        aws->at("swap");      aws->callback(swap_clusters);                                    aws->create_button("Swap",     "Swap stored");
1022
1023        // column 4
1024
1025        aws->at("clear");  aws->callback(makeWindowCallback(delete_clusters, ALL_CLUSTERS)); aws->create_button("CLEAR", "Clear list");
1026        aws->at("delete"); aws->callback(makeWindowCallback(delete_clusters, SEL_CLUSTER));  aws->create_button("DEL",   "Delete selected");
1027
1028        // --------------------
1029        //      clusterlist
1030
1031        aws->at("cluster_list");
1032        global_data->clusterList = aws->create_selection_list(AWAR_CLUSTER_SELECTED, true);
1033        update_cluster_sellist();
1034
1035        aw_root->awar(AWAR_CLUSTER_SELECTED)->add_callback(select_cluster_cb);
1036        aw_root->awar(AWAR_CLUSTER_ORDER)->add_callback(sort_order_changed_cb);
1037        sort_order_changed_cb(aw_root);
1038    }
1039
1040    return aws;
1041}
1042
1043void DI_create_cluster_awars(AW_root *aw_root, AW_default def, AW_default db) {
1044    aw_root->awar_float(AWAR_CLUSTER_MAXDIST,   3.0,             def)->set_minmax(0.0, 100.0);
1045    aw_root->awar_int  (AWAR_CLUSTER_MINSIZE,   7,               def)->set_minmax(2, INT_MAX);
1046    aw_root->awar_int  (AWAR_CLUSTER_AUTOMARK,  1,               def);
1047    aw_root->awar_int  (AWAR_CLUSTER_MARKREP,   CMM_ALL_BUT_REP, def);
1048    aw_root->awar_int  (AWAR_CLUSTER_SELECTREP, 1,               def);
1049
1050    aw_root->awar_int   (AWAR_CLUSTER_ORDER,         SORT_BY_MEANDIST, def);
1051    aw_root->awar_string(AWAR_CLUSTER_RESTORE_LABEL, "None stored",    def);
1052
1053    aw_root->awar_int(AWAR_CLUSTER_GROUP_WHAT,     GROUP_LISTED,   def);
1054    aw_root->awar_int(AWAR_CLUSTER_GROUP_NOTFOUND, NOTFOUND_ABORT, def);
1055
1056    aw_root->awar_int   (AWAR_CLUSTER_GROUP_IDENTITY,     100, def)->set_minmax(1, 100);
1057    aw_root->awar_int   (AWAR_CLUSTER_GROUP_PREFIX_MATCH, 1,   def);
1058    aw_root->awar_string(AWAR_CLUSTER_GROUP_EXAMPLE,      "",  def);
1059
1060    aw_root->awar_int   (AWAR_CLUSTER_SELECTED,       0,                    def)->add_callback(update_example);
1061    aw_root->awar_int   (AWAR_CLUSTER_GROUP_EXISTING, EXISTING_GROUP_ABORT, def)->add_callback(update_example);
1062    aw_root->awar_string(AWAR_CLUSTER_GROUP_PREFIX,   "cluster",            def)->add_callback(update_example);
1063    aw_root->awar_string(AWAR_CLUSTER_GROUP_SUFFIX,   "%o%p",               def)->add_callback(update_example);
1064
1065    aw_root->awar_int(AWAR_TREE_REFRESH, 0, db);
1066
1067    update_example(aw_root);
1068}
1069
Note: See TracBrowser for help on using the repository browser.