3b3977da6a90ea0cc802dc4c2998626e4726b206
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanel.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.BasicStroke;
29 import java.awt.Color;
30 import java.awt.Cursor;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.FontMetrics;
34 import java.awt.GradientPaint;
35 import java.awt.Graphics;
36 import java.awt.Graphics2D;
37 import java.awt.Point;
38 import java.awt.Rectangle;
39 import java.awt.RenderingHints;
40 import java.awt.Stroke;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.awt.event.FocusAdapter;
44 import java.awt.event.FocusEvent;
45 import java.awt.event.InputEvent;
46 import java.awt.event.KeyAdapter;
47 import java.awt.event.KeyEvent;
48 import java.awt.event.MouseEvent;
49 import java.awt.event.MouseWheelEvent;
50 import java.awt.event.MouseWheelListener;
51 import java.awt.font.FontRenderContext;
52 import java.awt.font.TextAttribute;
53 import java.awt.font.TextLayout;
54 import java.awt.geom.AffineTransform;
55 import java.awt.geom.Arc2D;
56 import java.awt.geom.CubicCurve2D;
57 import java.awt.geom.Ellipse2D;
58 import java.awt.geom.Line2D;
59 import java.awt.geom.Path2D;
60 import java.awt.geom.QuadCurve2D;
61 import java.awt.geom.Rectangle2D;
62 import java.awt.image.BufferedImage;
63 import java.awt.print.PageFormat;
64 import java.awt.print.Printable;
65 import java.awt.print.PrinterException;
66 import java.io.File;
67 import java.io.IOException;
68 import java.io.UnsupportedEncodingException;
69 import java.net.URI;
70 import java.net.URISyntaxException;
71 import java.net.URLEncoder;
72 import java.text.AttributedString;
73 import java.text.DecimalFormat;
74 import java.text.DecimalFormatSymbols;
75 import java.text.NumberFormat;
76 import java.util.ArrayList;
77 import java.util.Collections;
78 import java.util.HashMap;
79 import java.util.HashSet;
80 import java.util.Hashtable;
81 import java.util.Iterator;
82 import java.util.List;
83 import java.util.Map;
84 import java.util.Set;
85 import java.util.SortedSet;
86
87 import javax.swing.BorderFactory;
88 import javax.swing.JColorChooser;
89 import javax.swing.JDialog;
90 import javax.swing.JMenuItem;
91 import javax.swing.JOptionPane;
92 import javax.swing.JPanel;
93 import javax.swing.JPopupMenu;
94 import javax.swing.JTextArea;
95 import javax.swing.Popup;
96 import javax.swing.PopupFactory;
97
98 import org.forester.archaeopteryx.Configuration.EXT_NODE_DATA_RETURN_ON;
99 import org.forester.archaeopteryx.ControlPanel.NodeClickAction;
100 import org.forester.archaeopteryx.Options.CLADOGRAM_TYPE;
101 import org.forester.archaeopteryx.Options.NODE_LABEL_DIRECTION;
102 import org.forester.archaeopteryx.Options.PHYLOGENY_GRAPHICS_TYPE;
103 import org.forester.archaeopteryx.phylogeny.data.RenderableDomainArchitecture;
104 import org.forester.archaeopteryx.phylogeny.data.RenderableMsaSequence;
105 import org.forester.archaeopteryx.phylogeny.data.RenderableVector;
106 import org.forester.archaeopteryx.tools.Blast;
107 import org.forester.archaeopteryx.tools.ImageLoader;
108 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
109 import org.forester.io.writers.SequenceWriter;
110 import org.forester.phylogeny.Phylogeny;
111 import org.forester.phylogeny.PhylogenyMethods;
112 import org.forester.phylogeny.PhylogenyMethods.DESCENDANT_SORT_PRIORITY;
113 import org.forester.phylogeny.PhylogenyNode;
114 import org.forester.phylogeny.data.Accession;
115 import org.forester.phylogeny.data.Annotation;
116 import org.forester.phylogeny.data.BranchColor;
117 import org.forester.phylogeny.data.Confidence;
118 import org.forester.phylogeny.data.DomainArchitecture;
119 import org.forester.phylogeny.data.Event;
120 import org.forester.phylogeny.data.NodeDataField;
121 import org.forester.phylogeny.data.NodeVisualData;
122 import org.forester.phylogeny.data.NodeVisualData.NodeFill;
123 import org.forester.phylogeny.data.NodeVisualData.NodeShape;
124 import org.forester.phylogeny.data.PhylogenyDataUtil;
125 import org.forester.phylogeny.data.ProteinDomain;
126 import org.forester.phylogeny.data.Sequence;
127 import org.forester.phylogeny.data.SequenceRelation;
128 import org.forester.phylogeny.data.Taxonomy;
129 import org.forester.phylogeny.data.Uri;
130 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
131 import org.forester.phylogeny.iterators.PreorderTreeIterator;
132 import org.forester.util.DescriptiveStatistics;
133 import org.forester.util.ForesterConstants;
134 import org.forester.util.ForesterUtil;
135 import org.forester.util.SequenceAccessionTools;
136 import org.forester.util.TaxonomyUtil;
137
138 public final class TreePanel extends JPanel implements ActionListener, MouseWheelListener, Printable {
139
140     final private class NodeColorizationActionListener implements ActionListener {
141
142         List<PhylogenyNode> _additional_nodes = null;
143         JColorChooser       _chooser          = null;
144         PhylogenyNode       _node             = null;
145
146         NodeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
147             _chooser = chooser;
148             _node = node;
149         }
150
151         NodeColorizationActionListener( final JColorChooser chooser,
152                                         final PhylogenyNode node,
153                                         final List<PhylogenyNode> additional_nodes ) {
154             _chooser = chooser;
155             _node = node;
156             _additional_nodes = additional_nodes;
157         }
158
159         @Override
160         public void actionPerformed( final ActionEvent e ) {
161             final Color c = _chooser.getColor();
162             if ( c != null ) {
163                 colorizeNodes( c, _node, _additional_nodes );
164             }
165         }
166     }
167
168     final private class SubtreeColorizationActionListener implements ActionListener {
169
170         List<PhylogenyNode> _additional_nodes = null;
171         JColorChooser       _chooser          = null;
172         PhylogenyNode       _node             = null;
173
174         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
175             _chooser = chooser;
176             _node = node;
177         }
178
179         SubtreeColorizationActionListener( final JColorChooser chooser,
180                                            final PhylogenyNode node,
181                                            final List<PhylogenyNode> additional_nodes ) {
182             _chooser = chooser;
183             _node = node;
184             _additional_nodes = additional_nodes;
185         }
186
187         @Override
188         public void actionPerformed( final ActionEvent e ) {
189             final Color c = _chooser.getColor();
190             if ( c != null ) {
191                 colorizeSubtree( c, _node, _additional_nodes );
192             }
193         }
194     }
195     public final static boolean           SPECIAL_DOMAIN_COLORING                            = true;
196     final static Cursor                   ARROW_CURSOR                                       = Cursor
197             .getPredefinedCursor( Cursor.DEFAULT_CURSOR );
198     final static Cursor                   CUT_CURSOR                                         = Cursor
199             .getPredefinedCursor( Cursor.CROSSHAIR_CURSOR );
200     final static Cursor                   HAND_CURSOR                                        = Cursor
201             .getPredefinedCursor( Cursor.HAND_CURSOR );
202     final static Cursor                   MOVE_CURSOR                                        = Cursor
203             .getPredefinedCursor( Cursor.MOVE_CURSOR );
204     final static Cursor                   WAIT_CURSOR                                        = Cursor
205             .getPredefinedCursor( Cursor.WAIT_CURSOR );
206     final private static double           _180_OVER_PI                                       = 180.0 / Math.PI;
207     private static final float            ANGLE_ROTATION_UNIT                                = ( float ) ( Math.PI
208             / 32 );
209     private final static int              CONFIDENCE_LEFT_MARGIN                             = 4;
210     private final static int              EURO_D                                             = 10;
211     private final static NumberFormat     FORMATTER_BRANCH_LENGTH;
212     private final static NumberFormat     FORMATTER_CONFIDENCE;
213     private static final float            HALF_PI                                            = ( float ) ( Math.PI
214             / 2.0 );
215     private final static int              LIMIT_FOR_HQ_RENDERING                             = 2000;
216     private final static int              MAX_NODE_FRAMES                                    = 10;
217     private final static int              MAX_SUBTREES                                       = 100;
218     private final static int              MIN_ROOT_LENGTH                                    = 3;
219     private final static int              MOVE                                               = 20;
220     private final static String           NODE_POPMENU_NODE_CLIENT_PROPERTY                  = "node";
221     private static final float            ONEHALF_PI                                         = ( float ) ( 1.5
222             * Math.PI );
223     private static final short            OV_BORDER                                          = 10;
224     private final static double           OVERVIEW_FOUND_NODE_BOX_SIZE                       = 2;
225     private final static double           OVERVIEW_FOUND_NODE_BOX_SIZE_HALF                  = 1;
226     private static final float            PI                                                 = ( float ) ( Math.PI );
227     final private static Font             POPUP_FONT                                         = new Font( Configuration
228             .getDefaultFontFamilyName(), Font.PLAIN, 12 );
229     private static final float            ROUNDED_D                                          = 8;
230     private final static long             serialVersionUID                                   = -978349745916505029L;
231     private static final BasicStroke      STROKE_0025                                        = new BasicStroke( 0.025f );
232     private static final BasicStroke      STROKE_005                                         = new BasicStroke( 0.05f );
233     private static final BasicStroke      STROKE_01                                          = new BasicStroke( 0.1f );
234     private static final BasicStroke      STROKE_025                                         = new BasicStroke( 0.25f );
235     private static final BasicStroke      STROKE_05                                          = new BasicStroke( 0.5f );
236     private static final BasicStroke      STROKE_075                                         = new BasicStroke( 0.75f );
237     private static final BasicStroke      STROKE_1                                           = new BasicStroke( 1f );
238     private static final BasicStroke      STROKE_2                                           = new BasicStroke( 2f );
239     private static final BasicStroke      STROKE_01_DASHED                                   = new BasicStroke( 0.1f,
240                                                                                                                 BasicStroke.CAP_SQUARE,
241                                                                                                                 BasicStroke.JOIN_ROUND,
242                                                                                                                 0,
243                                                                                                                 new float[] {
244                                                                                                                         2.0f },
245                                                                                                                 0f );
246     private static final BasicStroke      STROKE_005_DASHED                                  = new BasicStroke( 0.05f,
247                                                                                                                 BasicStroke.CAP_SQUARE,
248                                                                                                                 BasicStroke.JOIN_ROUND,
249                                                                                                                 0,
250                                                                                                                 new float[] {
251                                                                                                                         2.0f },
252                                                                                                                 0f );
253     private static final BasicStroke      STROKE_001_DASHED                                  = new BasicStroke( 0.01f,
254                                                                                                                 BasicStroke.CAP_SQUARE,
255                                                                                                                 BasicStroke.JOIN_ROUND,
256                                                                                                                 0,
257                                                                                                                 new float[] {
258                                                                                                                         2.0f },
259                                                                                                                 0f );
260     private static final double           TWO_PI                                             = 2 * Math.PI;
261     private final static int              WIGGLE                                             = 3;
262     private static final String           SHOW_ONLY_THIS_CONF_TYPE                           = null;                                                     //TODO remove me
263     HashMap<Long, Short>                  _nodeid_dist_to_leaf                               = new HashMap<Long, Short>();
264     final private Arc2D                   _arc                                               = new Arc2D.Double();
265     private AffineTransform               _at;
266     private int                             _clicked_x;
267     private int                           _circ_max_depth;
268     final private Set<Long>               _collapsed_external_nodeid_set                     = new HashSet<Long>();
269     private JColorChooser                 _color_chooser                                     = null;
270     private Configuration                 _configuration                                     = null;
271     private ControlPanel                  _control_panel                                     = null;
272     private final CubicCurve2D            _cubic_curve                                       = new CubicCurve2D.Float();
273     private Set<Long>                     _current_external_nodes                            = null;
274     private StringBuilder                 _current_external_nodes_data_buffer                = new StringBuilder();
275     private int                           _current_external_nodes_data_buffer_change_counter = 0;
276     private int                           _domain_structure_e_value_thr_exp                  = AptxConstants.DOMAIN_STRUCTURE_E_VALUE_THR_DEFAULT_EXP;
277     private double                        _domain_structure_width                            = AptxConstants.DOMAIN_STRUCTURE_DEFAULT_WIDTH;
278     private int                           _dynamic_hiding_factor                             = 0;
279     private boolean                       _edited                                            = false;
280     private final Ellipse2D               _ellipse                                           = new Ellipse2D.Float();
281     private int                           _external_node_index                               = 0;
282     private Set<Long>                     _found_nodes_0                                     = null;
283     private Set<Long>                     _found_nodes_1                                     = null;
284     private final FontRenderContext       _frc                                               = new FontRenderContext( null,
285                                                                                                                       false,
286                                                                                                                       false );
287     private float                           _furthest_node_x;
288     private PHYLOGENY_GRAPHICS_TYPE       _graphics_type                                     = PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR;
289     private PhylogenyNode                 _highlight_node                                    = null;
290     private boolean                       _in_ov                                             = false;
291     private boolean                       _in_ov_rect                                        = false;
292     private float                         _last_drag_point_x                                 = 0;
293     private float                         _last_drag_point_y                                 = 0;
294     private final Line2D                  _line                                              = new Line2D.Float();
295     private int                           _longest_ext_node_info                             = 0;
296     private PhylogenyNode                 _ext_node_with_longest_txt_info                    = null;
297     private MainPanel                     _main_panel                                        = null;
298     private double                        _max_distance_to_root                              = -1;
299     private Popup                         _node_desc_popup;
300     private int                           _node_frame_index                                  = 0;
301     private final NodeFrame[]             _node_frames                                       = new NodeFrame[ TreePanel.MAX_NODE_FRAMES ];
302     private JPopupMenu                    _node_popup_menu                                   = null;
303     private JMenuItem                     _node_popup_menu_items[]                           = null;
304     private PhylogenyNode[]               _nodes_in_preorder                                 = null;
305     private Options                       _options                                           = null;
306     private float                         _ov_max_height                                     = 0;
307     private float                         _ov_max_width                                      = 0;
308     private boolean                       _ov_on                                             = false;
309     private final Rectangle2D             _ov_rectangle                                      = new Rectangle2D.Float();
310     private final Rectangle               _ov_virtual_rectangle                              = new Rectangle();
311     private float                         _ov_x_correction_factor                            = 0.0f;
312     private float                         _ov_x_distance                                     = 0;
313     private int                           _ov_x_position                                     = 0;
314     private float                         _ov_y_distance                                     = 0;
315     private int                           _ov_y_position                                     = 0;
316     private int                           _ov_y_start                                        = 0;
317     private boolean                     _partition_tree                                     = false;
318     private final boolean                 _phy_has_branch_lengths;
319     private Phylogeny                     _phylogeny                                         = null;
320     private final Path2D.Float            _polygon                                           = new Path2D.Float();
321     private final StringBuffer            _popup_buffer                                      = new StringBuffer();
322     private final QuadCurve2D             _quad_curve                                        = new QuadCurve2D.Float();
323     private Sequence                      _query_sequence                                    = null;
324     private final Rectangle2D             _rectangle                                         = new Rectangle2D.Float();
325     private final RenderingHints          _rendering_hints                                   = new RenderingHints( RenderingHints.KEY_RENDERING,
326                                                                                                                    RenderingHints.VALUE_RENDER_DEFAULT );
327     private JTextArea                     _rollover_popup;
328     private PhylogenyNode                 _root;
329     private float                           _root_x;
330     private final StringBuilder           _sb                                                = new StringBuilder();
331     private double                        _scale_distance                                    = 0.0;
332     private String                        _scale_label                                       = null;
333     private DescriptiveStatistics         _statistics_for_vector_data;
334     private final Phylogeny[]             _sub_phylogenies                                   = new Phylogeny[ TreePanel.MAX_SUBTREES ];
335     private final PhylogenyNode[]         _sub_phylogenies_temp_roots                        = new PhylogenyNode[ TreePanel.MAX_SUBTREES ];
336     private int                           _subtree_index                                     = 0;
337     private File                          _treefile                                          = null;
338     private float                         _urt_factor                                        = 1;
339     private float                         _urt_factor_ov                                     = 1;
340     final private HashMap<Long, Double>   _urt_nodeid_angle_map                              = new HashMap<Long, Double>();
341     final private HashMap<Long, Integer>  _urt_nodeid_index_map                              = new HashMap<Long, Integer>();
342     private double                        _urt_starting_angle                                = ( float ) ( Math.PI
343             / 2 );
344     private float                         _x_correction_factor                               = 0.0f;
345     private float                         _x_distance                                        = 0.0f;
346     private float                         _y_distance                                        = 0.0f;
347     private int                           _length_of_longest_text;
348     private int                           _longest_domain;
349     private Map<String, AttributedString> _attributed_string_map                             = null;
350     private int                           _depth_collapse_level                              = -1;
351     private int                           _rank_collapse_level                               = -1;
352
353
354
355     static {
356         final DecimalFormatSymbols dfs = new DecimalFormatSymbols();
357         dfs.setDecimalSeparator( '.' );
358         FORMATTER_CONFIDENCE = new DecimalFormat( "#.###", dfs );
359         FORMATTER_BRANCH_LENGTH = new DecimalFormat( "#.###", dfs );
360     }
361
362     TreePanel( final Phylogeny t, final Configuration configuration, final MainPanel tjp ) {
363         requestFocusInWindow();
364         addKeyListener( new KeyAdapter() {
365
366             @Override
367             public void keyPressed( final KeyEvent key_event ) {
368                 keyPressedCalls( key_event );
369                 requestFocusInWindow();
370             }
371         } );
372         addFocusListener( new FocusAdapter() {
373
374             @Override
375             public void focusGained( final FocusEvent e ) {
376                 requestFocusInWindow();
377             }
378         } );
379         if ( ( t == null ) || t.isEmpty() ) {
380             throw new IllegalArgumentException( "attempt to draw phylogeny which is null or empty" );
381         }
382         _graphics_type = tjp.getOptions().getPhylogenyGraphicsType();
383         _main_panel = tjp;
384         _configuration = configuration;
385         _phylogeny = t;
386         _phy_has_branch_lengths = AptxUtil.isHasAtLeastOneBranchLengthLargerThanZero( _phylogeny );
387         init();
388         // if ( !_phylogeny.isEmpty() ) {
389         _phylogeny.recalculateNumberOfExternalDescendants( true );
390         // }
391         setBackground( getTreeColorSet().getBackgroundColor() );
392         final MouseListener mouse_listener = new MouseListener( this );
393         addMouseListener( mouse_listener );
394         addMouseMotionListener( mouse_listener );
395         addMouseWheelListener( this );
396         calculateScaleDistance();
397         FORMATTER_CONFIDENCE.setMaximumFractionDigits( configuration.getNumberOfDigitsAfterCommaForConfidenceValues() );
398         FORMATTER_BRANCH_LENGTH
399                 .setMaximumFractionDigits( configuration.getNumberOfDigitsAfterCommaForBranchLengthValues() );
400     }
401
402     @Override
403     final public void actionPerformed( final ActionEvent e ) {
404         boolean done = false;
405         final JMenuItem node_popup_menu_item = ( JMenuItem ) e.getSource();
406         for( int index = 0; ( index < _node_popup_menu_items.length ) && !done; index++ ) {
407             // NOTE: index corresponds to the indices of click-to options
408             // in the control panel.
409             if ( node_popup_menu_item == _node_popup_menu_items[ index ] ) {
410                 // Set this as the new default click-to action
411                 _main_panel.getControlPanel().setClickToAction( index );
412                 final PhylogenyNode node = ( PhylogenyNode ) _node_popup_menu
413                         .getClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY );
414                 handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
415                 done = true;
416             }
417         }
418         repaint();
419         requestFocusInWindow();
420     }
421
422     public synchronized Hashtable<String, BufferedImage> getImageMap() {
423         return getMainPanel().getImageMap();
424     }
425
426     final public MainPanel getMainPanel() {
427         return _main_panel;
428     }
429
430     /**
431      * Get a pointer to the phylogeny
432      *
433      * @return a pointer to the phylogeny
434      */
435     public final Phylogeny getPhylogeny() {
436         return _phylogeny;
437     }
438
439     public final TreeColorSet getTreeColorSet() {
440         return getMainPanel().getTreeColorSet();
441     }
442
443     @Override
444     final public void mouseWheelMoved( final MouseWheelEvent e ) {
445         final int notches = e.getWheelRotation();
446         if ( inOvVirtualRectangle( e ) ) {
447             if ( !isInOvRect() ) {
448                 setInOvRect( true );
449                 repaint();
450             }
451         }
452         else {
453             if ( isInOvRect() ) {
454                 setInOvRect( false );
455                 repaint();
456             }
457         }
458         if ( e.isControlDown() && e.isShiftDown() ) {
459             if ( notches < 0 ) {
460                 getTreeFontSet().increaseFontSize();
461             }
462             else {
463                 getTreeFontSet().decreaseFontSize( 1, false );
464             }
465             getControlPanel().displayedPhylogenyMightHaveChanged( true );
466             resetPreferredSize();
467             updateOvSizes();
468             repaint();
469         }
470         else if ( e.isShiftDown() && e.isAltDown() ) {
471             if ( notches < 0 ) {
472                 for( int i = 0; i < ( -notches ); ++i ) {
473                     getControlPanel().zoomInX( AptxConstants.WHEEL_ZOOM_IN_FACTOR, AptxConstants.WHEEL_ZOOM_IN_FACTOR );
474                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
475                 }
476             }
477             else {
478                 for( int i = 0; i < notches; ++i ) {
479                     getControlPanel().zoomOutX( AptxConstants.WHEEL_ZOOM_OUT_FACTOR,
480                                                 AptxConstants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
481                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
482                 }
483             }
484         }
485         else if ( e.isShiftDown() ) {
486             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
487                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
488                 if ( notches < 0 ) {
489                     for( int i = 0; i < ( -notches ); ++i ) {
490                         setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
491                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
492                     }
493                 }
494                 else {
495                     for( int i = 0; i < notches; ++i ) {
496                         setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
497                         if ( getStartingAngle() < 0 ) {
498                             setStartingAngle( TWO_PI + getStartingAngle() );
499                         }
500                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
501                     }
502                 }
503             }
504             else {
505                 if ( notches < 0 ) {
506                     for( int i = 0; i < ( -notches ); ++i ) {
507                         getControlPanel().zoomInY( AptxConstants.WHEEL_ZOOM_IN_FACTOR );
508                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
509                     }
510                 }
511                 else {
512                     for( int i = 0; i < notches; ++i ) {
513                         getControlPanel().zoomOutY( AptxConstants.WHEEL_ZOOM_OUT_FACTOR );
514                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
515                     }
516                 }
517             }
518         }
519         else {
520             if ( notches < 0 ) {
521                 for( int i = 0; i < ( -notches ); ++i ) {
522                     getControlPanel().zoomInX( AptxConstants.WHEEL_ZOOM_IN_FACTOR,
523                                                AptxConstants.WHEEL_ZOOM_IN_X_CORRECTION_FACTOR );
524                     getControlPanel().zoomInY( AptxConstants.WHEEL_ZOOM_IN_FACTOR );
525                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
526                 }
527             }
528             else {
529                 for( int i = 0; i < notches; ++i ) {
530                     getControlPanel().zoomOutY( AptxConstants.WHEEL_ZOOM_OUT_FACTOR );
531                     getControlPanel().zoomOutX( AptxConstants.WHEEL_ZOOM_OUT_FACTOR,
532                                                 AptxConstants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
533                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
534                 }
535             }
536         }
537         requestFocus();
538         requestFocusInWindow();
539         requestFocus();
540     }
541
542     @Override
543     final public void paintComponent( final Graphics g ) {
544         final Graphics2D g2d = ( Graphics2D ) g;
545         g2d.setRenderingHints( _rendering_hints );
546         paintPhylogeny( g2d, false, false, 0, 0, 0, 0 );
547     }
548
549     @Override
550     final public int print( final Graphics g, final PageFormat page_format, final int page_index )
551             throws PrinterException {
552         if ( page_index > 0 ) {
553             return ( NO_SUCH_PAGE );
554         }
555         else {
556             final Graphics2D g2d = ( Graphics2D ) g;
557             g2d.translate( page_format.getImageableX(), page_format.getImageableY() );
558             // Turn off double buffering !?
559             paintPhylogeny( g2d, true, false, 0, 0, 0, 0 );
560             // Turn double buffering back on !?
561             return ( PAGE_EXISTS );
562         }
563     }
564
565     public final void setEdited( final boolean edited ) {
566         _edited = edited;
567     }
568
569     public synchronized void setImageMap( final Hashtable<String, BufferedImage> image_map ) {
570         getMainPanel().setImageMap( image_map );
571     }
572
573     /**
574      * Set a phylogeny tree.
575      *
576      * @param t
577      *            an instance of a Phylogeny
578      */
579     public final void setTree( final Phylogeny t ) {
580         setNodeInPreorderToNull();
581         _phylogeny = t;
582     }
583
584     public final void setWaitCursor() {
585         setCursor( WAIT_CURSOR );
586         repaint();
587     }
588
589     @Override
590     public void update( final Graphics g ) {
591         paint( g );
592     }
593
594     private void abbreviateScientificName( final String sn, final StringBuilder sb ) {
595         final String[] a = sn.split( "\\s+" );
596         sb.append( a[ 0 ].substring( 0, 1 ) );
597         sb.append( a[ 1 ].substring( 0, 2 ) );
598         if ( a.length > 2 ) {
599             for( int i = 2; i < a.length; i++ ) {
600                 sb.append( " " );
601                 sb.append( a[ i ] );
602             }
603         }
604     }
605
606     final private void addEmptyNode( final PhylogenyNode node ) {
607         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
608             errorMessageNoCutCopyPasteInUnrootedDisplay();
609             return;
610         }
611         final String label = createASimpleTextRepresentationOfANode( node );
612         String msg = "";
613         if ( ForesterUtil.isEmpty( label ) ) {
614             msg = "How to add the new, empty node?";
615         }
616         else {
617             msg = "How to add the new, empty node to node" + label + "?";
618         }
619         final Object[] options = { "As sibling", "As descendant", "Cancel" };
620         final int r = JOptionPane.showOptionDialog( this,
621                                                     msg,
622                                                     "Addition of Empty New Node",
623                                                     JOptionPane.CLOSED_OPTION,
624                                                     JOptionPane.QUESTION_MESSAGE,
625                                                     null,
626                                                     options,
627                                                     options[ 2 ] );
628         boolean add_as_sibling = true;
629         if ( r == 1 ) {
630             add_as_sibling = false;
631         }
632         else if ( r != 0 ) {
633             return;
634         }
635         final Phylogeny phy = new Phylogeny();
636         phy.setRoot( new PhylogenyNode() );
637         phy.setRooted( true );
638         if ( add_as_sibling ) {
639             if ( node.isRoot() ) {
640                 JOptionPane.showMessageDialog( this,
641                                                "Cannot add sibling to root",
642                                                "Attempt to add sibling to root",
643                                                JOptionPane.ERROR_MESSAGE );
644                 return;
645             }
646             phy.addAsSibling( node );
647         }
648         else {
649             phy.addAsChild( node );
650         }
651         setNodeInPreorderToNull();
652         _phylogeny.externalNodesHaveChanged();
653         _phylogeny.clearHashIdToNodeMap();
654         _phylogeny.recalculateNumberOfExternalDescendants( true );
655         resetNodeIdToDistToLeafMap();
656         setEdited( true );
657         repaint();
658     }
659
660     final private void addToCurrentExternalNodes( final long i ) {
661         if ( _current_external_nodes == null ) {
662             _current_external_nodes = new HashSet<Long>();
663         }
664         _current_external_nodes.add( i );
665     }
666
667     final private void assignGraphicsForBranchWithColorForParentBranch( final PhylogenyNode node,
668                                                                         final boolean is_vertical,
669                                                                         final Graphics g,
670                                                                         final boolean to_pdf,
671                                                                         final boolean to_graphics_file ) {
672         final NodeClickAction action = _control_panel.getActionWhenNodeClicked();
673         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
674             g.setColor( Color.BLACK );
675         }
676         else if ( ( ( action == NodeClickAction.COPY_SUBTREE ) || ( action == NodeClickAction.CUT_SUBTREE )
677                 || ( action == NodeClickAction.DELETE_NODE_OR_SUBTREE ) || ( action == NodeClickAction.PASTE_SUBTREE )
678                 || ( action == NodeClickAction.ADD_NEW_NODE ) ) && ( getCutOrCopiedTree() != null )
679                 && ( getCopiedAndPastedNodes() != null ) && !to_pdf && !to_graphics_file
680                 && getCopiedAndPastedNodes().contains( node.getId() ) ) {
681             g.setColor( getTreeColorSet().getFoundColor0() );
682         }
683         else if ( getControlPanel().isUseVisualStyles() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
684             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
685         }
686         else if ( to_pdf ) {
687             g.setColor( getTreeColorSet().getBranchColorForPdf() );
688         }
689         else {
690             g.setColor( getTreeColorSet().getBranchColor() );
691         }
692     }
693
694     final private void blast( final PhylogenyNode node ) {
695         if ( !isCanBlast( node ) ) {
696             JOptionPane.showMessageDialog( this,
697                                            "Insufficient information present",
698                                            "Cannot Blast",
699                                            JOptionPane.INFORMATION_MESSAGE );
700             return;
701         }
702         else {
703             final String query = Blast.obtainQueryForBlast( node );
704             System.out.println( "query for BLAST is: " + query );
705             char type = '?';
706             if ( !ForesterUtil.isEmpty( query ) ) {
707                 if ( node.getNodeData().isHasSequence() ) {
708                     if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getType() ) ) {
709                         if ( node.getNodeData().getSequence().getType().toLowerCase()
710                                 .equals( PhyloXmlUtil.SEQ_TYPE_PROTEIN ) ) {
711                             type = 'p';
712                         }
713                         else {
714                             type = 'n';
715                         }
716                     }
717                     else if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) {
718                         if ( ForesterUtil
719                                 .seqIsLikelyToBeAa( node.getNodeData().getSequence().getMolecularSequence() ) ) {
720                             type = 'p';
721                         }
722                         else {
723                             type = 'n';
724                         }
725                     }
726                 }
727                 if ( type == '?' ) {
728                     if ( SequenceAccessionTools.isProteinDbQuery( query ) ) {
729                         type = 'p';
730                     }
731                     else {
732                         type = 'n';
733                     }
734                 }
735                 try {
736                     Blast.openNcbiBlastWeb( query, type == 'n', this );
737                 }
738                 catch ( final Exception e ) {
739                     e.printStackTrace();
740                 }
741             }
742         }
743     }
744
745     private final int calcDynamicHidingFactor() {
746         return ( int ) ( 0.5 + ( getFontMetricsForLargeDefaultFont().getHeight() / ( 1.5 * getYdistance() ) ) );
747     }
748
749     final private int calcLengthOfLongestText() {
750         final StringBuilder sb = new StringBuilder();
751         if ( _ext_node_with_longest_txt_info != null ) {
752             nodeDataAsSB( _ext_node_with_longest_txt_info, sb );
753             if ( _ext_node_with_longest_txt_info.getNodeData().isHasTaxonomy() ) {
754                 nodeTaxonomyDataAsSB( _ext_node_with_longest_txt_info.getNodeData().getTaxonomy(), sb );
755             }
756         }
757         return getFontMetricsForLargeDefaultFont().stringWidth( sb.toString() );
758     }
759
760     /**
761      * Calculate the length of the distance between the given node and its
762      * parent.
763      *
764      * @param node
765      * @param ext_node_x
766      * @factor
767      * @return the distance value
768      */
769     final private float calculateBranchLengthToParent( final PhylogenyNode node, final float factor ) {
770         if ( getControlPanel().isDrawPhylogram() ) {
771             if ( node.getDistanceToParent() < 0.0 ) {
772                 return 0.0f;
773             }
774             return ( float ) ( getXcorrectionFactor() * node.getDistanceToParent() );
775         }
776         else {
777             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
778                 return getXdistance();
779             }
780             return getXdistance() * factor;
781         }
782     }
783
784     final private Color calculateColorForAnnotation( final SortedSet<Annotation> ann ) {
785         Color c = getTreeColorSet().getAnnotationColor();
786         if ( getControlPanel().isColorAccordingToAnnotation() && ( getControlPanel().getAnnotationColors() != null ) ) {
787             final StringBuilder sb = new StringBuilder();
788             for( final Annotation a : ann ) {
789                 sb.append( !ForesterUtil.isEmpty( a.getRefValue() ) ? a.getRefValue() : a.getDesc() );
790             }
791             final String ann_str = sb.toString();
792             if ( !ForesterUtil.isEmpty( ann_str ) ) {
793                 c = getControlPanel().getAnnotationColors().get( ann_str );
794                 if ( c == null ) {
795                     c = AptxUtil.calculateColorFromString( ann_str, false );
796                     getControlPanel().getAnnotationColors().put( ann_str, c );
797                 }
798                 if ( c == null ) {
799                     c = getTreeColorSet().getAnnotationColor();
800                 }
801             }
802         }
803         return c;
804     }
805
806     final private float calculateOvBranchLengthToParent( final PhylogenyNode node, final int factor ) {
807         if ( getControlPanel().isDrawPhylogram() ) {
808             if ( node.getDistanceToParent() < 0.0 ) {
809                 return 0.0f;
810             }
811             return ( float ) ( getOvXcorrectionFactor() * node.getDistanceToParent() );
812         }
813         else {
814             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
815                 return getOvXDistance();
816             }
817             return getOvXDistance() * factor;
818         }
819     }
820
821     final private void cannotOpenBrowserWarningMessage( final String type_type ) {
822         JOptionPane.showMessageDialog( this,
823                                        "Cannot launch web browser for " + type_type + " data of this node",
824                                        "Cannot launch web browser",
825                                        JOptionPane.WARNING_MESSAGE );
826     }
827
828     private void changeNodeFont( final PhylogenyNode node ) {
829         final FontChooser fc = new FontChooser();
830         Font f = null;
831         if ( ( node.getNodeData().getNodeVisualData() != null ) && !node.getNodeData().getNodeVisualData().isEmpty() ) {
832             f = node.getNodeData().getNodeVisualData().getFont();
833         }
834         if ( f != null ) {
835             fc.setFont( f );
836         }
837         else {
838             fc.setFont( getMainPanel().getTreeFontSet().getLargeFont() );
839         }
840         List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
841         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
842             nodes = getFoundNodesAsListOfPhylogenyNodes();
843         }
844         if ( !nodes.contains( node ) ) {
845             nodes.add( node );
846         }
847         final int count = nodes.size();
848         String title = "Change the font for ";
849         if ( count == 1 ) {
850             title += "one node";
851         }
852         else {
853             title += ( count + " nodes" );
854         }
855         fc.showDialog( this, title );
856         if ( ( fc.getFont() != null ) && !ForesterUtil.isEmpty( fc.getFont().getFamily().trim() ) ) {
857             for( final PhylogenyNode n : nodes ) {
858                 if ( n.getNodeData().getNodeVisualData() == null ) {
859                     n.getNodeData().setNodeVisualData( new NodeVisualData() );
860                 }
861                 final NodeVisualData vd = n.getNodeData().getNodeVisualData();
862                 final Font ff = fc.getFont();
863                 vd.setFontName( ff.getFamily().trim() );
864                 int s = ff.getSize();
865                 if ( s < 0 ) {
866                     s = 0;
867                 }
868                 if ( s > Byte.MAX_VALUE ) {
869                     s = Byte.MAX_VALUE;
870                 }
871                 vd.setFontSize( s );
872                 vd.setFontStyle( ff.getStyle() );
873             }
874             if ( _control_panel.getUseVisualStylesCb() != null ) {
875                 getControlPanel().getUseVisualStylesCb().setSelected( true );
876             }
877         }
878         setEdited( true );
879         repaint();
880     }
881
882     final private void colorizeNodes( final Color c,
883                                       final PhylogenyNode node,
884                                       final List<PhylogenyNode> additional_nodes ) {
885         _control_panel.setColorBranches( true );
886         if ( _control_panel.getUseVisualStylesCb() != null ) {
887             _control_panel.getUseVisualStylesCb().setSelected( true );
888         }
889         if ( node != null ) {
890             colorizeNodesHelper( c, node );
891         }
892         if ( additional_nodes != null ) {
893             for( final PhylogenyNode n : additional_nodes ) {
894                 colorizeNodesHelper( c, n );
895             }
896         }
897         repaint();
898     }
899
900     final private void colorizeSubtree( final Color c,
901                                         final PhylogenyNode node,
902                                         final List<PhylogenyNode> additional_nodes ) {
903         _control_panel.setColorBranches( true );
904         if ( _control_panel.getUseVisualStylesCb() != null ) {
905             _control_panel.getUseVisualStylesCb().setSelected( true );
906         }
907         if ( node != null ) {
908             for( final PreorderTreeIterator it = new PreorderTreeIterator( node ); it.hasNext(); ) {
909                 it.next().getBranchData().setBranchColor( new BranchColor( c ) );
910             }
911         }
912         if ( additional_nodes != null ) {
913             for( final PhylogenyNode an : additional_nodes ) {
914                 for( final PreorderTreeIterator it = new PreorderTreeIterator( an ); it.hasNext(); ) {
915                     it.next().getBranchData().setBranchColor( new BranchColor( c ) );
916                 }
917             }
918         }
919         repaint();
920     }
921
922     private void colorNodeFont( final PhylogenyNode node ) {
923         _color_chooser.setPreviewPanel( new JPanel() );
924         NodeColorizationActionListener al;
925         int count = 1;
926         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
927             final List<PhylogenyNode> additional_nodes = getFoundNodesAsListOfPhylogenyNodes();
928             al = new NodeColorizationActionListener( _color_chooser, node, additional_nodes );
929             count = additional_nodes.size();
930             if ( !additional_nodes.contains( node ) ) {
931                 count++;
932             }
933         }
934         else {
935             al = new NodeColorizationActionListener( _color_chooser, node );
936         }
937         String title = "Change the (node and font) color for ";
938         if ( count == 1 ) {
939             title += "one node";
940         }
941         else {
942             title += ( count + " nodes" );
943         }
944         final JDialog dialog = JColorChooser.createDialog( this, title, true, _color_chooser, al, null );
945         setEdited( true );
946         dialog.setVisible( true );
947     }
948
949     final private void colorSubtree( final PhylogenyNode node ) {
950         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
951             JOptionPane.showMessageDialog( this,
952                                            "Cannot colorize subtree in unrooted display type",
953                                            "Attempt to colorize subtree in unrooted display",
954                                            JOptionPane.WARNING_MESSAGE );
955             return;
956         }
957         _color_chooser.setPreviewPanel( new JPanel() );
958         final SubtreeColorizationActionListener al;
959         final boolean color_found = getOptions().isColorAllFoundNodesWhenColoringSubtree();
960         if ( color_found && ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) ) {
961             final List<PhylogenyNode> additional_nodes = getFoundNodesAsListOfPhylogenyNodes();
962             al = new SubtreeColorizationActionListener( _color_chooser, node, additional_nodes );
963         }
964         else {
965             al = new SubtreeColorizationActionListener( _color_chooser, node );
966         }
967         final JDialog dialog = JColorChooser.createDialog( this,
968                                                            "Subtree colorization",
969                                                            true,
970                                                            _color_chooser,
971                                                            al,
972                                                            null );
973         setEdited( true );
974         dialog.setVisible( true );
975     }
976
977     final private void copySubtree( final PhylogenyNode node ) {
978         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
979             errorMessageNoCutCopyPasteInUnrootedDisplay();
980             return;
981         }
982         setNodeInPreorderToNull();
983         setCutOrCopiedTree( _phylogeny.copy( node ) );
984         final List<PhylogenyNode> nodes = PhylogenyMethods.getAllDescendants( node );
985         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
986         for( final PhylogenyNode n : nodes ) {
987             node_ids.add( n.getId() );
988         }
989         node_ids.add( node.getId() );
990         setCopiedAndPastedNodes( node_ids );
991         repaint();
992     }
993
994     final private String createASimpleTextRepresentationOfANode( final PhylogenyNode node ) {
995         final String tax = PhylogenyMethods.getSpecies( node );
996         String label = node.getName();
997         if ( !ForesterUtil.isEmpty( label ) && !ForesterUtil.isEmpty( tax ) ) {
998             label = label + " " + tax;
999         }
1000         else if ( !ForesterUtil.isEmpty( tax ) ) {
1001             label = tax;
1002         }
1003         else {
1004             label = "";
1005         }
1006         if ( !ForesterUtil.isEmpty( label ) ) {
1007             label = " [" + label + "]";
1008         }
1009         return label;
1010     }
1011
1012     final private void cutSubtree( final PhylogenyNode node ) {
1013         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
1014             errorMessageNoCutCopyPasteInUnrootedDisplay();
1015             return;
1016         }
1017         if ( node.isRoot() ) {
1018             JOptionPane.showMessageDialog( this,
1019                                            "Cannot cut entire tree as subtree",
1020                                            "Attempt to cut entire tree",
1021                                            JOptionPane.ERROR_MESSAGE );
1022             return;
1023         }
1024         final String label = createASimpleTextRepresentationOfANode( node );
1025         final int r = JOptionPane.showConfirmDialog( null,
1026                                                      "Cut subtree" + label + "?",
1027                                                      "Confirm Cutting of Subtree",
1028                                                      JOptionPane.YES_NO_OPTION );
1029         if ( r != JOptionPane.OK_OPTION ) {
1030             return;
1031         }
1032         setNodeInPreorderToNull();
1033         setCopiedAndPastedNodes( null );
1034         setCutOrCopiedTree( _phylogeny.copy( node ) );
1035         _phylogeny.deleteSubtree( node, true );
1036         _phylogeny.clearHashIdToNodeMap();
1037         _phylogeny.recalculateNumberOfExternalDescendants( true );
1038         resetNodeIdToDistToLeafMap();
1039         setEdited( true );
1040         repaint();
1041     }
1042
1043     final private void cycleColors() {
1044         getMainPanel().getTreeColorSet().cycleColorScheme();
1045         for( final TreePanel tree_panel : getMainPanel().getTreePanels() ) {
1046             tree_panel.setBackground( getMainPanel().getTreeColorSet().getBackgroundColor() );
1047         }
1048     }
1049
1050     final private void decreaseOvSize() {
1051         if ( ( getOvMaxWidth() > 20 ) && ( getOvMaxHeight() > 20 ) ) {
1052             setOvMaxWidth( getOvMaxWidth() - 5 );
1053             setOvMaxHeight( getOvMaxHeight() - 5 );
1054             updateOvSettings();
1055             getControlPanel().displayedPhylogenyMightHaveChanged( false );
1056         }
1057     }
1058
1059     final private void deleteNodeOrSubtree( final PhylogenyNode node ) {
1060         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
1061             errorMessageNoCutCopyPasteInUnrootedDisplay();
1062             return;
1063         }
1064         if ( node.isRoot() && ( node.getNumberOfDescendants() != 1 ) ) {
1065             JOptionPane.showMessageDialog( this,
1066                                            "Cannot delete entire tree",
1067                                            "Attempt to delete entire tree",
1068                                            JOptionPane.ERROR_MESSAGE );
1069             return;
1070         }
1071         final String label = createASimpleTextRepresentationOfANode( node );
1072         final Object[] options = { "Node only", "Entire subtree", "Cancel" };
1073         final int r = JOptionPane.showOptionDialog( this,
1074                                                     "Delete" + label + "?",
1075                                                     "Delete Node/Subtree",
1076                                                     JOptionPane.CLOSED_OPTION,
1077                                                     JOptionPane.QUESTION_MESSAGE,
1078                                                     null,
1079                                                     options,
1080                                                     options[ 2 ] );
1081         setNodeInPreorderToNull();
1082         boolean node_only = true;
1083         if ( r == 1 ) {
1084             node_only = false;
1085         }
1086         else if ( r != 0 ) {
1087             return;
1088         }
1089         if ( node_only ) {
1090             PhylogenyMethods.removeNode( node, _phylogeny );
1091         }
1092         else {
1093             _phylogeny.deleteSubtree( node, true );
1094         }
1095         _phylogeny.externalNodesHaveChanged();
1096         _phylogeny.clearHashIdToNodeMap();
1097         _phylogeny.recalculateNumberOfExternalDescendants( true );
1098         resetNodeIdToDistToLeafMap();
1099         setEdited( true );
1100         repaint();
1101     }
1102
1103     final private void displayNodePopupMenu( final PhylogenyNode node, final int x, final int y ) {
1104         makePopupMenus( node );
1105         _node_popup_menu.putClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY, node );
1106         _node_popup_menu.show( this, x, y );
1107     }
1108
1109     final private void drawArc( final double x,
1110                                 final double y,
1111                                 final double width,
1112                                 final double heigth,
1113                                 final double start_angle,
1114                                 final double arc_angle,
1115                                 final Graphics2D g ) {
1116         _arc.setArc( x, y, width, heigth, _180_OVER_PI * start_angle, _180_OVER_PI * arc_angle, Arc2D.OPEN );
1117         g.draw( _arc );
1118     }
1119
1120     final private void drawLine( final double x1,
1121                                  final double y1,
1122                                  final double x2,
1123                                  final double y2,
1124                                  final Graphics2D g ) {
1125         if ( ( x1 == x2 ) && ( y1 == y2 ) ) {
1126             return;
1127         }
1128         _line.setLine( x1, y1, x2, y2 );
1129         g.draw( _line );
1130     }
1131
1132     final private void drawOval( final double x,
1133                                  final double y,
1134                                  final double width,
1135                                  final double heigth,
1136                                  final Graphics2D g ) {
1137         _ellipse.setFrame( x, y, width, heigth );
1138         g.draw( _ellipse );
1139     }
1140
1141     final private void drawOvalFilled( final double x,
1142                                        final double y,
1143                                        final double width,
1144                                        final double heigth,
1145                                        final Graphics2D g ) {
1146         _ellipse.setFrame( x, y, width, heigth );
1147         g.fill( _ellipse );
1148     }
1149
1150     final private void drawOvalGradient( final float x,
1151                                          final float y,
1152                                          final float width,
1153                                          final float heigth,
1154                                          final Graphics2D g,
1155                                          final Color color_1,
1156                                          final Color color_2,
1157                                          final Color color_border ) {
1158         _ellipse.setFrame( x, y, width, heigth );
1159         g.setPaint( new GradientPaint( x, y, color_1, ( x + width ), ( y + heigth ), color_2, false ) );
1160         g.fill( _ellipse );
1161         if ( color_border != null ) {
1162             g.setPaint( color_border );
1163             g.draw( _ellipse );
1164         }
1165     }
1166
1167     final private void drawRect( final float x,
1168                                  final float y,
1169                                  final float width,
1170                                  final float heigth,
1171                                  final Graphics2D g ) {
1172         _rectangle.setFrame( x, y, width, heigth );
1173         g.draw( _rectangle );
1174     }
1175
1176     final private void drawRectFilled( final double x,
1177                                        final double y,
1178                                        final double width,
1179                                        final double heigth,
1180                                        final Graphics2D g ) {
1181         _rectangle.setFrame( x, y, width, heigth );
1182         g.fill( _rectangle );
1183     }
1184
1185     final private void drawRectGradient( final float x,
1186                                          final float y,
1187                                          final float width,
1188                                          final float heigth,
1189                                          final Graphics2D g,
1190                                          final Color color_1,
1191                                          final Color color_2,
1192                                          final Color color_border ) {
1193         _rectangle.setFrame( x, y, width, heigth );
1194         g.setPaint( new GradientPaint( x, y, color_1, ( x + width ), ( y + heigth ), color_2, false ) );
1195         g.fill( _rectangle );
1196         if ( color_border != null ) {
1197             g.setPaint( color_border );
1198             g.draw( _rectangle );
1199         }
1200     }
1201
1202     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
1203         final List<Uri> us = new ArrayList<Uri>();
1204         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
1205             for( final Uri uri : t.getUris() ) {
1206                 us.add( uri );
1207             }
1208         }
1209         double offset = 0;
1210         for( final Uri uri : us ) {
1211             if ( uri != null ) {
1212                 final String uri_str = uri.getValue().toString().toLowerCase();
1213                 if ( getImageMap().containsKey( uri_str ) ) {
1214                     final BufferedImage bi = getImageMap().get( uri_str );
1215                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
1216                         double scaling_factor = 1;
1217                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
1218                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
1219                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
1220                         }
1221                         // y = y - ( 0.9 * getYdistance() );
1222                         final double hs = bi.getHeight() * scaling_factor;
1223                         double ws = ( bi.getWidth() * scaling_factor ) + offset;
1224                         final double my_y = y - ( 0.5 * hs );
1225                         final int x_w = ( int ) ( x + ws + 0.5 );
1226                         final int y_h = ( int ) ( my_y + hs + 0.5 );
1227                         if ( ( ( x_w - x ) > 7 ) && ( ( y_h - my_y ) > 7 ) ) {
1228                             g.drawImage( bi,
1229                                          ( int ) ( x + 0.5 + offset ),
1230                                          ( int ) ( my_y + 0.5 ),
1231                                          x_w,
1232                                          y_h,
1233                                          0,
1234                                          0,
1235                                          bi.getWidth(),
1236                                          bi.getHeight(),
1237                                          null );
1238                             ws += 8;
1239                         }
1240                         else {
1241                             ws = 0.0;
1242                         }
1243                         offset = ws;
1244                     }
1245                 }
1246             }
1247         }
1248         return offset;
1249     }
1250
1251     final private void errorMessageNoCutCopyPasteInUnrootedDisplay() {
1252         JOptionPane.showMessageDialog( this,
1253                                        "Cannot cut, copy, paste, add, or delete subtrees/nodes in unrooted display",
1254                                        "Attempt to cut/copy/paste/add/delete in unrooted display",
1255                                        JOptionPane.ERROR_MESSAGE );
1256     }
1257
1258     private final Color getColorForFoundNode( final PhylogenyNode n ) {
1259         if ( isInCurrentExternalNodes( n ) ) {
1260             return getTreeColorSet().getFoundColor0();
1261         }
1262         else if ( isInFoundNodes0( n ) && !isInFoundNodes1( n ) ) {
1263             return getTreeColorSet().getFoundColor0();
1264         }
1265         else if ( !isInFoundNodes0( n ) && isInFoundNodes1( n ) ) {
1266             return getTreeColorSet().getFoundColor1();
1267         }
1268         else {
1269             return getTreeColorSet().getFoundColor0and1();
1270         }
1271     }
1272
1273     final private Set<Long> getCopiedAndPastedNodes() {
1274         return getMainPanel().getCopiedAndPastedNodes();
1275     }
1276
1277     final private Set<Long> getCurrentExternalNodes() {
1278         return _current_external_nodes;
1279     }
1280
1281     final private Phylogeny getCutOrCopiedTree() {
1282         return getMainPanel().getCutOrCopiedTree();
1283     }
1284
1285     private FontMetrics getFontMetricsForLargeDefaultFont() {
1286         return getTreeFontSet().getFontMetricsLarge();
1287     }
1288
1289     final private float getLastDragPointX() {
1290         return _last_drag_point_x;
1291     }
1292
1293     final private float getLastDragPointY() {
1294         return _last_drag_point_y;
1295     }
1296
1297     final private double getMaxDistanceToRoot() {
1298         if ( _max_distance_to_root < 0 ) {
1299             recalculateMaxDistanceToRoot();
1300         }
1301         return _max_distance_to_root;
1302     }
1303
1304     final private float getOvMaxHeight() {
1305         return _ov_max_height;
1306     }
1307
1308     final private float getOvMaxWidth() {
1309         return _ov_max_width;
1310     }
1311
1312     final private float getOvXcorrectionFactor() {
1313         return _ov_x_correction_factor;
1314     }
1315
1316     final private float getOvXDistance() {
1317         return _ov_x_distance;
1318     }
1319
1320     final private int getOvXPosition() {
1321         return _ov_x_position;
1322     }
1323
1324     final private float getOvYDistance() {
1325         return _ov_y_distance;
1326     }
1327
1328     final private int getOvYPosition() {
1329         return _ov_y_position;
1330     }
1331
1332     final private int getOvYStart() {
1333         return _ov_y_start;
1334     }
1335
1336     final private List<Accession> getPdbAccs( final PhylogenyNode node ) {
1337         final List<Accession> pdb_ids = new ArrayList<Accession>();
1338         if ( node.getNodeData().isHasSequence() ) {
1339             final Sequence seq = node.getNodeData().getSequence();
1340             if ( !ForesterUtil.isEmpty( seq.getCrossReferences() ) ) {
1341                 final SortedSet<Accession> cross_refs = seq.getCrossReferences();
1342                 for( final Accession acc : cross_refs ) {
1343                     if ( acc.getSource().equalsIgnoreCase( "pdb" ) ) {
1344                         pdb_ids.add( acc );
1345                     }
1346                 }
1347             }
1348         }
1349         return pdb_ids;
1350     }
1351
1352     final private double getScaleDistance() {
1353         return _scale_distance;
1354     }
1355
1356     final private String getScaleLabel() {
1357         return _scale_label;
1358     }
1359
1360     final private TreeFontSet getTreeFontSet() {
1361         return getMainPanel().getTreeFontSet();
1362     }
1363
1364     final private float getUrtFactor() {
1365         return _urt_factor;
1366     }
1367
1368     final private float getUrtFactorOv() {
1369         return _urt_factor_ov;
1370     }
1371
1372     final private void handleClickToAction( final NodeClickAction action, final PhylogenyNode node ) {
1373         switch ( action ) {
1374             case SHOW_DATA:
1375                 showNodeFrame( node );
1376                 break;
1377             case COLLAPSE:
1378                 collapse( node );
1379                 break;
1380             case REROOT:
1381                 reRoot( node );
1382                 break;
1383             case SUBTREE:
1384                 subTree( node );
1385                 break;
1386             case SWAP:
1387                 swap( node );
1388                 break;
1389             case COLOR_SUBTREE:
1390                 colorSubtree( node );
1391                 break;
1392             case COLOR_NODE_FONT:
1393                 colorNodeFont( node );
1394                 break;
1395             case CHANGE_NODE_FONT:
1396                 changeNodeFont( node );
1397                 break;
1398             case OPEN_SEQ_WEB:
1399                 openSeqWeb( node );
1400                 break;
1401             case BLAST:
1402                 blast( node );
1403                 break;
1404             case OPEN_TAX_WEB:
1405                 openTaxWeb( node );
1406                 break;
1407             case OPEN_PDB_WEB:
1408                 openPdbWeb( node );
1409                 break;
1410             case CUT_SUBTREE:
1411                 cutSubtree( node );
1412                 break;
1413             case COPY_SUBTREE:
1414                 copySubtree( node );
1415                 break;
1416             case PASTE_SUBTREE:
1417                 pasteSubtree( node );
1418                 break;
1419             case DELETE_NODE_OR_SUBTREE:
1420                 deleteNodeOrSubtree( node );
1421                 break;
1422             case ADD_NEW_NODE:
1423                 addEmptyNode( node );
1424                 break;
1425             case EDIT_NODE_DATA:
1426                 showNodeEditFrame( node );
1427                 break;
1428             case SELECT_NODES:
1429                 selectNode( node );
1430                 break;
1431             case SORT_DESCENDENTS:
1432                 sortDescendants( node );
1433                 break;
1434             case GET_EXT_DESC_DATA:
1435                 showExtDescNodeData( node, '_' );
1436                 break;
1437             case UNCOLLAPSE_ALL:
1438                 uncollapseAll( node );
1439                 break;
1440             case ORDER_SUBTREE:
1441                 orderSubtree( node );
1442                 break;
1443             default:
1444                 throw new IllegalArgumentException( "unknown action: " + action );
1445         }
1446     }
1447
1448     final private void increaseCurrentExternalNodesDataBufferChangeCounter() {
1449         _current_external_nodes_data_buffer_change_counter++;
1450     }
1451
1452     final private void increaseOvSize() {
1453         if ( ( getOvMaxWidth() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth()
1454                 / 2 ) )
1455                 && ( getOvMaxHeight() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
1456                         .getHeight() / 2 ) ) ) {
1457             setOvMaxWidth( getOvMaxWidth() + 5 );
1458             setOvMaxHeight( getOvMaxHeight() + 5 );
1459             updateOvSettings();
1460             getControlPanel().displayedPhylogenyMightHaveChanged( false );
1461         }
1462     }
1463
1464     final private void init() {
1465         _color_chooser = new JColorChooser();
1466         _rollover_popup = new JTextArea();
1467         _rollover_popup.setFont( POPUP_FONT );
1468         resetNodeIdToDistToLeafMap();
1469         setTextAntialias();
1470         setTreeFile( null );
1471         setEdited( false );
1472         initializeOvSettings();
1473         resetDepthCollapseDepthValue();
1474         resetRankCollapseRankValue();
1475         setStartingAngle( ( TWO_PI * 3 ) / 4 );
1476         final ImageLoader il = new ImageLoader( this );
1477         new Thread( il ).start();
1478     }
1479
1480     final private void initializeOvSettings() {
1481         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
1482         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
1483     }
1484
1485     final private boolean inOvVirtualRectangle( final int x, final int y ) {
1486         return ( ( x >= ( getOvVirtualRectangle().x - 1 ) )
1487                 && ( x <= ( getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 ) )
1488                 && ( y >= ( getOvVirtualRectangle().y - 1 ) )
1489                 && ( y <= ( getOvVirtualRectangle().y + getOvVirtualRectangle().height + 1 ) ) );
1490     }
1491
1492     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
1493         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
1494     }
1495
1496     final private boolean isCanBlast( final PhylogenyNode node ) {
1497         if ( !node.getNodeData().isHasSequence() && ForesterUtil.isEmpty( node.getName() ) ) {
1498             return false;
1499         }
1500         return Blast.isContainsQueryForBlast( node );
1501     }
1502
1503     final private String isCanOpenSeqWeb( final PhylogenyNode node ) {
1504         final Accession a = SequenceAccessionTools.obtainAccessorFromDataFields( node );
1505         if ( a != null ) {
1506             return a.getValue();
1507         }
1508         return null;
1509     }
1510
1511     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
1512         if ( node.getNodeData().isHasTaxonomy() && ( ( !ForesterUtil
1513                 .isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
1514                 || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
1515                 || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) )
1516                 || ( ( node.getNodeData().getTaxonomy().getIdentifier() != null )
1517                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) ) ) ) {
1518             return true;
1519         }
1520         else {
1521             return false;
1522         }
1523     }
1524
1525     final private boolean isInCurrentExternalNodes( final PhylogenyNode node ) {
1526         return ( ( getCurrentExternalNodes() != null ) && getCurrentExternalNodes().contains( node.getId() ) );
1527     }
1528
1529     private boolean isInFoundNodes( final PhylogenyNode n ) {
1530         return isInFoundNodes0( n ) || isInFoundNodes1( n );
1531     }
1532
1533     final private boolean isInFoundNodes0( final PhylogenyNode node ) {
1534         return ( ( getFoundNodes0() != null ) && getFoundNodes0().contains( node.getId() ) );
1535     }
1536
1537     final private boolean isInFoundNodes1( final PhylogenyNode node ) {
1538         return ( ( getFoundNodes1() != null ) && getFoundNodes1().contains( node.getId() ) );
1539     }
1540
1541     final private boolean isInOv() {
1542         return _in_ov;
1543     }
1544
1545     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
1546         int y_dist = 40;
1547         if ( getControlPanel().isShowTaxonomyImages() ) {
1548             y_dist = 40 + ( int ) getYdistance();
1549         }
1550         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - y_dist ) )
1551                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + y_dist ) )
1552                 || ( ( node.getParent() != null ) && ( node.getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
1553     }
1554
1555     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
1556         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - 20 ) )
1557                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + 20 ) )
1558                 || ( node.getXcoord() < ( getVisibleRect().getMinX() - 20 ) )
1559                 || ( node.getXcoord() > ( getVisibleRect().getMaxX() + 20 ) ) );
1560     }
1561
1562     final private boolean isNonLinedUpCladogram() {
1563         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
1564     }
1565
1566     final private void keyPressedCalls( final KeyEvent e ) {
1567         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
1568             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
1569                 if ( !isInOvRect() ) {
1570                     setInOvRect( true );
1571                 }
1572             }
1573             else if ( isInOvRect() ) {
1574                 setInOvRect( false );
1575             }
1576         }
1577         if ( e.isAltDown() ) {
1578             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
1579                     || ( e.getKeyCode() == KeyEvent.VK_C ) || ( e.getKeyCode() == KeyEvent.VK_BACK_SPACE ) ) {
1580                 getControlPanel().showWhole();
1581             }
1582             else if ( e.isShiftDown()
1583                     && ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) ) {
1584                 getMainPanel().getTreeFontSet().decreaseFontSize( 1, false );
1585                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1586             }
1587             else if ( e.isShiftDown() && plusPressed( e.getKeyCode() ) ) {
1588                 getMainPanel().getTreeFontSet().increaseFontSize();
1589                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1590             }
1591             else if ( e.getKeyCode() == KeyEvent.VK_O ) {
1592                 getControlPanel().orderPressed( this );
1593             }
1594             else if ( e.getKeyCode() == KeyEvent.VK_R ) {
1595                 getControlPanel().returnedToSuperTreePressed();
1596             }
1597             else if ( e.getKeyCode() == KeyEvent.VK_U ) {
1598                 getControlPanel().uncollapseAll( this );
1599                 getControlPanel().displayedPhylogenyMightHaveChanged( false );
1600             }
1601             else if ( e.getKeyCode() == KeyEvent.VK_UP ) {
1602                 getMainPanel().getControlPanel().zoomInY( AptxConstants.WHEEL_ZOOM_IN_FACTOR );
1603                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1604             }
1605             else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
1606                 getMainPanel().getControlPanel().zoomOutY( AptxConstants.WHEEL_ZOOM_OUT_FACTOR );
1607                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1608             }
1609             else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
1610                 getMainPanel().getControlPanel().zoomOutX( AptxConstants.WHEEL_ZOOM_OUT_FACTOR,
1611                                                            AptxConstants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
1612                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1613             }
1614             else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
1615                 getMainPanel().getControlPanel().zoomInX( AptxConstants.WHEEL_ZOOM_IN_FACTOR,
1616                                                           AptxConstants.WHEEL_ZOOM_IN_FACTOR );
1617                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1618             }
1619             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
1620                 getMainPanel().getControlPanel().zoomOutY( AptxConstants.WHEEL_ZOOM_OUT_FACTOR );
1621                 getMainPanel().getControlPanel().zoomOutX( AptxConstants.WHEEL_ZOOM_OUT_FACTOR,
1622                                                            AptxConstants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
1623                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1624             }
1625             else if ( plusPressed( e.getKeyCode() ) ) {
1626                 getMainPanel().getControlPanel().zoomInX( AptxConstants.WHEEL_ZOOM_IN_FACTOR,
1627                                                           AptxConstants.WHEEL_ZOOM_IN_FACTOR );
1628                 getMainPanel().getControlPanel().zoomInY( AptxConstants.WHEEL_ZOOM_IN_FACTOR );
1629                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1630             }
1631         }
1632         else {
1633             if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
1634                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
1635                 final int d = 80;
1636                 int dx = 0;
1637                 int dy = -d;
1638                 if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
1639                     dy = d;
1640                 }
1641                 else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
1642                     dx = -d;
1643                     dy = 0;
1644                 }
1645                 else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
1646                     dx = d;
1647                     dy = 0;
1648                 }
1649                 final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1650                 scroll_position.x = scroll_position.x + dx;
1651                 scroll_position.y = scroll_position.y + dy;
1652                 if ( scroll_position.x <= 0 ) {
1653                     scroll_position.x = 0;
1654                 }
1655                 else {
1656                     final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1657                             - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1658                     if ( scroll_position.x >= max_x ) {
1659                         scroll_position.x = max_x;
1660                     }
1661                 }
1662                 if ( scroll_position.y <= 0 ) {
1663                     scroll_position.y = 0;
1664                 }
1665                 else {
1666                     final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1667                             - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1668                     if ( scroll_position.y >= max_y ) {
1669                         scroll_position.y = max_y;
1670                     }
1671                 }
1672                 repaint();
1673                 getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1674             }
1675             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
1676                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1677                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1678                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
1679                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
1680                 }
1681             }
1682             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
1683                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1684                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1685                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
1686                     if ( getStartingAngle() < 0 ) {
1687                         setStartingAngle( TWO_PI + getStartingAngle() );
1688                     }
1689                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
1690                 }
1691             }
1692             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
1693                 boolean selected = false;
1694                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
1695                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
1696                     selected = true;
1697                 }
1698                 else {
1699                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
1700                 }
1701                 getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
1702                 repaint();
1703             }
1704             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
1705                 switchDisplaygetPhylogenyGraphicsType();
1706                 repaint();
1707             }
1708             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
1709                 cycleColors();
1710                 repaint();
1711             }
1712             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
1713                 MainFrame.cycleOverview( getOptions(), this );
1714                 repaint();
1715             }
1716             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
1717                 increaseOvSize();
1718             }
1719             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
1720                 decreaseOvSize();
1721             }
1722         }
1723         if ( e.getKeyCode() == KeyEvent.VK_HOME || e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
1724             getControlPanel().showWhole();
1725         }
1726         else if ( e.getKeyCode() == KeyEvent.VK_PAGE_UP ) {
1727             getMainPanel().getTreeFontSet().increaseFontSize();
1728             getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1729         }
1730         else if ( e.getKeyCode() == KeyEvent.VK_PAGE_DOWN ) {
1731             getMainPanel().getTreeFontSet().decreaseFontSize( 1, false );
1732             getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1733         }
1734         e.consume();
1735     }
1736
1737     final private void makePopupMenus( final PhylogenyNode node ) {
1738         _node_popup_menu = new JPopupMenu();
1739         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
1740         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
1741         for( int i = 0; i < clickto_names.size(); i++ ) {
1742             final String title = clickto_names.get( i );
1743             _node_popup_menu_items[ i ] = new JMenuItem( title );
1744             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
1745                 final String id = isCanOpenSeqWeb( node );
1746                 if ( !ForesterUtil.isEmpty( id ) ) {
1747                     _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " [" + id + "]" );
1748                     _node_popup_menu_items[ i ].setEnabled( true );
1749                 }
1750                 else {
1751                     _node_popup_menu_items[ i ].setEnabled( false );
1752                 }
1753             }
1754             else if ( title.equals( Configuration.clickto_options[ Configuration.open_pdb_web ][ 0 ] ) ) {
1755                 final List<Accession> accs = getPdbAccs( node );
1756                 _node_popup_menu_items[ i ] = new JMenuItem( title );
1757                 if ( !ForesterUtil.isEmpty( accs ) ) {
1758                     if ( accs.size() == 1 ) {
1759                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
1760                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + "]" );
1761                         _node_popup_menu_items[ i ].setEnabled( true );
1762                     }
1763                     else if ( accs.size() == 2 ) {
1764                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
1765                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
1766                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + "]" );
1767                         _node_popup_menu_items[ i ].setEnabled( true );
1768                     }
1769                     else if ( accs.size() == 3 ) {
1770                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
1771                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
1772                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + ", "
1773                                 + TreePanelUtil.pdbAccToString( accs, 2 ) + "]" );
1774                         _node_popup_menu_items[ i ].setEnabled( true );
1775                     }
1776                     else {
1777                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
1778                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
1779                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + ", "
1780                                 + TreePanelUtil.pdbAccToString( accs, 2 ) + ", + " + ( accs.size() - 3 ) + " more]" );
1781                         _node_popup_menu_items[ i ].setEnabled( true );
1782                     }
1783                 }
1784                 else {
1785                     _node_popup_menu_items[ i ].setEnabled( false );
1786                 }
1787             }
1788             else if ( title.startsWith( Configuration.clickto_options[ Configuration.get_ext_desc_data ][ 0 ] ) ) {
1789                 _node_popup_menu_items[ i ]
1790                         .setText( Configuration.clickto_options[ Configuration.get_ext_desc_data ][ 0 ] + ": "
1791                                 + getOptions().getExtDescNodeDataToReturn().toString() );
1792             }
1793             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
1794                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
1795             }
1796             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
1797                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
1798             }
1799             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
1800                 if ( !getOptions().isEditable() ) {
1801                     continue;
1802                 }
1803                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
1804             }
1805             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
1806                 if ( !getOptions().isEditable() ) {
1807                     continue;
1808                 }
1809                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
1810             }
1811             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
1812                 if ( !getOptions().isEditable() ) {
1813                     continue;
1814                 }
1815                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
1816             }
1817             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
1818                 if ( !getOptions().isEditable() ) {
1819                     continue;
1820                 }
1821                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
1822             }
1823             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
1824                 if ( !getOptions().isEditable() ) {
1825                     continue;
1826                 }
1827             }
1828             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
1829                 if ( !getOptions().isEditable() ) {
1830                     continue;
1831                 }
1832             }
1833             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
1834                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
1835             }
1836             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
1837                 _node_popup_menu_items[ i ].setEnabled( ( isCanCollapse() && !node.isExternal() ) );
1838             }
1839             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
1840                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
1841             }
1842             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
1843                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
1844             }
1845             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
1846                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
1847             }
1848             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
1849                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
1850             }
1851             else if ( title.equals( Configuration.clickto_options[ Configuration.uncollapse_all ][ 0 ] ) ) {
1852                 _node_popup_menu_items[ i ].setEnabled( isCanUncollapseAll( node ) );
1853             }
1854             _node_popup_menu_items[ i ].addActionListener( this );
1855             _node_popup_menu.add( _node_popup_menu_items[ i ] );
1856         }
1857     }
1858
1859     private final void nodeDataAsSB( final PhylogenyNode node, final StringBuilder sb ) {
1860         if ( node != null ) {
1861             if ( getControlPanel().isShowNodeNames() && ( !ForesterUtil.isEmpty( node.getName() ) ) ) {
1862                 if ( sb.length() > 0 ) {
1863                     sb.append( " " );
1864                 }
1865                 sb.append( node.getName() );
1866             }
1867             if ( node.getNodeData().isHasSequence() ) {
1868                 if ( getControlPanel().isShowSeqSymbols()
1869                         && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
1870                     if ( sb.length() > 0 ) {
1871                         sb.append( " " );
1872                     }
1873                     sb.append( node.getNodeData().getSequence().getSymbol() );
1874                 }
1875                 if ( getControlPanel().isShowGeneNames()
1876                         && ( node.getNodeData().getSequence().getGeneName().length() > 0 ) ) {
1877                     if ( sb.length() > 0 ) {
1878                         sb.append( " " );
1879                     }
1880                     sb.append( node.getNodeData().getSequence().getGeneName() );
1881                 }
1882                 if ( getControlPanel().isShowSeqNames()
1883                         && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
1884                     if ( sb.length() > 0 ) {
1885                         sb.append( " " );
1886                     }
1887                     sb.append( node.getNodeData().getSequence().getName() );
1888                 }
1889                 if ( getControlPanel().isShowSequenceAcc()
1890                         && ( node.getNodeData().getSequence().getAccession() != null ) ) {
1891                     if ( sb.length() > 0 ) {
1892                         sb.append( " " );
1893                     }
1894                     if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
1895                         sb.append( node.getNodeData().getSequence().getAccession().getSource() );
1896                         sb.append( ":" );
1897                     }
1898                     sb.append( node.getNodeData().getSequence().getAccession().getValue() );
1899                 }
1900             }
1901             if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
1902                 if ( sb.length() > 0 ) {
1903                     sb.append( " " );
1904                 }
1905                 sb.append( propertiesToString( node ) );
1906             }
1907         }
1908     }
1909
1910     private final void nodeTaxonomyDataAsSB( final Taxonomy taxonomy, final StringBuilder sb ) {
1911         if ( _control_panel.isShowTaxonomyRank() && !ForesterUtil.isEmpty( taxonomy.getRank() ) ) {
1912             sb.append( "[" );
1913             sb.append( taxonomy.getRank() );
1914             sb.append( "] " );
1915         }
1916         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
1917             sb.append( taxonomy.getTaxonomyCode() );
1918             sb.append( " " );
1919         }
1920         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
1921             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
1922                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
1923                 if ( getOptions().isAbbreviateScientificTaxonNames()
1924                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
1925                     abbreviateScientificName( taxonomy.getScientificName(), sb );
1926                 }
1927                 else {
1928                     sb.append( taxonomy.getScientificName() );
1929                 }
1930                 sb.append( " (" );
1931                 sb.append( taxonomy.getCommonName() );
1932                 sb.append( ") " );
1933             }
1934             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
1935                 if ( getOptions().isAbbreviateScientificTaxonNames()
1936                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
1937                     abbreviateScientificName( taxonomy.getScientificName(), sb );
1938                 }
1939                 else {
1940                     sb.append( taxonomy.getScientificName() );
1941                 }
1942                 sb.append( " " );
1943             }
1944             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
1945                 sb.append( taxonomy.getCommonName() );
1946                 sb.append( " " );
1947             }
1948         }
1949         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
1950             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
1951                 if ( getOptions().isAbbreviateScientificTaxonNames()
1952                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
1953                     abbreviateScientificName( taxonomy.getScientificName(), sb );
1954                 }
1955                 else {
1956                     sb.append( taxonomy.getScientificName() );
1957                 }
1958                 sb.append( " " );
1959             }
1960         }
1961         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
1962             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
1963                 sb.append( taxonomy.getCommonName() );
1964                 sb.append( " " );
1965             }
1966         }
1967     }
1968
1969     private final String obtainTitleForExtDescNodeData() {
1970         return getOptions().getExtDescNodeDataToReturn().toString();
1971     }
1972
1973     final private void openPdbWeb( final PhylogenyNode node ) {
1974         final List<Accession> pdb_ids = getPdbAccs( node );
1975         if ( ForesterUtil.isEmpty( pdb_ids ) ) {
1976             cannotOpenBrowserWarningMessage( "PDB" );
1977             return;
1978         }
1979         final List<String> uri_strs = TreePanelUtil.createUrisForPdbWeb( node, pdb_ids, getConfiguration(), this );
1980         if ( !ForesterUtil.isEmpty( uri_strs ) ) {
1981             for( final String uri_str : uri_strs ) {
1982                 try {
1983                     AptxUtil.launchWebBrowser( new URI( uri_str ), "_aptx_seq" );
1984                 }
1985                 catch ( final IOException e ) {
1986                     AptxUtil.showErrorMessage( this, e.toString() );
1987                     e.printStackTrace();
1988                 }
1989                 catch ( final URISyntaxException e ) {
1990                     AptxUtil.showErrorMessage( this, e.toString() );
1991                     e.printStackTrace();
1992                 }
1993             }
1994         }
1995         else {
1996             cannotOpenBrowserWarningMessage( "PDB" );
1997         }
1998     }
1999
2000     final private void openSeqWeb( final PhylogenyNode node ) {
2001         if ( ForesterUtil.isEmpty( isCanOpenSeqWeb( node ) ) ) {
2002             cannotOpenBrowserWarningMessage( "sequence" );
2003             return;
2004         }
2005         final String uri_str = TreePanelUtil.createUriForSeqWeb( node, getConfiguration(), this );
2006         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2007             try {
2008                 AptxUtil.launchWebBrowser( new URI( uri_str ), "_aptx_seq" );
2009             }
2010             catch ( final IOException e ) {
2011                 AptxUtil.showErrorMessage( this, e.toString() );
2012                 e.printStackTrace();
2013             }
2014             catch ( final URISyntaxException e ) {
2015                 AptxUtil.showErrorMessage( this, e.toString() );
2016                 e.printStackTrace();
2017             }
2018         }
2019         else {
2020             cannotOpenBrowserWarningMessage( "sequence" );
2021         }
2022     }
2023
2024     final private void openTaxWeb( final PhylogenyNode node ) {
2025         if ( !isCanOpenTaxWeb( node ) ) {
2026             cannotOpenBrowserWarningMessage( "taxonomic" );
2027             return;
2028         }
2029         String uri_str = null;
2030         final Taxonomy tax = node.getNodeData().getTaxonomy();
2031         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
2032                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
2033             try {
2034                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
2035             }
2036             catch ( final URISyntaxException e ) {
2037                 AptxUtil.showErrorMessage( this, e.toString() );
2038                 uri_str = null;
2039                 e.printStackTrace();
2040             }
2041         }
2042         else if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
2043                 && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
2044                 && ( tax.getIdentifier().getProvider().equalsIgnoreCase( "ncbi" )
2045                         || tax.getIdentifier().getProvider().equalsIgnoreCase( "uniprot" ) ) ) {
2046             try {
2047                 uri_str = "http://www.uniprot.org/taxonomy/"
2048                         + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF_8 );
2049             }
2050             catch ( final UnsupportedEncodingException e ) {
2051                 AptxUtil.showErrorMessage( this, e.toString() );
2052                 e.printStackTrace();
2053             }
2054         }
2055         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
2056             try {
2057                 uri_str = "http://www.uniprot.org/taxonomy/?query="
2058                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF_8 );
2059             }
2060             catch ( final UnsupportedEncodingException e ) {
2061                 AptxUtil.showErrorMessage( this, e.toString() );
2062                 e.printStackTrace();
2063             }
2064         }
2065         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
2066             try {
2067                 uri_str = "http://www.uniprot.org/taxonomy/?query="
2068                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF_8 );
2069             }
2070             catch ( final UnsupportedEncodingException e ) {
2071                 AptxUtil.showErrorMessage( this, e.toString() );
2072                 e.printStackTrace();
2073             }
2074         }
2075         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
2076             try {
2077                 uri_str = "http://www.uniprot.org/taxonomy/?query="
2078                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF_8 );
2079             }
2080             catch ( final UnsupportedEncodingException e ) {
2081                 AptxUtil.showErrorMessage( this, e.toString() );
2082                 e.printStackTrace();
2083             }
2084         }
2085         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2086             try {
2087                 AptxUtil.launchWebBrowser( new URI( uri_str ), "_aptx_tax" );
2088             }
2089             catch ( final IOException e ) {
2090                 AptxUtil.showErrorMessage( this, e.toString() );
2091                 e.printStackTrace();
2092             }
2093             catch ( final URISyntaxException e ) {
2094                 AptxUtil.showErrorMessage( this, e.toString() );
2095                 e.printStackTrace();
2096             }
2097         }
2098         else {
2099             cannotOpenBrowserWarningMessage( "taxonomic" );
2100         }
2101     }
2102
2103     final private void paintBranchLength( final Graphics2D g,
2104                                           final PhylogenyNode node,
2105                                           final boolean to_pdf,
2106                                           final boolean to_graphics_file ) {
2107         g.setFont( getTreeFontSet().getSmallFont() );
2108         if ( to_pdf || ( to_graphics_file && getOptions().isPrintBlackAndWhite() ) ) {
2109             g.setColor( Color.BLACK );
2110         }
2111         else {
2112             g.setColor( getTreeColorSet().getBranchLengthColor() );
2113         }
2114         if ( !node.isRoot() ) {
2115             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2116                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ),
2117                                       node.getParent().getXcoord() + EURO_D,
2118                                       node.getYcoord() - getTreeFontSet().getSmallMaxDescent(),
2119                                       g );
2120             }
2121             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2122                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ),
2123                                       node.getParent().getXcoord() + ROUNDED_D,
2124                                       node.getYcoord() - getTreeFontSet().getSmallMaxDescent(),
2125                                       g );
2126             }
2127             else {
2128                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ),
2129                                       node.getParent().getXcoord() + 3,
2130                                       node.getYcoord() - getTreeFontSet().getSmallMaxDescent(),
2131                                       g );
2132             }
2133         }
2134         else {
2135             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ),
2136                                   3,
2137                                   node.getYcoord() - getTreeFontSet().getSmallMaxDescent(),
2138                                   g );
2139         }
2140     }
2141
2142     final private void paintBranchLite( final Graphics2D g,
2143                                         final float x1,
2144                                         final float x2,
2145                                         final float y1,
2146                                         final float y2,
2147                                         final PhylogenyNode node ) {
2148         g.setColor( getTreeColorSet().getOvColor() );
2149         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2150             drawLine( x1, y1, x2, y2, g );
2151         }
2152         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2153             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2154             ( g ).draw( _quad_curve );
2155         }
2156         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2157             final float dx = x2 - x1;
2158             final float dy = y2 - y1;
2159             _cubic_curve.setCurve( x1,
2160                                    y1,
2161                                    x1 + ( dx * 0.4f ),
2162                                    y1 + ( dy * 0.2f ),
2163                                    x1 + ( dx * 0.6f ),
2164                                    y1 + ( dy * 0.8f ),
2165                                    x2,
2166                                    y2 );
2167             ( g ).draw( _cubic_curve );
2168         }
2169         else {
2170             final float x2a = x2;
2171             final float x1a = x1;
2172             // draw the vertical line
2173             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
2174                 drawLine( x1, y1, x1, y2, g );
2175             }
2176             // draw the horizontal line
2177             drawLine( x1a, y2, x2a, y2, g );
2178         }
2179     }
2180
2181     /**
2182      * Paint a branch which consists of a vertical and a horizontal bar
2183      * @param is_ind_found_nodes
2184      */
2185     final private void paintBranchRectangular( final Graphics2D g,
2186                                                final float x1,
2187                                                final float x2,
2188                                                final float y1,
2189                                                final float y2,
2190                                                final PhylogenyNode node,
2191                                                final boolean to_pdf,
2192                                                final boolean to_graphics_file ) {
2193         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
2194         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2195             drawLine( x1, y1, x2, y2, g );
2196         }
2197         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2198             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2199             g.draw( _quad_curve );
2200         }
2201         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2202             final float dx = x2 - x1;
2203             final float dy = y2 - y1;
2204             _cubic_curve.setCurve( x1,
2205                                    y1,
2206                                    x1 + ( dx * 0.4f ),
2207                                    y1 + ( dy * 0.2f ),
2208                                    x1 + ( dx * 0.6f ),
2209                                    y1 + ( dy * 0.8f ),
2210                                    x2,
2211                                    y2 );
2212             g.draw( _cubic_curve );
2213         }
2214         else {
2215             final float x2a = x2;
2216             final float x1a = x1;
2217             float y2_r = 0;
2218             if ( node.isFirstChildNode() || node.isLastChildNode()
2219                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
2220                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2221                 if ( !to_graphics_file && !to_pdf
2222                         && ( ( ( y2 < ( getVisibleRect().getMinY() - 20 ) )
2223                                 && ( y1 < ( getVisibleRect().getMinY() - 20 ) ) )
2224                                 || ( ( y2 > ( getVisibleRect().getMaxY() + 20 ) )
2225                                         && ( y1 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) ) {
2226                     // Do nothing.
2227                 }
2228                 else {
2229                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2230                         float x2c = x1 + EURO_D;
2231                         if ( x2c > x2a ) {
2232                             x2c = x2a;
2233                         }
2234                         drawLine( x1, y1, x2c, y2, g );
2235                     }
2236                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2237                         if ( y2 > y1 ) {
2238                             y2_r = y2 - ROUNDED_D;
2239                             if ( y2_r < y1 ) {
2240                                 y2_r = y1;
2241                             }
2242                             drawLine( x1, y1, x1, y2_r, g );
2243                         }
2244                         else {
2245                             y2_r = y2 + ROUNDED_D;
2246                             if ( y2_r > y1 ) {
2247                                 y2_r = y1;
2248                             }
2249                             drawLine( x1, y1, x1, y2_r, g );
2250                         }
2251                     }
2252                     else {
2253                         drawLine( x1, y1, x1, y2, g );
2254                     }
2255                 }
2256             }
2257             // draw the horizontal line
2258             if ( !to_graphics_file && !to_pdf && ( ( y2 < ( getVisibleRect().getMinY() - 20 ) )
2259                     || ( y2 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) {
2260                 return;
2261             }
2262             float x1_r = 0;
2263             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
2264                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2265                     x1_r = x1a + ROUNDED_D;
2266                     if ( x1_r < x2a ) {
2267                         drawLine( x1_r, y2, x2a, y2, g );
2268                     }
2269                 }
2270                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2271                     final float x1c = x1a + EURO_D;
2272                     if ( x1c < x2a ) {
2273                         drawLine( x1c, y2, x2a, y2, g );
2274                     }
2275                 }
2276                 else {
2277                     drawLine( x1a, y2, x2a, y2, g );
2278                 }
2279             }
2280             else {
2281                 final double w = PhylogenyMethods.getBranchWidthValue( node );
2282                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2283                     x1_r = x1a + ROUNDED_D;
2284                     if ( x1_r < x2a ) {
2285                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
2286                     }
2287                 }
2288                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2289                     final float x1c = x1a + EURO_D;
2290                     if ( x1c < x2a ) {
2291                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
2292                     }
2293                 }
2294                 else {
2295                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
2296                 }
2297             }
2298             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2299                 if ( x1_r > x2a ) {
2300                     x1_r = x2a;
2301                 }
2302                 if ( y2 > y2_r ) {
2303                     final double diff = y2 - y2_r;
2304                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
2305                 }
2306                 else {
2307                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
2308                 }
2309                 g.draw( _arc );
2310             }
2311         }
2312         if ( node.isExternal() ) {
2313             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file );
2314         }
2315     }
2316
2317     final private double paintCirculars( final PhylogenyNode n,
2318                                          final Phylogeny phy,
2319                                          final float center_x,
2320                                          final float center_y,
2321                                          final double radius,
2322                                          final boolean radial_labels,
2323                                          final Graphics2D g,
2324                                          final boolean to_pdf,
2325                                          final boolean to_graphics_file ) {
2326         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
2327             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
2328                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
2329             }
2330             return _urt_nodeid_angle_map.get( n.getId() );
2331         }
2332         else {
2333             final List<PhylogenyNode> descs = n.getDescendants();
2334             double sum = 0;
2335             for( final PhylogenyNode desc : descs ) {
2336                 sum += paintCirculars( desc,
2337                                        phy,
2338                                        center_x,
2339                                        center_y,
2340                                        radius,
2341                                        radial_labels,
2342                                        g,
2343                                        to_pdf,
2344                                        to_graphics_file );
2345             }
2346             double r = 0;
2347             if ( !n.isRoot() ) {
2348                 r = 1 - ( ( ( double ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
2349             }
2350             final double theta = sum / descs.size();
2351             n.setXcoord( ( float ) ( center_x + ( r * radius * Math.cos( theta ) ) ) );
2352             n.setYcoord( ( float ) ( center_y + ( r * radius * Math.sin( theta ) ) ) );
2353             _urt_nodeid_angle_map.put( n.getId(), theta );
2354             for( final PhylogenyNode desc : descs ) {
2355                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
2356             }
2357             return theta;
2358         }
2359     }
2360
2361     final private void paintCircularsLite( final PhylogenyNode n,
2362                                            final Phylogeny phy,
2363                                            final int center_x,
2364                                            final int center_y,
2365                                            final int radius,
2366                                            final Graphics2D g ) {
2367         if ( n.isExternal() ) {
2368             return;
2369         }
2370         else {
2371             final List<PhylogenyNode> descs = n.getDescendants();
2372             for( final PhylogenyNode desc : descs ) {
2373                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
2374             }
2375             float r = 0;
2376             if ( !n.isRoot() ) {
2377                 r = 1 - ( ( ( float ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
2378             }
2379             final double theta = _urt_nodeid_angle_map.get( n.getId() );
2380             n.setXSecondary( ( float ) ( center_x + ( radius * r * Math.cos( theta ) ) ) );
2381             n.setYSecondary( ( float ) ( center_y + ( radius * r * Math.sin( theta ) ) ) );
2382             for( final PhylogenyNode desc : descs ) {
2383                 paintBranchCircularLite( n, desc, g );
2384             }
2385         }
2386     }
2387
2388     final private void paintCollapsedNode( final Graphics2D g,
2389                                            final PhylogenyNode node,
2390                                            final boolean to_graphics_file,
2391                                            final boolean to_pdf,
2392                                            final boolean is_in_found_nodes ) {
2393         ////
2394         //// TODO
2395         ////
2396         Color c = null;
2397         int res[] = null;
2398         if ( _found_nodes_0 != null || _found_nodes_1 != null ) {
2399             res = calcFoundNodesInSubtree( node );
2400         }
2401         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2402             c = Color.BLACK;
2403         }
2404         //TODO
2405         //FIXME
2406         // else if ( is_in_found_nodes ) {
2407         //     c = getColorForFoundNode( node );
2408         // }
2409         // else if ( getControlPanel().isColorAccordingToSequence() ) {
2410         //     c = getSequenceBasedColor( node );
2411         // }
2412         // else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2413         //     c = getTaxonomyBasedColor( node );
2414         // }
2415         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
2416                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2417             c = PhylogenyMethods.getBranchColorValue( node );
2418         }
2419         else if ( to_pdf ) {
2420             g.setColor( getTreeColorSet().getBranchColorForPdf() );
2421         }
2422         else {
2423             c = getTreeColorSet().getCollapseFillColor();
2424         }
2425         double d = node.getAllExternalDescendants().size();
2426         float xxx;
2427         double s = 0;
2428         if ( getControlPanel().isDrawPhylogram() ) {
2429             if ( d > 1000 ) {
2430                 d = 0.75 * _y_distance;
2431             }
2432             else {
2433                 d = 0.25 * Math.log10( d ) * _y_distance;
2434             }
2435             final float half_box_size = 0.5f * getOptions().getDefaultNodeShapeSize();
2436             if ( d < half_box_size ) {
2437                 d = half_box_size;
2438             }
2439             _polygon.reset();
2440             final float xx = node.getXcoord() - ( getOptions().getDefaultNodeShapeSize() );
2441             xxx = xx > ( node.getParent().getXcoord() + 1 ) ? xx : node.getParent().getXcoord() + 1;
2442             _polygon.moveTo( xxx, node.getYcoord() + 0.5 );
2443             _polygon.lineTo( xxx, node.getYcoord() - 0.5 );
2444             s = _options.isCollapsedWithAverageHeigh()
2445                     ? PhylogenyMethods.calculateAverageTreeHeight( node ) * _x_correction_factor : 1;
2446             _polygon.lineTo( node.getXcoord() + s, node.getYcoord() - d );
2447             _polygon.lineTo( node.getXcoord() + s, node.getYcoord() + d );
2448             _polygon.closePath();
2449         }
2450         else {
2451             if ( d > 1000 ) {
2452                 d = _y_distance;
2453             }
2454             else {
2455                 d = ( Math.log10( d ) * _y_distance ) / 2.5;
2456             }
2457             final int box_size = getOptions().getDefaultNodeShapeSize() + 1;
2458             if ( d < box_size ) {
2459                 d = box_size;
2460             }
2461             final float xx = node.getXcoord() - ( 2 * box_size );
2462             xxx = xx > ( node.getParent().getXcoord() + 1 ) ? xx : node.getParent().getXcoord() + 1;
2463             _polygon.reset();
2464             _polygon.moveTo( xxx, node.getYcoord() );
2465             _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() - d );
2466             _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() + d );
2467             _polygon.closePath();
2468         }
2469         if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.SOLID ) {
2470             g.setColor( c );
2471             g.fill( _polygon );
2472         }
2473         else if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.NONE ) {
2474             g.setColor( getBackground() );
2475             g.fill( _polygon );
2476             g.setColor( c );
2477             g.draw( _polygon );
2478         }
2479         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2480             g.setPaint( new GradientPaint( xxx,
2481                                            node.getYcoord(),
2482                                            getBackground(),
2483                                            node.getXcoord(),
2484                                            ( float ) ( node.getYcoord() - d ),
2485                                            c,
2486                                            false ) );
2487             g.fill( _polygon );
2488             g.setPaint( c );
2489             g.draw( _polygon );
2490         }
2491         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes, s );
2492     }
2493
2494     final private void paintConfidenceValues( final Graphics2D g,
2495                                               final PhylogenyNode node,
2496                                               final boolean to_pdf,
2497                                               final boolean to_graphics_file ) {
2498         final List<Confidence> confidences = node.getBranchData().getConfidences();
2499         boolean not_first = false;
2500         Collections.sort( confidences );
2501         final StringBuilder sb = new StringBuilder();
2502         for( final Confidence confidence : confidences ) {
2503             if ( ForesterUtil.isEmpty( SHOW_ONLY_THIS_CONF_TYPE ) || ( !ForesterUtil.isEmpty( confidence.getType() )
2504                     && confidence.getType().equalsIgnoreCase( SHOW_ONLY_THIS_CONF_TYPE ) ) ) {
2505                 final double value = confidence.getValue();
2506                 if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2507                     if ( value < getOptions().getMinConfidenceValue() ) {
2508                         return;
2509                     }
2510                     if ( not_first ) {
2511                         sb.append( "/" );
2512                     }
2513                     else {
2514                         not_first = true;
2515                     }
2516                     sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil
2517                             .round( value, getOptions().getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2518                     if ( getOptions().isShowConfidenceStddev() ) {
2519                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2520                             sb.append( "(" );
2521                             sb.append( FORMATTER_CONFIDENCE
2522                                     .format( ForesterUtil.round( confidence.getStandardDeviation(),
2523                                                                  getOptions()
2524                                                                          .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2525                             sb.append( ")" );
2526                         }
2527                     }
2528                 }
2529             }
2530         }
2531         if ( sb.length() > 0 ) {
2532             final float parent_x = node.getParent().getXcoord();
2533             float x = node.getXcoord();
2534             g.setFont( getTreeFontSet().getSmallFont() );
2535             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2536                 x += EURO_D;
2537             }
2538             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2539                 x += ROUNDED_D;
2540             }
2541             if ( to_pdf || ( to_graphics_file && getOptions().isPrintBlackAndWhite() ) ) {
2542                 g.setColor( Color.BLACK );
2543             }
2544             else {
2545                 g.setColor( getTreeColorSet().getConfidenceColor() );
2546             }
2547             final String conf_str = sb.toString();
2548             TreePanel.drawString( conf_str,
2549                                   parent_x + ( ( x - parent_x
2550                                           - getTreeFontSet().getFontMetricsSmall().stringWidth( conf_str ) ) / 2 ),
2551                                   ( node.getYcoord() + getTreeFontSet().getSmallMaxAscent() ) - 1,
2552                                   g );
2553         }
2554     }
2555
2556     final private void paintGainedAndLostCharacters( final Graphics2D g,
2557                                                      final PhylogenyNode node,
2558                                                      final String gained,
2559                                                      final String lost ) {
2560         if ( node.getParent() != null ) {
2561             final float parent_x = node.getParent().getXcoord();
2562             final float x = node.getXcoord();
2563             g.setFont( getTreeFontSet().getLargeFont() );
2564             g.setColor( getTreeColorSet().getGainedCharactersColor() );
2565             if ( AptxConstants.SPECIAL_CUSTOM ) {
2566                 g.setColor( Color.BLUE );
2567             }
2568             TreePanel.drawString( gained,
2569                                   parent_x + ( ( x - parent_x
2570                                           - getFontMetricsForLargeDefaultFont().stringWidth( gained ) ) / 2 ),
2571                                   ( node.getYcoord() - getFontMetricsForLargeDefaultFont().getMaxDescent() ),
2572                                   g );
2573             g.setColor( getTreeColorSet().getLostCharactersColor() );
2574             TreePanel
2575                     .drawString( lost,
2576                                  parent_x + ( ( x - parent_x - getFontMetricsForLargeDefaultFont().stringWidth( lost ) )
2577                                          / 2 ),
2578                                  ( node.getYcoord() + getFontMetricsForLargeDefaultFont().getMaxAscent() ),
2579                                  g );
2580         }
2581     }
2582
2583     private void paintMolecularSequences( final Graphics2D g, final PhylogenyNode node, final boolean to_pdf ) {
2584         final RenderableMsaSequence rs = RenderableMsaSequence
2585                 .createInstance( node.getNodeData().getSequence().getMolecularSequence(),
2586                                  node.getNodeData().getSequence().getType(),
2587                                  getConfiguration() );
2588         if ( rs != null ) {
2589             final int default_height = 8;
2590             final float y = getYdistance();
2591             final int h = ( y / 2 ) < default_height ? ForesterUtil.roundToInt( y * 2 ) : default_height;
2592             rs.setRenderingHeight( h > 1 ? h : 1 );
2593             if ( getControlPanel().isDrawPhylogram() ) {
2594                 rs.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
2595                            node.getYcoord() - ( h / 2.0f ),
2596                            g,
2597                            this,
2598                            to_pdf );
2599             }
2600             else {
2601                 rs.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
2602                            node.getYcoord() - ( h / 2.0f ),
2603                            g,
2604                            this,
2605                            to_pdf );
2606             }
2607         }
2608     }
2609
2610     /**
2611      * Draw a box at the indicated node.
2612      *
2613      * @param x
2614      * @param y
2615      * @param node
2616      * @param g
2617      */
2618     final private void paintNodeBox( final float x,
2619                                      final float y,
2620                                      final PhylogenyNode node,
2621                                      final Graphics2D g,
2622                                      final boolean to_pdf,
2623                                      final boolean to_graphics_file ) {
2624         if ( node.isCollapse() ) {
2625             return;
2626         }
2627         // if this node should be highlighted, do so
2628         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
2629             g.setColor( getTreeColorSet().getFoundColor0() );
2630             drawOval( x - 8, y - 8, 16, 16, g );
2631             drawOval( x - 9, y - 8, 17, 17, g );
2632             drawOval( x - 9, y - 9, 18, 18, g );
2633         }
2634         if ( ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) )
2635                 || ( getOptions().isShowDefaultNodeShapesExternal() && node.isExternal() )
2636                 || ( getOptions().isShowDefaultNodeShapesInternal() && node.isInternal() )
2637                 || ( getOptions().isShowDefaultNodeShapesForMarkedNodes()
2638                         && ( node.getNodeData().getNodeVisualData() != null )
2639                         && ( !node.getNodeData().getNodeVisualData().isEmpty() ) )
2640                 || ( getControlPanel().isUseVisualStyles() && ( ( node.getNodeData().getNodeVisualData() != null )
2641                         && ( ( node.getNodeData().getNodeVisualData().getNodeColor() != null )
2642                                 || ( node.getNodeData().getNodeVisualData().getSize() != NodeVisualData.DEFAULT_SIZE )
2643                                 || ( node.getNodeData().getNodeVisualData().getFillType() != NodeFill.DEFAULT )
2644                                 || ( node.getNodeData().getNodeVisualData().getShape() != NodeShape.DEFAULT ) ) ) )
2645                 || ( getControlPanel().isEvents() && node.isHasAssignedEvent()
2646                         && ( node.getNodeData().getEvent().isDuplication()
2647                                 || node.getNodeData().getEvent().isSpeciation()
2648                                 || node.getNodeData().getEvent().isSpeciationOrDuplication() ) ) ) {
2649             NodeVisualData vis = null;
2650             if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
2651                     && ( !node.getNodeData().getNodeVisualData().isEmpty() ) ) {
2652                 vis = node.getNodeData().getNodeVisualData();
2653             }
2654             float box_size = getOptions().getDefaultNodeShapeSize();
2655             if ( ( vis != null ) && ( vis.getSize() != NodeVisualData.DEFAULT_SIZE ) ) {
2656                 box_size = vis.getSize();
2657             }
2658             final float half_box_size = box_size / 2.0f;
2659             Color outline_color = null;
2660             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2661                 outline_color = Color.BLACK;
2662             }
2663             else if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
2664                 outline_color = getColorForFoundNode( node );
2665             }
2666             else if ( vis != null ) {
2667                 if ( vis.getNodeColor() != null ) {
2668                     outline_color = vis.getNodeColor();
2669                 }
2670                 else if ( vis.getFontColor() != null ) {
2671                     outline_color = vis.getFontColor();
2672                 }
2673             }
2674             else if ( getControlPanel().isEvents() && TreePanelUtil.isHasAssignedEvent( node ) ) {
2675                 final Event event = node.getNodeData().getEvent();
2676                 if ( event.isDuplication() ) {
2677                     outline_color = getTreeColorSet().getDuplicationBoxColor();
2678                 }
2679                 else if ( event.isSpeciation() ) {
2680                     outline_color = getTreeColorSet().getSpecBoxColor();
2681                 }
2682                 else if ( event.isSpeciationOrDuplication() ) {
2683                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
2684                 }
2685             }
2686             if ( outline_color == null ) {
2687                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
2688                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
2689                     outline_color = getTreeColorSet().getBranchColorForPdf();
2690                 }
2691             }
2692             NodeShape shape = null;
2693             if ( vis != null ) {
2694                 if ( vis.getShape() == NodeShape.CIRCLE ) {
2695                     shape = NodeShape.CIRCLE;
2696                 }
2697                 else if ( vis.getShape() == NodeShape.RECTANGLE ) {
2698                     shape = NodeShape.RECTANGLE;
2699                 }
2700             }
2701             if ( shape == null ) {
2702                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
2703                     shape = NodeShape.CIRCLE;
2704                 }
2705                 else if ( getOptions().getDefaultNodeShape() == NodeShape.RECTANGLE ) {
2706                     shape = NodeShape.RECTANGLE;
2707                 }
2708             }
2709             NodeFill fill = null;
2710             if ( vis != null ) {
2711                 if ( vis.getFillType() == NodeFill.SOLID ) {
2712                     fill = NodeFill.SOLID;
2713                 }
2714                 else if ( vis.getFillType() == NodeFill.NONE ) {
2715                     fill = NodeFill.NONE;
2716                 }
2717                 else if ( vis.getFillType() == NodeFill.GRADIENT ) {
2718                     fill = NodeFill.GRADIENT;
2719                 }
2720             }
2721             if ( fill == null ) {
2722                 if ( getOptions().getDefaultNodeFill() == NodeFill.SOLID ) {
2723                     fill = NodeFill.SOLID;
2724                 }
2725                 else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
2726                     fill = NodeFill.NONE;
2727                 }
2728                 else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2729                     fill = NodeFill.GRADIENT;
2730                 }
2731             }
2732             Color vis_fill_color = null;
2733             if ( ( vis != null ) && ( vis.getNodeColor() != null ) ) {
2734                 vis_fill_color = vis.getNodeColor();
2735             }
2736             if ( shape == NodeShape.CIRCLE ) {
2737                 if ( fill == NodeFill.GRADIENT ) {
2738                     drawOvalGradient( x - half_box_size,
2739                                       y - half_box_size,
2740                                       box_size,
2741                                       box_size,
2742                                       g,
2743                                       to_pdf ? Color.WHITE : outline_color,
2744                                       to_pdf ? outline_color : getBackground(),
2745                                       outline_color );
2746                 }
2747                 else if ( fill == NodeFill.NONE ) {
2748                     Color background = getBackground();
2749                     if ( to_pdf ) {
2750                         background = Color.WHITE;
2751                     }
2752                     drawOvalGradient( x - half_box_size,
2753                                       y - half_box_size,
2754                                       box_size,
2755                                       box_size,
2756                                       g,
2757                                       background,
2758                                       background,
2759                                       outline_color );
2760                 }
2761                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
2762                     if ( vis_fill_color != null ) {
2763                         g.setColor( vis_fill_color );
2764                     }
2765                     else {
2766                         g.setColor( outline_color );
2767                     }
2768                     drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2769                 }
2770             }
2771             else if ( shape == NodeVisualData.NodeShape.RECTANGLE ) {
2772                 if ( fill == NodeVisualData.NodeFill.GRADIENT ) {
2773                     drawRectGradient( x - half_box_size,
2774                                       y - half_box_size,
2775                                       box_size,
2776                                       box_size,
2777                                       g,
2778                                       to_pdf ? Color.WHITE : outline_color,
2779                                       to_pdf ? outline_color : getBackground(),
2780                                       outline_color );
2781                 }
2782                 else if ( fill == NodeVisualData.NodeFill.NONE ) {
2783                     Color background = getBackground();
2784                     if ( to_pdf ) {
2785                         background = Color.WHITE;
2786                     }
2787                     drawRectGradient( x - half_box_size,
2788                                       y - half_box_size,
2789                                       box_size,
2790                                       box_size,
2791                                       g,
2792                                       background,
2793                                       background,
2794                                       outline_color );
2795                 }
2796                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
2797                     if ( vis_fill_color != null ) {
2798                         g.setColor( vis_fill_color );
2799                     }
2800                     else {
2801                         g.setColor( outline_color );
2802                     }
2803                     drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2804                 }
2805             }
2806         }
2807     }
2808
2809     final private int paintNodeData( final Graphics2D g,
2810                                      final PhylogenyNode node,
2811                                      final boolean to_graphics_file,
2812                                      final boolean to_pdf,
2813                                      final boolean is_in_found_nodes,
2814                                      final double add ) {
2815         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
2816             return 0;
2817         }
2818         if ( getControlPanel().isWriteBranchLengthValues()
2819                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
2820                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
2821                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
2822                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
2823             paintBranchLength( g, node, to_pdf, to_graphics_file );
2824         }
2825         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
2826             return 0;
2827         }
2828         if ( !getControlPanel().isShowExternalData() && ( node.isExternal() || node.isCollapse() ) ) {
2829             return 0;
2830         }
2831         _sb.setLength( 0 );
2832         int x = 0;
2833         if ( add > 0 ) {
2834             x += add;
2835         }
2836         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2837         if ( getControlPanel().isShowTaxonomyImages() && ( getImageMap() != null ) && !getImageMap().isEmpty()
2838                 && node.getNodeData().isHasTaxonomy() && ( ( node.getNodeData().getTaxonomy().getUris() != null )
2839                         && !node.getNodeData().getTaxonomy().getUris().isEmpty() ) ) {
2840             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
2841         }
2842         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames()
2843                 || getControlPanel().isShowTaxonomyCommonNames() || getControlPanel().isShowTaxonomyRank() )
2844                 && node.getNodeData().isHasTaxonomy() ) {
2845             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
2846         }
2847         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
2848         final boolean saw_species = _sb.length() > 0;
2849         _sb.setLength( 0 );
2850         nodeDataAsSB( node, _sb );
2851         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
2852             if ( ( _sb.length() == 0 ) && !saw_species ) {
2853                 if ( getOptions().isShowAbbreviatedLabelsForCollapsedNodes()
2854                         && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames()
2855                                 || getControlPanel().isShowSeqNames() || getControlPanel().isShowNodeNames() ) ) {
2856                     final PhylogenyNode first = PhylogenyMethods.getFirstExternalNode( node );
2857                     final PhylogenyNode last = PhylogenyMethods.getLastExternalNode( node );
2858                     if ( getControlPanel().isShowTaxonomyCode() && first.getNodeData().isHasTaxonomy()
2859                             && last.getNodeData().isHasTaxonomy()
2860                             && !ForesterUtil.isEmpty( first.getNodeData().getTaxonomy().getTaxonomyCode() )
2861                             && !ForesterUtil.isEmpty( last.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
2862                         addLabelForCollapsed( first.getNodeData().getTaxonomy().getTaxonomyCode(),
2863                                               last.getNodeData().getTaxonomy().getTaxonomyCode(),
2864                                               node.getAllExternalDescendants().size(),
2865                                               node );
2866                     }
2867                     else if ( getControlPanel().isShowTaxonomyScientificNames() && first.getNodeData().isHasTaxonomy()
2868                             && last.getNodeData().isHasTaxonomy()
2869                             && !ForesterUtil.isEmpty( first.getNodeData().getTaxonomy().getScientificName() )
2870                             && !ForesterUtil.isEmpty( last.getNodeData().getTaxonomy().getScientificName() ) ) {
2871                         addLabelForCollapsed( first.getNodeData().getTaxonomy().getScientificName(),
2872                                               last.getNodeData().getTaxonomy().getScientificName(),
2873                                               node.getAllExternalDescendants().size(),
2874                                               node );
2875                     }
2876                     else if ( getControlPanel().isShowSeqNames() && first.getNodeData().isHasSequence()
2877                             && last.getNodeData().isHasSequence()
2878                             && !ForesterUtil.isEmpty( first.getNodeData().getSequence().getName() )
2879                             && !ForesterUtil.isEmpty( last.getNodeData().getSequence().getName() ) ) {
2880                         addLabelForCollapsed( first.getNodeData().getSequence().getName(),
2881                                               last.getNodeData().getSequence().getName(),
2882                                               node.getAllExternalDescendants().size(),
2883                                               node );
2884                     }
2885                     else if ( getControlPanel().isShowNodeNames() && !ForesterUtil.isEmpty( first.getName() )
2886                             && !ForesterUtil.isEmpty( last.getName() ) ) {
2887                         addLabelForCollapsed( first.getName(),
2888                                               last.getName(),
2889                                               node.getAllExternalDescendants().size(),
2890                                               node );
2891                     }
2892                 }
2893             }
2894             else if ( ( _sb.length() > 0 ) || saw_species ) {
2895                 //  _sb.setLength( 0 );
2896                 _sb.append( " [" );
2897                 _sb.append( node.getAllExternalDescendants().size() );
2898                 _sb.append( "]" );
2899                 if ( _found_nodes_0 != null || _found_nodes_1 != null ) {
2900                     int[] res = calcFoundNodesInSubtree( node );
2901                     if ( res[ 0 ] > 0 ) {
2902                         _sb.append( " [" );
2903                         _sb.append( res[ 0 ] );
2904                         _sb.append( "/" );
2905                         _sb.append( res[ 1 ] );
2906                         _sb.append( "]" );
2907                     }
2908                 }
2909             }
2910         }
2911         else {
2912             // _sb.setLength( 0 );
2913         }
2914         // nodeDataAsSB( node, _sb );
2915         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
2916         float down_shift_factor = 3.0f;
2917         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
2918             down_shift_factor = 1;
2919         }
2920         float pos_x;
2921         if ( getControlPanel().getTreeDisplayType() == Options.PHYLOGENY_DISPLAY_TYPE.ALIGNED_PHYLOGRAM
2922                 && ( node.isExternal() || node.isCollapse() ) ) {
2923             pos_x = ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
2924                     + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x + ( 2 * TreePanel.MOVE ) + getXdistance()
2925                     + 3 );
2926         }
2927         else {
2928             pos_x = node.getXcoord() + x + 2 + half_box_size;
2929         }
2930         float pos_y;
2931         if ( !using_visual_font ) {
2932             pos_y = ( node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ) );
2933         }
2934         else {
2935             pos_y = ( node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / down_shift_factor ) );
2936         }
2937         if ( getControlPanel().getTreeDisplayType() == Options.PHYLOGENY_DISPLAY_TYPE.ALIGNED_PHYLOGRAM
2938                 && ( node.isExternal() || node.isCollapse() ) ) {
2939             drawConnection( node.getXcoord(), pos_x - x, node.getYcoord(), 5, 20, g, to_pdf );
2940             if ( node.isCollapse() ) {
2941                 pos_x -= add;
2942             }
2943         }
2944         final String sb_str = _sb.toString();
2945         // GUILHEM_BEG ______________
2946         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
2947                 && ( _query_sequence != null ) ) {
2948             x = paintSequenceRelation( g, node, x, half_box_size, pos_x, pos_y, sb_str );
2949         }
2950         // GUILHEM_END _____________
2951         if ( sb_str.length() > 0 ) {
2952             if ( !isAllowAttributedStrings() ) {
2953                 TreePanel.drawString( sb_str, pos_x, pos_y, g );
2954             }
2955             else {
2956                 drawStringX( sb_str, pos_x, pos_y, g );
2957             }
2958         }
2959         if ( _sb.length() > 0 ) {
2960             if ( !using_visual_font && !is_in_found_nodes ) {
2961                 x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
2962             }
2963             else {
2964                 x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
2965             }
2966         }
2967         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
2968                 && ( node.getNodeData().getSequence().getAnnotations() != null )
2969                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
2970             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
2971             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2972                 g.setColor( Color.BLACK );
2973             }
2974             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
2975                 g.setColor( calculateColorForAnnotation( ann ) );
2976             }
2977             final String ann_str = TreePanelUtil.createAnnotationString( ann,
2978                                                                          getOptions().isShowAnnotationRefSource() );
2979             TreePanel.drawString( ann_str,
2980                                   node.getXcoord() + x + 3 + half_box_size,
2981                                   node.getYcoord()
2982                                           + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ),
2983                                   g );
2984             _sb.setLength( 0 );
2985             _sb.append( ann_str );
2986             if ( _sb.length() > 0 ) {
2987                 if ( !using_visual_font && !is_in_found_nodes ) {
2988                     x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
2989                 }
2990                 else {
2991                     x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
2992                 }
2993             }
2994         }
2995         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
2996                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
2997                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2998             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
2999                     && node.getNodeData().isHasBinaryCharacters() ) {
3000                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3001                     g.setColor( Color.BLACK );
3002                 }
3003                 else {
3004                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
3005                 }
3006                 if ( getControlPanel().isShowBinaryCharacters() ) {
3007                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
3008                             .toString(),
3009                                           node.getXcoord() + x + 1 + half_box_size,
3010                                           node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent()
3011                                                   / down_shift_factor ),
3012                                           g );
3013                     paintGainedAndLostCharacters( g,
3014                                                   node,
3015                                                   node.getNodeData().getBinaryCharacters()
3016                                                           .getGainedCharactersAsStringBuffer().toString(),
3017                                                   node.getNodeData().getBinaryCharacters()
3018                                                           .getLostCharactersAsStringBuffer().toString() );
3019                 }
3020                 else {
3021                     TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
3022                                           node.getXcoord() + x + 4 + half_box_size,
3023                                           node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent()
3024                                                   / down_shift_factor ),
3025                                           g );
3026                     paintGainedAndLostCharacters( g,
3027                                                   node,
3028                                                   "+" + node.getNodeData().getBinaryCharacters().getGainedCount(),
3029                                                   "-" + node.getNodeData().getBinaryCharacters().getLostCount() );
3030                 }
3031             }
3032         }
3033         return x;
3034     }
3035
3036     private final int paintSequenceRelation( final Graphics2D g,
3037                                              final PhylogenyNode node,
3038                                              int x,
3039                                              final int half_box_size,
3040                                              final float pos_x,
3041                                              final float pos_y,
3042                                              final String sb_str ) {
3043         int nodeTextBoundsWidth = 0;
3044         if ( sb_str.length() > 0 ) {
3045             final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
3046             nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
3047         }
3048         if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
3049             if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
3050                 g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
3051                 g.setColor( getTreeColorSet().getBackgroundColor() );
3052             }
3053         }
3054         else {
3055             final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
3056             for( final SequenceRelation seqRelation : seqRelations ) {
3057                 final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence )
3058                         || seqRelation.getRef1().isEqual( _query_sequence ) )
3059                         && seqRelation.getType()
3060                                 .equals( getControlPanel().getSequenceRelationTypeBox().getSelectedItem() );
3061                 if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
3062                     final double linePosX = node.getXcoord() + 2 + half_box_size;
3063                     final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence()
3064                             || ( seqRelation.getConfidence() == null ) ) ? null
3065                                     : " (" + seqRelation.getConfidence().getValue() + ")";
3066                     if ( sConfidence != null ) {
3067                         float confidenceX = pos_x;
3068                         if ( sb_str.length() > 0 ) {
3069                             confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
3070                                     + CONFIDENCE_LEFT_MARGIN;
3071                         }
3072                         if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code
3073                             final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
3074                                     .getBounds().getWidth();
3075                             TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
3076                             x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
3077                         }
3078                     }
3079                     if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
3080                     {
3081                         if ( nodeTextBoundsWidth == 0 ) {
3082                             nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
3083                         }
3084                         else {
3085                             nodeTextBoundsWidth += 2;
3086                         }
3087                         g.drawLine( ( int ) linePosX + 1,
3088                                     3 + ( int ) pos_y,
3089                                     ( int ) linePosX + x + nodeTextBoundsWidth,
3090                                     3 + ( int ) pos_y );
3091                         break;
3092                     }
3093                 }
3094             }
3095         }
3096         return x;
3097     }
3098
3099     private final void drawConnection( final float x1,
3100                                        final float x2,
3101                                        final float y,
3102                                        final int dist_left,
3103                                        final int dist_right,
3104                                        final Graphics2D g,
3105                                        final boolean pdf ) {
3106         if ( ( ( x1 + dist_left ) < ( x2 - dist_right ) ) ) {
3107             final Stroke strok = g.getStroke();
3108             Color col = null;
3109             if ( strok == STROKE_005 ) {
3110                 g.setStroke( STROKE_001_DASHED );
3111             }
3112             else if ( strok == STROKE_01 ) {
3113                 g.setStroke( STROKE_005_DASHED );
3114             }
3115             else {
3116                 g.setStroke( STROKE_01_DASHED );
3117             }
3118             if ( pdf ) {
3119                 col = g.getColor();
3120                 g.setColor( lighter( col ) );
3121             }
3122             drawLine( x1 + dist_left, y, x2 - dist_right, y, g );
3123             g.setStroke( strok );
3124             if ( pdf ) {
3125                 g.setColor( col );
3126             }
3127         }
3128     }
3129
3130     public static Color lighter( final Color color ) {
3131         if ( ( color.getRed() == 0 ) && ( color.getGreen() == 0 ) && ( color.getBlue() == 0 ) ) {
3132             return new Color( 200, 200, 200 );
3133         }
3134         else {
3135             return color;
3136         }
3137     }
3138
3139     private final void addLabelForCollapsed( final String first,
3140                                              final String last,
3141                                              final int size,
3142                                              final PhylogenyNode node ) {
3143         _sb.append( first.length() < AptxConstants.MAX_LENGTH_FOR_COLLAPSED_NAME ? first
3144                 : first.substring( 0, AptxConstants.MAX_LENGTH_FOR_COLLAPSED_NAME - 1 ) );
3145         _sb.append( " ... " );
3146         _sb.append( last.length() < AptxConstants.MAX_LENGTH_FOR_COLLAPSED_NAME ? last
3147                 : last.substring( 0, AptxConstants.MAX_LENGTH_FOR_COLLAPSED_NAME - 1 ) );
3148         _sb.append( " (" + size + ")" );
3149         if ( _found_nodes_0 != null || _found_nodes_1 != null ) {
3150             /////
3151             /////
3152             int[] res = calcFoundNodesInSubtree( node );
3153             if ( res[ 0 ] > 0 ) {
3154                 _sb.append( " [" );
3155                 _sb.append( res[ 0 ] );
3156                 _sb.append( "/" );
3157                 _sb.append( res[ 1 ] );
3158                 _sb.append( "]" );
3159             }
3160         }
3161     }
3162
3163     private final int[] calcFoundNodesInSubtree( final PhylogenyNode node ) {
3164         final List<PhylogenyNode> all_descs = PhylogenyMethods.getAllDescendants( node );
3165         int res[] = new int[ 2 ];
3166         int found = 0;
3167         int total = 0;
3168         for( final PhylogenyNode desc : all_descs ) {
3169             if ( desc.isHasNodeData() ) {
3170                 if ( ( _found_nodes_0 != null && _found_nodes_0.contains( desc.getId() ) )
3171                         || ( _found_nodes_1 != null && _found_nodes_1.contains( desc.getId() ) ) ) {
3172                     ++found;
3173                 }
3174                 ++total;
3175             }
3176         }
3177         res[ 0 ] = found;
3178         res[ 1 ] = total;
3179         return res;
3180     }
3181
3182     private final boolean isAllowAttributedStrings() {
3183         return false;
3184     }
3185
3186     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
3187                                                   final PhylogenyNode node,
3188                                                   final boolean to_pdf,
3189                                                   final boolean to_graphics_file,
3190                                                   final boolean radial_labels,
3191                                                   final double ur_angle,
3192                                                   final boolean is_in_found_nodes ) {
3193         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
3194             return;
3195         }
3196         _sb.setLength( 0 );
3197         _sb.append( " " );
3198         if ( node.getNodeData().isHasTaxonomy()
3199                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames()
3200                         || getControlPanel().isShowTaxonomyCommonNames() ) ) {
3201             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3202             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3203                 _sb.append( taxonomy.getTaxonomyCode() );
3204                 _sb.append( " " );
3205             }
3206             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3207                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3208                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3209                     _sb.append( taxonomy.getScientificName() );
3210                     _sb.append( " (" );
3211                     _sb.append( taxonomy.getCommonName() );
3212                     _sb.append( ") " );
3213                 }
3214                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3215                     _sb.append( taxonomy.getScientificName() );
3216                     _sb.append( " " );
3217                 }
3218                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3219                     _sb.append( taxonomy.getCommonName() );
3220                     _sb.append( " " );
3221                 }
3222             }
3223             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3224                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3225                     _sb.append( taxonomy.getScientificName() );
3226                     _sb.append( " " );
3227                 }
3228             }
3229             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3230                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3231                     _sb.append( taxonomy.getCommonName() );
3232                     _sb.append( " " );
3233                 }
3234             }
3235         }
3236         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3237             _sb.append( " [" );
3238             _sb.append( node.getAllExternalDescendants().size() );
3239             _sb.append( "]" );
3240         }
3241         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3242             if ( _sb.length() > 0 ) {
3243                 _sb.append( " " );
3244             }
3245             _sb.append( node.getName() );
3246         }
3247         if ( node.getNodeData().isHasSequence() ) {
3248             if ( getControlPanel().isShowSequenceAcc()
3249                     && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3250                 if ( _sb.length() > 0 ) {
3251                     _sb.append( " " );
3252                 }
3253                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3254                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3255                     _sb.append( ":" );
3256                 }
3257                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3258             }
3259             if ( getControlPanel().isShowSeqNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3260                 if ( _sb.length() > 0 ) {
3261                     _sb.append( " " );
3262                 }
3263                 _sb.append( node.getNodeData().getSequence().getName() );
3264             }
3265         }
3266         //g.setFont( getTreeFontSet().getLargeFont() );
3267         //if ( is_in_found_nodes ) {
3268         //    g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3269         // }
3270         if ( _sb.length() > 1 ) {
3271             setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
3272             final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
3273             final String sb_str = _sb.toString();
3274             double m = 0;
3275             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3276                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
3277             }
3278             else {
3279                 m = ( float ) ( ur_angle % TWO_PI );
3280             }
3281             _at = g.getTransform();
3282             boolean need_to_reset = false;
3283             final float x_coord = node.getXcoord();
3284             float y_coord;
3285             if ( !using_visual_font ) {
3286                 y_coord = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / 3.0f );
3287             }
3288             else {
3289                 y_coord = node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / 3.0f );
3290             }
3291             if ( radial_labels ) {
3292                 need_to_reset = true;
3293                 boolean left = false;
3294                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3295                     m -= PI;
3296                     left = true;
3297                 }
3298                 g.rotate( m, x_coord, node.getYcoord() );
3299                 if ( left ) {
3300                     if ( !using_visual_font ) {
3301                         g.translate( -( getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth() ),
3302                                      0 );
3303                     }
3304                     else {
3305                         g.translate( -( getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth() ), 0 );
3306                     }
3307                 }
3308             }
3309             else {
3310                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3311                     need_to_reset = true;
3312                     if ( !using_visual_font ) {
3313                         g.translate( -getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth(), 0 );
3314                     }
3315                     else {
3316                         g.translate( -getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth(), 0 );
3317                     }
3318                 }
3319             }
3320             TreePanel.drawString( sb_str, x_coord, y_coord, g );
3321             if ( need_to_reset ) {
3322                 g.setTransform( _at );
3323             }
3324         }
3325     }
3326
3327     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
3328         if ( node.isCollapse() ) {
3329             return;
3330         }
3331         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
3332             g.setColor( getColorForFoundNode( node ) );
3333             drawRectFilled( node.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3334                             node.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3335                             OVERVIEW_FOUND_NODE_BOX_SIZE,
3336                             OVERVIEW_FOUND_NODE_BOX_SIZE,
3337                             g );
3338         }
3339         float new_x = 0;
3340         if ( !node.isExternal() && !node.isCollapse() ) {
3341             boolean first_child = true;
3342             float y2 = 0.0f;
3343             //final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3344             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3345                 final PhylogenyNode child_node = node.getChildNode( i );
3346                 final int factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3347                 if ( first_child ) {
3348                     first_child = false;
3349                     y2 = node.getYSecondary() - ( getOvYDistance()
3350                             * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3351                 }
3352                 else {
3353                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3354                 }
3355                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
3356                 new_x = x2 + node.getXSecondary();
3357                 final float diff_y = node.getYSecondary() - y2;
3358                 final float diff_x = node.getXSecondary() - new_x;
3359                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
3360                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
3361                 }
3362                 child_node.setXSecondary( new_x );
3363                 child_node.setYSecondary( y2 );
3364                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3365             }
3366         }
3367     }
3368
3369     final private void paintNodeRectangular( final Graphics2D g,
3370                                              final PhylogenyNode node,
3371                                              final boolean to_pdf,
3372                                              final boolean dynamically_hide,
3373                                              final int dynamic_hiding_factor,
3374                                              final boolean to_graphics_file,
3375                                              final boolean disallow_shortcutting ) {
3376         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
3377         if ( node.isCollapse() ) {
3378             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) ) {
3379                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3380             }
3381             return;
3382         }
3383         if ( node.isExternal() ) {
3384             ++_external_node_index;
3385         }
3386         // Confidence values
3387         if ( getControlPanel().isShowConfidenceValues() && !node.isExternal() && !node.isRoot()
3388                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
3389                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3390                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3391                 && node.getBranchData().isHasConfidences() ) {
3392             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
3393         }
3394         // Draw a line to root:
3395         if ( node.isRoot() && _phylogeny.isRooted() ) {
3396             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
3397         }
3398         float new_x = 0;
3399         float new_x_min = Float.MAX_VALUE;
3400         float min_dist = 1.5f;
3401         if ( !disallow_shortcutting ) {
3402             if ( dynamic_hiding_factor > 4000 ) {
3403                 min_dist = 4;
3404             }
3405             else if ( dynamic_hiding_factor > 1000 ) {
3406                 min_dist = 3;
3407             }
3408             else if ( dynamic_hiding_factor > 100 ) {
3409                 min_dist = 2;
3410             }
3411         }
3412         if ( !node.isExternal() && !node.isCollapse() ) {
3413             boolean first_child = true;
3414             float y2 = 0.0f;
3415             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3416                 final PhylogenyNode child_node = node.getChildNode( i );
3417                 final int factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3418                 if ( first_child ) {
3419                     first_child = false;
3420                     y2 = node.getYcoord() - ( _y_distance
3421                             * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3422                 }
3423                 else {
3424                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
3425                 }
3426                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
3427                 new_x = x2 + node.getXcoord();
3428                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
3429                     new_x_min = x2;
3430                 }
3431                 final float diff_y = node.getYcoord() - y2;
3432                 final float diff_x = node.getXcoord() - new_x;
3433                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
3434                         || ( diff_x < -min_dist ) ) {
3435                     paintBranchRectangular( g,
3436                                             node.getXcoord(),
3437                                             new_x,
3438                                             node.getYcoord(),
3439                                             y2,
3440                                             child_node,
3441                                             to_pdf,
3442                                             to_graphics_file );
3443                 }
3444                 child_node.setXcoord( new_x );
3445                 child_node.setYcoord( y2 );
3446                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
3447             }
3448             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file );
3449         }
3450         if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
3451                 && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
3452                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
3453             paintMolecularSequences( g, node, to_pdf );
3454         }
3455         if ( dynamically_hide && ( ( node.isExternal() && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) )
3456                 || ( !node.isExternal() && ( ( new_x_min < 20 )
3457                         || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getFontMetricsForLargeDefaultFont()
3458                                 .getHeight() ) ) ) ) ) {
3459             return;
3460         }
3461         final int x = paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes, 0 );
3462         paintNodeWithRenderableData( x, g, node, to_graphics_file, to_pdf );
3463     }
3464
3465     final private void paintNodeWithRenderableData( final int x,
3466                                                     final Graphics2D g,
3467                                                     final PhylogenyNode node,
3468                                                     final boolean to_graphics_file,
3469                                                     final boolean to_pdf ) {
3470         if ( isNodeDataInvisible( node ) && !( to_graphics_file || to_pdf ) ) {
3471             return;
3472         }
3473         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
3474             return;
3475         }
3476         if ( ( !getControlPanel().isShowExternalData() && node.isExternal() ) ) {
3477             return;
3478         }
3479         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3480                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) && ( node.getNodeData()
3481                         .getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
3482             RenderableDomainArchitecture rds = null;
3483             try {
3484                 rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
3485             }
3486             catch ( final ClassCastException cce ) {
3487                 cce.printStackTrace();
3488             }
3489             if ( rds != null ) {
3490                 final int default_height = 7;
3491                 float y = getYdistance();
3492                 if ( getControlPanel().isDynamicallyHideData() ) {
3493                     y = getTreeFontSet().getFontMetricsLarge().getHeight();
3494                 }
3495                 final int h = y < default_height ? ForesterUtil.roundToInt( y ) : default_height;
3496                 rds.setRenderingHeight( h > 1 ? h : 2 );
3497                 if ( getControlPanel().isDrawPhylogram() ) {
3498                     if ( getOptions().isLineUpRendarableNodeData() ) {
3499                         if ( getOptions().isRightLineUpDomains() ) {
3500                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
3501                                     + _length_of_longest_text + 50 //TODO why plus 50?
3502                                     + ( ( _longest_domain - rds.getTotalLength() ) * rds.getRenderingFactorWidth() ) ),
3503                                         node.getYcoord() - ( h / 2.0f ),
3504                                         g,
3505                                         this,
3506                                         to_pdf );
3507                         }
3508                         else {
3509                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
3510                                     + _length_of_longest_text + 50 ),
3511                                         node.getYcoord() - ( h / 2.0f ),
3512                                         g,
3513                                         this,
3514                                         to_pdf );
3515                         }
3516                     }
3517                     else {
3518                         rds.render( node.getXcoord() + x, node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
3519                     }
3520                 }
3521                 else {
3522                     if ( getOptions().isRightLineUpDomains() ) {
3523                         rds.render( ( ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text )
3524                                 - 20 ) + ( ( _longest_domain - rds.getTotalLength() ) * rds.getRenderingFactorWidth() ),
3525                                     node.getYcoord() - ( h / 2.0f ),
3526                                     g,
3527                                     this,
3528                                     to_pdf );
3529                     }
3530                     else {
3531                         rds.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
3532                                     node.getYcoord() - ( h / 2.0f ),
3533                                     g,
3534                                     this,
3535                                     to_pdf );
3536                     }
3537                 }
3538             }
3539         }
3540         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
3541                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
3542             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
3543                                                                          getStatisticsForExpressionValues(),
3544                                                                          getConfiguration() );
3545             if ( rv != null ) {
3546                 double domain_add = 0;
3547                 if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3548                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
3549                     domain_add = _domain_structure_width + 10;
3550                 }
3551                 if ( getControlPanel().isDrawPhylogram() ) {
3552                     rv.render( ( float ) ( node.getXcoord() + x + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
3553                 }
3554                 else {
3555                     rv.render( ( float ) ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text
3556                             + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
3557                 }
3558             }
3559         }
3560         //if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
3561         //        && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
3562         //        && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
3563         //   paintMolecularSequences( g, node, to_pdf );
3564         //}
3565     }
3566
3567     final private void paintOvRectangle( final Graphics2D g ) {
3568         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
3569         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
3570         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
3571         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
3572         final float width = getOvMaxWidth() / w_ratio;
3573         final float height = getOvMaxHeight() / h_ratio;
3574         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
3575         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
3576         g.setColor( getTreeColorSet().getFoundColor0() );
3577         getOvRectangle().setRect( x, y, width, height );
3578         final Stroke s = g.getStroke();
3579         g.setStroke( STROKE_1 );
3580         if ( ( width < 6 ) && ( height < 6 ) ) {
3581             drawRectFilled( x, y, 6, 6, g );
3582             getOvVirtualRectangle().setRect( x, y, 6, 6 );
3583         }
3584         else if ( width < 6 ) {
3585             drawRectFilled( x, y, 6, height, g );
3586             getOvVirtualRectangle().setRect( x, y, 6, height );
3587         }
3588         else if ( height < 6 ) {
3589             drawRectFilled( x, y, width, 6, g );
3590             getOvVirtualRectangle().setRect( x, y, width, 6 );
3591         }
3592         else {
3593             drawRect( x, y, width, height, g );
3594             if ( isInOvRect() ) {
3595                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
3596             }
3597             getOvVirtualRectangle().setRect( x, y, width, height );
3598         }
3599         g.setStroke( s );
3600     }
3601
3602     final private void paintPhylogenyLite( final Graphics2D g ) {
3603         _phylogeny.getRoot().setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition()
3604                 + ( MOVE / ( getVisibleRect().width / getOvRectangle().getWidth() ) ) ) );
3605         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
3606         final Stroke s = g.getStroke();
3607         g.setStroke( STROKE_05 );
3608         for( final PhylogenyNode element : _nodes_in_preorder ) {
3609             paintNodeLite( g, element );
3610         }
3611         g.setStroke( s );
3612         paintOvRectangle( g );
3613     }
3614
3615     /**
3616      * Paint the root branch. (Differs from others because it will always be a
3617      * single horizontal line).
3618      * @param to_graphics_file
3619      *
3620      * @return new x1 value
3621      */
3622     final private void paintRootBranch( final Graphics2D g,
3623                                         final float x1,
3624                                         final float y1,
3625                                         final PhylogenyNode root,
3626                                         final boolean to_pdf,
3627                                         final boolean to_graphics_file ) {
3628         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
3629         float d = getXdistance();
3630         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
3631             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
3632         }
3633         if ( d < MIN_ROOT_LENGTH ) {
3634             d = MIN_ROOT_LENGTH;
3635         }
3636         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
3637             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
3638         }
3639         else {
3640             final double w = PhylogenyMethods.getBranchWidthValue( root );
3641             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
3642         }
3643         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file );
3644     }
3645
3646     final private void paintScale( final Graphics2D g,
3647                                    int x1,
3648                                    int y1,
3649                                    final boolean to_pdf,
3650                                    final boolean to_graphics_file ) {
3651         x1 += MOVE;
3652         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
3653         y1 -= 12;
3654         final int y2 = y1 - 8;
3655         final int y3 = y1 - 4;
3656         g.setFont( getTreeFontSet().getSmallFont() );
3657         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3658             g.setColor( Color.BLACK );
3659         }
3660         else {
3661             g.setColor( getTreeColorSet().getBranchLengthColor() );
3662         }
3663         final Stroke s = g.getStroke();
3664         g.setStroke( STROKE_1 );
3665         drawLine( x1, y1, x1, y2, g );
3666         drawLine( x2, y1, x2, y2, g );
3667         drawLine( x1, y3, x2, y3, g );
3668         if ( getScaleLabel() != null ) {
3669             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
3670         }
3671         g.setStroke( s );
3672     }
3673
3674     final private int paintTaxonomy( final Graphics2D g,
3675                                      final PhylogenyNode node,
3676                                      final boolean is_in_found_nodes,
3677                                      final boolean to_pdf,
3678                                      final boolean to_graphics_file,
3679                                      final float x_shift ) {
3680         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3681         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
3682         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getTaxonomyColor() );
3683         float start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
3684         if ( getControlPanel().getTreeDisplayType() == Options.PHYLOGENY_DISPLAY_TYPE.ALIGNED_PHYLOGRAM
3685                 && node.isExternal() ) {
3686             start_x = ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
3687                     + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift + ( 2 * TreePanel.MOVE ) + getXdistance()
3688                     + 3 );
3689         }
3690         float start_y;
3691         if ( !using_visual_font ) {
3692             start_y = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent()
3693                     / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
3694         }
3695         else {
3696             start_y = node.getYcoord()
3697                     + ( getFontMetrics( g.getFont() ).getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
3698         }
3699         _sb.setLength( 0 );
3700         nodeTaxonomyDataAsSB( taxonomy, _sb );
3701         final String label = _sb.toString();
3702         /* GUILHEM_BEG */
3703         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
3704                 && ( node.getNodeData().isHasSequence() )
3705                 && node.getNodeData().getSequence().equals( _query_sequence ) ) {
3706             // invert font color and background color to show that this is the query sequence
3707             final Rectangle2D nodeTextBounds = new TextLayout( label,
3708                                                                g.getFont(),
3709                                                                new FontRenderContext( null, false, false ) )
3710                                                                        .getBounds();
3711             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
3712             g.setColor( getTreeColorSet().getBackgroundColor() );
3713         }
3714         /* GUILHEM_END */
3715         TreePanel.drawString( label, start_x, start_y, g );
3716         if ( !using_visual_font && !is_in_found_nodes ) {
3717             return getFontMetricsForLargeDefaultFont().stringWidth( label );
3718         }
3719         return getFontMetrics( g.getFont() ).stringWidth( label );
3720     }
3721
3722     final private void paintUnrooted( final PhylogenyNode n,
3723                                       final double low_angle,
3724                                       final double high_angle,
3725                                       final boolean radial_labels,
3726                                       final Graphics2D g,
3727                                       final boolean to_pdf,
3728                                       final boolean to_graphics_file ) {
3729         if ( n.isRoot() ) {
3730             n.setXcoord( getWidth() / 2 );
3731             n.setYcoord( getHeight() / 2 );
3732         }
3733         if ( n.isExternal() ) {
3734             paintNodeDataUnrootedCirc( g,
3735                                        n,
3736                                        to_pdf,
3737                                        to_graphics_file,
3738                                        radial_labels,
3739                                        ( high_angle + low_angle ) / 2,
3740                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
3741             return;
3742         }
3743         final float num_enclosed = n.getNumberOfExternalNodes();
3744         final float x = n.getXcoord();
3745         final float y = n.getYcoord();
3746         double current_angle = low_angle;
3747         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
3748         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
3749         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
3750         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
3751         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
3752             final PhylogenyNode desc = n.getChildNode( i );
3753             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
3754             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
3755             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
3756             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
3757             //     continue;
3758             // }
3759             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
3760             //    continue;
3761             //}
3762             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
3763             //    continue;
3764             // }
3765             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
3766             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
3767             float length;
3768             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
3769                 if ( desc.getDistanceToParent() < 0 ) {
3770                     length = 0;
3771                 }
3772                 else {
3773                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
3774                 }
3775             }
3776             else {
3777                 length = getUrtFactor();
3778             }
3779             final double mid_angle = current_angle + ( arc_size / 2 );
3780             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
3781             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
3782             desc.setXcoord( new_x );
3783             desc.setYcoord( new_y );
3784             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
3785             current_angle += arc_size;
3786             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
3787             drawLine( x, y, new_x, new_y, g );
3788             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file );
3789         }
3790         if ( n.isRoot() ) {
3791             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file );
3792         }
3793     }
3794
3795     final private void paintUnrootedLite( final PhylogenyNode n,
3796                                           final double low_angle,
3797                                           final double high_angle,
3798                                           final Graphics2D g,
3799                                           final float urt_ov_factor ) {
3800         if ( n.isRoot() ) {
3801             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
3802             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
3803             n.setXSecondary( x_pos );
3804             n.setYSecondary( y_pos );
3805         }
3806         if ( n.isExternal() ) {
3807             return;
3808         }
3809         final float num_enclosed = n.getNumberOfExternalNodes();
3810         final float x = n.getXSecondary();
3811         final float y = n.getYSecondary();
3812         double current_angle = low_angle;
3813         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
3814             final PhylogenyNode desc = n.getChildNode( i );
3815             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
3816             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
3817             float length;
3818             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
3819                 if ( desc.getDistanceToParent() < 0 ) {
3820                     length = 0;
3821                 }
3822                 else {
3823                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
3824                 }
3825             }
3826             else {
3827                 length = urt_ov_factor;
3828             }
3829             final double mid_angle = current_angle + ( arc_size / 2 );
3830             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
3831             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
3832             desc.setXSecondary( new_x );
3833             desc.setYSecondary( new_y );
3834             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
3835                 g.setColor( getColorForFoundNode( desc ) );
3836                 drawRectFilled( desc.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3837                                 desc.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3838                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
3839                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
3840                                 g );
3841                 g.setColor( getTreeColorSet().getOvColor() );
3842             }
3843             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
3844             current_angle += arc_size;
3845             drawLine( x, y, new_x, new_y, g );
3846         }
3847     }
3848
3849     final private void pasteSubtree( final PhylogenyNode node ) {
3850         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
3851             errorMessageNoCutCopyPasteInUnrootedDisplay();
3852             return;
3853         }
3854         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
3855             JOptionPane.showMessageDialog( this,
3856                                            "No tree in buffer (need to copy or cut a subtree first)",
3857                                            "Attempt to paste with empty buffer",
3858                                            JOptionPane.ERROR_MESSAGE );
3859             return;
3860         }
3861         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
3862         final Object[] options = { "As sibling", "As descendant", "Cancel" };
3863         final int r = JOptionPane.showOptionDialog( this,
3864                                                     "How to paste subtree" + label + "?",
3865                                                     "Paste Subtree",
3866                                                     JOptionPane.CLOSED_OPTION,
3867                                                     JOptionPane.QUESTION_MESSAGE,
3868                                                     null,
3869                                                     options,
3870                                                     options[ 2 ] );
3871         boolean paste_as_sibling = true;
3872         if ( r == 1 ) {
3873             paste_as_sibling = false;
3874         }
3875         else if ( r != 0 ) {
3876             return;
3877         }
3878         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
3879         buffer_phy.setAllNodesToNotCollapse();
3880         PhylogenyMethods.preOrderReId( buffer_phy );
3881         buffer_phy.setRooted( true );
3882         boolean need_to_show_whole = false;
3883         if ( paste_as_sibling ) {
3884             if ( node.isRoot() ) {
3885                 JOptionPane.showMessageDialog( this,
3886                                                "Cannot paste sibling to root",
3887                                                "Attempt to paste sibling to root",
3888                                                JOptionPane.ERROR_MESSAGE );
3889                 return;
3890             }
3891             buffer_phy.addAsSibling( node );
3892         }
3893         else {
3894             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
3895                 need_to_show_whole = true;
3896                 _phylogeny = buffer_phy;
3897             }
3898             else {
3899                 buffer_phy.addAsChild( node );
3900             }
3901         }
3902         if ( getCopiedAndPastedNodes() == null ) {
3903             setCopiedAndPastedNodes( new HashSet<Long>() );
3904         }
3905         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
3906         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
3907         for( final PhylogenyNode n : nodes ) {
3908             node_ids.add( n.getId() );
3909         }
3910         node_ids.add( node.getId() );
3911         getCopiedAndPastedNodes().addAll( node_ids );
3912         setNodeInPreorderToNull();
3913         _phylogeny.externalNodesHaveChanged();
3914         _phylogeny.clearHashIdToNodeMap();
3915         _phylogeny.recalculateNumberOfExternalDescendants( true );
3916         resetNodeIdToDistToLeafMap();
3917         setEdited( true );
3918         if ( need_to_show_whole ) {
3919             getControlPanel().showWhole();
3920         }
3921         repaint();
3922     }
3923
3924     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
3925         return node.getNodeData().getProperties().asText();
3926     }
3927
3928     private void setColor( final Graphics2D g,
3929                            final PhylogenyNode node,
3930                            final boolean to_graphics_file,
3931                            final boolean to_pdf,
3932                            final boolean is_in_found_nodes,
3933                            final Color default_color ) {
3934         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3935             g.setColor( Color.BLACK );
3936         }
3937         else if ( is_in_found_nodes ) {
3938             g.setColor( getColorForFoundNode( node ) );
3939         }
3940         else if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
3941                 && ( node.getNodeData().getNodeVisualData().getFontColor() != null ) ) {
3942             g.setColor( node.getNodeData().getNodeVisualData().getFontColor() );
3943         }
3944         else if ( getControlPanel().isColorAccordingToSequence() ) {
3945             g.setColor( getSequenceBasedColor( node ) );
3946         }
3947         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3948             g.setColor( getTaxonomyBasedColor( node ) );
3949         }
3950         else if ( getControlPanel().isColorAccordingToAnnotation()
3951                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null )
3952                         && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
3953             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
3954         }
3955         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
3956                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3957             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3958         }
3959         else if ( to_pdf ) {
3960             g.setColor( Color.BLACK );
3961         }
3962         else {
3963             g.setColor( default_color );
3964         }
3965     }
3966
3967     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
3968         getMainPanel().setCopiedAndPastedNodes( nodeIds );
3969     }
3970
3971     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
3972         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
3973     }
3974
3975     private boolean setFont( final Graphics2D g, final PhylogenyNode node, final boolean is_in_found_nodes ) {
3976         Font visual_font = null;
3977         if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null ) ) {
3978             visual_font = node.getNodeData().getNodeVisualData().getFont();
3979             g.setFont( visual_font != null ? visual_font : getTreeFontSet().getLargeFont() );
3980         }
3981         else {
3982             g.setFont( getTreeFontSet().getLargeFont() );
3983         }
3984         if ( is_in_found_nodes ) {
3985             g.setFont( g.getFont().deriveFont( Font.BOLD ) );
3986         }
3987         return visual_font != null;
3988     }
3989
3990     final private void setInOv( final boolean in_ov ) {
3991         _in_ov = in_ov;
3992     }
3993
3994     final private void setOvMaxHeight( final float ov_max_height ) {
3995         _ov_max_height = ov_max_height;
3996     }
3997
3998     final private void setOvMaxWidth( final float ov_max_width ) {
3999         _ov_max_width = ov_max_width;
4000     }
4001
4002     final private void setOvXcorrectionFactor( final float f ) {
4003         _ov_x_correction_factor = f;
4004     }
4005
4006     final private void setOvXDistance( final float ov_x_distance ) {
4007         _ov_x_distance = ov_x_distance;
4008     }
4009
4010     final private void setOvXPosition( final int ov_x_position ) {
4011         _ov_x_position = ov_x_position;
4012     }
4013
4014     final private void setOvYDistance( final float ov_y_distance ) {
4015         _ov_y_distance = ov_y_distance;
4016     }
4017
4018     final private void setOvYPosition( final int ov_y_position ) {
4019         _ov_y_position = ov_y_position;
4020     }
4021
4022     final private void setOvYStart( final int ov_y_start ) {
4023         _ov_y_start = ov_y_start;
4024     }
4025
4026     final private void setScaleDistance( final double scale_distance ) {
4027         _scale_distance = scale_distance;
4028     }
4029
4030     final private void setScaleLabel( final String scale_label ) {
4031         _scale_label = scale_label;
4032     }
4033
4034     private final void setupStroke( final Graphics2D g ) {
4035         if ( getYdistance() < 0.0001 ) {
4036             g.setStroke( STROKE_0025 );
4037         }
4038         if ( getYdistance() < 0.001 ) {
4039             g.setStroke( STROKE_005 );
4040         }
4041         else if ( getYdistance() < 0.01 ) {
4042             g.setStroke( STROKE_01 );
4043         }
4044         else if ( getYdistance() < 0.5 ) {
4045             g.setStroke( STROKE_025 );
4046         }
4047         else if ( getYdistance() < 1 ) {
4048             g.setStroke( STROKE_05 );
4049         }
4050         else if ( getYdistance() < 2 ) {
4051             g.setStroke( STROKE_075 );
4052         }
4053         else if ( ( getYdistance() < 20 ) || !getConfiguration().isAllowThickStrokes() ) {
4054             g.setStroke( STROKE_1 );
4055         }
4056         else {
4057             g.setStroke( STROKE_2 );
4058         }
4059     }
4060
4061     final private void setUpUrtFactor() {
4062         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
4063                 : getVisibleRect().height;
4064         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4065             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
4066         }
4067         else {
4068             final int max_depth = _circ_max_depth;
4069             if ( max_depth > 0 ) {
4070                 setUrtFactor( d / ( 2 * max_depth ) );
4071             }
4072             else {
4073                 setUrtFactor( d / 2 );
4074             }
4075         }
4076         setUrtFactorOv( getUrtFactor() );
4077     }
4078
4079     final private void setUrtFactor( final float urt_factor ) {
4080         _urt_factor = urt_factor;
4081     }
4082
4083     final private void setUrtFactorOv( final float urt_factor_ov ) {
4084         _urt_factor_ov = urt_factor_ov;
4085     }
4086
4087     private void showExtDescNodeData( final PhylogenyNode node, final char separator ) {
4088         final List<String> data = new ArrayList<String>();
4089         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
4090         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
4091             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
4092                 if ( !nodes.contains( n ) ) {
4093                     nodes.add( n );
4094                 }
4095             }
4096         }
4097         for( final PhylogenyNode n : nodes ) {
4098             switch ( getOptions().getExtDescNodeDataToReturn() ) {
4099                 case NODE_NAME:
4100                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
4101                         data.add( n.getName() );
4102                     }
4103                     break;
4104                 case SEQUENCE_NAME:
4105                     if ( n.getNodeData().isHasSequence()
4106                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
4107                         data.add( n.getNodeData().getSequence().getName() );
4108                     }
4109                     break;
4110                 case GENE_NAME:
4111                     if ( n.getNodeData().isHasSequence()
4112                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
4113                         data.add( n.getNodeData().getSequence().getGeneName() );
4114                     }
4115                     break;
4116                 case SEQUENCE_SYMBOL:
4117                     if ( n.getNodeData().isHasSequence()
4118                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
4119                         data.add( n.getNodeData().getSequence().getSymbol() );
4120                     }
4121                     break;
4122                 case SEQUENCE_MOL_SEQ_FASTA:
4123                     final StringBuilder sb = new StringBuilder();
4124                     if ( n.getNodeData().isHasSequence()
4125                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
4126                         final StringBuilder ann = new StringBuilder();
4127                         if ( getControlPanel().isShowNodeNames() && !ForesterUtil.isEmpty( n.getName() ) ) {
4128                             ann.append( n.getName() );
4129                             ann.append( separator );
4130                         }
4131                         if ( n.getNodeData().isHasTaxonomy() ) {
4132                             if ( getControlPanel().isShowTaxonomyCode()
4133                                     && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
4134                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
4135                                 ann.append( separator );
4136                             }
4137                             if ( getControlPanel().isShowTaxonomyScientificNames()
4138                                     && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
4139                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
4140                                 ann.append( separator );
4141                             }
4142                             if ( getControlPanel().isShowTaxonomyCommonNames()
4143                                     && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) ) {
4144                                 ann.append( n.getNodeData().getTaxonomy().getCommonName() );
4145                                 ann.append( separator );
4146                             }
4147                         }
4148                         if ( getControlPanel().isShowSeqSymbols()
4149                                 && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
4150                             ann.append( n.getNodeData().getSequence().getSymbol() );
4151                             ann.append( separator );
4152                         }
4153                         if ( getControlPanel().isShowSeqNames()
4154                                 && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
4155                             ann.append( n.getNodeData().getSequence().getName() );
4156                             ann.append( separator );
4157                         }
4158                         if ( getControlPanel().isShowGeneNames()
4159                                 && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
4160                             ann.append( n.getNodeData().getSequence().getGeneName() );
4161                             ann.append( separator );
4162                         }
4163                         if ( getControlPanel().isShowSequenceAcc()
4164                                 && n.getNodeData().getSequence().getAccession() != null ) {
4165                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
4166                             ann.append( separator );
4167                         }
4168                        
4169                         final String ann_str;
4170                         if ( ann.length() > 0 && ann.charAt( ann.length() - 1 ) == separator ) {
4171                             ann_str = ann.substring( 0, ann.length() - 1 );
4172                         }
4173                         else {
4174                             ann_str = ann.toString();
4175                         }
4176                         sb.append( SequenceWriter.toFasta( ann_str,
4177                                                            n.getNodeData().getSequence().getMolecularSequence(),
4178                                                            60 ) );
4179                         data.add( sb.toString() );
4180                     }
4181                     break;
4182                 case SEQUENCE_ACC:
4183                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
4184                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
4185                         data.add( n.getNodeData().getSequence().getAccession().toString() );
4186                     }
4187                     break;
4188                 case TAXONOMY_SCIENTIFIC_NAME:
4189                     if ( n.getNodeData().isHasTaxonomy()
4190                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
4191                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
4192                     }
4193                     break;
4194                 case TAXONOMY_CODE:
4195                     if ( n.getNodeData().isHasTaxonomy()
4196                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
4197                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
4198                     }
4199                     break;
4200                 case DOMAINS_ALL:
4201                 case DOMAINS_COLLAPSED_PER_PROTEIN:
4202                     if ( n.getNodeData().isHasSequence()
4203                             && ( n.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4204                         final DomainArchitecture da = n.getNodeData().getSequence().getDomainArchitecture();
4205                         final Set<String> s = new HashSet<String>();
4206                         for( int i = 0; i < da.getDomains().size(); ++i ) {
4207                             final ProteinDomain d = da.getDomain( i );
4208                             if ( d.getConfidence() <= Math.pow( 10, getDomainStructureEvalueThresholdExp() ) ) {
4209                                 final String name = d.getName();
4210                                 if ( !( s.contains( name ) ) ) {
4211                                     data.add( name );
4212                                     if ( getOptions()
4213                                             .getExtDescNodeDataToReturn() == NodeDataField.DOMAINS_COLLAPSED_PER_PROTEIN ) {
4214                                         s.add( name );
4215                                     }
4216                                 }
4217                             }
4218                         }
4219                     }
4220                     break;
4221                 case SEQ_ANNOTATIONS:
4222                     if ( n.getNodeData().isHasSequence() ) {
4223                         if ( n.getNodeData().isHasSequence()
4224                                 && ( n.getNodeData().getSequence().getAnnotations() != null ) ) {
4225                             final SortedSet<Annotation> a = n.getNodeData().getSequence().getAnnotations();
4226                             for( int i = 0; i < a.size(); ++i ) {
4227                                 data.add( n.getNodeData().getSequence().getAnnotation( i ).toString() );
4228                             }
4229                         }
4230                     }
4231                     break;
4232                 case GO_TERM_IDS:
4233                     if ( n.getNodeData().isHasSequence() ) {
4234                         if ( n.getNodeData().isHasSequence()
4235                                 && ( n.getNodeData().getSequence().getAnnotations() != null ) ) {
4236                             final SortedSet<Annotation> a = n.getNodeData().getSequence().getAnnotations();
4237                             for( int i = 0; i < a.size(); ++i ) {
4238                                 final Annotation ann = n.getNodeData().getSequence().getAnnotation( i );
4239                                 final String ref = ann.getRef();
4240                                 if ( ref.toUpperCase().startsWith( "GO:" ) ) {
4241                                     data.add( ref );
4242                                 }
4243                             }
4244                         }
4245                     }
4246                     break;
4247                 case UNKNOWN:
4248                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
4249                     break;
4250                 default:
4251                     throw new IllegalArgumentException( "unknown data element: "
4252                             + getOptions().getExtDescNodeDataToReturn() );
4253             }
4254         } // for loop
4255         final StringBuilder sb = new StringBuilder();
4256         final int size = TreePanelUtil.nodeDataIntoStringBuffer( data, getOptions(), sb );
4257         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
4258                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
4259             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
4260                 System.out.println( sb );
4261             }
4262             if ( sb.length() < 1 ) {
4263                 clearCurrentExternalNodesDataBuffer();
4264             }
4265             else {
4266                 setCurrentExternalNodesDataBuffer( sb );
4267             }
4268         }
4269         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
4270             if ( sb.length() < 1 ) {
4271                 TreePanelUtil.showInformationMessage( this,
4272                                                       "No Appropriate Data (" + obtainTitleForExtDescNodeData() + ")",
4273                                                       "Descendants of selected node do not contain selected data" );
4274                 clearCurrentExternalNodesDataBuffer();
4275             }
4276             else {
4277                 setCurrentExternalNodesDataBuffer( sb );
4278                 String title;
4279                 if ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() ) {
4280                     title = ( getOptions().getExtDescNodeDataToReturn() == NodeDataField.UNKNOWN ? "Data"
4281                             : obtainTitleForExtDescNodeData() ) + " for " + data.size() + " nodes, unique entries: "
4282                             + size;
4283                 }
4284                 else {
4285                     title = ( getOptions().getExtDescNodeDataToReturn() == NodeDataField.UNKNOWN ? "Data"
4286                             : obtainTitleForExtDescNodeData() ) + " for " + data.size() + "/"
4287                             + node.getNumberOfExternalNodes() + " external descendats of node " + node
4288                             + ", unique entries: " + size;
4289                 }
4290                 final String s = sb.toString().trim();
4291                 getMainPanel().getMainFrame().showTextFrame( s, title );
4292             }
4293         }
4294     }
4295
4296     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
4297         try {
4298             if ( ( node.getName().length() > 0 )
4299                     || ( node.getNodeData().isHasTaxonomy()
4300                             && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
4301                     || ( node.getNodeData().isHasSequence()
4302                             && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) )
4303                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
4304                     || node.getBranchData().isHasConfidences() ) {
4305                 _popup_buffer.setLength( 0 );
4306                 short lines = 0;
4307                 if ( node.getName().length() > 0 ) {
4308                     lines++;
4309                     _popup_buffer.append( node.getName() );
4310                 }
4311                 if ( node.getNodeData().isHasTaxonomy()
4312                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
4313                     lines++;
4314                     boolean enc_data = false;
4315                     final Taxonomy tax = node.getNodeData().getTaxonomy();
4316                     if ( _popup_buffer.length() > 0 ) {
4317                         _popup_buffer.append( "\n" );
4318                     }
4319                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4320                         _popup_buffer.append( "[" );
4321                         _popup_buffer.append( tax.getTaxonomyCode() );
4322                         _popup_buffer.append( "]" );
4323                         enc_data = true;
4324                     }
4325                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4326                         if ( enc_data ) {
4327                             _popup_buffer.append( " " );
4328                         }
4329                         _popup_buffer.append( tax.getScientificName() );
4330                         enc_data = true;
4331                     }
4332                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
4333                         if ( enc_data ) {
4334                             _popup_buffer.append( " (" );
4335                         }
4336                         else {
4337                             _popup_buffer.append( "(" );
4338                         }
4339                         _popup_buffer.append( tax.getCommonName() );
4340                         _popup_buffer.append( ")" );
4341                         enc_data = true;
4342                     }
4343                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
4344                         if ( enc_data ) {
4345                             _popup_buffer.append( " (" );
4346                         }
4347                         else {
4348                             _popup_buffer.append( "(" );
4349                         }
4350                         _popup_buffer.append( tax.getAuthority() );
4351                         _popup_buffer.append( ")" );
4352                         enc_data = true;
4353                     }
4354                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
4355                         if ( enc_data ) {
4356                             _popup_buffer.append( " [" );
4357                         }
4358                         else {
4359                             _popup_buffer.append( "[" );
4360                         }
4361                         _popup_buffer.append( tax.getRank() );
4362                         _popup_buffer.append( "]" );
4363                         enc_data = true;
4364                     }
4365                     if ( tax.getSynonyms().size() > 0 ) {
4366                         if ( enc_data ) {
4367                             _popup_buffer.append( " " );
4368                         }
4369                         _popup_buffer.append( "[" );
4370                         int counter = 1;
4371                         for( final String syn : tax.getSynonyms() ) {
4372                             if ( !ForesterUtil.isEmpty( syn ) ) {
4373                                 enc_data = true;
4374                                 _popup_buffer.append( syn );
4375                                 if ( counter < tax.getSynonyms().size() ) {
4376                                     _popup_buffer.append( ", " );
4377                                 }
4378                             }
4379                             counter++;
4380                         }
4381                         _popup_buffer.append( "]" );
4382                     }
4383                     if ( !enc_data ) {
4384                         if ( ( tax.getIdentifier() != null )
4385                                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
4386                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
4387                                 _popup_buffer.append( "[" );
4388                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
4389                                 _popup_buffer.append( "] " );
4390                             }
4391                             _popup_buffer.append( tax.getIdentifier().getValue() );
4392                         }
4393                     }
4394                 }
4395                 if ( node.getNodeData().isHasSequence()
4396                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
4397                     lines++;
4398                     boolean enc_data = false;
4399                     if ( _popup_buffer.length() > 0 ) {
4400                         _popup_buffer.append( "\n" );
4401                     }
4402                     final Sequence seq = node.getNodeData().getSequence();
4403                     if ( seq.getAccession() != null ) {
4404                         _popup_buffer.append( "[" );
4405                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
4406                             _popup_buffer.append( seq.getAccession().getSource() );
4407                             _popup_buffer.append( ":" );
4408                         }
4409                         _popup_buffer.append( seq.getAccession().getValue() );
4410                         _popup_buffer.append( "]" );
4411                         enc_data = true;
4412                     }
4413                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
4414                         if ( enc_data ) {
4415                             _popup_buffer.append( " [" );
4416                         }
4417                         else {
4418                             _popup_buffer.append( "[" );
4419                         }
4420                         _popup_buffer.append( seq.getSymbol() );
4421                         _popup_buffer.append( "]" );
4422                         enc_data = true;
4423                     }
4424                     if ( !ForesterUtil.isEmpty( seq.getGeneName() ) ) {
4425                         if ( enc_data ) {
4426                             _popup_buffer.append( " [" );
4427                         }
4428                         else {
4429                             _popup_buffer.append( "[" );
4430                         }
4431                         _popup_buffer.append( seq.getGeneName() );
4432                         _popup_buffer.append( "]" );
4433                         enc_data = true;
4434                     }
4435                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
4436                         if ( enc_data ) {
4437                             _popup_buffer.append( " " );
4438                         }
4439                         _popup_buffer.append( seq.getName() );
4440                     }
4441                 }
4442                 if ( node.getNodeData().isHasDate() ) {
4443                     lines++;
4444                     if ( _popup_buffer.length() > 0 ) {
4445                         _popup_buffer.append( "\n" );
4446                     }
4447                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
4448                 }
4449                 if ( node.getNodeData().isHasDistribution() ) {
4450                     lines++;
4451                     if ( _popup_buffer.length() > 0 ) {
4452                         _popup_buffer.append( "\n" );
4453                     }
4454                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
4455                 }
4456                 if ( node.getBranchData().isHasConfidences() ) {
4457                     final List<Confidence> confs = node.getBranchData().getConfidences();
4458                     for( final Confidence confidence : confs ) {
4459                         lines++;
4460                         if ( _popup_buffer.length() > 0 ) {
4461                             _popup_buffer.append( "\n" );
4462                         }
4463                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
4464                             _popup_buffer.append( "[" );
4465                             _popup_buffer.append( confidence.getType() );
4466                             _popup_buffer.append( "] " );
4467                         }
4468                         _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
4469                                                                                                getOptions()
4470                                                                                                        .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4471                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4472                             _popup_buffer.append( " (sd=" );
4473                             _popup_buffer.append( FORMATTER_CONFIDENCE
4474                                     .format( ForesterUtil.round( confidence.getStandardDeviation(),
4475                                                                  getOptions()
4476                                                                          .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4477                             _popup_buffer.append( ")" );
4478                         }
4479                     }
4480                 }
4481                 if ( node.getNodeData().isHasProperties() ) {
4482                     if ( _popup_buffer.length() > 0 ) {
4483                         _popup_buffer.append( "\n" );
4484                     }
4485                     _popup_buffer.append( node.getNodeData().getProperties().asText() );
4486                 }
4487                 if ( _popup_buffer.length() > 0 ) {
4488                     if ( !getConfiguration().isUseNativeUI() ) {
4489                         _rollover_popup
4490                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
4491                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
4492                         if ( isInFoundNodes0( node ) && !isInFoundNodes1( node ) ) {
4493                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0() );
4494                         }
4495                         else if ( !isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
4496                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor1() );
4497                         }
4498                         else if ( isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
4499                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0and1() );
4500                         }
4501                         else {
4502                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
4503                         }
4504                     }
4505                     else {
4506                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
4507                     }
4508                     _rollover_popup.setText( _popup_buffer.toString() );
4509                     _node_desc_popup = PopupFactory.getSharedInstance()
4510                             .getPopup( null,
4511                                        _rollover_popup,
4512                                        e.getLocationOnScreen().x + 10,
4513                                        e.getLocationOnScreen().y - ( lines * 20 ) );
4514                     _node_desc_popup.show();
4515                 }
4516             }
4517         }
4518         catch ( final Exception ex ) {
4519             // Do nothing.
4520         }
4521     }
4522
4523     final private void showNodeEditFrame( final PhylogenyNode n ) {
4524         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4525             // pop up edit box for single node
4526             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
4527             _node_frame_index++;
4528         }
4529         else {
4530             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4531         }
4532     }
4533
4534     final private void showNodeFrame( final PhylogenyNode n ) {
4535         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4536             // pop up edit box for single node
4537             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
4538             _node_frame_index++;
4539         }
4540         else {
4541             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4542         }
4543     }
4544
4545     final private void switchDisplaygetPhylogenyGraphicsType() {
4546         switch ( getPhylogenyGraphicsType() ) {
4547             case RECTANGULAR:
4548                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4549                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4550                 break;
4551             case EURO_STYLE:
4552                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4553                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4554                 break;
4555             case ROUNDED:
4556                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4557                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4558                 break;
4559             case CURVED:
4560                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4561                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4562                 break;
4563             case TRIANGULAR:
4564                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4565                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4566                 break;
4567             case CONVEX:
4568                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4569                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4570                 break;
4571             case UNROOTED:
4572                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4573                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4574                 break;
4575             case CIRCULAR:
4576                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4577                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4578                 break;
4579             default:
4580                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
4581         }
4582         if ( getControlPanel().getDynamicallyHideData() != null ) {
4583             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4584                 getControlPanel().getDynamicallyHideData().setEnabled( false );
4585             }
4586             else {
4587                 getControlPanel().getDynamicallyHideData().setEnabled( true );
4588             }
4589         }
4590         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
4591             getControlPanel().setDrawPhylogramEnabled( true );
4592         }
4593         else {
4594             getControlPanel().setDrawPhylogramEnabled( false );
4595         }
4596         getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
4597     }
4598
4599     final void calcMaxDepth() {
4600         if ( _phylogeny != null ) {
4601             _circ_max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4602         }
4603     }
4604
4605     /**
4606      * Set parameters for printing the displayed tree
4607      *
4608      */
4609     final void calcParametersForPainting( final int x, final int y ) {
4610         // updateStyle(); not needed?
4611         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4612             initNodeData();
4613             calculateLongestExtNodeInfo();
4614             if ( ( getLongestExtNodeInfo() > ( x * 0.6 ) )
4615                     && ( getTreeFontSet().getLargeFont().getSize() > ( 2 + TreeFontSet.FONT_SIZE_CHANGE_STEP ) ) ) {
4616                 while ( ( getLongestExtNodeInfo() > ( x * 0.7 ) )
4617                         && ( getTreeFontSet().getLargeFont().getSize() > 2 ) ) {
4618                     getMainPanel().getTreeFontSet().decreaseFontSize( getConfiguration().getMinBaseFontSize(), true );
4619                     calculateLongestExtNodeInfo();
4620                 }
4621             }
4622             else {
4623                 while ( ( getLongestExtNodeInfo() < ( x * 0.6 ) ) && ( getTreeFontSet().getLargeFont()
4624                         .getSize() <= ( getTreeFontSet().getLargeFontMemory().getSize()
4625                                 - TreeFontSet.FONT_SIZE_CHANGE_STEP ) ) ) {
4626                     getMainPanel().getTreeFontSet().increaseFontSize();
4627                     calculateLongestExtNodeInfo();
4628                 }
4629             }
4630             //_length_of_longest_text = calcLengthOfLongestText();
4631             int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4632             final int max_depth = PhylogenyMethods.calculateMaxDepthConsiderCollapsed( _phylogeny ) + 1;
4633             if ( ext_nodes == 1 ) {
4634                 ext_nodes = max_depth;
4635                 if ( ext_nodes < 1 ) {
4636                     ext_nodes = 1;
4637                 }
4638             }
4639             updateOvSizes();
4640             float xdist = 0;
4641             float ov_xdist = 0;
4642             if ( !isNonLinedUpCladogram() ) {
4643                 xdist = ( float ) ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( ext_nodes + 3.0 ) );
4644                 ov_xdist = ( float ) ( getOvMaxWidth() / ( ext_nodes + 3.0 ) );
4645             }
4646             else {
4647                 xdist = ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( max_depth + 1 ) );
4648                 ov_xdist = ( getOvMaxWidth() / ( max_depth + 1 ) );
4649             }
4650             float ydist = ( float ) ( ( y - TreePanel.MOVE ) / ( ext_nodes * 2.0 ) );
4651             if ( xdist < 0.0 ) {
4652                 xdist = 0.0f;
4653             }
4654             if ( ov_xdist < 0.0 ) {
4655                 ov_xdist = 0.0f;
4656             }
4657             if ( ydist < 0.0 ) {
4658                 ydist = 0.0f;
4659             }
4660             setXdistance( xdist );
4661             setYdistance( ydist );
4662             setOvXDistance( ov_xdist );
4663             final double height = _phylogeny.calculateHeight( !_options.isCollapsedWithAverageHeigh() );
4664             //final double height = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4665             if ( height > 0 ) {
4666                 final float corr = ( float ) ( ( x - ( 2.0 * TreePanel.MOVE ) - getLongestExtNodeInfo()
4667                         - getXdistance() ) / height );
4668                 setXcorrectionFactor( corr > 0 ? corr : 0 );
4669                 final float ov_corr = ( float ) ( ( getOvMaxWidth() - getOvXDistance() ) / height );
4670                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
4671             }
4672             else {
4673                 setXcorrectionFactor( 0 );
4674                 setOvXcorrectionFactor( 0 );
4675             }
4676             _circ_max_depth = max_depth;
4677             setUpUrtFactor();
4678             //
4679             if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
4680                     && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
4681                 //                int dynamic_hiding_factor = calcDynamicHidingFactor();
4682                 //                if ( dynamic_hiding_factor > 1 ) {
4683                 //                    while ( dynamic_hiding_factor > 1
4684                 //                            && getTreeFontSet()._fm_large.getHeight() > TreeFontSet.SMALL_FONTS_BASE ) {
4685                 //                        getTreeFontSet().decreaseFontSize( 1, true );
4686                 //                        dynamic_hiding_factor = calcDynamicHidingFactor();
4687                 //                    }
4688                 //                }
4689                 //                else if ( getTreeFontSet().isDecreasedSizeBySystem() ) {
4690                 //                    while ( dynamic_hiding_factor < 1 && getTreeFontSet()._fm_large.getHeight() < 12 ) {
4691                 //                        getTreeFontSet().increaseFontSize();
4692                 //                        dynamic_hiding_factor = calcDynamicHidingFactor();
4693                 //                    }
4694                 //                }
4695             }
4696             //
4697         }
4698     }
4699
4700     final void calculateLongestExtNodeInfo() {
4701         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4702             return;
4703         }
4704         int max_possible_length = ForesterUtil
4705                 .roundToInt( ( getSize().getWidth() - ( 2 * MOVE ) ) * AptxConstants.EXT_NODE_INFO_LENGTH_MAX_RATIO );
4706         if ( max_possible_length < 20 ) {
4707             max_possible_length = 20;
4708         }
4709         int longest = 30;
4710         int longest_txt = 0;
4711         _longest_domain = 0;
4712         PhylogenyNode longest_txt_node = _phylogeny.getFirstExternalNode();
4713         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
4714             int sum = 0;
4715             if ( node.isCollapse() ) {
4716                 continue;
4717             }
4718             final StringBuilder sb = new StringBuilder();
4719             nodeDataAsSB( node, sb );
4720             if ( node.getNodeData().isHasTaxonomy() ) {
4721                 nodeTaxonomyDataAsSB( node.getNodeData().getTaxonomy(), sb );
4722             }
4723             final int txt = sb.length();
4724             if ( txt > longest_txt ) {
4725                 longest_txt = txt;
4726                 longest_txt_node = node;
4727             }
4728             boolean use_vis = false;
4729             final Graphics2D g = ( Graphics2D ) getGraphics();
4730             if ( g != null && getControlPanel().isUseVisualStyles() ) {
4731                 use_vis = setFont( g, node, false );
4732             }
4733             if ( !use_vis ) {
4734                 sum = getFontMetricsForLargeDefaultFont().stringWidth( sb.toString() );
4735             }
4736             else {
4737                 sum = getFontMetrics( g.getFont() ).stringWidth( sb.toString() );
4738             }
4739             if ( getControlPanel().isShowBinaryCharacters() && node.getNodeData().isHasBinaryCharacters() ) {
4740                 sum += getFontMetricsForLargeDefaultFont().stringWidth( node.getNodeData().getBinaryCharacters()
4741                         .getGainedCharactersAsStringBuffer().toString() );
4742             }
4743             if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4744                     && ( node.getNodeData().getVector().size() > 0 ) ) {
4745                 if ( getConfiguration() != null ) {
4746                     sum += getConfiguration().getVectorDataWidth() + 10;
4747                 }
4748                 else {
4749                     sum += RenderableVector.VECTOR_DEFAULT_WIDTH + 10;
4750                 }
4751             }
4752             if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4753                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4754                 // FIXME
4755                 // TODO this might need some clean up
4756                 final DomainArchitecture d = node.getNodeData().getSequence().getDomainArchitecture();
4757                 sum += ( ( _domain_structure_width
4758                         / ( ( RenderableDomainArchitecture ) d ).getOriginalSize().getWidth() ) * d.getTotalLength() )
4759                         + 10;
4760                 if ( d.getTotalLength() > _longest_domain ) {
4761                     _longest_domain = d.getTotalLength();
4762                 }
4763             }
4764             if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
4765                     && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
4766                     && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
4767                 // FIXME
4768                 sum += RenderableMsaSequence.DEFAULT_WIDTH + 30;
4769             }
4770             if ( sum >= max_possible_length ) {
4771                 _longest_ext_node_info = max_possible_length;
4772                 // return; //FIXME why?
4773             }
4774             if ( sum > longest ) {
4775                 longest = sum;
4776             }
4777         }
4778         _ext_node_with_longest_txt_info = longest_txt_node;
4779         if ( longest >= max_possible_length ) {
4780             _longest_ext_node_info = max_possible_length;
4781         }
4782         else {
4783             _longest_ext_node_info = longest;
4784         }
4785         _length_of_longest_text = calcLengthOfLongestText();
4786     }
4787
4788     final void calculateScaleDistance() {
4789         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4790             return;
4791         }
4792         final double height = getMaxDistanceToRoot();
4793         if ( height > 0 ) {
4794             if ( ( height <= 0.5 ) ) {
4795                 setScaleDistance( 0.01 );
4796             }
4797             else if ( height <= 5.0 ) {
4798                 setScaleDistance( 0.1 );
4799             }
4800             else if ( height <= 50.0 ) {
4801                 setScaleDistance( 1 );
4802             }
4803             else if ( height <= 500.0 ) {
4804                 setScaleDistance( 10 );
4805             }
4806             else {
4807                 setScaleDistance( 100 );
4808             }
4809         }
4810         else {
4811             setScaleDistance( 0.0 );
4812         }
4813         String scale_label = String.valueOf( getScaleDistance() );
4814         if ( !ForesterUtil.isEmpty( _phylogeny.getDistanceUnit() ) ) {
4815             scale_label += " [" + _phylogeny.getDistanceUnit() + "]";
4816         }
4817         setScaleLabel( scale_label );
4818     }
4819
4820     final Color calculateSequenceBasedColor( final Sequence seq ) {
4821         if ( ForesterUtil.isEmpty( seq.getName() ) ) {
4822             return getTreeColorSet().getSequenceColor();
4823         }
4824         Color c = null;
4825         final String seq_name = seq.getName();
4826         c = getControlPanel().getSequenceColors().get( seq_name );
4827         if ( c == null ) {
4828             c = AptxUtil.calculateColorFromString( seq_name, false );
4829             getControlPanel().getSequenceColors().put( seq_name, c );
4830         }
4831         return c;
4832     }
4833
4834     final Color calculateTaxonomyBasedColor( final Taxonomy tax ) {
4835         if ( getOptions().isColorByTaxonomicGroup() ) {
4836             if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4837                 boolean ex = false;
4838                 String group = null;
4839                 try {
4840                     group = TaxonomyUtil.getTaxGroupByTaxCode( tax.getTaxonomyCode() );
4841                 }
4842                 catch ( final Exception e ) {
4843                     ex = true;
4844                 }
4845                 if ( !ex && !ForesterUtil.isEmpty( group ) ) {
4846                     final Color c = ForesterUtil.obtainColorDependingOnTaxonomyGroup( group );
4847                     if ( c != null ) {
4848                         return c;
4849                     }
4850                 }
4851             }
4852             return getTreeColorSet().getTaxonomyColor();
4853         }
4854         else {
4855             if ( ForesterUtil.isEmpty( tax.getTaxonomyCode() ) && ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4856                 return getTreeColorSet().getTaxonomyColor();
4857             }
4858             Color c = null;
4859             if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4860                 c = getControlPanel().getSpeciesColors().get( tax.getTaxonomyCode() );
4861             }
4862             if ( ( c == null ) && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4863                 c = getControlPanel().getSpeciesColors().get( tax.getScientificName() );
4864             }
4865             if ( c == null ) {
4866                 if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4867                     c = AptxUtil.calculateColorFromString( tax.getTaxonomyCode(), true );
4868                     getControlPanel().getSpeciesColors().put( tax.getTaxonomyCode(), c );
4869                 }
4870                 else {
4871                     c = AptxUtil.calculateColorFromString( tax.getScientificName(), true );
4872                     getControlPanel().getSpeciesColors().put( tax.getScientificName(), c );
4873                 }
4874             }
4875             return c;
4876         }
4877     }
4878
4879     void clearCurrentExternalNodesDataBuffer() {
4880         setCurrentExternalNodesDataBuffer( new StringBuilder() );
4881     }
4882
4883     /**
4884      * Collapse the tree from the given node
4885      *
4886      * @param node
4887      *            a PhylogenyNode
4888      */
4889     final void collapse( final PhylogenyNode node ) {
4890         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4891             JOptionPane.showMessageDialog( this,
4892                                            "Cannot collapse in unrooted display type",
4893                                            "Attempt to collapse in unrooted display",
4894                                            JOptionPane.WARNING_MESSAGE );
4895             return;
4896         }
4897         if ( !node.isExternal() && !node.isRoot() ) {
4898             final boolean collapse = !node.isCollapse();
4899             TreePanelUtil.collapseSubtree( node, collapse );
4900             updateSetOfCollapsedExternalNodes();
4901             _phylogeny.recalculateNumberOfExternalDescendants( true );
4902             resetNodeIdToDistToLeafMap();
4903             calculateLongestExtNodeInfo();
4904             setNodeInPreorderToNull();
4905             _control_panel.displayedPhylogenyMightHaveChanged( true );
4906             resetPreferredSize();
4907             updateOvSizes();
4908             _main_panel.adjustJScrollPane();
4909             repaint();
4910         }
4911     }
4912
4913     final void uncollapseAll( final PhylogenyNode node ) {
4914         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4915             JOptionPane.showMessageDialog( this,
4916                                            "Cannot uncollapse in unrooted display type",
4917                                            "Attempt to uncollapse in unrooted display",
4918                                            JOptionPane.WARNING_MESSAGE );
4919             return;
4920         }
4921         if ( !node.isExternal() ) {
4922             TreePanelUtil.uncollapseSubtree( node );
4923             updateSetOfCollapsedExternalNodes();
4924             _phylogeny.recalculateNumberOfExternalDescendants( true );
4925             resetNodeIdToDistToLeafMap();
4926             calculateLongestExtNodeInfo();
4927             setNodeInPreorderToNull();
4928             _control_panel.displayedPhylogenyMightHaveChanged( true );
4929             resetPreferredSize();
4930             updateOvSizes();
4931             _main_panel.adjustJScrollPane();
4932             repaint();
4933         }
4934     }
4935
4936     final void collapseSpeciesSpecificSubtrees() {
4937         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4938             return;
4939         }
4940         setWaitCursor();
4941         TreePanelUtil.collapseSpeciesSpecificSubtrees( _phylogeny );
4942         updateSetOfCollapsedExternalNodes();
4943         _phylogeny.recalculateNumberOfExternalDescendants( true );
4944         resetNodeIdToDistToLeafMap();
4945         calculateLongestExtNodeInfo();
4946         setNodeInPreorderToNull();
4947         resetPreferredSize();
4948         resetDepthCollapseDepthValue();
4949         resetRankCollapseRankValue();
4950         _main_panel.adjustJScrollPane();
4951         getControlPanel().showWhole();
4952         setArrowCursor();
4953     }
4954
4955     final void colorRank( final String rank ) {
4956         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4957             return;
4958         }
4959         setWaitCursor();
4960         AptxUtil.removeBranchColors( _phylogeny );
4961         final int colorizations = TreePanelUtil.colorPhylogenyAccordingToRanks( _phylogeny, rank, this );
4962         if ( colorizations > 0 ) {
4963             _control_panel.setColorBranches( true );
4964             if ( _control_panel.getUseVisualStylesCb() != null ) {
4965                 _control_panel.getUseVisualStylesCb().setSelected( true );
4966             }
4967             if ( _control_panel.getColorAccSpeciesCb() != null ) {
4968                 _control_panel.getColorAccSpeciesCb().setSelected( false );
4969             }
4970             _options.setColorLabelsSameAsParentBranch( true );
4971             if ( getMainPanel().getMainFrame()._color_labels_same_as_parent_branch != null ) {
4972                 getMainPanel().getMainFrame()._color_labels_same_as_parent_branch.setSelected( true );
4973             }
4974             _control_panel.repaint();
4975         }
4976         setArrowCursor();
4977         repaint();
4978         if ( colorizations > 0 ) {
4979             String msg = "Taxonomy colorization via " + rank + " completed:\n";
4980             if ( colorizations > 1 ) {
4981                 msg += "colorized " + colorizations + " subtrees";
4982             }
4983             else {
4984                 msg += "colorized one subtree";
4985             }
4986             setEdited( true );
4987             JOptionPane.showMessageDialog( this,
4988                                            msg,
4989                                            "Taxonomy Rank-Colorization Completed (" + rank + ")",
4990                                            JOptionPane.INFORMATION_MESSAGE );
4991         }
4992         else {
4993             String msg = "Could not taxonomy rank-colorize any subtree via " + rank + ".\n";
4994             msg += "Possible solutions (given that suitable taxonomic information is present):\n";
4995             msg += "select a different rank (e.g. phylum, genus, ...)\n";
4996             msg += "  and/or\n";
4997             msg += "execute:\n";
4998             msg += "1. \"" + MainFrame.OBTAIN_DETAILED_TAXONOMIC_INFORMATION + "\" (Tools)\n";
4999             msg += "2. \"" + MainFrame.INFER_ANCESTOR_TAXONOMIES + "\" (Analysis)";
5000             JOptionPane.showMessageDialog( this,
5001                                            msg,
5002                                            "Taxonomy Rank-Colorization Failed",
5003                                            JOptionPane.WARNING_MESSAGE );
5004         }
5005     }
5006
5007     final void confColor() {
5008         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5009             return;
5010         }
5011         setWaitCursor();
5012         AptxUtil.removeBranchColors( _phylogeny );
5013         TreePanelUtil.colorPhylogenyAccordingToConfidenceValues( _phylogeny, this );
5014         _control_panel.setColorBranches( true );
5015         if ( _control_panel.getUseVisualStylesCb() != null ) {
5016             _control_panel.getUseVisualStylesCb().setSelected( true );
5017         }
5018         setArrowCursor();
5019         repaint();
5020     }
5021
5022     final void decreaseDomainStructureEvalueThresholdExp() {
5023         if ( _domain_structure_e_value_thr_exp > -20 ) {
5024             _domain_structure_e_value_thr_exp -= 1;
5025         }
5026     }
5027
5028     /**
5029      * Find the node, if any, at the given location
5030      *
5031      * @param x
5032      * @param y
5033      * @return pointer to the node at x,y, null if not found
5034      */
5035     public final PhylogenyNode findNode( final int x, final int y ) {
5036         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5037             return null;
5038         }
5039         final int half_box_size_plus_wiggle = ( getOptions().getDefaultNodeShapeSize() / 2 ) + WIGGLE;
5040         for( final PhylogenyNodeIterator iter = _phylogeny.iteratorPostorder(); iter.hasNext(); ) {
5041             final PhylogenyNode node = iter.next();
5042             if ( ( _phylogeny.isRooted() || !node.isRoot() || ( node.getNumberOfDescendants() > 2 ) )
5043                     && ( ( node.getXcoord() - half_box_size_plus_wiggle ) <= x )
5044                     && ( ( node.getXcoord() + half_box_size_plus_wiggle ) >= x )
5045                     && ( ( node.getYcoord() - half_box_size_plus_wiggle ) <= y )
5046                     && ( ( node.getYcoord() + half_box_size_plus_wiggle ) >= y ) ) {
5047                 return node;
5048             }
5049         }
5050         return null;
5051     }
5052
5053     final Configuration getConfiguration() {
5054         return _configuration;
5055     }
5056
5057     final ControlPanel getControlPanel() {
5058         return _control_panel;
5059     }
5060
5061     String getCurrentExternalNodesDataBufferAsString() {
5062         return _current_external_nodes_data_buffer.toString();
5063     }
5064
5065     int getCurrentExternalNodesDataBufferChangeCounter() {
5066         return _current_external_nodes_data_buffer_change_counter;
5067     }
5068
5069     final int getDomainStructureEvalueThresholdExp() {
5070         return _domain_structure_e_value_thr_exp;
5071     }
5072
5073     public final Set<Long> getFoundNodes0() {
5074         return _found_nodes_0;
5075     }
5076
5077     public final Set<Long> getFoundNodes1() {
5078         return _found_nodes_1;
5079     }
5080
5081     public List<PhylogenyNode> getFoundNodesAsListOfPhylogenyNodes() {
5082         final List<PhylogenyNode> additional_nodes = new ArrayList<PhylogenyNode>();
5083         if ( getFoundNodes0() != null ) {
5084             for( final Long id : getFoundNodes0() ) {
5085                 final PhylogenyNode n = _phylogeny.getNode( id );
5086                 if ( n != null ) {
5087                     additional_nodes.add( n );
5088                 }
5089             }
5090         }
5091         if ( getFoundNodes1() != null ) {
5092             for( final Long id : getFoundNodes1() ) {
5093                 if ( ( getFoundNodes0() == null ) || !getFoundNodes0().contains( id ) ) {
5094                     final PhylogenyNode n = _phylogeny.getNode( id );
5095                     if ( n != null ) {
5096                         additional_nodes.add( n );
5097                     }
5098                 }
5099             }
5100         }
5101         return additional_nodes;
5102     }
5103
5104     final Color getGraphicsForNodeBoxWithColorForParentBranch( final PhylogenyNode node ) {
5105         if ( getControlPanel().isUseVisualStyles() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
5106             return ( PhylogenyMethods.getBranchColorValue( node ) );
5107         }
5108         else {
5109             return ( getTreeColorSet().getBranchColor() );
5110         }
5111     }
5112
5113     final int getLongestExtNodeInfo() {
5114         return _longest_ext_node_info;
5115     }
5116
5117     final Options getOptions() {
5118         if ( _options == null ) {
5119             _options = getControlPanel().getOptions();
5120         }
5121         return _options;
5122     }
5123
5124     final Rectangle2D getOvRectangle() {
5125         return _ov_rectangle;
5126     }
5127
5128     final Rectangle getOvVirtualRectangle() {
5129         return _ov_virtual_rectangle;
5130     }
5131
5132     final PHYLOGENY_GRAPHICS_TYPE getPhylogenyGraphicsType() {
5133         return _graphics_type;
5134     }
5135
5136     final Color getSequenceBasedColor( final PhylogenyNode node ) {
5137         if ( node.getNodeData().isHasSequence() ) {
5138             return calculateSequenceBasedColor( node.getNodeData().getSequence() );
5139         }
5140         // return non-colorized color
5141         return getTreeColorSet().getSequenceColor();
5142     }
5143
5144     final double getStartingAngle() {
5145         return _urt_starting_angle;
5146     }
5147
5148     DescriptiveStatistics getStatisticsForExpressionValues() {
5149         return _statistics_for_vector_data;
5150     }
5151
5152     final Color getTaxonomyBasedColor( final PhylogenyNode node ) {
5153         if ( node.isExternal() && node.getNodeData().isHasTaxonomy() ) {
5154             return calculateTaxonomyBasedColor( node.getNodeData().getTaxonomy() );
5155         }
5156         // return non-colorized color
5157         return getTreeColorSet().getTaxonomyColor();
5158     }
5159
5160     public final File getTreeFile() {
5161         return _treefile;
5162     }
5163
5164     final float getXcorrectionFactor() {
5165         return _x_correction_factor;
5166     }
5167
5168     final float getXdistance() {
5169         return _x_distance;
5170     }
5171
5172     final float getYdistance() {
5173         return _y_distance;
5174     }
5175
5176     final void increaseDomainStructureEvalueThresholdExp() {
5177         if ( _domain_structure_e_value_thr_exp < 3 ) {
5178             _domain_structure_e_value_thr_exp += 1;
5179         }
5180     }
5181
5182     final void initNodeData() {
5183         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5184             return;
5185         }
5186         double _max_original_domain_structure_width = 0.0;
5187         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
5188             if ( node.getNodeData().isHasSequence()
5189                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
5190                 RenderableDomainArchitecture rds = null;
5191                 if ( !( node.getNodeData().getSequence()
5192                         .getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
5193                     if ( SPECIAL_DOMAIN_COLORING ) {
5194                         rds = new RenderableDomainArchitecture( node.getNodeData().getSequence()
5195                                 .getDomainArchitecture(), node.getName() );
5196                     }
5197                     else {
5198                         rds = new RenderableDomainArchitecture( node.getNodeData().getSequence()
5199                                 .getDomainArchitecture() );
5200                     }
5201                     node.getNodeData().getSequence().setDomainArchitecture( rds );
5202                 }
5203                 else {
5204                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
5205                 }
5206                 if ( getControlPanel().isShowDomainArchitectures() ) {
5207                     final double dsw = rds.getOriginalSize().getWidth();
5208                     if ( dsw > _max_original_domain_structure_width ) {
5209                         _max_original_domain_structure_width = dsw;
5210                     }
5211                 }
5212             }
5213         }
5214         if ( getControlPanel().isShowDomainArchitectures() ) {
5215             final float ds_factor_width = ( float ) ( _domain_structure_width / _max_original_domain_structure_width );
5216             for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
5217                 if ( node.getNodeData().isHasSequence()
5218                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
5219                     final RenderableDomainArchitecture rds = ( RenderableDomainArchitecture ) node.getNodeData()
5220                             .getSequence().getDomainArchitecture();
5221                     rds.setRenderingFactorWidth( ds_factor_width );
5222                     rds.setParameter( _domain_structure_e_value_thr_exp );
5223                 }
5224             }
5225         }
5226     }
5227
5228     final boolean inOv( final MouseEvent e ) {
5229         return ( ( e.getX() > ( getVisibleRect().x + getOvXPosition() + 1 ) )
5230                 && ( e.getX() < ( ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() ) - 1 ) )
5231                 && ( e.getY() > ( getVisibleRect().y + getOvYPosition() + 1 ) )
5232                 && ( e.getY() < ( ( getVisibleRect().y + getOvYPosition() + getOvMaxHeight() ) - 1 ) ) );
5233     }
5234
5235     final boolean inOvRectangle( final MouseEvent e ) {
5236         return ( ( e.getX() >= ( getOvRectangle().getX() - 1 ) )
5237                 && ( e.getX() <= ( getOvRectangle().getX() + getOvRectangle().getWidth() + 1 ) )
5238                 && ( e.getY() >= ( getOvRectangle().getY() - 1 ) )
5239                 && ( e.getY() <= ( getOvRectangle().getY() + getOvRectangle().getHeight() + 1 ) ) );
5240     }
5241
5242     final boolean isCanCollapse() {
5243         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5244     }
5245
5246     final boolean isCanUncollapseAll( final PhylogenyNode node ) {
5247         if ( node.isExternal() || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) ) {
5248             return false;
5249         }
5250         if ( node.isCollapse() ) {
5251             return true;
5252         }
5253         final PhylogenyNodeIterator it = new PreorderTreeIterator( node );
5254         while ( it.hasNext() ) {
5255             if ( it.next().isCollapse() ) {
5256                 return true;
5257             }
5258         }
5259         return false;
5260     }
5261
5262     final boolean isCanColorSubtree() {
5263         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5264     }
5265
5266     final boolean isCanCopy() {
5267         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
5268     }
5269
5270     final boolean isCanCut( final PhylogenyNode node ) {
5271         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
5272                 && !node.isRoot() );
5273     }
5274
5275     final boolean isCanDelete() {
5276         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
5277     }
5278
5279     final boolean isCanPaste() {
5280         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
5281                 && ( getCutOrCopiedTree() != null ) && !getCutOrCopiedTree().isEmpty() );
5282     }
5283
5284     final boolean isCanReroot() {
5285         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && ( _subtree_index < 1 ) );
5286     }
5287
5288     final boolean isCanSubtree( final PhylogenyNode node ) {
5289         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && !node.isExternal()
5290                 && ( !node.isRoot() || ( _subtree_index > 0 ) ) );
5291     }
5292
5293     final boolean isCurrentTreeIsSubtree() {
5294         return ( _subtree_index > 0 );
5295     }
5296
5297     final boolean isEdited() {
5298         return _edited;
5299     }
5300
5301     final boolean isInOvRect() {
5302         return _in_ov_rect;
5303     }
5304
5305     final boolean isOvOn() {
5306         return _ov_on;
5307     }
5308
5309     final boolean isPhyHasBranchLengths() {
5310         return _phy_has_branch_lengths;
5311     }
5312
5313     final void midpointRoot() {
5314         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5315             return;
5316         }
5317         if ( !_phylogeny.isRerootable() ) {
5318             JOptionPane.showMessageDialog( this,
5319                                            "This is not rerootable",
5320                                            "Not rerootable",
5321                                            JOptionPane.WARNING_MESSAGE );
5322             return;
5323         }
5324         setNodeInPreorderToNull();
5325         setWaitCursor();
5326         PhylogenyMethods.midpointRoot( _phylogeny );
5327         resetNodeIdToDistToLeafMap();
5328         setArrowCursor();
5329         setEdited( true );
5330         repaint();
5331     }
5332
5333     final void mouseClicked( final MouseEvent e ) {
5334         if ( getOptions().isShowOverview() && isOvOn() && isInOv() ) {
5335             final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
5336             final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
5337             double x = ( e.getX() - getVisibleRect().x - getOvXPosition() - ( getOvRectangle().getWidth() / 2.0 ) )
5338                     * w_ratio;
5339             double y = ( e.getY() - getVisibleRect().y - getOvYPosition() - ( getOvRectangle().getHeight() / 2.0 ) )
5340                     * h_ratio;
5341             if ( x < 0 ) {
5342                 x = 0;
5343             }
5344             if ( y < 0 ) {
5345                 y = 0;
5346             }
5347             final double max_x = getWidth() - getVisibleRect().width;
5348             final double max_y = getHeight() - getVisibleRect().height;
5349             if ( x > max_x ) {
5350                 x = max_x;
5351             }
5352             if ( y > max_y ) {
5353                 y = max_y;
5354             }
5355             getMainPanel().getCurrentScrollPane().getViewport()
5356                     .setViewPosition( new Point( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ) ) );
5357             setInOvRect( true );
5358             repaint();
5359         }
5360         else {
5361             final PhylogenyNode node = findNode( e.getX(), e.getY() );
5362             if ( node != null ) {
5363                 if ( !node.isRoot() && node.getParent().isCollapse() ) {
5364                     return;
5365                 }
5366                 _highlight_node = node;
5367                 // Check if shift key is down
5368                 if ( ( e.getModifiers() & InputEvent.SHIFT_MASK ) != 0 ) {
5369                     // Yes, so add to _found_nodes
5370                     if ( getFoundNodes0() == null ) {
5371                         setFoundNodes0( new HashSet<Long>() );
5372                     }
5373                     getFoundNodes0().add( node.getId() );
5374                     // Check if control key is down
5375                 }
5376                 else if ( ( e.getModifiers() & InputEvent.CTRL_MASK ) != 0 ) {
5377                     // Yes, so pop-up menu
5378                     displayNodePopupMenu( node, e.getX(), e.getY() );
5379                     // Handle unadorned click
5380                 }
5381                 else {
5382                     // Check for right mouse button
5383                     if ( e.getModifiers() == 4 ) {
5384                         displayNodePopupMenu( node, e.getX(), e.getY() );
5385                     }
5386                     else {
5387                         // if not in _found_nodes, clear _found_nodes
5388                         handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
5389                     }
5390                 }
5391             }
5392             else {
5393                 // no node was clicked so partition tree instead
5394                 _highlight_node = null;
5395                 
5396                 _clicked_x = e.getX();
5397                 if (!getPhylogeny().isEmpty()) {
5398                 // should be calculated on each partition as the tree can theoretically
5399                 // change in the meantime
5400                 PhylogenyNode furthestNode = PhylogenyMethods.calculateNodeWithMaxDistanceToRoot( _phylogeny );
5401                 _furthest_node_x = furthestNode.getXcoord();
5402                 _root_x = _phylogeny.getRoot().getXcoord();
5403                 
5404                 // don't bother if 0 distance tree or clicked x lies outside of tree
5405                 if (_furthest_node_x != _root_x && !(_clicked_x < _root_x || _clicked_x > _furthest_node_x)) 
5406                 {
5407                     _partition_tree = true;
5408
5409                 }
5410
5411
5412         }
5413                 }
5414             }
5415         repaint();
5416     }
5417
5418     final void mouseDragInBrowserPanel( final MouseEvent e ) {
5419         setCursor( MOVE_CURSOR );
5420         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
5421         scroll_position.x -= ( e.getX() - getLastDragPointX() );
5422         scroll_position.y -= ( e.getY() - getLastDragPointY() );
5423         if ( scroll_position.x < 0 ) {
5424             scroll_position.x = 0;
5425         }
5426         else {
5427             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
5428                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
5429             if ( scroll_position.x > max_x ) {
5430                 scroll_position.x = max_x;
5431             }
5432         }
5433         if ( scroll_position.y < 0 ) {
5434             scroll_position.y = 0;
5435         }
5436         else {
5437             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
5438                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
5439             if ( scroll_position.y > max_y ) {
5440                 scroll_position.y = max_y;
5441             }
5442         }
5443         if ( isOvOn() || getOptions().isShowScale() ) {
5444             repaint();
5445         }
5446         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
5447     }
5448
5449     final void mouseDragInOvRectangle( final MouseEvent e ) {
5450         setCursor( HAND_CURSOR );
5451         final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
5452         final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
5453         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
5454         double dx = ( ( w_ratio * e.getX() ) - ( w_ratio * getLastDragPointX() ) );
5455         double dy = ( ( h_ratio * e.getY() ) - ( h_ratio * getLastDragPointY() ) );
5456         scroll_position.x = ForesterUtil.roundToInt( scroll_position.x + dx );
5457         scroll_position.y = ForesterUtil.roundToInt( scroll_position.y + dy );
5458         if ( scroll_position.x <= 0 ) {
5459             scroll_position.x = 0;
5460             dx = 0;
5461         }
5462         else {
5463             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
5464                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
5465             if ( scroll_position.x >= max_x ) {
5466                 dx = 0;
5467                 scroll_position.x = max_x;
5468             }
5469         }
5470         if ( scroll_position.y <= 0 ) {
5471             dy = 0;
5472             scroll_position.y = 0;
5473         }
5474         else {
5475             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
5476                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
5477             if ( scroll_position.y >= max_y ) {
5478                 dy = 0;
5479                 scroll_position.y = max_y;
5480             }
5481         }
5482         repaint();
5483         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
5484         setLastMouseDragPointX( ( float ) ( e.getX() + dx ) );
5485         setLastMouseDragPointY( ( float ) ( e.getY() + dy ) );
5486     }
5487
5488     final void mouseMoved( final MouseEvent e ) {
5489         requestFocusInWindow();
5490         if ( _current_external_nodes != null ) {
5491             _current_external_nodes = null;
5492             repaint();
5493         }
5494         if ( getControlPanel().isNodeDescPopup() ) {
5495             if ( _node_desc_popup != null ) {
5496                 _node_desc_popup.hide();
5497                 _node_desc_popup = null;
5498             }
5499         }
5500         if ( getOptions().isShowOverview() && isOvOn() ) {
5501             if ( inOvVirtualRectangle( e ) ) {
5502                 if ( !isInOvRect() ) {
5503                     setInOvRect( true );
5504                     repaint();
5505                 }
5506             }
5507             else {
5508                 if ( isInOvRect() ) {
5509                     setInOvRect( false );
5510                     repaint();
5511                 }
5512             }
5513         }
5514         if ( inOv( e ) && getOptions().isShowOverview() && isOvOn() ) {
5515             if ( !isInOv() ) {
5516                 setInOv( true );
5517             }
5518         }
5519         else {
5520             if ( isInOv() ) {
5521                 setInOv( false );
5522             }
5523             final PhylogenyNode node = findNode( e.getX(), e.getY() );
5524             if ( ( node != null ) && ( node.isRoot() || !node.getParent().isCollapse() ) ) {
5525                 if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.GET_EXT_DESC_DATA ) ) {
5526                     for( final PhylogenyNode n : node.getAllExternalDescendants() ) {
5527                         addToCurrentExternalNodes( n.getId() );
5528                     }
5529                     setCursor( HAND_CURSOR );
5530                     repaint();
5531                 }
5532                 else if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.CUT_SUBTREE )
5533                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.COPY_SUBTREE )
5534                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.PASTE_SUBTREE )
5535                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.DELETE_NODE_OR_SUBTREE )
5536                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.REROOT )
5537                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.ADD_NEW_NODE ) ) {
5538                     setCursor( CUT_CURSOR );
5539                 }
5540                 else {
5541                     setCursor( HAND_CURSOR );
5542                     if ( getControlPanel().isNodeDescPopup() ) {
5543                         showNodeDataPopup( e, node );
5544                     }
5545                 }
5546             }
5547             else {
5548                 setCursor( ARROW_CURSOR );
5549             }
5550         }
5551     }
5552
5553     final void mouseReleasedInBrowserPanel( final MouseEvent e ) {
5554         setCursor( ARROW_CURSOR );
5555     }
5556
5557     final void multiplyUrtFactor( final float f ) {
5558         _urt_factor *= f;
5559     }
5560
5561     final void paintBranchCircular( final PhylogenyNode p,
5562                                     final PhylogenyNode c,
5563                                     final Graphics2D g,
5564                                     final boolean radial_labels,
5565                                     final boolean to_pdf,
5566                                     final boolean to_graphics_file ) {
5567         final double angle = _urt_nodeid_angle_map.get( c.getId() );
5568         final double root_x = _root.getXcoord();
5569         final double root_y = _root.getYcoord();
5570         final double dx = root_x - p.getXcoord();
5571         final double dy = root_y - p.getYcoord();
5572         final double parent_radius = Math.sqrt( ( dx * dx ) + ( dy * dy ) );
5573         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
5574         assignGraphicsForBranchWithColorForParentBranch( c, false, g, to_pdf, to_graphics_file );
5575         if ( ( c.isFirstChildNode() || c.isLastChildNode() )
5576                 && ( ( Math.abs( parent_radius * arc ) > 1.5 ) || to_pdf || to_graphics_file ) ) {
5577             final double r2 = 2.0 * parent_radius;
5578             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
5579         }
5580         drawLine( c.getXcoord(),
5581                   c.getYcoord(),
5582                   root_x + ( Math.cos( angle ) * parent_radius ),
5583                   root_y + ( Math.sin( angle ) * parent_radius ),
5584                   g );
5585         paintNodeBox( c.getXcoord(), c.getYcoord(), c, g, to_pdf, to_graphics_file );
5586         if ( c.isExternal() ) {
5587             final boolean is_in_found_nodes = isInFoundNodes0( c ) || isInFoundNodes1( c )
5588                     || isInCurrentExternalNodes( c );
5589             if ( ( _dynamic_hiding_factor > 1 ) && !is_in_found_nodes
5590                     && ( ( _urt_nodeid_index_map.get( c.getId() ) % _dynamic_hiding_factor ) != 1 ) ) {
5591                 return;
5592             }
5593             paintNodeDataUnrootedCirc( g, c, to_pdf, to_graphics_file, radial_labels, 0, is_in_found_nodes );
5594         }
5595     }
5596
5597     final void paintBranchCircularLite( final PhylogenyNode p, final PhylogenyNode c, final Graphics2D g ) {
5598         final double angle = _urt_nodeid_angle_map.get( c.getId() );
5599         final double root_x = _root.getXSecondary();
5600         final double root_y = _root.getYSecondary();
5601         final double dx = root_x - p.getXSecondary();
5602         final double dy = root_y - p.getYSecondary();
5603         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
5604         final double parent_radius = Math.sqrt( ( dx * dx ) + ( dy * dy ) );
5605         g.setColor( getTreeColorSet().getOvColor() );
5606         if ( ( c.isFirstChildNode() || c.isLastChildNode() ) && ( Math.abs( arc ) > 0.02 ) ) {
5607             final double r2 = 2.0 * parent_radius;
5608             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
5609         }
5610         drawLine( c.getXSecondary(),
5611                   c.getYSecondary(),
5612                   root_x + ( Math.cos( angle ) * parent_radius ),
5613                   root_y + ( Math.sin( angle ) * parent_radius ),
5614                   g );
5615         if ( isInFoundNodes( c ) || isInCurrentExternalNodes( c ) ) {
5616             g.setColor( getColorForFoundNode( c ) );
5617             drawRectFilled( c.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5618                             c.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5619                             OVERVIEW_FOUND_NODE_BOX_SIZE,
5620                             OVERVIEW_FOUND_NODE_BOX_SIZE,
5621                             g );
5622         }
5623     }
5624
5625     final void paintCircular( final Phylogeny phy,
5626                               final double starting_angle,
5627                               final int center_x,
5628                               final int center_y,
5629                               final int radius,
5630                               final Graphics2D g,
5631                               final boolean to_pdf,
5632                               final boolean to_graphics_file ) {
5633         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes() - _collapsed_external_nodeid_set.size();
5634         System.out.println( "# collapsed external = " + _collapsed_external_nodeid_set.size() );
5635         _root = phy.getRoot();
5636         _root.setXcoord( center_x );
5637         _root.setYcoord( center_y );
5638         final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
5639         double current_angle = starting_angle;
5640         int i = 0;
5641         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
5642             final PhylogenyNode n = it.next();
5643             if ( !n.isCollapse() ) {
5644                 n.setXcoord( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
5645                 n.setYcoord( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
5646                 _urt_nodeid_angle_map.put( n.getId(), current_angle );
5647                 _urt_nodeid_index_map.put( n.getId(), i++ );
5648                 current_angle += ( TWO_PI / circ_num_ext_nodes );
5649             }
5650             else {
5651                 //TODO remove me
5652                 System.out.println( "is collapse" + n.getName() );
5653             }
5654         }
5655         paintCirculars( phy.getRoot(), phy, center_x, center_y, radius, radial_labels, g, to_pdf, to_graphics_file );
5656         paintNodeBox( _root.getXcoord(), _root.getYcoord(), _root, g, to_pdf, to_graphics_file );
5657     }
5658
5659     final void paintCircularLite( final Phylogeny phy,
5660                                   final double starting_angle,
5661                                   final int center_x,
5662                                   final int center_y,
5663                                   final int radius,
5664                                   final Graphics2D g ) {
5665         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes();
5666         _root = phy.getRoot();
5667         _root.setXSecondary( center_x );
5668         _root.setYSecondary( center_y );
5669         double current_angle = starting_angle;
5670         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
5671             final PhylogenyNode n = it.next();
5672             n.setXSecondary( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
5673             n.setYSecondary( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
5674             _urt_nodeid_angle_map.put( n.getId(), current_angle );
5675             current_angle += ( TWO_PI / circ_num_ext_nodes );
5676         }
5677         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
5678     }
5679
5680     final void paintPhylogeny( final Graphics2D g,
5681                                final boolean to_pdf,
5682                                final boolean to_graphics_file,
5683                                final int graphics_file_width,
5684                                final int graphics_file_height,
5685                                final int graphics_file_x,
5686                                final int graphics_file_y ) {
5687         if (_partition_tree) {
5688 //            float threshold = (_clicked_x - _root_x) / (_furthest_node_x - _root_x);
5689 //            drawLine( _clicked_x, 0, _clicked_x, getHeight(),g);
5690             
5691             _partition_tree = false;
5692         }
5693         
5694         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5695             return;
5696         }
5697         if ( _control_panel.isShowSequenceRelations() ) {
5698             _query_sequence = _control_panel.getSelectedQuerySequence();
5699         }
5700         // Color the background
5701         if ( !to_pdf ) {
5702             final Rectangle r = getVisibleRect();
5703             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
5704                 g.setColor( getTreeColorSet().getBackgroundColor() );
5705                 if ( !to_graphics_file ) {
5706                     g.fill( r );
5707                 }
5708                 else {
5709                     if ( getOptions().isPrintBlackAndWhite() ) {
5710                         g.setColor( Color.WHITE );
5711                     }
5712                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
5713                 }
5714             }
5715             else {
5716                 if ( !to_graphics_file ) {
5717                     g.setPaint( new GradientPaint( r.x,
5718                                                    r.y,
5719                                                    getTreeColorSet().getBackgroundColor(),
5720                                                    r.x,
5721                                                    r.y + r.height,
5722                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
5723                     g.fill( r );
5724                 }
5725                 else {
5726                     g.setPaint( new GradientPaint( graphics_file_x,
5727                                                    graphics_file_y,
5728                                                    getTreeColorSet().getBackgroundColor(),
5729                                                    graphics_file_x,
5730                                                    graphics_file_y + graphics_file_height,
5731                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
5732                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
5733                 }
5734             }
5735             setupStroke( g );
5736         }
5737         else {
5738             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
5739         }
5740         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
5741                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5742             _external_node_index = 0;
5743             // Position starting X of tree
5744             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/ ) {
5745                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
5746             }
5747             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
5748                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE
5749                         + ( _phylogeny.getRoot().getDistanceToParent() * getXcorrectionFactor() ) ) );
5750             }
5751             else {
5752                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
5753             }
5754             // Position starting Y of tree
5755             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
5756                     + ( TreePanel.MOVE / 2.0f ) );
5757             final int dynamic_hiding_factor = calcDynamicHidingFactor();
5758             if ( getControlPanel().isDynamicallyHideData() ) {
5759                 if ( dynamic_hiding_factor > 1 ) {
5760                     getControlPanel().setDynamicHidingIsOn( true );
5761                 }
5762                 else {
5763                     getControlPanel().setDynamicHidingIsOn( false );
5764                 }
5765             }
5766             if ( _nodes_in_preorder == null ) {
5767                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
5768                 int i = 0;
5769                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
5770                     _nodes_in_preorder[ i++ ] = it.next();
5771                 }
5772             }
5773             final boolean disallow_shortcutting = ( dynamic_hiding_factor < 40 )
5774                     /* || getControlPanel().isUseVisualStyles() || getOptions().isShowDefaultNodeShapesForMarkedNodes()*/ //TODO check if this is really not needed.
5775                     || to_graphics_file || to_pdf;
5776             for( final PhylogenyNode element : _nodes_in_preorder ) {
5777                 paintNodeRectangular( g,
5778                                       element,
5779                                       to_pdf,
5780                                       getControlPanel().isDynamicallyHideData() && ( dynamic_hiding_factor > 1 ),
5781                                       dynamic_hiding_factor,
5782                                       to_graphics_file,
5783                                       disallow_shortcutting );
5784             }
5785             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
5786                 if ( !( to_graphics_file || to_pdf ) ) {
5787                     paintScale( g,
5788                                 getVisibleRect().x,
5789                                 getVisibleRect().y + getVisibleRect().height,
5790                                 to_pdf,
5791                                 to_graphics_file );
5792                 }
5793                 else {
5794                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
5795                 }
5796             }
5797             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5798                 paintPhylogenyLite( g );
5799             }
5800         }
5801         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5802             if ( getControlPanel().getDynamicallyHideData() != null ) {
5803                 getControlPanel().setDynamicHidingIsOn( false );
5804             }
5805             final double angle = getStartingAngle();
5806             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
5807             _dynamic_hiding_factor = 0;
5808             if ( getControlPanel().isDynamicallyHideData() ) {
5809                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5
5810                         * getPhylogeny().getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
5811             }
5812             if ( getControlPanel().getDynamicallyHideData() != null ) {
5813                 if ( _dynamic_hiding_factor > 1 ) {
5814                     getControlPanel().setDynamicHidingIsOn( true );
5815                 }
5816                 else {
5817                     getControlPanel().setDynamicHidingIsOn( false );
5818                 }
5819             }
5820             paintUnrooted( _phylogeny.getRoot(),
5821                            angle,
5822                            ( float ) ( angle + ( 2 * Math.PI ) ),
5823                            radial_labels,
5824                            g,
5825                            to_pdf,
5826                            to_graphics_file );
5827             if ( getOptions().isShowScale() ) {
5828                 if ( !( to_graphics_file || to_pdf ) ) {
5829                     paintScale( g,
5830                                 getVisibleRect().x,
5831                                 getVisibleRect().y + getVisibleRect().height,
5832                                 to_pdf,
5833                                 to_graphics_file );
5834                 }
5835                 else {
5836                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
5837                 }
5838             }
5839             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5840                 g.setColor( getTreeColorSet().getOvColor() );
5841                 paintUnrootedLite( _phylogeny.getRoot(),
5842                                    angle,
5843                                    angle + ( 2 * Math.PI ),
5844                                    g,
5845                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
5846                 paintOvRectangle( g );
5847             }
5848         }
5849         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
5850             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() )
5851                     / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
5852             final int d = radius + MOVE + getLongestExtNodeInfo();
5853             _dynamic_hiding_factor = 0;
5854             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
5855                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5
5856                         * getPhylogeny().getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
5857             }
5858             if ( getControlPanel().getDynamicallyHideData() != null ) {
5859                 if ( _dynamic_hiding_factor > 1 ) {
5860                     getControlPanel().setDynamicHidingIsOn( true );
5861                 }
5862                 else {
5863                     getControlPanel().setDynamicHidingIsOn( false );
5864                 }
5865             }
5866             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
5867             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5868                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
5869                         : getOvMaxWidth() / 2 );
5870                 double x_scale = 1.0;
5871                 double y_scale = 1.0;
5872                 int x_pos = getVisibleRect().x + getOvXPosition();
5873                 int y_pos = getVisibleRect().y + getOvYPosition();
5874                 if ( getWidth() > getHeight() ) {
5875                     x_scale = ( double ) getHeight() / getWidth();
5876                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
5877                 }
5878                 else {
5879                     y_scale = ( double ) getWidth() / getHeight();
5880                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
5881                 }
5882                 _at = g.getTransform();
5883                 g.scale( x_scale, y_scale );
5884                 paintCircularLite( _phylogeny,
5885                                    getStartingAngle(),
5886                                    x_pos + radius_ov,
5887                                    y_pos + radius_ov,
5888                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo()
5889                                            / ( getVisibleRect().width / getOvRectangle().getWidth() ) ) ),
5890                                    g );
5891                 g.setTransform( _at );
5892                 paintOvRectangle( g );
5893             }
5894         }
5895     }
5896
5897     final void recalculateMaxDistanceToRoot() {
5898         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
5899         if ( getPhylogeny().getRoot().getDistanceToParent() > 0 ) {
5900             _max_distance_to_root += getPhylogeny().getRoot().getDistanceToParent();
5901         }
5902     }
5903
5904     /**
5905      * Remove all edit-node frames
5906      */
5907     final void removeAllEditNodeJFrames() {
5908         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
5909             if ( _node_frames[ i ] != null ) {
5910                 _node_frames[ i ].dispose();
5911                 _node_frames[ i ] = null;
5912             }
5913         }
5914         _node_frame_index = 0;
5915     }
5916
5917     /**
5918      * Remove a node-edit frame.
5919      */
5920     final void removeEditNodeFrame( final int i ) {
5921         _node_frame_index--;
5922         _node_frames[ i ] = null;
5923         if ( i < _node_frame_index ) {
5924             for( int j = 0; j < ( _node_frame_index - 1 ); j++ ) {
5925                 _node_frames[ j ] = _node_frames[ j + 1 ];
5926             }
5927             _node_frames[ _node_frame_index ] = null;
5928         }
5929     }
5930
5931     final void reRoot( final PhylogenyNode node ) {
5932         if ( !getPhylogeny().isRerootable() ) {
5933             JOptionPane.showMessageDialog( this,
5934                                            "This is not rerootable",
5935                                            "Not rerootable",
5936                                            JOptionPane.WARNING_MESSAGE );
5937             return;
5938         }
5939         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5940             JOptionPane.showMessageDialog( this,
5941                                            "Cannot reroot in unrooted display type",
5942                                            "Attempt to reroot tree in unrooted display",
5943                                            JOptionPane.WARNING_MESSAGE );
5944             return;
5945         }
5946         getPhylogeny().reRoot( node );
5947         getPhylogeny().recalculateNumberOfExternalDescendants( true );
5948         resetNodeIdToDistToLeafMap();
5949         setNodeInPreorderToNull();
5950         resetPreferredSize();
5951         getMainPanel().adjustJScrollPane();
5952         setEdited( true );
5953         repaint();
5954         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
5955             getControlPanel().showWhole();
5956         }
5957     }
5958
5959     final void resetNodeIdToDistToLeafMap() {
5960         _nodeid_dist_to_leaf = new HashMap<Long, Short>();
5961     }
5962
5963     final void resetPreferredSize() {
5964         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
5965             return;
5966         }
5967         int x = 0;
5968         int y = 0;
5969         y = TreePanel.MOVE
5970                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
5971         if ( getControlPanel().isDrawPhylogram() ) {
5972             x = TreePanel.MOVE + getLongestExtNodeInfo()
5973                     + ForesterUtil.roundToInt( ( getXcorrectionFactor()
5974                             * getPhylogeny().calculateHeight( !_options.isCollapsedWithAverageHeigh() ) )
5975                             + getXdistance() );
5976         }
5977         else {
5978             if ( !isNonLinedUpCladogram() ) {
5979                 x = TreePanel.MOVE + getLongestExtNodeInfo() + ForesterUtil
5980                         .roundToInt( getXdistance() * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
5981             }
5982             else {
5983                 x = TreePanel.MOVE + getLongestExtNodeInfo() + ForesterUtil
5984                         .roundToInt( getXdistance() * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
5985             }
5986         }
5987         setPreferredSize( new Dimension( x, y ) );
5988     }
5989
5990     final void selectNode( final PhylogenyNode node ) {
5991         if ( ( getFoundNodes0() != null ) && getFoundNodes0().contains( node.getId() ) ) {
5992             getFoundNodes0().remove( node.getId() );
5993             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
5994             if ( getFoundNodes0().size() < 1 ) {
5995                 getControlPanel().searchReset0();
5996             }
5997         }
5998         else {
5999             getControlPanel().getSearchFoundCountsLabel0().setVisible( true );
6000             getControlPanel().getSearchResetButton0().setEnabled( true );
6001             getControlPanel().getSearchResetButton0().setVisible( true );
6002             if ( getFoundNodes0() == null ) {
6003                 setFoundNodes0( new HashSet<Long>() );
6004             }
6005             getFoundNodes0().add( node.getId() );
6006             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
6007         }
6008     }
6009
6010     final void setArrowCursor() {
6011         setCursor( ARROW_CURSOR );
6012         repaint();
6013     }
6014
6015     final void setControlPanel( final ControlPanel atv_control ) {
6016         _control_panel = atv_control;
6017     }
6018
6019     void setCurrentExternalNodesDataBuffer( final StringBuilder sb ) {
6020         increaseCurrentExternalNodesDataBufferChangeCounter();
6021         _current_external_nodes_data_buffer = sb;
6022     }
6023
6024     public final void setFoundNodes0( final Set<Long> found_nodes ) {
6025         _found_nodes_0 = found_nodes;
6026     }
6027
6028     public final void setFoundNodes1( final Set<Long> found_nodes ) {
6029         _found_nodes_1 = found_nodes;
6030     }
6031
6032     final void setInOvRect( final boolean in_ov_rect ) {
6033         _in_ov_rect = in_ov_rect;
6034     }
6035
6036     final void setLargeFonts() {
6037         getTreeFontSet().largeFonts();
6038     }
6039
6040     final void setLastMouseDragPointX( final float x ) {
6041         _last_drag_point_x = x;
6042     }
6043
6044     final void setLastMouseDragPointY( final float y ) {
6045         _last_drag_point_y = y;
6046     }
6047
6048     final void setMediumFonts() {
6049         getTreeFontSet().mediumFonts();
6050     }
6051
6052     final void setNodeInPreorderToNull() {
6053         _nodes_in_preorder = null;
6054     }
6055
6056     final void setOvOn( final boolean ov_on ) {
6057         _ov_on = ov_on;
6058     }
6059
6060     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
6061         _graphics_type = graphics_type;
6062         setTextAntialias();
6063     }
6064
6065     final void setSmallFonts() {
6066         getTreeFontSet().smallFonts();
6067     }
6068
6069     final void setStartingAngle( final double starting_angle ) {
6070         _urt_starting_angle = starting_angle;
6071     }
6072
6073     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
6074         _statistics_for_vector_data = statistics_for_expression_values;
6075     }
6076
6077     final void setSuperTinyFonts() {
6078         getTreeFontSet().superTinyFonts();
6079     }
6080
6081     final void setTextAntialias() {
6082         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
6083             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
6084                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
6085             }
6086             else {
6087                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
6088             }
6089         }
6090         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
6091             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
6092             // try {
6093             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
6094             // }
6095             // catch ( final Throwable e ) {
6096             //    _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
6097             //}
6098         }
6099         else {
6100             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
6101             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
6102         }
6103     }
6104
6105     final void setTinyFonts() {
6106         getTreeFontSet().tinyFonts();
6107     }
6108
6109     public final void setTreeFile( final File treefile ) {
6110         _treefile = treefile;
6111     }
6112
6113     final void setXcorrectionFactor( final float f ) {
6114         _x_correction_factor = f;
6115     }
6116
6117     final void setXdistance( final float x ) {
6118         _x_distance = x;
6119     }
6120
6121     final void setYdistance( final float y ) {
6122         _y_distance = y;
6123     }
6124
6125     final void sortDescendants( final PhylogenyNode node ) {
6126         if ( !node.isExternal() ) {
6127             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
6128             if ( getControlPanel().isShowTaxonomyScientificNames() || getControlPanel().isShowTaxonomyCode() ) {
6129                 pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
6130             }
6131             else if ( getControlPanel().isShowSeqNames() || getControlPanel().isShowSeqSymbols()
6132                     || getControlPanel().isShowGeneNames() ) {
6133                 pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
6134             }
6135             PhylogenyMethods.sortNodeDescendents( node, pri );
6136             setNodeInPreorderToNull();
6137             _phylogeny.externalNodesHaveChanged();
6138             _phylogeny.clearHashIdToNodeMap();
6139             _phylogeny.recalculateNumberOfExternalDescendants( true );
6140             resetNodeIdToDistToLeafMap();
6141             setEdited( true );
6142         }
6143         repaint();
6144     }
6145
6146     final void subTree( final PhylogenyNode node ) {
6147         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
6148             JOptionPane.showMessageDialog( this,
6149                                            "Cannot get a sub/super tree in unrooted display",
6150                                            "Attempt to get sub/super tree in unrooted display",
6151                                            JOptionPane.WARNING_MESSAGE );
6152             return;
6153         }
6154         if ( node.isExternal() ) {
6155             JOptionPane.showMessageDialog( this,
6156                                            "Cannot get a subtree of a external node",
6157                                            "Attempt to get subtree of external node",
6158                                            JOptionPane.WARNING_MESSAGE );
6159             return;
6160         }
6161         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
6162             JOptionPane.showMessageDialog( this,
6163                                            "Cannot get a subtree of the root node",
6164                                            "Attempt to get subtree of root node",
6165                                            JOptionPane.WARNING_MESSAGE );
6166             return;
6167         }
6168         setNodeInPreorderToNull();
6169         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
6170             _sub_phylogenies[ _subtree_index ] = _phylogeny;
6171             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
6172             ++_subtree_index;
6173             _phylogeny = TreePanelUtil.subTree( node, _phylogeny );
6174             if ( _phylogeny.getRoot().isCollapse() ) {
6175                 _phylogeny.getRoot().setCollapse( false );
6176             }
6177             _phylogeny.externalNodesHaveChanged();
6178             _phylogeny.clearHashIdToNodeMap();
6179             _phylogeny.recalculateNumberOfExternalDescendants( true );
6180             updateSubSuperTreeButton();
6181             getMainPanel().getControlPanel().search0();
6182             getMainPanel().getControlPanel().search1();
6183             resetRankCollapseRankValue();
6184             resetDepthCollapseDepthValue();
6185             getMainPanel().getControlPanel().updateDomainStructureEvaluethresholdDisplay();
6186             getMainPanel().getControlPanel().updateDepthCollapseDepthDisplay();
6187             getMainPanel().getControlPanel().updateRankCollapseRankDisplay();
6188         }
6189         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
6190             superTree();
6191         }
6192         _main_panel.getControlPanel().showWhole();
6193         repaint();
6194     }
6195
6196     final void superTree() {
6197         setNodeInPreorderToNull();
6198         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
6199         for( final PhylogenyNode n : temp_root.getDescendants() ) {
6200             n.setParent( temp_root );
6201         }
6202         _sub_phylogenies[ _subtree_index ] = null;
6203         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
6204         _phylogeny = _sub_phylogenies[ --_subtree_index ];
6205         _phylogeny.externalNodesHaveChanged();
6206         _phylogeny.clearHashIdToNodeMap();
6207         _phylogeny.recalculateNumberOfExternalDescendants( true );
6208         getMainPanel().getControlPanel().search0();
6209         getMainPanel().getControlPanel().search1();
6210         resetRankCollapseRankValue();
6211         resetDepthCollapseDepthValue();
6212         getMainPanel().getControlPanel().updateDomainStructureEvaluethresholdDisplay();
6213         getMainPanel().getControlPanel().updateDepthCollapseDepthDisplay();
6214         getMainPanel().getControlPanel().updateRankCollapseRankDisplay();
6215         updateSubSuperTreeButton();
6216     }
6217
6218     final void orderSubtree( final PhylogenyNode node ) {
6219         if ( node.isExternal() ) {
6220             return;
6221         }
6222         DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
6223         if ( getControlPanel().isShowTaxonomyScientificNames() || getControlPanel().isShowTaxonomyCode() ) {
6224             pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
6225         }
6226         else if ( getControlPanel().isShowSeqNames() || getControlPanel().isShowSeqSymbols()
6227                 || getControlPanel().isShowGeneNames() ) {
6228             pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
6229         }
6230         PhylogenyMethods.orderAppearanceX( node, true, pri );
6231         setNodeInPreorderToNull();
6232         getPhylogeny().externalNodesHaveChanged();
6233         getPhylogeny().clearHashIdToNodeMap();
6234         getPhylogeny().recalculateNumberOfExternalDescendants( true );
6235         resetNodeIdToDistToLeafMap();
6236         setEdited( true );
6237         getControlPanel().displayedPhylogenyMightHaveChanged( true );
6238         repaint();
6239     }
6240
6241     final void swap( final PhylogenyNode node ) {
6242         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
6243             return;
6244         }
6245         if ( node.getNumberOfDescendants() > 2 ) {
6246             JOptionPane.showMessageDialog( this,
6247                                            "Cannot swap descendants of nodes with more than 2 descendants",
6248                                            "Cannot swap descendants",
6249                                            JOptionPane.ERROR_MESSAGE );
6250             return;
6251         }
6252         if ( !node.isExternal() ) {
6253             node.swapChildren();
6254             setNodeInPreorderToNull();
6255             _phylogeny.externalNodesHaveChanged();
6256             _phylogeny.clearHashIdToNodeMap();
6257             _phylogeny.recalculateNumberOfExternalDescendants( true );
6258             resetNodeIdToDistToLeafMap();
6259             setEdited( true );
6260         }
6261         repaint();
6262     }
6263
6264     final void taxColor() {
6265         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
6266             return;
6267         }
6268         setWaitCursor();
6269         TreePanelUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
6270         _control_panel.setColorBranches( true );
6271         if ( _control_panel.getUseVisualStylesCb() != null ) {
6272             _control_panel.getUseVisualStylesCb().setSelected( true );
6273         }
6274         setEdited( true );
6275         setArrowCursor();
6276         repaint();
6277     }
6278
6279     final void updateOvSettings() {
6280         switch ( getOptions().getOvPlacement() ) {
6281             case LOWER_LEFT:
6282                 setOvXPosition( OV_BORDER );
6283                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
6284                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
6285                 break;
6286             case LOWER_RIGHT:
6287                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
6288                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
6289                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
6290                 break;
6291             case UPPER_RIGHT:
6292                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
6293                 setOvYPosition( OV_BORDER );
6294                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
6295                 break;
6296             default:
6297                 setOvXPosition( OV_BORDER );
6298                 setOvYPosition( OV_BORDER );
6299                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
6300                 break;
6301         }
6302     }
6303
6304     final void updateOvSizes() {
6305         if ( ( getWidth() > ( 1.05 * getVisibleRect().width ) )
6306                 || ( getHeight() > ( 1.05 * getVisibleRect().height ) ) ) {
6307             setOvOn( true );
6308             float l = getLongestExtNodeInfo();
6309             final float w_ratio = getOvMaxWidth() / getWidth();
6310             l *= w_ratio;
6311             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
6312             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
6313             float ov_xdist = 0;
6314             if ( !isNonLinedUpCladogram() ) {
6315                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
6316             }
6317             else {
6318                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
6319             }
6320             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
6321             if ( ov_xdist < 0.0 ) {
6322                 ov_xdist = 0.0f;
6323             }
6324             if ( ydist < 0.0 ) {
6325                 ydist = 0.0f;
6326             }
6327             setOvXDistance( ov_xdist );
6328             final double height = _phylogeny.calculateHeight( !_options.isCollapsedWithAverageHeigh() );
6329             if ( height > 0 ) {
6330                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
6331                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
6332             }
6333             else {
6334                 setOvXcorrectionFactor( 0 );
6335             }
6336         }
6337         else {
6338             setOvOn( false );
6339         }
6340     }
6341
6342     void updateSetOfCollapsedExternalNodes() {
6343         final Phylogeny phy = getPhylogeny();
6344         _collapsed_external_nodeid_set.clear();
6345         if ( phy != null ) {
6346             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
6347                 final PhylogenyNode ext_node = it.next();
6348                 PhylogenyNode n = ext_node;
6349                 while ( !n.isRoot() ) {
6350                     if ( n.isCollapse() ) {
6351                         _collapsed_external_nodeid_set.add( ext_node.getId() );
6352                         ext_node.setCollapse( true );
6353                         continue E;
6354                     }
6355                     n = n.getParent();
6356                 }
6357             }
6358         }
6359     }
6360
6361     final void updateSubSuperTreeButton() {
6362         if ( _subtree_index < 1 ) {
6363             getControlPanel().deactivateButtonToReturnToSuperTree();
6364         }
6365         else {
6366             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
6367         }
6368     }
6369
6370     final void updateButtonToUncollapseAll() {
6371         if ( PhylogenyMethods.isHasCollapsedNodes( _phylogeny ) ) {
6372             getControlPanel().activateButtonToUncollapseAll();
6373         }
6374         else {
6375             getControlPanel().deactivateButtonToUncollapseAll();
6376         }
6377     }
6378
6379     final void zoomInDomainStructure() {
6380         if ( _domain_structure_width < 2000 ) {
6381             _domain_structure_width *= 1.2;
6382         }
6383     }
6384
6385     final void zoomOutDomainStructure() {
6386         if ( _domain_structure_width > 20 ) {
6387             _domain_structure_width *= 0.8;
6388         }
6389     }
6390
6391     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
6392         if ( node.getNodeData().getNodeVisualData() == null ) {
6393             node.getNodeData().setNodeVisualData( new NodeVisualData() );
6394         }
6395         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
6396     }
6397
6398     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
6399         g.drawString( str, x, y );
6400     }
6401
6402     final private void drawStringX( final String str, final float x, final float y, final Graphics2D g ) {
6403         //TODO
6404         //FIXME
6405         if ( getAttributedStringMap() == null /*&& getAttributedStringMap().containsKey(str) */ ) {
6406             final AttributedString as = new AttributedString( str );
6407             //Font plainFont = new Font("Times New Roman", Font.PLAIN, 24);
6408             as.addAttribute( TextAttribute.FONT, g.getFont() );
6409             as.addAttribute( TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 1, 3 );
6410             as.addAttribute( TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, 3, 4 );
6411             as.addAttribute( TextAttribute.FOREGROUND, Color.BLUE, 1, 2 );
6412             as.addAttribute( TextAttribute.FOREGROUND, Color.PINK, 3, 5 );
6413             as.addAttribute( TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, 2, 4 );
6414             g.drawString( as.getIterator(), x, y );
6415         }
6416         else {
6417             g.drawString( str, x, y );
6418         }
6419     }
6420
6421     private final Map<String, AttributedString> getAttributedStringMap() {
6422         return _attributed_string_map;
6423     }
6424
6425     private final void setAttributedStringMap( final Map<String, AttributedString> attributed_string_map ) {
6426         _attributed_string_map = attributed_string_map;
6427     }
6428
6429     final private static boolean plusPressed( final int key_code ) {
6430         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
6431                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON )
6432                 || ( key_code == KeyEvent.VK_1 ) );
6433     }
6434
6435     public void decreaseDepthCollapseLevel() {
6436         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6437             if ( _depth_collapse_level <= 1 ) {
6438                 _depth_collapse_level = PhylogenyMethods.calculateMaxDepth( _phylogeny );
6439                 uncollapseAll();
6440             }
6441             else {
6442                 --_depth_collapse_level;
6443                 PhylogenyMethods.collapseToDepth( _phylogeny, _depth_collapse_level );
6444             }
6445         }
6446     }
6447
6448     public void increaseDepthCollapseLevel() {
6449         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6450             final int max = PhylogenyMethods.calculateMaxDepth( _phylogeny );
6451             if ( _depth_collapse_level >= max ) {
6452                 _depth_collapse_level = 1;
6453             }
6454             else {
6455                 ++_depth_collapse_level;
6456             }
6457             PhylogenyMethods.collapseToDepth( _phylogeny, _depth_collapse_level );
6458         }
6459     }
6460
6461     public void decreaseRankCollapseLevel() {
6462         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6463             final String ranks[] = PhylogenyMethods.obtainPresentRanksSorted( _phylogeny );
6464             if ( ranks.length > 1 ) {
6465                 if ( _rank_collapse_level <= 0 ) {
6466                     _rank_collapse_level = ranks.length - 1;
6467                     uncollapseAll();
6468                 }
6469                 else {
6470                     --_rank_collapse_level;
6471                     PhylogenyMethods.collapseToRank( _phylogeny,
6472                                                      mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6473                 }
6474             }
6475         }
6476     }
6477
6478     public void increaseRankCollapseLevel() {
6479         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6480             final String ranks[] = PhylogenyMethods.obtainPresentRanksSorted( _phylogeny );
6481             if ( ranks.length > 1 ) {
6482                 if ( _rank_collapse_level >= ( ranks.length - 1 ) ) {
6483                     _rank_collapse_level = 0;
6484                     PhylogenyMethods.collapseToRank( _phylogeny,
6485                                                      mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6486                 }
6487                 else if ( _rank_collapse_level == ( ranks.length - 2 ) ) {
6488                     ++_rank_collapse_level;
6489                     uncollapseAll();
6490                 }
6491                 else {
6492                     ++_rank_collapse_level;
6493                     PhylogenyMethods.collapseToRank( _phylogeny,
6494                                                      mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6495                 }
6496             }
6497         }
6498     }
6499
6500     private final static int mapToAbsoluteRankLevel( final String present_ranks_sorted[],
6501                                                      final int rank_collapse_level ) {
6502         final String rank_str = present_ranks_sorted[ rank_collapse_level ];
6503         if ( !TaxonomyUtil.RANK_TO_INT.containsKey( rank_str ) ) {
6504             throw new IllegalStateException( "unexpected exception: cannot find rank " + rank_str );
6505         }
6506         return TaxonomyUtil.RANK_TO_INT.get( rank_str );
6507     }
6508
6509     private final void uncollapseAll() {
6510         final PhylogenyNodeIterator it = new PreorderTreeIterator( _phylogeny );
6511         while ( it.hasNext() ) {
6512             it.next().setCollapse( false );
6513         }
6514     }
6515
6516     final int resetDepthCollapseDepthValue() {
6517         return _depth_collapse_level = -1;
6518     }
6519
6520     final int getDepthCollapseDepthValue() {
6521         return _depth_collapse_level;
6522     }
6523
6524     final void setDepthCollapseDepthValue( final int depth_collapse_level ) {
6525         _depth_collapse_level = depth_collapse_level;
6526     }
6527
6528     final int resetRankCollapseRankValue() {
6529         return _rank_collapse_level = -1;
6530     }
6531
6532     final int getRankCollapseRankValue() {
6533         return _rank_collapse_level;
6534     }
6535
6536     final void setRankCollapseRankValue( final int rank_collapse_level ) {
6537         _rank_collapse_level = rank_collapse_level;
6538     }
6539 }