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