ARB
TreeDisplay.cxx
Go to the documentation of this file.
1 // =============================================================== //
2 // //
3 // File : TreeDisplay.cxx //
4 // Purpose : phylogenetic tree display //
5 // //
6 // Institute of Microbiology (Technical University Munich) //
7 // http://www.arb-home.de/ //
8 // //
9 // =============================================================== //
10 
11 #include "TreeDisplay.hxx"
12 #include "TreeCallbacks.hxx"
13 #include "GroupIterator.hxx"
14 
15 #include <AP_TreeColors.hxx>
16 #include <AP_TreeShader.hxx>
17 #include <AP_TreeSet.hxx>
18 #include <nds.h>
19 
20 #include <config_manager.hxx>
21 
22 #include <aw_preset.hxx>
23 #include <aw_awars.hxx>
24 #include <aw_msg.hxx>
25 #include <aw_root.hxx>
26 #include <aw_question.hxx>
27 
28 #include <arb_defs.h>
29 #include <arb_diff.h>
30 #include <arb_global_defs.h>
31 #include <arb_strbuf.h>
32 
33 #include <ad_cb.h>
34 
35 #include <unistd.h>
36 #include <iostream>
37 #include <cfloat>
38 #include <algorithm>
39 
40 #define RULER_LINEWIDTH "ruler/ruler_width" // line width of ruler
41 #define RULER_SIZE "ruler/size"
42 
43 #define DEFAULT_RULER_LINEWIDTH tree_defaults::LINEWIDTH
44 #define DEFAULT_RULER_LENGTH tree_defaults::LENGTH
45 
46 int TREE_canvas::count = 0;
47 
48 const int MARKER_COLORS = 12;
49 static int MarkerGC[MARKER_COLORS] = {
50  // double rainbow
51  AWT_GC_RED,
57 
64 };
65 
66 using namespace AW;
67 
68 static void nocb() {}
69 GraphicTreeCallback AWT_graphic_tree::group_changed_cb = makeGraphicTreeCallback(nocb);
70 
72  AW_gc_manager *gc_manager =
73  AW_manage_GC(aww,
74  ntw->get_gc_base_name(),
76  makeGcChangedCallback(TREE_GC_changed_cb, ntw),
77  "#8ce",
78 
79  // Important note :
80  // Many gc indices are shared between ABR_NTREE and ARB_PARSIMONY
81  // e.g. the tree drawing routines use same gc's for drawing both trees
82  // (check PARS_dtree.cxx AWT_graphic_parsimony::init_devices)
83  // (keep in sync with ../../PARSIMONY/PARS_dtree.cxx@init_devices)
84 
85  // Note: in radial tree display, branches with lower gc(-index) are drawn AFTER branches
86  // with higher gc(-index), i.e. marked branches are drawn on top of unmarked branches.
87 
88  "Cursor$white",
89  "Branch remarks$#3d8a99",
90  "+-Bootstrap$#abe3ff", "-B.(limited)$#cfe9ff",
91  "-IRS group box$#000",
92  "Marked$#ffe560",
93  "Some marked$#d9c45c",
94  "Not marked$#5d5d5d",
95  "Zombies etc.$#7aa3cc",
96 
97  "+-None (black)$#000000", "-All (white)$#ffffff",
98 
99  "+-P1(red)$#ff0000", "+-P2(green)$#00ff00", "-P3(blue)$#0000ff",
100  "+-P4(orange)$#ffd060", "+-P5(aqua)$#40ffc0", "-P6(purple)$#c040ff",
101  "+-P7(1&2,yellow)$#ffff00", "+-P8(2&3,cyan)$#00ffff", "-P9(3&1,magenta)$#ff00ff",
102  "+-P10(lawn)$#c0ff40", "+-P11(skyblue)$#40c0ff", "-P12(pink)$#f030b0",
103 
104  "&color_groups", // use color groups
105 
106  // color ranges:
107  "*Linear,linear:+-lower$#a00,-upper$#0a0",
108  "*Rainbow,cyclic:+-col1$#a00,-col2$#990,"
109  /* */ "+-col3$#0a0,-col4$#0aa,"
110  /* */ "+-col5$#00a,-col6$#b0b",
111  "*Planar,planar:+-off$#000,-dim1$#a00,"
112  /* */ "-dim2$#0a0",
113  "*Spatial,spatial:+-off$#000,-dim1$#a00,"
114  /* */ "+-dim2$#0a0,-dim3$#00a",
115 
116  NULp);
117 
118  return gc_manager;
119 }
120 
122  /*
123  mode does
124 
125  0 unmark all
126  1 mark all
127  2 invert all marks
128  3 count marked (=result)
129  */
130 
131  if (!at) return 0;
132 
133  if (at->is_leaf()) {
134  long count = 0;
135  if (at->gb_node) { // not a zombie
136  switch (mark_mode) {
137  case 0: GB_write_flag(at->gb_node, 0); break;
138  case 1: GB_write_flag(at->gb_node, 1); break;
139  case 2: GB_write_flag(at->gb_node, !GB_read_flag(at->gb_node)); break;
140  case 3: count = GB_read_flag(at->gb_node); break;
141  default: td_assert(0);
142  }
143  }
144  return count;
145  }
146 
147  return
148  mark_species_in_tree(at->get_leftson(), mark_mode) +
149  mark_species_in_tree(at->get_rightson(), mark_mode);
150 }
151 
152 long AWT_graphic_tree::mark_species_in_tree_that(AP_tree *at, int mark_mode, bool (*condition)(GBDATA*, void*), void *cd) {
153  /*
154  mark_mode does
155 
156  0 unmark all
157  1 mark all
158  2 invert all marks
159  3 count marked (=result)
160 
161  marks are only changed for those species for that condition() != 0
162  */
163 
164  if (!at) return 0;
165 
166  if (at->is_leaf()) {
167  long count = 0;
168  if (at->gb_node) { // not a zombie
169  int oldMark = GB_read_flag(at->gb_node);
170  if (oldMark != mark_mode && condition(at->gb_node, cd)) {
171  switch (mark_mode) {
172  case 0: GB_write_flag(at->gb_node, 0); break;
173  case 1: GB_write_flag(at->gb_node, 1); break;
174  case 2: GB_write_flag(at->gb_node, !oldMark); break;
175  case 3: count += !!oldMark; break;
176  default: td_assert(0);
177  }
178  }
179  }
180  return count;
181  }
182 
183  return
184  mark_species_in_tree_that(at->get_leftson(), mark_mode, condition, cd) +
185  mark_species_in_tree_that(at->get_rightson(), mark_mode, condition, cd);
186 }
187 
188 
190  // same as mark_species_in_tree but works on rest of tree
191  if (at) {
192  AP_tree *pa = at->get_father();
193  if (pa) {
194  mark_species_in_tree(at->get_brother(), mark_mode);
195  mark_species_in_rest_of_tree(pa, mark_mode);
196  }
197  }
198 }
199 
201  if (!at) return false;
202 
203  if (at->is_leaf()) {
204  if (!at->gb_node) return false; // zombie
205  int marked = GB_read_flag(at->gb_node);
206  return marked;
207  }
208 
209  return tree_has_marks(at->get_leftson()) || tree_has_marks(at->get_rightson());
210 }
211 
213  if (!at) return false;
214 
215  AP_tree *pa = at->get_father();
216  if (!pa) return false;
217 
218  return tree_has_marks(at->get_brother()) || rest_tree_has_marks(pa);
219 }
220 
222  // group counters:
223  int closed, opened;
224  int closed_terminal, opened_terminal;
225  int closed_with_marked;
226  int closed_with_unmarked;
227 
228  // species counters:
229  int marked_in_groups, marked_outside_groups;
230  int unmarked_in_groups, unmarked_outside_groups;
231 
232  friend void AWT_graphic_tree::detect_group_state(AP_tree *at, AWT_graphic_tree_group_state *state, AP_tree *skip_this_son);
233 
234 public:
235 
236  void clear() {
237  closed = 0;
238  opened = 0;
239  closed_terminal = 0;
240  opened_terminal = 0;
241  closed_with_marked = 0;
242  closed_with_unmarked = 0;
243 
244  marked_in_groups = 0;
245  marked_outside_groups = 0;
246  unmarked_in_groups = 0;
247  unmarked_outside_groups = 0;
248  }
249 
251 
252  bool has_groups() const { return closed+opened; }
253  int marked() const { return marked_in_groups+marked_outside_groups; }
254  int unmarked() const { return unmarked_in_groups+unmarked_outside_groups; }
255 
256  bool all_opened() const { return closed == 0 && opened>0; }
257  bool all_closed() const { return opened == 0 && closed>0; }
258  bool all_terminal_closed() const { return opened_terminal == 0 && closed_terminal == closed; }
259  bool all_marked_opened() const { return marked_in_groups > 0 && closed_with_marked == 0; }
260 
262  if (closed_with_unmarked) {
263  if (closed_with_marked) return EXPAND_MARKED;
264  return EXPAND_UNMARKED;
265  }
266  return EXPAND_ALL;
267  }
268 
270  if (all_terminal_closed()) return COLLAPSE_ALL;
271  return COLLAPSE_TERMINAL;
272  }
273 };
274 
276  if (!at) return;
277  if (at->is_leaf()) {
278  if (at->gb_node) {
279  // count marked/unmarked
280  if (GB_read_flag(at->gb_node)) state->marked_outside_groups++;
281  else state->unmarked_outside_groups++;
282  }
283  return; // leafs never get grouped
284  }
285 
286  if (at->is_normal_group()) {
288  if (at->leftson != skip_this_son) detect_group_state(at->get_leftson(), &sub_state, skip_this_son);
289  if (at->rightson != skip_this_son) detect_group_state(at->get_rightson(), &sub_state, skip_this_son);
290 
291  if (at->gr.grouped) { // a closed group
292  state->closed++;
293  if (!sub_state.has_groups()) state->closed_terminal++;
294  if (sub_state.marked()) state->closed_with_marked++;
295  if (sub_state.unmarked()) state->closed_with_unmarked++;
296  state->closed += sub_state.opened;
297  }
298  else { // an open group
299  state->opened++;
300  if (!sub_state.has_groups()) state->opened_terminal++;
301  state->opened += sub_state.opened;
302  }
303 
304  state->marked_in_groups += sub_state.marked();
305  state->unmarked_in_groups += sub_state.unmarked();
306 
307  state->closed += sub_state.closed;
308  state->opened_terminal += sub_state.opened_terminal;
309  state->closed_terminal += sub_state.closed_terminal;
310  state->closed_with_marked += sub_state.closed_with_marked;
311  state->closed_with_unmarked += sub_state.closed_with_unmarked;
312  }
313  else { // not a group
314  if (at->leftson != skip_this_son) detect_group_state(at->get_leftson(), state, skip_this_son);
315  if (at->rightson != skip_this_son) detect_group_state(at->get_rightson(), state, skip_this_son);
316  }
317 }
318 
319 void AWT_graphic_tree::group_rest_tree(AP_tree *at, CollapseMode mode, int color_group) {
320  if (at) {
321  AP_tree *pa = at->get_father();
322  if (pa) {
323  group_tree(at->get_brother(), mode, color_group);
324  group_rest_tree(pa, mode, color_group);
325  }
326  }
327 }
328 
329 bool AWT_graphic_tree::group_tree(AP_tree *at, CollapseMode mode, int color_group) {
335  if (!at) return true;
336 
337  GB_transaction ta(tree_static->get_gb_main());
338 
339  bool expand_me = false;
340  if (at->is_leaf()) {
341  if (mode & EXPAND_ALL) expand_me = true;
342  else if (at->gb_node) { // linked leaf
343  if (mode & (EXPAND_MARKED|EXPAND_UNMARKED)) {
344  expand_me = bool(GB_read_flag(at->gb_node)) == bool(mode & EXPAND_MARKED);
345  }
346 
347  if (!expand_me && (mode & EXPAND_COLOR)) { // do not group specified color_group
348  int my_color_group = GBT_get_color_group(at->gb_node);
349 
350  expand_me =
351  my_color_group == color_group || // specific or no color
352  (my_color_group != 0 && color_group == -1); // any color
353  }
354  }
355  else { // zombie
356  expand_me = mode & EXPAND_ZOMBIES;
357  }
358  }
359  else { // inner node
360  at->gr.grouped = false; // expand during descend (important because keeled group may fold 'at' from inside recursion!)
361 
362  expand_me = group_tree(at->get_leftson(), mode, color_group);
363  expand_me = group_tree(at->get_rightson(), mode, color_group) || expand_me;
364 
365  if (!expand_me) { // no son requests to be expanded
366  if (at->is_normal_group()) {
367  at->gr.grouped = true; // group me
368  if (mode & COLLAPSE_TERMINAL) expand_me = true; // do not group non-terminal groups (upwards)
369  }
370  if (at->is_keeled_group()) {
371  at->get_father()->gr.grouped = true; // group "keeled"-me
372  if (mode & COLLAPSE_TERMINAL) expand_me = true; // do not group non-terminal groups (upwards)
373  }
374  }
375  }
376  return expand_me;
377 }
378 
380  AP_tree *at = get_root_node();
381  if (at) {
382  at->reorder_tree(mode);
383  exports.request_save();
384  }
385 }
386 
388  circle_filter(AW_SCREEN|AW_PRINTER|AW_SIZE_UNSCALED),
390 {
391 }
392 
393 void BootstrapConfig::display_remark(AW_device *device, const char *remark, const AW::Position& center, double blen, double bdist, const AW::Position& textpos, AW_pos alignment) const {
394  double dboot;
395  GBT_RemarkType type = parse_remark(remark, dboot);
396 
397  bool is_bootstrap = false;
398  switch (type) {
399  case REMARK_BOOTSTRAP:
400  is_bootstrap = true;
401  break;
402 
403  case REMARK_NONE:
404  td_assert(show_100_if_empty); // otherwise method should not have been called
405 
406  is_bootstrap = true;
407  dboot = 100.0;
408  break;
409 
410  case REMARK_OTHER:
411  break;
412  }
413 
414  int bootstrap = is_bootstrap ? int(dboot) : -1;
415 
416  bool show = true;
417  const char *text = NULp;
418 
419  if (is_bootstrap) {
420  if (!show_boots || // hide bootstrap when disabled,
421  ((bootstrap<bootstrap_min ||
422  bootstrap>bootstrap_max) && // when outside of displayed range
423  bootstrap_max != 0) || // but not if bootstrap_max is zero,
424  blen == 0.0) // or when branch is part of a multifurcation (i.e. "does not exist")
425  {
426  show = false;
427  }
428  else {
429  static GBS_strstruct buf(10);
430  buf.erase();
431 
432  if (bootstrap<1) {
433  buf.put('<');
434  bootstrap = 1;
435  }
436 
437  if (style == BS_FLOAT) {
438  buf.nprintf(4, "%4.2f", double(bootstrap/100.0));
439  }
440  else {
441  buf.nprintf(3, "%i", bootstrap);
442  if (style == BS_PERCENT) {
443  buf.put('%');
444  }
445  }
446  text = buf.get_data();
447  }
448  }
449  else { // non-bootstrap remark (always shown)
450  text = remark;
451  }
452 
453 #if defined(DEBUG)
454  {
455  bool debug_bootstraps = !show_circle && ellipsoid;
456 #if defined(UNIT_TESTS)
457  if (debug_bootstraps && RUNNING_TEST()) debug_bootstraps = false; // do not interfere with test results
458 #endif
459 
460  if (debug_bootstraps) {
461  // print details about bootstrapping:
462  static GBS_strstruct buf(200);
463  buf.erase();
464 
465  if (!show) {
466  buf.nprintf(150, "[BS/hide, rem='%s']", remark);
467  show = true;
468  }
469  else {
470  buf.cat(text);
471  buf.put(' ');
472  if (is_bootstrap) {
473  buf.nprintf(150, "[BS=%i, rem='%s']", bootstrap, remark);
474  }
475  else {
476  buf.nprintf(150, "[rem='%s']", remark);
477  }
478  }
479  text = buf.get_data();
480  }
481  }
482 #endif
483 
484  if (show_circle && is_bootstrap && show) {
485  double radius = .01 * bootstrap; // bootstrap values are given in % (0..100)
486 
487  if (radius < .1) radius = .1;
488 
489  radius = 1.0 / sqrt(radius); // -> bootstrap->radius : 100% -> 1, 0% -> inf
490  radius -= 1.0; // -> bootstrap->radius : 100% -> 0, 0% -> inf
491 
492  radius *= zoom_factor * 2;
493 
494  // Note : radius goes against infinite, if bootstrap values go towards zero
495  // For this reason we limit radius here:
496 
497  int gc = AWT_GC_BOOTSTRAP;
498  if (radius > max_radius) {
499  radius = max_radius;
500  gc = AWT_GC_BOOTSTRAP_LIMITED; // draw limited bootstraps in different color
501  }
502 
503  const double radiusx = radius * blen; // multiply with length of branch (and zoomfactor)
504  const bool circleTooSmall = radiusx<0 || nearlyZero(radiusx);
505  if (!circleTooSmall) {
506  double radiusy;
507  if (ellipsoid) {
508  radiusy = bdist;
509  if (radiusy > radiusx) radiusy = radiusx;
510  }
511  else {
512  radiusy = radiusx;
513  }
514 
515  device->set_grey_level(gc, fill_level);
516  device->circle(gc, AW::FillStyle::SHADED_WITH_BORDER, center, Vector(radiusx, radiusy), circle_filter);
517  }
518  }
519 
520  if (show) {
521  td_assert(text);
522  device->text(AWT_GC_BRANCH_REMARK, text, textpos, alignment, text_filter);
523  }
524 }
525 
526 void BootstrapConfig::display_node_remark(AW_device *device, const AP_tree *at, const AW::Position& center, double blen, double bdist, AW::RoughDirection textArea) const {
527  td_assert(!at->is_leaf()); // leafs (should) have no remarks
528 
529  AW_pos alignment = (textArea & D_WEST) ? 1.0 : 0.0;
530  Position textpos(center);
531  textpos.movey(scaled_remark_ascend*((textArea & D_SOUTH) ? 1.2 : ((textArea & D_NORTH) ? -0.1 : 0.5)));
532 
533  display_remark(device, at->get_remark(), center, blen, bdist, textpos, alignment);
534 }
535 
536 static void AWT_graphic_tree_root_changed(void *cd, AP_tree *old, AP_tree *newroot) {
538  if (agt->get_logical_root() == old || agt->get_logical_root()->is_inside(old)) {
539  agt->set_logical_root_to(newroot);
540  }
541 }
542 
543 static void AWT_graphic_tree_node_deleted(void *cd, AP_tree *del) {
545  if (agt->get_logical_root() == del) {
546  agt->set_logical_root_to(agt->get_root_node());
547  }
548  if (agt->get_root_node() == del) {
550  }
551 }
552 
553 GB_ERROR AWT_graphic_tree::create_group(AP_tree *at) {
554  GB_ERROR error = NULp;
555 
556  if (at->has_group_info()) {
557  // only happens for nodes representing a keeled group
558  td_assert(at->keelTarget());
559  error = GBS_global_string("Cannot create group at position of keeled group '%s'", at->name);
560  }
561  else {
562  char *gname = aw_input("Enter name of new group");
563  if (gname && gname[0]) {
564  GBDATA *gb_tree = tree_static->get_gb_tree();
565  GBDATA *gb_mainT = GB_get_root(gb_tree);
566  GB_transaction ta(gb_mainT);
567 
568  GBDATA *gb_node = GB_create_container(gb_tree, "node");
569  if (!gb_node) error = GB_await_error();
570 
571  if (at->gb_node) { // already have existing node info (e.g. for linewidth)
572  if (!error) error = GB_copy_dropProtectMarksAndTempstate(gb_node, at->gb_node); // copy existing node and ..
573  if (!error) error = GB_delete(at->gb_node); // .. delete old one (to trigger invalidation of taxonomy-cache)
574  }
575 
576  if (!error) error = GBT_write_int(gb_node, "id", 0); // force re-creation of node-id
577 
578  if (!error) {
579  error = GBT_write_name_to_groupData(gb_node, true, gname, true);
580  }
581  if (!error) exports.request_save();
582  if (!error) {
583  at->gb_node = gb_node;
584  at->name = gname;
585 
586  at->setKeeledState(0); // new group is always unkeeled
587  }
588  error = ta.close(error);
589  }
590  }
591 
592  return error;
593 }
594 
596  GB_ERROR error = NULp;
597 
598  // @@@ use group mode to edit/add/remove bootstraps/custom remarks?
599 
600  if (at->is_leaf()) {
601  error = "Please select an inner node to create a group";
602  }
603  else if (at->is_clade()) { // existing group
604  bool keeled = at->is_keeled_group(); // -> prefer keeled over normal group
605  AP_tree *gat = keeled ? at->get_father() : at; // node linked to group
606 
607  const char *msg = GBS_global_string("What to do with group '%s'?", gat->name);
608 
609  switch (aw_question(NULp, msg, "KeelOver,Rename,Destroy,Cancel" + (keeled ? 0 : 9)) - (keeled ? 1 : 0)) {
610  case -1: { // keel over
611  td_assert(keeled);
613  gat->unkeelGroup();
614 
615  // need to flush taxonomy (otherwise group is displayed with leading '!'):
616  GBDATA *gb_gname = GB_entry(gat->gb_node, "group_name");
617  td_assert(gb_gname);
618  GB_touch(gb_gname);
619 
621  break;
622  }
623  case 0: { // rename
624  char *new_gname = aw_input("Rename group", "Change group name:", gat->name);
625  if (new_gname) {
626  error = GBT_write_name_to_groupData(gat->gb_node, true, new_gname, true);
627  if (!error) {
628  freeset(gat->name, new_gname);
629  select_group(at);
631  }
632  }
633  break;
634  }
635 
636  case 1: // destroy
637  if (selected_group.at_node(at)) deselect_group(); // deselect group only if selected group gets destroyed
638 
639  gat->gr.grouped = false;
640  gat->name = NULp;
641  error = GB_delete(gat->gb_node); // ODD: expecting this to also destroy linewidth, rot and spread - but it doesn't!
642  gat->gb_node = NULp;
643 
644  if (!error) exports.request_save(); // ODD: even when commenting out this line info is not deleted
645  break;
646 
647  case 2: break; // cancel
648  default: td_assert(0); break;
649  }
650  }
651  else {
652  error = create_group(at); // create new group
653  if (!error && at->has_group_info()) {
654  at->gr.grouped = true;
655  select_group(at);
656  }
657  }
658 
659  if (error) aw_message(error);
660 }
661 
662 class Dragged : public AWT_command_data {
666  AWT_graphic_exports& exports;
667 
668 protected:
669  AWT_graphic_exports& get_exports() { return exports; }
670 
671 public:
673 
674  Dragged(AWT_graphic_exports& exports_) : exports(exports_) {}
675 
676  static bool valid_drag_device(AW_device *device) { return device->type() == AW_DEVICE_SCREEN; }
677 
678  virtual void draw_drag_indicator(AW_device *device, int drag_gc) const = 0;
679  virtual void perform(DragAction action, const AW_clicked_element *target, const Position& mousepos) = 0;
680  virtual void abort() = 0;
681 
682  void do_drag(const AW_clicked_element *drag_target, const Position& mousepos) {
683  perform(DRAGGING, drag_target, mousepos);
684  }
685  void do_drop(const AW_clicked_element *drop_target, const Position& mousepos) {
686  perform(DROPPED, drop_target, mousepos);
687  }
688 
689  void hide_drag_indicator(AW_device *device, int drag_gc) const {
690  // hide by XOR-drawing
691  draw_drag_indicator(device, drag_gc);
692  }
693 };
694 
696  if (mode == AWT_MODE_ROTATE || mode == AWT_MODE_SPREAD) {
697  if (tree_style != AP_TREE_RADIAL) {
698  aw_message("Please select the radial tree display mode to use this command");
699  return true;
700  }
701  }
702  return false;
703 }
704 
705 inline bool is_cursor_keycode(AW_key_code kcode) {
706  return
707  kcode == AW_KEY_UP ||
708  kcode == AW_KEY_DOWN ||
709  kcode == AW_KEY_LEFT ||
710  kcode == AW_KEY_RIGHT ||
711  kcode == AW_KEY_HOME ||
712  kcode == AW_KEY_END;
713 }
714 
715 AP_tree *AWT_graphic_tree::find_selected_node() const {
719  AP_tree *node = selSpec.get_node(); // node stored by last refresh
720  if (!node && displayed_root && species_name[0]) {
721  node = displayed_root->findLeafNamed(species_name);
722  }
723  return node;
724 }
725 
726 AP_tree *AWT_graphic_tree::find_selected_group() {
731  // @@@ could use selGroup to speed up search (if selected group was already drawn)
732  AP_tree *node = NULp;
733  if (selected_group.is_valid()) {
734  if (!selected_group.is_located()) {
735  selected_group.locate(get_root_node());
736  }
737  node = selected_group.get_node();
738  }
739  return node;
740 }
741 
742 
743 static GBDATA *brute_force_find_next_species(GBDATA *gb_main, GBDATA *gb_sel, bool marked_only, bool upwards) {
744  if (upwards) {
745  if (gb_sel && marked_only && !GB_read_flag(gb_sel)) gb_sel = GBT_next_marked_species(gb_sel);
746 
747  GBDATA *gb_prev = marked_only ? GBT_first_marked_species(gb_main) : GBT_first_species(gb_main);
748  while (gb_prev) {
749  GBDATA *gb_next = marked_only ? GBT_next_marked_species(gb_prev) : GBT_next_species(gb_prev);
750  if (gb_next == gb_sel) {
751  return gb_prev;
752  }
753  gb_prev = gb_next;
754  }
755  return gb_sel ? brute_force_find_next_species(gb_main, NULp, marked_only, upwards) : NULp;
756  }
757 
758  // downwards
759  GBDATA *gb_found = NULp;
760  if (marked_only) {
761  if (gb_sel) gb_found = GBT_next_marked_species(gb_sel);
762  if (!gb_found) gb_found = GBT_first_marked_species(gb_main);
763  }
764  else {
765  if (gb_sel) gb_found = GBT_next_species(gb_sel);
766  if (!gb_found) gb_found = GBT_first_species(gb_main);
767  }
768  return gb_found;
769 }
770 
772  AP_tree_set unfolded; // nodes which have been unfolded
773 
774  static void need_update(AP_tree*& subtree, AP_tree *node) {
775  if (subtree) {
776  subtree = DOWNCAST(AP_tree*, node->ancestor_common_with(subtree));
777  }
778  else {
779  subtree = node;
780  }
781  }
782 
783 public:
784 
785  AP_tree *unfold(const AP_tree_set& want_unfolded) { // set has to contain all parent group-nodes as well (use collect_enclosing_groups)
786  AP_tree_set keep_unfolded;
787  AP_tree *affected_subtree = NULp;
788  for (AP_tree_set_citer g = want_unfolded.begin(); g != want_unfolded.end(); ++g) {
789  AP_tree *node = *g;
790  td_assert(node->has_group_info()); // ensure keeled groups add the parent node (where their group-info is stored!)
791  if (node->gr.grouped) {
792  node->gr.grouped = false; // auto-unfold
793  need_update(affected_subtree, node);
794  keep_unfolded.insert(node);
795  }
796  }
797 
798  for (AP_tree_set_iter g = unfolded.begin(); g != unfolded.end(); ++g) {
799  AP_tree *node = *g;
800  td_assert(node->has_group_info());
801  if (want_unfolded.find(node) == want_unfolded.end()) {
802  node->gr.grouped = true; // auto-refold
803  need_update(affected_subtree, node);
804  }
805  else {
806  keep_unfolded.insert(node); // auto-refold later
807  }
808  }
809  unfolded = keep_unfolded;
810  return affected_subtree;
811  }
812 
813  bool is_auto_unfolded() const { return !unfolded.empty(); }
814  void forget() { unfolded.clear(); }
815 
816  bool node_is_auto_unfolded(AP_tree *node) const {
817  return unfolded.find(node) != unfolded.end();
818  }
819 };
820 
822  AP_tree_set parentGroups;
823  if (want_visible) collect_enclosing_groups(want_visible, parentGroups);
824 
825  AP_tree *outdated_subtree = autoUnfolded->unfold(parentGroups);
826  if (outdated_subtree) {
827  fast_sync_changed_folding(outdated_subtree);
828  }
829 }
830 
832  autoUnfolded->forget();
833 }
834 
835 bool AWT_graphic_tree::handle_cursor(AW_key_code kcode, AW_key_mod mod) {
837 
838  bool handled = false;
839  if (!displayed_root) return false;
840 
841  if (mod == AW_KEYMODE_NONE || mod == AW_KEYMODE_SHIFT || mod == AW_KEYMODE_CONTROL) { // jump next/prev (marked) species
842  if (kcode != AW_KEY_LEFT && kcode != AW_KEY_RIGHT) { // cursor left/right not defined
843  GBDATA *gb_jump_to = NULp;
844  bool marked_only = (mod == AW_KEYMODE_CONTROL);
845 
846  bool upwards = false;
847  bool ignore_selected = false;
848 
849  switch (kcode) {
850  case AW_KEY_HOME: ignore_selected = true; // fall-through
851  case AW_KEY_DOWN: upwards = false; break;
852  case AW_KEY_END: ignore_selected = true; // fall-through
853  case AW_KEY_UP: upwards = true; break;
854  default: break;
855  }
856 
857  if (is_tree_style(tree_style)) {
858  bool descent_folded = marked_only || (mod == AW_KEYMODE_SHIFT);
859  AP_tree *sel_node = NULp;
860 
861  bool at_group = false;
862  if (!ignore_selected) {
863  sel_node = find_selected_node();
864  if (!sel_node) {
865  sel_node = find_selected_group();
866  at_group = sel_node;
867  }
868  }
869 
870  ARB_edge edge =
871  sel_node
872  ? (at_group
873  ? (upwards
874  ? ARB_edge(sel_node, sel_node->get_rightson()).previous()
875  : ARB_edge(sel_node->get_leftson(), sel_node))
876  : leafEdge(sel_node))
877  : rootEdge(get_tree_root());
878 
879  // limit edge iteration (to avoid deadlock, e.g. if all species are inside folded groups)
880  int maxIter = ARB_edge::iteration_count(get_root_node()->gr.leaf_sum)+2;
881  AP_tree *jump_to_node = NULp;
882 
883  while (!jump_to_node && maxIter-->0) {
884  edge = upwards ? edge.next() : edge.previous();
885  if (edge.is_edge_to_leaf()) {
886  AP_tree *leaf = DOWNCAST(AP_tree*, edge.dest());
887  if (leaf->gb_node && // skip zombies
888  (marked_only ? leaf->gr.mark_sum // skip unmarked leafs
889  : implicated(!descent_folded, !leaf->is_inside_folded_group())) && // skip folded leafs if !descent_folded
890  implicated(is_logically_zoomed(), displayed_root->is_ancestor_of(leaf))) // stay inside logically zoomed subtree
891  {
892  jump_to_node = leaf;
893  }
894  }
895  // @@@ optimize: no need to descend into unmarked subtrees (if marked_only)
896  // @@@ optimize: no need to descend into folded subtrees (if !marked_only)
897  }
898 
899  if (jump_to_node) {
900  // perform auto-unfolding unconditionally here
901  // (jump_to_node will only point to a hidden node here, if auto-unfolding shall happen)
902  auto_unfold(jump_to_node);
903  if (jump_to_node->is_leaf()) gb_jump_to = jump_to_node->gb_node; // select node (done below)
904  }
905  }
906  else {
907  if (nds_only_marked) marked_only = true;
908  if (!species_name[0]) ignore_selected = true;
909 
910  GBDATA *gb_sel = ignore_selected ? NULp : GBT_find_species(gb_main, species_name);
911  gb_jump_to = brute_force_find_next_species(gb_main, gb_sel, marked_only, upwards);
912  }
913 
914  if (gb_jump_to) {
915  GB_transaction ta(gb_main);
916  map_viewer_cb(gb_jump_to, ADMVT_SELECT);
917  handled = true;
918  }
919  }
920  }
921  else if (mod == AW_KEYMODE_ALT) { // jump to groups
922  if (is_tree_style(tree_style)) {
923  AP_tree *start_node = find_selected_group();
924  bool started_from_species = false;
925 
926  if (!start_node) { // if no group is selected => start at selected species
927  start_node = find_selected_node();
928  started_from_species = start_node;
929  }
930 
931  // if nothing selected -> 'iter' will point to first group (deepest one)
932  GroupIterator iter(start_node ? start_node : get_root_node());
933  AP_tree *unfold_to = NULp;
934 
935  bool at_target = false;
936  if (started_from_species) {
937  AP_tree *parentGroup = DOWNCAST(AP_tree*, start_node->find_parent_clade());
938 
939  if (parentGroup) {
940  iter = GroupIterator(parentGroup);
941  at_target = (kcode == AW_KEY_LEFT || kcode == AW_KEY_RIGHT);
942  }
943  else {
944  at_target = true; // stick with default group
945  }
946  }
947 
948  while (!at_target) {
949  AW_key_code inject_kcode = AW_KEY_NONE;
950  int start_clade_level = iter.get_clade_level();
951  AP_tree *start_group = iter.node(); // abort iteration (handles cases where only 1 group is found)
952 
953  switch (kcode) {
954  case AW_KEY_UP:
955  do {
956  iter.previous();
957  if (iter.node() == start_group) break;
958  }
959  while (iter.get_clade_level() > start_clade_level);
960 
961  if (iter.get_clade_level() < start_clade_level) {
962  iter = GroupIterator(start_group);
963  inject_kcode = AW_KEY_END;
964  }
965  break;
966 
967  case AW_KEY_DOWN:
968  if (start_node) { // otherwise iterator already points to wanted node
969  do {
970  iter.next();
971  if (iter.node() == start_group) break;
972  }
973  while (iter.get_clade_level() > start_clade_level);
974 
975  if (iter.get_clade_level() < start_clade_level) {
976  iter = GroupIterator(start_group);
977  inject_kcode = AW_KEY_HOME;
978  }
979  }
980  break;
981 
982  case AW_KEY_HOME: {
983  AP_tree *parent = DOWNCAST(AP_tree*, iter.node()->find_parent_clade());
984  if (parent) {
985  iter = GroupIterator(parent);
986  inject_kcode = AW_KEY_RIGHT;
987  }
988  else {
989  iter = GroupIterator(get_root_node());
990  }
991  break;
992  }
993  case AW_KEY_END: {
994  AP_tree *last_visited = NULp;
995  do {
996  if (iter.get_clade_level() == start_clade_level) {
997  last_visited = iter.node();
998  }
999  iter.next();
1000  if (iter.node() == start_group) break;
1001  }
1002  while (iter.get_clade_level() >= start_clade_level);
1003 
1004  td_assert(last_visited);
1005  iter = GroupIterator(last_visited);
1006  break;
1007  }
1008  case AW_KEY_LEFT: {
1009  // first keypress: fold if auto-unfolded
1010  // second keypress: select parent
1011  bool refoldFirst = autoUnfolded && autoUnfolded->node_is_auto_unfolded(start_group);
1012  if (!refoldFirst) {
1013  AP_tree *parent = DOWNCAST(AP_tree*, iter.node()->find_parent_clade());
1014  if (parent) iter = GroupIterator(parent);
1015  }
1016  // else just dont move (will refold group the group)
1017  break;
1018  }
1019  case AW_KEY_RIGHT: {
1020  iter.next();
1021  if (iter.node()->find_parent_clade() != start_group) { // has no child group =>
1022  iter = GroupIterator(start_group); // set back ..
1023  unfold_to = start_group->get_leftson(); // .. and temp. show group content
1024  }
1025  break;
1026  }
1027 
1028  default:
1029  td_assert(0); // avoid
1030  break;
1031  }
1032 
1033  if (inject_kcode == AW_KEY_NONE) at_target = true;
1034  else kcode = inject_kcode; // simulate keystroke:
1035  }
1036 
1037  if (iter.valid()) {
1038  AP_tree *jump_to = iter.node();
1039  select_group(jump_to);
1040  auto_unfold(unfold_to ? unfold_to : jump_to);
1041 #if defined(DEBUG)
1042  fprintf(stderr, "selected group '%s' (clade-level=%i)\n", jump_to->get_group_name(), jump_to->calc_clade_level());
1043 #endif
1044  }
1045  else {
1046  deselect_group();
1047  }
1049  }
1050  }
1051 
1052  return handled;
1053 }
1054 
1055 void AWT_graphic_tree::toggle_folding_at(AP_tree *at, bool force_jump) {
1056  if (at && !at->is_leaf() && at->is_clade()) {
1057  bool wasFolded = at->is_folded_group();
1058  AP_tree *top_change = NULp;
1059 
1060  if (at->is_normal_group()) {
1061  top_change = at;
1062  top_change->gr.grouped = !wasFolded;
1063  }
1064  if (at->is_keeled_group()) {
1065  top_change = at->get_father();
1066  top_change->gr.grouped = !wasFolded;
1067  }
1068 
1069  td_assert(top_change);
1070 
1071  if (!force_jump) {
1072  select_group(at);
1073  }
1074  fast_sync_changed_folding(top_change);
1075  if (force_jump) {
1076  deselect_group();
1077  select_group(at);
1078  }
1079  }
1080 }
1081 
1082 void AWT_graphic_tree::handle_key(AW_device *device, AWT_graphic_event& event) {
1084 
1085  td_assert(event.type() == AW_Keyboard_Press);
1086 
1087  if (event.key_code() == AW_KEY_NONE) return;
1088  if (event.key_code() == AW_KEY_ASCII && event.key_char() == 0) return;
1089 
1090 #if defined(DEBUG) && 0
1091  printf("key_char=%i (=%c)\n", int(event.key_char()), event.key_char());
1092 #endif // DEBUG
1093 
1094  // ------------------------
1095  // drag&drop keys
1096 
1097  if (event.key_code() == AW_KEY_ESCAPE) {
1098  AWT_command_data *cmddata = get_command_data();
1099  if (cmddata) {
1100  Dragged *dragging = dynamic_cast<Dragged*>(cmddata);
1101  if (dragging) {
1102  dragging->hide_drag_indicator(device, drag_gc);
1103  dragging->abort(); // abort what ever we did
1105  }
1106  }
1107  }
1108 
1109  // ----------------------------------------
1110  // commands independent of tree :
1111 
1112  bool handled = true;
1113  switch (event.key_char()) {
1114  case 9: { // Ctrl-i = invert all
1115  GBT_mark_all(gb_main, 2);
1117  break;
1118  }
1119  case 13: { // Ctrl-m = mark/unmark all
1120  int mark = !GBT_first_marked_species(gb_main); // no species marked -> mark all
1121  GBT_mark_all(gb_main, mark);
1123  break;
1124  }
1125  case ' ': { // Space = toggle mark(s) of selected species/group
1126  if (species_name[0]) {
1127  GB_transaction ta(gb_main);
1128  GBDATA *gb_species = GBT_find_species(gb_main, species_name);
1129  if (gb_species) {
1130  GB_write_flag(gb_species, !GB_read_flag(gb_species));
1132  }
1133  }
1134  else {
1135  AP_tree *subtree = find_selected_group();
1136  if (subtree) {
1137  GB_transaction ta(gb_main);
1138  mark_species_in_tree(subtree, !tree_has_marks(subtree));
1140  }
1141  }
1142  break;
1143  }
1144  default: handled = false; break;
1145  }
1146 
1147  if (!handled) {
1148  handled = true;
1149  switch (event.key_code()) {
1150  case AW_KEY_RETURN: // Return = fold/unfold selected group
1151  toggle_folding_at(find_selected_group(), true);
1152  break;
1153  default: handled = false; break;
1154  }
1155  }
1156 
1157  // -------------------------
1158  // cursor movement
1159  if (!handled && is_cursor_keycode(event.key_code())) {
1160  handled = handle_cursor(event.key_code(), event.key_modifier());
1161  }
1162 
1163  if (!handled) {
1164  // handle key events specific to pointed-to tree-element
1165  ClickedTarget pointed(this, event.best_click());
1166 
1167  if (pointed.species()) {
1168  handled = true;
1169  switch (event.key_char()) {
1170  case 'i':
1171  case 'm': { // i/m = mark/unmark species
1172  GB_write_flag(pointed.species(), 1-GB_read_flag(pointed.species()));
1174  break;
1175  }
1176  case 'I': { // I = invert all but current
1177  int mark = GB_read_flag(pointed.species());
1178  GBT_mark_all(gb_main, 2);
1179  GB_write_flag(pointed.species(), mark);
1181  break;
1182  }
1183  case 'M': { // M = mark/unmark all but current
1184  int mark = GB_read_flag(pointed.species());
1185  GB_write_flag(pointed.species(), 0); // unmark current
1186  GBT_mark_all(gb_main, !GBT_first_marked_species(gb_main));
1187  GB_write_flag(pointed.species(), mark); // restore mark of current
1189  break;
1190  }
1191  default: handled = false; break;
1192  }
1193  }
1194 
1195  if (!handled && event.key_char() == '0') {
1196  // handle reset-key promised by
1197  // - KEYINFO_ABORT_AND_RESET (AWT_MODE_ROTATE, AWT_MODE_LENGTH, AWT_MODE_MULTIFURC, AWT_MODE_LINE, AWT_MODE_SPREAD)
1198  // - KEYINFO_RESET (AWT_MODE_LZOOM)
1199 
1200  if (event.cmd() == AWT_MODE_LZOOM) {
1203  }
1204  else if (pointed.is_ruler()) {
1205  GBDATA *gb_tree = tree_static->get_gb_tree();
1206  td_assert(gb_tree);
1207 
1208  switch (event.cmd()) {
1209  case AWT_MODE_ROTATE: break; // ruler has no rotation
1210  case AWT_MODE_SPREAD: break; // ruler has no spread
1211  case AWT_MODE_LENGTH: {
1212  GB_transaction ta(gb_tree);
1213  GBDATA *gb_ruler_size = GB_searchOrCreate_float(gb_tree, RULER_SIZE, DEFAULT_RULER_LENGTH);
1214  GB_write_float(gb_ruler_size, DEFAULT_RULER_LENGTH);
1216  break;
1217  }
1218  case AWT_MODE_LINE: {
1219  GB_transaction ta(gb_tree);
1221  GB_write_int(gb_ruler_width, DEFAULT_RULER_LINEWIDTH);
1223  break;
1224  }
1225  default: break;
1226  }
1227  }
1228  else if (pointed.node()) {
1229  if (warn_inappropriate_mode(event.cmd())) return;
1230  switch (event.cmd()) {
1231  case AWT_MODE_ROTATE: pointed.node()->reset_subtree_angles(); exports.request_save(); break;
1232  case AWT_MODE_LENGTH: pointed.node()->set_branchlength_unrooted(0.0); exports.request_save(); break;
1233  case AWT_MODE_MULTIFURC: pointed.node()->multifurcate(); exports.request_save(); break;
1234  case AWT_MODE_LINE: pointed.node()->reset_subtree_linewidths(); exports.request_save(); break;
1235  case AWT_MODE_SPREAD: pointed.node()->reset_subtree_spreads(); exports.request_save(); break;
1236  default: break;
1237  }
1238  }
1239  return;
1240  }
1241 
1242  if (!handled && pointed.node()) {
1243  handled = true;
1244  switch (event.key_char()) {
1245  case 'm': { // m = mark/unmark (sub)tree
1246  GB_transaction ta(gb_main);
1247  mark_species_in_tree(pointed.node(), !tree_has_marks(pointed.node()));
1249  break;
1250  }
1251  case 'M': { // M = mark/unmark all but (sub)tree
1252  GB_transaction ta(gb_main);
1253  mark_species_in_rest_of_tree(pointed.node(), !rest_tree_has_marks(pointed.node()));
1255  break;
1256  }
1257  // @@@ add hotkeys to count marked (subtree + resttree)?
1258 
1259  case 'i': { // i = invert (sub)tree
1260  GB_transaction ta(gb_main);
1261  mark_species_in_tree(pointed.node(), 2);
1263  break;
1264  }
1265  case 'I': { // I = invert all but (sub)tree
1266  GB_transaction ta(gb_main);
1267  mark_species_in_rest_of_tree(pointed.node(), 2);
1269  break;
1270  }
1271  case 'c':
1272  case 'x': {
1274  AP_tree *at = pointed.node();
1275 
1276  detect_group_state(at, &state, NULp);
1277 
1278  if (!state.has_groups()) { // somewhere inside group
1279 do_parent :
1280  at = at->get_father();
1281  while (at) {
1282  if (at->is_normal_group()) break;
1283  at = at->get_father();
1284  }
1285 
1286  if (at) {
1287  state.clear();
1288  detect_group_state(at, &state, NULp);
1289  }
1290  }
1291 
1292  if (at) {
1293  CollapseMode next_group_mode;
1294 
1295  if (event.key_char() == 'x') { // expand
1296  next_group_mode = state.next_expand_mode();
1297  }
1298  else { // collapse
1299  if (state.all_closed()) goto do_parent;
1300  next_group_mode = state.next_collapse_mode();
1301  }
1302 
1303  group_tree(at, next_group_mode, 0);
1305  }
1306  break;
1307  }
1308  case 'C':
1309  case 'X': {
1310  AP_tree *root_node = pointed.node();
1311  while (root_node->father) root_node = root_node->get_father(); // search father
1312 
1313  td_assert(root_node);
1314 
1316  detect_group_state(root_node, &state, pointed.node());
1317 
1318  CollapseMode next_group_mode;
1319  if (event.key_char() == 'X') { // expand
1320  next_group_mode = state.next_expand_mode();
1321  }
1322  else { // collapse
1323  next_group_mode = state.next_collapse_mode();
1324  }
1325 
1326  group_rest_tree(pointed.node(), next_group_mode, 0);
1327  fast_sync_changed_folding(root_node);
1328 
1329  break;
1330  }
1331  default: handled = false; break;
1332  }
1333  }
1334  }
1335 }
1336 
1337 static bool command_on_GBDATA(GBDATA *gbd, const AWT_graphic_event& event, AD_map_viewer_cb map_viewer_cb) {
1338  // modes listed here are available in ALL tree-display-modes (i.e. as well in listmode)
1339 
1340  bool refresh = false;
1341 
1342  if (event.type() == AW_Mouse_Press && event.button() != AW_BUTTON_MIDDLE) {
1343  AD_MAP_VIEWER_TYPE selectType = ADMVT_NONE;
1344 
1345  switch (event.cmd()) {
1346  case AWT_MODE_MARK: // see also .@OTHER_MODE_MARK_HANDLER
1347  switch (event.button()) {
1348  case AW_BUTTON_LEFT:
1349  GB_write_flag(gbd, 1);
1350  selectType = ADMVT_SELECT;
1351  break;
1352  case AW_BUTTON_RIGHT:
1353  GB_write_flag(gbd, 0);
1354  break;
1355  default:
1356  break;
1357  }
1358  refresh = true;
1359  break;
1360 
1361  case AWT_MODE_WWW: selectType = ADMVT_WWW; break;
1362  case AWT_MODE_INFO: selectType = ADMVT_INFO; break;
1363  default: selectType = ADMVT_SELECT; break;
1364  }
1365 
1366  if (selectType != ADMVT_NONE) {
1367  map_viewer_cb(gbd, selectType);
1368  refresh = true;
1369  }
1370  }
1371 
1372  return refresh;
1373 }
1374 
1379  AW_clicked_element *elem;
1380 
1381 public:
1382  ClickedElement(const AW_clicked_element& e) : elem(e.clone()) {}
1383  ClickedElement(const ClickedElement& other) : elem(other.element()->clone()) {}
1385  ~ClickedElement() { delete elem; }
1386 
1387  const AW_clicked_element *element() const { return elem; }
1388 
1389  bool operator == (const ClickedElement& other) const { return *element() == *other.element(); }
1390  bool operator != (const ClickedElement& other) const { return !(*this == other); }
1391 };
1392 
1393 class DragNDrop : public Dragged {
1394  ClickedElement Drag, Drop;
1395 
1396  virtual void perform_drop() = 0;
1397 
1398  void drag(const AW_clicked_element *drag_target) {
1399  Drop = drag_target ? *drag_target : Drag;
1400  }
1401  void drop(const AW_clicked_element *drop_target) {
1402  drag(drop_target);
1403  perform_drop();
1404  }
1405 
1406  void perform(DragAction action, const AW_clicked_element *target, const Position&) FINAL_OVERRIDE {
1407  switch (action) {
1408  case DRAGGING: drag(target); break;
1409  case DROPPED: drop(target); break;
1410  }
1411  }
1412 
1413  void abort() OVERRIDE {
1414  perform(DROPPED, Drag.element(), Position()); // drop dragged element onto itself to abort
1415  }
1416 
1417 protected:
1418  const AW_clicked_element *source_element() const { return Drag.element(); }
1419  const AW_clicked_element *dest_element() const { return Drop.element(); }
1420 
1421 public:
1422  DragNDrop(const AW_clicked_element *dragFrom, AWT_graphic_exports& exports_) :
1423  Dragged(exports_),
1424  Drag(*dragFrom),
1425  Drop(Drag)
1426  {}
1427 
1428  void draw_drag_indicator(AW_device *device, int drag_gc) const FINAL_OVERRIDE {
1429  td_assert(valid_drag_device(device));
1430  source_element()->indicate_selected(device, drag_gc);
1431  if (Drag != Drop) {
1432  dest_element()->indicate_selected(device, drag_gc);
1433  device->line(drag_gc, source_element()->get_connecting_line(*dest_element()));
1434  }
1435  }
1436 };
1437 
1438 class BranchMover : public DragNDrop {
1439  AW_MouseButton button;
1440  AWT_graphic_tree& agt;
1441 
1442  void perform_drop() OVERRIDE {
1443  ClickedTarget source(source_element());
1444  ClickedTarget dest(dest_element());
1445 
1446  if (source.node() && dest.node() && source.node() != dest.node()) {
1447  GB_ERROR error = NULp;
1448  GBDATA *gb_node = source.node()->gb_node;
1449  agt.deselect_group();
1450  switch (button) {
1451  case AW_BUTTON_LEFT:
1452  error = source.node()->cantMoveNextTo(dest.node());
1453  if (!error) source.node()->moveNextTo(dest.node(), dest.get_rel_attach());
1454  break;
1455 
1456  case AW_BUTTON_RIGHT:
1457  error = source.node()->move_group_to(dest.node());
1458  break;
1459  default:
1460  td_assert(0);
1461  break;
1462  }
1463 
1464  if (error) {
1465  aw_message(error);
1466  }
1467  else {
1469  bool group_moved = !source.node()->is_leaf() && source.node()->is_normal_group();
1470  if (group_moved) agt.select_group(gb_node);
1471  }
1472  }
1473  else {
1474 #if defined(DEBUG)
1475  if (!source.node()) printf("No source.node\n");
1476  if (!dest.node()) printf("No dest.node\n");
1477  if (dest.node() == source.node()) printf("source==dest\n");
1478 #endif
1479  }
1480  }
1481 
1482 public:
1484  DragNDrop(dragFrom, agt_.exports),
1485  button(button_),
1486  agt(agt_)
1487  {}
1488 };
1489 
1490 
1491 class Scaler : public Dragged {
1492  Position mouse_start; // screen-coordinates
1493  Position last_drag_pos;
1494  double unscale;
1495 
1496  virtual void draw_scale_indicator(const AW::Position& drag_pos, AW_device *device, int drag_gc) const = 0;
1497  virtual void do_scale(const Position& drag_pos) = 0;
1498 
1499  void perform(DragAction action, const AW_clicked_element *, const Position& mousepos) FINAL_OVERRIDE {
1500  switch (action) {
1501  case DRAGGING:
1502  last_drag_pos = mousepos;
1503  FALLTHROUGH; // aka instantly apply drop-action while dragging
1504  case DROPPED:
1505  do_scale(mousepos);
1506  break;
1507  }
1508  }
1509  void abort() OVERRIDE {
1510  perform(DROPPED, NULp, mouse_start); // drop exactly where dragging started
1511  }
1512 
1513 
1514 protected:
1515  const Position& startpos() const { return mouse_start; }
1516  Vector scaling(const Position& current) const { return Vector(mouse_start, current)*unscale; } // returns world-coordinates
1517 
1518 public:
1519  Scaler(const Position& start, double unscale_, AWT_graphic_exports& exports_)
1520  : Dragged(exports_),
1521  mouse_start(start),
1522  last_drag_pos(start),
1523  unscale(unscale_)
1524  {
1525  td_assert(!is_nan_or_inf(unscale));
1526  }
1527 
1528  void draw_drag_indicator(AW_device *device, int drag_gc) const FINAL_OVERRIDE {
1529  draw_scale_indicator(last_drag_pos, device, drag_gc);
1530  }
1531 };
1532 
1533 inline double discrete_value(double analog_value, int discretion_factor) {
1534  // discretion_factor:
1535  // 10 -> 1st digit behind dot
1536  // 100 -> 2nd ------- " ------
1537  // 1000 -> 3rd ------- " ------
1538 
1539  if (analog_value<0.0) return -discrete_value(-analog_value, discretion_factor);
1540  return int(analog_value*discretion_factor+0.5)/double(discretion_factor);
1541 }
1542 
1545  GBDATA *gbd;
1546  GB_TYPES type;
1547  float min;
1548  float max;
1549  int discretion_factor;
1550  bool inversed;
1551 
1552  static CONSTEXPR double INTSCALE = 100.0;
1553 
1554  void init() {
1555  min = -DBL_MAX;
1556  max = DBL_MAX;
1557 
1558  discretion_factor = 0;
1559  inversed = false;
1560  }
1561 
1562 public:
1563  DB_scalable() : gbd(NULp), type(GB_NONE) { init(); }
1564  DB_scalable(GBDATA *gbd_) : gbd(gbd_), type(GB_read_type(gbd)) { init(); }
1565 
1566  GBDATA *data() { return gbd; }
1567 
1568  float read() {
1569  float res = 0.0;
1570  switch (type) {
1571  case GB_FLOAT: res = GB_read_float(gbd); break;
1572  case GB_INT: res = GB_read_int(gbd)/INTSCALE; break;
1573  default: break;
1574  }
1575  return inversed ? -res : res;
1576  }
1577  bool write(float val) {
1578  float old = read();
1579 
1580  if (inversed) val = -val;
1581 
1582  val = val<=min ? min : (val>=max ? max : val);
1583  val = discretion_factor ? discrete_value(val, discretion_factor) : val;
1584 
1585  switch (type) {
1586  case GB_FLOAT:
1587  GB_write_float(gbd, val);
1588  break;
1589  case GB_INT:
1590  GB_write_int(gbd, int(val*INTSCALE+0.5));
1591  break;
1592  default: break;
1593  }
1594 
1595  return old != read();
1596  }
1597 
1598  void set_discretion_factor(int df) { discretion_factor = df; }
1599  void set_min(float val) { min = (type == GB_INT) ? val*INTSCALE : val; }
1600  void set_max(float val) { max = (type == GB_INT) ? val*INTSCALE : val; }
1601  void inverse() { inversed = !inversed; }
1602 };
1603 
1604 class RulerScaler : public Scaler { // derived from Noncopyable
1605  Position awar_start;
1606  DB_scalable x, y; // DB entries scaled by x/y movement
1607 
1608  GBDATA *gbdata() {
1609  GBDATA *gbd = x.data();
1610  if (!gbd) gbd = y.data();
1611  td_assert(gbd);
1612  return gbd;
1613  }
1614 
1615  Position read_pos() { return Position(x.read(), y.read()); }
1616  bool write_pos(Position p) {
1617  bool xchanged = x.write(p.xpos());
1618  bool ychanged = y.write(p.ypos());
1619  return xchanged || ychanged;
1620  }
1621 
1622  void draw_scale_indicator(const AW::Position& , AW_device *, int) const {}
1623  void do_scale(const Position& drag_pos) {
1624  GB_transaction ta(gbdata());
1625  if (write_pos(awar_start+scaling(drag_pos))) get_exports().request_refresh();
1626  }
1627 public:
1628  RulerScaler(const Position& start, double unscale_, const DB_scalable& xs, const DB_scalable& ys, AWT_graphic_exports& exports_)
1629  : Scaler(start, unscale_, exports_),
1630  x(xs),
1631  y(ys)
1632  {
1633  GB_transaction ta(gbdata());
1634  awar_start = read_pos();
1635  }
1636 };
1637 
1638 static void text_near_head(AW_device *device, int gc, const LineVector& line, const char *text) {
1639  // @@@ should keep a little distance between the line-head and the text (depending on line orientation)
1640  Position at = line.head();
1641  device->text(gc, text, at);
1642 }
1643 
1645 
1646 class BranchScaler : public Scaler { // derived from Noncopyable
1647  ScaleMode mode;
1648  AP_tree *node;
1649 
1650  float start_val; // length or spread
1651  bool zero_val_removed;
1652 
1653  LineVector branch;
1654  Position attach; // point on 'branch' (next to click position)
1655 
1656  int discretion_factor; // !=0 = > scale to discrete values
1657 
1658  bool allow_neg_val;
1659 
1660  float get_val() const {
1661  switch (mode) {
1663  case SCALE_LENGTH: return node->get_branchlength_unrooted();
1664  case SCALE_SPREAD: return node->gr.spread;
1665  }
1666  td_assert(0);
1667  return 0.0;
1668  }
1669  void set_val(float val) {
1670  switch (mode) {
1671  case SCALE_LENGTH_PRESERVING: node->set_branchlength_preserving(val); break;
1672  case SCALE_LENGTH: node->set_branchlength_unrooted(val); break;
1673  case SCALE_SPREAD: node->gr.spread = val; break;
1674  }
1675  }
1676 
1677  void init_discretion_factor(bool discrete) {
1678  if (start_val != 0 && discrete) {
1679  discretion_factor = 10;
1680  while ((start_val*discretion_factor)<1) {
1681  discretion_factor *= 10;
1682  }
1683  }
1684  else {
1685  discretion_factor = 0;
1686  }
1687  }
1688 
1689  Position get_dragged_attach(const AW::Position& drag_pos) const {
1690  // return dragged position of 'attach'
1691  Vector moved = scaling(drag_pos);
1692  Vector attach2tip = branch.head()-attach;
1693 
1694  if (attach2tip.length()>0) {
1695  Vector moveOnBranch = orthogonal_projection(moved, attach2tip);
1696  return attach+moveOnBranch;
1697  }
1698  Vector attach2base = branch.start()-attach;
1699  if (attach2base.length()>0) {
1700  Vector moveOnBranch = orthogonal_projection(moved, attach2base);
1701  return attach+moveOnBranch;
1702  }
1703  return Position(); // no position
1704  }
1705 
1706 
1707  void draw_scale_indicator(const AW::Position& drag_pos, AW_device *device, int drag_gc) const {
1708  td_assert(valid_drag_device(device));
1709  Position attach_dragged = get_dragged_attach(drag_pos);
1710  if (attach_dragged.valid()) {
1711  Position drag_world = device->rtransform(drag_pos);
1712  LineVector to_dragged(attach_dragged, drag_world);
1713  LineVector to_start(attach, -to_dragged.line_vector());
1714 
1715  device->set_line_attributes(drag_gc, 1, AW_SOLID);
1716 
1717  device->line(drag_gc, to_start);
1718  device->line(drag_gc, to_dragged);
1719 
1720  text_near_head(device, drag_gc, to_start, GBS_global_string("old=%.3f", start_val));
1721  text_near_head(device, drag_gc, to_dragged, GBS_global_string("new=%.3f", get_val()));
1722  }
1723 
1724  device->set_line_attributes(drag_gc, 3, AW_SOLID);
1725  device->line(drag_gc, branch);
1726  }
1727 
1728  void do_scale(const Position& drag_pos) {
1729  double oldval = get_val();
1730 
1731  if (start_val == 0.0) { // can't scale
1732  if (!zero_val_removed) {
1733  switch (mode) {
1734  case SCALE_LENGTH:
1736  set_val(tree_defaults::LENGTH); // fake branchlength (can't scale zero-length branches)
1737  aw_message("Cannot scale zero sized branches\nBranchlength has been set to 0.1\nNow you may scale the branch");
1738  break;
1739  case SCALE_SPREAD:
1740  set_val(tree_defaults::SPREAD); // reset spread (can't scale unspreaded branches)
1741  aw_message("Cannot spread unspreaded branches\nSpreading has been set to 1.0\nNow you may spread the branch"); // @@@ clumsy
1742  break;
1743  }
1744  zero_val_removed = true;
1745  }
1746  }
1747  else {
1748  Position attach_dragged = get_dragged_attach(drag_pos);
1749  if (attach_dragged.valid()) {
1750  Vector to_attach(branch.start(), attach);
1751  Vector to_attach_dragged(branch.start(), attach_dragged);
1752 
1753  double tal = to_attach.length();
1754  double tdl = to_attach_dragged.length();
1755 
1756  if (tdl>0.0 && tal>0.0) {
1757  bool negate = are_antiparallel(to_attach, to_attach_dragged);
1758  double scale = tdl/tal * (negate ? -1 : 1);
1759 
1760  float val = start_val * scale;
1761  if (val<0.0) {
1762  if (node->is_leaf() || !allow_neg_val) {
1763  val = 0.0; // do NOT accept negative values
1764  }
1765  }
1766  if (discretion_factor) {
1767  val = discrete_value(val, discretion_factor);
1768  }
1769  set_val(NONAN(val));
1770  }
1771  }
1772  }
1773 
1774  if (oldval != get_val()) {
1776  }
1777  }
1778 
1779 public:
1780 
1781  BranchScaler(ScaleMode mode_, AP_tree *node_, const LineVector& branch_, const Position& attach_, const Position& start, double unscale_, bool discrete, bool allow_neg_values_, AWT_graphic_exports& exports_)
1782  : Scaler(start, unscale_, exports_),
1783  mode(mode_),
1784  node(node_),
1785  start_val(get_val()),
1786  zero_val_removed(false),
1787  branch(branch_),
1788  attach(attach_),
1789  allow_neg_val(allow_neg_values_)
1790  {
1791  init_discretion_factor(discrete);
1792  }
1793 };
1794 
1795 class BranchLinewidthScaler : public Scaler, virtual Noncopyable {
1796  AP_tree *node;
1797  int start_width;
1798  bool wholeSubtree;
1799 
1800 public:
1801  BranchLinewidthScaler(AP_tree *node_, const Position& start, bool wholeSubtree_, AWT_graphic_exports& exports_)
1802  : Scaler(start, 0.1, exports_), // 0.1 = > change linewidth dragpixel/10
1803  node(node_),
1804  start_width(node->get_linewidth()),
1805  wholeSubtree(wholeSubtree_)
1806  {}
1807 
1808  void draw_scale_indicator(const AW::Position& , AW_device *, int) const OVERRIDE {}
1809  void do_scale(const Position& drag_pos) OVERRIDE {
1810  Vector moved = scaling(drag_pos);
1811  double ymove = -moved.y();
1812  int old = node->get_linewidth();
1813 
1814  int width = start_width + ymove;
1816 
1817  if (width != old) {
1818  if (wholeSubtree) {
1819  node->set_linewidth_recursive(width);
1820  }
1821  else {
1822  node->set_linewidth(width);
1823  }
1825  }
1826  }
1827 };
1828 
1829 class BranchRotator FINAL_TYPE : public Dragged, virtual Noncopyable {
1830  AW_device *device;
1831  AP_tree *node;
1832  LineVector clicked_branch;
1833  float orig_angle; // of node
1834  Position hinge;
1835  Position mousepos_world;
1836 
1837  void perform(DragAction, const AW_clicked_element *, const Position& mousepos) OVERRIDE {
1838  mousepos_world = device->rtransform(mousepos);
1839 
1840  double prev_angle = node->get_angle();
1841 
1842  Angle current(hinge, mousepos_world);
1843  Angle orig(clicked_branch.line_vector());
1844  Angle diff = current-orig;
1845 
1846  node->set_angle(orig_angle + diff.radian());
1847 
1848  if (node->get_angle() != prev_angle) get_exports().request_save();
1849  }
1850 
1851  void abort() OVERRIDE {
1852  node->set_angle(orig_angle);
1854  }
1855 
1856 public:
1857  BranchRotator(AW_device *device_, AP_tree *node_, const LineVector& clicked_branch_, const Position& mousepos, AWT_graphic_exports& exports_)
1858  : Dragged(exports_),
1859  device(device_),
1860  node(node_),
1861  clicked_branch(clicked_branch_),
1862  orig_angle(node->get_angle()),
1863  hinge(clicked_branch.start()),
1864  mousepos_world(device->rtransform(mousepos))
1865  {
1866  td_assert(valid_drag_device(device));
1867  }
1868 
1869  void draw_drag_indicator(AW_device *IF_DEBUG(same_device), int drag_gc) const OVERRIDE {
1870  td_assert(valid_drag_device(same_device));
1871  td_assert(device == same_device);
1872 
1873  device->line(drag_gc, clicked_branch);
1874  device->line(drag_gc, LineVector(hinge, mousepos_world));
1875  device->circle(drag_gc, AW::FillStyle::EMPTY, hinge, device->rtransform(Vector(5, 5)));
1876  }
1877 };
1878 
1879 inline Position calc_text_coordinates_near_tip(AW_device *device, int gc, const Position& pos, const Angle& orientation, AW_pos& alignment, double dist_factor = 1.0) {
1888  const AW_font_limits& charLimits = device->get_font_limits(gc, 'A');
1889 
1890  const double text_height = charLimits.get_height() * device->get_unscale();
1891  const double dist = text_height * dist_factor;
1892 
1893  Vector shift = orientation.normal();
1894  // use sqrt of sin(=y) to move text faster between positions below and above branch:
1895  shift.sety(shift.y()>0 ? sqrt(shift.y()) : -sqrt(-shift.y()));
1896 
1897  Position near = pos + dist*shift;
1898  near.movey(.3*text_height); // @@@ just a hack. fix.
1899 
1900  alignment = .5 - .5*orientation.cos();
1901 
1902  return near;
1903 }
1904 
1905 inline Position calc_text_coordinates_aside_line(AW_device *device, int gc, const Position& pos, Angle orientation, bool right, AW_pos& alignment, double dist_factor = 1.0) {
1916  return calc_text_coordinates_near_tip(device, gc, pos, right ? orientation.rotate90deg() : orientation.rotate270deg(), alignment, dist_factor);
1917 }
1918 
1919 class MarkerIdentifier : public Dragged, virtual Noncopyable {
1920  AW_clicked_element *marker; // maybe box, line or text!
1921  Position click;
1922  std::string name;
1923 
1924  void draw_drag_indicator(AW_device *device, int drag_gc) const OVERRIDE {
1925  Position click_world = device->rtransform(click);
1926  Rectangle bbox = marker->get_bounding_box();
1927  Position center = bbox.centroid();
1928 
1929  Vector toClick(center, click_world);
1930  {
1931  double minLen = Vector(center, bbox.nearest_corner(click_world)).length();
1932  if (toClick.length()<minLen) toClick.set_length(minLen);
1933  }
1934  LineVector toHead(center, 1.5*toClick);
1935 
1936  marker->indicate_selected(device, drag_gc);
1937  device->line(drag_gc, toHead);
1938 
1939  Angle orientation(toHead.line_vector());
1940  AW_pos alignment;
1941  Position textPos = calc_text_coordinates_near_tip(device, drag_gc, toHead.head(), Angle(toHead.line_vector()), alignment);
1942 
1943  device->text(drag_gc, name.c_str(), textPos, alignment);
1944  }
1945  void perform(DragAction, const AW_clicked_element*, const Position& mousepos) OVERRIDE {
1946  click = mousepos;
1948  }
1949  void abort() OVERRIDE {
1951  }
1952 
1953 public:
1954  MarkerIdentifier(const AW_clicked_element *marker_, const Position& start, const char *name_, AWT_graphic_exports& exports_)
1955  : Dragged(exports_),
1956  marker(marker_->clone()),
1957  click(start),
1958  name(name_)
1959  {
1961  }
1963  delete marker;
1964  }
1965 
1966 };
1967 
1968 static AW_device_click::ClickPreference preferredForCommand(AWT_COMMAND_MODE mode) {
1969  // return preferred click target for tree-display
1970  // (Note: not made this function a member of AWT_graphic_event,
1971  // since modes are still reused in other ARB applications,
1972  // e.g. AWT_MODE_ROTATE in SECEDIT)
1973 
1974  switch (mode) {
1975  case AWT_MODE_LENGTH:
1976  case AWT_MODE_MULTIFURC:
1977  case AWT_MODE_SPREAD:
1978  return AW_device_click::PREFER_LINE;
1979 
1980  default:
1981  return AW_device_click::PREFER_NEARER;
1982  }
1983 }
1984 
1986  td_assert(event.button()!=AW_BUTTON_MIDDLE); // shall be handled by caller
1987 
1988  if (!tree_static) return; // no tree -> no commands
1989 
1990  if (event.type() == AW_Keyboard_Release) return;
1991  if (event.type() == AW_Keyboard_Press) return handle_key(device, event);
1992 
1993  // @@@ move code below into separate member function handle_mouse()
1994 
1995  if (event.button() != AW_BUTTON_LEFT && event.button() != AW_BUTTON_RIGHT) return; // nothing else is currently handled here
1996 
1997  ClickedTarget clicked(this, event.best_click(preferredForCommand(event.cmd())));
1998  // Note: during drag/release 'clicked'
1999  // - contains drop-target (only if AWT_graphic::drag_target_detection is requested)
2000  // - no longer contains initially clicked element (in all other modes)
2001  // see also ../CANVAS/canvas.cxx@motion_event
2002 
2003  if (clicked.species()) {
2004  if (command_on_GBDATA(clicked.species(), event, map_viewer_cb)) {
2006  }
2007  return;
2008  }
2009 
2010  if (!tree_static->get_root_node()) return; // no tree -> no commands
2011 
2012  const Position& mousepos = event.position();
2013 
2014  // -------------------------------------
2015  // generic drag & drop handler
2016  {
2017  AWT_command_data *cmddata = get_command_data();
2018  if (cmddata) {
2019  Dragged *dragging = dynamic_cast<Dragged*>(cmddata);
2020  if (dragging) {
2021  dragging->hide_drag_indicator(device, drag_gc);
2022  if (event.type() == AW_Mouse_Press) {
2023  // mouse pressed while dragging (e.g. press other button)
2024  dragging->abort(); // abort what ever we did
2026  }
2027  else {
2028  switch (event.type()) {
2029  case AW_Mouse_Drag:
2030  dragging->do_drag(clicked.element(), mousepos);
2031  dragging->draw_drag_indicator(device, drag_gc);
2032  break;
2033 
2034  case AW_Mouse_Release:
2035  dragging->do_drop(clicked.element(), mousepos);
2037  break;
2038  default:
2039  break;
2040  }
2041  }
2042  return;
2043  }
2044  }
2045  }
2046 
2047  if (event.type() != AW_Mouse_Press) return; // no drag/drop handling below!
2048 
2049  if (clicked.is_ruler()) {
2050  DB_scalable xdata;
2051  DB_scalable ydata;
2052  double unscale = device->get_unscale();
2053  GBDATA *gb_tree = tree_static->get_gb_tree();
2054 
2055  switch (event.cmd()) {
2056  case AWT_MODE_LENGTH:
2057  case AWT_MODE_MULTIFURC: { // scale ruler
2059 
2060  double rel = clicked.get_rel_attach();
2061  if (tree_style == AP_TREE_IRS) {
2062  unscale /= (rel-1)*irs_tree_ruler_scale_factor; // ruler has opposite orientation in IRS mode
2063  }
2064  else {
2065  unscale /= rel;
2066  }
2067 
2068  if (event.button() == AW_BUTTON_RIGHT) xdata.set_discretion_factor(10);
2069  xdata.set_min(0.01);
2070  break;
2071  }
2072  case AWT_MODE_LINE: // scale ruler linewidth
2074  ydata.set_min(0);
2075  ydata.inverse();
2076  break;
2077 
2078  default: { // move ruler or ruler text
2079  bool isText = clicked.is_text();
2080  xdata = GB_searchOrCreate_float(gb_tree, ruler_awar(isText ? "text_x" : "ruler_x"), 0.0);
2081  ydata = GB_searchOrCreate_float(gb_tree, ruler_awar(isText ? "text_y" : "ruler_y"), 0.0);
2082  break;
2083  }
2084  }
2085  if (!is_nan_or_inf(unscale)) {
2086  store_command_data(new RulerScaler(mousepos, unscale, xdata, ydata, exports));
2087  }
2088  return;
2089  }
2090 
2091  if (clicked.is_marker()) {
2092  if (clicked.element()->get_distance() <= 3) { // accept 3 pixel distance
2093  display_markers->handle_click(clicked.get_markerindex(), event.button(), exports);
2094  if (event.button() == AW_BUTTON_LEFT) {
2095  const char *name = display_markers->get_marker_name(clicked.get_markerindex());
2096  store_command_data(new MarkerIdentifier(clicked.element(), mousepos, name, exports));
2097  }
2098  }
2099  return;
2100  }
2101 
2102  if (warn_inappropriate_mode(event.cmd())) {
2103  return;
2104  }
2105 
2106  switch (event.cmd()) {
2107  // -----------------------------
2108  // two point commands:
2109 
2110  case AWT_MODE_MOVE:
2111  if (clicked.node() && clicked.node()->father) {
2112  drag_target_detection(true);
2113  BranchMover *mover = new BranchMover(clicked.element(), event.button(), *this);
2114  store_command_data(mover);
2115  mover->draw_drag_indicator(device, drag_gc);
2116  }
2117  break;
2118 
2119  case AWT_MODE_LENGTH:
2120  case AWT_MODE_MULTIFURC:
2121  if (clicked.node() && clicked.is_branch()) {
2122  bool allow_neg_branches = aw_root->awar(AWAR_EXPERT)->read_int();
2123  bool discrete_lengths = event.button() == AW_BUTTON_RIGHT;
2124 
2125  const AW_clicked_line *cl = dynamic_cast<const AW_clicked_line*>(clicked.element());
2126  td_assert(cl);
2127 
2129  BranchScaler *scaler = new BranchScaler(mode, clicked.node(), cl->get_line(), clicked.element()->get_attach_point(), mousepos, device->get_unscale(), discrete_lengths, allow_neg_branches, exports);
2130 
2131  store_command_data(scaler);
2132  scaler->draw_drag_indicator(device, drag_gc);
2133  }
2134  break;
2135 
2136  case AWT_MODE_ROTATE:
2137  if (clicked.node()) {
2138  BranchRotator *rotator = NULp;
2139  if (clicked.is_branch()) {
2140  const AW_clicked_line *cl = dynamic_cast<const AW_clicked_line*>(clicked.element());
2141  td_assert(cl);
2142  rotator = new BranchRotator(device, clicked.node(), cl->get_line(), mousepos, exports);
2143  }
2144  else { // rotate branches inside a folded group (allows to modify size of group triangle)
2145  const AW_clicked_polygon *poly = dynamic_cast<const AW_clicked_polygon*>(clicked.element());
2146  if (poly) {
2147  int npos;
2148  const AW::Position *pos = poly->get_polygon(npos);
2149 
2150  if (npos == 3) { // only makes sense in radial mode (which uses triangles)
2151  LineVector left(pos[0], pos[1]);
2152  LineVector right(pos[0], pos[2]);
2153 
2154  Position mousepos_world = device->rtransform(mousepos);
2155 
2156  if (Distance(mousepos_world, left) < Distance(mousepos_world, right)) {
2157  rotator = new BranchRotator(device, clicked.node()->get_leftson(), left, mousepos, exports);
2158  }
2159  else {
2160  rotator = new BranchRotator(device, clicked.node()->get_rightson(), right, mousepos, exports);
2161  }
2162  }
2163  }
2164  }
2165  if (rotator) {
2166  store_command_data(rotator);
2167  rotator->draw_drag_indicator(device, drag_gc);
2168  }
2169  }
2170  break;
2171 
2172  case AWT_MODE_LINE:
2173  if (clicked.node()) {
2174  BranchLinewidthScaler *widthScaler = new BranchLinewidthScaler(clicked.node(), mousepos, event.button() == AW_BUTTON_RIGHT, exports);
2175  store_command_data(widthScaler);
2176  widthScaler->draw_drag_indicator(device, drag_gc);
2177  }
2178  break;
2179 
2180  case AWT_MODE_SPREAD:
2181  if (clicked.node() && clicked.is_branch()) {
2182  const AW_clicked_line *cl = dynamic_cast<const AW_clicked_line*>(clicked.element());
2183  td_assert(cl);
2184  BranchScaler *spreader = new BranchScaler(SCALE_SPREAD, clicked.node(), cl->get_line(), clicked.element()->get_attach_point(), mousepos, device->get_unscale(), false, false, exports);
2185  store_command_data(spreader);
2186  spreader->draw_drag_indicator(device, drag_gc);
2187  }
2188  break;
2189 
2190  // -----------------------------
2191  // one point commands:
2192 
2193  case AWT_MODE_LZOOM:
2194  switch (event.button()) {
2195  case AW_BUTTON_LEFT:
2196  if (clicked.node()) {
2197  set_logical_root_to(clicked.node());
2199  }
2200  break;
2201  case AW_BUTTON_RIGHT:
2202  if (displayed_root->father) {
2203  set_logical_root_to(displayed_root->get_father());
2205  }
2206  break;
2207 
2208  default: td_assert(0); break;
2209  }
2210  break;
2211 
2212 act_like_group :
2213  case AWT_MODE_GROUP:
2214  if (clicked.node()) {
2215  switch (event.button()) {
2216  case AW_BUTTON_LEFT:
2217  toggle_folding_at(clicked.node(), false);
2218  break;
2219  case AW_BUTTON_RIGHT:
2220  if (tree_static->get_gb_tree()) {
2221  toggle_group(clicked.node());
2222  }
2223  break;
2224  default: td_assert(0); break;
2225  }
2226  }
2227  break;
2228 
2229  case AWT_MODE_SETROOT:
2230  switch (event.button()) {
2231  case AW_BUTTON_LEFT:
2232  if (clicked.node()) {
2233  clicked.node()->set_root();
2235  }
2236  break;
2237  case AW_BUTTON_RIGHT:
2238  tree_static->find_innermost_edge().set_root();
2240  break;
2241  default: td_assert(0); break;
2242  }
2244  break;
2245 
2246  case AWT_MODE_SWAP:
2247  if (clicked.node()) {
2248  switch (event.button()) {
2249  case AW_BUTTON_LEFT: clicked.node()->swap_sons(); break;
2250  case AW_BUTTON_RIGHT: clicked.node()->rotate_subtree(); break;
2251  default: td_assert(0); break;
2252  }
2254  }
2255  break;
2256 
2257  case AWT_MODE_MARK: // see also .@OTHER_MODE_MARK_HANDLER
2258  if (clicked.node()) {
2259  GB_transaction ta(tree_static->get_gb_main());
2260 
2261  switch (event.button()) {
2262  case AW_BUTTON_LEFT: mark_species_in_tree(clicked.node(), 1); break;
2263  case AW_BUTTON_RIGHT: mark_species_in_tree(clicked.node(), 0); break;
2264  default: td_assert(0); break;
2265  }
2266  tree_static->update_timers(); // do not reload the tree
2268  }
2269  break;
2270 
2271  case AWT_MODE_NONE:
2272  case AWT_MODE_SELECT:
2273  if (clicked.node()) {
2274  GB_transaction ta(tree_static->get_gb_main());
2275  exports.request_refresh(); // No refresh needed !! AD_map_viewer will do the refresh (needed by arb_pars)
2276  map_viewer_cb(clicked.node()->gb_node, ADMVT_SELECT);
2277 
2278  if (event.button() == AW_BUTTON_LEFT) goto act_like_group; // now do the same like in AWT_MODE_GROUP
2279  }
2280  break;
2281 
2282  // now handle all modes which only act on tips (aka species) and
2283  // shall perform identically in tree- and list-modes
2284 
2285  case AWT_MODE_INFO:
2286  case AWT_MODE_WWW: {
2287  if (clicked.node() && clicked.node()->gb_node) {
2288  if (command_on_GBDATA(clicked.node()->gb_node, event, map_viewer_cb)) {
2290  }
2291  }
2292  break;
2293  }
2294  default:
2295  break;
2296  }
2297 }
2298 
2300  if (is_list_style(style)) {
2301  if (tree_style == style) { // we are already in wanted view
2302  nds_only_marked = !nds_only_marked; // -> toggle between 'marked' and 'all'
2303  }
2304  else {
2305  nds_only_marked = false; // default to all
2306  }
2307  }
2308  tree_style = style;
2309  apply_zoom_settings_for_treetype(ntw); // sets default padding
2310 
2313 
2314  exports.dont_scroll = 0;
2315 
2316  switch (style) {
2317  case AP_TREE_RADIAL:
2318  break;
2319 
2320  case AP_LIST_SIMPLE:
2321  case AP_LIST_NDS:
2324 
2325  break;
2326 
2327  case AP_TREE_IRS: // folded dendrogram
2330  exports.dont_scroll = 1;
2331  break;
2332 
2333  case AP_TREE_NORMAL: // normal dendrogram
2336  break;
2337  }
2338 }
2339 
2341 static GraphicTreeCallback treeChangeIgnore_cb = makeGraphicTreeCallback(tree_change_ignore_cb);
2342 
2344  AWT_graphic(),
2345  species_name(NULp),
2346  baselinewidth(1),
2347  tree_proto(NULp),
2348  link_to_database(false),
2349  group_style(GS_TRAPEZE),
2350  line_filter (AW_SCREEN|AW_CLICK|AW_TRACK|AW_CLICK_DROP|AW_PRINTER|AW_SIZE), // horizontal lines (ie. lines towards leafs in dendro-view; all lines in radial view)
2351  vert_line_filter (AW_SCREEN|AW_CLICK|AW_CLICK_DROP|AW_PRINTER), // vertical lines (in dendro view; @@@ should be used in IRS as well!)
2352  mark_filter (AW_SCREEN|AW_CLICK|AW_TRACK|AW_CLICK_DROP|AW_PRINTER_EXT), // diamond at open group (dendro+radial); boxes at marked species (all views); origin (radial view); cursor box (all views); group-handle (IRS)
2354  leaf_text_filter (AW_SCREEN|AW_CLICK|AW_TRACK|AW_CLICK_DROP|AW_PRINTER|AW_SIZE_UNSCALED), // text at leafs (all views but IRS? @@@ should be used in IRS as well)
2356  other_text_filter (AW_SCREEN|AW_PRINTER|AW_SIZE_UNSCALED),
2357  ruler_filter (AW_SCREEN|AW_CLICK|AW_PRINTER), // appropriate size-filter added manually in code
2358  root_filter (AW_SCREEN|AW_PRINTER_EXT), // unused (@@@ should be used for radial root)
2359  marker_filter (AW_SCREEN|AW_CLICK|AW_PRINTER_EXT|AW_SIZE_UNSCALED), // species markers (eg. visualizing configs)
2360  group_info_pos(GIP_SEPARATED),
2361  group_count_mode(GCM_MEMBERS),
2362  branch_style(BS_RECTANGULAR),
2363  display_markers(NULp),
2364  map_viewer_cb(map_viewer_cb_),
2365  cmd_data(NULp),
2366  tree_static(NULp),
2367  displayed_root(NULp),
2368  tree_changed_cb(treeChangeIgnore_cb),
2369  autoUnfolded(new AP_tree_folding),
2370  aw_root(aw_root_),
2371  gb_main(gb_main_),
2372  nds_only_marked(false)
2373 {
2374  td_assert(gb_main);
2376 }
2377 
2379  delete cmd_data;
2380  free(species_name);
2381  destroy(tree_proto);
2382  delete tree_static;
2383  delete display_markers;
2384  delete autoUnfolded;
2385 }
2386 
2387 AP_tree_root *AWT_graphic_tree::create_tree_root(AliView *aliview, AP_sequence *seq_prototype, bool insert_delete_cbs) {
2388  return new AP_tree_root(aliview, seq_prototype, insert_delete_cbs, &groupScale);
2389 }
2390 
2391 void AWT_graphic_tree::init(AliView *aliview, AP_sequence *seq_prototype, bool link_to_database_, bool insert_delete_cbs) {
2392  tree_static = create_tree_root(aliview, seq_prototype, insert_delete_cbs);
2393  td_assert(!insert_delete_cbs || link_to_database); // inserting delete callbacks w/o linking to DB has no effect!
2394  link_to_database = link_to_database_;
2395 }
2396 
2397 void AWT_graphic_tree::unload() {
2399  if (display_markers) display_markers->flush_cache();
2400  deselect_group();
2401  destroy(tree_static->get_root_node());
2402  displayed_root = NULp;
2403 }
2404 
2406  GB_ERROR error = NULp;
2407 
2408  if (!name) { // happens in error-case (called by AWT_graphic::update_DB_and_model_as_requested to load previous state)
2409  if (tree_static) {
2410  name = tree_static->get_tree_name();
2411  td_assert(name);
2412  }
2413  else {
2414  error = "Please select a tree (name lost)";
2415  }
2416  }
2417 
2418  if (!error) {
2419  if (name[0] == 0 || strcmp(name, NO_TREE_SELECTED) == 0) {
2420  unload();
2421  zombies = 0;
2422  duplicates = 0;
2423  }
2424  else {
2425  GBDATA *gb_group = get_selected_group().get_group_data(); // remember selected group
2426  freenull(tree_static->gone_tree_name);
2427  {
2428  char *name_dup = strdup(name); // name might be freed by unload()
2429  unload();
2430  error = tree_static->loadFromDB(name_dup);
2431  free(name_dup);
2432  }
2433 
2434  if (!error && link_to_database) {
2435  error = tree_static->linkToDB_and_count(&zombies, &duplicates);
2436  }
2437 
2438  if (error) {
2439  destroy(tree_static->get_root_node());
2440  }
2441  else {
2442  displayed_root = get_root_node();
2445 
2446  td_assert(!display_markers || display_markers->cache_is_flushed());
2447 
2450  }
2451  select_group(gb_group);
2452  }
2453  }
2454 
2455  tree_changed_cb(this);
2456  return error;
2457 }
2458 
2459 GB_ERROR AWT_graphic_tree::save_to_DB(GBDATA * /* dummy */, const char * /* name */) {
2460  GB_ERROR error = NULp;
2461  if (get_root_node()) {
2462  error = tree_static->saveToDB();
2463  if (display_markers) display_markers->flush_cache();
2464  }
2465  else if (tree_static && tree_static->get_tree_name()) {
2466  if (tree_static->gb_tree_gone) {
2467  td_assert(!tree_static->gone_tree_name);
2468  tree_static->gone_tree_name = strdup(tree_static->get_tree_name());
2469 
2470  GB_transaction ta(gb_main);
2471  error = GB_delete(tree_static->gb_tree_gone);
2472  error = ta.close(error);
2473 
2474  if (!error) {
2475  aw_message(GBS_global_string("Tree '%s' lost all leafs and has been deleted", tree_static->get_tree_name()));
2476 
2477  // @@@ TODO: somehow update selected tree
2478 
2479  // solution: currently selected tree (in NTREE, maybe also in PARSIMONY)
2480  // needs to add a delete callback on treedata in DB
2481  }
2482 
2483  tree_static->gb_tree_gone = NULp; // do not delete twice
2484  }
2485  }
2486  tree_changed_cb(this);
2487  return error;
2488 }
2489 
2491  td_assert(exports.flags_writeable()); // otherwise fails on requests below
2492 
2493  if (tree_static) {
2494  AP_tree_root *troot = get_tree_root();
2495  if (troot) {
2496  GB_transaction ta(gb_main);
2497 
2498  AP_UPDATE_FLAGS flags = troot->check_update();
2499  switch (flags) {
2500  case AP_UPDATE_OK:
2501  case AP_UPDATE_ERROR:
2502  break;
2503 
2504  case AP_UPDATE_RELOADED: {
2505  const char *name = tree_static->get_tree_name();
2506  if (name) {
2507  GB_ERROR error = load_from_DB(gb_main, name);
2508  if (error) aw_message(error);
2509  else exports.request_resize();
2510  }
2511  break;
2512  }
2513  case AP_UPDATE_RELINKED: {
2514  AP_tree *tree_root = get_root_node();
2515  if (tree_root) {
2516  GB_ERROR error = tree_root->relink();
2517  if (error) aw_message(error);
2519  }
2520  break;
2521  }
2522  }
2523  }
2524  }
2525 }
2526 
2529 }
2530 
2531 void AWT_graphic_tree::summarizeGroupMarkers(AP_tree *at, NodeMarkers& markers) {
2535  td_assert(display_markers);
2536  td_assert(markers.getNodeSize() == 0);
2537  if (at->is_leaf()) {
2538  if (at->name) {
2539  display_markers->retrieve_marker_state(at->name, markers);
2540  }
2541  }
2542  else {
2543  if (at->is_clade()) {
2544  const NodeMarkers *cached = display_markers->read_cache(at);
2545  if (cached) {
2546  markers = *cached;
2547  return;
2548  }
2549  }
2550 
2551  summarizeGroupMarkers(at->get_leftson(), markers);
2552  NodeMarkers rightMarkers(display_markers->size());
2553  summarizeGroupMarkers(at->get_rightson(), rightMarkers);
2554  markers.add(rightMarkers);
2555 
2556  if (at->is_clade()) {
2557  display_markers->write_cache(at, markers);
2558  }
2559  }
2560 }
2561 
2562 class MarkerXPos {
2563  double Width;
2564  double Offset;
2565  int markers;
2566 public:
2567 
2568  static int marker_width;
2569 
2570  MarkerXPos(AW_pos scale, int markers_)
2571  : Width((marker_width-1) / scale),
2572  Offset(marker_width / scale),
2573  markers(markers_)
2574  {}
2575 
2576  double width() const { return Width; }
2577  double offset() const { return Offset; }
2578 
2579  double leftx (int markerIdx) const { return (markerIdx - markers - 0.0) * offset(); }
2580  double centerx(int markerIdx) const { return leftx(markerIdx) + width()/2; }
2581 };
2582 
2583 int MarkerXPos::marker_width = 3;
2584 
2585 class MarkerPosition : public MarkerXPos {
2586  double y1, y2;
2587 public:
2588  MarkerPosition(AW_pos scale, int markers_, double y1_, double y2_)
2589  : MarkerXPos(scale, markers_),
2590  y1(y1_),
2591  y2(y2_)
2592  {}
2593 
2594  Position pos(int markerIdx) const { return Position(leftx(markerIdx), y1); }
2595  Vector size() const { return Vector(width(), y2-y1); }
2596 };
2597 
2598 
2599 void AWT_graphic_tree::drawMarker(const class MarkerPosition& marker, const bool partial, const int markerIdx) {
2600  td_assert(display_markers);
2601 
2602  const int gc = MarkerGC[markerIdx % MARKER_COLORS];
2603 
2604  if (partial) disp_device->set_grey_level(gc, marker_greylevel);
2605  disp_device->box(gc, partial ? AW::FillStyle::SHADED : AW::FillStyle::SOLID, marker.pos(markerIdx), marker.size(), marker_filter);
2606 }
2607 
2608 void AWT_graphic_tree::detectAndDrawMarkers(AP_tree *at, const double y1, const double y2) {
2609  td_assert(display_markers);
2610 
2611  if (disp_device->type() != AW_DEVICE_SIZE) {
2612  // Note: extra device scaling (needed to show flags) is done by drawMarkerNames
2613 
2614  int numMarkers = display_markers->size();
2615  MarkerPosition flag(disp_device->get_scale(), numMarkers, y1, y2);
2616  NodeMarkers markers(numMarkers);
2617 
2618  summarizeGroupMarkers(at, markers);
2619 
2620  if (markers.getNodeSize()>0) {
2621  AW_click_cd clickflag(disp_device, 0, CL_FLAG);
2622  for (int markerIdx = 0 ; markerIdx < numMarkers ; markerIdx++) {
2623  if (markers.markerCount(markerIdx) > 0) {
2624  bool draw = at->is_leaf();
2625  bool partial = false;
2626 
2627  if (!draw) { // group
2628  td_assert(at->is_clade());
2629  double markRate = markers.getMarkRate(markerIdx);
2630  if (markRate>=groupThreshold.partiallyMarked && markRate>0.0) {
2631  draw = true;
2632  partial = markRate<groupThreshold.marked;
2633  }
2634  }
2635 
2636  if (draw) {
2637  clickflag.set_cd1(markerIdx);
2638  drawMarker(flag, partial, markerIdx);
2639  }
2640  }
2641  }
2642  }
2643  }
2644 }
2645 
2646 void AWT_graphic_tree::drawMarkerNames(Position& Pen) {
2647  td_assert(display_markers);
2648 
2649  int numMarkers = display_markers->size();
2650  MarkerXPos flag(disp_device->get_scale(), numMarkers);
2651 
2652  if (disp_device->type() != AW_DEVICE_SIZE) {
2653  Position pl1(flag.centerx(numMarkers-1), Pen.ypos()); // upper point of thin line
2654  Pen.movey(scaled_branch_distance);
2655  Position pl2(pl1.xpos(), Pen.ypos()); // lower point of thin line
2656 
2657  Vector sizeb(flag.width(), scaled_branch_distance); // size of boxes
2658  Vector b2t(flag.offset()+scaled_branch_distance, scaled_branch_distance); // offset box->text
2659  Vector toNext(-flag.offset(), scaled_branch_distance); // offset to next box
2660 
2661  Rectangle mbox(Position(flag.leftx(numMarkers-1), pl2.ypos()), sizeb); // the marker box
2662 
2663  AW_click_cd clickflag(disp_device, 0, CL_FLAG);
2664 
2665  for (int markerIdx = numMarkers - 1 ; markerIdx >= 0 ; markerIdx--) {
2666  const char *markerName = display_markers->get_marker_name(markerIdx);
2667  if (markerName) {
2668  int gc = MarkerGC[markerIdx % MARKER_COLORS];
2669 
2670  clickflag.set_cd1(markerIdx);
2671 
2672  disp_device->line(gc, pl1, pl2, marker_filter);
2673  disp_device->box(gc, AW::FillStyle::SOLID, mbox, marker_filter);
2674  disp_device->text(gc, markerName, mbox.upper_left_corner()+b2t, 0, marker_filter);
2675  }
2676 
2677  pl1.movex(toNext.x());
2678  pl2.move(toNext);
2679  mbox.move(toNext);
2680  }
2681 
2682  Pen.movey(scaled_branch_distance * (numMarkers+2));
2683  }
2684  else { // just reserve space on size device
2685  Pen.movey(scaled_branch_distance * (numMarkers+3));
2686  Position leftmost(flag.leftx(0), Pen.ypos());
2687  disp_device->line(AWT_GC_CURSOR, Pen, leftmost, marker_filter);
2688  }
2689 }
2690 
2691 void AWT_graphic_tree::pixel_box(int gc, const AW::Position& pos, int pixel_width, AW::FillStyle filled) {
2692  double diameter = disp_device->rtransform_pixelsize(pixel_width);
2693  Vector diagonal(diameter, diameter);
2694 
2695  td_assert(!filled.is_shaded()); // the pixel box is either filled or empty! (by design)
2696  if (filled.somehow()) disp_device->set_grey_level(gc, group_greylevel); // @@@ should not be needed here, but changes test-results (xfig-shading need fixes anyway)
2697  else disp_device->set_line_attributes(gc, 1, AW_SOLID);
2698  disp_device->box(gc, filled, pos-0.5*diagonal, diagonal, mark_filter);
2699 }
2700 
2701 void AWT_graphic_tree::diamond(int gc, const Position& posIn, int pixel_radius) {
2702  // filled box with one corner down
2703  Position spos = disp_device->transform(posIn);
2704  Vector hor = Vector(pixel_radius, 0);
2705  Vector ver = Vector(0, pixel_radius);
2706 
2707  Position corner[4] = {
2708  disp_device->rtransform(spos+hor),
2709  disp_device->rtransform(spos+ver),
2710  disp_device->rtransform(spos-hor),
2711  disp_device->rtransform(spos-ver),
2712  };
2713 
2714  disp_device->polygon(gc, AW::FillStyle::SOLID, 4, corner, mark_filter);
2715 }
2716 
2717 void AWT_graphic_tree::show_dendrogram(AP_tree *at, Position& Pen, DendroSubtreeLimits& limits, const NDS_Labeler& labeler) {
2725  if (disp_device->type() != AW_DEVICE_SIZE) { // tree below cliprect bottom can be cut
2726  Position p(0, Pen.ypos() - scaled_branch_distance *2.0);
2727  Position s = disp_device->transform(p);
2728 
2729  bool is_clipped = false;
2730  double offset = 0.0;
2731  if (disp_device->is_below_clip(s.ypos())) {
2732  offset = scaled_branch_distance;
2733  is_clipped = true;
2734  }
2735  else {
2736  p.sety(Pen.ypos() + scaled_branch_distance *(at->gr.view_sum+2));
2737  s = disp_device->transform(p);
2738 
2739  if (disp_device->is_above_clip(s.ypos())) {
2740  offset = scaled_branch_distance*at->gr.view_sum;
2741  is_clipped = true;
2742  }
2743  }
2744 
2745  if (is_clipped) {
2746  limits.x_right = Pen.xpos();
2747  limits.y_branch = Pen.ypos();
2748  Pen.movey(offset);
2749  limits.y_top = limits.y_bot = Pen.ypos();
2750  return;
2751  }
2752  }
2753 
2754  static int recursion_depth = 0;
2755 
2756  AW_click_cd cd(disp_device, (AW_CL)at, CL_NODE);
2757  if (at->is_leaf()) {
2758  if (at->gb_node && GB_read_flag(at->gb_node)) {
2760  filled_box(at->gr.gc, Pen, NT_BOX_WIDTH);
2761  }
2762 
2763  int gc = at->gr.gc;
2764  bool is_group = false;
2765 
2766  if (at->hasName(species_name)) {
2767  selSpec = PaintedNode(Pen, at);
2768  }
2769  if (at->is_keeled_group()) { // keeled groups may appear at leafs!
2770  is_group = true;
2771  bool is_selected = selected_group.at_node(at);
2772  if (is_selected) {
2773  selGroup = PaintedNode(Pen, at);
2774  gc = int(AWT_GC_CURSOR);
2775  }
2776  }
2777 
2778  if ((at->name || is_group) && (disp_device->get_filter() & leaf_text_filter)) {
2779  // display text
2780  const AW_font_limits& charLimits = disp_device->get_font_limits(gc, 'A');
2781 
2782  double unscale = disp_device->get_unscale();
2783  Position textPos = Pen + 0.5*Vector((charLimits.width+NT_BOX_WIDTH)*unscale, scaled_font.ascent);
2784 
2785  if (display_markers) {
2786  detectAndDrawMarkers(at, Pen.ypos() - scaled_branch_distance * 0.495, Pen.ypos() + scaled_branch_distance * 0.495);
2787  }
2788 
2789  const char *data = labeler.speciesLabel(this->gb_main, at->gb_node, at, tree_static->get_tree_name());
2790  if (is_group) {
2791  static SmartCharPtr buf;
2792 
2793  buf = strdup(data);
2794 
2795  const GroupInfo& info = get_group_info(at, GI_COMBINED, false, labeler); // retrieves group info for leaf!
2796 
2797  buf = GBS_global_string_copy("%s (=%s)", &*buf, info.name);
2798  data = &*buf;
2799  }
2800 
2801  SizedCstr sdata(data);
2802 
2803  disp_device->text(gc, sdata, textPos, 0.0, leaf_text_filter);
2804  double textsize = disp_device->get_string_size(gc, sdata) * unscale;
2805 
2806  limits.x_right = textPos.xpos() + textsize;
2807  }
2808  else {
2809  limits.x_right = Pen.xpos();
2810  }
2811 
2812  limits.y_top = limits.y_bot = limits.y_branch = Pen.ypos();
2813  Pen.movey(scaled_branch_distance);
2814  }
2815  else if (recursion_depth>=MAX_TREEDISP_RECURSION_DEPTH) { // limit recursion depth
2816  const char *data = TREEDISP_TRUNCATION_MESSAGE;
2817  SizedCstr sdata(data);
2818 
2819  int gc = AWT_GC_ONLY_ZOMBIES;
2820  const AW_font_limits& charLimits = disp_device->get_font_limits(gc, 'A');
2821  double unscale = disp_device->get_unscale();
2822  Position textPos = Pen + 0.5*Vector((charLimits.width+NT_BOX_WIDTH)*unscale, scaled_font.ascent);
2823  disp_device->text(gc, sdata, textPos, 0.0, leaf_text_filter);
2824  double textsize = disp_device->get_string_size(gc, sdata) * unscale;
2825 
2826  limits.x_right = textPos.xpos() + textsize;
2827  limits.y_top = limits.y_bot = limits.y_branch = Pen.ypos();
2828  Pen.movey(scaled_branch_distance);
2829  }
2830 
2831  // s0-------------n0
2832  // |
2833  // attach (to father)
2834  // |
2835  // s1------n1
2836 
2837  else if (at->is_folded_group()) {
2838  double height = scaled_branch_distance * at->gr.view_sum;
2839  double box_height = height-scaled_branch_distance;
2840 
2841  Position s0(Pen);
2842  Position s1(s0); s1.movey(box_height);
2843  Position n0(s0); n0.movex(at->gr.max_tree_depth);
2844  Position n1(s1); n1.movex(at->gr.min_tree_depth);
2845 
2847 
2848  if (display_markers) {
2849  detectAndDrawMarkers(at, s0.ypos(), s1.ypos());
2850  }
2851 
2852  disp_device->set_grey_level(at->gr.gc, group_greylevel);
2853 
2854  bool is_selected = selected_group.at_node(at);
2855  const int group_gc = is_selected ? int(AWT_GC_CURSOR) : at->gr.gc;
2856 
2857  Position s_attach; // parent attach point
2858  LineVector g_diag; // diagonal line at right side of group ("short side" -> "long side", ie. pointing rightwards)
2859  {
2860  Position group[4] = { s0, s1, n1, n0 }; // init with long side at top (=traditional orientation)
2861 
2862  bool flip = false;
2863  switch (group_orientation) {
2864  case GO_TOP: flip = false; break;
2865  case GO_BOTTOM: flip = true; break;
2866  case GO_EXTERIOR: flip = at->is_lower_son(); break;
2867  case GO_INTERIOR: flip = at->is_upper_son(); break;
2868  }
2869  if (flip) { // flip triangle/trapeze vertically
2870  double x2 = group[2].xpos();
2871  group[2].setx(group[3].xpos());
2872  group[3].setx(x2);
2873  g_diag = LineVector(group[3], group[2]); // n0 -> n1
2874  }
2875  else {
2876  g_diag = LineVector(group[2], group[3]); // n1 -> n0
2877  }
2878 
2879  s_attach = s1+(flip ? 1.0-attach_group : attach_group)*(s0-s1);
2880 
2881  if (group_style == GS_TRIANGLE) {
2882  group[1] = s_attach;
2883  disp_device->polygon(at->gr.gc, AW::FillStyle::SHADED_WITH_BORDER, 3, group+1, line_filter);
2884  if (is_selected) disp_device->polygon(group_gc, AW::FillStyle::EMPTY, 3, group+1, line_filter);
2885  }
2886  else {
2887  td_assert(group_style == GS_TRAPEZE); // traditional style
2888  disp_device->polygon(at->gr.gc, AW::FillStyle::SHADED_WITH_BORDER, 4, group, line_filter);
2889  if (is_selected) disp_device->polygon(at->gr.gc, AW::FillStyle::EMPTY, 4, group, line_filter);
2890  }
2891  }
2892 
2893  if (is_selected) selGroup = PaintedNode(s_attach, at);
2894 
2895  limits.x_right = n0.xpos();
2896 
2897  if (disp_device->get_filter() & group_text_filter) {
2898  const GroupInfo& info = get_group_info(at, group_info_pos == GIP_SEPARATED ? GI_SEPARATED : GI_COMBINED, group_info_pos == GIP_OVERLAYED, labeler);
2899  const AW_font_limits& charLimits = disp_device->get_font_limits(group_gc, 'A');
2900 
2901  const double text_ascent = charLimits.ascent * disp_device->get_unscale();
2902  const double char_width = charLimits.width * disp_device->get_unscale();
2903 
2904  if (info.name) { // attached info
2905 
2906  Position textPos;
2907 
2908  const double gy = g_diag.line_vector().y();
2909  const double group_height = fabs(gy);
2910 
2911  if (group_height<=text_ascent) {
2912  textPos = Position(g_diag.head().xpos(), g_diag.centroid().ypos()+text_ascent*0.5);
2913  }
2914  else {
2915  Position pmin(g_diag.start()); // text position at short side of polygon (=leftmost position)
2916  Position pmax(g_diag.head()); // text position at long side of polygon (=rightmost position)
2917 
2918  const double shift_right = g_diag.line_vector().x() * text_ascent / group_height; // rightward shift needed at short side (to avoid overlap with group polygon)
2919 
2920  if (gy < 0.0) { // long side at top
2921  pmin.movex(shift_right);
2922  pmax.movey(text_ascent);
2923  }
2924  else { // long side at bottom
2925  pmin.move(Vector(shift_right, text_ascent));
2926  }
2927 
2928  textPos = pmin + 0.125*(pmax-pmin);
2929  }
2930 
2931  textPos.movex(char_width);
2932 
2933  SizedCstr infoName(info.name, info.name_len);
2934  disp_device->text(group_gc, infoName, textPos, 0.0, group_text_filter);
2935 
2936  double textsize = disp_device->get_string_size(group_gc, infoName) * disp_device->get_unscale();
2937  limits.x_right = std::max(limits.x_right, textPos.xpos()+textsize);
2938  }
2939 
2940  if (info.count) { // overlayed info
2941  SizedCstr infoCount(info.count, info.count_len);
2942  const double textsize = disp_device->get_string_size(group_gc, infoCount) * disp_device->get_unscale();
2943  Position countPos;
2944  if (group_style == GS_TRIANGLE) {
2945  countPos = s_attach + Vector(g_diag.centroid()-s_attach)*0.666 + Vector(-textsize, text_ascent)*0.5;
2946  }
2947  else {
2948  countPos = s_attach + Vector(char_width, 0.5*text_ascent);
2949  }
2950  disp_device->text(group_gc, infoCount, countPos, 0.0, group_text_filter);
2951 
2952  limits.x_right = std::max(limits.x_right, countPos.xpos()+textsize);
2953  }
2954  }
2955 
2956  limits.y_top = s0.ypos();
2957  limits.y_bot = s1.ypos();
2958  limits.y_branch = s_attach.ypos();
2959 
2960  Pen.movey(height);
2961  }
2962  else { // furcation
2963  bool is_group = at->is_clade();
2964  bool is_selected = is_group && selected_group.at_node(at);
2965  const int group_gc = is_selected ? int(AWT_GC_CURSOR) : at->gr.gc;
2966 
2967  Position s0(Pen);
2968 
2969  Pen.movex(at->leftlen);
2970  Position n0(Pen);
2971 
2972  ++recursion_depth;
2973 
2974  show_dendrogram(at->get_leftson(), Pen, limits, labeler); // re-use limits for left branch
2975 
2976  n0.sety(limits.y_branch);
2977  s0.sety(limits.y_branch);
2978 
2979  Pen.setx(s0.xpos());
2980  Position subtree_border(Pen); subtree_border.movey(- .5*scaled_branch_distance); // attach point centered between both subtrees
2981  Pen.movex(at->rightlen);
2982  Position n1(Pen);
2983  {
2984  DendroSubtreeLimits right_lim;
2985  show_dendrogram(at->get_rightson(), Pen, right_lim, labeler);
2986  n1.sety(right_lim.y_branch);
2987  limits.combine(right_lim);
2988  }
2989 
2990  Position s1(s0.xpos(), n1.ypos());
2991  --recursion_depth;
2992 
2993  // calculate attach-point:
2994  Position attach = centroid(s0, s1);
2995  {
2996  Vector shift_by_size(ZeroVector);
2997  Vector shift_by_len(ZeroVector);
2998  int nonZero = 0;
2999 
3000  if (attach_size != 0.0) {
3001  ++nonZero;
3002  shift_by_size = -attach_size * (subtree_border-attach);
3003  }
3004 
3005  if (attach_len != 0.0) {
3006  Position barycenter;
3007  if (nearlyZero(at->leftlen)) {
3008  if (nearlyZero(at->rightlen)) {
3009  barycenter = attach;
3010  }
3011  else {
3012  barycenter = s1; // at(!) right branch
3013  }
3014  }
3015  else {
3016  if (nearlyZero(at->rightlen)) {
3017  barycenter = s0; // at(!) left branch
3018  }
3019  else {
3020  double sum = at->leftlen + at->rightlen;
3021  double fraction;
3022  Vector big2small;
3023  if (at->leftlen < at->rightlen) {
3024  fraction = at->leftlen/sum;
3025  big2small = s0-s1;
3026  }
3027  else {
3028  fraction = at->rightlen/sum;
3029  big2small = s1-s0;
3030  }
3031  barycenter = attach-big2small/2+big2small*fraction;
3032  }
3033  }
3034 
3035  Vector shift_to_barycenter = barycenter-attach;
3036  shift_by_len = shift_to_barycenter*attach_len;
3037 
3038  ++nonZero;
3039  }
3040 
3041  if (nonZero>1) {
3042  double sum = fabs(attach_size) + fabs(attach_len);
3043  double f_size = fabs(attach_size)/sum;
3044  double f_len = fabs(attach_len)/sum;
3045 
3046  attach += f_size * shift_by_size;
3047  attach += f_len * shift_by_len;
3048  }
3049  else {
3050  attach += shift_by_size;
3051  attach += shift_by_len;
3052  }
3053  }
3054 
3055  if (is_group && show_brackets) {
3056  double unscale = disp_device->get_unscale();
3057  const AW_font_limits& charLimits = disp_device->get_font_limits(group_gc, 'A');
3058  double half_text_ascent = charLimits.ascent * unscale * 0.5;
3059 
3060  double x1 = limits.x_right + scaled_branch_distance*0.1;
3061  double x2 = x1 + scaled_branch_distance * 0.3;
3062  double y1 = limits.y_top - half_text_ascent * 0.5;
3063  double y2 = limits.y_bot + half_text_ascent * 0.5;
3064 
3065  Rectangle bracket(Position(x1, y1), Position(x2, y2));
3066 
3068 
3069  disp_device->line(group_gc, bracket.upper_edge(), group_bracket_filter);
3070  disp_device->line(group_gc, bracket.lower_edge(), group_bracket_filter);
3071  disp_device->line(group_gc, bracket.right_edge(), group_bracket_filter);
3072 
3073  limits.x_right = x2;
3074 
3075  if (disp_device->get_filter() & group_text_filter) {
3076  LineVector worldBracket = disp_device->transform(bracket.right_edge());
3077  LineVector clippedWorldBracket;
3078 
3079  bool visible = disp_device->clip(worldBracket, clippedWorldBracket);
3080  if (visible) {
3081  const GroupInfo& info = get_group_info(at, GI_SEPARATED_PARENTIZED, false, labeler);
3082 
3083  if (info.name || info.count) {
3084  LineVector clippedBracket = disp_device->rtransform(clippedWorldBracket);
3085 
3086  if (info.name) {
3087  Position namePos = clippedBracket.centroid()+Vector(half_text_ascent, -0.2*half_text_ascent); // originally y-offset was half_text_ascent (w/o counter shown)
3088  SizedCstr infoName(info.name, info.name_len);
3089  disp_device->text(group_gc, infoName, namePos, 0.0, group_text_filter);
3090  if (info.name_len>=info.count_len) {
3091  double textsize = disp_device->get_string_size(group_gc, infoName) * unscale;
3092  limits.x_right = namePos.xpos() + textsize;
3093  }
3094  }
3095 
3096  if (info.count) {
3097  Position countPos = clippedBracket.centroid()+Vector(half_text_ascent, 2.2*half_text_ascent);
3098  SizedCstr infoCount(info.count, info.count_len);
3099  disp_device->text(group_gc, infoCount, countPos, 0.0, group_text_filter);
3100  if (info.count_len>info.name_len) {
3101  double textsize = disp_device->get_string_size(group_gc, infoCount) * unscale;
3102  limits.x_right = countPos.xpos() + textsize;
3103  }
3104  }
3105  }
3106  }
3107  }
3108  }
3109 
3110  for (int right = 0; right<2; ++right) {
3111  const Position& n = right ? n1 : n0; // node-position
3112  const Position& s = right ? s1 : s0; // upper/lower corner of rectangular branch
3113 
3114  AP_tree *son;
3115  GBT_LEN len;
3116  if (right) {
3117  son = at->get_rightson();
3118  len = at->rightlen;
3119  }
3120  else {
3121  son = at->get_leftson();
3122  len = at->leftlen;
3123  }
3124 
3125  AW_click_cd cds(disp_device, (AW_CL)son, CL_NODE);
3126 
3128  unsigned int gc = son->gr.gc;
3129 
3130  if (branch_style == BS_RECTANGULAR) {
3131  draw_branch_line(gc, s, n, line_filter);
3132  draw_branch_line(gc, attach, s, vert_line_filter);
3133  }
3134  else {
3135  td_assert(branch_style == BS_DIAGONAL);
3136  draw_branch_line(gc, attach, n, line_filter);
3137  }
3138 
3139  if (bconf.shall_show_remark_for(son)) {
3140  if (son->is_son_of_root()) {
3141  if (right) { // only draw once
3142  AW_click_cd cdr(disp_device, 0, CL_ROOTNODE);
3143  len += at->leftlen; // sum up length of both sons of root
3144  bconf.display_node_remark(disp_device, son, attach, len, scaled_branch_distance, D_EAST);
3145  }
3146  }
3147  else {
3148  bconf.display_node_remark(disp_device, son, n, len, scaled_branch_distance, right ? D_SOUTH_WEST : D_NORTH_WEST); // leftson is_upper_son
3149  }
3150  }
3151  }
3152  if (is_group) {
3153  diamond(group_gc, attach, NT_DIAMOND_RADIUS);
3154  if (is_selected) selGroup = PaintedNode(attach, at);
3155  }
3156  limits.y_branch = attach.ypos();
3157  }
3158 }
3159 
3160 struct Subinfo { // subtree info (used to implement branch draw precedence)
3162  double pc; // percent of space (depends on # of species in subtree)
3164  double len;
3165 };
3166 
3167 void AWT_graphic_tree::show_radial_tree(AP_tree *at, const AW::Position& base, const AW::Position& tip, const AW::Angle& orientation, const double tree_spread, const NDS_Labeler& labeler) {
3168  static int recursion_depth = 0;
3169 
3170  AW_click_cd cd(disp_device, (AW_CL)at, CL_NODE);
3172  draw_branch_line(at->gr.gc, base, tip, line_filter);
3173 
3174  if (at->is_leaf()) { // draw leaf node
3175  if (at->gb_node && GB_read_flag(at->gb_node)) { // draw mark box
3176  filled_box(at->gr.gc, tip, NT_BOX_WIDTH);
3177  }
3178 
3179  if (at->name && (disp_device->get_filter() & leaf_text_filter)) {
3180  if (at->hasName(species_name)) selSpec = PaintedNode(tip, at);
3181 
3182  AW_pos alignment;
3183  Position textpos = calc_text_coordinates_near_tip(disp_device, at->gr.gc, tip, orientation, alignment);
3184 
3185  const char *data = labeler.speciesLabel(this->gb_main, at->gb_node, at, tree_static->get_tree_name());
3186  disp_device->text(at->gr.gc, data,
3187  textpos,
3188  alignment,
3189  leaf_text_filter);
3190  }
3191  }
3192  else if (recursion_depth>=MAX_TREEDISP_RECURSION_DEPTH) { // limit recursion depth
3193  const char *data = TREEDISP_TRUNCATION_MESSAGE;
3194  AW_pos alignment;
3195  Position textpos = calc_text_coordinates_near_tip(disp_device, at->gr.gc, tip, orientation, alignment);
3196 
3197  disp_device->text(AWT_GC_ONLY_ZOMBIES,
3198  data,
3199  textpos,
3200  alignment,
3201  leaf_text_filter);
3202  }
3203  else if (at->is_folded_group()) { // draw folded group
3204  bool is_selected = at->name && selected_group.at_node(at); // @@@ superfluous+wrong test for group (at->name)
3205  const int group_gc = is_selected ? int(AWT_GC_CURSOR) : at->gr.gc;
3206 
3207  if (is_selected) selGroup = PaintedNode(tip, at);
3208 
3209  Position corner[3];
3210  corner[0] = tip;
3211  {
3212  Angle left(orientation.radian() + 0.25*tree_spread + at->gr.left_angle);
3213  corner[1] = tip + left.normal()*at->gr.min_tree_depth;
3214  }
3215  {
3216  Angle right(orientation.radian() - 0.25*tree_spread + at->gr.right_angle);
3217  corner[2] = tip + right.normal()*at->gr.max_tree_depth;
3218  }
3219 
3220  disp_device->set_grey_level(at->gr.gc, group_greylevel);
3221  disp_device->polygon(at->gr.gc, AW::FillStyle::SHADED_WITH_BORDER, 3, corner, line_filter);
3222  if (group_gc != int(at->gr.gc)) {
3223  disp_device->polygon(group_gc, AW::FillStyle::EMPTY, 3, corner, line_filter);
3224  }
3225 
3226  if (disp_device->get_filter() & group_text_filter) {
3227  const GroupInfo& info = get_group_info(at, group_info_pos == GIP_SEPARATED ? GI_SEPARATED : GI_COMBINED, group_info_pos == GIP_OVERLAYED, labeler);
3228  if (info.name) {
3229  Angle toText = orientation;
3230  toText.rotate90deg();
3231 
3232  AW_pos alignment;
3233  Position textpos = calc_text_coordinates_near_tip(disp_device, group_gc, corner[1], toText, alignment);
3234 
3235  disp_device->text(group_gc, SizedCstr(info.name, info.name_len), textpos, alignment, group_text_filter);
3236  }
3237  if (info.count) {
3238  Vector v01 = corner[1]-corner[0];
3239  Vector v02 = corner[2]-corner[0];
3240 
3241  Position incircleCenter = corner[0] + (v01*v02.length() + v02*v01.length()) / (v01.length()+v02.length()+Distance(v01.endpoint(), v02.endpoint()));
3242 
3243  disp_device->text(group_gc, SizedCstr(info.count, info.count_len), incircleCenter, 0.5, group_text_filter);
3244  }
3245  }
3246  }
3247  else { // draw subtrees
3248  bool is_selected = at->name && selected_group.at_node(at); // @@@ wrong test for group (at->name)
3249  if (is_selected) selGroup = PaintedNode(tip, at);
3250 
3251  Subinfo sub[2];
3252  sub[0].at = at->get_leftson();
3253  sub[1].at = at->get_rightson();
3254 
3255  sub[0].pc = sub[0].at->gr.view_sum / (double)at->gr.view_sum;
3256  sub[1].pc = 1.0-sub[0].pc;
3257 
3258  sub[0].orientation = Angle(orientation.radian() + sub[1].pc*0.5*tree_spread + at->gr.left_angle);
3259  sub[1].orientation = Angle(orientation.radian() - sub[0].pc*0.5*tree_spread + at->gr.right_angle);
3260 
3261  sub[0].len = at->leftlen;
3262  sub[1].len = at->rightlen;
3263 
3264  if (sub[0].at->gr.gc < sub[1].at->gr.gc) {
3265  std::swap(sub[0], sub[1]); // swap branch draw order (branches with lower gc are drawn on top of branches with higher gc)
3266  }
3267 
3268  ++recursion_depth;
3269  for (int s = 0; s<2; ++s) {
3270  show_radial_tree(sub[s].at,
3271  tip,
3272  tip + sub[s].len * sub[s].orientation.normal(),
3273  sub[s].orientation,
3274  sub[s].at->is_leaf() ? 1.0 : tree_spread * sub[s].pc * sub[s].at->gr.spread,
3275  labeler);
3276  }
3277  --recursion_depth;
3278 
3279  for (int s = 0; s<2; ++s) {
3280  AP_tree *son = sub[s].at;
3281  if (bconf.shall_show_remark_for(son)) {
3282  AW_click_cd sub_cd(disp_device, (AW_CL)son, CL_NODE);
3283 
3284  td_assert(!son->is_leaf());
3285  if (son->is_son_of_root()) {
3286  if (s) { // only at one son
3287  AW_click_cd cdr(disp_device, 0, CL_ROOTNODE);
3288  AW_pos alignment;
3289  Position text_pos = calc_text_coordinates_aside_line(disp_device, AWT_GC_BRANCH_REMARK, tip, sub[s].orientation, true, alignment, 1.0);
3290 
3291  bconf.display_remark(disp_device, son->get_remark(), tip, sub[0].len+sub[1].len, 0, text_pos, alignment);
3292  }
3293  }
3294  else {
3295  Position sub_branch_center = tip + (sub[s].len*.5) * sub[s].orientation.normal();
3296 
3297  AW_pos alignment;
3298  Position text_pos = calc_text_coordinates_aside_line(disp_device, AWT_GC_BRANCH_REMARK, sub_branch_center, sub[s].orientation, true, alignment, 0.5);
3299  bconf.display_remark(disp_device, son->get_remark(), sub_branch_center, sub[s].len, 0, text_pos, alignment);
3300  }
3301  }
3302  }
3303 
3304  if (at->is_clade()) {
3305  const int group_gc = selected_group.at_node(at) ? int(AWT_GC_CURSOR) : at->gr.gc;
3306  diamond(group_gc, tip, NT_DIAMOND_RADIUS);
3307  }
3308  }
3309 }
3310 
3311 const char *AWT_graphic_tree::ruler_awar(const char *name) {
3312  // return "ruler/TREETYPE/name" (path to entry below tree)
3313  const char *tree_awar = NULp;
3314  switch (tree_style) {
3315  case AP_TREE_NORMAL:
3316  tree_awar = "LIST";
3317  break;
3318  case AP_TREE_RADIAL:
3319  tree_awar = "RADIAL";
3320  break;
3321  case AP_TREE_IRS:
3322  tree_awar = "IRS";
3323  break;
3324  case AP_LIST_SIMPLE:
3325  case AP_LIST_NDS:
3326  // rulers not allowed in these display modes
3327  td_assert(0); // should not be called
3328  break;
3329  }
3330 
3331  static char awar_name[256];
3332  sprintf(awar_name, "ruler/%s/%s", tree_awar, name);
3333  return awar_name;
3334 }
3335 
3337  GBDATA *gb_tree = tree_static->get_gb_tree();
3338  if (!gb_tree) return; // no tree -> no ruler
3339 
3340  bool mode_has_ruler = ruler_awar(NULp);
3341  if (mode_has_ruler) {
3342  GB_transaction ta(gb_tree);
3343 
3344  float ruler_size = *GBT_readOrCreate_float(gb_tree, RULER_SIZE, DEFAULT_RULER_LENGTH);
3345  float ruler_y = 0.0;
3346 
3347  const char *awar = ruler_awar("ruler_y");
3348  if (!GB_search(gb_tree, awar, GB_FIND)) {
3349  if (device->type() == AW_DEVICE_SIZE) {
3350  AW_world world;
3351  DOWNCAST(AW_device_size*, device)->get_size_information(&world);
3352  ruler_y = world.b * 1.3;
3353  }
3354  }
3355 
3356  double half_ruler_width = ruler_size*0.5;
3357 
3358  float ruler_add_y = 0.0;
3359  float ruler_add_x = 0.0;
3360  switch (tree_style) {
3361  case AP_TREE_IRS:
3362  // scale is different for IRS tree -> adjust:
3363  half_ruler_width *= irs_tree_ruler_scale_factor;
3364  ruler_y = 0;
3365  ruler_add_y = this->list_tree_ruler_y;
3366  ruler_add_x = -half_ruler_width;
3367  break;
3368  case AP_TREE_NORMAL:
3369  ruler_y = 0;
3370  ruler_add_y = this->list_tree_ruler_y;
3371  ruler_add_x = half_ruler_width;
3372  break;
3373  default:
3374  break;
3375  }
3376  ruler_y = ruler_add_y + *GBT_readOrCreate_float(gb_tree, awar, ruler_y);
3377 
3378  float ruler_x = 0.0;
3379  ruler_x = ruler_add_x + *GBT_readOrCreate_float(gb_tree, ruler_awar("ruler_x"), ruler_x);
3380 
3381  td_assert(!is_nan_or_inf(ruler_x));
3382 
3383  float ruler_text_x = 0.0;
3384  ruler_text_x = *GBT_readOrCreate_float(gb_tree, ruler_awar("text_x"), ruler_text_x);
3385 
3386  td_assert(!is_nan_or_inf(ruler_text_x));
3387 
3388  float ruler_text_y = 0.0;
3389  ruler_text_y = *GBT_readOrCreate_float(gb_tree, ruler_awar("text_y"), ruler_text_y);
3390 
3391  td_assert(!is_nan_or_inf(ruler_text_y));
3392 
3393  int ruler_width = *GBT_readOrCreate_int(gb_tree, RULER_LINEWIDTH, DEFAULT_RULER_LINEWIDTH);
3394 
3395  device->set_line_attributes(gc, ruler_width+baselinewidth, AW_SOLID);
3396 
3397  AW_click_cd cd(device, 0, CL_RULER);
3398  device->line(gc,
3399  ruler_x - half_ruler_width, ruler_y,
3400  ruler_x + half_ruler_width, ruler_y,
3401  this->ruler_filter|AW_SIZE);
3402 
3403  char ruler_text[20];
3404  sprintf(ruler_text, "%4.2f", ruler_size);
3405  device->text(gc, ruler_text,
3406  ruler_x + ruler_text_x,
3407  ruler_y + ruler_text_y,
3408  0.5,
3409  this->ruler_filter|AW_SIZE_UNSCALED);
3410  }
3411 }
3412 
3413 struct Column : virtual Noncopyable {
3414  char *text;
3415  size_t len;
3416  double print_width;
3417  bool is_numeric; // also true for empty text
3418 
3419  Column() : text(NULp) {}
3420  ~Column() { free(text); }
3421 
3422  void init(const char *text_, AW_device& device, int gc) {
3423  len = strlen(text_);
3424  text = ARB_strduplen(text_, len);
3425  print_width = device.get_string_size(gc, SizedCstr(text, len));
3426  is_numeric = (strspn(text, "0123456789.") == len);
3427  }
3428 };
3429 
3430 class ListDisplayRow : virtual Noncopyable {
3431  GBDATA *gb_species;
3432  AW_pos y_position;
3433  int gc;
3434  size_t part_count; // NDS columns
3435  Column *column;
3436 
3437 public:
3438  ListDisplayRow(GBDATA *gb_main, GBDATA *gb_species_, AW_pos y_position_, int gc_, AW_device& device, bool use_nds, const char *tree_name, const NDS_Labeler& labeler)
3439  : gb_species(gb_species_),
3440  y_position(y_position_),
3441  gc(gc_)
3442  {
3443  const char *nds = use_nds
3444  ? labeler.speciesLabel(gb_main, gb_species, NULp, tree_name)
3445  : GBT_get_name_or_description(gb_species);
3446 
3447  ConstStrArray parts;
3448  GBT_split_string(parts, nds, "\t", SPLIT_KEEPEMPTY);
3449  part_count = parts.size();
3450 
3451  column = new Column[part_count];
3452  for (size_t i = 0; i<part_count; ++i) {
3453  column[i].init(parts[i], device, gc);
3454  }
3455  }
3456 
3457  ~ListDisplayRow() { delete [] column; }
3458 
3459  size_t get_part_count() const { return part_count; }
3460  const Column& get_column(size_t p) const {
3461  td_assert(p<part_count);
3462  return column[p];
3463  }
3464  double get_print_width(size_t p) const { return get_column(p).print_width; }
3465  const char *get_text(size_t p, size_t& len) const {
3466  const Column& col = get_column(p);
3467  len = col.len;
3468  return col.text;
3469  }
3470  int get_gc() const { return gc; }
3471  double get_ypos() const { return y_position; }
3472  GBDATA *get_species() const { return gb_species; }
3473 };
3474 
3475 void AWT_graphic_tree::show_nds_list(GBDATA *, bool use_nds, const NDS_Labeler& labeler) {
3476  AW_pos y_position = scaled_branch_distance;
3477  AW_pos x_position = NT_SELECTED_WIDTH * disp_device->get_unscale();
3478 
3479  disp_device->text(nds_only_marked ? AWT_GC_ALL_MARKED : AWT_GC_CURSOR,
3480  GBS_global_string("%s of %s species", use_nds ? "NDS List" : "Simple list", nds_only_marked ? "marked" : "all"),
3481  (AW_pos) x_position, (AW_pos) 0,
3482  (AW_pos) 0, other_text_filter);
3483 
3484  double max_x = 0;
3485  double text_y_offset = scaled_font.ascent*.5;
3486 
3487  GBDATA *selected_species;
3488  {
3489  GBDATA *selected_name = GB_find_string(GBT_get_species_data(gb_main), "name", this->species_name, GB_IGNORE_CASE, SEARCH_GRANDCHILD);
3490  selected_species = selected_name ? GB_get_father(selected_name) : NULp;
3491  }
3492 
3493  const char *tree_name = tree_static ? tree_static->get_tree_name() : NULp;
3494 
3495  AW_pos y1, y2;
3496  {
3497  const AW_screen_area& clip_rect = disp_device->get_cliprect();
3498 
3499  AW_pos Y1 = clip_rect.t;
3500  AW_pos Y2 = clip_rect.b;
3501 
3502  AW_pos x;
3503  disp_device->rtransform(0, Y1, x, y1);
3504  disp_device->rtransform(0, Y2, x, y2);
3505  }
3506 
3507  y1 -= 2*scaled_branch_distance; // add two lines for safety
3508  y2 += 2*scaled_branch_distance;
3509 
3510  size_t displayed_rows = (y2-y1)/scaled_branch_distance+1;
3511  ListDisplayRow **row = new ListDisplayRow*[displayed_rows];
3512 
3513  size_t species_count = 0;
3514  size_t max_parts = 0;
3515 
3516  GBDATA *gb_species = nds_only_marked ? GBT_first_marked_species(gb_main) : GBT_first_species(gb_main);
3517  if (gb_species) {
3518  int skip_over = (y1-y_position)/scaled_branch_distance-2;
3519  if (skip_over>0) {
3520  gb_species = nds_only_marked
3521  ? GB_following_marked(gb_species, "species", skip_over-1)
3522  : GB_followingEntry(gb_species, skip_over-1);
3523  y_position += skip_over*scaled_branch_distance;
3524  }
3525  }
3526 
3527  const AP_TreeShader *shader = AP_tree::get_tree_shader();
3528  const_cast<AP_TreeShader*>(shader)->update_settings();
3529 
3530  for (; gb_species; gb_species = nds_only_marked ? GBT_next_marked_species(gb_species) : GBT_next_species(gb_species)) {
3531  y_position += scaled_branch_distance;
3532 
3533  if (gb_species == selected_species) selSpec = PaintedNode(Position(0, y_position), NULp);
3534 
3535  if (y_position>y1) {
3536  if (y_position>y2) break; // no need to examine rest of species
3537 
3538  bool is_marked = nds_only_marked || GB_read_flag(gb_species);
3539  if (is_marked) {
3540  disp_device->set_line_attributes(AWT_GC_ALL_MARKED, baselinewidth, AW_SOLID);
3542  }
3543 
3544  bool colorize_marked = is_marked && !nds_only_marked; // do not use mark-color if only showing marked
3545 
3546  int gc = shader->calc_leaf_GC(gb_species, colorize_marked);
3547  if (gc == AWT_GC_NONE_MARKED && shader->does_shade()) { // may show shaded color
3548  gc = shader->to_GC(shader->calc_shaded_leaf_GC(gb_species));
3549  }
3550 
3551  ListDisplayRow *curr = new ListDisplayRow(gb_main, gb_species, y_position+text_y_offset, gc, *disp_device, use_nds, tree_name, labeler);
3552  max_parts = std::max(max_parts, curr->get_part_count());
3553  row[species_count++] = curr;
3554  }
3555  }
3556 
3557  td_assert(species_count <= displayed_rows);
3558 
3559  // calculate column offsets and detect column alignment
3560  double *max_part_width = new double[max_parts];
3561  bool *align_right = new bool[max_parts];
3562 
3563  for (size_t p = 0; p<max_parts; ++p) {
3564  max_part_width[p] = 0;
3565  align_right[p] = true;
3566  }
3567 
3568  for (size_t s = 0; s<species_count; ++s) {
3569  size_t parts = row[s]->get_part_count();
3570  for (size_t p = 0; p<parts; ++p) {
3571  const Column& col = row[s]->get_column(p);
3572  max_part_width[p] = std::max(max_part_width[p], col.print_width);
3573  align_right[p] = align_right[p] && col.is_numeric;
3574  }
3575  }
3576 
3577  double column_space = scaled_branch_distance;
3578 
3579  double *part_x_pos = new double[max_parts];
3580  for (size_t p = 0; p<max_parts; ++p) {
3581  part_x_pos[p] = x_position;
3582  x_position += max_part_width[p]+column_space;
3583  }
3584  max_x = x_position;
3585 
3586  // draw
3587 
3588  for (size_t s = 0; s<species_count; ++s) {
3589  const ListDisplayRow& Row = *row[s];
3590 
3591  size_t parts = Row.get_part_count();
3592  int gc = Row.get_gc();
3593  AW_pos y = Row.get_ypos();
3594 
3595  GBDATA *gb_sp = Row.get_species();
3596  AW_click_cd cd(disp_device, (AW_CL)gb_sp, CL_SPECIES);
3597 
3598  for (size_t p = 0; p<parts; ++p) {
3599  const Column& col = Row.get_column(p);
3600 
3601  AW_pos x = part_x_pos[p];
3602  if (align_right[p]) x += max_part_width[p] - col.print_width;
3603 
3604  disp_device->text(gc, SizedCstr(col.text, col.len), x, y, 0.0, leaf_text_filter);
3605  }
3606  }
3607 
3608  delete [] part_x_pos;
3609  delete [] align_right;
3610  delete [] max_part_width;
3611 
3612  for (size_t s = 0; s<species_count; ++s) delete row[s];
3613  delete [] row;
3614 
3615  disp_device->invisible(Origin); // @@@ remove when size-dev works
3616  disp_device->invisible(Position(max_x, y_position+scaled_branch_distance)); // @@@ remove when size-dev works
3617 }
3618 
3620  scaled_branch_distance = aw_root->awar(AWAR_DTREE_VERICAL_DIST)->read_float(); // not final value!
3621  group_greylevel = aw_root->awar(AWAR_DTREE_GREY_LEVEL)->read_int() * 0.01;
3622  baselinewidth = aw_root->awar(AWAR_DTREE_BASELINEWIDTH)->read_int();
3623  group_count_mode = GroupCountMode(aw_root->awar(AWAR_DTREE_GROUPCOUNTMODE)->read_int());
3624  group_info_pos = GroupInfoPosition(aw_root->awar(AWAR_DTREE_GROUPINFOPOS)->read_int());
3625  show_brackets = aw_root->awar(AWAR_DTREE_SHOW_BRACKETS)->read_int();
3628  group_style = GroupStyle(aw_root->awar(AWAR_DTREE_GROUP_STYLE)->read_int());
3629  group_orientation = GroupOrientation(aw_root->awar(AWAR_DTREE_GROUP_ORIENT)->read_int());
3630  branch_style = BranchStyle(aw_root->awar(AWAR_DTREE_BRANCH_STYLE)->read_int());
3631  attach_size = aw_root->awar(AWAR_DTREE_ATTACH_SIZE)->read_float();
3632  attach_len = aw_root->awar(AWAR_DTREE_ATTACH_LEN)->read_float();
3633  attach_group = (aw_root->awar(AWAR_DTREE_ATTACH_GROUP)->read_float()+1)/2; // projection: [-1 .. 1] -> [0 .. 1]
3634 
3635  bconf.show_boots = aw_root->awar(AWAR_DTREE_BOOTSTRAP_SHOW)->read_int();
3636  bconf.bootstrap_min = aw_root->awar(AWAR_DTREE_BOOTSTRAP_MIN)->read_int();
3637  bconf.bootstrap_max = aw_root->awar(AWAR_DTREE_BOOTSTRAP_MAX)->read_int();
3639  bconf.zoom_factor = aw_root->awar(AWAR_DTREE_CIRCLE_ZOOM)->read_float();
3640  bconf.max_radius = aw_root->awar(AWAR_DTREE_CIRCLE_LIMIT)->read_float();
3641  bconf.show_circle = aw_root->awar(AWAR_DTREE_CIRCLE_SHOW)->read_int();
3642  bconf.fill_level = aw_root->awar(AWAR_DTREE_CIRCLE_FILL)->read_int() * 0.01;
3643  bconf.ellipsoid = aw_root->awar(AWAR_DTREE_CIRCLE_ELLIPSE)->read_int();
3644 
3645  freeset(species_name, aw_root->awar(AWAR_SPECIES_NAME)->read_string());
3646 
3647  if (display_markers) {
3648  groupThreshold.marked = aw_root->awar(AWAR_DTREE_GROUP_MARKED_THRESHOLD)->read_float() * 0.01;
3649  groupThreshold.partiallyMarked = aw_root->awar(AWAR_DTREE_GROUP_PARTIALLY_MARKED_THRESHOLD)->read_float() * 0.01;
3650  MarkerXPos::marker_width = aw_root->awar(AWAR_DTREE_MARKER_WIDTH)->read_int();
3651  marker_greylevel = aw_root->awar(AWAR_DTREE_PARTIAL_GREYLEVEL)->read_int() * 0.01;
3652  }
3653 }
3654 
3657 
3658  if (ntw) {
3659  bool zoom_fit_text = false;
3660  int left_padding = 0;
3661  int right_padding = 0;
3662 
3663  switch (tree_style) {
3664  case AP_TREE_RADIAL:
3665  zoom_fit_text = aw_root->awar(AWAR_DTREE_RADIAL_ZOOM_TEXT)->read_int();
3666  left_padding = aw_root->awar(AWAR_DTREE_RADIAL_XPAD)->read_int();
3667  right_padding = left_padding;
3668  break;
3669 
3670  case AP_TREE_NORMAL:
3671  case AP_TREE_IRS:
3672  zoom_fit_text = aw_root->awar(AWAR_DTREE_DENDRO_ZOOM_TEXT)->read_int();
3673  left_padding = STANDARD_PADDING;
3674  right_padding = aw_root->awar(AWAR_DTREE_DENDRO_XPAD)->read_int();
3675  break;
3676 
3677  default :
3678  break;
3679  }
3680 
3681  exports.set_default_padding(STANDARD_PADDING, STANDARD_PADDING, left_padding, right_padding);
3682 
3683  ntw->set_consider_text_for_zoom_reset(zoom_fit_text);
3684  }
3685 }
3686 
3690 
3691  disp_device = device;
3692  disp_device->reset_style();
3693 
3694  {
3695  const AW_font_limits& charLimits = disp_device->get_font_limits(AWT_GC_ALL_MARKED, 0);
3696  scaled_font.init(charLimits, device->get_unscale());
3697  }
3698  {
3699  const AW_font_limits& remarkLimits = disp_device->get_font_limits(AWT_GC_BRANCH_REMARK, 0);
3700  AWT_scaled_font_limits scaledRemarkLimits;
3701  scaledRemarkLimits.init(remarkLimits, device->get_unscale());
3702  bconf.scaled_remark_ascend = scaledRemarkLimits.ascent;
3703  }
3704  scaled_branch_distance *= scaled_font.height;
3705 
3706  selSpec = PaintedNode(); // not painted yet
3707  selGroup = PaintedNode(); // not painted yet
3708 
3709  if (!displayed_root && is_tree_style(tree_style)) { // there is no tree => show message instead
3710  static const char *no_tree_text[] = {
3711  "No tree (selected)",
3712  "",
3713  "In the top area you may click on",
3714  "- the listview-button to see a plain list of species",
3715  "- the tree-selection-button to select a tree",
3716  NULp
3717  };
3718 
3719  Position p0(0, -3*scaled_branch_distance);
3720  Position cursor = p0;
3721  for (int i = 0; no_tree_text[i]; ++i) {
3722  cursor.movey(scaled_branch_distance);
3723  device->text(AWT_GC_CURSOR, no_tree_text[i], cursor);
3724  }
3725 
3726  // add some space between vertical line and text:
3727  p0.movex(-scaled_branch_distance);
3728  cursor.movex(-scaled_branch_distance);
3729 
3730  // add some space between horizontal line and text:
3731  p0.movey(-scaled_branch_distance);
3732  cursor.movey(scaled_branch_distance);
3733 
3734  Position horizontal = p0 + 2*Vector(p0, cursor).rotate270deg();
3735  device->line(AWT_GC_CURSOR, p0, cursor);
3736  device->line(AWT_GC_CURSOR, p0, horizontal);
3737 
3738  selSpec = PaintedNode(cursor, NULp);
3739  }
3740  else {
3741  double range_display_size = scaled_branch_distance;
3742  bool allow_range_display = true;
3743  Position range_origin = Origin;
3744 
3745  NDS_Labeler labeler(tree_style == AP_LIST_NDS ? NDS_OUTPUT_TAB_SEPARATED : NDS_OUTPUT_LEAFTEXT);
3746 
3747  switch (tree_style) {
3748  case AP_TREE_NORMAL: {
3749  DendroSubtreeLimits limits;
3750  Position pen(0, 0.05);
3751 
3752  show_dendrogram(displayed_root, pen, limits, labeler);
3753 
3754  int rulerOffset = 2;
3755  if (display_markers) {
3756  drawMarkerNames(pen);
3757  ++rulerOffset;
3758  }
3759  list_tree_ruler_y = pen.ypos() + double(rulerOffset) * scaled_branch_distance;
3760  break;
3761  }
3762  case AP_TREE_RADIAL: {
3763  LocallyModify<bool> onlyUseCircles(bconf.ellipsoid, false); // radial tree never shows bootstrap circles as ellipsoids
3764 
3765  {
3766  AW_click_cd cdr(device, 0, CL_ROOTNODE);
3767  empty_box(displayed_root->gr.gc, Origin, NT_ROOT_WIDTH);
3768  }
3769  show_radial_tree(displayed_root, Origin, Origin, Eastwards, 2*M_PI, labeler);
3770 
3771  range_display_size = 3.0/AW_PLANAR_COLORS;
3772  range_origin += Vector(-range_display_size*AW_PLANAR_COLORS/2, -range_display_size*AW_PLANAR_COLORS/2);
3773  break;
3774  }
3775  case AP_TREE_IRS:
3776  show_irs_tree(displayed_root, scaled_branch_distance, labeler);
3777  break;
3778 
3779  case AP_LIST_NDS: // this is the list all/marked species mode (no tree)
3780  show_nds_list(gb_main, true, labeler);
3781  break;
3782 
3783  case AP_LIST_SIMPLE: // simple list of names (used at startup only)
3784  // don't see why we need to draw ANY tree at startup -> disabled
3785  // show_nds_list(gb_main, false);
3786  allow_range_display = false;
3787  break;
3788  }
3789  if (selSpec.was_displayed()) {
3790  AP_tree *selNode = selSpec.get_node();
3791  AW_click_cd cd(device, AW_CL(selNode ? selNode->gb_node : NULp), CL_SPECIES);
3792  empty_box(AWT_GC_CURSOR, selSpec.get_pos(), NT_SELECTED_WIDTH);
3793  }
3794  if (is_tree_style(tree_style)) show_ruler(disp_device, AWT_GC_CURSOR);
3795 
3796  if (allow_range_display) {
3797  AW_displayColorRange(disp_device, AWT_GC_FIRST_RANGE_COLOR, range_origin, range_display_size, range_display_size);
3798  }
3799  }
3800 
3801  if (cmd_data && Dragged::valid_drag_device(disp_device)) {
3802  Dragged *dragging = dynamic_cast<Dragged*>(cmd_data);
3803  if (dragging) {
3804  // if tree is redisplayed while dragging, redraw the drag indicator.
3805  // (happens in modes which modify the tree during drag, e.g. when scaling branches)
3806  dragging->draw_drag_indicator(disp_device, drag_gc);
3807  }
3808  }
3809 
3810  disp_device = NULp;
3811 }
3812 
3813 inline unsigned percentMarked(const AP_tree_members& gr) {
3814  double percent = double(gr.mark_sum)/gr.leaf_sum;
3815  unsigned pc = unsigned(percent*100.0+0.5);
3816 
3817  if (pc == 0) {
3818  td_assert(gr.mark_sum>0); // otherwise function should not be called
3819  pc = 1; // do not show '0%' for range ]0.0 .. 0.05[
3820  }
3821  else if (pc == 100) {
3822  if (gr.mark_sum<gr.leaf_sum) {
3823  pc = 99; // do not show '100%' for range [0.95 ... 1.0[
3824  }
3825  }
3826  return pc;
3827 }
3828 
3829 const GroupInfo& AWT_graphic_tree::get_group_info(AP_tree *at, GroupInfoMode mode, bool swap, const NDS_Labeler& labeler) const {
3830  static GroupInfo info = { NULp, NULp, 0, 0 };
3831 
3832  info.name = NULp;
3833  if (at->father) {
3834  static SmartCharPtr copy;
3835  if (!at->is_leaf() && at->is_normal_group()) {
3836  if (at->is_keeled_group()) { // keeled + named
3837  info.name = labeler.groupLabel(gb_main, at->gb_node, at, tree_static->get_tree_name()); // normal
3838  copy = strdup(info.name);
3839  info.name = labeler.groupLabel(gb_main, at->father->gb_node, at, tree_static->get_tree_name()); // keeled
3840 
3841  copy = GBS_global_string_copy("%s = %s", &*copy, info.name);
3842  info.name = &*copy;
3843  }
3844  else { // only named group
3845  info.name = labeler.groupLabel(gb_main, at->gb_node, at, tree_static->get_tree_name());
3846  }
3847  }
3848  else if (at->is_keeled_group()) {
3849  info.name = labeler.groupLabel(gb_main, at->father->gb_node, at, tree_static->get_tree_name());
3850  }
3851 #if defined(ASSERTION_USED)
3852  else {
3853  td_assert(0); // why was get_group_info called?
3854  }
3855 #endif
3856  }
3857  else {
3858  if (at->gb_node) {
3859  td_assert(0); // if this never happens -> remove case
3860  info.name = tree_static->get_tree_name();
3861  }
3862  }
3863  if (info.name && !info.name[0]) info.name = NULp;
3864  info.name_len = info.name ? strlen(info.name) : 0;
3865 
3866  static char countBuf[50];
3867  countBuf[0] = 0;
3868 
3869  GroupCountMode count_mode = group_count_mode;
3870 
3871  if (!at->gr.mark_sum) { // do not display zero marked
3872  switch (count_mode) {
3873  case GCM_NONE:
3874  case GCM_MEMBERS: break; // unchanged
3875 
3876  case GCM_PERCENT:
3877  case GCM_MARKED: count_mode = GCM_NONE; break; // completely skip
3878 
3879  case GCM_BOTH:
3880  case GCM_BOTH_PC: count_mode = GCM_MEMBERS; break; // fallback to members-only
3881  }
3882  }
3883 
3884  switch (count_mode) {
3885  case GCM_NONE: break;
3886  case GCM_MEMBERS: sprintf(countBuf, "%u", at->gr.leaf_sum); break;
3887  case GCM_MARKED: sprintf(countBuf, "%u", at->gr.mark_sum); break;
3888  case GCM_BOTH: sprintf(countBuf, "%u/%u", at->gr.mark_sum, at->gr.leaf_sum); break;
3889  case GCM_PERCENT: sprintf(countBuf, "%u%%", percentMarked(at->gr)); break;
3890  case GCM_BOTH_PC: sprintf(countBuf, "%u%%/%u", percentMarked(at->gr), at->gr.leaf_sum); break;
3891  }
3892 
3893  if (countBuf[0]) {
3894  info.count = countBuf;
3895  info.count_len = strlen(info.count);
3896 
3897  bool parentize = mode != GI_SEPARATED;
3898  if (parentize) {
3899  memmove(countBuf+1, countBuf, info.count_len);
3900  countBuf[0] = '(';
3901  strcpy(countBuf+info.count_len+1, ")");
3902  info.count_len += 2;
3903  }
3904  }
3905  else {
3906  info.count = NULp;
3907  info.count_len = 0;
3908  }
3909 
3910  if (mode == GI_COMBINED) {
3911  if (info.name) {
3912  if (info.count) {
3913  info.name = GBS_global_string("%s %s", info.name, info.count);
3914  info.name_len += info.count_len+1;
3915 
3916  info.count = NULp;
3917  info.count_len = 0;
3918  }
3919  }
3920  else if (info.count) {
3921  swap = !swap;
3922  }
3923  }
3924 
3925  if (swap) {
3926  std::swap(info.name, info.count);
3927  std::swap(info.name_len, info.count_len);
3928  }
3929 
3930  return info;
3931 }
3932 
3933 void AWT_graphic_tree::install_tree_changed_callback(const GraphicTreeCallback& gtcb) {
3939  td_assert(tree_changed_cb == treeChangeIgnore_cb);
3940  tree_changed_cb = gtcb;
3941 }
3943  td_assert(!(tree_changed_cb == treeChangeIgnore_cb));
3944  tree_changed_cb = treeChangeIgnore_cb;
3945 }
3946 
3948  // Does work normally done by [save_to_DB + update_structure],
3949  // but works only correctly if nothing but folding has changed.
3950 
3951  if (!exports.needs_save()) {
3952  // td_assert(!exports.needs_structure_update()); // if that happens -> do what????
3953 #if defined(ASSERTION_USED)
3954  bool needed_structure_update = exports.needs_structure_update();
3955 #endif
3956  if (display_markers) display_markers->flush_cache();
3957  parent_of_all_changes->recompute_and_write_folding();
3958 
3959  td_assert(needed_structure_update == exports.needs_structure_update()); // structure update gets delayed (@@@ not correct, but got no idea how to fix it correctly)
3961  notify_synchronized(NULp); // avoid reload
3962  }
3963 }
3964 
3965 const char *TREE_canvas::suffixed_title_for_index(const char *primary_title, int index) {
3973  if (index) {
3974  static char *last_suffixed_title = NULp;
3975  freeset(last_suffixed_title, GBS_global_string_copy("%s (#%i)", primary_title, index+1));
3976  return last_suffixed_title;
3977  }
3978  return primary_title;
3979  }
3980 
3982  AWT_graphic_tree *apdt = new AWT_graphic_tree(root, gb_main, map_viewer_cb);
3983  apdt->init(new AliView(gb_main), NULp, true, false); // tree w/o sequence data
3984  return apdt;
3985 }
3986 
3987 static void markerThresholdChanged_cb(AW_root *root, bool partChanged) {
3988  static bool avoid_recursion = false;
3989  if (!avoid_recursion) {
3990  LocallyModify<bool> flag(avoid_recursion, true);
3991 
3992  AW_awar *awar_marked = root->awar(AWAR_DTREE_GROUP_MARKED_THRESHOLD);
3993  AW_awar *awar_partMarked = root->awar(AWAR_DTREE_GROUP_PARTIALLY_MARKED_THRESHOLD);
3994 
3995  float marked = awar_marked->read_float();
3996  float partMarked = awar_partMarked->read_float();
3997 
3998  if (partMarked>marked) { // unwanted state
3999  if (partChanged) {
4000  awar_marked->write_float(partMarked);
4001  }
4002  else {
4003  awar_partMarked->write_float(marked);
4004  }
4005  }
4006  root->awar(AWAR_TREE_REFRESH)->touch();
4007  }
4008 }
4009 
4011  aw_root->awar_int (AWAR_DTREE_BASELINEWIDTH, 1) ->set_minmax (1, 10);
4012  aw_root->awar_float(AWAR_DTREE_VERICAL_DIST, 1.0)->set_minmax (0.01, 30);
4014  aw_root->awar_float(AWAR_DTREE_ATTACH_SIZE, -1.0)->set_minmax (-1.0, 1.0);
4015  aw_root->awar_float(AWAR_DTREE_ATTACH_LEN, 0.0)->set_minmax (-1.0, 1.0);
4016  aw_root->awar_float(AWAR_DTREE_ATTACH_GROUP, 0.0)->set_minmax (-1.0, 1.0);
4017  aw_root->awar_float(AWAR_DTREE_GROUP_DOWNSCALE, 0.33)->set_minmax(0.0, 1.0);
4018  aw_root->awar_float(AWAR_DTREE_GROUP_SCALE, 1.0)->set_minmax (0.01, 10.0);
4019 
4022  aw_root->awar_int(AWAR_DTREE_AUTO_UNFOLD, 1);
4023 
4026 
4027  aw_root->awar_int(AWAR_DTREE_SHOW_BRACKETS, 1);
4028 
4029  aw_root->awar_int (AWAR_DTREE_BOOTSTRAP_SHOW, 1);
4031  aw_root->awar_int (AWAR_DTREE_BOOTSTRAP_MIN, 0)->set_minmax(0,100);
4032  aw_root->awar_int (AWAR_DTREE_BOOTSTRAP_MAX, 99)->set_minmax(0,100);
4033  aw_root->awar_int (AWAR_DTREE_CIRCLE_SHOW, 0);
4034  aw_root->awar_int (AWAR_DTREE_CIRCLE_ELLIPSE, 1);
4035  aw_root->awar_int (AWAR_DTREE_CIRCLE_FILL, 50)->set_minmax(0, 100); // draw bootstrap circles 50% greyscaled
4036  aw_root->awar_float(AWAR_DTREE_CIRCLE_ZOOM, 1.0)->set_minmax(0.01, 30);
4037  aw_root->awar_float(AWAR_DTREE_CIRCLE_LIMIT, 2.0)->set_minmax(0.01, 30);
4038 
4041 
4042  aw_root->awar_int(AWAR_DTREE_GREY_LEVEL, 20)->set_minmax(0, 100);
4043 
4045  aw_root->awar_int(AWAR_DTREE_RADIAL_XPAD, 150)->set_minmax(-100, 2000);
4047  aw_root->awar_int(AWAR_DTREE_DENDRO_XPAD, 300)->set_minmax(-100, 2000);
4048 
4049  aw_root->awar_int (AWAR_DTREE_MARKER_WIDTH, 3) ->set_minmax(1, 20);
4050  aw_root->awar_int (AWAR_DTREE_PARTIAL_GREYLEVEL, 37) ->set_minmax(0, 100);
4051  aw_root->awar_float(AWAR_DTREE_GROUP_MARKED_THRESHOLD, 100.0)->set_minmax(0, 100);
4053 
4054  aw_root->awar_int(AWAR_TREE_REFRESH, 0, db);
4055  aw_root->awar_int(AWAR_TREE_RECOMPUTE, 0, db);
4056 }
4057 
4059  AWT_auto_refresh allowed_on(ntw);
4061  AP_tree *root = gt->get_root_node();
4062  if (root) {
4063  gt->read_tree_settings(); // update settings for group-scaling
4064  ntw->request_structure_update();
4065  }
4066  ntw->request_resize();
4067 }
4068 static void TREE_resize_cb(UNFIXED, TREE_canvas *ntw) {
4069  AWT_auto_refresh allowed_on(ntw);
4070  ntw->request_resize();
4071 }
4072 
4073 static void bootstrap_range_changed_cb(AW_root *awr, TREE_canvas *ntw, int upper_changed) {
4074  // couple limits of bootstrap range
4075  static bool in_recursion = false;
4076  if (!in_recursion) {
4077  LocallyModify<bool> avoid(in_recursion, true);
4078 
4079  AW_awar *alower = awr->awar(AWAR_DTREE_BOOTSTRAP_MIN);
4080  AW_awar *aupper = awr->awar(AWAR_DTREE_BOOTSTRAP_MAX);
4081 
4082  int rlower = alower->read_int();
4083  int rupper = aupper->read_int();
4084 
4085  if (rlower>rupper) { // need correction
4086  if (upper_changed) {
4087  alower->write_int(rupper);
4088  }
4089  else {
4090  aupper->write_int(rlower);
4091  }
4092  }
4093 
4094  AWT_auto_refresh allowed_on(ntw);
4095  ntw->request_refresh();
4096  }
4097 }
4098 
4100  // install all callbacks needed to make the tree-display update properly
4101 
4102  AW_root *awr = ntw->awr;
4103 
4104  // bind to all options available in 'Tree options'
4105  RootCallback expose_cb = makeRootCallback(AWT_expose_cb, static_cast<AWT_canvas*>(ntw));
4106  awr->awar(AWAR_DTREE_BASELINEWIDTH) ->add_callback(expose_cb);
4107  awr->awar(AWAR_DTREE_BOOTSTRAP_SHOW) ->add_callback(expose_cb);
4108  awr->awar(AWAR_DTREE_BOOTSTRAP_STYLE)->add_callback(expose_cb);
4109  awr->awar(AWAR_DTREE_CIRCLE_SHOW) ->add_callback(expose_cb);
4110  awr->awar(AWAR_DTREE_CIRCLE_FILL) ->add_callback(expose_cb);
4111  awr->awar(AWAR_DTREE_SHOW_BRACKETS) ->add_callback(expose_cb);
4112  awr->awar(AWAR_DTREE_CIRCLE_ZOOM) ->add_callback(expose_cb);
4113  awr->awar(AWAR_DTREE_CIRCLE_LIMIT) ->add_callback(expose_cb);
4114  awr->awar(AWAR_DTREE_CIRCLE_ELLIPSE) ->add_callback(expose_cb);
4115  awr->awar(AWAR_DTREE_GROUP_STYLE) ->add_callback(expose_cb);
4116  awr->awar(AWAR_DTREE_GROUP_ORIENT) ->add_callback(expose_cb);
4117  awr->awar(AWAR_DTREE_GREY_LEVEL) ->add_callback(expose_cb);
4118  awr->awar(AWAR_DTREE_GROUPCOUNTMODE) ->add_callback(expose_cb);
4119  awr->awar(AWAR_DTREE_GROUPINFOPOS) ->add_callback(expose_cb);
4120  awr->awar(AWAR_DTREE_BRANCH_STYLE) ->add_callback(expose_cb);
4121  awr->awar(AWAR_DTREE_ATTACH_SIZE) ->add_callback(expose_cb);
4122  awr->awar(AWAR_DTREE_ATTACH_LEN) ->add_callback(expose_cb);
4123  awr->awar(AWAR_DTREE_ATTACH_GROUP) ->add_callback(expose_cb);
4124 
4125  awr->awar(AWAR_DTREE_BOOTSTRAP_MIN) ->add_callback(makeRootCallback(bootstrap_range_changed_cb, ntw, 0));
4126  awr->awar(AWAR_DTREE_BOOTSTRAP_MAX) ->add_callback(makeRootCallback(bootstrap_range_changed_cb, ntw, 1));
4127 
4128  RootCallback reinit_treetype_cb = makeRootCallback(NT_reinit_treetype, ntw);
4129  awr->awar(AWAR_DTREE_RADIAL_ZOOM_TEXT)->add_callback(reinit_treetype_cb);
4130  awr->awar(AWAR_DTREE_RADIAL_XPAD) ->add_callback(reinit_treetype_cb);
4131  awr->awar(AWAR_DTREE_DENDRO_ZOOM_TEXT)->add_callback(reinit_treetype_cb);
4132  awr->awar(AWAR_DTREE_DENDRO_XPAD) ->add_callback(reinit_treetype_cb);
4133 
4134  RootCallback resize_cb = makeRootCallback(TREE_resize_cb, ntw);
4135  awr->awar(AWAR_DTREE_VERICAL_DIST)->add_callback(resize_cb);
4136 
4137  RootCallback recompute_and_resize_cb = makeRootCallback(TREE_recompute_and_resize_cb, ntw);
4138  awr->awar(AWAR_DTREE_GROUP_SCALE) ->add_callback(recompute_and_resize_cb);
4139  awr->awar(AWAR_DTREE_GROUP_DOWNSCALE)->add_callback(recompute_and_resize_cb);
4140 
4141  // global refresh trigger (used where a refresh is/was missing)
4142  awr->awar(AWAR_TREE_REFRESH)->add_callback(expose_cb);
4143  awr->awar(AWAR_TREE_RECOMPUTE)->add_callback(recompute_and_resize_cb);
4144 
4145  // refresh on NDS changes
4146  GBDATA *gb_arb_presets = GB_search(ntw->gb_main, "arb_presets", GB_CREATE_CONTAINER);
4147  GB_add_callback(gb_arb_presets, GB_CB_CHANGED, makeDatabaseCallback(AWT_expose_cb, static_cast<AWT_canvas*>(ntw)));
4148 
4149  // track selected species (autoscroll)
4151 
4152  // refresh on changes of marker display settings
4153  awr->awar(AWAR_DTREE_MARKER_WIDTH) ->add_callback(expose_cb);
4154  awr->awar(AWAR_DTREE_PARTIAL_GREYLEVEL) ->add_callback(expose_cb);
4157 }
4158 
4159 static void tree_insert_jump_option_menu(AW_window *aws, const char *label, const char *awar_name) {
4160  aws->label(label);
4161  aws->create_option_menu(awar_name);
4162  aws->insert_default_option("do nothing", "n", AP_DONT_JUMP);
4163  aws->insert_option ("keep visible", "k", AP_JUMP_KEEP_VISIBLE);
4164  aws->insert_option ("center vertically", "v", AP_JUMP_FORCE_VCENTER);
4165  aws->insert_option ("center", "c", AP_JUMP_FORCE_CENTER);
4166  aws->update_option_menu();
4167  aws->at_newline();
4168 }
4169 
4171 
4172  // main tree settings:
4173  { AWAR_DTREE_BASELINEWIDTH, "line_width" },
4174  { AWAR_DTREE_BRANCH_STYLE, "branch_style" },
4175  { AWAR_DTREE_SHOW_BRACKETS, "show_brackets" },
4176  { AWAR_DTREE_GROUP_STYLE, "group_style" },
4177  { AWAR_DTREE_GROUP_ORIENT, "group_orientation" },
4178  { AWAR_DTREE_GREY_LEVEL, "grey_level" },
4179  { AWAR_DTREE_GROUPCOUNTMODE, "group_countmode" },
4180  { AWAR_DTREE_GROUPINFOPOS, "group_infopos" },
4181  { AWAR_DTREE_VERICAL_DIST, "vert_dist" },
4182  { AWAR_DTREE_GROUP_SCALE, "group_scale" },
4183  { AWAR_DTREE_GROUP_DOWNSCALE, "group_downscale" },
4184  { AWAR_DTREE_AUTO_JUMP, "auto_jump" },
4185  { AWAR_DTREE_AUTO_JUMP_TREE, "auto_jump_tree" },
4186  { AWAR_DTREE_AUTO_UNFOLD, "auto_unfold" },
4187 
4188  // bootstrap sub window:
4189  { AWAR_DTREE_BOOTSTRAP_SHOW, "show_bootstrap" },
4190  { AWAR_DTREE_BOOTSTRAP_MIN, "bootstrap_min" },
4191  { AWAR_DTREE_BOOTSTRAP_MAX, "bootstrap_max" },
4192  { AWAR_DTREE_BOOTSTRAP_STYLE, "bootstrap_style" },
4193  { AWAR_DTREE_CIRCLE_SHOW, "show_circle" },
4194  { AWAR_DTREE_CIRCLE_FILL, "fill_circle" },
4195  { AWAR_DTREE_CIRCLE_ELLIPSE, "use_ellipse" },
4196  { AWAR_DTREE_CIRCLE_ZOOM, "circle_zoom" },
4197  { AWAR_DTREE_CIRCLE_LIMIT, "circle_limit" },
4198 
4199  // expert settings:
4200  { AWAR_DTREE_ATTACH_SIZE, "attach_size" },
4201  { AWAR_DTREE_ATTACH_LEN, "attach_len" },
4202  { AWAR_DTREE_ATTACH_GROUP, "attach_group" },
4203  { AWAR_DTREE_DENDRO_ZOOM_TEXT, "dendro_zoomtext" },
4204  { AWAR_DTREE_DENDRO_XPAD, "dendro_xpadding" },
4205  { AWAR_DTREE_RADIAL_ZOOM_TEXT, "radial_zoomtext" },
4206  { AWAR_DTREE_RADIAL_XPAD, "radial_xpadding" },
4207 
4208  { NULp, NULp }
4209 };
4210 
4211 static const int SCALER_WIDTH = 250; // pixel
4212 static const int LABEL_WIDTH = 30; // char
4213 
4214 static void insert_section_header(AW_window *aws, const char *title) {
4215  char *button_text = GBS_global_string_copy("%*s%s ]", LABEL_WIDTH+1, "[ ", title);
4216  aws->create_autosize_button(NULp, button_text);
4217  aws->at_newline();
4218  free(button_text);
4219 }
4220 
4222  static AW_window_simple *aws = NULp;
4223  if (!aws) {
4224  aws = new AW_window_simple;
4225  aws->init(aw_root, "TREE_EXPERT_SETUP", "Expert tree settings");
4226 
4227  aws->at(5, 5);
4228  aws->auto_space(5, 5);
4229  aws->label_length(LABEL_WIDTH);
4230  aws->button_length(8);
4231 
4232  aws->callback(AW_POPDOWN);
4233  aws->create_button("CLOSE", "CLOSE", "C");
4234  aws->callback(makeHelpCallback("nt_tree_settings_expert.hlp"));
4235  aws->create_button("HELP", "HELP", "H");
4236  aws->at_newline();
4237 
4238  insert_section_header(aws, "parent attach position");
4239 
4240  aws->label("Attach by size");
4241  aws->create_input_field_with_scaler(AWAR_DTREE_ATTACH_SIZE, 4, SCALER_WIDTH);
4242  aws->at_newline();
4243 
4244  aws->label("Attach by len");
4245  aws->create_input_field_with_scaler(AWAR_DTREE_ATTACH_LEN, 4, SCALER_WIDTH);
4246  aws->at_newline();
4247 
4248  aws->label("Attach (at groups)");
4249  aws->create_input_field_with_scaler(AWAR_DTREE_ATTACH_GROUP, 4, SCALER_WIDTH);
4250  aws->at_newline();
4251 
4252  insert_section_header(aws, "text zooming / padding");
4253 
4254  const int PAD_SCALER_WIDTH = SCALER_WIDTH-39;
4255 
4256  aws->label("Text zoom/pad (dendro)");
4257  aws->create_toggle(AWAR_DTREE_DENDRO_ZOOM_TEXT);
4258  aws->create_input_field_with_scaler(AWAR_DTREE_DENDRO_XPAD, 4, PAD_SCALER_WIDTH);
4259  aws->at_newline();
4260 
4261  aws->label("Text zoom/pad (radial)");
4262  aws->create_toggle(AWAR_DTREE_RADIAL_ZOOM_TEXT);
4263  aws->create_input_field_with_scaler(AWAR_DTREE_RADIAL_XPAD, 4, PAD_SCALER_WIDTH);
4264  aws->at_newline();
4265 
4266  aws->window_fit();
4267  }
4268  return aws;
4269 }
4270 
4272  static AW_window_simple *aws = NULp;
4273  if (!aws) {
4274  aws = new AW_window_simple;
4275  aws->init(aw_root, "TREE_BOOT_SETUP", "Bootstrap display settings");
4276 
4277  aws->at(5, 5);
4278  aws->auto_space(5, 5);
4279  aws->label_length(LABEL_WIDTH);
4280  aws->button_length(8);
4281 
4282  aws->callback(AW_POPDOWN);
4283  aws->create_button("CLOSE", "CLOSE", "C");
4284  aws->callback(makeHelpCallback("nt_tree_settings_bootstrap.hlp"));
4285  aws->create_button("HELP", "HELP", "H");
4286  aws->at_newline();
4287 
4288  insert_section_header(aws, "visibility");
4289 
4290  aws->label("Show bootstraps");
4291  aws->create_toggle(AWAR_DTREE_BOOTSTRAP_SHOW);
4292  aws->at_newline();
4293 
4294  aws->label("Hide bootstraps below");
4295  aws->create_input_field_with_scaler(AWAR_DTREE_BOOTSTRAP_MIN, 4, SCALER_WIDTH);
4296  aws->at_newline();
4297 
4298  aws->label("Hide bootstraps above\n(0=disable limits)");
4299  aws->create_input_field_with_scaler(AWAR_DTREE_BOOTSTRAP_MAX, 4, SCALER_WIDTH);
4300  aws->at_newline();
4301 
4302  insert_section_header(aws, "style");
4303 
4304  aws->label("Bootstrap style");
4305  aws->create_option_menu(AWAR_DTREE_BOOTSTRAP_STYLE);
4306  aws->insert_default_option("percent%", "p", BS_PERCENT);
4307  aws->insert_option ("percent", "c", BS_PERCENT_NOSIGN);
4308  aws->insert_option ("float", "f", BS_FLOAT);
4309  aws->update_option_menu();
4310  aws->at_newline();
4311 
4312  insert_section_header(aws, "circles");
4313 
4314  aws->label("Show bootstrap circles");
4315  aws->create_toggle(AWAR_DTREE_CIRCLE_SHOW);
4316  aws->at_newline();
4317 
4318  aws->label("Greylevel of circles (%)");
4319  aws->create_input_field_with_scaler(AWAR_DTREE_CIRCLE_FILL, 4, SCALER_WIDTH);
4320  aws->at_newline();
4321 
4322  aws->label("Use ellipses");
4323  aws->create_toggle(AWAR_DTREE_CIRCLE_ELLIPSE);
4324  aws->at_newline();
4325 
4326  aws->label("Bootstrap circle zoom factor");
4327  aws->create_input_field_with_scaler(AWAR_DTREE_CIRCLE_ZOOM, 4, SCALER_WIDTH);
4328  aws->at_newline();
4329 
4330  aws->label("Bootstrap radius limit");
4331  aws->create_input_field_with_scaler(AWAR_DTREE_CIRCLE_LIMIT, 4, SCALER_WIDTH, AW_SCALER_EXP_LOWER);
4332  aws->at_newline();
4333 
4334  aws->window_fit();
4335  }
4336  return aws;
4337 }
4338 
4340  static AW_window_simple *aws = NULp;
4341  if (!aws) {
4342  aws = new AW_window_simple;
4343  aws->init(aw_root, "TREE_SETUP", "Tree settings");
4344  aws->load_xfig("awt/tree_settings.fig");
4345 
4346  aws->at("close");
4347  aws->auto_space(5, 5);
4348  aws->label_length(LABEL_WIDTH);
4349  aws->button_length(8);
4350 
4351  aws->callback(AW_POPDOWN);
4352  aws->create_button("CLOSE", "CLOSE", "C");
4353  aws->callback(makeHelpCallback("nt_tree_settings.hlp"));
4354  aws->create_button("HELP", "HELP", "H");
4355 
4356  aws->at("button");
4357 
4358  insert_section_header(aws, "branches");
4359 
4360  aws->label("Base line width");
4361  aws->create_input_field_with_scaler(AWAR_DTREE_BASELINEWIDTH, 4, SCALER_WIDTH);
4362  aws->at_newline();
4363 
4364  aws->label("Branch style");
4365  aws->create_option_menu(AWAR_DTREE_BRANCH_STYLE);
4366  aws->insert_default_option("Rectangular", "R", BS_RECTANGULAR);
4367  aws->insert_option ("Diagonal", "D", BS_DIAGONAL);
4368  aws->update_option_menu();
4369  aws->at_newline();
4370 
4371  insert_section_header(aws, "groups");
4372 
4373  aws->label("Show group brackets");
4374  aws->create_toggle(AWAR_DTREE_SHOW_BRACKETS);
4375  aws->at_newline();
4376 
4377  aws->label("Group style");
4378  aws->create_option_menu(AWAR_DTREE_GROUP_STYLE);
4379  aws->insert_default_option("Trapeze", "z", GS_TRAPEZE);
4380  aws->insert_option ("Triangle", "i", GS_TRIANGLE);
4381  aws->update_option_menu();
4382  aws->create_option_menu(AWAR_DTREE_GROUP_ORIENT);
4383  aws->insert_default_option("Top", "T", GO_TOP);
4384  aws->insert_option ("Bottom", "B", GO_BOTTOM);
4385  aws->insert_option ("Interior", "I", GO_INTERIOR);
4386  aws->insert_option ("Exterior", "E", GO_EXTERIOR);
4387  aws->update_option_menu();
4388  aws->at_newline();
4389 
4390  aws->label("Greylevel of groups (%)");
4391  aws->create_input_field_with_scaler(AWAR_DTREE_GREY_LEVEL, 4, SCALER_WIDTH);
4392  aws->at_newline();
4393 
4394  aws->label("Show group counter");
4395  aws->create_option_menu(AWAR_DTREE_GROUPCOUNTMODE);
4396  aws->insert_default_option("None", "N", GCM_NONE);
4397  aws->insert_option ("Members", "M", GCM_MEMBERS);
4398  aws->insert_option ("Marked", "a", GCM_MARKED);
4399  aws->insert_option ("Marked/Members", "b", GCM_BOTH);
4400  aws->insert_option ("%Marked", "%", GCM_PERCENT);
4401  aws->insert_option ("%Marked/Members", "e", GCM_BOTH_PC);
4402  aws->update_option_menu();
4403  aws->at_newline();
4404 
4405  aws->label("Group counter position");
4406  aws->create_option_menu(AWAR_DTREE_GROUPINFOPOS);
4407  aws->insert_default_option("Attached", "A", GIP_ATTACHED);
4408  aws->insert_option ("Overlayed", "O", GIP_OVERLAYED);
4409  aws->insert_option ("Separated", "a", GIP_SEPARATED);
4410  aws->update_option_menu();
4411  aws->at_newline();
4412 
4413  insert_section_header(aws, "vertical scaling");
4414 
4415  aws->label("Vertical distance");
4416  aws->create_input_field_with_scaler(AWAR_DTREE_VERICAL_DIST, 4, SCALER_WIDTH, AW_SCALER_EXP_LOWER);
4417  aws->at_newline();
4418 
4419  aws->label("Vertical group scaling");
4420  aws->create_input_field_with_scaler(AWAR_DTREE_GROUP_SCALE, 4, SCALER_WIDTH);
4421  aws->at_newline();
4422 
4423  aws->label("'Biggroup' scaling");
4424  aws->create_input_field_with_scaler(AWAR_DTREE_GROUP_DOWNSCALE, 4, SCALER_WIDTH);
4425  aws->at_newline();
4426 
4427  insert_section_header(aws, "auto focus");
4428 
4429  tree_insert_jump_option_menu(aws, "On species change", AWAR_DTREE_AUTO_JUMP);
4431 
4432  aws->label("Auto unfold selected species?");
4433  aws->create_toggle(AWAR_DTREE_AUTO_UNFOLD);
4434 
4435  // complete top area of window
4436 
4437  aws->at("config");
4438  AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "tree_settings", tree_setting_config_mapping);
4439 
4440  aws->button_length(19);
4441 
4442  aws->at("bv");
4443  aws->create_toggle(AWAR_DTREE_BOOTSTRAP_SHOW);
4444 
4445  aws->at("bootstrap");
4447  aws->create_button("bootstrap", "Bootstrap settings", "B");
4448 
4449  aws->at("expert");
4450  aws->callback(create_tree_expert_settings_window);
4451  aws->create_button("expert", "Expert settings", "E");
4452  }
4453  return aws;
4454 }
4455 
4456 // --------------------------------------------------------------------------------
4457 
4459  static AW_window_simple *aws = NULp;
4460 
4461  if (!aws) {
4462  aws = new AW_window_simple;
4463 
4464  aws->init(root, "MARKER_SETTINGS", "Tree marker settings");
4465 
4466  aws->auto_space(10, 10);
4467 
4468  aws->callback(AW_POPDOWN);
4469  aws->create_button("CLOSE", "CLOSE", "C");
4470  aws->callback(makeHelpCallback("nt_tree_marker_settings.hlp"));
4471  aws->create_button("HELP", "HELP", "H");
4472  aws->at_newline();
4473 
4474  const int FIELDSIZE = 5;
4475  const int SCALERSIZE = 250;
4476  aws->label_length(35);
4477 
4478  aws->label("Group marked threshold");
4479  aws->create_input_field_with_scaler(AWAR_DTREE_GROUP_MARKED_THRESHOLD, FIELDSIZE, SCALERSIZE);
4480 
4481  aws->at_newline();
4482 
4483  aws->label("Group partially marked threshold");
4484  aws->create_input_field_with_scaler(AWAR_DTREE_GROUP_PARTIALLY_MARKED_THRESHOLD, FIELDSIZE, SCALERSIZE);
4485 
4486  aws->at_newline();
4487 
4488  aws->label("Marker width");
4489  aws->create_input_field_with_scaler(AWAR_DTREE_MARKER_WIDTH, FIELDSIZE, SCALERSIZE);
4490 
4491  aws->at_newline();
4492 
4493  aws->label("Partial marker greylevel");
4494  aws->create_input_field_with_scaler(AWAR_DTREE_PARTIAL_GREYLEVEL, FIELDSIZE, SCALERSIZE);
4495 
4496  aws->at_newline();
4497  }
4498 
4499  return aws;
4500 }
4501 
4502 // --------------------------------------------------------------------------------
4503 
4504 #ifdef UNIT_TESTS
4505 #include <test_unit.h>
4506 #include <../../WINDOW/aw_common.hxx>
4507 
4508 static void fake_AD_map_viewer_cb(GBDATA *, AD_MAP_VIEWER_TYPE) {}
4509 
4510 static AW_rgb colors_def[] = {
4512  0x30b0e0,
4513  0xff8800, // AWT_GC_CURSOR
4514  0xa3b3cf, // AWT_GC_BRANCH_REMARK
4515  0x53d3ff, // AWT_GC_BOOTSTRAP
4516  0x808080, // AWT_GC_BOOTSTRAP_LIMITED
4517  0x000000, // AWT_GC_IRS_GROUP_BOX
4518  0xf0c000, // AWT_GC_ALL_MARKED
4519  0xbb8833, // AWT_GC_SOME_MARKED
4520  0x622300, // AWT_GC_NONE_MARKED
4521  0x977a0e, // AWT_GC_ONLY_ZOMBIES
4522 
4523  0x000000, // AWT_GC_BLACK
4524  0x808080, // AWT_GC_WHITE
4525 
4526  0xff0000, // AWT_GC_RED
4527  0x00ff00, // AWT_GC_GREEN
4528  0x0000ff, // AWT_GC_BLUE
4529 
4530  0xc0ff40, // AWT_GC_ORANGE
4531  0x40c0ff, // AWT_GC_AQUAMARIN
4532  0xf030b0, // AWT_GC_PURPLE
4533 
4534  0xffff00, // AWT_GC_YELLOW
4535  0x00ffff, // AWT_GC_CYAN
4536  0xff00ff, // AWT_GC_MAGENTA
4537 
4538  0xc0ff40, // AWT_GC_LAWNGREEN
4539  0x40c0ff, // AWT_GC_SKYBLUE
4540  0xf030b0, // AWT_GC_PINK
4541 
4542  0xd50000, // AWT_GC_FIRST_COLOR_GROUP
4543  0x00c0a0,
4544  0x00ff77,
4545  0xc700c7,
4546  0x0000ff,
4547  0xffce5b,
4548  0xab2323,
4549  0x008888,
4550  0x008800,
4551  0x880088,
4552  0x000088,
4553  0x888800,
4554  AW_NO_COLOR
4555 };
4556 static AW_rgb *fcolors = colors_def;
4557 static AW_rgb *dcolors = colors_def;
4558 static long dcolors_count = ARRAY_ELEMS(colors_def);
4559 
4560 class fake_AW_GC : public AW_GC {
4561  void wm_set_foreground_color(AW_rgb /*col*/) OVERRIDE { }
4562  void wm_set_function(AW_function /*mode*/) OVERRIDE { td_assert(0); }
4563  void wm_set_lineattributes(short /*lwidth*/, AW_linestyle /*lstyle*/) OVERRIDE {}
4564  void wm_set_font(AW_font /*font_nr*/, int size, int */*found_size*/) OVERRIDE {
4565  unsigned int i;
4567  set_char_size(i, size, 0, size-2); // good fake size for Courier 8pt
4568  }
4569  }
4570 public:
4571  fake_AW_GC(AW_common *common_) : AW_GC(common_) {}
4572  int get_available_fontsizes(AW_font /*font_nr*/, int */*available_sizes*/) const OVERRIDE {
4573  td_assert(0);
4574  return 0;
4575  }
4576 };
4577 
4578 struct fake_AW_common : public AW_common {
4579  fake_AW_common()
4580  : AW_common(fcolors, dcolors, dcolors_count)
4581  {
4582  for (int gc = 0; gc < dcolors_count-AW_STD_COLOR_IDX_MAX; ++gc) { // gcs used in this example
4583  new_gc(gc);
4584  AW_GC *gcm = map_mod_gc(gc);
4585  gcm->set_line_attributes(1, AW_SOLID);
4586  gcm->set_function(AW_COPY);
4587  gcm->set_font(12, 8, NULp); // 12 is Courier (use monospaced here, cause font limits are faked)
4588 
4589  gcm->set_fg_color(colors_def[gc+AW_STD_COLOR_IDX_MAX]);
4590  }
4591  }
4592  ~fake_AW_common() OVERRIDE {}
4593 
4594  virtual AW_GC *create_gc() {
4595  return new fake_AW_GC(this);
4596  }
4597 };
4598 
4599 class fake_AWT_graphic_tree FINAL_TYPE : public AWT_graphic_tree {
4600  int var_mode; // current range: [0..3]
4601  double att_size, att_len;
4602  BranchStyle bstyle;
4603 
4604  void read_tree_settings() OVERRIDE {
4605  scaled_branch_distance = 1.0; // not final value!
4606 
4607  // var_mode is in range [0..3]
4608  // it is used to vary tree settings such that many different combinations get tested
4609 
4610  static const double group_attach[] = { 0.5, 1.0, 0.0, };
4611  static const double tested_greylevel[] = { 0.0, 0.25, 0.75, 1.0 };
4612 
4613  int mvar_mode = var_mode+int(tree_style)+int(group_style); // [0..3] + [0..5] + [0..1] = [0..9]
4614 
4615  groupScale.pow = .33;
4616  groupScale.linear = (tree_style == AP_TREE_RADIAL) ? 7.0 : 1.0;
4617  group_greylevel = tested_greylevel[mvar_mode%4];
4618 
4619  baselinewidth = (var_mode == 3)+1;
4620  show_brackets = (var_mode != 2);
4621  group_style = ((var_mode%2) == (bstyle == BS_DIAGONAL)) ? GS_TRAPEZE : GS_TRIANGLE;
4622  group_orientation = GroupOrientation((var_mode+1)%4);
4623  attach_size = att_size;
4624  attach_len = att_len;
4625  attach_group = group_attach[var_mode%3];
4626  branch_style = bstyle;
4627 
4628  bconf.zoom_factor = 1.3;
4629  bconf.max_radius = 1.95;
4630  bconf.show_circle = var_mode%3;
4631  bconf.fill_level = 1.0 - group_greylevel; // for bootstrap circles use inverse shading of groups
4632 
4633  bconf.ellipsoid = var_mode%2;
4634  bconf.show_boots = !(var_mode == 0 && bstyle == BS_DIAGONAL); // hide BS in dendro_MN_diagonal.fig
4635  bconf.bootstrap_min = var_mode<2 ? 0 : 5; // remove BS<5% for var_mode 0,1
4636  bconf.bootstrap_max = !(var_mode%2) ? 100 : 95; // remove BS>95% for var_mode 0,2
4637  if (var_mode != ((bconf.show_circle+branch_style)%3) && bconf.bootstrap_max == 100) {
4638  bconf.bootstrap_max = 99; // hide 100% branches in those figs previously excluded via 'auto_add_100'
4639  }
4640 
4641  bconf.style = (mvar_mode%4) ? BS_PERCENT : (int(group_style)%2 ? BS_PERCENT_NOSIGN : BS_FLOAT);
4642 
4643  group_info_pos = GIP_SEPARATED;
4644  switch (var_mode) {
4645  case 2: group_info_pos = GIP_ATTACHED; break;
4646  case 3: group_info_pos = GIP_OVERLAYED; break;
4647  }
4648 
4649  switch (var_mode) {
4650  case 0: group_count_mode = GCM_MEMBERS; break;
4651  case 1: group_count_mode = GCM_NONE; break;
4652  case 2: group_count_mode = (tree_style%2) ? GCM_MARKED : GCM_PERCENT; break;
4653  case 3: group_count_mode = (tree_style%2) ? GCM_BOTH : GCM_BOTH_PC; break;
4654  }
4655  }
4656 
4657 public:
4658  fake_AWT_graphic_tree(GBDATA *gbmain, const char *selected_species)
4659  : AWT_graphic_tree(NULp, gbmain, fake_AD_map_viewer_cb),
4660  var_mode(0),
4661  att_size(0),
4662  att_len(0),
4663  bstyle(BS_RECTANGULAR)
4664  {
4665  species_name = strdup(selected_species);
4666  exports.modifying = 1; // hack to workaround need for AWT_auto_refresh
4667  }
4668 
4669  void set_var_mode(int mode) { var_mode = mode; }
4670  void set_attach(double asize, double alen) { att_size = asize; att_len = alen; }
4671  void set_branchstyle(BranchStyle bstyle_) { bstyle = bstyle_; }
4672 
4673  void test_show_tree(AW_device *device, bool force_compute) {
4674  if (force_compute) {
4675  // force reload and recompute (otherwise changing groupScale.linear has DELAYED effect)
4678  }
4679  check_for_DB_update(get_gbmain()); // hack to workaround need for AWT_auto_refresh
4680  show(device);
4681  }
4682 
4683  void test_print_tree(AW_device_print *print_device, AP_tree_display_style style, bool show_handles) {
4684  const int SCREENSIZE = 541; // use a prime as screensize to reduce rounding errors
4685  AW_device_size size_device(print_device->get_common());
4686 
4687  size_device.reset();
4688  size_device.zoom(1.0);
4689  size_device.set_filter(AW_SIZE|AW_SIZE_UNSCALED);
4690  test_show_tree(&size_device, true);
4691 
4692  Rectangle drawn = size_device.get_size_information();
4693 
4694  td_assert(drawn.surface() >= 0.0);
4695 
4696  double zoomx = SCREENSIZE/drawn.width();
4697  double zoomy = SCREENSIZE/drawn.height();
4698  double zoom = 0.0;
4699 
4700  switch (style) {
4701  case AP_LIST_SIMPLE:
4702  case AP_TREE_RADIAL:
4703  zoom = std::max(zoomx, zoomy);
4704  break;
4705 
4706  case AP_TREE_NORMAL:
4707  case AP_TREE_IRS:
4708  zoom = zoomx;
4709  break;
4710 
4711  case AP_LIST_NDS:
4712  zoom = 1.0;
4713  break;
4714  }
4715 
4716  if (!nearlyEqual(zoom, 1.0)) {
4717  // recalculate size
4718  size_device.restart_tracking();
4719  size_device.reset();
4720  size_device.zoom(zoom);
4721  size_device.set_filter(AW_SIZE|AW_SIZE_UNSCALED);
4722  test_show_tree(&size_device, false);
4723  }
4724 
4725  drawn = size_device.get_size_information();
4726 
4727  const AW_borders& text_overlap = size_device.get_unscaleable_overlap();
4728  Rectangle drawn_text = size_device.get_size_information_inclusive_text();
4729 
4730  int EXTRA = SCREENSIZE*0.05;
4731  AW_screen_area clipping;
4732 
4733  clipping.l = 0; clipping.r = drawn.width()+text_overlap.l+text_overlap.r + 2*EXTRA;
4734  clipping.t = 0; clipping.b = drawn.height()+text_overlap.t+text_overlap.b + 2*EXTRA;
4735 
4736  print_device->get_common()->set_screen(clipping);
4737  print_device->set_filter(AW_PRINTER|(show_handles ? AW_PRINTER_EXT : 0));
4738  print_device->reset();
4739 
4740  print_device->zoom(zoom);
4741 
4742  Rectangle drawn_world = print_device->rtransform(drawn);
4743  Rectangle drawn_text_world = print_device->rtransform(drawn_text);
4744 
4745  Vector extra_shift = Vector(EXTRA, EXTRA);
4746  Vector corner_shift = -Vector(drawn.upper_left_corner());
4747  Vector text_shift = Vector(text_overlap.l, text_overlap.t);
4748 
4749  Vector offset(extra_shift+corner_shift+text_shift);
4750  print_device->set_offset(offset/(zoom*zoom)); // dont really understand this, but it does the right shift
4751 
4752  test_show_tree(print_device, false);
4753  print_device->box(AWT_GC_CURSOR, AW::FillStyle::EMPTY, drawn_world);
4754  print_device->box(AWT_GC_IRS_GROUP_BOX, AW::FillStyle::EMPTY, drawn_text_world);
4755  }
4756 };
4757 
4759 void AW_init_color_groups(AW_root *awr, AW_default def);
4760 
4761 static bool replaceFirstRemark(TreeNode *node, const char *oldRem, const char *newRem) {
4762  if (!node->is_leaf()) {
4763  const char *rem = node->get_remark();
4764  if (rem && strcmp(rem, oldRem) == 0) {
4765  node->set_remark(newRem);
4766  return true;
4767  }
4768 
4769  return
4770  replaceFirstRemark(node->get_leftson(), oldRem, newRem) ||
4771  replaceFirstRemark(node->get_rightson(), oldRem, newRem);
4772  }
4773  return false;
4774 }
4775 
4776 void TEST_treeDisplay() {
4777  GB_shell shell;
4778  GBDATA *gb_main = GB_open("../../demo.arb", "r");
4779 
4780  fake_AWT_graphic_tree agt(gb_main, "OctSprin");
4781  fake_AW_common fake_common;
4782 
4783  AW_device_print print_dev(&fake_common);
4786 
4787  agt.init(new AliView(gb_main), NULp, true, false);
4788 
4789  {
4790  GB_transaction ta(gb_main);
4791  ASSERT_RESULT(const char *, NULp, agt.load_from_DB(NULp, "tree_test")); // calls compute_tree once
4792  }
4793 
4794  const char *spoolnameof[] = {
4795  "dendro",
4796  "radial",
4797  "irs",
4798  "nds",
4799  NULp, // "simple", (too simple, need no test)
4800  };
4801 
4802  // modify some tree comments
4803  {
4804  AP_tree *rootNode = agt.get_root_node();
4805  TEST_EXPECT(replaceFirstRemark(rootNode, "97%", "hello")); // is displayed when bootstrap display is OFF (e.g. in dendro_MN_diagonal.fig)
4806  TEST_EXPECT(replaceFirstRemark(rootNode, "44%", "100%"));
4807 
4808  GB_transaction ta(gb_main);
4809  ASSERT_RESULT(const char *, NULp, agt.save_to_DB(NULp, "tree_test"));
4810  }
4811 
4812  for (int show_handles = 0; show_handles <= 1; ++show_handles) {
4813  for (int color = 0; color <= 1; ++color) {
4814  print_dev.set_color_mode(color);
4815  // for (int istyle = AP_TREE_NORMAL; istyle <= AP_LIST_SIMPLE; ++istyle) {
4816  for (int istyle = AP_LIST_SIMPLE; istyle >= AP_TREE_NORMAL; --istyle) {
4818  if (spoolnameof[style]) {
4819  char *spool_name = GBS_global_string_copy("display/%s_%c%c", spoolnameof[style], "MC"[color], "NH"[show_handles]);
4820 
4821  agt.set_tree_style(style, NULp);
4822 
4823  int var_mode = show_handles+2*color;
4824 
4825  static struct AttachSettings {
4826  const char *suffix;
4827  double bySize;
4828  double byLength;
4829  } attach_settings[] = {
4830  { "", -1, 0 }, // [size only] traditional attach point (produces old test results)
4831  { "_long", 0, 1 }, // [len only] attach at long branch
4832  { "_shortSmall", -1, -1 }, // [both] attach at short+small branch
4833  { "_centered", 0, 0 }, // [none] center attach points
4834  { NULp, 0, 0 },
4835  };
4836 
4837  for (int attach_style = 0; attach_settings[attach_style].suffix; ++attach_style) {
4838  if (attach_style>0) {
4839  if (style != AP_TREE_NORMAL) continue; // test attach-styles only for dendrogram-view
4840  if (attach_style != var_mode) continue; // do not create too many permutations
4841  }
4842 
4843  const AttachSettings& SETT = attach_settings[attach_style];
4844  char *spool_name2 = GBS_global_string_copy("%s%s", spool_name, SETT.suffix);
4845 
4846  for (BranchStyle bstyle = BS_RECTANGULAR; bstyle<=BS_DIAGONAL; bstyle = BranchStyle(bstyle+1)) {
4847  if (bstyle != BS_RECTANGULAR) { // test alternate branch-styles only ..
4848  if (istyle != AP_TREE_NORMAL) continue; // .. for dendrogram view
4849  if (attach_style != 0 && attach_style != 3) continue; // .. for traditional and centered attach_points
4850  }
4851 
4852  static const char *suffix[] = {
4853  "",
4854  "_diagonal",
4855  };
4856 
4857  char *spool_name3 = GBS_global_string_copy("%s%s", spool_name2, suffix[bstyle]);
4858 
4859 // #define TEST_AUTO_UPDATE // dont test, instead update expected results
4860  {
4861  char *spool_file = GBS_global_string_copy("%s_curr.fig", spool_name3);
4862  char *spool_expected = GBS_global_string_copy("%s.fig", spool_name3);
4863 
4864 #if defined(TEST_AUTO_UPDATE)
4865 #warning TEST_AUTO_UPDATE is active (non-default)
4866  TEST_EXPECT_NO_ERROR(print_dev.open(spool_expected));
4867 #else
4868  TEST_EXPECT_NO_ERROR(print_dev.open(spool_file));
4869 #endif
4870 
4871  {
4872  GB_transaction ta(gb_main);
4873  agt.set_var_mode(var_mode);
4874  agt.set_attach(SETT.bySize, SETT.byLength);
4875  agt.set_branchstyle(bstyle);
4876  agt.test_print_tree(&print_dev, style, show_handles);
4877  }
4878 
4879  print_dev.close();
4880 
4881 #if !defined(TEST_AUTO_UPDATE)
4882  TEST_EXPECT_TEXTFILES_EQUAL(spool_expected, spool_file);
4883  TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(spool_file));
4884 #endif
4885  free(spool_expected);
4886  free(spool_file);
4887  }
4888  free(spool_name3);
4889  }
4890  free(spool_name2);
4891  }
4892  free(spool_name);
4893  }
4894  }
4895  }
4896  }
4897 
4898  GB_close(gb_main);
4899 }
4900 
4901 #endif // UNIT_TESTS
4902 
Angle & rotate90deg()
#define MAX_TREEDISP_RECURSION_DEPTH
Definition: TreeDisplay.hxx:35
AP_tree * get_node() const
Definition: Group.hxx:54
#define AWAR_DTREE_GROUP_STYLE
Definition: TreeDisplay.hxx:53
void reorderTree(TreeOrder mode)
Position nearest_corner(const Position &topos) const
Definition: AW_position.cxx:67
float get_angle() const
Definition: AP_Tree.hxx:331
#define AWAR_DTREE_BOOTSTRAP_MAX
Definition: TreeDisplay.hxx:60
void insert_option(AW_label choice_label, const char *mnemonic, const char *var_value, const char *name_of_color=NULp)
GB_ERROR GB_copy_dropProtectMarksAndTempstate(GBDATA *dest, GBDATA *source)
Definition: arbdb.cxx:2152
void set_branchlength_unrooted(GBT_LEN newlen)
Definition: TreeNode.h:324
GBDATA * get_gb_main() const
Definition: ARB_Tree.hxx:80
AW::Vector transform(const AW::Vector &vec) const
Definition: aw_device.hxx:144
bool are_antiparallel(const Vector &v1, const Vector &v2)
returns true, if two vectors have opposite orientations
const char * GB_ERROR
Definition: arb_core.h:25
GBDATA * get_group_data() const
Definition: Group.hxx:55
MarkerXPos(AW_pos scale, int markers_)
int getNodeSize() const
unsigned view_sum
Definition: AP_Tree.hxx:159
AW_key_code key_code() const
Definition: canvas.hxx:236
ClickedElement(const ClickedElement &other)
GBDATA * GB_open(const char *path, const char *opent)
Definition: ad_load.cxx:1363
GB_TYPES type
AP_tree * node() const
double print_width
AP_tree * get_logical_root()
bool warn_inappropriate_mode(AWT_COMMAND_MODE mode)
AP_tree_root * get_tree_root()
void set_max(float val)
virtual AP_tree_root * create_tree_root(AliView *aliview, AP_sequence *seq_prototype, bool insert_delete_cbs)
void set_grey_level(int gc, AW_grey_level grey_level)
Definition: AW_device.cxx:450
void recompute_and_write_folding()
Definition: AP_Tree.cxx:756
#define TREEDISP_TRUNCATION_MESSAGE
Definition: TreeDisplay.hxx:36
AW_pos get_scale() const
Definition: aw_device.hxx:124
GBDATA * GBT_first_marked_species(GBDATA *gb_main)
Definition: aditem.cxx:113
const AW_bitset AW_SIZE
Definition: aw_device.hxx:37
AWT_graphic_tree * TREE_generate_display(AW_root *root, GBDATA *gb_main, AD_map_viewer_cb map_viewer_cb)
#define NT_ROOT_WIDTH
Definition: TreeDisplay.hxx:82
size_t size() const
Definition: arb_strarray.h:85
const AW_bitset AW_PRINTER_EXT
Definition: aw_device.hxx:40
const AW_bitset AW_SCREEN
Definition: aw_device.hxx:34
static void resize_cb()
Definition: PH_main.cxx:103
mark_mode
Definition: db_query.cxx:1562
double centroid(const double &val1, const double &val2)
Definition: aw_position.hxx:77
double pc
bool node_is_auto_unfolded(AP_tree *node) const
AP_tree * unfold(const AP_tree_set &want_unfolded)
#define AWAR_DTREE_BASELINEWIDTH
Definition: TreeDisplay.hxx:41
#define AWAR_DTREE_GROUP_SCALE
Definition: TreeDisplay.hxx:48
GB_ERROR load_from_DB(GBDATA *gb_main, const char *name) FINAL_OVERRIDE __ATTR__USERESULT
long GB_read_int(GBDATA *gbd)
Definition: arbdb.cxx:729
void request_structure_update()
Definition: canvas.hxx:166
GB_ERROR move_group_to(AP_tree *new_group) __ATTR__USERESULT
Definition: AP_Tree.cxx:629
short get_height() const
#define implicated(hypothesis, conclusion)
Definition: arb_assert.h:289
Position centroid() const
TreeNode * findLeafNamed(const char *wantedName)
Definition: TreeNode.cxx:276
static bool valid_drag_device(AW_device *device)
static char * y[maxsp+1]
DECLARE_ASSIGNMENT_OPERATOR(ClickedElement)
return string(buffer, length)
void set_line_attributes(short new_width, AW_linestyle new_style)
Definition: aw_common.hxx:163
void AWT_insert_config_manager(AW_window *aww, AW_default default_file_, const char *id, const StoreConfigCallback &store_cb, const RestoreConfigCallback &load_or_reset_cb, const char *macro_id, const AWT_predefined_config *predef)
bool valid() const
size_t get_part_count() const
static AW_device_click::ClickPreference preferredForCommand(AWT_COMMAND_MODE mode)
void request_zoom_reset()
Definition: canvas.hxx:165
int calc_clade_level() const
Definition: TreeNode.h:611
void do_drop(const AW_clicked_element *drop_target, const Position &mousepos)
bool nearlyEqual(const double &val1, const double &val2)
Definition: aw_position.hxx:75
const double & radian() const
static void tree_insert_jump_option_menu(AW_window *aws, const char *label, const char *awar_name)
void group_rest_tree(AP_tree *at, CollapseMode mode, int color_group)
void set_logical_root_to(AP_tree *node)
static const int SCALER_WIDTH
bool has_group_info() const
Definition: TreeNode.h:498
void check_for_DB_update(GBDATA *gb_main) FINAL_OVERRIDE
DB_scalable(GBDATA *gbd_)
GB_ERROR GB_add_callback(GBDATA *gbd, GB_CB_TYPE type, const DatabaseCallback &dbcb)
Definition: ad_cb.cxx:356
void set_line_attributes(int gc, short width, AW_linestyle style)
Definition: AW_device.cxx:465
#define AWAR_DTREE_AUTO_JUMP_TREE
Definition: TreeDisplay.hxx:50
virtual void draw_drag_indicator(AW_device *device, int drag_gc) const =0
long GBT_mark_all(GBDATA *gb_main, int flag)
Definition: aditem.cxx:295
double width() const
bool is_auto_unfolded() const
void draw_drag_indicator(AW_device *device, int drag_gc) const FINAL_OVERRIDE
#define AWAR_DTREE_CIRCLE_LIMIT
Definition: TreeDisplay.hxx:67
bool is_upper_son() const
Definition: ARB_Tree.hxx:160
virtual void moveNextTo(AP_tree *new_brother, AP_FLOAT rel_pos)
Definition: AP_Tree.cxx:353
const AW_screen_area & get_cliprect() const
Definition: aw_device.hxx:237
const AW_bitset circle_filter
void request_refresh()
Definition: canvas.hxx:372
void detect_group_state(AP_tree *at, AWT_graphic_tree_group_state *state, AP_tree *skip_this_son)
void set_font(AW_font font_nr, int size, int *found_size)
Definition: AW_xfont.cxx:839
void store_command_data(AWT_command_data *new_cmd_data)
unsigned mark_sum
Definition: AP_Tree.hxx:158
float min_tree_depth
Definition: AP_Tree.hxx:162
void request_resize()
Definition: canvas.hxx:164
unsigned leaf_sum
Definition: AP_Tree.hxx:157
void set_node_deleted_callback(AP_nodeDelCb cb, void *cd)
Definition: AP_Tree.cxx:255
#define td_assert(cond)
Definition: Group.hxx:18
bool is_below_clip(double ypos) const
Definition: aw_device.hxx:210
GBT_LEN get_branchlength_unrooted() const
Definition: TreeNode.h:317
bool is_clade() const
Definition: TreeNode.h:547
#define AW_FONTINFO_CHAR_ASCII_MAX
Definition: aw_common.hxx:27
long mark_species_in_tree_that(AP_tree *at, int mark, bool(*condition)(GBDATA *, void *), void *cd)
ListDisplayRow(GBDATA *gb_main, GBDATA *gb_species_, AW_pos y_position_, int gc_, AW_device &device, bool use_nds, const char *tree_name, const NDS_Labeler &labeler)
int drag_gc
Definition: canvas.hxx:256
AW_GC * map_mod_gc(int gc)
Definition: aw_common.hxx:268
void expose_cb()
Definition: PH_main.cxx:96
#define ASSERT_RESULT(Type, Expected, Expr)
Definition: arb_assert.h:336
GB_ERROR save_to_DB(GBDATA *gb_main, const char *name) FINAL_OVERRIDE __ATTR__USERESULT
void notify_synchronized(GBDATA *gb_main) FINAL_OVERRIDE
AP_tree * get_node() const
#define NO_TREE_SELECTED
static void TREE_recompute_and_resize_cb(UNFIXED, TREE_canvas *ntw)
void setKeeledState(int keeledState)
Definition: TreeNode.h:532
bool is_folded_group() const
Definition: AP_Tree.hxx:348
long read_int() const
Definition: AW_awar.cxx:184
#define AWAR_DTREE_BRANCH_STYLE
Definition: TreeDisplay.hxx:43
std::set< AP_tree * > AP_tree_set
Definition: AP_TreeSet.hxx:21
AD_MAP_VIEWER_TYPE
bool clip(AW_pos x0, AW_pos y0, AW_pos x1, AW_pos y1, AW_pos &x0out, AW_pos &y0out, AW_pos &x1out, AW_pos &y1out)
AW_awar * set_minmax(float min, float max)
Definition: AW_awar.cxx:530
virtual AW::Rectangle get_bounding_box() const =0
#define AWAR_TREE_RECOMPUTE
NOT4PERL long * GBT_readOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value)
Definition: adtools.cxx:402
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:203
static void markerThresholdChanged_cb(AW_root *root, bool partChanged)
Angle orientation
AW_root * awr
Definition: canvas.hxx:348
const char * title
Definition: readseq.c:22
#define RULER_SIZE
Definition: TreeDisplay.cxx:41
void erase()
Definition: arb_strbuf.h:141
void forget_auto_unfolded()
AP_tree_members gr
Definition: AP_Tree.hxx:214
void AW_POPDOWN(AW_window *window)
Definition: AW_window.cxx:52
#define AWAR_DTREE_BOOTSTRAP_STYLE
Definition: TreeDisplay.hxx:62
void set_standard_default_padding()
Definition: canvas.hxx:187
void TREE_auto_jump_cb(UNFIXED, TREE_canvas *ntw, AP_tree_jump_reason cause)
unsigned long AW_rgb
Definition: aw_base.hxx:51
AWT_COMMAND_MODE
Definition: canvas.hxx:35
ARB_edge previous() const
Definition: TreeNode.h:882
void dislocate_selected_group()
void NT_reinit_treetype(UNFIXED, TREE_canvas *ntw)
GB_ERROR cantMoveNextTo(AP_tree *new_brother)
Definition: AP_Tree.cxx:340
const AW_clicked_element * best_click(AW_device_click::ClickPreference prefer=AW_device_click::PREFER_NEARER)
Definition: canvas.hxx:243
void cat(const char *from)
Definition: arb_strbuf.h:204
long mark_species_in_tree(AP_tree *at, int mark)
void TREE_GC_changed_cb(GcChange whatChanged, AWT_canvas *ntw)
virtual int get_available_fontsizes(AW_font font_nr, int *available_sizes) const =0
bool tree_has_marks(AP_tree *at)
virtual int to_GC(const ShadedValue &val) const =0
GBDATA * get_gbmain() const
ScaleMode
bool polygon(int gc, AW::FillStyle filled, int npoints, const AW_pos *points, AW_bitset filteri=AW_ALL_DEVICES_SCALED)
Definition: aw_device.hxx:500
void request_refresh()
Definition: canvas.hxx:163
bool at_node(AP_tree *test) const
Definition: Group.hxx:66
bool is_logically_zoomed()
double getMarkRate(size_t markerIdx) const
static void tree_change_ignore_cb(AWT_graphic_tree *)
AW_MouseButton button() const
Definition: canvas.hxx:233
bool nearlyZero(const double &val)
Definition: aw_position.hxx:76
const char * count
#define AWAR_DTREE_GROUP_MARKED_THRESHOLD
Definition: TreeDisplay.hxx:75
static const char * suffixed_title_for_index(const char *primary_title, int index)
void add(const NodeMarkers &other)
bool is_valid() const
Definition: Group.hxx:48
ARB_edge rootEdge(TreeRoot *root)
Definition: TreeNode.h:965
Vector & sety(const double &Y)
AW_gc_manager * AW_manage_GC(AW_window *aww, const char *gc_base_name, AW_device *device, int base_drag, AW_GCM_AREA area, const GcChangedCallback &changecb, const char *default_background_color,...)
Definition: AW_preset.cxx:971
static void bootstrap_range_changed_cb(AW_root *awr, TREE_canvas *ntw, int upper_changed)
unsigned int dont_scroll
Definition: canvas.hxx:140
bool needs_structure_update() const
Definition: canvas.hxx:172
#define ARRAY_ELEMS(array)
Definition: arb_defs.h:19
const float LENGTH
Definition: AP_Tree.hxx:142
double discrete_value(double analog_value, int discretion_factor)
bool hasName(const char *Name) const
Definition: AP_Tree.hxx:374
#define AW_FONTINFO_CHAR_ASCII_MIN
Definition: aw_common.hxx:26
const double & cos() const
double len
GBDATA * GB_get_father(GBDATA *gbd)
Definition: arbdb.cxx:1722
void update_option_menu()
static void nocb()
Definition: TreeDisplay.cxx:68
AW_gc_manager * init_devices(AW_window *, AW_device *, AWT_canvas *ntw) OVERRIDE
Definition: TreeDisplay.cxx:71
const Vector & normal() const
GroupOrientation
void AW_init_color_group_defaults(const char *for_program)
Definition: AW_preset.cxx:1115
#define NT_DIAMOND_RADIUS
Definition: TreeDisplay.hxx:81
GBT_LEN leftlen
Definition: TreeNode.h:224
TreeNode * rightson
Definition: TreeNode.h:223
#define DOWNCAST(totype, expr)
Definition: downcast.h:141
#define M_PI
#define FINAL_OVERRIDE
Definition: cxxforward.h:114
#define AWAR_DTREE_AUTO_UNFOLD
Definition: TreeDisplay.hxx:51
BootstrapStyle style
RoughDirection
void diamond(int gc, const AW::Position &pos, int pixel_radius)
AP_UPDATE_FLAGS
Definition: AP_Tree.hxx:28
AW_awar * add_callback(const RootCallback &cb)
Definition: AW_awar.cxx:231
GB_ERROR GB_delete(GBDATA *&source)
Definition: arbdb.cxx:1916
struct Unfixed_cb_parameter * UNFIXED
Definition: cb_base.h:15
void flush_cache()
int get_gc() const
void new_gc(int gc)
Definition: aw_common.hxx:265
void(* AD_map_viewer_cb)(GBDATA *gbd, AD_MAP_VIEWER_TYPE type)
const AW::Position & get_pos() const
const NodeMarkers * read_cache(const AP_tree *at) const
Vector & set_length(double new_length)
bool flags_writeable() const
Definition: canvas.hxx:148
const AW_bitset AW_TRACK
Definition: aw_device.hxx:42
ARB_edge next() const
Definition: TreeNode.h:871
static HelixNrInfo * start
void set_tree_style(AP_tree_display_style style, AWT_canvas *ntw)
GBT_RemarkType parse_remark(const char *remark, double &bootstrap)
Definition: TreeNode.h:136
const Vector ZeroVector
CONSTEXPR_INLINE bool is_nan_or_inf(const T &n)
Definition: arbtools.h:178
#define AWAR_DTREE_RADIAL_ZOOM_TEXT
Definition: TreeDisplay.hxx:69
void auto_unfold(AP_tree *want_visible)
const AW_font_limits & get_font_limits(int gc, char c) const
Definition: AW_device.cxx:399
AP_tree_set::const_iterator AP_tree_set_citer
Definition: AP_TreeSet.hxx:23
void set_linewidth_recursive(int width)
Definition: AP_Tree.cxx:1603
char * text
void set_root()
Definition: TreeNode.h:944
const AW_bitset AW_PRINTER
Definition: aw_device.hxx:39
#define AW_PLANAR_COLORS
MarkerPosition(AW_pos scale, int markers_, double y1_, double y2_)
bool does_shade() const
bool is_above_clip(double ypos) const
Definition: aw_device.hxx:211
const Position & start() const
void reset_style()
Definition: AW_device.cxx:477
void set_angle(float angle)
Definition: AP_Tree.hxx:332
uchar flags
Definition: probe_tree.h:38
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:342
bool invisible(const AW::Position &pos, AW_bitset filteri=AW_ALL_DEVICES_SCALED)
Definition: aw_device.hxx:467
bool needs_save() const
Definition: canvas.hxx:173
static int diff(int v1, int v2, int v3, int v4, int st, int en)
Definition: ClustalV.cxx:534
double AW_pos
Definition: aw_base.hxx:29
GBDATA * GB_create_container(GBDATA *father, const char *key)
Definition: arbdb.cxx:1829
AW_window * TREE_create_marker_settings_window(AW_root *root)
void drag_target_detection(bool detect)
Definition: canvas.hxx:281
WindowCallback makeHelpCallback(const char *helpfile)
Definition: aw_window.hxx:106
#define TEST_EXPECT(cond)
Definition: test_unit.h:1328
Definition: arbdb.h:67
bool is_numeric
void collect_enclosing_groups(AP_tree *node, AP_tree_set &groups)
Definition: AP_TreeSet.cxx:15
#define AWAR_DTREE_DENDRO_XPAD
Definition: TreeDisplay.hxx:73
AW_bitset get_filter() const
Definition: aw_device.hxx:383
virtual void read_tree_settings()
bool somehow() const
Definition: aw_position.hxx:70
char * ARB_strduplen(const char *p, unsigned len)
Definition: arb_string.h:33
GB_TYPES GB_read_type(GBDATA *gbd)
Definition: arbdb.cxx:1643
void compute_tree() FINAL_OVERRIDE
Definition: AP_Tree.cxx:878
#define AWAR_DTREE_RADIAL_XPAD
Definition: TreeDisplay.hxx:70
const Position & startpos() const
Vector & rotate270deg()
const char * get_tree_name() const
Definition: ARB_Tree.hxx:85
int get_string_size(int gc, long textlen) const
Definition: AW_device.cxx:443
char key_char() const
Definition: canvas.hxx:237
virtual const char * get_marker_name(int markerIdx) const =0
Scaler(const Position &start, double unscale_, AWT_graphic_exports &exports_)
bool is_son_of_root() const
Definition: TreeNode.h:307
double surface() const
GBDATA * get_gb_tree() const
Definition: ARB_Tree.hxx:82
int get_linewidth() const
Definition: AP_Tree.hxx:326
virtual void abort()=0
const TreeNode * ancestor_common_with(const TreeNode *other) const
Definition: TreeNode.cxx:901
bool circle(int gc, AW::FillStyle filled, const AW::Position &center, const AW::Vector &radius, AW_bitset filteri=AW_ALL_DEVICES_SCALED)
Definition: aw_device.hxx:481
const float SPREAD
Definition: AP_Tree.hxx:139
AW_awar * awar_float(const char *var_name, float default_value=0.0, AW_default default_file=AW_ROOT_DEFAULT)
Definition: AW_root.cxx:612
AW_MouseButton
Definition: aw_window.hxx:70
const double & ypos() const
#define AWAR_DTREE_BOOTSTRAP_MIN
Definition: TreeDisplay.hxx:59
const char * get_gc_base_name() const
Definition: canvas.hxx:401
unsigned name_len
#define AWAR_DTREE_CIRCLE_ELLIPSE
Definition: TreeDisplay.hxx:65
Angle & rotate270deg()
AWT_fit_mode fit_mode
Definition: canvas.hxx:138
ValueCounter< double > Distance
Definition: AP_Tree.cxx:1317
bool is_shaded() const
Definition: aw_position.hxx:68
Dragged(AWT_graphic_exports &exports_)
int GBT_get_color_group(GBDATA *gb_item)
Definition: ad_colorset.cxx:16
AW_pos get_unscale() const
Definition: aw_device.hxx:125
bool is_located() const
Definition: Group.hxx:49
AP_tree_set::iterator AP_tree_set_iter
Definition: AP_TreeSet.hxx:22
ARB_edge leafEdge(TreeNode *leaf)
Definition: TreeNode.h:960
static int group[MAXN+1]
Definition: ClustalV.cxx:65
static int marker_width
#define AWAR_DTREE_GROUPCOUNTMODE
Definition: TreeDisplay.hxx:56
double height() const
#define false
Definition: ureadseq.h:13
static void TREE_resize_cb(UNFIXED, TREE_canvas *ntw)
void fake_AW_init_color_groups()
#define AWAR_DTREE_GROUPINFOPOS
Definition: TreeDisplay.hxx:57
size_t len
void set_char_size(int i, int ascent, int descent, int width)
Definition: aw_common.hxx:104
#define AWAR_DTREE_BOOTSTRAP_SHOW
Definition: TreeDisplay.hxx:61
bool line(int gc, const AW::LineVector &Line, AW_bitset filteri=AW_ALL_DEVICES_SCALED)
Definition: aw_device.hxx:430
GBDATA * get_species() const
void request_save_and_zoom_reset()
Definition: canvas.hxx:169
void touch()
Definition: AW_awar.cxx:207
#define AWAR_DTREE_ATTACH_GROUP
Definition: TreeDisplay.hxx:46
AW::Vector rtransform(const AW::Vector &vec) const
Definition: aw_device.hxx:145
TreeNode * father
Definition: TreeNode.h:223
static void error(const char *msg)
Definition: mkptypes.cxx:96
const char * get_text(size_t p, size_t &len) const
const AW_bitset AW_CLICK_DROP
Definition: aw_device.hxx:36
GBDATA * GB_get_root(GBDATA *gbd)
Definition: arbdb.cxx:1740
const Position & endpoint() const
#define AWAR_TREE_REFRESH
bool rest_tree_has_marks(AP_tree *at)
Vector scaling(const Position &current) const
unsigned percentMarked(const AP_tree_members &gr)
const Position & upper_left_corner() const
GBDATA * GBT_next_marked_species(GBDATA *gb_species)
Definition: aditem.cxx:116
CONSTEXPR_INLINE_Cxx14 void swap(unsigned char &c1, unsigned char &c2)
Definition: ad_io_inline.h:19
GBDATA * GB_searchOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value)
Definition: adquery.cxx:569
void label(const char *label)
Definition: AW_window.cxx:111
GB_ERROR loadFromDB(const char *name) FINAL_OVERRIDE
Definition: AP_Tree.cxx:893
bool is_keeled_group() const
Definition: TreeNode.h:542
void movey(const double &Y)
void unkeelGroup()
Definition: TreeNode.h:525
float GB_read_float(GBDATA *gbd)
Definition: arbdb.cxx:744
static bool command_on_GBDATA(GBDATA *gbd, const AWT_graphic_event &event, AD_map_viewer_cb map_viewer_cb)
void hide_drag_indicator(AW_device *device, int drag_gc) const
double scaled_remark_ascend
#define AWAR_DTREE_SHOW_BRACKETS
Definition: TreeDisplay.hxx:52
TreeNode * get_brother()
Definition: TreeNode.h:489
#define STANDARD_PADDING
Definition: canvas.hxx:80
GB_write_int const char GB_write_autoconv_string WRITE_SKELETON(write_pointer, GBDATA *,"%p", GB_write_pointer) char *AW_awa if)(!gb_var) return strdup("")
Definition: AW_awar.cxx:163
#define AWAR_SPECIES_NAME
#define TEST_EXPECT_ZERO_OR_SHOW_ERRNO(iocond)
Definition: test_unit.h:1090
const double & y() const
group_scaling groupScale
CollapseMode
void handle_command(AW_device *device, AWT_graphic_event &event) OVERRIDE
virtual AW_DEVICE_TYPE type()=0
~AWT_graphic_tree() OVERRIDE
AWT_command_data * get_command_data()
void setx(const double &X)
int size() const
void AWT_expose_cb(UNFIXED, AWT_canvas *scr)
Definition: canvas.cxx:384
virtual int indicate_selected(AW_device *d, int gc) const =0
bool operator==(const ClickedElement &other) const
double get_ypos() const
int GB_read_flag(GBDATA *gbd)
Definition: arbdb.cxx:2796
bool is_inside_folded_group() const
Definition: AP_Tree.cxx:1642
char * read_string() const
Definition: AW_awar.cxx:198
AP_tree_nlen * rootNode()
Definition: ap_main.hxx:54
void uninstall_tree_changed_callback()
GBDATA * GB_followingEntry(GBDATA *entry, size_t skip_over)
Definition: adquery.cxx:344
void fast_sync_changed_folding(AP_tree *parent_of_all_changes)
const AW_bitset AW_CLICK
Definition: aw_device.hxx:35
TreeNode * leftson
Definition: TreeNode.h:223
AW_awar * awar(const char *awar)
Definition: AW_root.cxx:606
void movex(const double &X)
virtual void retrieve_marker_state(const char *speciesName, NodeMarkers &matches)=0
GBT_RemarkType
Definition: arbdbt.h:30
#define RUNNING_TEST()
Definition: arb_assert.h:278
void toggle_group(AP_tree *at)
GBDATA * GB_following_marked(GBDATA *gbd, const char *keystring, size_t skip_over)
Definition: adquery.cxx:663
Definition: arbdb.h:86
BranchRotator(AW_device *device_, AP_tree *node_, const LineVector &clicked_branch_, const Position &mousepos, AWT_graphic_exports &exports_)
GBT_LEN rightlen
Definition: TreeNode.h:224
#define AWAR_DTREE_ATTACH_LEN
Definition: TreeDisplay.hxx:45
NOT4PERL float * GBT_readOrCreate_float(GBDATA *gb_container, const char *fieldpath, float default_value)
Definition: adtools.cxx:422
Vector orthogonal_projection(const Vector &projectee, const Vector &target)
#define DEFAULT_RULER_LENGTH
Definition: TreeDisplay.cxx:44
#define AWAR_DTREE_PARTIAL_GREYLEVEL
Definition: TreeDisplay.hxx:78
char * gone_tree_name
Definition: AP_Tree.hxx:93
GB_ERROR GB_write_float(GBDATA *gbd, float f)
Definition: arbdb.cxx:1281
void show_ruler(AW_device *device, int gc)
int draw_branch_line(int gc, const AW::Position &root, const AW::Position &leaf, AW_bitset filter)
bool is_ancestor_of(const TreeNode *descendant) const
Definition: TreeNode.h:293
BranchLinewidthScaler(AP_tree *node_, const Position &start, bool wholeSubtree_, AWT_graphic_exports &exports_)
static const int LABEL_WIDTH
static AW_window * create_tree_bootstrap_settings_window(AW_root *aw_root)
GB_ERROR GB_write_int(GBDATA *gbd, long i)
Definition: arbdb.cxx:1250
void create_autosize_button(const char *macro_name, AW_label label, const char *mnemonic=NULp, unsigned xtraSpace=1)
Definition: AW_button.cxx:421
bool is_list_style(AP_tree_display_style style)
const AW::LineVector & get_line() const
double pow
Definition: AP_Tree.hxx:63
float read_float() const
Definition: AW_awar.cxx:177
Vector size() const
static void text_near_head(AW_device *device, int gc, const LineVector &line, const char *text)
double offset() const
bool group_tree(AP_tree *at, CollapseMode mode, int color_group)
const AW_clicked_element * source_element() const
AW_pos b
Definition: aw_base.hxx:32
void insert_default_option(AW_label choice_label, const char *mnemonic, const char *var_value, const char *name_of_color=NULp)
void write_cache(const AP_tree *at, const NodeMarkers &markers)
const Vector & line_vector() const
const Column & get_column(size_t p) const
GB_ERROR saveToDB() OVERRIDE
Definition: AP_Tree.cxx:538
void reorder_tree(TreeOrder mode)
Definition: TreeNode.cxx:159
long int flag
Definition: f2c.h:39
const AW_clicked_element * element() const
void display_node_remark(AW_device *device, const AP_tree *at, const AW::Position &center, double blen, double bdist, AW::RoughDirection textArea) const
void set_default_padding(int t, int b, int l, int r)
Definition: canvas.hxx:177
TreeNode * dest() const
Definition: TreeNode.h:835
int aw_question(const char *unique_id, const char *question, const char *buttons, bool sameSizeButtons, const char *helpfile)
Definition: AW_question.cxx:26
AWT_graphic * gfx
Definition: canvas.hxx:349
#define AWAR_DTREE_GROUP_DOWNSCALE
Definition: TreeDisplay.hxx:47
Definition: arbdb.h:63
const Angle Eastwards
const Group & get_selected_group() const
static void copy(double **i, double **j)
Definition: trnsprob.cxx:32
bool is_edge_to_leaf() const
Definition: TreeNode.h:931
bool locate(AP_tree *subtree) const
Definition: Group.cxx:29
BranchScaler(ScaleMode mode_, AP_tree *node_, const LineVector &branch_, const Position &attach_, const Position &start, double unscale_, bool discrete, bool allow_neg_values_, AWT_graphic_exports &exports_)
void init(const char *text_, AW_device &device, int gc)
bool is_leaf() const
Definition: TreeNode.h:263
void set_branchlength_preserving(GBT_LEN new_len)
Definition: TreeNode.cxx:598
AW_awar * awar_int(const char *var_name, long default_value=0, AW_default default_file=AW_ROOT_DEFAULT)
Definition: AW_root.cxx:632
const double & length() const
void AW_displayColorRange(AW_device *device, int first_range_gc, AW::Position start, AW_pos xsize, AW_pos ysize)
Definition: AW_preset.cxx:1386
#define AWAR_DTREE_GROUP_ORIENT
Definition: TreeDisplay.hxx:54
BootstrapStyle
double centerx(int markerIdx) const
static void insert_section_header(AW_window *aws, const char *title)
xml element
GB_ERROR close(GB_ERROR error)
Definition: arbdbpp.cxx:35
AWT_graphic_exports & get_exports()
void GB_write_flag(GBDATA *gbd, long flag)
Definition: arbdb.cxx:2773
GBDATA * GB_searchOrCreate_float(GBDATA *gb_container, const char *fieldpath, float default_value)
Definition: adquery.cxx:591
void display_remark(AW_device *device, const char *remark, const AW::Position &center, double blen, double bdist, const AW::Position &textpos, AW_pos alignment) const
long AW_CL
Definition: cb.h:21
int markerCount(size_t markerIdx) const
void set_fg_color(AW_rgb col)
Definition: aw_common.hxx:152
void draw_scale_indicator(const AW::Position &, AW_device *, int) const OVERRIDE
bool is_inside(const TreeNode *subtree) const
Definition: TreeNode.h:290
BranchStyle
void request_structure_update()
Definition: canvas.hxx:375
static AWT_config_mapping_def tree_setting_config_mapping[]
void update_timers()
Definition: AP_Tree.cxx:125
AW_key_code
Definition: aw_keysym.hxx:14
#define NT_BOX_WIDTH
Definition: TreeDisplay.hxx:80
GroupInfoMode
#define OVERRIDE
Definition: cxxforward.h:112
void GB_touch(GBDATA *gbd)
Definition: arbdb.cxx:2802
void draw_drag_indicator(AW_device *device, int drag_gc) const FINAL_OVERRIDE
AWT_COMMAND_MODE cmd() const
Definition: canvas.hxx:232
double get_print_width(size_t p) const
GroupStyle
unsigned count_len
#define RULER_LINEWIDTH
Definition: TreeDisplay.cxx:40
AP_tree_display_style
Definition: TreeDisplay.hxx:87
BranchMover(const AW_clicked_element *dragFrom, AW_MouseButton button_, AWT_graphic_tree &agt_)
#define AWAR_DTREE_GREY_LEVEL
Definition: TreeDisplay.hxx:55
char * name
Definition: TreeNode.h:226
void nprintf(size_t maxlen, const char *templat,...) __ATTR__FORMAT_MEMBER(2)
Definition: arb_strbuf.cxx:29
double get_rel_attach() const
Position pos(int markerIdx) const
void filled_box(int gc, const AW::Position &pos, int pixel_width)
void set_min(float val)
#define AWAR_DTREE_CIRCLE_FILL
Definition: TreeDisplay.hxx:64
void do_drag(const AW_clicked_element *drag_target, const Position &mousepos)
#define AWAR_DTREE_DENDRO_ZOOM_TEXT
Definition: TreeDisplay.hxx:72
void set_line_attributes_for(AP_tree *at) const
AP_tree * get_root_node()
double leftx(int markerIdx) const
CollapseMode next_collapse_mode() const
AWT_graphic_exports exports
Definition: canvas.hxx:259
double linear
Definition: AP_Tree.hxx:62
virtual ShadedValue calc_shaded_leaf_GC(GBDATA *gb_node) const =0
RulerScaler(const Position &start, double unscale_, const DB_scalable &xs, const DB_scalable &ys, AWT_graphic_exports &exports_)
static void tree_changed_cb(AW_root *, BranchWindow *bw)
void reset()
Definition: AW_device.cxx:280
const int MARKER_COLORS
Definition: TreeDisplay.cxx:48
GBDATA * GBT_first_species(GBDATA *gb_main)
Definition: aditem.cxx:124
TreeOrder
Definition: TreeNode.h:35
static AW_window * create_tree_expert_settings_window(AW_root *aw_root)
const AW_bitset AW_SIZE_UNSCALED
Definition: aw_device.hxx:38
AW_window * TREE_create_settings_window(AW_root *aw_root)
const AW_bitset text_filter
Position head() const
void set_remark(const char *newRemark)
Definition: TreeNode.h:372
void draw_drag_indicator(AW_device *IF_DEBUG(same_device), int drag_gc) const OVERRIDE
const char * ruler_awar(const char *name)
#define TEST_EXPECT_NO_ERROR(call)
Definition: test_unit.h:1118
void aw_message(const char *msg)
Definition: AW_status.cxx:1142
AW_option_menu_struct * create_option_menu(const char *awar_name)
bool was_displayed() const
const AW_clicked_element * dest_element() const
GBDATA * GB_find_string(GBDATA *gbd, const char *key, const char *str, GB_CASE case_sens, GB_SEARCH_TYPE gbs)
Definition: adquery.cxx:302
AW_event_type type() const
Definition: canvas.hxx:239
float GBT_LEN
Definition: arbdb_base.h:34
virtual void perform(DragAction action, const AW_clicked_element *target, const Position &mousepos)=0
const char LINEWIDTH
Definition: AP_Tree.hxx:141
static int MarkerGC[MARKER_COLORS]
Definition: TreeDisplay.cxx:49
const double & xpos() const
static int line
Definition: arb_a2ps.c:296
GBDATA * GBT_next_species(GBDATA *gb_species)
Definition: aditem.cxx:128
#define NULp
Definition: cxxforward.h:116
AWT_graphic_tree(AW_root *aw_root, GBDATA *gb_main, AD_map_viewer_cb map_viewer_cb)
GB_ERROR GBT_write_name_to_groupData(GBDATA *gb_group, bool createNameEntry, const char *new_group_name, bool pedantic)
Definition: adtree.cxx:354
void set_function(AW_function mode)
Definition: aw_common.hxx:154
AW_key_mod
Definition: aw_keysym.hxx:46
GBDATA * GBT_find_species(GBDATA *gb_main, const char *name)
Definition: aditem.cxx:139
CollapseMode next_expand_mode() const
bool operator!=(const ClickedElement &other) const
GroupCountMode
double width() const
ARB_edge find_innermost_edge()
Definition: TreeNode.cxx:397
GroupInfoPosition
virtual void handle_click(int markerIdx, AW_MouseButton button, AWT_graphic_exports &exports)=0
#define DEFAULT_RULER_LINEWIDTH
Definition: TreeDisplay.cxx:43
void GBT_split_string(ConstStrArray &dest, const char *namelist, const char *separator, SplitMode mode)
Definition: arb_strarray.h:223
#define offset(field)
Definition: GLwDrawA.c:73
static GBDATA * brute_force_find_next_species(GBDATA *gb_main, GBDATA *gb_sel, bool marked_only, bool upwards)
AP_tree * at
AW_function
Definition: aw_device.hxx:283
const char * get_data() const
Definition: arb_strbuf.h:120
bool is_tree_style(AP_tree_display_style style)
void init(AliView *aliview, AP_sequence *seq_prototype, bool link_to_database_, bool insert_delete_cbs)
GB_TYPES
Definition: arbdb.h:62
#define AW_NO_COLOR
Definition: aw_base.hxx:50
GBDATA * data()
#define FALLTHROUGH
Definition: cxxforward.h:136
TreeNode * keelTarget()
Definition: TreeNode.h:515
MarkerIdentifier(const AW_clicked_element *marker_, const Position &start, const char *name_, AWT_graphic_exports &exports_)
static void AWT_graphic_tree_root_changed(void *cd, AP_tree *old, AP_tree *newroot)
GB_transaction ta(gb_var)
GB_ERROR GBT_write_int(GBDATA *gb_container, const char *fieldpath, long content)
Definition: adtools.cxx:471
static const AP_TreeShader * get_tree_shader()
Definition: AP_Tree.hxx:372
bool box(int gc, AW::FillStyle filled, const AW::Rectangle &rect, AW_bitset filteri=AW_ALL_DEVICES_SCALED)
Definition: aw_device.hxx:471
void install_tree_changed_callback(const GraphicTreeCallback &gtcb)
#define AWAR_DTREE_VERICAL_DIST
Definition: TreeDisplay.hxx:42
bool shall_show_remark_for(const TreeNode *node) const
void update_empty_branch_behavior(const TreeRoot *troot)
void destroy(TreeNode *that)
Definition: TreeNode.h:667
static GraphicTreeCallback treeChangeIgnore_cb
AWT_zoom_mode zoom_mode
Definition: canvas.hxx:137
DragNDrop(const AW_clicked_element *dragFrom, AWT_graphic_exports &exports_)
#define AWAR_EXPERT
GBDATA * gb_node
Definition: TreeNode.h:225
GBDATA * gb_main
Definition: adname.cxx:32
void set_consider_text_for_zoom_reset(bool consider)
Definition: canvas.hxx:388
GB_ERROR write_float(float aw_float)
void set_linewidth(int width)
Definition: AP_Tree.hxx:327
void empty_box(int gc, const AW::Position &pos, int pixel_width)
#define IF_DEBUG(x)
Definition: arb_assert.h:303
GBDATA * GB_search(GBDATA *gbd, const char *fieldpath, GB_TYPES create)
Definition: adquery.cxx:531
#define AWAR_DTREE_ATTACH_SIZE
Definition: TreeDisplay.hxx:44
int calc_leaf_GC(GBDATA *gb_node, bool is_marked) const
GB_CSTR GBT_get_name_or_description(GBDATA *gb_item)
Definition: aditem.cxx:459
bool is_cursor_keycode(AW_key_code kcode)
const char * get_remark() const
Definition: TreeNode.h:357
void set_root_changed_callback(AP_rootChangedCb cb, void *cd)
Definition: AP_Tree.cxx:250
char * aw_input(const char *title, const char *prompt, const char *default_input)
Definition: AW_modal.cxx:252
const TreeNode * find_parent_clade() const
Definition: TreeNode.h:582
size_t length
#define NONAN(d)
Definition: aw_position.hxx:46
static int info[maxsites+1]
const Position Origin
#define TEST_EXPECT_TEXTFILES_EQUAL(fgot, fwant)
Definition: test_unit.h:1424
static void AWT_graphic_tree_node_deleted(void *cd, AP_tree *del)
uint32_t gc
Definition: AP_Tree.hxx:152
#define AWAR_DTREE_CIRCLE_ZOOM
Definition: TreeDisplay.hxx:66
double rtransform_pixelsize(int pixelsize) const
Definition: aw_device.hxx:138
GB_ERROR relink() __ATTR__USERESULT
Definition: AP_Tree.cxx:902
void at_newline()
Definition: AW_at.cxx:119
void do_scale(const Position &drag_pos) OVERRIDE
#define AWAR_DTREE_AUTO_JUMP
Definition: TreeDisplay.hxx:49
#define AWAR_DTREE_GROUP_PARTIALLY_MARKED_THRESHOLD
Definition: TreeDisplay.hxx:76
void TREE_create_awars(AW_root *aw_root, AW_default db)
AW_linestyle
Definition: aw_device.hxx:276
#define CONSTEXPR
Definition: cxxforward.h:108
bool cache_is_flushed() const
const char * get_group_name() const
Definition: TreeNode.h:553
#define AW_ROOT_DEFAULT
Definition: aw_base.hxx:106
bool is_lower_son() const
Definition: ARB_Tree.hxx:161
float max_tree_depth
Definition: AP_Tree.hxx:161
GB_ERROR write_int(long aw_int)
bool text(int gc, const SizedCstr &cstr, const AW::Position &pos, AW_pos alignment=0.0, AW_bitset filteri=AW_ALL_DEVICES_UNSCALED)
Definition: aw_device.hxx:440
Position calc_text_coordinates_near_tip(AW_device *device, int gc, const Position &pos, const Angle &orientation, AW_pos &alignment, double dist_factor=1.0)
const double & x() const
bool is_normal_group() const
Definition: TreeNode.h:537
void show(AW_device *device) OVERRIDE
GBDATA * GB_entry(GBDATA *father, const char *key)
Definition: adquery.cxx:334
void TREE_install_update_callbacks(TREE_canvas *ntw)
void init(const AW_font_limits &font_limits, double factor)
void mark_species_in_rest_of_tree(AP_tree *at, int mark)
#define AWAR_DTREE_MARKER_WIDTH
Definition: TreeDisplay.hxx:77
GBDATA * gb_tree_gone
Definition: AP_Tree.hxx:92
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:194
bool write(float val)
void GB_close(GBDATA *gbd)
Definition: arbdb.cxx:655
ClickedElement(const AW_clicked_element &e)
#define NT_SELECTED_WIDTH
Definition: TreeDisplay.hxx:83
GB_ERROR linkToDB_and_count(int *zombies, int *duplicates) __ATTR__USERESULT
Definition: ARB_Tree.cxx:130
virtual AP_UPDATE_FLAGS check_update()
Definition: AP_Tree.cxx:909
GBDATA * gb_main
Definition: canvas.hxx:346
const char * label
Position calc_text_coordinates_aside_line(AW_device *device, int gc, const Position &pos, Angle orientation, bool right, AW_pos &alignment, double dist_factor=1.0)
static int iteration_count(int leafs_in_tree)
Definition: TreeNode.h:917
void put(char c)
Definition: arb_strbuf.h:179
void request_resize()
Definition: canvas.hxx:373
Definition: arbdb.h:66
#define AWAR_DTREE_CIRCLE_SHOW
Definition: TreeDisplay.hxx:63
void apply_zoom_settings_for_treetype(AWT_canvas *ntw)
GBDATA * GBT_get_species_data(GBDATA *gb_main)
Definition: aditem.cxx:105
void set_discretion_factor(int df)
#define max(a, b)
Definition: f2c.h:154
GB_write_int const char s
Definition: AW_awar.cxx:154