| 1 | // =============================================================== // |
|---|
| 2 | // // |
|---|
| 3 | // File : TreeDisplay.hxx // |
|---|
| 4 | // Purpose : // |
|---|
| 5 | // // |
|---|
| 6 | // Institute of Microbiology (Technical University Munich) // |
|---|
| 7 | // http://www.arb-home.de/ // |
|---|
| 8 | // // |
|---|
| 9 | // =============================================================== // |
|---|
| 10 | |
|---|
| 11 | #ifndef TREEDISPLAY_HXX |
|---|
| 12 | #define TREEDISPLAY_HXX |
|---|
| 13 | |
|---|
| 14 | #ifndef AP_TREE_HXX |
|---|
| 15 | #include <AP_Tree.hxx> |
|---|
| 16 | #endif |
|---|
| 17 | #ifndef GROUP_HXX |
|---|
| 18 | #include <Group.hxx> |
|---|
| 19 | #endif |
|---|
| 20 | #ifndef AWT_CANVAS_HXX |
|---|
| 21 | #include <awt_canvas.hxx> |
|---|
| 22 | #endif |
|---|
| 23 | #ifndef _GLIBCXX_VECTOR |
|---|
| 24 | #include <vector> |
|---|
| 25 | #endif |
|---|
| 26 | #ifndef _GLIBCXX_MAP |
|---|
| 27 | #include <map> |
|---|
| 28 | #endif |
|---|
| 29 | |
|---|
| 30 | // Increase stacklimit to 20Mb (before was ~8Mb on my system). |
|---|
| 31 | // This was done to fix #824 (crash on display of huge trees). |
|---|
| 32 | #define TREEDISP_STACKSIZE (1024L*1024L*20) // stacksize required for tree-display |
|---|
| 33 | |
|---|
| 34 | #define td_assert(cond) arb_assert(cond) |
|---|
| 35 | |
|---|
| 36 | #define AWAR_DTREE_BASELINEWIDTH "awt/dtree/baselinewidth" |
|---|
| 37 | #define AWAR_DTREE_VERICAL_DIST "awt/dtree/verticaldist" |
|---|
| 38 | #define AWAR_DTREE_BRANCH_STYLE "awt/dtree/branch_style" |
|---|
| 39 | #define AWAR_DTREE_ATTACH_SIZE "awt/dtree/attach_size" |
|---|
| 40 | #define AWAR_DTREE_ATTACH_LEN "awt/dtree/attach_len" |
|---|
| 41 | #define AWAR_DTREE_ATTACH_GROUP "awt/dtree/attach_group" |
|---|
| 42 | #define AWAR_DTREE_GROUP_DOWNSCALE "awt/dtree/downscaling" |
|---|
| 43 | #define AWAR_DTREE_GROUP_SCALE "awt/dtree/groupscaling" |
|---|
| 44 | #define AWAR_DTREE_AUTO_JUMP "awt/dtree/autojump" |
|---|
| 45 | #define AWAR_DTREE_AUTO_JUMP_TREE "awt/dtree/autojump_tree" |
|---|
| 46 | #define AWAR_DTREE_AUTO_UNFOLD "awt/dtree/auto_unfold" |
|---|
| 47 | #define AWAR_DTREE_SHOW_BRACKETS "awt/dtree/show_brackets" |
|---|
| 48 | #define AWAR_DTREE_GROUP_STYLE "awt/dtree/groupstyle" |
|---|
| 49 | #define AWAR_DTREE_GROUP_ORIENT "awt/dtree/grouporient" |
|---|
| 50 | #define AWAR_DTREE_GREY_LEVEL "awt/dtree/greylevel" |
|---|
| 51 | #define AWAR_DTREE_GROUPCOUNTMODE "awt/dtree/groupcountmode" |
|---|
| 52 | #define AWAR_DTREE_GROUPINFOPOS "awt/dtree/groupinfopos" |
|---|
| 53 | |
|---|
| 54 | #define AWAR_DTREE_BOOTSTRAP_MIN "awt/dtree/bootstrap/min" |
|---|
| 55 | #define AWAR_DTREE_BOOTSTRAP_MAX "awt/dtree/bootstrap/max" |
|---|
| 56 | #define AWAR_DTREE_BOOTSTRAP_SHOW "awt/dtree/bootstrap/show" |
|---|
| 57 | #define AWAR_DTREE_BOOTSTRAP_STYLE "awt/dtree/bootstrap/style" |
|---|
| 58 | #define AWAR_DTREE_CIRCLE_SHOW "awt/dtree/bootstrap/circle/show" |
|---|
| 59 | #define AWAR_DTREE_CIRCLE_FILL "awt/dtree/bootstrap/circle/fill" |
|---|
| 60 | #define AWAR_DTREE_CIRCLE_ELLIPSE "awt/dtree/bootstrap/circle/ellipse" |
|---|
| 61 | #define AWAR_DTREE_CIRCLE_ZOOM "awt/dtree/bootstrap/circle/zoom" |
|---|
| 62 | #define AWAR_DTREE_CIRCLE_LIMIT "awt/dtree/bootstrap/circle/limit" |
|---|
| 63 | |
|---|
| 64 | #define AWAR_DTREE_RADIAL_ZOOM_TEXT "awt/dtree/radial/zoomtext" |
|---|
| 65 | #define AWAR_DTREE_RADIAL_XPAD "awt/dtree/radial/xpadding" |
|---|
| 66 | |
|---|
| 67 | #define AWAR_DTREE_DENDRO_ZOOM_TEXT "awt/dtree/dendro/zoomtext" |
|---|
| 68 | #define AWAR_DTREE_DENDRO_XPAD "awt/dtree/dendro/xpadding" |
|---|
| 69 | |
|---|
| 70 | #define AWAR_DTREE_GROUP_MARKED_THRESHOLD "awt/dtree/markers/group_marked_threshold" |
|---|
| 71 | #define AWAR_DTREE_GROUP_PARTIALLY_MARKED_THRESHOLD "awt/dtree/markers/group_partially_marked_threshold" |
|---|
| 72 | #define AWAR_DTREE_MARKER_WIDTH "awt/dtree/markers/marker_width" |
|---|
| 73 | #define AWAR_DTREE_PARTIAL_GREYLEVEL "awt/dtree/markers/partial_greylevel" |
|---|
| 74 | |
|---|
| 75 | #define NT_BOX_WIDTH 7 // pixel |
|---|
| 76 | #define NT_DIAMOND_RADIUS 5 |
|---|
| 77 | #define NT_ROOT_WIDTH 9 |
|---|
| 78 | #define NT_SELECTED_WIDTH 11 |
|---|
| 79 | |
|---|
| 80 | #define AWT_TREE(ntw) (ntw)->get_graphic_tree() |
|---|
| 81 | |
|---|
| 82 | enum AP_tree_display_style { |
|---|
| 83 | AP_TREE_NORMAL, // normal tree display (dendrogram) |
|---|
| 84 | AP_TREE_RADIAL, // radial tree display |
|---|
| 85 | AP_TREE_IRS, // like AP_TREE_NORMAL, with folding line |
|---|
| 86 | AP_LIST_NDS, |
|---|
| 87 | AP_LIST_SIMPLE // simple display only showing name (used at startup to avoid NDS error messages) |
|---|
| 88 | }; |
|---|
| 89 | |
|---|
| 90 | enum AP_tree_jump_type { // bit-values |
|---|
| 91 | AP_JUMP_KEEP_VISIBLE = 1, // automatically make selected node visible (on changes) [VALUE STORED IN AWAR!] |
|---|
| 92 | AP_JUMP_LOGICAL_UNZOOM = 2, // adapts logical zoom (if target is outside zoomed subtree) |
|---|
| 93 | AP_JUMP_FORCE_VCENTER = 4, // force vertical centering (even if visible) |
|---|
| 94 | AP_JUMP_ALLOW_HCENTER = 8, // force horizontal centering (if vertically centered); only works together with AP_JUMP_FORCE_VCENTER |
|---|
| 95 | AP_JUMP_FORCE_HCENTER = 16, // force horizontal centering |
|---|
| 96 | AP_JUMP_BE_VERBOOSE = 32, // tell why nothing happened etc. |
|---|
| 97 | AP_JUMP_AUTO_UNFOLD = 64, // temporarily auto-unfold folded target node |
|---|
| 98 | |
|---|
| 99 | // convenience defs: |
|---|
| 100 | AP_DONT_JUMP = 0, // [VALUE STORED IN AWAR!] |
|---|
| 101 | AP_JUMP_SMART_CENTER = AP_JUMP_FORCE_VCENTER|AP_JUMP_ALLOW_HCENTER, // [VALUE STORED IN AWAR!] |
|---|
| 102 | AP_JUMP_FORCE_CENTER = AP_JUMP_FORCE_VCENTER|AP_JUMP_FORCE_HCENTER, // [VALUE STORED IN AWAR!] |
|---|
| 103 | |
|---|
| 104 | AP_JUMP_BY_BUTTON = AP_JUMP_SMART_CENTER|AP_JUMP_LOGICAL_UNZOOM|AP_JUMP_AUTO_UNFOLD|AP_JUMP_BE_VERBOOSE, |
|---|
| 105 | }; |
|---|
| 106 | |
|---|
| 107 | enum AP_tree_jump_reason { |
|---|
| 108 | AP_JUMP_REASON_TREE, |
|---|
| 109 | AP_JUMP_REASON_STYLE, |
|---|
| 110 | AP_JUMP_REASON_SPECIES, |
|---|
| 111 | AP_JUMP_REASON_GROUP, |
|---|
| 112 | }; |
|---|
| 113 | |
|---|
| 114 | enum ClickedType { |
|---|
| 115 | CL_NODE = 1, |
|---|
| 116 | CL_SPECIES, |
|---|
| 117 | CL_RULER, |
|---|
| 118 | CL_FLAG, |
|---|
| 119 | CL_BRANCH, |
|---|
| 120 | CL_ROOTNODE, |
|---|
| 121 | }; |
|---|
| 122 | |
|---|
| 123 | inline bool is_list_style(AP_tree_display_style style) { return style == AP_LIST_NDS || style == AP_LIST_SIMPLE; } |
|---|
| 124 | inline bool is_tree_style(AP_tree_display_style style) { return !is_list_style(style); } |
|---|
| 125 | |
|---|
| 126 | class NDS_Labeler; |
|---|
| 127 | class AWT_graphic_tree_group_state; |
|---|
| 128 | |
|---|
| 129 | struct AWT_scaled_font_limits { |
|---|
| 130 | double ascent; |
|---|
| 131 | double descent; |
|---|
| 132 | double height; |
|---|
| 133 | double width; |
|---|
| 134 | |
|---|
| 135 | void init(const AW_font_limits& font_limits, double factor) { |
|---|
| 136 | ascent = font_limits.ascent*factor; |
|---|
| 137 | descent = font_limits.descent*factor; |
|---|
| 138 | height = font_limits.get_height()*factor; |
|---|
| 139 | width = font_limits.width*factor; |
|---|
| 140 | } |
|---|
| 141 | }; |
|---|
| 142 | |
|---|
| 143 | enum AD_MAP_VIEWER_TYPE { |
|---|
| 144 | ADMVT_NONE = 0, |
|---|
| 145 | ADMVT_INFO, |
|---|
| 146 | ADMVT_WWW, |
|---|
| 147 | ADMVT_SELECT |
|---|
| 148 | }; |
|---|
| 149 | |
|---|
| 150 | typedef void (*AD_map_viewer_cb)(GBDATA *gbd, AD_MAP_VIEWER_TYPE type); |
|---|
| 151 | |
|---|
| 152 | struct DendroSubtreeLimits { |
|---|
| 153 | double y_branch; // ypos of branch to subtree |
|---|
| 154 | double y_top; // top ypos of whole subtree |
|---|
| 155 | double y_bot; // bottom ypos of whole subtree |
|---|
| 156 | double x_right; // rightmost xpos of whole subtree |
|---|
| 157 | |
|---|
| 158 | void combine(const DendroSubtreeLimits& other) { |
|---|
| 159 | y_top = std::min(y_top, other.y_top); |
|---|
| 160 | y_bot = std::max(y_bot, other.y_bot); |
|---|
| 161 | x_right = std::max(x_right, other.x_right); |
|---|
| 162 | } |
|---|
| 163 | }; |
|---|
| 164 | |
|---|
| 165 | struct AWT_command_data { |
|---|
| 166 | /*! any kind of data which has to be stored between different events (e.g. to support drag&drop) |
|---|
| 167 | * Purpose of this class is to allow to delete such data w/o knowing anything else. |
|---|
| 168 | */ |
|---|
| 169 | virtual ~AWT_command_data() {} |
|---|
| 170 | }; |
|---|
| 171 | |
|---|
| 172 | enum CollapseMode { |
|---|
| 173 | COLLAPSE_ALL = 0, |
|---|
| 174 | EXPAND_MARKED = 1, // do not collapse groups containing marked species |
|---|
| 175 | COLLAPSE_TERMINAL = 2, // do not collapse groups with subgroups |
|---|
| 176 | EXPAND_ALL = 4, |
|---|
| 177 | EXPAND_COLOR = 8, // do not collapse groups containing species with color == parameter 'color_group' (or any color if 'color_group' is -1) |
|---|
| 178 | EXPAND_ZOMBIES = 16, // do not collapse groups containing zombies |
|---|
| 179 | }; |
|---|
| 180 | |
|---|
| 181 | class NodeMarkers { |
|---|
| 182 | // represents markers at a node (species or group) |
|---|
| 183 | |
|---|
| 184 | int nodeSize; // number of species in group (or 1) |
|---|
| 185 | std::vector<int> mark; // how often each marker is set in group |
|---|
| 186 | public: |
|---|
| 187 | NodeMarkers() {} // default for cache |
|---|
| 188 | explicit NodeMarkers(int numMarks) |
|---|
| 189 | : nodeSize(0), |
|---|
| 190 | mark(numMarks, 0) |
|---|
| 191 | {} |
|---|
| 192 | |
|---|
| 193 | void incMarker(size_t markerIdx) { |
|---|
| 194 | td_assert(markerIdx<mark.size()); |
|---|
| 195 | mark[markerIdx]++; |
|---|
| 196 | } |
|---|
| 197 | int markerCount(size_t markerIdx) const { |
|---|
| 198 | td_assert(markerIdx<mark.size()); |
|---|
| 199 | return mark[markerIdx]; |
|---|
| 200 | } |
|---|
| 201 | |
|---|
| 202 | void incNodeSize() { nodeSize++; } |
|---|
| 203 | int getNodeSize() const { return nodeSize; } |
|---|
| 204 | |
|---|
| 205 | double getMarkRate(size_t markerIdx) const { return markerCount(markerIdx) / double(getNodeSize()); } |
|---|
| 206 | |
|---|
| 207 | void add(const NodeMarkers& other) { |
|---|
| 208 | size_t size = mark.size(); |
|---|
| 209 | td_assert(size == other.mark.size()); |
|---|
| 210 | for (size_t i = 0; i<size; ++i) { |
|---|
| 211 | mark[i] += other.mark[i]; |
|---|
| 212 | } |
|---|
| 213 | nodeSize += other.nodeSize; |
|---|
| 214 | } |
|---|
| 215 | }; |
|---|
| 216 | |
|---|
| 217 | class MarkerDisplay { |
|---|
| 218 | // defines which markers shall be displayed |
|---|
| 219 | |
|---|
| 220 | typedef std::map<const AP_tree*,NodeMarkers> GroupMarkerCache; |
|---|
| 221 | |
|---|
| 222 | GroupMarkerCache cache; |
|---|
| 223 | int numMarkers; |
|---|
| 224 | |
|---|
| 225 | public: |
|---|
| 226 | MarkerDisplay(int numMarkers_) |
|---|
| 227 | : numMarkers(numMarkers_) |
|---|
| 228 | { |
|---|
| 229 | td_assert(numMarkers>0); |
|---|
| 230 | } |
|---|
| 231 | virtual ~MarkerDisplay() {} |
|---|
| 232 | |
|---|
| 233 | virtual const char *get_marker_name(int markerIdx) const = 0; |
|---|
| 234 | virtual void retrieve_marker_state(const char *speciesName, NodeMarkers& matches) = 0; |
|---|
| 235 | virtual void handle_click(int markerIdx, AW_MouseButton button, AWT_graphic_exports& exports) = 0; |
|---|
| 236 | |
|---|
| 237 | const NodeMarkers *read_cache(const AP_tree *at) const { |
|---|
| 238 | GroupMarkerCache::const_iterator found = cache.find(at); |
|---|
| 239 | return found == cache.end() ? NULp : &found->second; |
|---|
| 240 | } |
|---|
| 241 | void write_cache(const AP_tree *at, const NodeMarkers& markers) { cache[at] = markers; } |
|---|
| 242 | void flush_cache() { cache.erase(cache.begin(), cache.end()); } |
|---|
| 243 | #if defined(ASSERTION_USED) |
|---|
| 244 | bool cache_is_flushed() const { return cache.empty(); } |
|---|
| 245 | #endif |
|---|
| 246 | |
|---|
| 247 | int size() const { return numMarkers; } |
|---|
| 248 | }; |
|---|
| 249 | |
|---|
| 250 | struct GroupInfo { |
|---|
| 251 | const char *name; |
|---|
| 252 | const char *count; |
|---|
| 253 | unsigned name_len; |
|---|
| 254 | unsigned count_len; |
|---|
| 255 | }; |
|---|
| 256 | |
|---|
| 257 | enum GroupInfoMode { |
|---|
| 258 | GI_COMBINED, // only sets GroupInfo::name (will contain "name (count)" or only "name" if counters disabled) |
|---|
| 259 | GI_SEPARATED, // set GroupInfo::name and GroupInfo::count (to "name" and "count") |
|---|
| 260 | GI_SEPARATED_PARENTIZED, // like GI_SEPARATED, but GroupInfo::count will be "(count)" |
|---|
| 261 | }; |
|---|
| 262 | |
|---|
| 263 | enum GroupInfoPosition { |
|---|
| 264 | GIP_SEPARATED, // name attached, count overlayed (=old hardcoded default for AP_TREE_NORMAL and AP_TREE_IRS) |
|---|
| 265 | GIP_ATTACHED, // "name (count)" attached "next to" group (=old hardcoded default for AP_TREE_RADIAL) |
|---|
| 266 | GIP_OVERLAYED, // "name (count)" overlayed with group polygon |
|---|
| 267 | }; |
|---|
| 268 | |
|---|
| 269 | enum GroupCountMode { |
|---|
| 270 | GCM_NONE, // do not show group count (=old hardcoded default for AP_TREE_RADIAL) |
|---|
| 271 | GCM_MEMBERS, // show number of group members (=old hardcoded default for AP_TREE_NORMAL and AP_TREE_IRS) |
|---|
| 272 | GCM_MARKED, // show number of marked group members (show nothing if none marked) |
|---|
| 273 | GCM_BOTH, // show "marked/members" (or "members" if none marked) |
|---|
| 274 | GCM_PERCENT, // show percent of marked group members (show nothing if none marked) |
|---|
| 275 | GCM_BOTH_PC, // show "percent/members" (or "members" if none marked) |
|---|
| 276 | }; |
|---|
| 277 | |
|---|
| 278 | enum BranchStyle { |
|---|
| 279 | BS_RECTANGULAR, // traditional rectangular branches |
|---|
| 280 | BS_DIAGONAL, // diagonal branches (directly from fathers to sons attach point) |
|---|
| 281 | }; |
|---|
| 282 | |
|---|
| 283 | enum GroupStyle { |
|---|
| 284 | GS_TRAPEZE, // traditional style |
|---|
| 285 | GS_TRIANGLE, |
|---|
| 286 | }; |
|---|
| 287 | |
|---|
| 288 | enum GroupOrientation { |
|---|
| 289 | GO_TOP, // long clade side at top |
|---|
| 290 | GO_BOTTOM, // long clade side at bottom |
|---|
| 291 | GO_INTERIOR, // long clade side towards center of subtree |
|---|
| 292 | GO_EXTERIOR, // long clade side towards margin of subtree |
|---|
| 293 | }; |
|---|
| 294 | |
|---|
| 295 | class AWT_graphic_tree; |
|---|
| 296 | DECLARE_CBTYPE_FVV_AND_BUILDERS(GraphicTreeCallback, void, AWT_graphic_tree*); // generates makeGraphicTreeCallback |
|---|
| 297 | |
|---|
| 298 | class PaintedNode { |
|---|
| 299 | AW::Position pos; |
|---|
| 300 | AP_tree *node; |
|---|
| 301 | public: |
|---|
| 302 | PaintedNode() : pos(AW::Origin), node(NULp) {} |
|---|
| 303 | PaintedNode(const PaintedNode& other) : pos(other.pos), node(other.node) {} |
|---|
| 304 | PaintedNode(const AW::Position& p, AP_tree *n) : pos(p), node(n) {} |
|---|
| 305 | DECLARE_ASSIGNMENT_OPERATOR(PaintedNode); |
|---|
| 306 | ~PaintedNode() {} |
|---|
| 307 | |
|---|
| 308 | bool was_displayed() const { return node || are_distinct(AW::Origin, pos); } |
|---|
| 309 | |
|---|
| 310 | const AW::Position& get_pos() const { return pos; } |
|---|
| 311 | AP_tree *get_node() const { return node; } // NULp in list-mode or if got no tree |
|---|
| 312 | }; |
|---|
| 313 | |
|---|
| 314 | enum BootstrapStyle { |
|---|
| 315 | BS_PERCENT, |
|---|
| 316 | BS_PERCENT_NOSIGN, |
|---|
| 317 | BS_FLOAT, |
|---|
| 318 | }; |
|---|
| 319 | |
|---|
| 320 | struct BootstrapConfig { |
|---|
| 321 | const AW_bitset circle_filter; |
|---|
| 322 | const AW_bitset text_filter; |
|---|
| 323 | |
|---|
| 324 | int bootstrap_min; // minimum shown bootstrap (lower values are hidden) |
|---|
| 325 | int bootstrap_max; // maximum shown bootstrap (higher values are hidden) |
|---|
| 326 | |
|---|
| 327 | BootstrapStyle style; |
|---|
| 328 | |
|---|
| 329 | bool show_boots; |
|---|
| 330 | bool show_circle; |
|---|
| 331 | float fill_level; |
|---|
| 332 | bool elipsoid; |
|---|
| 333 | |
|---|
| 334 | bool show_100_if_empty; // automatically add 100% at branches w/o bootstraps (last loaded state contains bootstraps!) |
|---|
| 335 | |
|---|
| 336 | float zoom_factor; |
|---|
| 337 | float max_radius; |
|---|
| 338 | |
|---|
| 339 | double scaled_remark_ascend; |
|---|
| 340 | |
|---|
| 341 | BootstrapConfig(); |
|---|
| 342 | |
|---|
| 343 | 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; |
|---|
| 344 | void display_node_remark(AW_device *device, const AP_tree *at, const AW::Position& center, double blen, double bdist, AW::RoughDirection textArea) const; |
|---|
| 345 | |
|---|
| 346 | void update_empty_branch_behavior(const TreeRoot *troot) { |
|---|
| 347 | show_100_if_empty = troot->has_bootstrap(); |
|---|
| 348 | } |
|---|
| 349 | |
|---|
| 350 | bool shall_show_remark_for(const TreeNode *node) const { |
|---|
| 351 | return !node->is_leaf() && (show_100_if_empty || node->get_remark_ptr().isSet()); |
|---|
| 352 | } |
|---|
| 353 | }; |
|---|
| 354 | |
|---|
| 355 | class AWT_graphic_tree : public AWT_graphic, virtual Noncopyable { |
|---|
| 356 | char *species_name; |
|---|
| 357 | Group selected_group; |
|---|
| 358 | |
|---|
| 359 | PaintedNode selSpec; // is set while tree display gets refreshed |
|---|
| 360 | PaintedNode selGroup; // is set while tree display gets refreshed // @@@ no need to store node; store rectangle area instead of position |
|---|
| 361 | |
|---|
| 362 | int baselinewidth; |
|---|
| 363 | |
|---|
| 364 | BootstrapConfig bconf; |
|---|
| 365 | |
|---|
| 366 | int zombies; // # of zombies during last load() |
|---|
| 367 | int duplicates; // # of duplicates during last load() |
|---|
| 368 | |
|---|
| 369 | AP_tree *tree_proto; |
|---|
| 370 | |
|---|
| 371 | bool show_brackets; |
|---|
| 372 | |
|---|
| 373 | bool link_to_database; // link on load? |
|---|
| 374 | |
|---|
| 375 | GroupStyle group_style; |
|---|
| 376 | GroupOrientation group_orientation; |
|---|
| 377 | |
|---|
| 378 | double list_tree_ruler_y; |
|---|
| 379 | double irs_tree_ruler_scale_factor; |
|---|
| 380 | double attach_size; // 1.0 = at bigger subtree ; 0.0 = centered; -1.0 = at smaller subtree (trad.) |
|---|
| 381 | double attach_len; // 1.0 = at longer branch; 0.0 = centered (trad.); -1.0 = at shorter branch |
|---|
| 382 | double attach_group; // 1.0 = at longer side; 0.5 = centered (trad.); 0.0 = at shorter side (of group polygon) |
|---|
| 383 | |
|---|
| 384 | AWT_scaled_font_limits scaled_font; |
|---|
| 385 | |
|---|
| 386 | double scaled_branch_distance; // vertical distance between branches (may be extra-scaled in options) |
|---|
| 387 | |
|---|
| 388 | AW_grey_level group_greylevel; |
|---|
| 389 | AW_grey_level marker_greylevel; |
|---|
| 390 | |
|---|
| 391 | AW_device *disp_device; // device for recursive functions |
|---|
| 392 | |
|---|
| 393 | const AW_bitset line_filter, vert_line_filter, mark_filter, group_bracket_filter; |
|---|
| 394 | const AW_bitset leaf_text_filter, group_text_filter, other_text_filter; |
|---|
| 395 | const AW_bitset ruler_filter, root_filter, marker_filter; |
|---|
| 396 | |
|---|
| 397 | GroupInfoPosition group_info_pos; |
|---|
| 398 | GroupCountMode group_count_mode; |
|---|
| 399 | BranchStyle branch_style; |
|---|
| 400 | |
|---|
| 401 | MarkerDisplay *display_markers; |
|---|
| 402 | struct { |
|---|
| 403 | double marked; |
|---|
| 404 | double partiallyMarked; |
|---|
| 405 | } groupThreshold; |
|---|
| 406 | |
|---|
| 407 | AD_map_viewer_cb map_viewer_cb; |
|---|
| 408 | AWT_command_data *cmd_data; |
|---|
| 409 | |
|---|
| 410 | AP_tree_root *tree_static; |
|---|
| 411 | AP_tree *displayed_root; // root node of shown (sub-)tree; differs from real root if tree is zoomed logically |
|---|
| 412 | |
|---|
| 413 | GraphicTreeCallback tree_changed_cb; |
|---|
| 414 | static GraphicTreeCallback group_changed_cb; |
|---|
| 415 | |
|---|
| 416 | class AP_tree_folding *autoUnfolded; |
|---|
| 417 | |
|---|
| 418 | AW_root *aw_root; |
|---|
| 419 | GBDATA *gb_main; |
|---|
| 420 | |
|---|
| 421 | AP_tree_display_style tree_style; |
|---|
| 422 | bool nds_only_marked; // true -> display only marked species (only for list-styles) |
|---|
| 423 | |
|---|
| 424 | AW_pos paint_irs_sub_tree(AP_tree *node, AW_pos x_offset, const NDS_Labeler& labeler); // returns y pos |
|---|
| 425 | void unload(); |
|---|
| 426 | |
|---|
| 427 | // functions to compute displayinformation |
|---|
| 428 | |
|---|
| 429 | void show_dendrogram(AP_tree *at, AW::Position& pen, DendroSubtreeLimits& limits, const NDS_Labeler& labeler); |
|---|
| 430 | void 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); |
|---|
| 431 | void show_nds_list(GBDATA * gb_main, bool use_nds, const NDS_Labeler& labeler); |
|---|
| 432 | void show_irs_tree(AP_tree *at, double height, const NDS_Labeler& labeler); |
|---|
| 433 | |
|---|
| 434 | void summarizeGroupMarkers(AP_tree *at, NodeMarkers& markers); |
|---|
| 435 | void drawMarker(const class MarkerPosition& marker, const bool partial, const int midx); |
|---|
| 436 | void detectAndDrawMarkers(AP_tree *at, double y1, double y2); |
|---|
| 437 | void drawMarkerNames(AW::Position& Pen); |
|---|
| 438 | |
|---|
| 439 | void pixel_box(int gc, const AW::Position& pos, int pixel_width, AW::FillStyle filled); |
|---|
| 440 | |
|---|
| 441 | AP_tree *find_selected_node() const; |
|---|
| 442 | AP_tree *find_selected_group(); |
|---|
| 443 | |
|---|
| 444 | void toggle_folding_at(AP_tree *at, bool force_jump); |
|---|
| 445 | |
|---|
| 446 | const GroupInfo& get_group_info(AP_tree *at, GroupInfoMode mode, bool swap, const NDS_Labeler& labeler) const; |
|---|
| 447 | |
|---|
| 448 | bool handle_cursor(AW_key_code kcode, AW_key_mod mod); |
|---|
| 449 | void handle_key(AW_device *device, AWT_graphic_event& event); |
|---|
| 450 | |
|---|
| 451 | GB_ERROR create_group(AP_tree * at) __ATTR__USERESULT; |
|---|
| 452 | |
|---|
| 453 | protected: |
|---|
| 454 | group_scaling groupScale; // scaling for folded groups |
|---|
| 455 | |
|---|
| 456 | void store_command_data(AWT_command_data *new_cmd_data) { |
|---|
| 457 | delete cmd_data; |
|---|
| 458 | cmd_data = new_cmd_data; |
|---|
| 459 | } |
|---|
| 460 | AWT_command_data *get_command_data() { return cmd_data; } |
|---|
| 461 | |
|---|
| 462 | public: |
|---|
| 463 | AWT_graphic_tree(AW_root *aw_root, GBDATA *gb_main, AD_map_viewer_cb map_viewer_cb); |
|---|
| 464 | ~AWT_graphic_tree() OVERRIDE; |
|---|
| 465 | |
|---|
| 466 | void filled_box(int gc, const AW::Position& pos, int pixel_width) { pixel_box(gc, pos, pixel_width, AW::FillStyle::SOLID); } |
|---|
| 467 | void empty_box(int gc, const AW::Position& pos, int pixel_width) { pixel_box(gc, pos, pixel_width, AW::FillStyle::EMPTY); } |
|---|
| 468 | void diamond(int gc, const AW::Position& pos, int pixel_radius); |
|---|
| 469 | |
|---|
| 470 | const char *ruler_awar(const char *name); |
|---|
| 471 | |
|---|
| 472 | void set_line_attributes_for(AP_tree *at) const { |
|---|
| 473 | disp_device->set_line_attributes(at->gr.gc, at->get_linewidth()+baselinewidth, AW_SOLID); |
|---|
| 474 | } |
|---|
| 475 | |
|---|
| 476 | virtual void read_tree_settings(); |
|---|
| 477 | void update_structure() FINAL_OVERRIDE { |
|---|
| 478 | AP_tree *root = get_root_node(); |
|---|
| 479 | if (root) { |
|---|
| 480 | forget_auto_unfolded(); |
|---|
| 481 | root->compute_tree(); |
|---|
| 482 | } |
|---|
| 483 | } |
|---|
| 484 | void apply_zoom_settings_for_treetype(AWT_canvas *ntw); |
|---|
| 485 | |
|---|
| 486 | int draw_branch_line(int gc, const AW::Position& root, const AW::Position& leaf, AW_bitset filter) { |
|---|
| 487 | const AW_click_cd *old = disp_device->get_click_cd(); |
|---|
| 488 | td_assert(old && old->get_cd1() && old->get_cd2() == CL_NODE); // cd1 should be the node |
|---|
| 489 | |
|---|
| 490 | AW_click_cd branch(disp_device, old->get_cd1(), CL_BRANCH); |
|---|
| 491 | return disp_device->line(gc, root, leaf, filter); |
|---|
| 492 | } |
|---|
| 493 | |
|---|
| 494 | bool warn_inappropriate_mode(AWT_COMMAND_MODE mode); |
|---|
| 495 | |
|---|
| 496 | virtual AP_tree_root *create_tree_root(AliView *aliview, AP_sequence *seq_prototype, bool insert_delete_cbs); |
|---|
| 497 | |
|---|
| 498 | AW_root *get_root() const { return aw_root; } |
|---|
| 499 | GBDATA *get_gbmain() const { return gb_main; } |
|---|
| 500 | |
|---|
| 501 | AP_tree_root *get_tree_root() { return tree_static; } |
|---|
| 502 | |
|---|
| 503 | AP_tree *get_root_node() { return tree_static ? tree_static->get_root_node() : NULp; } |
|---|
| 504 | const AP_tree *get_root_node() const { return const_cast<AWT_graphic_tree*>(this)->get_root_node(); } |
|---|
| 505 | |
|---|
| 506 | AP_tree *get_logical_root() { return displayed_root; } |
|---|
| 507 | const AP_tree *get_logical_root() const { return displayed_root; } |
|---|
| 508 | |
|---|
| 509 | bool is_logically_zoomed() { return displayed_root != get_root_node(); } |
|---|
| 510 | void set_logical_root_to(AP_tree *node) { |
|---|
| 511 | displayed_root = node; |
|---|
| 512 | tree_changed_cb(this); |
|---|
| 513 | } |
|---|
| 514 | |
|---|
| 515 | void init(AliView *aliview, AP_sequence *seq_prototype, bool link_to_database_, bool insert_delete_cbs); |
|---|
| 516 | AW_gc_manager *init_devices(AW_window *, AW_device *, AWT_canvas *ntw) OVERRIDE; |
|---|
| 517 | |
|---|
| 518 | void show(AW_device *device) OVERRIDE; |
|---|
| 519 | const AW::Position& get_cursor() const { return selSpec.get_pos(); } |
|---|
| 520 | const AW::Position& get_group_cursor() const { return selGroup.get_pos(); } |
|---|
| 521 | |
|---|
| 522 | void handle_command(AW_device *device, AWT_graphic_event& event) OVERRIDE; |
|---|
| 523 | |
|---|
| 524 | void mark_species_in_tree(AP_tree *at, int mark); |
|---|
| 525 | void mark_species_in_tree_that(AP_tree *at, int mark, bool (*condition)(GBDATA*, void*), void *cd); |
|---|
| 526 | |
|---|
| 527 | void mark_species_in_rest_of_tree(AP_tree *at, int mark); |
|---|
| 528 | void mark_species_in_rest_of_tree_that(AP_tree *at, int mark, bool (*condition)(GBDATA*, void*), void *cd); |
|---|
| 529 | |
|---|
| 530 | bool tree_has_marks(AP_tree *at); |
|---|
| 531 | bool rest_tree_has_marks(AP_tree *at); |
|---|
| 532 | |
|---|
| 533 | void detect_group_state(AP_tree *at, AWT_graphic_tree_group_state *state, AP_tree *skip_this_son); |
|---|
| 534 | |
|---|
| 535 | bool group_tree(AP_tree *at, CollapseMode mode, int color_group); |
|---|
| 536 | void group_rest_tree(AP_tree *at, CollapseMode mode, int color_group); |
|---|
| 537 | void reorderTree(TreeOrder mode); |
|---|
| 538 | void toggle_group(AP_tree * at); |
|---|
| 539 | |
|---|
| 540 | GB_ERROR load_from_DB(GBDATA *gb_main, const char *name) FINAL_OVERRIDE __ATTR__USERESULT; |
|---|
| 541 | GB_ERROR save_to_DB(GBDATA *gb_main, const char *name) FINAL_OVERRIDE __ATTR__USERESULT; |
|---|
| 542 | void check_for_DB_update(GBDATA *gb_main) FINAL_OVERRIDE; |
|---|
| 543 | void notify_synchronized(GBDATA *gb_main) FINAL_OVERRIDE; |
|---|
| 544 | |
|---|
| 545 | void fast_sync_changed_folding(AP_tree *parent_of_all_changes); // use carefully |
|---|
| 546 | |
|---|
| 547 | void set_tree_style(AP_tree_display_style style, AWT_canvas *ntw); |
|---|
| 548 | AP_tree_display_style get_tree_style() const { return tree_style; } |
|---|
| 549 | |
|---|
| 550 | double get_irs_tree_ruler_scale_factor() const { return irs_tree_ruler_scale_factor; } |
|---|
| 551 | void show_ruler(AW_device *device, int gc); |
|---|
| 552 | void get_zombies_and_duplicates(int& zomb, int& dups) const { zomb = zombies; dups = duplicates; } |
|---|
| 553 | |
|---|
| 554 | void hide_marker_display() { |
|---|
| 555 | delete display_markers; |
|---|
| 556 | display_markers = NULp; |
|---|
| 557 | } |
|---|
| 558 | void set_marker_display(MarkerDisplay *display) { // takes ownership of 'display' |
|---|
| 559 | hide_marker_display(); |
|---|
| 560 | display_markers = display; |
|---|
| 561 | } |
|---|
| 562 | MarkerDisplay *get_marker_display() { return display_markers; } |
|---|
| 563 | |
|---|
| 564 | void install_tree_changed_callback(const GraphicTreeCallback& gtcb); |
|---|
| 565 | void uninstall_tree_changed_callback(); |
|---|
| 566 | |
|---|
| 567 | static void install_group_changed_callback(const GraphicTreeCallback& gccb) { |
|---|
| 568 | group_changed_cb = gccb; |
|---|
| 569 | } |
|---|
| 570 | |
|---|
| 571 | void auto_unfold(AP_tree *want_visible); |
|---|
| 572 | void forget_auto_unfolded(); |
|---|
| 573 | |
|---|
| 574 | private: |
|---|
| 575 | void select_group(const Group& group) { |
|---|
| 576 | if (selected_group != group) { |
|---|
| 577 | selected_group = group; |
|---|
| 578 | group_changed_cb(this); |
|---|
| 579 | } |
|---|
| 580 | } |
|---|
| 581 | public: |
|---|
| 582 | void select_group(AP_tree *node) { // select + locate |
|---|
| 583 | td_assert(node && node->is_clade()); |
|---|
| 584 | select_group(Group(node)); |
|---|
| 585 | } |
|---|
| 586 | void select_group(GBDATA *gb_group) { // unlocated select (or deselect if called with NULp) |
|---|
| 587 | select_group(gb_group ? Group(gb_group) : Group()); |
|---|
| 588 | } |
|---|
| 589 | void deselect_group() { |
|---|
| 590 | select_group(Group()); |
|---|
| 591 | } |
|---|
| 592 | const Group& get_selected_group() const { |
|---|
| 593 | return selected_group; |
|---|
| 594 | } |
|---|
| 595 | AP_tree *locate_selected_group(AP_tree *in_subtree) { |
|---|
| 596 | AP_tree *node = NULp; |
|---|
| 597 | if (selected_group.is_valid()) { |
|---|
| 598 | if (selected_group.locate(in_subtree)) { |
|---|
| 599 | node = selected_group.get_node(); |
|---|
| 600 | } |
|---|
| 601 | } |
|---|
| 602 | return node; |
|---|
| 603 | } |
|---|
| 604 | void dislocate_selected_group() { |
|---|
| 605 | // has to be called whenever the keeled-state of a group may change |
|---|
| 606 | selected_group.dislocate(); |
|---|
| 607 | } |
|---|
| 608 | |
|---|
| 609 | #if defined(UNIT_TESTS) // UT_DIFF |
|---|
| 610 | friend class fake_AWT_graphic_tree; |
|---|
| 611 | #endif |
|---|
| 612 | PREPARE_MARK_NONFINAL_CLASS(AWT_graphic_tree); |
|---|
| 613 | }; |
|---|
| 614 | MARK_NONFINAL_CLASS(AWT_graphic_tree); |
|---|
| 615 | MARK_NONFINAL_FUNCTION(AWT_graphic_tree,AP_tree_root*,create_tree_root,(AliView*,AP_sequence*,bool),NULp); |
|---|
| 616 | |
|---|
| 617 | class ClickedTarget { |
|---|
| 618 | /*! Represents any target corresponding to some (mouse-)position in the tree display. |
|---|
| 619 | * |
|---|
| 620 | * The target is e.g. used as target for keystrokes or mouse clicks. |
|---|
| 621 | * |
|---|
| 622 | * For AP_LIST_NDS, this only represents the species (w/o any tree information). |
|---|
| 623 | * For other tree display modes, this represents a specific tree node. |
|---|
| 624 | * |
|---|
| 625 | * The space outside the tree does represent the whole tree (aka the root-node). |
|---|
| 626 | * (the necessary distance to the tree-structure/-text is defined by AWT_CATCH) |
|---|
| 627 | */ |
|---|
| 628 | |
|---|
| 629 | AP_tree *tree_node; |
|---|
| 630 | GBDATA *gb_species; |
|---|
| 631 | |
|---|
| 632 | bool ruler; |
|---|
| 633 | bool branch; |
|---|
| 634 | int markerflag; // = markerindex + 1 |
|---|
| 635 | |
|---|
| 636 | const AW_clicked_element *elem; |
|---|
| 637 | |
|---|
| 638 | void init() { |
|---|
| 639 | tree_node = NULp; |
|---|
| 640 | gb_species = NULp; |
|---|
| 641 | ruler = false; |
|---|
| 642 | branch = false; |
|---|
| 643 | markerflag = 0; |
|---|
| 644 | } |
|---|
| 645 | |
|---|
| 646 | void identify(AWT_graphic_tree *agt) { |
|---|
| 647 | init(); |
|---|
| 648 | if (elem && elem->does_exist()) { |
|---|
| 649 | ClickedType what = (ClickedType)elem->cd2(); |
|---|
| 650 | |
|---|
| 651 | switch (what) { |
|---|
| 652 | case CL_SPECIES: |
|---|
| 653 | gb_species = (GBDATA*)elem->cd1(); |
|---|
| 654 | td_assert(gb_species); |
|---|
| 655 | break; |
|---|
| 656 | |
|---|
| 657 | case CL_RULER: |
|---|
| 658 | ruler = !elem->cd1(); |
|---|
| 659 | break; |
|---|
| 660 | |
|---|
| 661 | case CL_FLAG: |
|---|
| 662 | markerflag = elem->cd1()+1; |
|---|
| 663 | break; |
|---|
| 664 | |
|---|
| 665 | case CL_BRANCH: |
|---|
| 666 | branch = true; |
|---|
| 667 | // fall-through! |
|---|
| 668 | case CL_NODE: |
|---|
| 669 | tree_node = (AP_tree*)elem->cd1(); |
|---|
| 670 | break; |
|---|
| 671 | |
|---|
| 672 | case CL_ROOTNODE: |
|---|
| 673 | if (agt) tree_node = agt->get_root_node(); |
|---|
| 674 | break; |
|---|
| 675 | |
|---|
| 676 | #if defined(DEBUG) |
|---|
| 677 | default: |
|---|
| 678 | td_assert(0); // unknown element type |
|---|
| 679 | #endif |
|---|
| 680 | } |
|---|
| 681 | } |
|---|
| 682 | else { // use whole tree if mouse does not point to a subtree |
|---|
| 683 | tree_node = agt ? agt->get_root_node() : NULp; |
|---|
| 684 | } |
|---|
| 685 | td_assert(implicated(branch, tree_node)); |
|---|
| 686 | } |
|---|
| 687 | |
|---|
| 688 | public: |
|---|
| 689 | |
|---|
| 690 | ClickedTarget(AWT_graphic_tree *agt, const AW_clicked_element *clicked) : elem(clicked) { |
|---|
| 691 | // uses root of tree as target, when a position outside of the tree is selected |
|---|
| 692 | // (e.g. used for key-commands) |
|---|
| 693 | identify(agt); |
|---|
| 694 | } |
|---|
| 695 | ClickedTarget(const AW_clicked_element *clicked) : elem(clicked) { |
|---|
| 696 | // accept only normal branches as targets |
|---|
| 697 | identify(NULp); |
|---|
| 698 | } |
|---|
| 699 | |
|---|
| 700 | const AW_clicked_element *element() const { return elem; } |
|---|
| 701 | AP_tree *node() const { return tree_node; } |
|---|
| 702 | GBDATA *species() const { return gb_species; } |
|---|
| 703 | int get_markerindex() const { return markerflag-1; } |
|---|
| 704 | |
|---|
| 705 | bool is_text() const { return elem && elem->is_text(); } |
|---|
| 706 | bool is_line() const { return elem && elem->is_line(); } |
|---|
| 707 | bool is_branch() const { return branch; } |
|---|
| 708 | bool is_ruler() const { return ruler; } |
|---|
| 709 | bool is_marker() const { return markerflag; } |
|---|
| 710 | |
|---|
| 711 | double get_rel_attach() const { |
|---|
| 712 | // return [0..1] according to exact position where element is dropped |
|---|
| 713 | if (is_line() && (is_branch() || ruler)) return elem->get_rel_pos(); |
|---|
| 714 | return 0.5; // act like "drop on branch-center" |
|---|
| 715 | } |
|---|
| 716 | }; |
|---|
| 717 | |
|---|
| 718 | class TREE_canvas : public AWT_canvas { // derived from Noncopyable |
|---|
| 719 | AW_awar *awar_tree; // awar containing name of displayed tree |
|---|
| 720 | int index; // unique index [0..MAX_NT_WINDOWS-1] |
|---|
| 721 | |
|---|
| 722 | static int count; |
|---|
| 723 | |
|---|
| 724 | public: |
|---|
| 725 | TREE_canvas(GBDATA *gb_main_, AW_window *aww_, const char *gc_base_name_, AWT_graphic *gfx_, AW_awar *awar_tree_) : |
|---|
| 726 | AWT_canvas(gb_main_, aww_, gc_base_name_, gfx_), |
|---|
| 727 | awar_tree(awar_tree_), |
|---|
| 728 | index(count++) |
|---|
| 729 | {} |
|---|
| 730 | |
|---|
| 731 | void at_screen_update_call(screen_update_callback cb, AW_CL cd) { |
|---|
| 732 | td_assert(!announce_update_cb || (announce_update_cb == cb && user_data == cd)); |
|---|
| 733 | |
|---|
| 734 | announce_update_cb = cb; |
|---|
| 735 | user_data = cd; |
|---|
| 736 | } |
|---|
| 737 | |
|---|
| 738 | AWT_graphic_tree *get_graphic_tree() const { return DOWNCAST(AWT_graphic_tree*, gfx); } |
|---|
| 739 | AW_awar *get_awar_tree() const { return awar_tree; } |
|---|
| 740 | int get_index() const { return index; } |
|---|
| 741 | }; |
|---|
| 742 | |
|---|
| 743 | |
|---|
| 744 | void TREE_create_awars(AW_root *aw_root, AW_default db); |
|---|
| 745 | void TREE_install_update_callbacks(TREE_canvas *ntw); |
|---|
| 746 | AW_window *TREE_create_settings_window(AW_root *aw_root); |
|---|
| 747 | AW_window *TREE_create_marker_settings_window(AW_root *root); |
|---|
| 748 | |
|---|
| 749 | AWT_graphic_tree *NT_generate_tree(AW_root *root, GBDATA *gb_main, AD_map_viewer_cb map_viewer_cb); |
|---|
| 750 | |
|---|
| 751 | #else |
|---|
| 752 | #error TreeDisplay.hxx included twice |
|---|
| 753 | #endif // TREEDISPLAY_HXX |
|---|