source: tags/svn.1.5.4/DIST/DI_clusters.cxx

Last change on this file was 7931, checked in by westram, 13 years ago

merge from dev [7905] [7908] [7927]

  • added __ATTR__DEPRECATED_FUNCTION.. (helps to declare ctors deprecated)
  • silenced several warnings
    • new flavors for attributes USERESULT / DEPRECATED (toggle with WARN_TODO from config.makefile)
    • some TODOs
  • fix broken attribute; introduced by [7585]

Note: [7908] merged partially only (cb.h skipped!)

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