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