File: AWT_TreeAwars.cxx

package info (click to toggle)
arb 6.0.6-8
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid, trixie
  • size: 66,204 kB
  • sloc: ansic: 394,911; cpp: 250,290; makefile: 19,644; sh: 15,879; perl: 10,473; fortran: 6,019; ruby: 683; xml: 503; python: 53; awk: 32
file content (236 lines) | stat: -rw-r--r-- 7,993 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// =============================================================== //
//                                                                 //
//   File      : AWT_TreeAwars.cxx                                 //
//   Purpose   : tree awar registry                                //
//                                                                 //
//   Coded by Ralf Westram (coder@reallysoft.de) in January 2014   //
//   Institute of Microbiology (Technical University Munich)       //
//   http://www.arb-home.de/                                       //
//                                                                 //
// =============================================================== //

#include "awt_TreeAwars.hxx"

#include <arbdbt.h>
#include <ad_cb.h>

#include <arb_global_defs.h>

#include <aw_awar.hxx>
#include <aw_root.hxx>
#include <aw_global_awars.hxx>

#include <set>
#include <cstddef>

#define awt_assert(cond) arb_assert(cond)

/*! bind a callback to a tree-awar and the associated tree
 * i.e. callback is triggered if awar changes to different tree
 * or if tree itself changes.
 *
 * The awar has to be type string and has to contain a tree name
 */
class BoundTreeAwarCallback : virtual Noncopyable {
    mutable AW_awar  *awar;
    GBDATA           *gb_tree;
    TreeAwarCallback  cb;
    mutable bool      triggerOnDataChange;

public:
    BoundTreeAwarCallback(AW_awar *awar_, const TreeAwarCallback& cb_, bool triggerIfTreeDataChanges);
    ~BoundTreeAwarCallback();

    void bind_tree_callback();
    void call(bool treeDataChanged) { cb(awar, treeDataChanged); }

    void rename_if(const char *oldname, const char *newname) const {
        if (strcmp(awar->read_char_pntr(), oldname) == 0) {
            awar->write_string(newname);
        }
    }

    bool is_less_than(const BoundTreeAwarCallback& other) const {
        ptrdiff_t diff = (const char*)awar - (const char*)other.awar;
        return diff<0;
    }

    void tree_lost() { gb_tree = NULL; }
};

typedef SmartPtr<BoundTreeAwarCallback> BoundTreeAwarCallbackPtr;

inline bool operator<(const BoundTreeAwarCallbackPtr& bc1, const BoundTreeAwarCallbackPtr& bc2) {
    return bc1->is_less_than(*bc2);
}

typedef std::set<BoundTreeAwarCallbackPtr> BoundTreeAwarCallbacks;

/*! registry for TreeAwars
 */
class TreeAwarRegistry : virtual Noncopyable {
    GBDATA                 *gb_main;
    BoundTreeAwarCallbacks  callbacks;

public:
    TreeAwarRegistry(GBDATA *gb_main_);

    static SmartPtr<TreeAwarRegistry> SINGLETON;

    GBDATA *get_gb_main() { return gb_main; }
    void add(BoundTreeAwarCallbackPtr bcb) { callbacks.insert(bcb); }

    void tree_renamed(const char *oldname, const char *newname);
};

SmartPtr<TreeAwarRegistry> TreeAwarRegistry::SINGLETON;

static void destroy_TreeAwarRegistry(GBDATA*,void*) {
    TreeAwarRegistry::SINGLETON.SetNull();
}

// --------------------------
//      BoundTreeAwarCallback

static void TreeDataChanged_cb(UNFIXED, BoundTreeAwarCallback *tac, GB_CB_TYPE cbtype) {
    if (cbtype == GB_CB_DELETE) {
        tac->tree_lost();
    }
    tac->call(true);
}
static void TreeAwarChanged_cb(UNFIXED, BoundTreeAwarCallback *tac) {
    tac->bind_tree_callback();
    tac->call(false);
}

void BoundTreeAwarCallback::bind_tree_callback() {
    if (triggerOnDataChange) {
        DatabaseCallback dbcb = makeDatabaseCallback(TreeDataChanged_cb, this);
        {
            GBDATA         *gb_main = TreeAwarRegistry::SINGLETON->get_gb_main();
            GB_transaction  ta(gb_main);
            if (gb_tree) {
                GB_remove_callback(gb_tree, GB_CB_CHANGED_OR_DELETED, dbcb);
                gb_tree = NULL;
            }

            char *treename = awar->read_string();
            gb_tree        = GBT_find_tree(gb_main, treename);

            if (gb_tree) GB_add_callback(gb_tree, GB_CB_CHANGED_OR_DELETED, dbcb);
            free(treename);
        }
    }
}

BoundTreeAwarCallback::BoundTreeAwarCallback(AW_awar *awar_, const TreeAwarCallback& cb_, bool triggerIfTreeDataChanges)
    : awar(awar_),
      gb_tree(NULL),
      cb(cb_),
      triggerOnDataChange(triggerIfTreeDataChanges)
{
    awar->add_callback(makeRootCallback(TreeAwarChanged_cb, this));
    bind_tree_callback();
}
BoundTreeAwarCallback::~BoundTreeAwarCallback() {
    awar->remove_callback(makeRootCallback(TreeAwarChanged_cb, this));

    // remove DB callback w/o triggering it:
    char *old = awar->read_string();
    awar->write_string("");
    bind_tree_callback();
    awar->write_string(old);
    free(old);
}

// --------------------------
//      TreeAwarRegistry

static void tree_renamed_cb(AW_root *aw_root) {
    AW_awar *awar_renamed = aw_root->awar(AWAR_ARB_TREE_RENAMED);
    char    *name_change  = awar_renamed->read_string();

    if (name_change[0]) {
        char     *equal   = strchr(name_change, '=');
        GB_ERROR  error   = NULL;
        if (!equal) error = "Expected '=' missing";
        else {
            equal[0] = 0;

            const char *oldname = name_change;
            const char *newname = equal+1;
            TreeAwarRegistry::SINGLETON->tree_renamed(oldname, newname);
        }

        if (error) {
            fprintf(stderr, "Warning: Cannot handle invalid tree-name-change ('%s'; %s)",
                    awar_renamed->read_char_pntr(), error);
        }
    }
}

TreeAwarRegistry::TreeAwarRegistry(GBDATA *gb_main_)
    : gb_main(gb_main_)
{
    // uses global awar AWAR_ARB_TREE_RENAMED to synchronize between multiple ARB apps
    AW_root::SINGLETON->awar(AWAR_ARB_TREE_RENAMED)->add_callback(tree_renamed_cb);
    GB_atclose(gb_main_, destroy_TreeAwarRegistry, NULL);
}

void TreeAwarRegistry::tree_renamed(const char *oldname, const char *newname) {
    for (BoundTreeAwarCallbacks::iterator bcb = callbacks.begin(); bcb != callbacks.end(); ++bcb) {
        (*bcb)->rename_if(oldname, newname);
    }
}

// -------------------
//      interface

void AWT_initTreeAwarRegistry(GBDATA *gbmain) {
    /*! initialize TreeAwarRegistry.
     *
     * Has to be called after ARB_init_global_awars().
     * Will be destroyed on GB_close().
     *
     * Allows to use other functions provided by this module.
     */
    awt_assert(gbmain);
    if (TreeAwarRegistry::SINGLETON.isSet()) {
        if (TreeAwarRegistry::SINGLETON->get_gb_main() != gbmain) {
            GBK_terminate("double init of TreeAwarRegistry with different gbmain");
        }
    }
    else {
        TreeAwarRegistry::SINGLETON = new TreeAwarRegistry(gbmain);
    }
}

void AWT_registerTreeAwarCallback(AW_awar *awar, const TreeAwarCallback& tacb, bool triggerIfTreeDataChanges) {
    /*! bind and register TreeAwarCallback
     *
     * Signature of TreeAwarCallback is 'void cb(AW_awar *awar, bool treeDataChanged, ...)'
     * 'treeDataChanged' is true, if the callback is triggered by a change of the tree-data
     * 'treeDataChanged' is false, if the callback is triggered by a change of the awar
     *
     * if 'triggerIfTreeDataChanges' is false, the callback will not trigger if tree-data changes
     */

    TreeAwarRegistry::SINGLETON->add(new BoundTreeAwarCallback(awar, tacb, triggerIfTreeDataChanges));
}

static void null_cb() {}
void AWT_registerTreeAwarSimple(AW_awar *awar) {
    /*! just register a tree awar, w/o any client cb.
     * -> awar will just automatically follow tree-renames
     */
    AWT_registerTreeAwarCallback(awar, makeTreeAwarCallback(null_cb), false);
}

static void announce_renamed(const char *oldname, const char *newname) {
    AW_awar *awar_renamed = AW_root::SINGLETON->awar(AWAR_ARB_TREE_RENAMED);
    awar_renamed->write_string(GBS_global_string("%s=%s", oldname, newname)); // triggers tree_renamed_cb (in this and all other ARB apps)
}

void AWT_announce_tree_renamed(const char *oldname, const char *newname) { announce_renamed(oldname, newname); }
void AWT_announce_tree_deleted(const char *name)                         { announce_renamed(name,    NO_TREE_SELECTED); }