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