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