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