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