source: tags/ms_r16q2/EDIT4/ED4_manager.cxx

Last change on this file was 14483, checked in by westram, 8 years ago
  • use typed callbacks in route_down_hierarchy
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.5 KB
Line 
1#include <arbdbt.h>
2
3#include <aw_preset.hxx>
4#include <aw_awar.hxx>
5#include <aw_msg.hxx>
6#include <aw_root.hxx>
7
8#include "ed4_class.hxx"
9#include "ed4_awars.hxx"
10#include "ed4_edit_string.hxx"
11#include "ed4_tools.hxx"
12#include "ed4_list.hxx"
13#include "ed4_ProteinViewer.hxx"
14#include "ed4_protein_2nd_structure.hxx"
15#include "ed4_seq_colors.hxx"
16
17#if defined(DEBUG)
18#define TEST_REFRESH_FLAG
19#endif
20
21// -----------------------------------------------------------------
22//      Manager static properties (used by manager-constructors)
23
24static ED4_objspec main_manager_spec(
25    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_HORIZONTAL), // static props
26    ED4_L_ROOT,                                            // level
27    ED4_L_ROOTGROUP,                                       // allowed children level
28    ED4_L_NO_LEVEL,                                        // handled object
29    ED4_L_NO_LEVEL                                         // restriction level
30    );
31
32static ED4_objspec device_manager_spec(
33    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_HORIZONTAL), // static props
34    ED4_L_DEVICE,                                          // level
35    (ED4_level)(ED4_L_AREA | ED4_L_SPACER | ED4_L_LINE),   // allowed children level
36    ED4_L_NO_LEVEL,                                        // handled object
37    ED4_L_NO_LEVEL                                         // restriction level
38    );
39
40static ED4_objspec area_manager_spec(
41    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_VERTICAL),          // static props
42    ED4_L_AREA,                                                   // level
43    (ED4_level)(ED4_L_MULTI_SPECIES | ED4_L_TREE | ED4_L_SPACER), // allowed children level
44    ED4_L_NO_LEVEL,                                               // handled object
45    ED4_L_NO_LEVEL                                                // restriction level
46    );
47
48static ED4_objspec multi_species_manager_spec(
49    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_HORIZONTAL),   // static props
50    ED4_L_MULTI_SPECIES,                                     // level
51    (ED4_level)(ED4_L_SPECIES | ED4_L_GROUP | ED4_L_SPACER), // allowed children level
52    ED4_L_NO_LEVEL,                                          // handled object
53    ED4_L_NO_LEVEL                                           // restriction level
54    );
55
56static ED4_objspec species_manager_spec(
57    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_VERTICAL),  // static props
58    ED4_L_SPECIES,                                        // level
59    // (ED4_level)(ED4_L_MULTI_SEQUENCE | ED4_L_MULTI_NAME), // allowed children level
60    (ED4_level)(ED4_L_MULTI_SEQUENCE | ED4_L_MULTI_NAME | // (used by normal species)
61                ED4_L_SPECIES_NAME | ED4_L_SEQUENCE), // allowed children level (used by consensus)
62    ED4_L_NO_LEVEL,                                       // handled object
63    ED4_L_NO_LEVEL                                        // restriction level
64    );
65static ED4_objspec multi_sequence_manager_spec(
66    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_HORIZONTAL), // static props
67    ED4_L_MULTI_SEQUENCE,                                  // level
68    ED4_L_SEQUENCE,                                        // allowed children level
69    ED4_L_NO_LEVEL,                                        // handled object
70    ED4_L_NO_LEVEL                                         // restriction level
71    );
72
73static ED4_objspec sequence_manager_spec(
74    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_VERTICAL),      // static props
75    ED4_L_SEQUENCE,                                           // level
76    (ED4_level)(ED4_L_SEQUENCE_INFO | ED4_L_SEQUENCE_STRING | ED4_L_ORF | ED4_L_PURE_TEXT | ED4_L_COL_STAT), // allowed children level
77    ED4_L_NO_LEVEL,                                           // handled object
78    ED4_L_SPECIES                                             // restriction level
79    );
80
81static ED4_objspec multi_name_manager_spec(
82    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_HORIZONTAL), // static props
83    ED4_L_MULTI_NAME,                                      // level
84    ED4_L_NAME_MANAGER,                                    // allowed children level
85    ED4_L_NO_LEVEL,                                        // handled object
86    ED4_L_NO_LEVEL                                         // restriction level
87    );
88
89static ED4_objspec name_manager_spec(
90    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_VERTICAL), // static props
91    ED4_L_NAME_MANAGER,                                  // level
92    (ED4_level)(ED4_L_SPECIES_NAME),                     // allowed children level
93    ED4_L_NO_LEVEL,                                      // handled object
94    ED4_L_SPECIES                                        // restriction level
95    );
96
97static ED4_objspec group_manager_spec(
98    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_VERTICAL),             // static props
99    ED4_L_GROUP,                                                     // level
100    (ED4_level)(ED4_L_MULTI_SPECIES | ED4_L_BRACKET), // allowed children level
101    ED4_L_NO_LEVEL,                                                  // handled object
102    ED4_L_NO_LEVEL                                                   // restriction level
103    );
104
105static ED4_objspec root_group_manager_spec(
106    (ED4_properties)(ED4_P_IS_MANAGER | ED4_P_VERTICAL), // static props
107    ED4_L_ROOTGROUP,                                     // level
108    (ED4_level)(ED4_L_DEVICE),                           // allowed children level
109    ED4_L_NO_LEVEL,                                      // handled object
110    ED4_L_NO_LEVEL                                       // restriction level
111    );
112
113// ----------------------------
114//      ED4_manager methods
115
116ED4_returncode ED4_manager::rebuild_consensi(ED4_base *start_species, ED4_update_flag update_flag) {
117    int                        i;
118    ED4_base                  *temp_parent;
119    ED4_multi_species_manager *multi_species_manager = NULL;
120    ED4_group_manager         *first_group_manager   = NULL;
121
122    temp_parent = start_species;
123
124    switch (update_flag) {
125        case ED4_U_UP:          // rebuild consensus from a certain starting point upwards
126            while (temp_parent) {
127                if (temp_parent->is_group_manager()) {
128                    multi_species_manager = temp_parent->to_group_manager()->get_multi_species_manager();
129                    for (i=0; i<multi_species_manager->children->members(); i++) {
130                        if (multi_species_manager->children->member(i)->is_consensus_manager()) {
131                            rebuild_consensus(multi_species_manager->children->member(i)).expect_no_error();
132                        }
133                    }
134                }
135                temp_parent = temp_parent->parent;
136            }
137            break;
138        case ED4_U_UP_DOWN:     // only search first groupmanager and update consensi recursively downwards
139            while (temp_parent) {
140                if (temp_parent->is_group_manager()) {
141                    first_group_manager = temp_parent->to_group_manager();
142                }
143                temp_parent = temp_parent->parent;
144            }
145            if (first_group_manager)
146                first_group_manager->route_down_hierarchy(makeED4_route_cb(rebuild_consensus)).expect_no_error();
147            break;
148    }
149    return ED4_R_OK;
150}
151
152ED4_returncode ED4_manager::check_in_bases(ED4_base *added_base) {
153    if (added_base->is_species_manager()) { // add a sequence
154        ED4_species_manager *species_manager = added_base->to_species_manager();
155        const ED4_terminal *sequence_terminal = species_manager->get_consensus_relevant_terminal();
156
157        if (sequence_terminal) {
158            int   seq_len;
159            char *seq          = sequence_terminal->resolve_pointer_to_string_copy(&seq_len);
160            ED4_returncode res = update_bases(0, 0, seq, seq_len);
161            free(seq);
162            return res;
163        }
164    }
165
166    e4_assert(!added_base->is_root_group_manager());
167    if (added_base->is_group_manager()) { // add a group
168        return update_bases(0, &added_base->to_group_manager()->table());
169    }
170
171    e4_assert(0); // wrong type
172   
173    return ED4_R_OK;
174}
175
176ED4_returncode ED4_manager::check_out_bases(ED4_base *subbed_base) {
177    if (subbed_base->is_species_manager()) { // sub a sequence
178        ED4_species_manager *species_manager = subbed_base->to_species_manager();
179        const ED4_terminal *sequence_terminal = species_manager->get_consensus_relevant_terminal();
180
181        if (sequence_terminal) {
182            int   seq_len;
183            char *seq          = sequence_terminal->resolve_pointer_to_string_copy(&seq_len);
184            ED4_returncode res = update_bases(seq, seq_len, (const char *)0, 0);
185            free(seq);
186            return res;
187        }
188    }
189    else {
190        e4_assert(!subbed_base->is_root_group_manager());
191        if (subbed_base->is_group_manager()) { // sub a group
192            return update_bases(&subbed_base->to_group_manager()->table(), 0);
193        }
194        e4_assert(0); // wrong type
195    }
196    return ED4_R_OK;
197}
198
199ED4_returncode ED4_manager::update_bases(const char *old_sequence, int old_len, const ED4_base *new_base, PosRange range) {
200    if (!new_base) {
201        return update_bases(old_sequence, old_len, 0, 0, range);
202    }
203
204    e4_assert(new_base->is_species_manager());
205
206    const ED4_species_manager *new_species_manager   = new_base->to_species_manager();
207    const ED4_terminal        *new_sequence_terminal = new_species_manager->get_consensus_relevant_terminal();
208
209    int   new_len;
210    char *new_sequence = new_sequence_terminal->resolve_pointer_to_string_copy(&new_len);
211
212    if (range.is_whole()) {
213        const PosRange *restricted = BaseFrequencies::changed_range(old_sequence, new_sequence, std::min(old_len, new_len));
214       
215        e4_assert(restricted);
216        range = *restricted;
217    }
218
219    ED4_returncode res = update_bases(old_sequence, old_len, new_sequence, new_len, range);
220    free(new_sequence);
221    return res;
222}
223
224ED4_returncode ED4_manager::update_bases_and_rebuild_consensi(const char *old_sequence, int old_len, ED4_base *new_base, ED4_update_flag update_flag, PosRange range) {
225    e4_assert(new_base);
226    e4_assert(new_base->is_species_manager());
227
228    ED4_species_manager *new_species_manager   = new_base->to_species_manager();
229    const ED4_terminal  *new_sequence_terminal = new_species_manager->get_consensus_relevant_terminal();
230
231    int         new_len;
232    const char *new_sequence = new_sequence_terminal->resolve_pointer_to_char_pntr(&new_len);
233
234#if defined(DEBUG) && 0
235    printf("old: %s\n", old_sequence);
236    printf("new: %s\n", new_sequence);
237#endif // DEBUG
238
239    const PosRange *changedRange = 0;
240    if (range.is_whole()) {
241        changedRange = BaseFrequencies::changed_range(old_sequence, new_sequence, std::min(old_len, new_len));
242    }
243    else {
244        changedRange = &range; // @@@ use method similar to changed_range here, which just reduces the existing range
245    }
246
247    ED4_returncode result = ED4_R_OK;
248    if (changedRange) {
249        ED4_returncode result1 = update_bases(old_sequence, old_len, new_sequence, new_len, *changedRange);
250        ED4_returncode result2 = rebuild_consensi(new_base, update_flag);
251
252        result = (result1 != ED4_R_OK) ? result1 : result2;
253
254        // Refresh aminoacid sequence terminals in Protein Viewer or protstruct // @@@ this is definitely wrong here (omg)
255        if (ED4_ROOT->alignment_type == GB_AT_DNA) {
256            PV_SequenceUpdate_CB(GB_CB_CHANGED);
257        }
258        else if (ED4_ROOT->alignment_type == GB_AT_AA) { 
259            GB_ERROR err = ED4_pfold_set_SAI(&ED4_ROOT->protstruct, GLOBAL_gb_main, ED4_ROOT->alignment_name, &ED4_ROOT->protstruct_len);
260            if (err) { aw_message(err); result = ED4_R_WARNING; }
261        }
262    }
263    return result;
264}
265
266ED4_returncode ED4_manager::update_bases(const ED4_base *old_base, const ED4_base *new_base, PosRange range) {
267    e4_assert(old_base);
268    e4_assert(new_base);
269
270    if (old_base->is_species_manager()) {
271        e4_assert(new_base->is_species_manager());
272        const ED4_species_manager *old_species_manager   = old_base->to_species_manager();
273        const ED4_species_manager *new_species_manager   = new_base->to_species_manager();
274        const ED4_terminal        *old_sequence_terminal = old_species_manager->get_consensus_relevant_terminal();
275        const ED4_terminal        *new_sequence_terminal = new_species_manager->get_consensus_relevant_terminal();
276
277        int   old_len;
278        int   new_len;
279        char *old_seq = old_sequence_terminal->resolve_pointer_to_string_copy(&old_len);
280        char *new_seq = new_sequence_terminal->resolve_pointer_to_string_copy(&new_len);
281
282        ED4_returncode res = update_bases(old_seq, old_len, new_seq, new_len, range);
283        free(new_seq);
284        free(old_seq);
285        return res;
286    }
287
288    e4_assert(!old_base->is_root_group_manager());
289    e4_assert(!new_base->is_root_group_manager());
290    if (old_base->is_group_manager()) {
291        e4_assert(new_base->is_group_manager());
292
293        return update_bases(&old_base->to_group_manager()->table(),
294                           &new_base->to_group_manager()->table(),
295                           range);
296    }
297
298    return ED4_R_OK;
299}
300
301// WITH_ALL_ABOVE_GROUP_MANAGER_TABLES performs a command with all groupmanager-tables
302// starting at walk_up (normally the current) until top (or until one table has an ignore flag)
303
304#define WITH_ALL_ABOVE_GROUP_MANAGER_TABLES(walk_up, COMMAND)                                   \
305    do {                                                                                        \
306        while (walk_up) {                                                                       \
307            if (walk_up->is_abstract_group_manager()) {                                         \
308                BaseFrequencies& char_table = walk_up->to_abstract_group_manager()->table();     \
309                char_table.COMMAND;                                                             \
310                if (char_table.is_ignored()) break; /* @@@ problematic */                       \
311            }                                                                                   \
312            walk_up = walk_up->parent;                                                          \
313        }                                                                                       \
314    } while (0)
315
316ED4_returncode ED4_manager::update_bases(const char *old_sequence, int old_len, const char *new_sequence, int new_len, PosRange range) {
317    ED4_manager *walk_up = this;
318
319    if (old_sequence) {
320        if (new_sequence) {
321            if (range.is_whole()) {
322                const PosRange *restricted = BaseFrequencies::changed_range(old_sequence, new_sequence, std::min(old_len, new_len));
323                if (!restricted) return ED4_R_OK;
324               
325                range = *restricted;
326            }
327
328#if defined(DEBUG) && 0
329            printf("update_bases(..., %i, %i)\n", start_pos, end_pos);
330#endif
331
332            WITH_ALL_ABOVE_GROUP_MANAGER_TABLES(walk_up, sub_and_add(old_sequence, new_sequence, range));
333        }
334        else {
335            e4_assert(range.is_whole());
336            WITH_ALL_ABOVE_GROUP_MANAGER_TABLES(walk_up, sub(old_sequence, old_len));
337        }
338    }
339    else {
340        if (new_sequence) {
341            e4_assert(range.is_whole());
342            WITH_ALL_ABOVE_GROUP_MANAGER_TABLES(walk_up, add(new_sequence, new_len));
343        }
344        else {
345            return ED4_R_OK;
346        }
347    }
348
349    ED4_ROOT->root_group_man->remap()->mark_compile_needed();
350    return ED4_R_OK;
351}
352
353ED4_returncode ED4_manager::update_bases(const BaseFrequencies *old_table, const BaseFrequencies *new_table, PosRange range) {
354    ED4_manager *walk_up = this;
355
356    if (old_table) {
357        if (new_table) {
358            if (range.is_whole()) {
359                const PosRange *restricted = old_table->changed_range(*new_table);
360                if (!restricted) return ED4_R_OK;
361
362                range = *restricted;
363            }
364            WITH_ALL_ABOVE_GROUP_MANAGER_TABLES(walk_up, sub_and_add(*old_table, *new_table, range));
365        }
366        else {
367            e4_assert(range.is_whole());
368            WITH_ALL_ABOVE_GROUP_MANAGER_TABLES(walk_up, sub(*old_table));
369        }
370    }
371    else {
372        if (new_table) {
373            e4_assert(range.is_whole());
374            WITH_ALL_ABOVE_GROUP_MANAGER_TABLES(walk_up, add(*new_table));
375        }
376        else {
377            return ED4_R_OK;
378        }
379    }
380
381    ED4_ROOT->root_group_man->remap()->mark_compile_needed();
382    return ED4_R_OK;
383}
384
385#undef WITH_ALL_ABOVE_GROUP_MANAGER_TABLES
386
387ED4_returncode ED4_manager::remove_callbacks() {
388    // removes callbacks
389    int i;
390
391    for (i=0; i < children->members(); i++) {
392        if (children->member(i)->is_terminal()) {
393            children->member(i)->to_terminal()->remove_callbacks();
394        }
395        else {
396            children->member(i)->to_manager()->remove_callbacks();
397        }
398    }
399
400    return ED4_R_OK;
401}
402
403void ED4_manager::update_consensus(ED4_manager *old_parent, ED4_manager *new_parent, ED4_base *sequence) {
404    if (old_parent) old_parent->check_out_bases(sequence);
405    if (new_parent) new_parent->check_in_bases(sequence);
406}
407
408ED4_terminal* ED4_manager::get_first_terminal(int start_index) const {
409    ED4_terminal *terminal = 0;
410
411    if (children) {
412        int i;
413        for (i=start_index; !terminal && i>=0 && i<children->members(); i++) {
414            ED4_base *base = children->member(i);
415            if (base->is_terminal()) {
416                terminal = base->to_terminal();
417            }
418            else {
419                terminal = base->to_manager()->get_first_terminal();
420            }
421        }
422    }
423
424    return terminal;
425}
426
427ED4_terminal* ED4_manager::get_last_terminal(int start_index) const {
428    ED4_terminal *terminal = 0;
429
430    if (children) {
431        int i;
432        if (start_index<0) start_index = children->members()-1;
433        for (i=start_index; !terminal && i>=0 && i<children->members(); i--) {
434            ED4_base *base = children->member(i);
435            if (base->is_terminal()) {
436                terminal = base->to_terminal();
437            }
438            else {
439                terminal = base->to_manager()->get_last_terminal();
440            }
441        }
442    }
443
444    return terminal;
445}
446
447ED4_base* ED4_manager::get_competent_child(AW_pos x, AW_pos y, ED4_properties relevant_prop) {
448    ED4_extension  ext;
449    ED4_index      temp_index;
450    ED4_base      *child;
451
452    ext.position[X_POS] = x;
453    ext.position[Y_POS] = y;
454    ED4_base::touch_world_cache();
455
456    temp_index = children->search_member(&ext, spec.static_prop);
457
458    if ((temp_index < 0) || (temp_index >= children->members())) {     // no child at given location
459        return (NULL);
460    }
461
462    child = children->member(temp_index);
463
464    return (child->spec.static_prop & relevant_prop) ? child : (ED4_base*)NULL;
465}
466
467ED4_base* ED4_manager::get_competent_clicked_child(AW_pos x, AW_pos y, ED4_properties relevant_prop) {
468    ED4_extension  ext;
469    ED4_base      *child;
470    ED4_base      *temp_parent = NULL;
471
472    ext.position[X_POS] = x;
473    ext.position[Y_POS] = y;
474    ED4_base::touch_world_cache();
475
476    children->search_target_species(&ext, spec.static_prop, &temp_parent, ED4_L_MULTI_SPECIES);
477
478    if (!temp_parent) {
479        temp_parent = this;
480    }
481
482    child = temp_parent;
483    if (child->spec.static_prop & relevant_prop) {
484        return (child);
485    }
486    else {
487        return (NULL);
488    }
489}
490
491ED4_returncode  ED4_manager::handle_move(ED4_move_info *mi) {
492    // handles a move request with target world
493    // coordinates within current object's borders
494
495    if ((mi == NULL) || (mi->object->spec.level <= spec.level)) {
496        return (ED4_R_IMPOSSIBLE);
497    }
498
499    AW_pos rel_x = mi->end_x; // calculate target position relative to current object
500    AW_pos rel_y = mi->end_y;
501    calc_rel_coords(&rel_x, &rel_y);
502
503    if ((mi->preferred_parent & spec.level) ||             // desired parent or levels match = > can handle moving
504        (mi->object->spec.level & spec.allowed_children)) // object(s) myself, take info from list of selected objects
505    {
506        ED4_base *object;
507        bool      i_am_consensus = false;
508
509        if (mi->object->dynamic_prop & ED4_P_IS_HANDLE) { // object is a handle for an object up in the hierarchy = > search it
510            ED4_level mlevel = mi->object->spec.handled_level;
511
512            object = mi->object;
513
514            while ((object != NULL) && !(object->spec.level & mlevel)) object = object->parent;
515            if (object == NULL) return (ED4_R_IMPOSSIBLE); // no target level found
516        }
517        else {
518            object = mi->object; // selected object is no handle => take it directly
519
520            if (object->is_consensus_manager()) {
521                if (this->is_child_of(object->parent)) return ED4_R_IMPOSSIBLE; // has to pass multi_species_manager
522                i_am_consensus = true;
523                if (object->parent != this) {
524                    object = object->parent->parent;
525                    mi->object = object;
526                }
527            }
528        }
529
530
531        ED4_manager *old_parent = object->parent;
532
533        AW_pos x_off = 0;
534
535        // now do move action => determine insertion offsets and insert object
536
537        if (ED4_ROOT->selected_objects->size()>1) {
538            ED4_selected_elem *list_elem = ED4_ROOT->selected_objects->head();
539            while (list_elem != NULL) {
540                ED4_selection_entry *sel_info   = list_elem->elem();
541                ED4_terminal        *sel_object = sel_info->object;
542
543                if (sel_object==mi->object) break;
544                if (spec.static_prop & ED4_P_VERTICAL) x_off += sel_info->actual_width;
545
546                list_elem = list_elem->next();
547            }
548        }
549
550        {
551            ED4_extension loc;
552            loc.position[Y_POS] = mi->end_y;
553            loc.position[X_POS] = mi->end_x;
554            ED4_base::touch_world_cache();
555
556            ED4_base *found_member = NULL;
557            {
558                ED4_manager *dummy_mark = ED4_ROOT->main_manager->search_spec_child_rek(ED4_L_DEVICE)->to_manager();
559                dummy_mark->children->search_target_species(&loc, ED4_P_HORIZONTAL, &found_member, ED4_L_NO_LEVEL);
560            }
561
562            if (found_member==object) { // if we are dropped on ourself => don't move
563                return ED4_R_BREAK;
564            }
565        }
566
567        {
568            ED4_base *parent_man = object->get_parent(ED4_L_MULTI_SPECIES);
569            object->parent->children->remove_member(object);
570            parent_man->to_multi_species_manager()->invalidate_species_counters();
571        }
572
573        object->extension.position[X_POS] = rel_x + x_off;
574        object->extension.position[Y_POS] = rel_y;
575        ED4_base::touch_world_cache();
576
577        object->parent = this;
578
579        if (old_parent != this) { // check whether consensus has to be calculated new or not
580            GB_push_transaction(GLOBAL_gb_main);
581            update_consensus(old_parent, this, object);
582            rebuild_consensi(old_parent, ED4_U_UP);
583            rebuild_consensi(this, ED4_U_UP);
584            GB_pop_transaction(GLOBAL_gb_main);
585        }
586
587        if ((i_am_consensus && object->parent != old_parent) || !i_am_consensus) {
588            object->set_width();
589
590            if (dynamic_prop & ED4_P_IS_FOLDED) { // add spacer and consensusheight
591                object->extension.position[Y_POS] = children->member(0)->extension.size[HEIGHT] + children->member(1)->extension.size[HEIGHT];
592                object->flag.hidden = 1;
593                ED4_base::touch_world_cache();
594            }
595        }
596
597        children->insert_member(object);
598        if (is_multi_species_manager()) {
599            to_multi_species_manager()->invalidate_species_counters();
600        }
601        else {
602            get_parent(ED4_L_MULTI_SPECIES)->to_multi_species_manager()->invalidate_species_counters();
603        }
604
605        return (ED4_R_OK);
606    }
607    else {
608        // levels do not match = > ask competent manager child to handle move request
609        ED4_manager *manager = (ED4_manager *) get_competent_child(rel_x, rel_y, ED4_P_IS_MANAGER);
610        if (!manager) return ED4_R_IMPOSSIBLE; // no manager child covering target location = > move not possible
611        return manager->move_requested_by_parent(mi); // there is a manager child covering target location = > pass on move request
612    }
613}
614
615
616ED4_returncode  ED4_manager::move_requested_by_parent(ED4_move_info *mi) {
617    // handles a move request with target world coordinates coming from parent
618    if ((mi == NULL) || !(in_border(mi->end_x, mi->end_y, mi->mode)))
619        return (ED4_R_IMPOSSIBLE);
620
621    return (handle_move(mi));
622}
623
624
625ED4_returncode  ED4_manager::move_requested_by_child(ED4_move_info *mi) {
626    // handles a move request coming from a child,
627    // target location can be out of borders
628    ED4_base *temp_parent = NULL;
629
630    if (mi == NULL)
631        return (ED4_R_IMPOSSIBLE);
632
633    if (spec.level < mi->object->spec.restriction_level) return (ED4_R_IMPOSSIBLE); // check if there is a level restriction to the move request
634
635    if (mi->object->dynamic_prop & ED4_P_IS_HANDLE) { // determine first if we could be the moving object
636        if ((dynamic_prop & ED4_P_MOVABLE) && (spec.level & mi->object->spec.handled_level)) { // yes, we are meant to be the moving object
637            mi->object = this;
638        }
639
640        if (parent == NULL) return (ED4_R_WARNING);
641
642        return (parent->move_requested_by_child(mi));
643    }
644
645    if (!(in_border(mi->end_x, mi->end_y, mi->mode))) {
646        // determine if target location is within
647        // the borders of current object, do boundary
648        // adjustment of target coordinates if necessary
649        // target location is not within borders =>
650        // ask parent, i.e. move recursively up
651        if (parent == NULL) return (ED4_R_WARNING);
652        return (parent->move_requested_by_child(mi));
653    }
654    else { // target location within current borders = > handle move myself
655        temp_parent = get_competent_clicked_child(mi->end_x, mi->end_y, ED4_P_IS_MANAGER);
656
657        if (!temp_parent) {
658            return (handle_move(mi));
659        }
660        else {
661            if ((temp_parent->is_group_manager()) || (temp_parent->is_area_manager())) {
662                temp_parent = temp_parent->to_group_manager()->get_defined_level(ED4_L_MULTI_SPECIES);
663            }
664            else if (!(temp_parent->is_multi_species_manager())) {
665                temp_parent = temp_parent->get_parent(ED4_L_MULTI_SPECIES);
666            }
667            if (!temp_parent) {
668                return ED4_R_IMPOSSIBLE;
669            }
670            return (temp_parent->handle_move(mi));
671        }
672    }
673}
674
675ED4_returncode  ED4_manager::event_sent_by_parent(AW_event *event, AW_window *aww) {
676    // handles an input event coming from parent
677    ED4_extension  ext;
678    ED4_index      temp_index;
679    ED4_returncode returncode;
680
681    if (flag.hidden)
682        return ED4_R_BREAK;
683
684    ext.position[X_POS] = event->x;
685    ext.position[Y_POS] = event->y;
686
687    calc_rel_coords(&ext.position[X_POS], &ext.position[Y_POS]);
688
689    temp_index = children->search_member(&ext, spec.static_prop); // search child who is competent for the location of given event
690
691    if ((temp_index >= children->members()) || (temp_index < 0)) {
692        return (ED4_R_IMPOSSIBLE); // no suitable member found
693    }
694
695    returncode = children->member(temp_index)->event_sent_by_parent(event, aww);
696    return (returncode);
697}
698
699ED4_returncode  ED4_manager::refresh_requested_by_child() {
700    // handles a refresh-request from a child
701    if (!update_info.refresh) { // determine if there were more refresh requests already => no need to tell parent about it
702        update_info.set_refresh(1); // this is the first refresh request
703        if (parent) parent->refresh_requested_by_child(); // if we have a parent, tell him about the refresh request
704    }
705
706#ifdef TEST_REFRESH_FLAG
707    e4_assert(refresh_flag_ok());
708#endif
709    return ED4_R_OK;
710}
711
712int ED4_manager::refresh_flag_ok() {
713    ED4_index i;
714
715    for (i=0; i<children->members(); i++) {
716        ED4_base *child = children->member(i);
717
718        if (child->is_manager()) {
719            if (!child->to_manager()->refresh_flag_ok()) {
720                return 0;
721            }
722        }
723
724        if (child->update_info.refresh==1 && update_info.refresh==0) {
725            printf("Forgotten refresh-flag in '%s' (son of '%s')\n", child->id, id);
726            fflush(stdout);
727            return 0;
728        }
729    }
730
731    return 1;
732}
733
734inline void ED4_base::resize_requested_by_link(ED4_base *IF_ASSERTION_USED(link)) {
735    e4_assert((width_link == link) || (height_link == link)); // wrong link
736    if (calc_bounding_box()) request_resize();
737}
738
739void ED4_base::request_resize_of_linked() {
740    if (linked_objects) {
741        ED4_base_list_elem *current_list_elem = linked_objects->head();
742        while (current_list_elem) {
743            ED4_base *object = current_list_elem->elem();
744            object->resize_requested_by_link(this);
745            current_list_elem = current_list_elem->next();
746        }
747    }
748}
749
750bool ED4_manager::calc_bounding_box() {
751    // calculates the smallest rectangle containing the object.
752    // returns true if bounding box has changed.
753    AW_pos     sum_width  = 0;
754    AW_pos     sum_height = 0;
755    AW_pos     max_x      = 0;
756    AW_pos     max_y      = 0;
757    AW_pos     dummy      = 0;
758    ED4_index  i          = 0;
759    bool       bb_changed = false;
760    ED4_base  *child;
761
762    // initialize with first child
763    while ((child = children->member(i++)) != NULL) { // check all children
764        if (!child->flag.hidden) {
765            sum_width  += child->extension.size[WIDTH];
766            sum_height += child->extension.size[HEIGHT];
767
768            dummy = child->extension.position[X_POS] + child->extension.size[WIDTH];
769            if (dummy > max_x) {
770                max_x = dummy;
771            }
772
773            dummy = child->extension.position[Y_POS] + child->extension.size[HEIGHT];
774            if (dummy > max_y) {
775                max_y = dummy;
776            }
777            ED4_base::touch_world_cache();
778        }
779    }
780
781
782    if (spec.static_prop & ED4_P_HORIZONTAL) {
783        if (int(extension.size[WIDTH]) != int(max_x)) { // because compares between floats fail sometimes (AW_pos==float)
784            extension.size[WIDTH] = max_x;
785            bb_changed = true;
786        }
787
788        if (int(extension.size[HEIGHT]) != int(sum_height)) {
789            extension.size[HEIGHT] = sum_height;
790            bb_changed = true;
791        }
792    }
793
794    if (spec.static_prop & ED4_P_VERTICAL) {
795        if (int(extension.size[WIDTH]) != int(sum_width)) {
796            extension.size[WIDTH] = sum_width;
797            bb_changed = true;
798        }
799        if (int(extension.size[HEIGHT]) != int(max_y)) {
800            extension.size[HEIGHT] = max_y;
801            bb_changed = true;
802        }
803    }
804
805    if (bb_changed) request_resize_of_linked();
806    return bb_changed;
807}
808
809ED4_returncode ED4_manager::distribute_children() {
810    // distributes all children of current object according to current object's properties and
811    // justification value; a recalculation of current object's extension will take place if necessary
812
813    ED4_index  current_index;
814    ED4_index  rel_pos        = 0;
815    ED4_index  rel_size       = 0;
816    ED4_index  other_pos      = 0;
817    ED4_index  other_size     = 0;
818    AW_pos     max_rel_size   = 0;
819    AW_pos     max_other_size = 0;
820    ED4_base  *current_child;
821
822    // set extension-indexes rel_pos and rel_size according to properties
823    if (spec.static_prop & ED4_P_HORIZONTAL) {
824        rel_pos    = X_POS;
825        other_pos  = Y_POS;
826        rel_size   = WIDTH;
827        other_size = HEIGHT;
828    }
829    if (spec.static_prop & ED4_P_VERTICAL) {
830        rel_pos    = Y_POS;
831        other_pos  = X_POS;
832        rel_size   = HEIGHT;
833        other_size = WIDTH;
834    }
835
836    current_index = 0;  // get maximal relevant and other size of children, set children's other position increasingly
837    while ((current_child = children->member(current_index)) != NULL) {
838        max_rel_size = std::max(int(max_rel_size), int(current_child->extension.size[rel_size]));
839        if (current_child->extension.position[other_pos] != max_other_size) {
840            current_child->extension.position[other_pos] = max_other_size;
841            ED4_base::touch_world_cache();
842        }
843        max_other_size += current_child->extension.size[other_size];
844        current_index++;
845    }
846
847    // set children's relevant position according to justification value
848    // (0.0 means top- or left-justified, 1.0 means bottom- or right-justified)
849    current_index = 0;
850    while ((current_child = children->member(current_index++)) != NULL) {
851        current_child->extension.position[rel_pos] = 0.0;
852        ED4_base::touch_world_cache();
853    }
854
855    refresh_requested_by_child();
856    return (ED4_R_OK);
857}
858
859void ED4_manager::resize_requested_children() {
860    if (update_info.resize) { // object wants to resize
861        update_info.set_resize(0); // first clear the resize flag (remember it could be set again from somewhere below the hierarchy)
862
863        ED4_index i = 0;
864        while (1) {
865            ED4_base *child = children->member(i++);
866
867            if (!child) break;
868
869            child->update_info.set_resize(1);
870            child->resize_requested_children();
871        }
872
873        distribute_children();
874        if (calc_bounding_box()) request_resize();
875    }
876}
877void ED4_root_group_manager::resize_requested_children() {
878    if (update_info.resize) {
879        if (update_remap()) ED4_ROOT->request_refresh_for_specific_terminals(ED4_L_SEQUENCE_STRING);
880        ED4_manager::resize_requested_children();
881    }
882    else {
883        e4_assert(!update_remap());
884    }
885}
886
887static void update_scrolled_rectangles(ED4_window *win) { win->update_scrolled_rectangle(); }
888void ED4_main_manager::resize_requested_children() {
889    if (update_info.resize) {
890        ED4_manager::resize_requested_children();
891        ED4_with_all_edit_windows(update_scrolled_rectangles);
892    }
893}
894
895ED4_returncode ED4_main_manager::Show(int refresh_all, int is_cleared) {
896#ifdef TEST_REFRESH_FLAG
897    e4_assert(refresh_flag_ok());
898#endif
899
900    AW_device *device = current_device();
901
902    if (!flag.hidden && (refresh_all || update_info.refresh)) {
903#if defined(TRACE_REFRESH)
904        fprintf(stderr, "- really paint in ED4_main_manager::Show(refresh_all=%i, is_cleared=%i)\n", refresh_all, is_cleared); fflush(stderr);
905#endif
906        const AW_screen_area& area_rect = device->get_area_size();
907
908        // if update all -> clear_background
909
910        if (update_info.clear_at_refresh && !is_cleared) {
911            device->push_clip_scale();
912            if (device->reduceClipBorders(area_rect.t, area_rect.b, area_rect.l, area_rect.r)) {
913                clear_background();
914            }
915            is_cleared = 1;
916            device->pop_clip_scale();
917        }
918
919        // loop through all rectangles between folding lines:
920
921        int x1, y1, x2, y2;
922        ED4_window& win = *current_ed4w();
923        x1 = area_rect.l;
924        for (const ED4_folding_line *flv = win.get_vertical_folding(); ; flv = flv->get_next()) {
925            if (flv) {
926                x2 = int(flv->get_pos()); // @@@ use AW_INT ?
927            }
928            else {
929                x2 = area_rect.r;
930                if (x1==x2) break; // do not draw last range, if it's only 1 pixel width
931            }
932
933            y1 = area_rect.t;
934            for (const ED4_folding_line *flh = win.get_horizontal_folding(); ; flh = flh->get_next()) {
935                if (flh) {
936                    y2 = int(flh->get_pos()); // @@@ use AW_INT ?
937                }
938                else {
939                    y2 = area_rect.b;
940                    if (y1==y2) break; // do not draw last range, if it's only 1 pixel high
941                }
942
943                device->push_clip_scale();
944                if (device->reduceClipBorders(y1, y2-1, x1, x2-1)) {
945                    ED4_manager::Show(refresh_all, is_cleared);
946                }
947                device->pop_clip_scale();
948
949                if (!flh) break; // break out after drawing lowest range
950                y1 = y2;
951            }
952            if (!flv) break; // break out after drawing rightmost range
953
954            x1 = x2;
955        }
956
957        // to avoid text clipping problems between top and middle area we redraw the top-middle-spacer :
958        {
959            device->push_clip_scale();
960            const AW_screen_area& clip_rect = device->get_cliprect();
961            device->set_top_clip_border(clip_rect.t-TERMINALHEIGHT);
962
963            int char_width = ED4_ROOT->font_group.get_max_width();
964            device->set_left_clip_border(clip_rect.l-char_width);
965            device->set_right_clip_border(clip_rect.r+char_width);
966
967            get_top_middle_spacer_terminal()->Show(true, false);
968            get_top_middle_line_terminal()->Show(true, false);
969            device->pop_clip_scale();
970        }
971
972        // always draw cursor
973        ED4_cursor& cursor = current_cursor();
974        if (cursor.owner_of_cursor && cursor.allowed_to_draw) {
975            if (cursor.is_partly_visible()) {
976                cursor.ShowCursor(0, ED4_C_NONE, 0);
977            }
978        }
979    }
980#ifdef TEST_REFRESH_FLAG
981    e4_assert(refresh_flag_ok());
982#endif
983
984    return ED4_R_OK;
985}
986
987
988ED4_returncode ED4_root_group_manager::Show(int refresh_all, int is_cleared) {
989    if (update_remap()) { // @@@ dont call here ?
990#if defined(TRACE_REFRESH)
991        printf("map updated in ED4_root_group_manager::Show (bad?)\n");
992#endif
993    }
994    return ED4_manager::Show(refresh_all, is_cleared);
995}
996
997ED4_returncode ED4_manager::Show(int refresh_all, int is_cleared) {
998#ifdef TEST_REFRESH_FLAG
999    e4_assert(refresh_flag_ok());
1000#endif
1001
1002    if (!flag.hidden && (refresh_all || update_info.refresh)) {
1003        if (update_info.clear_at_refresh && !is_cleared) {
1004            clear_background();
1005            is_cleared = 1;
1006        }
1007
1008        AW_screen_area rect; // clipped rectangle in world coordinates
1009
1010        {
1011            const AW_screen_area &clip_rect = current_device()->get_cliprect();      // clipped rectangle in win coordinates
1012           
1013            double x, y;
1014            x = clip_rect.l;
1015            y = clip_rect.t;
1016
1017            current_ed4w()->win_to_world_coords(&x, &y);
1018
1019            rect.l = int(x);
1020            rect.t = int(y);
1021
1022            e4_assert(AW::nearlyEqual(current_device()->get_scale(), 1.0)); // assumed by calculation below
1023            rect.r = rect.l+(clip_rect.r-clip_rect.l);
1024            rect.b = rect.t+(clip_rect.b-clip_rect.t);
1025        }
1026
1027        // binary search to find first visible child
1028
1029        int first_visible_child = 0; //@@@FIXME: this variable is never again set
1030
1031        {
1032            int l = 0;
1033            int h = children->members()-1;
1034
1035            while (l<h) {
1036
1037                while (children->member(l)->flag.hidden && l<h) l++;
1038                while (children->member(h)->flag.hidden && l<h) h--;
1039
1040                int m = (l+h)/2;
1041                int min_m = m;
1042                int max_m = m+1;
1043
1044                while (children->member(m)->flag.hidden) {
1045                    if (m==h) {
1046                        m = (l+h)/2-1;
1047                        while (children->member(m)->flag.hidden) {
1048                            if (m==l) {
1049                                // all children between l..h are flag.hidden
1050                                goto no_visible_child_found;
1051                            }
1052                            m--;
1053                        }
1054                        min_m = m;
1055                        break;
1056                    }
1057                    m++;
1058                    max_m = m;
1059                }
1060
1061                ED4_base *child = children->member(m);
1062                e4_assert(!child->flag.hidden);
1063
1064                AW_pos x, y;
1065                child->calc_world_coords(&x, &y);
1066
1067                if (spec.static_prop & ED4_P_HORIZONTAL) { // horizontal manager
1068                    e4_assert((spec.static_prop&ED4_P_VERTICAL)==0);   // otherwise this binary search will not work correctly
1069                    if ((x+child->extension.size[WIDTH])<=rect.l) { // left of clipping range
1070                        l = max_m;
1071                    }
1072                    else {
1073                        h = min_m;
1074                    }
1075                }
1076                else if (spec.static_prop & ED4_P_VERTICAL) { // vertical manager
1077                    if ((y+child->extension.size[HEIGHT])<=rect.t) { // above clipping range
1078                        l = max_m;
1079                    }
1080                    else {
1081                        h = min_m;
1082                    }
1083                }
1084                else {
1085                    e4_assert(0);
1086                }
1087            }
1088        }
1089
1090    no_visible_child_found :
1091
1092        ED4_index i = 0;
1093
1094        while (1) {
1095            ED4_base *child = children->member(i++);
1096            if (!child) break;
1097
1098            if (!child->flag.hidden && (refresh_all || child->update_info.refresh) && i>=first_visible_child) {
1099                AW_pos x, y;
1100                child->calc_world_coords(&x, &y);
1101
1102                AW_device *device = current_device();
1103
1104                if (!(((y-rect.b)>0.5) ||
1105                      ((rect.t-(y+child->extension.size[HEIGHT]-1))>0.5) ||
1106                      ((x-rect.r)>0.5) ||
1107                      ((rect.l-(x+child->extension.size[WIDTH]-1))>0.5)
1108                     ))
1109                {
1110                    // they overlap -> show it
1111                    device->push_clip_scale();
1112                    if (child->adjust_clipping_rectangle()) {
1113                        child->Show(refresh_all, is_cleared);
1114                    }
1115                    device->pop_clip_scale();
1116                }
1117            }
1118        }
1119    }
1120
1121#ifdef TEST_REFRESH_FLAG
1122    e4_assert(refresh_flag_ok());
1123#endif
1124
1125    return ED4_R_OK;
1126}
1127
1128ED4_returncode ED4_manager::clear_refresh() {
1129    e4_assert(update_info.refresh);
1130
1131    for (int i=0; i<children->members(); i++) {
1132        ED4_base *child = children->member(i);
1133
1134        if (child->update_info.refresh) {
1135            if (child->is_manager()) {
1136                child->to_manager()->clear_refresh();
1137            }
1138            else {
1139                child->update_info.set_refresh(0);
1140                child->update_info.set_clear_at_refresh(0);
1141            }
1142        }
1143    }
1144
1145    update_info.set_refresh(0);
1146    update_info.set_clear_at_refresh(0);
1147
1148    return ED4_R_OK;
1149}
1150
1151void ED4_manager::update_requested_by_child() { // @@@ same as set_update -> DRY
1152    if (!update_info.update_requested) {
1153        if (parent) parent->update_requested_by_child();
1154        update_info.update_requested = 1;
1155    }
1156}
1157void ED4_manager::delete_requested_by_child() {
1158    if (!update_info.delete_requested) {
1159        if (parent) parent->delete_requested_by_child();
1160        update_info.delete_requested = 1;
1161    }
1162}
1163void ED4_terminal::delete_requested_children() {
1164    e4_assert(update_info.delete_requested);
1165    e4_assert(tflag.deleted);
1166
1167    ED4_ROOT->announce_deletion(this);
1168
1169    unlink_from_parent();
1170    delete this;
1171}
1172
1173void ED4_manager::update_requested_children() {
1174    e4_assert(update_info.update_requested);
1175
1176    for (int i=0; i<children->members(); i++) {
1177        ED4_base *child = children->member(i);
1178        if (child->update_info.update_requested) {
1179            child->update_requested_children();
1180        }
1181    }
1182
1183    update_info.update_requested = 0;
1184}
1185
1186void ED4_multi_species_manager::update_requested_children() {
1187    e4_assert(update_info.update_requested);
1188    ED4_manager::update_requested_children();
1189    update_species_counters();
1190    update_group_id();
1191
1192    ED4_base *group_base = get_parent(ED4_L_GROUP);
1193    if (group_base) {
1194        e4_assert(group_base->is_group_manager());
1195        e4_assert(!group_base->is_root_group_manager());
1196
1197        ED4_group_manager *group_man    = parent->to_group_manager();
1198        ED4_base          *bracket_base = group_man->get_defined_level(ED4_L_BRACKET);
1199
1200        if (bracket_base) bracket_base->request_refresh();
1201    }
1202}
1203
1204void ED4_manager::delete_requested_children() {
1205    e4_assert(update_info.delete_requested);
1206
1207    for (int i = children->members()-1; i >= 0; --i) {
1208        ED4_base *child = children->member(i);
1209        if (child->update_info.delete_requested) {
1210            child->delete_requested_children();
1211        }
1212    }
1213
1214    update_info.delete_requested = 0;
1215
1216    if (!children->members()) {
1217        ED4_ROOT->announce_deletion(this);
1218       
1219        unlink_from_parent();
1220        delete this;
1221    }
1222}
1223
1224void ED4_multi_species_manager::delete_requested_children() {
1225    e4_assert(update_info.delete_requested);
1226    invalidate_species_counters();
1227    ED4_manager::delete_requested_children();
1228}
1229
1230void ED4_terminal::Delete() {
1231    if (!tflag.deleted) {
1232        tflag.deleted                = 1;
1233        update_info.delete_requested = 1;
1234        parent->delete_requested_by_child();
1235    }
1236}
1237
1238void ED4_manager::Delete() {
1239    delete_cbs.call(this);
1240    delete_cbs.clear();
1241
1242    for (int i=0; i<children->members(); i++) {
1243        children->member(i)->Delete();
1244    }
1245}
1246
1247void ED4_manager::request_refresh(int clear) {
1248    // sets refresh flag of current object and its children
1249    update_info.set_refresh(1);
1250    update_info.set_clear_at_refresh(clear);
1251
1252    if (parent) parent->refresh_requested_by_child();
1253
1254    ED4_index  current_index = 0;
1255    ED4_base  *current_child;
1256    while ((current_child = children->member(current_index++)) != NULL) {
1257        current_child->request_refresh(0); // do not trigger clear for childs
1258    }
1259}
1260
1261
1262ED4_base* ED4_manager::search_ID(const char *temp_id) {
1263    if (strcmp(temp_id, id) == 0) return this; // this object is the sought one
1264
1265    ED4_index  current_index = 0;
1266    ED4_base  *current_child;
1267    while ((current_child = children->member(current_index))) { // search whole memberlist recursively for object with the given id
1268        ED4_base *object = current_child->search_ID(temp_id);
1269        if (object) return object;
1270        current_index++;
1271    }
1272
1273    return NULL; // no object found
1274}
1275
1276
1277ED4_manager::ED4_manager(const ED4_objspec& spec_, const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent) :
1278    ED4_base(spec_, temp_id, x, y, width, height, temp_parent)
1279{
1280    children = new ED4_members(this);
1281}
1282
1283ED4_manager::~ED4_manager() {
1284    ED4_base *current_child;
1285
1286    while (children->members() > 0) {
1287        current_child = children->member(0);
1288        children->remove_member(current_child);
1289        current_child->parent = NULL;
1290
1291        if (current_child->is_terminal())       delete current_child->to_terminal();
1292        else if (current_child->is_manager())   delete current_child->to_manager();
1293    }
1294
1295    delete children;
1296}
1297
1298// --------------------------
1299//      ED4_main_manager
1300
1301ED4_main_manager::ED4_main_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent) :
1302    ED4_manager(main_manager_spec, temp_id, x, y, width, height, temp_parent),
1303    top_middle_line(NULL), 
1304    top_middle_spacer(NULL)
1305{
1306}
1307
1308// ----------------------------
1309//      ED4_device_manager
1310
1311ED4_device_manager::ED4_device_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent) :
1312    ED4_manager(device_manager_spec, temp_id, x, y, width, height, temp_parent)
1313{
1314}
1315
1316// --------------------------
1317//      ED4_area_manager
1318
1319ED4_area_manager::ED4_area_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent) :
1320    ED4_manager(area_manager_spec, temp_id, x, y, width, height, temp_parent)
1321{
1322}
1323
1324// -----------------------------------
1325//      ED4_multi_species_manager
1326
1327ED4_multi_species_manager::ED4_multi_species_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent) :
1328    ED4_manager(multi_species_manager_spec, temp_id, x, y, width, height, temp_parent),
1329    species(-1),
1330    selected_species(-1)
1331{
1332}
1333
1334int ED4_multi_species_manager::get_no_of_species() {
1335    if (!has_valid_counters()) {
1336        update_species_counters();
1337        e4_assert(has_valid_counters());
1338    }
1339    return species;
1340}
1341
1342int ED4_multi_species_manager::get_no_of_selected_species() {
1343    if (!has_valid_counters()) {
1344        update_species_counters();
1345        e4_assert(has_valid_counters());
1346    }
1347    return selected_species;
1348}
1349
1350void ED4_multi_species_manager::invalidate_species_counters() {
1351    if (has_valid_counters()) {
1352        species          = -1;
1353        selected_species = -1;
1354
1355        ED4_base *pms = get_parent(ED4_L_MULTI_SPECIES);
1356        if (pms) pms->to_multi_species_manager()->invalidate_species_counters();
1357       
1358        update_requested_by_child();
1359    }
1360}
1361
1362void ED4_multi_species_manager::set_species_counters(int no_of_species, int no_of_selected) {
1363
1364#if defined(DEBUG)
1365    int sp, sel;
1366
1367    count_species(&sp, &sel);
1368    e4_assert(no_of_species==sp);
1369    e4_assert(no_of_selected==sel);
1370#endif
1371
1372    e4_assert(no_of_species>=no_of_selected);
1373
1374    if (species!=no_of_species || selected_species!=no_of_selected) {
1375        int species_diff  = no_of_species-species;
1376        int selected_diff = no_of_selected-selected_species;
1377
1378        int quickSet = 1;
1379        if (species==-1 || selected_species==-1) {
1380            quickSet = 0;
1381        }
1382
1383        species          = no_of_species;
1384        selected_species = no_of_selected;
1385
1386        ED4_base *gm = get_parent(ED4_L_GROUP);
1387        if (gm) gm->to_manager()->search_spec_child_rek(ED4_L_BRACKET)->request_refresh();
1388
1389        ED4_base *ms = get_parent(ED4_L_MULTI_SPECIES);
1390        if (ms) {
1391            ED4_multi_species_manager *parent_multi_species_man = ms->to_multi_species_manager();
1392
1393            if (!quickSet) parent_multi_species_man->invalidate_species_counters();
1394
1395            if (parent_multi_species_man->has_valid_counters()) {
1396                parent_multi_species_man->set_species_counters(parent_multi_species_man->species+species_diff,
1397                                                               parent_multi_species_man->selected_species+selected_diff);
1398            }
1399        }
1400    }
1401}
1402
1403#ifdef DEBUG
1404void ED4_multi_species_manager::count_species(int *speciesPtr, int *selectedPtr) const {
1405    int m;
1406    int sp  = 0;
1407    int sel = 0;
1408
1409    for (m=0; m<children->members(); m++) {
1410        ED4_base *member = children->member(m);
1411
1412        if (member->is_group_manager()) {
1413            ED4_multi_species_manager *multi_species_man = member->to_group_manager()->get_multi_species_manager();
1414
1415            if (!multi_species_man->has_valid_counters()) {
1416                int sp1, sel1;
1417
1418                multi_species_man->count_species(&sp1, &sel1);
1419                sel += sel1;
1420                sp += sp1;
1421            }
1422            else {
1423                sel += multi_species_man->get_no_of_selected_species();
1424                sp += multi_species_man->get_no_of_species();
1425            }
1426        }
1427        else if (member->is_species_manager()) {
1428            ED4_species_manager *species_man = member->to_species_manager();
1429            if (!species_man->is_consensus_manager()) {
1430                sp++;
1431                if (species_man->is_selected()) sel++;
1432            }
1433        }
1434    }
1435
1436    *speciesPtr = sp;
1437    *selectedPtr = sel;
1438}
1439#endif
1440
1441void ED4_multi_species_manager::update_species_counters() {
1442    int m;
1443    int sp  = 0;
1444    int sel = 0;
1445
1446    for (m=0; m<children->members(); m++) {
1447        ED4_base *member = children->member(m);
1448
1449        if (member->is_group_manager()) {
1450            ED4_multi_species_manager *multi_species_man = member->to_group_manager()->get_multi_species_manager();
1451
1452            if (!multi_species_man->has_valid_counters()) {
1453                multi_species_man->update_species_counters();
1454            }
1455            sel += multi_species_man->get_no_of_selected_species();
1456            sp += multi_species_man->get_no_of_species();
1457        }
1458        else if (member->is_species_manager()) {
1459            ED4_species_manager *species_man = member->to_species_manager();
1460
1461            if (!species_man->is_consensus_manager()) {
1462                sp++;
1463                if (species_man->is_selected()) sel++;
1464            }
1465        }
1466    }
1467    set_species_counters(sp, sel);
1468}
1469
1470void ED4_multi_species_manager::select_all(bool only_species) {
1471    int m;
1472    int sp  = 0;
1473    int sel = 0;
1474
1475    for (m=0; m<children->members(); m++) {
1476        ED4_base *member = children->member(m);
1477
1478        if (member->is_group_manager()) {
1479            ED4_multi_species_manager *multi_species_man = member->to_group_manager()->get_multi_species_manager();
1480            multi_species_man->select_all(only_species);
1481            sp += multi_species_man->get_no_of_species();
1482            sel += multi_species_man->get_no_of_selected_species();
1483        }
1484        else if (member->is_species_manager()) {
1485            ED4_species_manager *species_man = member->to_species_manager();
1486
1487            if (!species_man->is_consensus_manager()) {
1488                sp++;
1489                if (!species_man->is_selected()) {
1490                    if (!only_species || !species_man->is_SAI_manager()) {
1491                        ED4_species_name_terminal *species_name = species_man->search_spec_child_rek(ED4_L_SPECIES_NAME)->to_species_name_terminal();
1492                        ED4_ROOT->add_to_selected(species_name);
1493                    }
1494                }
1495                if (species_man->is_selected()) sel++;
1496            }
1497        }
1498    }
1499    set_species_counters(sp, sel);
1500}
1501void ED4_multi_species_manager::deselect_all_species_and_SAI() {
1502    int m;
1503    int sp = 0;
1504
1505    for (m=0; m<children->members(); m++) {
1506        ED4_base *member = children->member(m);
1507
1508        if (member->is_group_manager()) {
1509            ED4_multi_species_manager *multi_species_man = member->to_group_manager()->get_multi_species_manager();
1510            multi_species_man->deselect_all_species_and_SAI();
1511            sp += multi_species_man->get_no_of_species();
1512        }
1513        else if (member->is_species_manager()) {
1514            ED4_species_manager *species_man = member->to_species_manager();
1515
1516            if (!species_man->is_consensus_manager()) {
1517                sp++;
1518                if (species_man->is_selected()) {
1519                    ED4_species_name_terminal *species_name = species_man->search_spec_child_rek(ED4_L_SPECIES_NAME)->to_species_name_terminal();
1520                    ED4_ROOT->remove_from_selected(species_name);
1521                }
1522            }
1523        }
1524        else {
1525            e4_assert(!member->is_manager());
1526        }
1527    }
1528    set_species_counters(sp, 0);
1529}
1530void ED4_multi_species_manager::invert_selection_of_all_species() {
1531    int m;
1532    int sp  = 0;
1533    int sel = 0;
1534
1535    for (m=0; m<children->members(); m++) {
1536        ED4_base *member = children->member(m);
1537
1538        if (member->is_group_manager()) {
1539            ED4_multi_species_manager *multi_species_man = member->to_group_manager()->get_multi_species_manager();
1540            multi_species_man->invert_selection_of_all_species();
1541            sp += multi_species_man->get_no_of_species();
1542            sel += multi_species_man->get_no_of_selected_species();
1543        }
1544        else if (member->is_species_manager()) {
1545            ED4_species_manager *species_man = member->to_species_manager();
1546
1547            if (!species_man->is_consensus_manager()) {
1548                sp++;
1549
1550                if (!species_man->is_SAI_manager()) {
1551                    ED4_species_name_terminal *species_name = species_man->search_spec_child_rek(ED4_L_SPECIES_NAME)->to_species_name_terminal();
1552
1553                    if (species_man->is_selected()) ED4_ROOT->remove_from_selected(species_name);
1554                    else                            ED4_ROOT->add_to_selected(species_name);
1555                }
1556                if (species_man->is_selected()) sel++;
1557            }
1558        }
1559        else {
1560            e4_assert(!member->is_manager());
1561        }
1562    }
1563
1564    e4_assert(get_no_of_selected_species()==sel);
1565    e4_assert(get_no_of_species()==sp);
1566}
1567void ED4_multi_species_manager::marked_species_select(bool select) {
1568    int m;
1569    int sp  = 0;
1570    int sel = 0;
1571
1572    for (m=0; m<children->members(); m++) {
1573        ED4_base *member = children->member(m);
1574
1575        if (member->is_group_manager()) {
1576            ED4_multi_species_manager *multi_species_man = member->to_group_manager()->get_multi_species_manager();
1577            multi_species_man->marked_species_select(select);
1578            sp += multi_species_man->get_no_of_species();
1579            sel += multi_species_man->get_no_of_selected_species();
1580        }
1581        else if (member->is_species_manager()) {
1582            ED4_species_manager *species_man = member->to_species_manager();
1583
1584            if (!species_man->is_consensus_manager()) {
1585                sp++;
1586
1587                if (species_man->is_species_seq_manager()) {
1588                    GBDATA *gbd = species_man->get_species_pointer();
1589                    e4_assert(gbd);
1590                    int is_marked = GB_read_flag(gbd);
1591
1592                    if (is_marked) {
1593                        if (select) { // select marked
1594                            if (!species_man->is_selected()) {
1595                                ED4_species_name_terminal *species_name = species_man->search_spec_child_rek(ED4_L_SPECIES_NAME)->to_species_name_terminal();
1596                                ED4_ROOT->add_to_selected(species_name);
1597                            }
1598                        }
1599                        else { // de-select marked
1600                            if (species_man->is_selected()) {
1601                                ED4_species_name_terminal *species_name = species_man->search_spec_child_rek(ED4_L_SPECIES_NAME)->to_species_name_terminal();
1602                                ED4_ROOT->remove_from_selected(species_name);
1603                            }
1604                        }
1605                    }
1606                }
1607                if (species_man->is_selected()) sel++;
1608            }
1609        }
1610        else {
1611            e4_assert(!member->is_manager());
1612        }
1613    }
1614    set_species_counters(sp, sel);
1615}
1616void ED4_multi_species_manager::selected_species_mark(bool mark) {
1617    int m;
1618    int sp  = 0;
1619    int sel = 0;
1620
1621    for (m=0; m<children->members(); m++) {
1622        ED4_base *member = children->member(m);
1623
1624        if (member->is_group_manager()) {
1625            ED4_multi_species_manager *multi_species_man = member->to_group_manager()->get_multi_species_manager();
1626            multi_species_man->selected_species_mark(mark);
1627            sp += multi_species_man->get_no_of_species();
1628            sel += multi_species_man->get_no_of_selected_species();
1629        }
1630        else if (member->is_species_manager()) {
1631            ED4_species_manager *species_man = member->to_species_manager();
1632
1633            if (!species_man->is_consensus_manager()) {
1634                sp++;
1635                if (species_man->is_selected()) {
1636                    if (species_man->is_species_seq_manager()) {
1637                        GBDATA *gbd = species_man->get_species_pointer();
1638                        e4_assert(gbd);
1639
1640                        GB_write_flag(gbd, mark ? 1 : 0);
1641                    }
1642                    sel++;
1643                }
1644            }
1645        }
1646        else {
1647            e4_assert(!member->is_manager());
1648        }
1649    }
1650    set_species_counters(sp, sel);
1651}
1652
1653ED4_species_manager *ED4_multi_species_manager::get_consensus_manager() const {
1654    ED4_species_manager *consensus_manager = 0;
1655
1656    for (int i=0; i<children->members(); i++) {
1657        ED4_base *member = children->member(i);
1658        if (member->is_consensus_manager()) {
1659            consensus_manager = member->to_species_manager();
1660            break;
1661        }
1662    }
1663
1664    return consensus_manager;
1665}
1666
1667// -----------------------------
1668//      ED4_species_manager
1669
1670ED4_species_manager::ED4_species_manager(ED4_species_type type_, const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent) :
1671    ED4_manager(species_manager_spec, temp_id, x, y, width, height, temp_parent),
1672    type(type_),
1673    selected(false)
1674{
1675    e4_assert(type != ED4_SP_NONE);
1676    if (type == ED4_SP_SAI) ED4_ROOT->loadable_SAIs_may_have_changed();
1677}
1678
1679ED4_species_manager::~ED4_species_manager() {
1680    if (type == ED4_SP_SAI) ED4_ROOT->loadable_SAIs_may_have_changed();
1681}
1682
1683void ED4_root::remove_all_callbacks() {
1684    for (ED4_window *ew = first_window; ew; ew = ew->next) {
1685        ew->cursor.prepare_shutdown(); // removes any callbacks
1686    }
1687    ED4_viewDifferences_disable();
1688}
1689
1690// ------------------------
1691//      group managers
1692
1693ED4_abstract_group_manager::ED4_abstract_group_manager(const ED4_objspec& spec_, const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent) :
1694    ED4_manager(spec_, temp_id, x, y, width, height, temp_parent), 
1695    my_table(0)
1696{
1697}
1698
1699ED4_group_manager::ED4_group_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent) :
1700    ED4_abstract_group_manager(group_manager_spec, temp_id, x, y, width, height, temp_parent)
1701{
1702}
1703
1704// -------------------
1705//      ED4_remap
1706
1707ED4_remap::ED4_remap() {
1708    mode               = ED4_RM_NONE;
1709    show_above_percent = 0;
1710
1711    sequence_table_len = 1;
1712    screen_table_len   = 1;
1713
1714    screen_to_sequence_tab = new int[1];        screen_to_sequence_tab[0] = 0;
1715    sequence_to_screen_tab = new int[1];        sequence_to_screen_tab[0] = 0;
1716
1717    sequence_len               = 0;
1718    MAXSEQUENCECHARACTERLENGTH = screen_len = 0;
1719
1720    changed       = 0;
1721    update_needed = 1;
1722
1723}
1724ED4_remap::~ED4_remap() {
1725    delete [] screen_to_sequence_tab;
1726    delete [] sequence_to_screen_tab;
1727}
1728int ED4_remap::screen_to_sequence(int screen_pos) const {
1729    if (size_t(screen_pos) == screen_len) {
1730        return screen_to_sequence_tab[screen_len-1];
1731    }
1732    e4_assert(screen_pos>=0 && size_t(screen_pos)<screen_len);
1733    return screen_to_sequence_tab[screen_pos];
1734}
1735int ED4_remap::clipped_sequence_to_screen_PLAIN(int sequence_pos) const {
1736    if (sequence_pos<0) {
1737        sequence_pos = 0;
1738    }
1739    else if (size_t(sequence_pos)>sequence_len) {
1740        sequence_pos = sequence_len;
1741    }
1742    return sequence_to_screen_PLAIN(sequence_pos);
1743}
1744int ED4_remap::sequence_to_screen(int sequence_pos) const {
1745    int scr_pos = sequence_to_screen_PLAIN(sequence_pos);
1746    if (scr_pos<0) scr_pos = -scr_pos;
1747    return scr_pos;
1748}
1749
1750void ED4_remap::adjacent_screen_positions(int seq_pos, int& screen_pos_left, int& screen_pos_right) {
1751    e4_assert(!is_shown(seq_pos)); // otherwise use sequence_to_screen()
1752
1753    screen_pos_left = -1;
1754    for (int p = seq_pos-1; screen_pos_left<0 && p>=0; --p) {
1755        screen_pos_left = sequence_to_screen_PLAIN(p);
1756    }
1757    screen_pos_right = -1;
1758    for (int p = seq_pos+1; screen_pos_right<0 && size_t(p)<=sequence_len; ++p) {
1759        screen_pos_right = sequence_to_screen_PLAIN(p);
1760    }
1761}
1762
1763inline void ED4_remap::set_sequence_to_screen(int pos, int newVal) {
1764    e4_assert(pos>=0 && size_t(pos)<sequence_table_len);
1765    if (sequence_to_screen_tab[pos]!=newVal) {
1766        sequence_to_screen_tab[pos] = newVal;
1767        changed = 1;
1768    }
1769}
1770void ED4_remap::mark_compile_needed_force() {
1771    if (!update_needed) {
1772        update_needed = 1;
1773        if (ED4_ROOT && ED4_ROOT->root_group_man) {                     // test if root_group_man already exists
1774            ED4_ROOT->root_group_man->resize_requested_by_child();      // remapping is recompiled while re-displaying the root_group_manager
1775        }
1776    }
1777}
1778void ED4_remap::mark_compile_needed() {
1779    if (mode!=ED4_RM_NONE) mark_compile_needed_force();
1780}
1781
1782GB_ERROR ED4_remap::compile(ED4_root_group_manager *gm)
1783{
1784    e4_assert(update_needed);
1785
1786    const BaseFrequencies& table = gm->table();
1787    size_t i, j;
1788
1789    changed = 0; // is changed by set_sequence_to_screen
1790    update_needed = 0;
1791
1792    sequence_len = table.size(); // take size of any table
1793    if ((sequence_len+1) > sequence_table_len) {
1794        delete [] sequence_to_screen_tab;
1795        sequence_to_screen_tab = new int[sequence_table_len = sequence_len+1];
1796        memset(sequence_to_screen_tab, 0, sequence_table_len*sizeof(int));
1797        changed = 1;
1798    }
1799
1800    int above_percent;
1801    switch (gm->remap()->get_mode()) {
1802        default: e4_assert(0);
1803        case ED4_RM_NONE: {
1804        dont_map :
1805            for (i=0; i<sequence_table_len; i++) {
1806                set_sequence_to_screen(i, i);
1807            }
1808            screen_len = sequence_len;
1809            break;
1810        }
1811        case ED4_RM_SHOW_ABOVE: {
1812            above_percent = show_above_percent;
1813            goto calc_percent;
1814        }
1815        case ED4_RM_MAX_ALIGN:
1816        case ED4_RM_MAX_EDIT: {
1817            above_percent = 0;
1818        calc_percent :
1819            for (i=0, j=0; i<(sequence_table_len-1); i++) {
1820                int bases;
1821                int gaps;
1822
1823                table.bases_and_gaps_at(i, &bases, &gaps);
1824
1825                if (bases==0 && gaps==0) {  // special case (should occur only after inserting columns)
1826                    set_sequence_to_screen(i, -j); // hide
1827                }
1828                else {
1829                    int percent = (int)((bases*100L)/table.added_sequences());
1830
1831                    e4_assert(percent==((bases*100)/(bases+gaps)));
1832
1833                    if (bases && percent>=above_percent) {
1834                        set_sequence_to_screen(i, j++);
1835                    }
1836                    else {
1837                        set_sequence_to_screen(i, -j);
1838                    }
1839                }
1840            }
1841            for (; i<sequence_table_len; i++) { // fill rest of table
1842                set_sequence_to_screen(i, j++);
1843            }
1844            screen_len = j;
1845            break;
1846        }
1847        case ED4_RM_DYNAMIC_GAPS: {
1848            for (i=0, j=0; i<(sequence_table_len-1); i++) {
1849                int bases;
1850
1851                table.bases_and_gaps_at(i, &bases, 0);
1852                if (bases) {
1853                    set_sequence_to_screen(i, j++);
1854                }
1855                else {
1856                    size_t k = i+1;
1857
1858                    while (k<(sequence_table_len-1)) {
1859                        int bases2;
1860
1861                        table.bases_and_gaps_at(k, &bases2, 0);
1862                        if (bases2) {
1863                            break;
1864                        }
1865                        k++;
1866                    }
1867
1868                    int gaps = k-i;
1869                    int shown_gapsize;
1870
1871                    if (gaps<100) {
1872                        shown_gapsize = gaps/10 + 1;
1873                    }
1874                    else if (gaps<1000) {
1875                        shown_gapsize = gaps/100 + 10;
1876                    }
1877                    else {
1878                        shown_gapsize = gaps/1000 + 19;
1879                    }
1880
1881                    for (; i<k && shown_gapsize; i++, shown_gapsize--) {
1882                        set_sequence_to_screen(i, j++);
1883                    }
1884                    for (; i<k; i++) {
1885                        set_sequence_to_screen(i, -j);
1886                    }
1887                    i--;
1888                }
1889            }
1890            for (; i<sequence_table_len; i++) {
1891                set_sequence_to_screen(i, j++); // fill rest of table
1892            }
1893            screen_len = j;
1894            break;
1895        }
1896    }
1897
1898    if (sequence_table_len) {
1899        if (!screen_len && sequence_len) {
1900            goto dont_map;
1901        }
1902        if ((screen_len+1) > screen_table_len) {
1903            delete [] screen_to_sequence_tab;
1904            screen_to_sequence_tab = new int[screen_table_len = screen_len+1];
1905        }
1906        memset(screen_to_sequence_tab, 0, sizeof(int)*screen_table_len);
1907        for (i=0; i<sequence_table_len; i++) {
1908            int screen_pos = sequence_to_screen_tab[i];
1909            if (screen_pos>=0) {
1910                screen_to_sequence_tab[screen_pos] = i;
1911            }
1912        }
1913    }
1914
1915    if (sequence_len>1) {
1916        MAXSEQUENCECHARACTERLENGTH = sequence_len;
1917    }
1918
1919    return NULL;
1920}
1921
1922// --------------------------------
1923//      ED4_root_group_manager
1924
1925ED4_root_group_manager::ED4_root_group_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent)
1926    : ED4_abstract_group_manager(root_group_manager_spec, temp_id, x, y, width, height, temp_parent),
1927      my_remap()
1928{
1929    AW_root *awr = ED4_ROOT->aw_root;
1930    my_remap.set_mode((ED4_remap_mode)awr->awar(ED4_AWAR_COMPRESS_SEQUENCE_TYPE)->read_int(),
1931                      awr->awar(ED4_AWAR_COMPRESS_SEQUENCE_PERCENT)->read_int());
1932    my_remap.mark_compile_needed_force();
1933}
1934
1935bool ED4_root_group_manager::update_remap() {
1936    bool remapped = false;
1937
1938    if (my_remap.compile_needed()) {
1939        my_remap.compile(this);
1940        if (my_remap.was_changed()) remapped = true;
1941    }
1942
1943    return remapped;
1944}
1945
1946// -----------------------------------
1947//      ED4_multi_species_manager
1948
1949ED4_multi_sequence_manager::ED4_multi_sequence_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent)
1950    : ED4_manager(multi_sequence_manager_spec, temp_id, x, y, width, height, temp_parent)
1951{
1952}
1953
1954ED4_sequence_manager::ED4_sequence_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent)
1955    : ED4_manager(sequence_manager_spec, temp_id, x, y, width, height, temp_parent)
1956{
1957}
1958
1959ED4_multi_name_manager::ED4_multi_name_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent)
1960    : ED4_manager(multi_name_manager_spec, temp_id, x, y, width, height, temp_parent)
1961{
1962}
1963
1964ED4_name_manager::ED4_name_manager(const char *temp_id, AW_pos x, AW_pos y, AW_pos width, AW_pos height, ED4_manager *temp_parent)
1965    : ED4_manager(name_manager_spec, temp_id, x, y, width, height, temp_parent)
1966{
1967}
1968
1969
Note: See TracBrowser for help on using the repository browser.