root/branches/stable_5.0/AWT/AWT_dtree.cxx

Revision 6143, 91.7 KB (checked in by westram, 3 years ago)
  • backport [6141] (parts changing code, but only strings and comments)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <cstdio>
2#include <cstdlib>
3#include <cmath>
4#include <cstring>
5
6#include <iostream>
7
8#include <arbdb.h>
9#include <arbdbt.h>
10#include <aw_root.hxx>
11#include <aw_device.hxx>
12#include <aw_keysym.hxx>
13#include <aw_window.hxx>
14#include <awt.hxx>
15#include <awt_canvas.hxx>
16#include <awt_nds.hxx>
17#include <aw_preset.hxx>
18#include <aw_awars.hxx>
19
20#include "awt_tree.hxx"
21#include "awt_dtree.hxx"
22#include "awt_attributes.hxx"
23
24/***************************
25      class AP_tree
26****************************/
27
28using namespace AW;
29
30AW_gc_manager AWT_graphic_tree::init_devices(AW_window *aww, AW_device *device, AWT_canvas* ntw, AW_CL cd2)
31{
32    AW_gc_manager preset_window = 
33        AW_manage_GC(aww,device,AWT_GC_CURSOR, AWT_GC_MAX, AW_GCM_DATA_AREA,
34                     (AW_CB)AWT_resize_cb, (AW_CL)ntw, cd2,
35                     true,      // define color groups
36                     "#3be",
37
38                     // Important note :
39                     // Many gc indices are shared between ABR_NTREE and ARB_PARSIMONY
40                     // e.g. the tree drawing routines use same gc's for drawing both trees
41                     // (check PARS_dtree.cxx AWT_graphic_parsimony::init_devices)
42
43                     "Cursor$white",
44                     "Branch remarks$#b6ffb6",
45                     "+-Bootstrap$#53d3ff",    "-B.(limited)$white",
46                     "-GROUP_BRACKETS$#000",
47                     "Marked$#ffe560",
48                     "Some marked$#bb8833",
49                     "Not marked$#622300",
50                     "Zombies etc.$#977a0e",
51
52                     "+-No probe$black",    "-Probes 1+2$yellow",
53                     "+-Probe 1$red",       "-Probes 1+3$magenta",
54                     "+-Probe 2$green",     "-Probes 2+3$cyan",
55                     "+-Probe 3$blue",      "-All probes$white",
56                     NULL );
57
58    return preset_window;
59}
60
61AP_tree *AWT_graphic_tree::search(AP_tree *root, const char *name)
62{
63    if(!root) return 0;
64    if (root->is_leaf) {
65        if(root->name){
66            if (!strcmp(name,root->name)) {
67                return root;
68            }
69        }
70    }else{
71        AP_tree *result = this->search(root->leftson,name);
72        if (result) return result;
73        result = this->search(root->rightson,name);
74        if (result) return result;
75    }
76    return 0;
77}
78
79void AWT_graphic_tree::jump(AP_tree *at, const char *name)
80{
81    if (sort_is_list_style(tree_sort)) return;
82
83    at = search(at,name);
84    if(!at) return;
85    if (tree_sort == AP_TREE_NORMAL) {
86        tree_root_display = tree_root;
87    }else{
88        while (at->father &&
89               at->gr.view_sum<15 &&
90               0 == at->gr.grouped)
91        {
92            at = at->father;
93        }
94        tree_root_display = at;
95    }
96}
97
98void AWT_graphic_tree::mark_species_in_tree(AP_tree *at, int mark_mode) {
99    /*
100      mode      does
101
102      0         unmark all
103      1         mark all
104      2         invert all marks
105    */
106
107    if (!at) return;
108
109    GB_transaction dummy(tree_static->gb_species_data);
110    if (at->is_leaf) {
111        if(at->gb_node) {       // not a zombie
112            switch (mark_mode) {
113                case 0: GB_write_flag(at->gb_node, 0); break;
114                case 1: GB_write_flag(at->gb_node, 1); break;
115                case 2: GB_write_flag(at->gb_node, !GB_read_flag(at->gb_node)); break;
116                default : awt_assert(0);
117            }
118        }
119    }
120
121    mark_species_in_tree(at->leftson, mark_mode);
122    mark_species_in_tree(at->rightson, mark_mode);
123}
124
125void AWT_graphic_tree::mark_species_in_tree_that(AP_tree *at, int mark_mode, int (*condition)(GBDATA*, void*), void *cd) {
126    /*
127      mark_mode does
128
129      0         unmark all
130      1         mark all
131      2         invert all marks
132
133      marks are only changed for those species for that condition() != 0
134    */
135
136    if (!at) return;
137
138    GB_transaction dummy(tree_static->gb_species_data);
139    if (at->is_leaf) {
140        if(at->gb_node) {       // not a zombie
141            int oldMark = GB_read_flag(at->gb_node);
142            if (oldMark != mark_mode && condition(at->gb_node, cd)) {
143                switch (mark_mode) {
144                    case 0: GB_write_flag(at->gb_node, 0); break;
145                    case 1: GB_write_flag(at->gb_node, 1); break;
146                    case 2: GB_write_flag(at->gb_node, !oldMark); break;
147                    default : awt_assert(0);
148                }
149            }
150        }
151    }
152
153    mark_species_in_tree_that(at->leftson, mark_mode, condition, cd);
154    mark_species_in_tree_that(at->rightson, mark_mode, condition, cd);
155}
156
157
158// same as mark_species_in_tree but works on rest of tree
159void AWT_graphic_tree::mark_species_in_rest_of_tree(AP_tree *at, int mark_mode) {
160    if (!at) return;
161
162    AP_tree *pa = at->father;
163    if (!pa) return;
164
165    GB_transaction dummy(tree_static->gb_species_data);
166
167    if (at == pa->leftson)  mark_species_in_tree(pa->rightson, mark_mode);
168    else mark_species_in_tree(pa->leftson, mark_mode);
169
170    mark_species_in_rest_of_tree(pa, mark_mode);
171}
172
173// same as mark_species_in_tree_that but works on rest of tree
174void AWT_graphic_tree::mark_species_in_rest_of_tree_that(AP_tree *at, int mark_mode, int (*condition)(GBDATA*, void*), void *cd) {
175    if (!at) return;
176
177    AP_tree *pa = at->father;
178    if (!pa) return;
179
180    GB_transaction dummy(tree_static->gb_species_data);
181
182    if (at == pa->leftson)  mark_species_in_tree_that(pa->rightson, mark_mode, condition, cd);
183    else mark_species_in_tree_that(pa->leftson, mark_mode, condition, cd);
184
185    mark_species_in_rest_of_tree_that(pa, mark_mode, condition, cd);
186}
187
188bool AWT_graphic_tree::tree_has_marks(AP_tree *at) {
189    if (!at) return false;
190
191    if (at->is_leaf) {
192        if (!at->gb_node) return false; // zombie
193        int marked = GB_read_flag(at->gb_node);
194        return marked;
195    }
196
197    return tree_has_marks(at->leftson) || tree_has_marks(at->rightson);
198}
199
200bool AWT_graphic_tree::rest_tree_has_marks(AP_tree *at) {
201    if (!at) return false;
202
203    AP_tree *pa = at->father;
204    if (!pa) return false;
205
206    if (at == pa->leftson) {    // i am the left son
207        return tree_has_marks(pa->rightson) || rest_tree_has_marks(pa);
208    }
209    return tree_has_marks(pa->leftson) || rest_tree_has_marks(pa);
210}
211
212struct AWT_graphic_tree_group_state {
213    int closed, opened;
214    int closed_terminal, opened_terminal;
215    int closed_with_marked, opened_with_marked;
216    int marked_in_groups, marked_outside_groups;
217
218    void clear() {
219        closed                = 0;
220        opened                = 0;
221        closed_terminal       = 0;
222        opened_terminal       = 0;
223        closed_with_marked    = 0;
224        opened_with_marked    = 0;
225        marked_in_groups      = 0;
226        marked_outside_groups = 0;
227    }
228
229    AWT_graphic_tree_group_state() { clear(); }
230
231    bool has_groups() const { return closed+opened; }
232    int marked() const { return marked_in_groups+marked_outside_groups; }
233
234    bool all_opened() const { return closed == 0 && opened>0; }
235    bool all_closed() const { return opened == 0 && closed>0; }
236    bool all_terminal_closed() const { return opened_terminal == 0 && closed_terminal == closed; }
237    bool all_marked_opened() const { return marked_in_groups > 0 && closed_with_marked == 0; }
238
239    int next_expand_mode() const {
240        if (closed_with_marked) return 1; // expand marked
241        return 4; // expand all
242    }
243
244    int next_collapse_mode() const {
245        if (all_terminal_closed()) return 0; // collapse all
246        return 2; // group terminal
247    }
248
249    int next_group_mode() const {
250        if (all_terminal_closed()) {
251            if (marked_in_groups && !all_marked_opened()) return 1; // all but marked
252            return 4; // expand all
253        }
254        if (all_marked_opened()) {
255            if (all_opened()) return 0; // collapse all
256            return 4;           // expand all
257        }
258        if (all_opened()) return 0; // collapse all
259        if (!all_closed()) return 0; // collapse all
260
261        if (!all_terminal_closed()) return 2; // group terminal
262        if (marked_in_groups) return 1; // all but marked
263        return 4; // expand all
264    }
265
266    void dump(void) {
267        printf("closed=%i               opened=%i\n", closed, opened);
268        printf("closed_terminal=%i      opened_terminal=%i\n", closed_terminal, opened_terminal);
269        printf("closed_with_marked=%i   opened_with_marked=%i\n", closed_with_marked, opened_with_marked);
270        printf("marked_in_groups=%i     marked_outside_groups=%i\n", marked_in_groups, marked_outside_groups);
271
272        printf("all_opened=%i all_closed=%i all_terminal_closed=%i all_marked_opened=%i\n",
273               all_opened(), all_closed(), all_terminal_closed(), all_marked_opened());
274    }
275};
276
277// start of implementation of class AWT_graphic_tree_group_state:
278
279// -end- of implementation of class AWT_graphic_tree_group_state.
280
281void AWT_graphic_tree::detect_group_state(AP_tree *at, AWT_graphic_tree_group_state *state, AP_tree *skip_this_son) {
282    if (!at) return;
283    if (at->is_leaf) {
284        if (at->gb_node && GB_read_flag(at->gb_node)) state->marked_outside_groups++; // count marks
285        return;                 // leave are never grouped
286    }
287
288    if (at->gb_node) {          // i am a group
289        AWT_graphic_tree_group_state sub_state;
290        if (at->leftson != skip_this_son) detect_group_state(at->leftson, &sub_state, skip_this_son);
291        if (at->rightson != skip_this_son) detect_group_state(at->rightson, &sub_state, skip_this_son);
292
293        if (at->gr.grouped) {   // a closed group
294            state->closed++;
295            if (!sub_state.has_groups()) state->closed_terminal++;
296            if (sub_state.marked()) state->closed_with_marked++;
297            state->closed += sub_state.opened;
298        }
299        else { // an open group
300            state->opened++;
301            if (!sub_state.has_groups()) state->opened_terminal++;
302            if (sub_state.marked()) state->opened_with_marked++;
303            state->opened += sub_state.opened;
304        }
305
306        state->marked_in_groups += sub_state.marked();
307
308        state->closed             += sub_state.closed;
309        state->opened_terminal    += sub_state.opened_terminal;
310        state->closed_terminal    += sub_state.closed_terminal;
311        state->opened_with_marked += sub_state.opened_with_marked;
312        state->closed_with_marked += sub_state.closed_with_marked;
313    }
314    else { // not a group
315        if (at->leftson != skip_this_son) detect_group_state(at->leftson, state, skip_this_son);
316        if (at->rightson != skip_this_son) detect_group_state(at->rightson, state, skip_this_son);
317    }
318}
319
320int AWT_graphic_tree::group_rest_tree(AP_tree *at, int mode, int color_group) {
321    if (!at) return 1;
322
323    AP_tree *pa = at->father;
324    if (!pa) return 1;
325
326    if (at == pa->leftson) { // i am the left son
327        group_tree(pa->rightson, mode, color_group);
328    }
329    else {
330        group_tree(pa->leftson, mode, color_group);
331    }
332
333    group_rest_tree(pa, mode, color_group);
334    return 1; // non-rest is always ungrouped
335}
336
337int AWT_graphic_tree::group_tree(AP_tree *at, int mode, int color_group)    // run on father !!!
338{
339    /*
340      mode      does group
341
342      0         all
343      1         all but marked
344      2         all but groups with subgroups
345      4         none (ungroups all)
346      8         all but color_group
347
348    */
349
350    if (!at) return 1;
351
352    GB_transaction dummy(this->tree_static->gb_species_data);
353
354    if (at->is_leaf) {
355        int ungroup_me = 0;
356
357        if (mode & 4) ungroup_me = 1;
358        if (at->gb_node) { // not a zombie
359            if (!ungroup_me && (mode & 1)) { // do not group marked
360                if (GB_read_flag(at->gb_node)) { // i am a marked species
361                    ungroup_me = 1;
362                }
363            }
364            if (!ungroup_me && (mode & 8)) { // do not group one color_group
365                int my_color_group = AW_find_color_group(at->gb_node, true);
366                if (my_color_group == color_group) { // i am of that color group
367                    ungroup_me = 1;
368                }
369            }
370        }
371
372        return ungroup_me;
373    }
374
375    int flag  = this->group_tree(at->leftson, mode, color_group);
376    flag     += this->group_tree(at->rightson, mode, color_group);
377
378    at->gr.grouped = 0;
379
380    if (!flag) { // no son requests to be shown
381        if (at->gb_node) { // i am a group
382            GBDATA *gn = GB_entry(at->gb_node, "group_name");
383            if (gn) {
384                if (strlen(GB_read_char_pntr(gn))>0){ // and I have a name
385                    at->gr.grouped     = 1;
386                    if (mode & 2) flag = 1; // do not group non-terminal groups
387                }
388            }
389        }
390    }
391    if (!at->father) this->tree_root->compute_tree(this->tree_static->gb_species_data);
392
393    return flag;
394}
395
396
397
398int AWT_graphic_tree::resort_tree( int mode, struct AP_tree *at ) // run on father !!!
399{
400    /* mode
401
402       0    to top
403       1    to bottom
404       2    center (to top)
405       3    center (to bottom; don't use this - it's used internal)
406
407       post-condition: leafname contains alphabetically "smallest" name of subtree
408
409    */
410    static const char *leafname;
411
412    if (!at) {
413        GB_transaction dummy(this->gb_main);
414        at = this->tree_root;
415        if(!at) return 0;
416        at->arb_tree_set_leafsum_viewsum();
417
418        this->resort_tree(mode,at);
419        at->compute_tree(this->gb_main);
420        return 0;
421    }
422
423    if (at->is_leaf) {
424        leafname = at->name;
425        return 1;
426    }
427    int leftsize = at->leftson->gr.leave_sum;
428    int rightsize = at->rightson->gr.leave_sum;
429
430    if ( (mode &1) == 0 ) { // to top
431        if (rightsize >leftsize ) {
432            at->swap_sons();
433        }
434    }else{
435        if (rightsize < leftsize ) {
436            at->swap_sons();
437        }
438    }
439
440    int lmode = mode;
441    int rmode = mode;
442    if ( (mode & 2) == 2){
443        lmode = 2;
444        rmode = 3;
445    }
446
447    resort_tree(lmode,at->leftson);
448    gb_assert(leafname);
449    const char *leftleafname = leafname;
450
451    resort_tree(rmode,at->rightson);
452    gb_assert(leafname);
453    const char *rightleafname = leafname;
454
455    gb_assert(leftleafname && rightleafname);
456
457    if (leftleafname && rightleafname) {
458        int name_cmp = strcmp(leftleafname,rightleafname);
459        if (name_cmp<0) { // leftleafname < rightleafname
460            leafname = leftleafname;
461        } else { // (name_cmp>=0) aka: rightleafname <= leftleafname
462            leafname = rightleafname;
463            if (rightsize==leftsize && name_cmp>0) { // if sizes of subtrees are equal and rightleafname<leftleafname -> swap branches
464                at->swap_sons();
465            }
466        }
467    }else{
468        if (leftleafname) leafname = leftleafname;
469        else leafname = rightleafname;
470    }
471
472    return 0;
473}
474
475
476void AWT_graphic_tree::rot_show_line(AW_device *device)
477{
478    double             sx, sy;
479    double             x, y;
480    sx = (this->old_rot_cl.x0+this->old_rot_cl.x1)*.5;
481    sy = (this->old_rot_cl.y0+this->old_rot_cl.y1)*.5;
482    x = this->rot_cl.x0 * (1.0-this->rot_cl.length) + this->rot_cl.x1 * this->rot_cl.length;
483    y = this->rot_cl.y0 * (1.0-this->rot_cl.length) + this->rot_cl.y1 * this->rot_cl.length;
484    int gc = this->drag_gc;
485    device->line(gc, sx, sy, x, y, AWT_F_ALL, 0, 0);
486}
487
488void AWT_graphic_tree::rot_show_triangle(AW_device *device)
489{
490    double   w;
491    double   len;
492    double   sx, sy;
493    double   x1, y1, x2, y2;
494    AP_tree *at;
495    double   scale = 1.0;
496
497    if (tree_sort == AP_TREE_IRS) scale = irs_tree_ruler_scale_factor;
498
499    at = this->rot_at;
500
501    if (!at || !at->father)
502        return;
503
504    sx = this->old_rot_cl.x0;
505    sy = this->old_rot_cl.y0;
506
507    if (at == at->father->leftson) len = at->father->leftlen;
508    else len = at->father->rightlen;
509
510    len *= scale;
511
512    w = this->rot_orientation;  x1 = this->old_rot_cl.x0 + cos(w) * len;
513    y1 = this->old_rot_cl.y0 + sin(w) * len;
514
515    int gc = this->drag_gc;
516
517    device->line(gc, sx, sy, x1, y1, AWT_F_ALL, 0, 0);
518
519
520    if (!at->is_leaf) {
521        sx = x1; sy = y1;
522        len = at->gr.tree_depth;
523        w = this->rot_orientation - this->rot_spread*.5*.5 + at->gr.right_angle;
524        x1 = sx + cos(w) * len;
525        y1 = sy + sin(w) * len;
526        w = this->rot_orientation + this->rot_spread*.5*.5 + at->gr.left_angle;
527        x2 = sx + cos(w) * len;
528        y2 = sy + sin(w) * len;
529
530        device->line(gc, sx, sy, x1, y1, AWT_F_ALL, 0, 0);
531        device->line(gc, sx, sy, x2, y2, AWT_F_ALL, 0, 0);
532        device->line(gc, x2, y2, x1, y1, AWT_F_ALL, 0, 0);
533
534    }
535
536
537}
538void AWT_show_bootstrap_circle(AW_device *device, const char *bootstrap, double zoom_factor, double max_radius, double len, double x, double y, bool elipsoid, double elip_ysize, int filter, AW_CL cd1,AW_CL cd2){
539    double radius           = .01 * atoi(bootstrap); // bootstrap values are given in % (0..100)
540    if (radius < .1) radius = .1;
541
542    radius  = 1.0 / sqrt(radius); // -> bootstrap->radius : 100% -> 1, 0% -> inf
543    radius -= 1.0;              // -> bootstrap->radius : 100% -> 0, 0% -> inf
544    radius *= 2; // diameter ?
545
546    if (radius < 0) return;     // skip too small circles
547
548    // Note : radius goes against infinite, if bootstrap values go against zero
549    //        For this reason we do some limitaion here:
550
551    // printf("bootstrap=%i -> radius=%f\n", atoi(bootstrap), radius);
552
553    // was 0.06
554// #define BOOTSTRAP_RADIUS_LIMIT (len*2)
555// #define BOOTSTRAP_RADIUS_LIMIT 2.0
556#define BOOTSTRAP_RADIUS_LIMIT max_radius
557
558    int gc = AWT_GC_BOOTSTRAP;
559
560    if (radius > BOOTSTRAP_RADIUS_LIMIT) {
561        radius = BOOTSTRAP_RADIUS_LIMIT;
562        gc     = AWT_GC_BOOTSTRAP_LIMITED;
563    }
564
565    double     radiusx = radius * len * zoom_factor; // multiply with length of branch (and zoomfactor)
566    double     radiusy;
567    if (elipsoid) {
568        radiusy = elip_ysize * zoom_factor;
569        if (radiusy > radiusx) radiusy = radiusx;
570    }
571    else {
572        radiusy = radiusx;
573    }
574
575    device->circle(gc, false, x, y, radiusx, radiusy, filter, cd1, (AW_CL)cd2);
576}
577
578double comp_rot_orientation(AW_clicked_line *cl)
579{
580    return atan2(cl->y1 - cl->y0, cl->x1 - cl->x0);
581}
582
583double comp_rot_spread(AP_tree *at, AWT_graphic_tree *ntw)
584{
585    double zw = 0;
586    AP_tree *bt;
587
588    if(!at) return 0;
589
590    zw = 1.0;
591
592    for(bt=at; bt->father && (bt!=ntw->tree_root_display); bt=bt->father){
593        zw *= bt->gr.spread;
594    }
595
596    zw *= bt->gr.spread;
597    zw *= (double)at->gr.view_sum / (double)bt->gr.view_sum;
598
599    switch (ntw->tree_sort) {
600        case AP_TREE_NORMAL:
601            zw *= 0.5*M_PI;
602            break;
603        case AP_TREE_IRS:
604            zw *= 0.5*M_PI * ntw->get_irs_tree_ruler_scale_factor();
605            break;
606        case AP_TREE_RADIAL:
607            zw *= 2*M_PI;
608            break;
609        default:
610            awt_assert(0);
611    }
612
613    return zw;
614}
615
616void reset_spread(AP_tree *at)
617{
618    if(!at) return;
619    at->gr.spread =  1.0;
620    reset_spread(at->leftson);
621    reset_spread(at->rightson);
622}
623
624void reset_rotation(AP_tree *at)
625{
626    if(!at) return;
627    at->gr.left_angle = 0.0;
628    at->gr.right_angle = 0.0;
629    reset_rotation(at->leftson);
630    reset_rotation(at->rightson);
631}
632
633void reset_line_width(AP_tree *at)
634{
635    if(!at) return;
636    at->gr.left_linewidth =  0;
637    at->gr.right_linewidth =  0;
638    reset_line_width(at->leftson);
639    reset_line_width(at->rightson);
640}
641
642char *AWT_graphic_tree_root_changed(void *cd, AP_tree *old, AP_tree *newroot)
643{
644    AWT_graphic_tree *agt = (AWT_graphic_tree*)cd;
645    if (agt->tree_root_display == old ||
646        agt->tree_root_display->is_son(old)) agt->tree_root_display = newroot;
647    if (agt->tree_root == old ||
648        agt->tree_root->is_son(old)) agt->tree_root = newroot;
649    return 0;
650}
651char *AWT_graphic_tree_node_deleted(void *cd, AP_tree *old)
652{
653    AWT_graphic_tree *agt = (AWT_graphic_tree*)cd;
654    if (agt->tree_root_display == old) agt->tree_root_display = agt->tree_root;
655    if (old == agt->tree_root) {
656        agt->tree_root = 0;
657        agt->tree_root_display = 0;
658    }
659    return 0;
660}
661void AWT_graphic_tree::toggle_group(AP_tree * at)
662{
663    GB_ERROR error = NULL;
664   
665    if (at->gb_node) {                                            // existing group
666        char *gname = GBT_read_string(at->gb_node, "group_name");
667        if (gname) {
668            const char *msg = GBS_global_string("What to do with group '%s'?",gname);
669
670            switch (aw_question(msg, "Rename,Destroy,Cancel")) {
671                case 0: {                                                    // rename
672                    char *new_gname = aw_input("Rename group", "Change group name:", at->name);
673                    if (new_gname) {
674                        freeset(at->name, new_gname);
675                        error = GBT_write_string(at->gb_node, "group_name", new_gname);
676                    }
677                    break;
678                }
679
680                case 1:                                      // destroy
681                    at->gr.grouped = 0;
682                    at->name       = 0;
683                    error          = GB_delete(at->gb_node);
684                    at->gb_node    = 0;
685                    break;
686
687                case 2:         // cancel
688                    break;
689            }
690
691            free(gname);
692        }
693    }
694    else {
695        error = create_group(at); // create new group
696        if (!error && at->name) at->gr.grouped = 1;
697    }
698
699    if (error) aw_message(error);
700}
701
702GB_ERROR AWT_graphic_tree::create_group(AP_tree * at) {
703    GB_ERROR error = NULL;
704    if (!at->name) {
705        char *gname = aw_input("Enter Name of Group");
706        if (gname) {
707            GBDATA         *gb_mainT = GB_get_root(tree_static->gb_tree);
708            GB_transaction  ta(gb_mainT);
709
710            if (!at->gb_node) {
711                at->gb_node   = GB_create_container(this->tree_static->gb_tree, "node");
712                if (!at->gb_node) error = GB_await_error();
713                else {
714                    error = GBT_write_int(at->gb_node, "id", 0);
715                    this->exports.save = !error;
716                }
717            }
718
719            if (!error) {
720                GBDATA *gb_name     = GB_search(at->gb_node, "group_name", GB_STRING);
721                if (!gb_name) error = GB_await_error();
722                else {
723                    error = GBT_write_group_name(gb_name, gname);
724                    if (!error) at->name = gname;
725                }
726            }
727            error = ta.close(error);
728        }
729    }
730    else if (!at->gb_node) {
731        at->gb_node = GB_create_container(this->tree_static->gb_tree, "node");
732
733        if (!at->gb_node) error = GB_await_error();
734        else {
735            error = GBT_write_int(at->gb_node, "id", 0);
736            this->exports.save = !error;
737        }
738    }
739
740    return error;
741}
742
743void AWT_graphic_tree::key_command(AWT_COMMAND_MODE /*cmd*/, AW_key_mod key_modifier, char key_char,
744                                   AW_pos /*x*/, AW_pos /*y*/, AW_clicked_line *cl, AW_clicked_text *ct)
745{
746    bool update_timer = true;
747    bool calc_color   = true;
748    bool refresh      = true;
749    bool Save         = false;
750    bool resize       = false;
751    bool compute      = false;
752
753    if (key_modifier != AW_KEYMODE_NONE) return;
754    if (key_char == 0) return;
755
756#if defined(DEBUG)
757    printf("key_char=%i (=%c)\n", int(key_char), key_char);
758#endif // DEBUG
759
760    // ----------------------------------------
761    //      commands independent of tree :
762    // ----------------------------------------
763
764    bool global_key = true;
765    switch (key_char) {
766        case 9:  {    // Ctrl-i = invert all
767            GBT_mark_all(gb_main, 2);
768            break;
769        }
770        case 13:  {    // Ctrl-m = mark/unmark all
771            int mark   = GBT_first_marked_species(gb_main) == 0; // no species marked -> mark all
772            GBT_mark_all(gb_main, mark);
773            break;
774        }
775        default: {
776            global_key = false;
777            break;
778        }
779    }
780
781    if (!global_key && tree_proto) {
782
783        AP_tree    *at   = 0;
784        const char *what = 0;
785
786        if (ct->exists) {
787            at   = (AP_tree*)ct->client_data1;
788            what = (char*)ct->client_data2;
789        }
790        else if (cl->exists) {
791            at   = (AP_tree*)cl->client_data1;
792            what = (char*)cl->client_data2;
793        }
794
795#if defined(DEBUG)
796        printf("what='%s' at=%p\n", what, at);
797#endif // DEBUG
798
799        if (what && strcmp(what, "species") == 0) {
800            GBDATA *gb_species = 0;
801            if (ct->exists) {
802                gb_species = (GBDATA *)ct->client_data1;
803            }
804            else {
805                awt_assert(0);
806            }
807
808            // ------------------------------------
809            //      commands in species list :
810            // ------------------------------------
811
812            switch (key_char) {
813                case 'i':
814                case 'm':  {    // i/m = mark/unmark species
815                    GB_write_flag(gb_species, 1-GB_read_flag(gb_species));
816                    break;
817                }
818                case 'I':  {    // I = invert all but current;
819                    int mark = GB_read_flag(gb_species);
820                    GBT_mark_all(gb_main, 2);
821                    GB_write_flag(gb_species, mark);
822                    break;
823                }
824                case 'M':  {    // M = mark/unmark all but current;
825                    int mark = GB_read_flag(gb_species);
826                    GB_write_flag(gb_species, 0); // unmark current
827                    GBT_mark_all(gb_main, GBT_first_marked_species(gb_main) == 0);
828                    GB_write_flag(gb_species, mark); // restore mark of current
829                    break;
830                }
831            }
832        }
833        else {
834            if (!at) {
835                // many commands work on whole tree if mouse does not point to subtree
836                at = tree_root;
837            }
838
839            // -------------------------------------
840            //      command working with tree :
841            // -------------------------------------
842
843            if (at) {
844                switch (key_char) {
845                    case 'm':  {    // m = mark/unmark (sub)tree
846                        mark_species_in_tree(at, !tree_has_marks(at));
847                        break;
848                    }
849                    case 'M':  {    // M = mark/unmark all but (sub)tree
850                        mark_species_in_rest_of_tree(at, !rest_tree_has_marks(at));
851                        break;
852                    }
853
854                    case 'i':  {    // i = invert (sub)tree
855                        mark_species_in_tree(at, 2);
856                        break;
857                    }
858                    case 'I':  {    // I = invert all but (sub)tree
859                        mark_species_in_rest_of_tree(at, 2);
860                        break;
861                    }
862                    case 'c':
863                    case 'x':  {
864                        AWT_graphic_tree_group_state state;
865                        detect_group_state(at, &state, 0);
866
867                        if (!state.has_groups()) { // somewhere inside group
868                        do_parent:
869                            at = at->father;
870                            while (at) {
871                                if (at->gb_node) break;
872                                at = at->father;
873                            }
874
875                            if (at) {
876                                state.clear();
877                                detect_group_state(at, &state, 0);
878                            }
879                        }
880
881                        if (at) {
882                            int next_group_mode;
883
884                            if (key_char == 'x')  { // expand
885                                next_group_mode = state.next_expand_mode();
886                            }
887                            else { // collapse
888                                if (state.all_closed()) goto do_parent;
889                                next_group_mode = state.next_collapse_mode();
890                            }
891
892                            /*int result = */
893                            group_tree(at, next_group_mode, 0);
894
895                            Save    = true;
896                            resize  = true;
897                            compute = true;
898                        }
899                        break;
900                    }
901                    case 'C':
902                    case 'X':  {
903                        AP_tree *root_node                  = at;
904                        while (root_node->father) root_node = root_node->father; // search father
905
906                        awt_assert(root_node);
907
908                        AWT_graphic_tree_group_state state;
909                        detect_group_state(root_node, &state, at);
910
911                        int next_group_mode;
912                        if (key_char == 'X')  { // expand
913                            next_group_mode = state.next_expand_mode();
914                        }
915                        else { // collapse
916                            next_group_mode = state.next_collapse_mode();
917                        }
918
919                        /*int result = */
920                        group_rest_tree(at, next_group_mode, 0);
921
922                        Save    = true;
923                        resize  = true;
924                        compute = true;
925
926
927                        break;
928                    }
929                }
930            }
931        }
932    }
933
934    if (Save) {
935        update_timer = false;
936        exports.save = 1;
937    }
938
939    if (compute && tree_root) {
940        tree_root->compute_tree(gb_main);
941        resize     = true;
942        calc_color = false;
943    }
944
945    if (update_timer && tree_static) {
946        tree_static->update_timers(); // do not reload the tree
947    }
948
949    if (resize) {
950        exports.resize = 1;
951        refresh        = true;
952    }
953
954    if (calc_color && tree_root) {
955        tree_root->calc_color();
956        refresh = true;
957    }
958    if (refresh) {
959        exports.refresh = 1;
960    }
961}
962
963inline double discrete_ruler_lenght(double analog_ruler_lenth, double min_length) {
964    double drl = int(analog_ruler_lenth*10+0.5)/10.0;
965    if (drl<min_length) {
966        drl = min_length;
967    }
968    return drl;
969}
970
971void AWT_graphic_tree::command(AW_device *device, AWT_COMMAND_MODE cmd,
972                               int button, AW_key_mod key_modifier, AW_key_code /*key_code*/, char key_char,
973                               AW_event_type type, AW_pos x, AW_pos y,
974                               AW_clicked_line *cl, AW_clicked_text *ct)
975{
976    static int rot_drag_flag, bl_drag_flag;
977
978    if (type == AW_Keyboard_Press) {
979        return key_command(cmd, key_modifier, key_char, x, y, cl, ct);
980    }
981
982    AP_tree *at;
983    if ( ct->exists && ct->client_data2 && !strcmp((char *) ct->client_data2, "species")){
984        // clicked on a species list :
985        GBDATA *gbd     = (GBDATA *)ct->client_data1;
986        bool    select  = false;
987        bool    refresh = false;
988
989        switch (cmd) {
990            case AWT_MODE_MARK:  {
991                if (type == AW_Mouse_Press) {
992                    switch (button) {
993                        case AWT_M_LEFT:
994                            GB_write_flag(gbd, 1);
995                            refresh = true;
996                            select  = true;
997                            break;
998                        case AWT_M_RIGHT:
999                            GB_write_flag(gbd, 0);
1000                            refresh = true;
1001                            break;
1002                    }
1003                }
1004                break;
1005            }
1006            default :  { // all other modes just select a species :
1007                select = true;
1008                break;
1009            }
1010        }
1011
1012        if (select && type == AW_Mouse_Press) AD_map_viewer(gbd);
1013        if (refresh) this->exports.refresh = 1;
1014
1015        return;
1016    }
1017
1018    if (!this->tree_static) return;     // no tree -> no commands
1019
1020    if ( (rot_ct.exists && (rot_ct.distance == 0) && (!rot_ct.client_data1) &&
1021          rot_ct.client_data2 && !strcmp((char *) rot_ct.client_data2, "ruler") ) ||
1022         (ct && ct->exists && (!ct->client_data1) &&
1023          ct->client_data2 && !strcmp((char *) ct->client_data2, "ruler")))
1024    {
1025        const char *tree_awar;
1026        char awar[256];
1027        float h;
1028        switch(cmd){
1029            case AWT_MODE_SELECT:
1030            case AWT_MODE_ROT:
1031            case AWT_MODE_SPREAD:
1032            case AWT_MODE_SWAP:
1033            case AWT_MODE_SETROOT:
1034            case AWT_MODE_LENGTH:
1035            case AWT_MODE_MOVE: // Move Ruler text
1036                switch(type){
1037                    case AW_Mouse_Press:
1038                        rot_ct          = *ct;
1039                        rot_ct.textArea.moveTo(Position(x, y));
1040                        break;
1041                    case AW_Mouse_Drag: {
1042                        double          scale = device->get_scale();
1043                        const Position &start = rot_ct.textArea.start();
1044
1045                        tree_awar    = show_ruler(device, drag_gc);
1046                        sprintf(awar,"ruler/%s/text_x", tree_awar);
1047
1048                        h = (x - start.xpos())/scale + *GBT_readOrCreate_float(tree_static->gb_tree, awar, 0.0);
1049                        GBT_write_float(this->tree_static->gb_tree, awar, h);
1050                        sprintf(awar,"ruler/%s/text_y", tree_awar);
1051                        h = (y - start.ypos())/scale + *GBT_readOrCreate_float(tree_static->gb_tree, awar, 0.0);
1052                        GBT_write_float(tree_static->gb_tree, awar, h);
1053                        rot_ct.textArea.moveTo(Position(x, y));
1054                        show_ruler(device, drag_gc);
1055                        break;
1056                    }
1057                    case AW_Mouse_Release:
1058                        rot_ct.exists = false;
1059                        exports.resize = 1;
1060                        break;
1061                    default: break;
1062                }
1063                return;
1064            default:    break;
1065        }
1066    }
1067
1068
1069    if ((rot_cl.exists && (!rot_cl.client_data1) &&
1070         rot_cl.client_data2 && !strcmp((char *) rot_cl.client_data2, "ruler") ) ||
1071        (cl && cl->exists && (!cl->client_data1) &&
1072         cl->client_data2 && !strcmp((char *) cl->client_data2, "ruler")))
1073    {
1074        const char *tree_awar;
1075        char awar[256];
1076        float h;
1077        switch(cmd){
1078            case AWT_MODE_ROT:
1079            case AWT_MODE_SPREAD:
1080            case AWT_MODE_SWAP:
1081            case AWT_MODE_SETROOT:
1082            case AWT_MODE_MOVE: // Move Ruler
1083                switch(type){
1084                    case AW_Mouse_Press:
1085                        rot_cl = *cl;
1086                        rot_cl.x0 = x;
1087                        rot_cl.y0 = y;
1088                        break;
1089                    case AW_Mouse_Drag:
1090                        tree_awar = show_ruler(device, this->drag_gc);
1091                        sprintf(awar,"ruler/%s/ruler_x",tree_awar);
1092                        h = (x - rot_cl.x0)/device->get_scale() + *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, 0.0);
1093                        GBT_write_float(this->tree_static->gb_tree, awar, h);
1094                        sprintf(awar,"ruler/%s/ruler_y",tree_awar);
1095                        h = (y - rot_cl.y0)/device->get_scale() + *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, 0.0);
1096                        GBT_write_float(this->tree_static->gb_tree, awar, h);
1097
1098                        rot_cl.x0 = x;
1099                        rot_cl.y0 = y;
1100                        show_ruler(device, this->drag_gc);
1101                        break;
1102                    case AW_Mouse_Release:
1103                        rot_cl.exists = false;
1104                        this->exports.resize = 1;
1105                        break;
1106                    default:
1107                        break;
1108                }
1109                break;
1110
1111
1112            case AWT_MODE_LENGTH:
1113                switch(type){   // resize Ruler
1114                    case AW_Mouse_Press:
1115                        rot_cl = *cl;
1116                        rot_cl.x0 = x;
1117                        if (button==AWT_M_RIGHT) { // if right mouse button is used -> adjust to 1 digit behind comma
1118                            sprintf(awar,"ruler/size");
1119                            tree_awar = show_ruler(device, this->drag_gc);
1120                            double rulerSize = *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, 0.0);
1121                            GBT_write_float(this->tree_static->gb_tree, awar, discrete_ruler_lenght(rulerSize, 0.1));
1122                            tree_awar = show_ruler(device, this->drag_gc);
1123                        }
1124                        break;
1125                    case AW_Mouse_Drag: {
1126                        sprintf(awar,"ruler/size");
1127                        h = *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, 0.0);
1128                        if (button == AWT_M_RIGHT) {
1129                            GBT_write_float(this->tree_static->gb_tree, awar, discrete_ruler_lenght(h, 0.1));
1130                        }
1131                        tree_awar = show_ruler(device, this->drag_gc);
1132
1133                        if (tree_sort == AP_TREE_IRS) {
1134                            double scale = device->get_scale() * irs_tree_ruler_scale_factor;
1135                            h += (rot_cl.x0 - x)/scale;
1136                        }
1137                        else {
1138                            h += (x - rot_cl.x0)/device->get_scale();
1139                        }
1140                        if (h<0.01) h = 0.01;
1141
1142                        double h_rounded = h;
1143                        if (button==AWT_M_RIGHT) { // if right mouse button is used -> adjust to 1 digit behind comma
1144                            h_rounded = discrete_ruler_lenght(h, 0.1);
1145                            GBT_write_float(this->tree_static->gb_tree, awar, h_rounded);
1146                            show_ruler(device, this->drag_gc);
1147                            GBT_write_float(this->tree_static->gb_tree, awar, h);
1148                        }
1149                        else {
1150                            GBT_write_float(this->tree_static->gb_tree, awar, h);
1151                            show_ruler(device, this->drag_gc);
1152                        }
1153
1154                        rot_cl.x0 = x;
1155                        break;
1156                    }
1157                    case AW_Mouse_Release:
1158                        rot_cl.exists = false;
1159                        this->exports.refresh = 1;
1160                        if (button==AWT_M_RIGHT) { // if right mouse button is used -> adjust to 1 digit behind comma
1161                            sprintf(awar,"ruler/size");
1162                            double rulerSize = *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, 0.0);
1163                            GBT_write_float(this->tree_static->gb_tree, awar, discrete_ruler_lenght(rulerSize, 0.1));
1164                        }
1165                        break;
1166                    default:
1167                        break;
1168                }
1169                break;
1170            case AWT_MODE_LINE:
1171                if (type == AW_Mouse_Press) {
1172                    long i;
1173                    sprintf(awar,"ruler/ruler_width");
1174                    i = *GBT_readOrCreate_int(this->tree_static->gb_tree, awar , 0);
1175                    switch(button){
1176                        case AWT_M_LEFT:
1177                            i --;
1178                            if (i<0) i= 0;
1179                            else{
1180                                GBT_write_int(this->tree_static->gb_tree, awar,i);
1181                                this->exports.refresh = 1;
1182                            }
1183                            break;
1184                        case AWT_M_RIGHT:
1185                            i++;
1186                            GBT_write_int(this->tree_static->gb_tree, awar,i);
1187                            show_ruler(device, AWT_GC_CURSOR);
1188                            break;
1189                        default: break;
1190                    }
1191                }
1192                break;
1193            default:    break;
1194        }
1195        return;
1196    }
1197
1198    switch(cmd) {
1199        case AWT_MODE_MOVE:             // two point commands !!!!!
1200            if(button==AWT_M_MIDDLE){
1201                break;
1202            }
1203            switch(type){
1204                case AW_Mouse_Press:
1205                    if( !(cl && cl->exists) ){
1206                        break;
1207                    }
1208
1209                    /*** check security level @@@ ***/
1210                    at = (AP_tree *)cl->client_data1;
1211                    if(at && at->father){
1212                        bl_drag_flag = 1;
1213                        this->rot_at = at;
1214                        this->rot_cl = *cl;
1215                        this->old_rot_cl = *cl;
1216                    }
1217                    break;
1218
1219                case AW_Mouse_Drag:
1220                    if( bl_drag_flag && this->rot_at && this->rot_at->father){
1221                        this->rot_show_line(device);
1222                        if (cl->exists) {
1223                            this->rot_cl = *cl;
1224                        }else{
1225                            rot_cl = old_rot_cl;
1226                        }
1227                        this->rot_show_line(device);
1228                    }
1229                    break;
1230                case AW_Mouse_Release:
1231                    if( bl_drag_flag && this->rot_at && this->rot_at->father){
1232                        this->rot_show_line(device);
1233                        AP_tree *dest= 0;
1234                        if (cl->exists) dest = (AP_tree *)cl->client_data1;
1235                        AP_tree *source = rot_at;
1236                        if (!(source && dest)) {
1237                            aw_message("Please Drag Line to a valid branch");
1238                            break;
1239                        }
1240                        if (source == dest) {
1241                            aw_message("Please drag mouse from source to destination");
1242                            break;
1243                        }
1244
1245                        GB_ERROR error;
1246                        switch(button){
1247                            case AWT_M_LEFT:
1248                                error = source->cantMoveTo(dest);
1249                                if (!error) source->moveTo(dest,cl->length);
1250                                break;
1251                               
1252                            case AWT_M_RIGHT:
1253                                error = source->move_group_info(dest);
1254                                break;
1255                            default:
1256                                error = "????? 45338";
1257                        }
1258
1259                        if (error){
1260                            aw_message(error);
1261                        }else{
1262                            this->exports.refresh = 1;
1263                            this->exports.save = 1;
1264                            this->exports.resize = 1;
1265                            this->tree_root->test_tree();
1266                            this->tree_root->compute_tree(gb_main);
1267                        }
1268                    }
1269                    break;
1270                default:
1271                    break;
1272            }
1273            break;
1274
1275
1276        case AWT_MODE_LENGTH:
1277            if (button == AWT_M_MIDDLE) {
1278                break;
1279            }
1280            switch(type){
1281                case AW_Mouse_Press:
1282                    if( cl && cl->exists ){
1283                        at = (AP_tree *)cl->client_data1;
1284                        if (!at) break;
1285                        bl_drag_flag = 1;
1286                        this->rot_at = at;
1287                        this->rot_cl = *cl;
1288                        this->old_rot_cl = *cl;
1289                        device->transform(cl->x0, cl->y0, rot_cl.x0, rot_cl.y0);
1290                        device->transform(cl->x1, cl->y1, rot_cl.x1, rot_cl.y1);
1291
1292                        this->rot_orientation = comp_rot_orientation(&rot_cl);
1293                        this->rot_spread      = comp_rot_spread(at, this);
1294#if defined(DEBUG)
1295                        printf("rot_spread=%f\n", rot_spread);
1296#endif // DEBUG
1297                        rot_show_triangle(device);
1298                    }
1299                    break;
1300
1301                case AW_Mouse_Drag:
1302                    if( bl_drag_flag && this->rot_at && this->rot_at->father){
1303                        double len, ex, ey, scale;
1304
1305                        rot_show_triangle(device);
1306
1307                        if (rot_at == rot_at->father->leftson){
1308                            len = rot_at->father->leftlen;
1309                        }else{
1310                            len = rot_at->father->rightlen;
1311                        }
1312
1313                        ex = x-rot_cl.x0;
1314                        ey = y-rot_cl.y0;
1315
1316                        len = ex * cos(this->rot_orientation) +
1317                            ey * sin(this->rot_orientation);
1318
1319                        if (button==AWT_M_RIGHT) { // if right mouse button is used -> adjust to 1 digit behind comma
1320                            len = discrete_ruler_lenght(len, 0.0);
1321                        }
1322                        else if (len<0.0) {
1323                            len = 0.0;
1324                        }
1325
1326                        scale = device->get_scale();
1327                        if (tree_sort == AP_TREE_IRS) {
1328                            scale *= irs_tree_ruler_scale_factor;
1329                        }
1330                        len = len/scale;
1331
1332                        if (rot_at == rot_at->father->leftson) {
1333                            rot_at->father->leftlen = len;
1334                        }
1335                        else {
1336                            rot_at->father->rightlen = len;
1337                        }
1338
1339                        rot_show_triangle(device);
1340                    }
1341                    break;
1342
1343                case AW_Mouse_Release:
1344                    this->exports.refresh = 1;
1345                    this->exports.save = 1;
1346                    rot_drag_flag = 0;
1347                    this->tree_root->compute_tree(gb_main);
1348                    break;
1349                default:
1350                    break;
1351            }
1352            break;
1353
1354        case AWT_MODE_ROT:
1355            if(button!=AWT_M_LEFT){
1356                break;
1357            }
1358            switch(type){
1359                case AW_Mouse_Press:
1360                    if( cl && cl->exists ){
1361                        at = (AP_tree *)cl->client_data1;
1362                        if (!at) break;
1363                        rot_drag_flag = 1;
1364                        this->rot_at = at;
1365                        this->rot_cl = *cl;
1366                        this->old_rot_cl = *cl;
1367                        device->transform(cl->x0, cl->y0, rot_cl.x0, rot_cl.y0);
1368                        device->transform(cl->x1, cl->y1, rot_cl.x1, rot_cl.y1);
1369
1370                        this->rot_orientation = comp_rot_orientation(&rot_cl);
1371                        this->rot_spread = comp_rot_spread(at, this);
1372                        rot_show_triangle(device);
1373                    }
1374                    break;
1375
1376                case AW_Mouse_Drag:
1377                    if( rot_drag_flag && this->rot_at && this->rot_at->father){
1378                        double new_a;
1379
1380                        rot_show_triangle(device);
1381                        new_a = atan2(y - rot_cl.y0, x - rot_cl.x0);
1382                        new_a -= this->rot_orientation;
1383                        this->rot_orientation += new_a;
1384                        if (this->rot_at == this->rot_at->father->leftson) {
1385                            this->rot_at->father->gr.left_angle += new_a;
1386                            if (!this->rot_at->father->father) {
1387                                this->rot_at->father->gr.right_angle += new_a;
1388                            }
1389                        }else{
1390                            this->rot_at->father->gr.right_angle += new_a;
1391                            if (!this->rot_at->father->father) {
1392                                this->rot_at->father->gr.left_angle += new_a;
1393                            }
1394                        }
1395                        rot_show_triangle(device);
1396                    }
1397                    break;
1398
1399                case AW_Mouse_Release:
1400                    this->exports.refresh = 1;
1401                    this->exports.save = 1;
1402                    rot_drag_flag = 0;
1403                    break;
1404                default:    break;
1405            }
1406
1407            break;
1408
1409        case AWT_MODE_LZOOM:
1410            if(type==AW_Mouse_Press){
1411                switch(button){
1412                    case AWT_M_LEFT:
1413                        if(cl->exists){
1414                            at = (AP_tree *)cl->client_data1;
1415                            if (!at) break;
1416                            this->tree_root_display = at;
1417                            this->exports.zoom_reset = 1;
1418                            this->exports.refresh = 1;
1419                        }
1420                        break;
1421                    case AWT_M_RIGHT:
1422                        if(this->tree_root_display->father){
1423                            this->tree_root_display =
1424                                this->tree_root_display->father;
1425                            this->exports.refresh = 1;
1426                            this->exports.zoom_reset = 1;
1427                        }
1428                        break;
1429                }
1430                this->tree_root->compute_tree(gb_main);
1431            } /* if type */
1432            break;
1433
1434        case AWT_MODE_RESET:
1435            if(type==AW_Mouse_Press){
1436                switch(button){
1437                    case AWT_M_LEFT:
1438                        /** reset rotation **/
1439                        if(cl->exists){
1440                            at = (AP_tree *)cl->client_data1;
1441                            if (!at) break;
1442                            reset_rotation(at);
1443                            this->exports.save = 1;
1444                            this->exports.refresh = 1;
1445                        }
1446                        break;
1447                    case AWT_M_MIDDLE:
1448                        /** reset spread **/
1449                        if(cl->exists){
1450                            at = (AP_tree *)cl->client_data1;
1451                            if (!at) break;
1452                            reset_spread(at);
1453                            this->exports.save = 1;
1454                            this->exports.refresh = 1;
1455                        }
1456                        break;
1457                    case AWT_M_RIGHT:
1458                        /** reset linewidth **/
1459                        if(cl->exists){
1460                            at = (AP_tree *)cl->client_data1;
1461                            if (!at) break;
1462
1463                            reset_line_width(at);
1464                            if (at->father) { // reset clicked branch
1465                                if (at->father->leftson == at) at->father->gr.left_linewidth = 0;
1466                                else at->father->gr.right_linewidth                          = 0;
1467                            }
1468                            this->exports.save = 1;
1469                            this->exports.refresh = 1;
1470                        }
1471                        break;
1472                }
1473                this->tree_root->compute_tree(gb_main);
1474            } /* if press */
1475            break;
1476
1477        case AWT_MODE_LINE:
1478            if(type==AW_Mouse_Press){
1479                switch(button){
1480                    case AWT_M_LEFT:
1481                        if(cl->exists){
1482                            at = (AP_tree *)cl->client_data1;
1483                            if (!at) break;
1484                            if(at->father){
1485                                if(at->father->leftson == at) {
1486                                    at->father->gr.left_linewidth-=1;
1487                                    if(at->father->gr.left_linewidth<0) at->father->gr.left_linewidth=0;
1488                                }
1489                                else {
1490                                    at->father->gr.right_linewidth-=1;
1491                                    if(at->father->gr.right_linewidth<0) at->father->gr.right_linewidth=0;
1492                                }
1493                                this->exports.save = 1;
1494                                this->exports.refresh = 1;
1495                            }
1496
1497                        }
1498                        break;
1499                    case AWT_M_RIGHT:
1500                        if(cl->exists){
1501                            at = (AP_tree *)cl->client_data1;
1502                            if (!at) break;
1503                            if(at->father){
1504                                if(at->father->leftson == at){
1505                                    at->father->gr.left_linewidth+=1;
1506                                }else{
1507                                    at->father->gr.right_linewidth+=1;
1508                                }
1509                                this->exports.save = 1;
1510                                this->exports.refresh = 1;
1511                            }
1512#if defined(DEBUG)
1513                            printf("key_modifier=%i\n", key_modifier);
1514#endif // DEBUG
1515                        }
1516                        break;
1517                } /* switch button */
1518            } /* if type */
1519            break;
1520
1521        case AWT_MODE_SPREAD:
1522            if(type==AW_Mouse_Press){
1523                switch(button){
1524                    case AWT_M_LEFT:
1525                        if(cl->exists){
1526                            at = (AP_tree *)cl->client_data1;
1527                            if (!at) break;
1528                            at->gr.spread -= PH_CLICK_SPREAD;
1529                            if(at->gr.spread<0) at->gr.spread = 0;
1530                            this->exports.refresh = 1;
1531                            this->exports.save = 1;
1532                        }
1533                        break;
1534                    case AWT_M_RIGHT:
1535                        if(cl->exists){
1536                            at = (AP_tree *)cl->client_data1;
1537                            if (!at) break;
1538                            at->gr.spread += PH_CLICK_SPREAD;
1539                            this->exports.refresh = 1;
1540                            this->exports.save = 1;
1541                        }
1542                        break;
1543                }
1544            } /* if type */
1545            break;
1546
1547    act_like_group:
1548        case AWT_MODE_GROUP:
1549            if(type==AW_Mouse_Press){
1550                if(cl->exists){
1551                    at = (AP_tree *)cl->client_data1;
1552                    if (!at) break;
1553                }else break;
1554
1555                switch(button){
1556                    case AWT_M_LEFT:
1557                        if ( (!at->gr.grouped) && (!at->name)) {
1558                            break; // not grouped and no name
1559                        }
1560
1561                        if (at->is_leaf) break; // don't touch zombies
1562
1563                        if (this->tree_static->gb_tree) {
1564                            GB_ERROR error = this->create_group(at);
1565                            if (error) aw_message(error);
1566                        }
1567                        if (at->name) {
1568                            at->gr.grouped ^= 1;
1569                            this->exports.refresh = 1;
1570                            this->exports.save = 1;
1571                            this->exports.resize = 1;
1572                            this->tree_root->compute_tree(gb_main);
1573                        }
1574                        break;
1575                    case AWT_M_RIGHT:
1576                        if (this->tree_static->gb_tree) {
1577                            this->toggle_group(at);
1578                        }
1579                        this->exports.refresh = 1;
1580                        this->exports.save = 1;
1581                        this->exports.resize = 1;
1582                        this->tree_root->compute_tree(gb_main);
1583
1584                        break;
1585                    default:
1586                        break;
1587                }
1588            } /* if type */
1589            break;
1590
1591        case AWT_MODE_SETROOT:
1592            if(type==AW_Mouse_Press){
1593                switch(button){
1594                    case AWT_M_LEFT:
1595                        if(cl->exists){
1596                            at = (AP_tree *)cl->client_data1;
1597                            if (!at) break;
1598                            at->set_root();
1599                            this->exports.save = 1;
1600                            this->exports.zoom_reset = 1;
1601                            this->tree_root->compute_tree(gb_main);
1602                        }
1603                        break;
1604                }
1605            } /* if type */
1606            break;
1607
1608        case AWT_MODE_SWAP:
1609            if(type==AW_Mouse_Press){
1610                switch(button){
1611                    case AWT_M_LEFT:
1612                        if(cl->exists){
1613                            at = (AP_tree *)cl->client_data1;
1614                            if (!at) break;
1615                            at->swap_sons();
1616
1617                            this->exports.refresh = 1;
1618                            this->exports.save = 1;
1619                        }
1620                        break;
1621                    case AWT_M_RIGHT:
1622                        break;
1623                }
1624            } /* if type */
1625            break;
1626
1627        case AWT_MODE_MARK:
1628            if (type == AW_Mouse_Press && (cl->exists || ct->exists)) {
1629                if (cl->exists) at = (AP_tree *)cl->client_data1;
1630                else at            = (AP_tree *)ct->client_data1;
1631
1632                if (at) {
1633                    GB_transaction dummy(this->tree_static->gb_species_data);
1634
1635                    if (type == AW_Mouse_Press) {
1636                        switch (button) {
1637                            case AWT_M_LEFT:
1638                                mark_species_in_tree(at, 1);
1639                                break;
1640                            case AWT_M_RIGHT:
1641                                mark_species_in_tree(at, 0);
1642                                break;
1643                        }
1644                    }
1645                    this->exports.refresh = 1;
1646                    this->tree_static->update_timers(); // do not reload the tree
1647                    this->tree_root->calc_color();
1648                }
1649            }
1650            break;
1651
1652        case AWT_MODE_NONE:
1653        case AWT_MODE_SELECT:
1654            if(type==AW_Mouse_Press && (cl->exists || ct->exists) && button != AWT_M_MIDDLE){
1655                GB_transaction dummy(this->tree_static->gb_species_data);
1656                if (cl->exists) {
1657                    at = (AP_tree *)cl->client_data1;
1658                }else{
1659                    at = (AP_tree *)ct->client_data1;
1660                }
1661                if (!at) break;
1662
1663                this->exports.refresh = 1;        // No refresh needed !! AD_map_viewer will do the refresh (needed by arb_pars)
1664                AD_map_viewer(at->gb_node, ADMVT_SELECT);
1665
1666                if (button == AWT_M_LEFT) goto act_like_group; // now do the same like in AWT_MODE_GROUP
1667            }
1668            break;
1669
1670        case AWT_MODE_MOD:
1671            if(type==AW_Mouse_Press && (cl->exists || ct->exists) ){
1672                GB_transaction dummy(this->tree_static->gb_species_data);
1673                if (cl->exists) {
1674                    at = (AP_tree *)cl->client_data1;
1675                }else{
1676                    at = (AP_tree *)ct->client_data1;
1677                }
1678                if (!at) break;
1679                AD_map_viewer(at->gb_node,ADMVT_INFO);
1680            }
1681            break;
1682        case AWT_MODE_WWW:
1683            if(type==AW_Mouse_Press && (cl->exists || ct->exists) ){
1684                GB_transaction dummy(this->tree_static->gb_species_data);
1685                if (cl->exists) {
1686                    at = (AP_tree *)cl->client_data1;
1687                }else{
1688                    at = (AP_tree *)ct->client_data1;
1689                }
1690                if (!at) break;
1691                AD_map_viewer(at->gb_node,ADMVT_WWW);
1692            }
1693            break;
1694        default:
1695            break;
1696    }
1697}
1698
1699void AWT_graphic_tree::set_tree_type(AP_tree_sort type)
1700{
1701    if (sort_is_list_style(type)) {
1702        if (tree_sort == type) { // we are already in wanted view
1703            nds_show_all = !nds_show_all; // -> toggle between 'marked' and 'all'
1704        }
1705        else {
1706            nds_show_all = true; // default to all
1707        }
1708    }
1709    tree_sort = type;
1710    switch(type) {
1711        case AP_TREE_RADIAL:
1712            exports.dont_fit_x      = 0;
1713            exports.dont_fit_y      = 0;
1714            exports.dont_fit_larger = 0;
1715            exports.left_offset     = 150;
1716            exports.right_offset    = 150;
1717            exports.top_offset      = 30;
1718            exports.bottom_offset   = 30;
1719            exports.dont_scroll     = 0;
1720            break;
1721
1722        case AP_LIST_SIMPLE:
1723        case AP_LIST_NDS:
1724            exports.dont_fit_x      = 1;
1725            exports.dont_fit_y      = 1;
1726            exports.dont_fit_larger = 0;
1727            exports.left_offset     = short(2*NT_SELECTED_WIDTH+0.5);
1728            exports.right_offset    = 300;
1729            exports.top_offset      = 30;
1730            exports.bottom_offset   = 30;
1731            exports.dont_scroll     = 0;
1732            break;
1733
1734        case AP_TREE_IRS: // folded dendogram
1735            exports.dont_fit_x      = 1;
1736            exports.dont_fit_y      = 1;
1737            exports.dont_fit_larger = 0;
1738            exports.left_offset     = 0;
1739            exports.right_offset    = 300;
1740            exports.top_offset      = 30;
1741            exports.bottom_offset   = 30;
1742            exports.dont_scroll     = 1;
1743            break;
1744
1745        case AP_TREE_NORMAL: // normal dendogram
1746            exports.dont_fit_x      = 0;
1747            exports.dont_fit_y      = 1;
1748            exports.dont_fit_larger = 0;
1749            exports.left_offset     = 0;
1750            exports.right_offset    = 300;
1751            exports.top_offset      = 30;
1752            exports.bottom_offset   = 30;
1753            exports.dont_scroll     = 0;
1754            break;
1755    }
1756}
1757
1758AWT_graphic_tree::AWT_graphic_tree(AW_root *aw_rooti, GBDATA *gb_maini):AWT_graphic()
1759{
1760    line_filter       = AW_SCREEN|AW_CLICK|AW_CLICK_DRAG|AW_SIZE|AW_PRINTER;
1761    vert_line_filter  = AW_SCREEN|AW_PRINTER;
1762    text_filter       = AW_SCREEN|AW_CLICK|AW_PRINTER;
1763    mark_filter       = AW_SCREEN|AW_PRINTER_EXT;
1764    ruler_filter      = AW_SCREEN|AW_CLICK|AW_PRINTER|AW_SIZE;
1765    root_filter       = AW_SCREEN|AW_CLICK|AW_PRINTER_EXT;
1766    set_tree_type(AP_TREE_NORMAL);
1767    tree_root_display = 0;
1768    tree_root         = 0;
1769    y_pos             = 0;
1770    tree_proto        = 0;
1771    tree_static       = 0;
1772    tree_name         = 0;
1773    baselinewidth     = 0;
1774    species_name      = 0;
1775    this->aw_root     = aw_rooti;
1776    this->gb_main     = gb_maini;
1777    rot_ct.exists     = false;
1778    rot_cl.exists     = false;
1779    nds_show_all      = true;
1780}
1781
1782AWT_graphic_tree::~AWT_graphic_tree(void)
1783{
1784    delete this->tree_proto;
1785    delete this->tree_root;
1786    delete this->tree_static;
1787    free(tree_name);
1788}
1789
1790void AWT_graphic_tree::init(AP_tree * tree_prot)
1791{
1792    this->tree_proto = tree_prot->dup();
1793}
1794
1795void AWT_graphic_tree::unload(void)
1796{
1797    rot_at = 0;
1798
1799    delete tree_root;
1800    delete tree_static;
1801    free(tree_name);
1802
1803    tree_root         = 0;
1804    tree_static       = 0;
1805    tree_root_display = 0;
1806    tree_name         = 0;
1807}
1808
1809GB_ERROR AWT_graphic_tree::load(GBDATA *, const char *name,AW_CL link_to_database, AW_CL insert_delete_cbs) {
1810    GB_ERROR error = 0;
1811
1812    if (name[0] == 0 || strcmp(name, "tree_????") == 0) { // no tree selected
1813        unload();
1814        zombies    = 0;
1815        duplicates = 0;
1816    }
1817    else {
1818        AP_tree      *apt = tree_proto->dup();
1819        AP_tree_root *tr  = new AP_tree_root(gb_main,apt,name);
1820
1821        error = apt->load(tr,(int)link_to_database,(GB_BOOL)insert_delete_cbs, GB_TRUE, &zombies, &duplicates); // show status
1822        unload();
1823
1824        if (error) {
1825            delete tr;
1826            delete apt;
1827        }
1828        else {
1829            tree_root         = apt;
1830            tree_static       = tr;
1831            tree_root_display = this->tree_root;
1832
1833            tree_root->compute_tree(gb_main);
1834           
1835            tree_name = strdup(name);
1836
1837            tr->root_changed_cd = (void*)this;
1838            tr->root_changed    = AWT_graphic_tree_root_changed;
1839            tr->node_deleted_cd = (void*)this;
1840            tr->node_deleted    = AWT_graphic_tree_node_deleted;
1841        }
1842    }
1843
1844    return error;
1845}
1846
1847GB_ERROR AWT_graphic_tree::save(GBDATA */*dummy*/, const char */*name*/, AW_CL /*cd1*/, AW_CL /*cd2*/) {
1848    GB_ERROR error = NULL;
1849    if (tree_root) {
1850        error = tree_root->saveTree();
1851    }
1852    else if (tree_name) {
1853        if (tree_static && tree_static->gb_tree_gone) {
1854            GB_transaction ta(gb_main);
1855            error = GB_delete(tree_static->gb_tree_gone);
1856            error = ta.close(error);
1857
1858            if (!error) {
1859                aw_message(GBS_global_string("Tree '%s' lost all leaves and has been deleted", tree_name));
1860#if defined(DEVEL_RALF)
1861#warning somehow update selected tree
1862
1863                // solution: currently selected tree (in NTREE, maybe also in PARSIMONY)
1864                // needs to add a delete callback on treedata in DB
1865
1866#endif // DEVEL_RALF
1867            }
1868
1869            tree_static->gb_tree_gone = 0; // do not delete twice
1870        }
1871    }
1872    return error;
1873}
1874
1875int AWT_graphic_tree::check_update(GBDATA *gbdummy)
1876{
1877    AWUSE(gbdummy);
1878    AP_UPDATE_FLAGS flags;
1879    char *name;
1880    GB_ERROR error;
1881    if (!this->tree_static) return 0;
1882    GB_transaction dummy(gb_main);
1883
1884    if (!this->tree_root) {
1885        flags = AP_UPDATE_ERROR;
1886    }
1887    else {
1888        flags = this->tree_root->check_update();
1889        switch (flags){
1890            case AP_UPDATE_OK:
1891            case AP_UPDATE_ERROR:
1892                return flags;
1893            case AP_UPDATE_RELOADED:
1894                name = strdup(this->tree_static->tree_name);
1895                error = this->load(gb_main, name, 1, 0);
1896                if (error) aw_message(error);
1897                free(name);
1898                this->exports.resize = 1;
1899                break;
1900            case AP_UPDATE_RELINKED:
1901                error = this->tree_root->relink();
1902                if (error) aw_message(error);
1903                else    this->tree_root->compute_tree(gb_main);
1904                break;
1905        }
1906    }
1907    return (int)flags;
1908}
1909
1910void AWT_graphic_tree::update(GBDATA *gbdummy){
1911    AWUSE(gbdummy);
1912    if (!this->tree_static) return;
1913    if (!this->tree_root) return;
1914    GB_transaction dummy(gb_main);
1915    this->tree_root->update();
1916}
1917
1918void AWT_graphic_tree::NT_scalebox(int gc, double x, double y, double width)
1919{
1920    double diam  = width/disp_device->get_scale();
1921    double diam2 = diam+diam;
1922    disp_device->set_fill(gc, this->grey_level);
1923    disp_device->box(gc, true, x-diam, y-diam, diam2, diam2, mark_filter, 0, 0);
1924}
1925
1926void AWT_graphic_tree::NT_emptybox(int gc, double x, double y, double width)
1927{
1928    double diam  = width/disp_device->get_scale();
1929    double diam2 = diam+diam;
1930    disp_device->set_line_attributes(gc, 0.0, AW_SOLID);
1931    disp_device->box(gc, false, x-diam, y-diam, diam2, diam2, mark_filter, 0, 0);
1932}
1933
1934void AWT_graphic_tree::NT_rotbox(int gc, double x, double y, double width) // box with one corner down
1935{
1936    double diam = width / disp_device->get_scale();
1937    double x1   = x-diam;
1938    double x2   = x+diam;
1939    double y1   = y-diam;
1940    double y2   = y+diam;
1941
1942    disp_device->line(gc, x1, y, x, y1, mark_filter, 0, 0);
1943    disp_device->line(gc, x2, y, x, y1, mark_filter, 0, 0);
1944    disp_device->line(gc, x1, y, x, y2, mark_filter, 0, 0);
1945    disp_device->line(gc, x2, y, x, y2, mark_filter, 0, 0);
1946}
1947
1948bool AWT_show_remark_branch(AW_device *device, const char *remark_branch, bool is_leaf, AW_pos x, AW_pos y, AW_pos alignment, AW_bitset filteri, AW_CL cd1, AW_CL cd2) {
1949    // returns true if a bootstrap was DISPLAYED
1950    char       *end          = 0;
1951    int         bootstrap    = int(strtol(remark_branch, &end, 10));
1952    bool        is_bootstrap = end[0] == '%' && end[1] == 0;
1953    bool        show         = true;
1954    const char *text         = 0;
1955
1956    if (is_bootstrap) {
1957        if (bootstrap == 100) {
1958            show           = !is_leaf; // do not show 100% bootstraps at leafs
1959            if (show) text = "100%";
1960        }
1961        else {
1962            if (bootstrap == 0) {
1963                text = "<1%"; // show instead of '0%'
1964            }
1965            else {
1966                text = GBS_global_string("%i%%", bootstrap);
1967            }
1968        }
1969    }
1970    else {
1971        text = remark_branch;
1972    }
1973
1974    if (show) {
1975        awt_assert(text != 0);
1976        device->text(AWT_GC_BRANCH_REMARK, text, x, y, alignment, filteri, cd1, cd2);
1977    }
1978
1979    return is_bootstrap && show;
1980}
1981
1982
1983double AWT_graphic_tree::show_dendrogram(AP_tree *at, double x_father, double x_son)
1984{
1985    double ny0, ny1, nx0, nx1, ry, l_min, l_max, xoffset, yoffset;
1986    AWUSE(x_father);
1987    ny0 = y_pos;
1988
1989    if (disp_device->type() != AW_DEVICE_SIZE){ // tree below cliprect bottom can be cut
1990        AW_pos xs = 0;
1991        AW_pos ys = y_pos - scaled_branch_distance *2.0;
1992        AW_pos X,Y;
1993        disp_device->transform(xs,ys,X,Y);
1994        if ( Y > disp_device->clip_rect.b){
1995            y_pos += scaled_branch_distance;
1996            return ny0;
1997        }
1998        ys = y_pos + scaled_branch_distance * (at->gr.view_sum+2);
1999        disp_device->transform(xs,ys,X,Y);
2000
2001        if ( Y  < disp_device->clip_rect.t){
2002            y_pos += scaled_branch_distance*at->gr.view_sum;
2003            return ny0;
2004        }
2005    }
2006
2007    if (at->is_leaf) {
2008        /* draw mark box */
2009        if (at->gb_node && GB_read_flag(at->gb_node)){
2010            NT_scalebox(at->gr.gc, x_son, ny0, NT_BOX_WIDTH);
2011        }
2012        if (at->name &&
2013            at->name[0] == this->species_name[0] &&
2014            strcmp(at->name,this->species_name) == 0)
2015        {
2016            x_cursor = x_son;
2017            y_cursor = ny0;
2018        }
2019
2020        if (at->name && (disp_device->filter & text_filter) ){
2021            // text darstellen
2022            const char *data = make_node_text_nds(this->gb_main, at->gb_node,0,at->get_gbt_tree(), tree_name);
2023
2024            //             offset = scale*0.4;
2025
2026
2027            const AW_font_information *fontinfo = disp_device->get_font_information(at->gr.gc,'A');
2028
2029#if defined(DEBUG) && 0
2030            static bool dumped = false;
2031            if (!dumped) {
2032                for (int c = 32; c <= 255; c++) {
2033                    const AW_font_information *fi = disp_device->get_font_information(at->gr.gc,(char)c);
2034                    printf("fontinfo: %3i '%c' ascent=%2i descent=%2i width=%2i\n",
2035                           c, (char)c,
2036                           fi->this_letter.ascent,
2037                           fi->this_letter.descent,
2038                           fi->this_letter.width
2039                           );
2040                    if (c == 255) {
2041                        printf("fontinfo (all-maximas): ascent=%2i descent=%2i width=%2i\n",
2042                               fi->max_all_letter.ascent,
2043                               fi->max_all_letter.descent,
2044                               fi->max_all_letter.width
2045                               );
2046                        printf("fontinfo (ascii-maximas): ascent=%2i descent=%2i width=%2i\n",
2047                               fi->max_letter.ascent,
2048                               fi->max_letter.descent,
2049                               fi->max_letter.width
2050                               );
2051                    }
2052                }
2053
2054                dumped = true;
2055            }
2056#endif // DEBUG
2057
2058
2059            double unscale = 1.0/disp_device->get_scale();
2060            yoffset = scaled_font.ascent*.5;
2061            xoffset = ((fontinfo->max_letter.width * 0.5) + NT_BOX_WIDTH) * unscale;
2062
2063            disp_device->text(at->gr.gc,data ,
2064                              (AW_pos) x_son+xoffset,(AW_pos) ny0+yoffset,
2065                              (AW_pos) 0 , text_filter,
2066                              (AW_CL) at , (AW_CL) 0 );
2067        }
2068
2069
2070        y_pos += scaled_branch_distance;
2071        return ny0;
2072    }
2073
2074    if (at->gr.grouped) {
2075        l_min = at->gr.min_tree_depth;
2076        l_max = at->gr.tree_depth;
2077        ny1 = y_pos += scaled_branch_distance*(at->gr.view_sum-1);
2078        nx0 = x_son + l_max;
2079        nx1 = x_son + l_min;
2080
2081        int linewidth = 0;
2082        if (at->father) {
2083            if (at->father->leftson == at) {
2084                linewidth = at->father->gr.left_linewidth;
2085            }else{
2086                linewidth = at->father->gr.right_linewidth;
2087            }
2088        }
2089
2090        disp_device->set_line_attributes(at->gr.gc,linewidth+baselinewidth,AW_SOLID);
2091
2092        AW_pos q[8];
2093        q[0] = x_son;   q[1] = ny0;
2094        q[2] = x_son;   q[3] = ny1;
2095        q[4] = nx1;     q[5] = ny1;
2096        q[6] = nx0;     q[7] = ny0;
2097
2098        disp_device->set_fill(at->gr.gc, grey_level);
2099        disp_device->filled_area(at->gr.gc, 4, &q[0], line_filter, (AW_CL)at,0);
2100
2101        // double add_y_offset = scale*0.3;
2102
2103        const AW_font_information *fontinfo    = disp_device->get_font_information(at->gr.gc,'A');
2104        double                     text_ascent = fontinfo->max_letter.ascent/ disp_device->get_scale() ;
2105
2106        yoffset = (ny1-ny0+text_ascent)*.5;
2107        xoffset = text_ascent*.5;
2108
2109        if (at->gb_node && (disp_device->filter & text_filter)){
2110            const char *data = make_node_text_nds(this->gb_main, at->gb_node,0,at->get_gbt_tree(), tree_name);
2111
2112            disp_device->text(at->gr.gc,data ,
2113                              (AW_pos) nx0+xoffset,(AW_pos) ny0+yoffset,
2114                              (AW_pos) 0 , text_filter,
2115                              (AW_CL) at , (AW_CL) 0 );
2116        }
2117        disp_device->text(at->gr.gc,(char *)GBS_global_string(" %i",at->gr.leave_sum),
2118                          x_son+xoffset, ny0+yoffset,
2119                          0,text_filter,(AW_CL)at,0);
2120
2121        y_pos += scaled_branch_distance;
2122
2123        return (ny0+ny1)*.5;
2124    }
2125    nx0 = (x_son +  at->leftlen) ;
2126    nx1 = (x_son +  at->rightlen) ;
2127    ny0 = show_dendrogram(at->leftson,x_son, nx0);
2128    ry  = (double) y_pos - .5*scaled_branch_distance;
2129    ny1 = (double) show_dendrogram(at->rightson,x_son, nx1);
2130
2131    if (at->name) {
2132        NT_rotbox(at->gr.gc,x_son, ry, NT_BOX_WIDTH*2);
2133    }
2134
2135    if (at->leftson->remark_branch ) {
2136        bool bootstrap_shown = AWT_show_remark_branch(disp_device, at->leftson->remark_branch, at->leftson->is_leaf, nx0, ny0-scaled_font.ascent*0.1, 1, text_filter, (AW_CL)at, 0);
2137
2138        if (show_circle && bootstrap_shown){
2139            AWT_show_bootstrap_circle(disp_device,at->leftson->remark_branch, circle_zoom_factor, circle_max_size, at->leftlen,nx0, ny0, use_ellipse, scaled_branch_distance, text_filter, (AW_CL) at->leftson, (AW_CL) 0);
2140        }
2141    }
2142
2143    if (at->rightson->remark_branch ) {
2144        bool bootstrap_shown = AWT_show_remark_branch(disp_device, at->rightson->remark_branch, at->rightson->is_leaf, nx1, ny1-scaled_font.ascent*0.1, 1, text_filter, (AW_CL)at, 0);
2145
2146        if (show_circle && bootstrap_shown){
2147            AWT_show_bootstrap_circle(disp_device,at->rightson->remark_branch,circle_zoom_factor, circle_max_size, at->rightlen,nx1, ny1, use_ellipse, scaled_branch_distance, text_filter, (AW_CL) at->rightson, (AW_CL) 0);
2148        }
2149    }
2150
2151#if defined(DEBUG)
2152    if (at->gb_node) {
2153        disp_device->text(at->gr.gc, GBS_global_string("%p(gb_node)", at->gb_node), x_father, (ny0+ny1)/2, 0, text_filter, (AW_CL)at, 0);
2154    }
2155#endif // DEBUG
2156
2157
2158    int lw = at->gr.left_linewidth+baselinewidth;
2159    // disp_device->set_line_attributes(at->gr.gc,lw,AW_SOLID);
2160    disp_device->set_line_attributes(at->leftson->gr.gc,lw,AW_SOLID);
2161    disp_device->line(at->leftson->gr.gc, x_son, ny0, nx0, ny0, line_filter, (AW_CL)at->leftson,0);
2162    // disp_device->set_line_attributes(at->gr.gc,lw,AW_SOLID);
2163    disp_device->line(at->leftson->gr.gc, x_son, ny0, x_son, ry, vert_line_filter, (AW_CL)at,0);
2164
2165    int rw = at->gr.right_linewidth+baselinewidth;
2166    // disp_device->set_line_attributes(at->gr.gc,rw,AW_SOLID);
2167    disp_device->set_line_attributes(at->rightson->gr.gc,rw,AW_SOLID);
2168    disp_device->line(at->rightson->gr.gc,x_son, ny1, nx1, ny1, line_filter, (AW_CL)at->rightson,0);
2169    // disp_device->set_line_attributes(at->gr.gc,rw,AW_SOLID);
2170    disp_device->line(at->rightson->gr.gc, x_son, ry, x_son, ny1, vert_line_filter, (AW_CL)at,0);
2171
2172    return ry;
2173}
2174
2175
2176void AWT_graphic_tree::scale_text_koordinaten(AW_device *device, int gc,double& x,double& y,double orientation,int flag)
2177{
2178    const AW_font_information *fontinfo    = device->get_font_information(gc,'A');
2179    double                     text_height = fontinfo->max_letter.height/ disp_device->get_scale() ;
2180    double                     dist        = fontinfo->max_letter.height/ disp_device->get_scale();
2181   
2182    if (flag==1) {
2183        dist += 1;
2184    }
2185    else {
2186        x += cos(orientation) * dist;
2187        y += sin(orientation) * dist + 0.3*text_height;
2188    }
2189    return;
2190}
2191
2192
2193//  ********* shell and radial tree
2194void AWT_graphic_tree::show_radial_tree(AP_tree * at, double x_center,
2195                                        double y_center, double tree_spread,double tree_orientation,
2196                                        double x_root, double y_root, int linewidth)
2197{
2198    double l,r,w,z,l_min,l_max;
2199
2200    disp_device->set_line_attributes(at->gr.gc,linewidth+baselinewidth,AW_SOLID);
2201    disp_device->line(at->gr.gc, x_root, y_root, x_center, y_center, line_filter, (AW_CL)at,0);
2202
2203    // draw mark box
2204    if (at->gb_node && GB_read_flag(at->gb_node)) {
2205        NT_scalebox(at->gr.gc, x_center, y_center, NT_BOX_WIDTH);
2206    }
2207
2208    if( at->is_leaf){
2209        if (at->name && (disp_device->filter & text_filter) ){
2210            if (at->name[0] == this->species_name[0] &&
2211                !strcmp(at->name,this->species_name)) {
2212                x_cursor = x_center; y_cursor = y_center;
2213            }
2214            scale_text_koordinaten(disp_device,at->gr.gc, x_center,y_center,tree_orientation,0);
2215            const char *data =  make_node_text_nds(this->gb_main,at->gb_node,0,at->get_gbt_tree(), tree_name);
2216            // PJ vectorfont - example for calling
2217            //                      disp_device->zoomtext(at->gr.gc,data,
2218            //                              (AW_pos)x_center,(AW_pos) y_center,
2219            //                              0.005, .5-.5*cos(tree_orientation), 0,
2220            //                              text_filter, (AW_CL) at, (AW_CL) 0);
2221
2222            disp_device->text(at->gr.gc,data,
2223                              (AW_pos)x_center,(AW_pos) y_center,
2224                              (AW_pos) .5 - .5 * cos(tree_orientation),
2225                              text_filter,  (AW_CL) at , (AW_CL) 0 );
2226        }
2227        return;
2228    }
2229
2230    if( at->gr.grouped){
2231        l_min = at->gr.min_tree_depth;
2232        l_max = at->gr.tree_depth;
2233
2234        r    = l = 0.5;
2235        AW_pos q[6];
2236        q[0] = x_center;
2237        q[1] = y_center;
2238        w    = tree_orientation + r*0.5*tree_spread+ at->gr.right_angle;
2239        q[2] = x_center+l_min*cos(w);
2240        q[3] = y_center+l_min*sin(w);
2241        w    = tree_orientation - l*0.5*tree_spread + at->gr.right_angle;
2242        q[4] = x_center+l_max*cos(w);
2243        q[5] = y_center+l_max*sin(w);
2244
2245        disp_device->set_fill(at->gr.gc, grey_level);
2246        disp_device->filled_area(at->gr.gc, 3, &q[0], line_filter, (AW_CL)at,0);
2247
2248        if (at->gb_node && (disp_device->filter & text_filter) ){
2249            w = tree_orientation + at->gr.right_angle;
2250            l_max = (l_max+l_min)*.5;
2251            x_center= x_center+l_max*cos(w);
2252            y_center= y_center+l_max*sin(w);
2253            scale_text_koordinaten(disp_device,at->gr.gc,x_center,y_center,w,0);
2254
2255            /* insert text (e.g. name of group) */
2256            const char *data = make_node_text_nds(this->gb_main, at->gb_node,0,at->get_gbt_tree(), tree_name);
2257            //PJ vectorfont - possibly groups should be rendered bigger than species
2258            //                        disp_device->zoomtext(at->gr.gc ,data,
2259            //                                (AW_pos)x_center,(AW_pos) y_center, 0.01,
2260            //                                (AW_pos).5 - .5 * cos(tree_orientation),0 ,
2261            //                                text_filter,
2262            //                                (AW_CL) at , (AW_CL) 0 );
2263            disp_device->text(at->gr.gc,data,
2264                              (AW_pos)x_center,(AW_pos) y_center,
2265                              (AW_pos).5 - .5 * cos(tree_orientation),
2266                              text_filter,
2267                              (AW_CL) at , (AW_CL) 0 );
2268        }
2269        return;
2270    }
2271    l = (double) at->leftson->gr.view_sum / (double)at->gr.view_sum;
2272    r = 1.0 - (double)l;
2273
2274    if (at->leftson->gr.gc > at->rightson->gr.gc) {
2275        // bring selected gc to front
2276
2277        /*** left branch ***/
2278        w = r*0.5*tree_spread + tree_orientation + at->gr.left_angle;
2279        z = at->leftlen;
2280        show_radial_tree(at->leftson,
2281                         x_center+ z * cos(w),
2282                         y_center+ z * sin(w),
2283                         (at->leftson->is_leaf) ? 1.0 :
2284                         tree_spread * l * at->leftson->gr.spread,
2285                         w,
2286                         x_center, y_center, at->gr.left_linewidth );
2287
2288        /*** right branch ***/
2289        w = tree_orientation - l*0.5*tree_spread + at->gr.right_angle;
2290        z = at->rightlen;
2291        show_radial_tree(at->rightson,
2292                         x_center+ z * cos(w),
2293                         y_center+ z * sin(w),
2294                         (at->rightson->is_leaf) ? 1.0 :
2295                         tree_spread * r * at->rightson->gr.spread,
2296                         w,
2297                         x_center, y_center, at->gr.right_linewidth);
2298    }else{
2299        /*** right branch ***/
2300        w = tree_orientation - l*0.5*tree_spread + at->gr.right_angle;
2301        z = at->rightlen;
2302        show_radial_tree(at->rightson,
2303                         x_center+ z * cos(w),
2304                         y_center+ z * sin(w),
2305                         (at->rightson->is_leaf) ? 1.0 :
2306                         tree_spread * r * at->rightson->gr.spread,
2307                         w,
2308                         x_center, y_center, at->gr.right_linewidth);
2309
2310        /*** left branch ***/
2311        w = r*0.5*tree_spread + tree_orientation + at->gr.left_angle;
2312        z = at->leftlen;
2313        show_radial_tree(at->leftson,
2314                         x_center+ z * cos(w),
2315                         y_center+ z * sin(w),
2316                         (at->leftson->is_leaf) ? 1.0 :
2317                         tree_spread * l * at->leftson->gr.spread,
2318                         w,
2319                         x_center, y_center, at->gr.left_linewidth );
2320    }
2321    if (show_circle){
2322        /*** left branch ***/
2323        if (at->leftson->remark_branch){
2324            w = r*0.5*tree_spread + tree_orientation + at->gr.left_angle;
2325            z = at->leftlen * .5;
2326            AWT_show_bootstrap_circle(disp_device,at->leftson->remark_branch,circle_zoom_factor, circle_max_size, at->leftlen,x_center+ z * cos(w),y_center+ z * sin(w), false, 0, text_filter, (AW_CL)at,0);
2327        }
2328        if (at->rightson->remark_branch){
2329            /*** right branch ***/
2330            w = tree_orientation - l*0.5*tree_spread + at->gr.right_angle;
2331            z = at->rightlen * .5;
2332            AWT_show_bootstrap_circle(disp_device,at->rightson->remark_branch,circle_zoom_factor, circle_max_size, at->rightlen,x_center+ z * cos(w),y_center+ z * sin(w), false, 0, text_filter, (AW_CL)at,0);
2333        }
2334    }
2335}
2336
2337const char *AWT_graphic_tree::show_ruler(AW_device *device, int gc) {
2338    const char *tree_awar = 0;
2339
2340    char awar[256];
2341    if (!this->tree_static->gb_tree)    return 0;   // no ruler !!!!
2342    GB_transaction dummy(this->tree_static->gb_tree);
2343
2344    sprintf(awar,"ruler/size");
2345    float ruler_size = *GBT_readOrCreate_float( this->tree_static->gb_tree, awar, 0.1);
2346    float ruler_x = 0.0;
2347    float ruler_y = 0.0;
2348    float ruler_text_x = 0.0;
2349    float ruler_text_y = 0.0;
2350    float ruler_add_y = 0.0;
2351    float ruler_add_x = 0.0;
2352
2353    switch (tree_sort) {
2354        case AP_TREE_NORMAL:
2355            tree_awar = "LIST";
2356            break;
2357        case AP_TREE_RADIAL:
2358            tree_awar = "RADIAL";
2359            break;
2360        case AP_TREE_IRS:
2361            tree_awar = "IRS";
2362            break;
2363        case AP_LIST_SIMPLE:
2364        case AP_LIST_NDS:
2365            // rulers not allowed in these display modes
2366            awt_assert(0);
2367            tree_awar = 0;
2368            break;
2369    }
2370
2371    if (tree_awar) {
2372        sprintf(awar,"ruler/%s/ruler_y",tree_awar);
2373        if (!GB_search(this->tree_static->gb_tree,awar,GB_FIND)){
2374            if (device->type() == AW_DEVICE_SIZE) {
2375                AW_world world;
2376                device ->get_size_information(&world);
2377                ruler_y = world.b * 1.3;
2378            }
2379        }
2380
2381        double half_ruler_width = ruler_size*0.5;
2382
2383        switch (tree_sort) {
2384            case AP_TREE_IRS:
2385                // scale is different for IRS tree -> adjust:
2386                half_ruler_width *= irs_tree_ruler_scale_factor;
2387                ruler_y     = 0;
2388                ruler_add_y = this->list_tree_ruler_y;
2389                ruler_add_x = -half_ruler_width;
2390                break;
2391            case AP_TREE_NORMAL:
2392                ruler_y     = 0;
2393                ruler_add_y = this->list_tree_ruler_y;
2394                ruler_add_x = half_ruler_width;
2395                break;
2396            default:
2397                break;
2398        }
2399
2400
2401        ruler_y = ruler_add_y + *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, ruler_y);
2402
2403        sprintf(awar,"ruler/%s/ruler_x",tree_awar);
2404        ruler_x = ruler_add_x + *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, ruler_x);
2405
2406        sprintf(awar,"ruler/%s/text_x", tree_awar);
2407        // ruler_text_x = *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, ruler_text_x) * ruler_scale;
2408        ruler_text_x = *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, ruler_text_x);
2409
2410        sprintf(awar,"ruler/%s/text_y", tree_awar);
2411        // ruler_text_y = *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, ruler_text_y) * ruler_scale;
2412        ruler_text_y = *GBT_readOrCreate_float(this->tree_static->gb_tree, awar, ruler_text_y);
2413
2414        sprintf(awar,"ruler/ruler_width");
2415        double ruler_width = *GBT_readOrCreate_int(this->tree_static->gb_tree, awar, 0);
2416
2417        device->set_line_attributes(gc, ruler_width+baselinewidth, AW_SOLID);
2418
2419        device->line(gc,
2420                     ruler_x - half_ruler_width, ruler_y,
2421                     ruler_x + half_ruler_width, ruler_y,
2422                     this->ruler_filter,
2423                     0, (AW_CL)"ruler");
2424        char ruler_text[20];
2425        sprintf(ruler_text,"%4.2f",ruler_size);
2426
2427        device->text(gc, ruler_text,
2428                     ruler_x + ruler_text_x,
2429                     ruler_y + ruler_text_y,
2430                     0.5,
2431                     this->ruler_filter & ~AW_SIZE,
2432                     0, (AW_CL)"ruler");
2433    }
2434    return tree_awar;
2435}
2436
2437void AWT_graphic_tree::show_nds_list(GBDATA * dummy, bool use_nds)
2438{
2439    AWUSE(dummy);
2440    AW_pos y_position = scaled_branch_distance;
2441    AW_pos x_position = 2*NT_SELECTED_WIDTH / disp_device->get_scale();
2442    long   max_strlen = 0;
2443
2444    disp_device->text(nds_show_all ? AWT_GC_CURSOR : AWT_GC_SELECTED,
2445                      GBS_global_string("%s of %s species", use_nds ? "NDS List" : "Simple list", nds_show_all ? "all" : "marked"),
2446                      (AW_pos) x_position, (AW_pos) 0,
2447                      (AW_pos) 0, text_filter,
2448                      (AW_CL) 0, (AW_CL) 0);
2449
2450    for (GBDATA *gb_species = nds_show_all ? GBT_first_species(gb_main) : GBT_first_marked_species(gb_main);
2451         gb_species;
2452         gb_species = nds_show_all ? GBT_next_species(gb_species) : GBT_next_marked_species(gb_species))
2453    {
2454        y_position += scaled_branch_distance;
2455
2456        const char *name = GBT_read_name(gb_species);
2457
2458        if (strcmp(name, this->species_name) == 0) {
2459            x_cursor = 0;
2460            y_cursor = y_position;
2461        }
2462
2463        bool is_marked = GB_read_flag(gb_species);
2464        if (is_marked) NT_scalebox(AWT_GC_SELECTED, 0, y_position, NT_BOX_WIDTH);
2465
2466        {
2467            AW_pos xs = 0;
2468            AW_pos X,Y;
2469            disp_device->transform(xs, y_position+scaled_branch_distance, X, Y);
2470            if ( Y  < disp_device->clip_rect.t) continue;
2471            disp_device->transform(xs, y_position-scaled_branch_distance, X, Y);
2472            if ( Y > disp_device->clip_rect.b) continue;
2473        }
2474
2475        if (disp_device->type() != AW_DEVICE_SIZE){ // tree below cliprect bottom can be cut
2476            const char *data;
2477            if (use_nds) {
2478                data = make_node_text_nds(gb_main, gb_species, 1, 0, tree_name);
2479            }
2480            else {
2481                data = name;
2482            }
2483
2484            long slen = strlen(data);
2485            int  gc   = AWT_GC_NSELECTED;
2486
2487            if (nds_show_all && is_marked) {
2488                gc = AWT_GC_SELECTED;
2489            }
2490            else {
2491                int color_group     = AWT_species_get_dominant_color(gb_species);
2492                if (color_group) gc = AWT_GC_FIRST_COLOR_GROUP+color_group-1;
2493            }
2494
2495            disp_device->text(gc, data,
2496                              (AW_pos) x_position, (AW_pos) y_position + scaled_font.ascent*.5,
2497                              (AW_pos) 0, text_filter,
2498                              (AW_CL) gb_species, (AW_CL) "species", slen);
2499
2500            if (slen> max_strlen) max_strlen = slen;
2501        }
2502    }
2503
2504    disp_device->invisible(AWT_GC_CURSOR, 0, 0, -1, 0, 0);
2505    disp_device->invisible(AWT_GC_CURSOR, max_strlen*scaled_font.width, y_position+scaled_branch_distance, -1, 0, 0);
2506}
2507
2508void AWT_graphic_tree::show(AW_device *device)  {
2509    if (tree_static && tree_static->gb_tree) {
2510        check_update(gb_main);
2511    }
2512    if (!tree_root_display) { // if there is no tree
2513        if (sort_is_tree_style(tree_sort)) { // but display style needs tree
2514            set_tree_type(AP_LIST_NDS); // => switch display style
2515        }
2516    }
2517
2518    disp_device = device;
2519
2520    const AW_font_information *fontinfo = disp_device->get_font_information(AWT_GC_SELECTED, 0);
2521
2522    scaled_font.init(fontinfo->max_letter, 1/device->get_scale());
2523    scaled_branch_distance = scaled_font.height * aw_root->awar(AWAR_DTREE_VERICAL_DIST)->read_float();
2524
2525    make_node_text_init(gb_main);
2526
2527    grey_level         = aw_root->awar(AWAR_DTREE_GREY_LEVEL)->read_int()*.01;
2528    baselinewidth      = (int)aw_root->awar(AWAR_DTREE_BASELINEWIDTH)->read_int();
2529    show_circle        = (int)aw_root->awar(AWAR_DTREE_SHOW_CIRCLE)->read_int();
2530    circle_zoom_factor = aw_root->awar(AWAR_DTREE_CIRCLE_ZOOM)->read_float();
2531    circle_max_size    = aw_root->awar(AWAR_DTREE_CIRCLE_MAX_SIZE)->read_float();
2532    use_ellipse        = aw_root->awar(AWAR_DTREE_USE_ELLIPSE)->read_int();
2533
2534    freeset(species_name, aw_root->awar(AWAR_SPECIES_NAME)->read_string());
2535
2536    x_cursor = y_cursor = 0.0;
2537
2538    switch (tree_sort) {
2539        case AP_TREE_NORMAL:
2540            if (!tree_root_display) return;
2541            y_pos = 0.05;
2542            show_dendrogram(tree_root_display, 0, 0);
2543            list_tree_ruler_y = y_pos + 2.0 * scaled_branch_distance;
2544            break;
2545
2546        case AP_TREE_RADIAL:
2547            if (!tree_root_display)   return;
2548            NT_emptybox(tree_root_display->gr.gc, 0, 0, NT_ROOT_WIDTH);
2549            show_radial_tree(tree_root_display, 0,0,2*M_PI, 0.0,0, 0, tree_root_display->gr.left_linewidth);
2550            break;
2551
2552        case AP_TREE_IRS:
2553            show_irs_tree(tree_root_display,disp_device,fontinfo->max_letter.height);
2554            list_tree_ruler_y = y_pos;
2555            break;
2556
2557        case AP_LIST_NDS:       // this is the list all/marked species mode (no tree)
2558            show_nds_list(gb_main, true);
2559            break;
2560
2561        case AP_LIST_SIMPLE:    // simple list of names (used at startup only)
2562            show_nds_list(gb_main, false);
2563            break;           
2564    }
2565    if (x_cursor != 0.0 || y_cursor != 0.0) {
2566        NT_emptybox(AWT_GC_CURSOR, x_cursor, y_cursor, NT_SELECTED_WIDTH);
2567    }
2568    if (sort_is_tree_style(tree_sort)) { // show rulers in tree-style display modes
2569        show_ruler(device, AWT_GC_CURSOR);
2570    }
2571}
2572
2573void AWT_graphic_tree::info(AW_device *device, AW_pos x, AW_pos y,
2574                            AW_clicked_line *cl, AW_clicked_text *ct)
2575{
2576    aw_message("INFO MESSAGE");
2577    AWUSE(device);
2578    AWUSE(x);
2579    AWUSE(y);
2580    AWUSE(cl);
2581    AWUSE(ct);
2582
2583}
2584
2585AWT_graphic_tree *NT_generate_tree(AW_root *root, GBDATA *gb_main) {
2586    AWT_graphic_tree *apdt = new AWT_graphic_tree(root,gb_main);
2587    AP_tree tree_proto(0);
2588    apdt->init(&tree_proto);        // no tree_root !!! load will do this
2589    return apdt;
2590}
2591
2592void awt_create_dtree_awars(AW_root *aw_root,AW_default def)
2593{
2594    aw_root->awar_int(AWAR_DTREE_BASELINEWIDTH,1,def)->set_minmax(1, 10);
2595    aw_root->awar_float(AWAR_DTREE_VERICAL_DIST,1.0,def)->set_minmax(0.01,30);
2596    aw_root->awar_int(AWAR_DTREE_AUTO_JUMP,1,def);
2597
2598    aw_root->awar_int(AWAR_DTREE_SHOW_CIRCLE,0,def);
2599    aw_root->awar_int(AWAR_DTREE_USE_ELLIPSE, 1, def);
2600
2601    aw_root->awar_float(AWAR_DTREE_CIRCLE_ZOOM,1.0,def)     ->set_minmax(0.01,20);
2602    aw_root->awar_float(AWAR_DTREE_CIRCLE_MAX_SIZE,1.5,def) ->set_minmax(0.01,200);
2603    aw_root->awar_int(AWAR_DTREE_GREY_LEVEL,20,def)         ->set_minmax(0,100);
2604
2605    aw_root->awar_int(AWAR_DTREE_REFRESH,0,def);
2606}
2607
Note: See TracBrowser for help on using the browser.