| 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 AWT_CANVAS_HXX |
|---|
| 18 | #include <awt_canvas.hxx> |
|---|
| 19 | #endif |
|---|
| 20 | |
|---|
| 21 | #define td_assert(cond) arb_assert(cond) |
|---|
| 22 | |
|---|
| 23 | #define AWAR_DTREE_BASELINEWIDTH "awt/dtree/baselinewidth" |
|---|
| 24 | #define AWAR_DTREE_VERICAL_DIST "awt/dtree/verticaldist" |
|---|
| 25 | #define AWAR_DTREE_AUTO_JUMP "awt/dtree/autojump" |
|---|
| 26 | #define AWAR_DTREE_AUTO_JUMP_TREE "awt/dtree/autojump_tree" |
|---|
| 27 | #define AWAR_DTREE_SHOW_CIRCLE "awt/dtree/show_circle" |
|---|
| 28 | #define AWAR_DTREE_SHOW_BRACKETS "awt/dtree/show_brackets" |
|---|
| 29 | #define AWAR_DTREE_CIRCLE_ZOOM "awt/dtree/circle_zoom" |
|---|
| 30 | #define AWAR_DTREE_CIRCLE_MAX_SIZE "awt/dtree/max_size" |
|---|
| 31 | #define AWAR_DTREE_USE_ELLIPSE "awt/dtree/ellipse" |
|---|
| 32 | #define AWAR_DTREE_GREY_LEVEL "awt/dtree/greylevel" |
|---|
| 33 | |
|---|
| 34 | #define AWAR_DTREE_RADIAL_ZOOM_TEXT "awt/dtree/radial/zoomtext" |
|---|
| 35 | #define AWAR_DTREE_RADIAL_XPAD "awt/dtree/radial/xpadding" |
|---|
| 36 | |
|---|
| 37 | #define AWAR_DTREE_DENDRO_ZOOM_TEXT "awt/dtree/dendro/zoomtext" |
|---|
| 38 | #define AWAR_DTREE_DENDRO_XPAD "awt/dtree/dendro/xpadding" |
|---|
| 39 | |
|---|
| 40 | void awt_create_dtree_awars(AW_root *aw_root, AW_default db); |
|---|
| 41 | |
|---|
| 42 | #define NT_BOX_WIDTH 7 // pixel |
|---|
| 43 | #define NT_ROOT_WIDTH 9 |
|---|
| 44 | #define NT_SELECTED_WIDTH 11 |
|---|
| 45 | |
|---|
| 46 | #define AWT_TREE(ntw) DOWNCAST(AWT_graphic_tree*, (ntw)->gfx) |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | enum AP_tree_display_type { |
|---|
| 50 | AP_TREE_NORMAL, // normal tree display (dendrogram) |
|---|
| 51 | AP_TREE_RADIAL, // radial tree display |
|---|
| 52 | AP_TREE_IRS, // like AP_TREE_NORMAL, with folding line |
|---|
| 53 | AP_LIST_NDS, |
|---|
| 54 | AP_LIST_SIMPLE // simple display only showing name (used at startup to avoid NDS error messages) |
|---|
| 55 | }; |
|---|
| 56 | |
|---|
| 57 | enum AP_tree_jump_type { // bit-values |
|---|
| 58 | AP_JUMP_KEEP_VISIBLE = 1, // automatically make selected node visible (on changes) |
|---|
| 59 | AP_JUMP_UNFOLD_GROUPS = 2, // |
|---|
| 60 | AP_JUMP_FORCE_VCENTER = 4, // force vertical centering (even if visible) |
|---|
| 61 | AP_JUMP_ALLOW_HCENTER = 8, // force horizontal centering (if vertically centered); only works together with AP_JUMP_FORCE_VCENTER |
|---|
| 62 | AP_JUMP_FORCE_HCENTER = 16, // force horizontal centering |
|---|
| 63 | AP_JUMP_BE_VERBOOSE = 32, // tell why nothing happened etc. |
|---|
| 64 | |
|---|
| 65 | // convenience defs: |
|---|
| 66 | AP_DONT_JUMP = 0, |
|---|
| 67 | AP_JUMP_SMART_CENTER = AP_JUMP_FORCE_VCENTER|AP_JUMP_ALLOW_HCENTER, |
|---|
| 68 | AP_JUMP_FORCE_CENTER = AP_JUMP_FORCE_VCENTER|AP_JUMP_FORCE_HCENTER, |
|---|
| 69 | |
|---|
| 70 | AP_JUMP_BY_BUTTON = AP_JUMP_SMART_CENTER|AP_JUMP_UNFOLD_GROUPS|AP_JUMP_BE_VERBOOSE, |
|---|
| 71 | }; |
|---|
| 72 | |
|---|
| 73 | inline bool sort_is_list_style(AP_tree_display_type sort) { return sort == AP_LIST_NDS || sort == AP_LIST_SIMPLE; } |
|---|
| 74 | inline bool sort_is_tree_style(AP_tree_display_type sort) { return !sort_is_list_style(sort); } |
|---|
| 75 | |
|---|
| 76 | |
|---|
| 77 | class AWT_graphic_tree_group_state; |
|---|
| 78 | |
|---|
| 79 | struct AWT_scaled_font_limits { |
|---|
| 80 | double ascent; |
|---|
| 81 | double descent; |
|---|
| 82 | double height; |
|---|
| 83 | double width; |
|---|
| 84 | |
|---|
| 85 | void init(const AW_font_limits& font_limits, double factor) { |
|---|
| 86 | ascent = font_limits.ascent*factor; |
|---|
| 87 | descent = font_limits.descent*factor; |
|---|
| 88 | height = font_limits.height*factor; |
|---|
| 89 | width = font_limits.width*factor; |
|---|
| 90 | } |
|---|
| 91 | }; |
|---|
| 92 | |
|---|
| 93 | enum AD_MAP_VIEWER_TYPE { |
|---|
| 94 | ADMVT_NONE = 0, |
|---|
| 95 | ADMVT_INFO, |
|---|
| 96 | ADMVT_WWW, |
|---|
| 97 | ADMVT_SELECT |
|---|
| 98 | }; |
|---|
| 99 | |
|---|
| 100 | typedef void (*AD_map_viewer_cb)(GBDATA *gbd, AD_MAP_VIEWER_TYPE type); |
|---|
| 101 | |
|---|
| 102 | struct DendroSubtreeLimits { |
|---|
| 103 | double y_branch; // ypos of branch to subtree |
|---|
| 104 | double y_top; // top ypos of whole subtree |
|---|
| 105 | double y_bot; // bottom ypos of whole subtree |
|---|
| 106 | double x_right; // rightmost xpos of whole subtree |
|---|
| 107 | |
|---|
| 108 | void combine(const DendroSubtreeLimits& other) { |
|---|
| 109 | y_top = std::min(y_top, other.y_top); |
|---|
| 110 | y_bot = std::max(y_bot, other.y_bot); |
|---|
| 111 | x_right = std::max(x_right, other.x_right); |
|---|
| 112 | } |
|---|
| 113 | }; |
|---|
| 114 | |
|---|
| 115 | struct AWT_command_data { |
|---|
| 116 | /*! any kind of data which has to be stored between different events (e.g. to support drag&drop) |
|---|
| 117 | * Purpose of this class is to allow to delete such data w/o knowing anything else. |
|---|
| 118 | */ |
|---|
| 119 | virtual ~AWT_command_data() {} |
|---|
| 120 | }; |
|---|
| 121 | |
|---|
| 122 | enum CollapseMode { |
|---|
| 123 | COLLAPSE_ALL = 0, |
|---|
| 124 | EXPAND_MARKED = 1, // do not collapse groups containing marked species |
|---|
| 125 | COLLAPSE_TERMINAL = 2, // do not collapse groups with subgroups |
|---|
| 126 | EXPAND_ALL = 4, |
|---|
| 127 | EXPAND_COLOR = 8, // do not collapse groups containing species with color == parameter 'color_group' (or any color if 'color_group' is -1) |
|---|
| 128 | EXPAND_ZOMBIES = 16, // do not collapse groups containing zombies |
|---|
| 129 | }; |
|---|
| 130 | |
|---|
| 131 | class AWT_graphic_tree : public AWT_graphic, virtual Noncopyable { |
|---|
| 132 | char *species_name; |
|---|
| 133 | AW::Position cursor; |
|---|
| 134 | |
|---|
| 135 | int baselinewidth; |
|---|
| 136 | int show_brackets; |
|---|
| 137 | int show_circle; |
|---|
| 138 | int use_ellipse; |
|---|
| 139 | float circle_zoom_factor; |
|---|
| 140 | float circle_max_size; |
|---|
| 141 | |
|---|
| 142 | int zombies; // # of zombies during last load() |
|---|
| 143 | int duplicates; // # of duplicates during last load() |
|---|
| 144 | |
|---|
| 145 | AW_pos paint_irs_sub_tree(AP_tree *node, AW_pos x_offset); // returns y pos |
|---|
| 146 | |
|---|
| 147 | void unload(); |
|---|
| 148 | |
|---|
| 149 | // variables - tree compatibility |
|---|
| 150 | |
|---|
| 151 | AP_tree * tree_proto; |
|---|
| 152 | bool link_to_database; // link on load ? |
|---|
| 153 | |
|---|
| 154 | double list_tree_ruler_y; |
|---|
| 155 | double irs_tree_ruler_scale_factor; |
|---|
| 156 | |
|---|
| 157 | AWT_scaled_font_limits scaled_font; |
|---|
| 158 | double scaled_branch_distance; // vertical distance between branches (may be extra-scaled in options) |
|---|
| 159 | |
|---|
| 160 | AW_pos grey_level; |
|---|
| 161 | |
|---|
| 162 | AW_device *disp_device; // device for recursive functions |
|---|
| 163 | |
|---|
| 164 | const AW_bitset line_filter, vert_line_filter, mark_filter, group_bracket_filter, bs_circle_filter; |
|---|
| 165 | const AW_bitset leaf_text_filter, group_text_filter, remark_text_filter, other_text_filter; |
|---|
| 166 | const AW_bitset ruler_filter, root_filter; |
|---|
| 167 | |
|---|
| 168 | bool nds_show_all; |
|---|
| 169 | |
|---|
| 170 | AD_map_viewer_cb map_viewer_cb; |
|---|
| 171 | AWT_command_data *cmd_data; |
|---|
| 172 | |
|---|
| 173 | void scale_text_koordinaten(AW_device *device, int gc, double& x, double& y, double orientation, int flag); |
|---|
| 174 | |
|---|
| 175 | // functions to compute displayinformation |
|---|
| 176 | |
|---|
| 177 | void show_dendrogram(AP_tree *at, AW::Position& pen, DendroSubtreeLimits& limits); |
|---|
| 178 | |
|---|
| 179 | void show_radial_tree(AP_tree *at, |
|---|
| 180 | double x_center, |
|---|
| 181 | double y_center, |
|---|
| 182 | double tree_sprad, |
|---|
| 183 | double tree_orientation, |
|---|
| 184 | double x_root, |
|---|
| 185 | double y_root); |
|---|
| 186 | |
|---|
| 187 | void show_nds_list(GBDATA * gb_main, bool use_nds); |
|---|
| 188 | void show_irs_tree(AP_tree *at, double height); |
|---|
| 189 | |
|---|
| 190 | void box(int gc, const AW::Position& pos, int pixel_width, bool filled); |
|---|
| 191 | void filled_box(int gc, const AW::Position& pos, int pixel_width) { box(gc, pos, pixel_width, true); } |
|---|
| 192 | void empty_box(int gc, const AW::Position& pos, int pixel_width) { box(gc, pos, pixel_width, false); } |
|---|
| 193 | void diamond(int gc, const AW::Position& pos, int pixel_width); |
|---|
| 194 | |
|---|
| 195 | const char *ruler_awar(const char *name); |
|---|
| 196 | |
|---|
| 197 | void set_line_attributes_for(AP_tree *at) const { |
|---|
| 198 | disp_device->set_line_attributes(at->gr.gc, at->get_linewidth()+baselinewidth, AW_SOLID); |
|---|
| 199 | } |
|---|
| 200 | |
|---|
| 201 | virtual void read_tree_settings(); |
|---|
| 202 | void update_structure() { |
|---|
| 203 | AP_tree *root = get_root_node(); |
|---|
| 204 | if (root) root->compute_tree(); |
|---|
| 205 | } |
|---|
| 206 | void apply_zoom_settings_for_treetype(AWT_canvas *ntw); |
|---|
| 207 | |
|---|
| 208 | int draw_branch_line(int gc, const AW::Position& root, const AW::Position& leaf, AW_bitset filter) { |
|---|
| 209 | const AW_click_cd *old = disp_device->get_click_cd(); |
|---|
| 210 | td_assert(old && old->get_cd1() && !old->get_cd2()); // cd1 should be the node |
|---|
| 211 | |
|---|
| 212 | AW_click_cd branch(disp_device, old->get_cd1(), (AW_CL)"branch"); |
|---|
| 213 | return disp_device->line(gc, root, leaf, filter); |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | bool warn_inappropriate_mode(AWT_COMMAND_MODE mode); |
|---|
| 217 | |
|---|
| 218 | protected: |
|---|
| 219 | void store_command_data(AWT_command_data *new_cmd_data) { |
|---|
| 220 | delete cmd_data; |
|---|
| 221 | cmd_data = new_cmd_data; |
|---|
| 222 | } |
|---|
| 223 | AWT_command_data *get_command_data() { return cmd_data; } |
|---|
| 224 | |
|---|
| 225 | public: |
|---|
| 226 | |
|---|
| 227 | // *********** read only variables !!! |
|---|
| 228 | |
|---|
| 229 | AW_root *aw_root; |
|---|
| 230 | AP_tree_display_type tree_sort; |
|---|
| 231 | AP_tree *displayed_root; // root node of shown (sub-)tree; differs from real root if tree is zoomed logically |
|---|
| 232 | AP_tree_root *tree_static; |
|---|
| 233 | GBDATA *gb_main; |
|---|
| 234 | |
|---|
| 235 | // *********** public section |
|---|
| 236 | |
|---|
| 237 | AWT_graphic_tree(AW_root *aw_root, GBDATA *gb_main, AD_map_viewer_cb map_viewer_cb); |
|---|
| 238 | ~AWT_graphic_tree() OVERRIDE; |
|---|
| 239 | |
|---|
| 240 | AP_tree *get_root_node() { return tree_static ? tree_static->get_root_node() : NULL; } |
|---|
| 241 | bool is_logically_zoomed() { return displayed_root != get_root_node(); } |
|---|
| 242 | |
|---|
| 243 | void init(RootedTreeNodeFactory *nodeMaker_, AliView *aliview, AP_sequence *seq_prototype, bool link_to_database_, bool insert_delete_cbs); |
|---|
| 244 | AW_gc_manager init_devices(AW_window *, AW_device *, AWT_canvas *ntw) OVERRIDE; |
|---|
| 245 | |
|---|
| 246 | void show(AW_device *device) OVERRIDE; |
|---|
| 247 | const AW::Position& get_cursor() const { return cursor; } |
|---|
| 248 | |
|---|
| 249 | void info(AW_device *device, AW_pos x, AW_pos y, AW_clicked_line *cl, AW_clicked_text *ct) OVERRIDE; |
|---|
| 250 | |
|---|
| 251 | private: |
|---|
| 252 | void handle_key(AW_device *device, AWT_graphic_event& event); |
|---|
| 253 | public: |
|---|
| 254 | void handle_command(AW_device *device, AWT_graphic_event& event) OVERRIDE; |
|---|
| 255 | |
|---|
| 256 | void mark_species_in_tree(AP_tree *at, int mark); |
|---|
| 257 | void mark_species_in_tree_that(AP_tree *at, int mark, int (*condition)(GBDATA*, void*), void *cd); |
|---|
| 258 | |
|---|
| 259 | void mark_species_in_rest_of_tree(AP_tree *at, int mark); |
|---|
| 260 | void mark_species_in_rest_of_tree_that(AP_tree *at, int mark, int (*condition)(GBDATA*, void*), void *cd); |
|---|
| 261 | |
|---|
| 262 | bool tree_has_marks(AP_tree *at); |
|---|
| 263 | bool rest_tree_has_marks(AP_tree *at); |
|---|
| 264 | |
|---|
| 265 | void detect_group_state(AP_tree *at, AWT_graphic_tree_group_state *state, AP_tree *skip_this_son); |
|---|
| 266 | |
|---|
| 267 | bool group_tree(AP_tree *at, CollapseMode mode, int color_group); |
|---|
| 268 | void group_rest_tree(AP_tree *at, CollapseMode mode, int color_group); |
|---|
| 269 | void reorder_tree(TreeOrder mode); |
|---|
| 270 | GB_ERROR create_group(AP_tree * at) __ATTR__USERESULT; |
|---|
| 271 | void toggle_group(AP_tree * at); |
|---|
| 272 | GB_ERROR load(GBDATA *gb_main, const char *name, AW_CL, AW_CL) OVERRIDE __ATTR__USERESULT; |
|---|
| 273 | GB_ERROR save(GBDATA *gb_main, const char *name, AW_CL cd1, AW_CL cd2) OVERRIDE __ATTR__USERESULT; |
|---|
| 274 | int check_update(GBDATA *gb_main) OVERRIDE; // reload tree if needed |
|---|
| 275 | void update(GBDATA *gb_main) OVERRIDE; |
|---|
| 276 | void set_tree_type(AP_tree_display_type type, AWT_canvas *ntw); |
|---|
| 277 | |
|---|
| 278 | double get_irs_tree_ruler_scale_factor() const { return irs_tree_ruler_scale_factor; } |
|---|
| 279 | void show_ruler(AW_device *device, int gc); |
|---|
| 280 | void get_zombies_and_duplicates(int& zomb, int& dups) const { zomb = zombies; dups = duplicates; } |
|---|
| 281 | |
|---|
| 282 | #if defined(UNIT_TESTS) // UT_DIFF |
|---|
| 283 | friend class fake_AWT_graphic_tree; |
|---|
| 284 | #endif |
|---|
| 285 | }; |
|---|
| 286 | |
|---|
| 287 | class ClickedTarget { |
|---|
| 288 | /*! Represents any target corresponding to some (mouse-)position in the tree display. |
|---|
| 289 | * |
|---|
| 290 | * The target is e.g. used as target for keystrokes or mouse clicks. |
|---|
| 291 | * |
|---|
| 292 | * For AP_LIST_NDS, this only represents the species (w/o any tree information). |
|---|
| 293 | * For other tree display modes, this represents a specific tree node. |
|---|
| 294 | * |
|---|
| 295 | * The space outside the tree does represent the whole tree (aka the root-node). |
|---|
| 296 | * (the necessary distance to the tree-structure/-text is defined by AWT_CATCH) |
|---|
| 297 | */ |
|---|
| 298 | |
|---|
| 299 | AP_tree *tree_node; |
|---|
| 300 | GBDATA *gb_species; |
|---|
| 301 | bool ruler; |
|---|
| 302 | bool branch; |
|---|
| 303 | |
|---|
| 304 | const AW_clicked_element *elem; |
|---|
| 305 | |
|---|
| 306 | void init() { |
|---|
| 307 | tree_node = NULL; |
|---|
| 308 | gb_species = NULL; |
|---|
| 309 | ruler = false; |
|---|
| 310 | branch = false; |
|---|
| 311 | } |
|---|
| 312 | |
|---|
| 313 | void identify(AWT_graphic_tree *agt) { |
|---|
| 314 | init(); |
|---|
| 315 | if (elem && elem->exists) { |
|---|
| 316 | const char *what = (const char*)elem->cd2(); |
|---|
| 317 | |
|---|
| 318 | if (what) { |
|---|
| 319 | if (strcmp(what, "species") == 0) { // entry in NDS list |
|---|
| 320 | gb_species = (GBDATA*)elem->cd1(); |
|---|
| 321 | td_assert(gb_species); |
|---|
| 322 | } |
|---|
| 323 | else if (strcmp(what, "ruler") == 0) { |
|---|
| 324 | ruler = !elem->cd1(); |
|---|
| 325 | } |
|---|
| 326 | else if (strcmp(what, "branch") == 0) { |
|---|
| 327 | branch = true; // indicates that a line really IS the branch (opposed to other branch-related lines like e.g. group-brackets) |
|---|
| 328 | } |
|---|
| 329 | else { |
|---|
| 330 | td_assert(0); // unknown element type |
|---|
| 331 | } |
|---|
| 332 | } |
|---|
| 333 | |
|---|
| 334 | if (!(gb_species || ruler)) { |
|---|
| 335 | tree_node = (AP_tree*)elem->cd1(); |
|---|
| 336 | td_assert(branch || !what); |
|---|
| 337 | } |
|---|
| 338 | } |
|---|
| 339 | else { // use whole tree if mouse does not point to a subtree |
|---|
| 340 | tree_node = agt ? agt->get_root_node() : NULL; |
|---|
| 341 | } |
|---|
| 342 | td_assert(implicated(branch, tree_node)); |
|---|
| 343 | } |
|---|
| 344 | |
|---|
| 345 | public: |
|---|
| 346 | |
|---|
| 347 | ClickedTarget(AWT_graphic_tree *agt, const AW_clicked_element *clicked) : elem(clicked) { |
|---|
| 348 | // uses root of tree as target, when a position outside of the tree is selected |
|---|
| 349 | // (e.g. used for key-commands) |
|---|
| 350 | identify(agt); |
|---|
| 351 | } |
|---|
| 352 | ClickedTarget(const AW_clicked_element *clicked) : elem(clicked) { |
|---|
| 353 | // accept only normal branches as targets |
|---|
| 354 | identify(NULL); |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | const AW_clicked_element *element() const { return elem; } |
|---|
| 358 | AP_tree *node() const { return tree_node; } |
|---|
| 359 | GBDATA *species() const { return gb_species; } |
|---|
| 360 | |
|---|
| 361 | bool is_text() const { return elem && elem->is_text(); } |
|---|
| 362 | bool is_line() const { return elem && elem->is_line(); } |
|---|
| 363 | bool is_branch() const { return branch; } |
|---|
| 364 | bool is_ruler() const { return ruler; } |
|---|
| 365 | |
|---|
| 366 | double get_rel_attach() const { |
|---|
| 367 | // return [0..1] according to exact position where element is dropped |
|---|
| 368 | if (is_line() && (is_branch() || ruler)) return elem->get_rel_pos(); |
|---|
| 369 | return 0.5; // act like "drop on branch-center" |
|---|
| 370 | } |
|---|
| 371 | }; |
|---|
| 372 | |
|---|
| 373 | AWT_graphic_tree *NT_generate_tree(AW_root *root, GBDATA *gb_main, AD_map_viewer_cb map_viewer_cb); |
|---|
| 374 | bool AWT_show_branch_remark(AW_device *device, const char *remark_branch, bool is_leaf, AW_pos x, AW_pos y, AW_pos alignment, AW_bitset filteri); |
|---|
| 375 | void TREE_insert_jump_option_menu(AW_window *aws, const char *label, const char *awar_name); |
|---|
| 376 | |
|---|
| 377 | #else |
|---|
| 378 | #error TreeDisplay.hxx included twice |
|---|
| 379 | #endif // TREEDISPLAY_HXX |
|---|