fcb1580967eb32747b9fe76c544c33d6181562c4
[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( final 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     private String createAnnotationString( final SortedSet<Annotation> ann ) {
2343         final StringBuilder sb = new StringBuilder();
2344         boolean first = true;
2345         for( final Annotation a : ann ) {
2346             if ( !first ) {
2347                 sb.append( "|" );
2348             }
2349             else {
2350                 first = false;
2351             }
2352             sb.append( a.asSimpleText() );
2353         }
2354         final String ann_str = sb.toString();
2355         return ann_str;
2356     }
2357
2358     final private String createASimpleTextRepresentationOfANode( final PhylogenyNode node ) {
2359         final String tax = PhylogenyMethods.getSpecies( node );
2360         String label = node.getName();
2361         if ( !ForesterUtil.isEmpty( label ) && !ForesterUtil.isEmpty( tax ) ) {
2362             label = label + " " + tax;
2363         }
2364         else if ( !ForesterUtil.isEmpty( tax ) ) {
2365             label = tax;
2366         }
2367         else {
2368             label = "";
2369         }
2370         if ( !ForesterUtil.isEmpty( label ) ) {
2371             label = " [" + label + "]";
2372         }
2373         return label;
2374     }
2375
2376     final private void cutSubtree( final PhylogenyNode node ) {
2377         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2378             errorMessageNoCutCopyPasteInUnrootedDisplay();
2379             return;
2380         }
2381         if ( node.isRoot() ) {
2382             JOptionPane.showMessageDialog( this,
2383                                            "Cannot cut entire tree as subtree",
2384                                            "Attempt to cut entire tree",
2385                                            JOptionPane.ERROR_MESSAGE );
2386             return;
2387         }
2388         final String label = createASimpleTextRepresentationOfANode( node );
2389         final int r = JOptionPane.showConfirmDialog( null,
2390                                                      "Cut subtree" + label + "?",
2391                                                      "Confirm Cutting of Subtree",
2392                                                      JOptionPane.YES_NO_OPTION );
2393         if ( r != JOptionPane.OK_OPTION ) {
2394             return;
2395         }
2396         setNodeInPreorderToNull();
2397         setCopiedAndPastedNodes( null );
2398         setCutOrCopiedTree( _phylogeny.copy( node ) );
2399         _phylogeny.deleteSubtree( node, true );
2400         _phylogeny.clearHashIdToNodeMap();
2401         _phylogeny.recalculateNumberOfExternalDescendants( true );
2402         resetNodeIdToDistToLeafMap();
2403         setEdited( true );
2404         repaint();
2405     }
2406
2407     final private void cycleColors() {
2408         getMainPanel().getTreeColorSet().cycleColorScheme();
2409         for( final TreePanel tree_panel : getMainPanel().getTreePanels() ) {
2410             tree_panel.setBackground( getMainPanel().getTreeColorSet().getBackgroundColor() );
2411         }
2412     }
2413
2414     final private void decreaseOvSize() {
2415         if ( ( getOvMaxWidth() > 20 ) && ( getOvMaxHeight() > 20 ) ) {
2416             setOvMaxWidth( getOvMaxWidth() - 5 );
2417             setOvMaxHeight( getOvMaxHeight() - 5 );
2418             updateOvSettings();
2419             getControlPanel().displayedPhylogenyMightHaveChanged( false );
2420         }
2421     }
2422
2423     final private void deleteNodeOrSubtree( final PhylogenyNode node ) {
2424         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2425             errorMessageNoCutCopyPasteInUnrootedDisplay();
2426             return;
2427         }
2428         if ( node.isRoot() ) {
2429             JOptionPane.showMessageDialog( this,
2430                                            "Cannot delete entire tree",
2431                                            "Attempt to delete entire tree",
2432                                            JOptionPane.ERROR_MESSAGE );
2433             return;
2434         }
2435         final String label = createASimpleTextRepresentationOfANode( node );
2436         final Object[] options = { "Node only", "Entire subtree", "Cancel" };
2437         final int r = JOptionPane.showOptionDialog( this,
2438                                                     "Delete" + label + "?",
2439                                                     "Delete Node/Subtree",
2440                                                     JOptionPane.CLOSED_OPTION,
2441                                                     JOptionPane.QUESTION_MESSAGE,
2442                                                     null,
2443                                                     options,
2444                                                     options[ 2 ] );
2445         setNodeInPreorderToNull();
2446         boolean node_only = true;
2447         if ( r == 1 ) {
2448             node_only = false;
2449         }
2450         else if ( r != 0 ) {
2451             return;
2452         }
2453         if ( node_only ) {
2454             PhylogenyMethods.removeNode( node, _phylogeny );
2455         }
2456         else {
2457             _phylogeny.deleteSubtree( node, true );
2458         }
2459         _phylogeny.externalNodesHaveChanged();
2460         _phylogeny.clearHashIdToNodeMap();
2461         _phylogeny.recalculateNumberOfExternalDescendants( true );
2462         resetNodeIdToDistToLeafMap();
2463         setEdited( true );
2464         repaint();
2465     }
2466
2467     final private void displayNodePopupMenu( final PhylogenyNode node, final int x, final int y ) {
2468         makePopupMenus( node );
2469         _node_popup_menu.putClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY, node );
2470         _node_popup_menu.show( this, x, y );
2471     }
2472
2473     final private void drawArc( final double x,
2474                                 final double y,
2475                                 final double width,
2476                                 final double heigth,
2477                                 final double start_angle,
2478                                 final double arc_angle,
2479                                 final Graphics2D g ) {
2480         _arc.setArc( x, y, width, heigth, _180_OVER_PI * start_angle, _180_OVER_PI * arc_angle, Arc2D.OPEN );
2481         g.draw( _arc );
2482     }
2483
2484     final private void drawLine( final double x1, final double y1, final double x2, final double y2, final Graphics2D g ) {
2485         if ( ( x1 == x2 ) && ( y1 == y2 ) ) {
2486             return;
2487         }
2488         _line.setLine( x1, y1, x2, y2 );
2489         g.draw( _line );
2490     }
2491
2492     final private void drawOval( final double x,
2493                                  final double y,
2494                                  final double width,
2495                                  final double heigth,
2496                                  final Graphics2D g ) {
2497         _ellipse.setFrame( x, y, width, heigth );
2498         g.draw( _ellipse );
2499     }
2500
2501     final private void drawOvalFilled( final double x,
2502                                        final double y,
2503                                        final double width,
2504                                        final double heigth,
2505                                        final Graphics2D g ) {
2506         _ellipse.setFrame( x, y, width, heigth );
2507         g.fill( _ellipse );
2508     }
2509
2510     final private void drawOvalGradient( final double x,
2511                                          final double y,
2512                                          final double width,
2513                                          final double heigth,
2514                                          final Graphics2D g,
2515                                          final Color color_1,
2516                                          final Color color_2,
2517                                          final Color color_border ) {
2518         _ellipse.setFrame( x, y, width, heigth );
2519         g.setPaint( new GradientPaint( ( float ) x,
2520                                        ( float ) y,
2521                                        color_1,
2522                                        ( float ) ( x + width ),
2523                                        ( float ) ( y + heigth ),
2524                                        color_2,
2525                                        false ) );
2526         g.fill( _ellipse );
2527         if ( color_border != null ) {
2528             g.setPaint( color_border );
2529             g.draw( _ellipse );
2530         }
2531     }
2532
2533     final private void drawRect( final float x, final float y, final float width, final float heigth, final Graphics2D g ) {
2534         _rectangle.setFrame( x, y, width, heigth );
2535         g.draw( _rectangle );
2536     }
2537
2538     final private void drawRectFilled( final double x,
2539                                        final double y,
2540                                        final double width,
2541                                        final double heigth,
2542                                        final Graphics2D g ) {
2543         _rectangle.setFrame( x, y, width, heigth );
2544         g.fill( _rectangle );
2545     }
2546
2547     final private void drawRectGradient( final double x,
2548                                          final double y,
2549                                          final double width,
2550                                          final double heigth,
2551                                          final Graphics2D g,
2552                                          final Color color_1,
2553                                          final Color color_2,
2554                                          final Color color_border ) {
2555         _rectangle.setFrame( x, y, width, heigth );
2556         g.setPaint( new GradientPaint( ( float ) x,
2557                                        ( float ) y,
2558                                        color_1,
2559                                        ( float ) ( x + width ),
2560                                        ( float ) ( y + heigth ),
2561                                        color_2,
2562                                        false ) );
2563         g.fill( _rectangle );
2564         if ( color_border != null ) {
2565             g.setPaint( color_border );
2566             g.draw( _rectangle );
2567         }
2568     }
2569
2570     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
2571         final List<Uri> us = new ArrayList<Uri>();
2572         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
2573             for( final Uri uri : t.getUris() ) {
2574                 us.add( uri );
2575             }
2576         }
2577         double offset = 0;
2578         for( final Uri uri : us ) {
2579             if ( uri != null ) {
2580                 final String uri_str = uri.getValue().toString().toLowerCase();
2581                 if ( getImageMap().containsKey( uri_str ) ) {
2582                     final BufferedImage bi = getImageMap().get( uri_str );
2583                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
2584                         double scaling_factor = 1;
2585                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
2586                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
2587                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
2588                         }
2589                         // y = y - ( 0.9 * getYdistance() );
2590                         final double hs = bi.getHeight() * scaling_factor;
2591                         double ws = ( bi.getWidth() * scaling_factor ) + offset;
2592                         final double my_y = y - ( 0.5 * hs );
2593                         final int x_w = ( int ) ( x + ws + 0.5 );
2594                         final int y_h = ( int ) ( my_y + hs + 0.5 );
2595                         if ( ( ( x_w - x ) > 7 ) && ( ( y_h - my_y ) > 7 ) ) {
2596                             g.drawImage( bi,
2597                                          ( int ) ( x + 0.5 + offset ),
2598                                          ( int ) ( my_y + 0.5 ),
2599                                          x_w,
2600                                          y_h,
2601                                          0,
2602                                          0,
2603                                          bi.getWidth(),
2604                                          bi.getHeight(),
2605                                          null );
2606                             ws += 8;
2607                         }
2608                         else {
2609                             ws = 0.0;
2610                         }
2611                         offset = ws;
2612                     }
2613                 }
2614             }
2615         }
2616         return offset;
2617     }
2618
2619     final private void errorMessageNoCutCopyPasteInUnrootedDisplay() {
2620         JOptionPane.showMessageDialog( this,
2621                                        "Cannot cut, copy, paste, add, or delete subtrees/nodes in unrooted display",
2622                                        "Attempt to cut/copy/paste/add/delete in unrooted display",
2623                                        JOptionPane.ERROR_MESSAGE );
2624     }
2625
2626     final private Set<Integer> getCopiedAndPastedNodes() {
2627         return getMainPanel().getCopiedAndPastedNodes();
2628     }
2629
2630     final private Phylogeny getCutOrCopiedTree() {
2631         return getMainPanel().getCutOrCopiedTree();
2632     }
2633
2634     final private float getLastDragPointX() {
2635         return _last_drag_point_x;
2636     }
2637
2638     final private float getLastDragPointY() {
2639         return _last_drag_point_y;
2640     }
2641
2642     final private short getMaxBranchesToLeaf( final PhylogenyNode node ) {
2643         if ( !_nodeid_dist_to_leaf.containsKey( node.getId() ) ) {
2644             final short m = PhylogenyMethods.calculateMaxBranchesToLeaf( node );
2645             _nodeid_dist_to_leaf.put( node.getId(), m );
2646             return m;
2647         }
2648         else {
2649             return _nodeid_dist_to_leaf.get( node.getId() );
2650         }
2651     }
2652
2653     final private double getMaxDistanceToRoot() {
2654         if ( _max_distance_to_root < 0 ) {
2655             recalculateMaxDistanceToRoot();
2656         }
2657         return _max_distance_to_root;
2658     }
2659
2660     final private float getOvMaxHeight() {
2661         return _ov_max_height;
2662     }
2663
2664     final private float getOvMaxWidth() {
2665         return _ov_max_width;
2666     }
2667
2668     final private float getOvXcorrectionFactor() {
2669         return _ov_x_correction_factor;
2670     }
2671
2672     final private float getOvXDistance() {
2673         return _ov_x_distance;
2674     }
2675
2676     final private int getOvXPosition() {
2677         return _ov_x_position;
2678     }
2679
2680     final private float getOvYDistance() {
2681         return _ov_y_distance;
2682     }
2683
2684     final private int getOvYPosition() {
2685         return _ov_y_position;
2686     }
2687
2688     final private int getOvYStart() {
2689         return _ov_y_start;
2690     }
2691
2692     final private double getScaleDistance() {
2693         return _scale_distance;
2694     }
2695
2696     final private String getScaleLabel() {
2697         return _scale_label;
2698     }
2699
2700     final private TreeFontSet getTreeFontSet() {
2701         return getMainPanel().getTreeFontSet();
2702     }
2703
2704     final private float getUrtFactor() {
2705         return _urt_factor;
2706     }
2707
2708     final private float getUrtFactorOv() {
2709         return _urt_factor_ov;
2710     }
2711
2712     final private void handleClickToAction( final NodeClickAction action, final PhylogenyNode node ) {
2713         switch ( action ) {
2714             case SHOW_DATA:
2715                 showNodeFrame( node );
2716                 break;
2717             case COLLAPSE:
2718                 collapse( node );
2719                 break;
2720             case REROOT:
2721                 reRoot( node );
2722                 break;
2723             case SUBTREE:
2724                 subTree( node );
2725                 break;
2726             case SWAP:
2727                 swap( node );
2728                 break;
2729             case COLOR_SUBTREE:
2730                 colorSubtree( node );
2731                 break;
2732             case OPEN_SEQ_WEB:
2733                 openSeqWeb( node );
2734                 break;
2735             case BLAST:
2736                 blast( node );
2737                 break;
2738             case OPEN_TAX_WEB:
2739                 openTaxWeb( node );
2740                 break;
2741             case CUT_SUBTREE:
2742                 cutSubtree( node );
2743                 break;
2744             case COPY_SUBTREE:
2745                 copySubtree( node );
2746                 break;
2747             case PASTE_SUBTREE:
2748                 pasteSubtree( node );
2749                 break;
2750             case DELETE_NODE_OR_SUBTREE:
2751                 deleteNodeOrSubtree( node );
2752                 break;
2753             case ADD_NEW_NODE:
2754                 addEmptyNode( node );
2755                 break;
2756             case EDIT_NODE_DATA:
2757                 showNodeEditFrame( node );
2758                 break;
2759             case SELECT_NODES:
2760                 selectNode( node );
2761                 break;
2762             case SORT_DESCENDENTS:
2763                 sortDescendants( node );
2764                 break;
2765             case GET_EXT_DESC_DATA:
2766                 showExtDescNodeData( node );
2767                 break;
2768             default:
2769                 throw new IllegalArgumentException( "unknown action: " + action );
2770         }
2771     }
2772
2773     final private void increaseOvSize() {
2774         if ( ( getOvMaxWidth() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth() / 2 ) )
2775                 && ( getOvMaxHeight() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
2776                         .getHeight() / 2 ) ) ) {
2777             setOvMaxWidth( getOvMaxWidth() + 5 );
2778             setOvMaxHeight( getOvMaxHeight() + 5 );
2779             updateOvSettings();
2780             getControlPanel().displayedPhylogenyMightHaveChanged( false );
2781         }
2782     }
2783
2784     final private void init() {
2785         _color_chooser = new JColorChooser();
2786         _rollover_popup = new JTextArea();
2787         _rollover_popup.setFont( POPUP_FONT );
2788         resetNodeIdToDistToLeafMap();
2789         setTextAntialias();
2790         setTreeFile( null );
2791         setEdited( false );
2792         initializeOvSettings();
2793         setStartingAngle( ( TWO_PI * 3 ) / 4 );
2794         final ImageLoader il = new ImageLoader( this );
2795         new Thread( il ).start();
2796     }
2797
2798     final private void initializeOvSettings() {
2799         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
2800         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
2801     }
2802
2803     final private boolean inOvVirtualRectangle( final int x, final int y ) {
2804         return ( ( x >= ( getOvVirtualRectangle().x - 1 ) )
2805                 && ( x <= ( getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 ) )
2806                 && ( y >= ( getOvVirtualRectangle().y - 1 ) ) && ( y <= ( getOvVirtualRectangle().y
2807                 + getOvVirtualRectangle().height + 1 ) ) );
2808     }
2809
2810     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
2811         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
2812     }
2813
2814     final private boolean isCanBlast( final PhylogenyNode node ) {
2815         if ( !node.getNodeData().isHasSequence() && ForesterUtil.isEmpty( node.getName() ) ) {
2816             return false;
2817         }
2818         return Blast.isContainsQueryForBlast( node );
2819     }
2820
2821     final private boolean isCanOpenSeqWeb( final PhylogenyNode node ) {
2822         if ( node.getNodeData().isHasSequence()
2823                 && ( node.getNodeData().getSequence().getAccession() != null )
2824                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() )
2825                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getValue() )
2826                 && getConfiguration().isHasWebLink( node.getNodeData().getSequence().getAccession().getSource()
2827                         .toLowerCase() ) ) {
2828             return true;
2829         }
2830         return false;
2831     }
2832
2833     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
2834         if ( node.getNodeData().isHasTaxonomy()
2835                 && ( ( ( node.getNodeData().getTaxonomy().getIdentifier() != null )
2836                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getProvider() )
2837                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && getConfiguration()
2838                         .isHasWebLink( node.getNodeData().getTaxonomy().getIdentifier().getProvider().toLowerCase() ) )
2839                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
2840                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
2841                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) || ( ( node
2842                         .getNodeData().getTaxonomy().getIdentifier() != null )
2843                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && node
2844                         .getNodeData().getTaxonomy().getIdentifier().getValue().startsWith( "http://" ) ) ) ) {
2845             return true;
2846         }
2847         else {
2848             return false;
2849         }
2850     }
2851
2852     final private boolean isInFoundNodes( final PhylogenyNode node ) {
2853         return ( ( getFoundNodes() != null ) && getFoundNodes().contains( node.getId() ) );
2854     }
2855
2856     final private boolean isInOv() {
2857         return _in_ov;
2858     }
2859
2860     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
2861         int y_dist = 40;
2862         if ( getControlPanel().isShowTaxonomyImages() ) {
2863             y_dist = 40 + ( int ) getYdistance();
2864         }
2865         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - y_dist ) )
2866                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + y_dist ) ) || ( ( node.getParent() != null ) && ( node
2867                 .getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
2868     }
2869
2870     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
2871         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - 20 ) )
2872                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + 20 ) )
2873                 || ( node.getXcoord() < ( getVisibleRect().getMinX() - 20 ) ) || ( node.getXcoord() > ( getVisibleRect()
2874                 .getMaxX() + 20 ) ) );
2875     }
2876
2877     final private boolean isNonLinedUpCladogram() {
2878         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
2879     }
2880
2881     final private boolean isUniformBranchLengthsForCladogram() {
2882         return getOptions().getCladogramType() == CLADOGRAM_TYPE.TOTAL_NODE_SUM_DEP;
2883     }
2884
2885     final private void keyPressedCalls( final KeyEvent e ) {
2886         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
2887             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
2888                 if ( !isInOvRect() ) {
2889                     setInOvRect( true );
2890                 }
2891             }
2892             else if ( isInOvRect() ) {
2893                 setInOvRect( false );
2894             }
2895         }
2896         if ( e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK ) {
2897             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
2898                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
2899                 getMainPanel().getTreeFontSet().mediumFonts();
2900                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
2901             }
2902             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
2903                 getMainPanel().getTreeFontSet().decreaseFontSize();
2904                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
2905             }
2906             else if ( plusPressed( e.getKeyCode() ) ) {
2907                 getMainPanel().getTreeFontSet().increaseFontSize();
2908                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
2909             }
2910         }
2911         else {
2912             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
2913                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
2914                 getControlPanel().showWhole();
2915             }
2916             else if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
2917                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
2918                 if ( e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK ) {
2919                     if ( e.getKeyCode() == KeyEvent.VK_UP ) {
2920                         getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2921                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
2922                     }
2923                     else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
2924                         getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2925                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
2926                     }
2927                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
2928                         getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
2929                                                                    Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
2930                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
2931                     }
2932                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
2933                         getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
2934                                                                   Constants.WHEEL_ZOOM_IN_FACTOR );
2935                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
2936                     }
2937                 }
2938                 else {
2939                     final int d = 80;
2940                     int dx = 0;
2941                     int dy = -d;
2942                     if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
2943                         dy = d;
2944                     }
2945                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
2946                         dx = -d;
2947                         dy = 0;
2948                     }
2949                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
2950                         dx = d;
2951                         dy = 0;
2952                     }
2953                     final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
2954                     scroll_position.x = scroll_position.x + dx;
2955                     scroll_position.y = scroll_position.y + dy;
2956                     if ( scroll_position.x <= 0 ) {
2957                         scroll_position.x = 0;
2958                     }
2959                     else {
2960                         final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
2961                                 - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
2962                         if ( scroll_position.x >= max_x ) {
2963                             scroll_position.x = max_x;
2964                         }
2965                     }
2966                     if ( scroll_position.y <= 0 ) {
2967                         scroll_position.y = 0;
2968                     }
2969                     else {
2970                         final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
2971                                 - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
2972                         if ( scroll_position.y >= max_y ) {
2973                             scroll_position.y = max_y;
2974                         }
2975                     }
2976                     repaint();
2977                     getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
2978                 }
2979             }
2980             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
2981                 getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2982                 getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
2983                                                            Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
2984                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
2985             }
2986             else if ( plusPressed( e.getKeyCode() ) ) {
2987                 getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
2988                                                           Constants.WHEEL_ZOOM_IN_FACTOR );
2989                 getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2990                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
2991             }
2992             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
2993                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
2994                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
2995                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
2996                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2997                 }
2998             }
2999             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
3000                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3001                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3002                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
3003                     if ( getStartingAngle() < 0 ) {
3004                         setStartingAngle( TWO_PI + getStartingAngle() );
3005                     }
3006                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3007                 }
3008             }
3009             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
3010                 boolean selected = false;
3011                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
3012                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
3013                     selected = true;
3014                 }
3015                 else {
3016                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
3017                 }
3018                 if ( getMainPanel().getMainFrame() == null ) {
3019                     // Must be "E" applet version.
3020                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
3021                     if ( ae.getlabelDirectionCbmi() != null ) {
3022                         ae.getlabelDirectionCbmi().setSelected( selected );
3023                     }
3024                 }
3025                 else {
3026                     getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
3027                 }
3028                 repaint();
3029             }
3030             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
3031                 switchDisplaygetPhylogenyGraphicsType();
3032                 repaint();
3033             }
3034             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
3035                 cycleColors();
3036                 repaint();
3037             }
3038             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
3039                 MainFrame.cycleOverview( getOptions(), this );
3040                 repaint();
3041             }
3042             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
3043                 increaseOvSize();
3044             }
3045             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
3046                 decreaseOvSize();
3047             }
3048             e.consume();
3049         }
3050     }
3051
3052     final private void makePopupMenus( final PhylogenyNode node ) {
3053         _node_popup_menu = new JPopupMenu();
3054         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
3055         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
3056         for( int i = 0; i < clickto_names.size(); i++ ) {
3057             final String title = clickto_names.get( i );
3058             _node_popup_menu_items[ i ] = new JMenuItem( title );
3059             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
3060                 _node_popup_menu_items[ i ].setEnabled( isCanOpenSeqWeb( node ) );
3061             }
3062             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
3063                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
3064             }
3065             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
3066                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
3067             }
3068             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
3069                 if ( !getOptions().isEditable() ) {
3070                     continue;
3071                 }
3072                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
3073             }
3074             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
3075                 if ( !getOptions().isEditable() ) {
3076                     continue;
3077                 }
3078                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
3079             }
3080             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
3081                 if ( !getOptions().isEditable() ) {
3082                     continue;
3083                 }
3084                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
3085             }
3086             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
3087                 if ( !getOptions().isEditable() ) {
3088                     continue;
3089                 }
3090                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
3091             }
3092             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
3093                 if ( !getOptions().isEditable() ) {
3094                     continue;
3095                 }
3096             }
3097             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
3098                 if ( !getOptions().isEditable() ) {
3099                     continue;
3100                 }
3101             }
3102             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
3103                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
3104             }
3105             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
3106                 _node_popup_menu_items[ i ].setEnabled( ( isCanCollapse() && !node.isExternal() ) );
3107             }
3108             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
3109                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
3110             }
3111             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
3112                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
3113             }
3114             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
3115                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
3116             }
3117             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
3118                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
3119             }
3120             _node_popup_menu_items[ i ].addActionListener( this );
3121             _node_popup_menu.add( _node_popup_menu_items[ i ] );
3122         }
3123     }
3124
3125     private final String obtainTitleForExtDescNodeData() {
3126         switch ( getOptions().getExtDescNodeDataToReturn() ) {
3127             case NODE_NAME:
3128                 return "Node Names";
3129             case SEQUENCE_NAME:
3130                 return "Sequence Names";
3131             case SEQUENCE_SYMBOL:
3132                 return "Sequence Symbols";
3133             case SEQUENCE_MOL_SEQ:
3134                 return "Molecular Sequences";
3135             case SEQUENCE_ACC:
3136                 return "Sequence Accessors";
3137             case TAXONOMY_SCIENTIFIC_NAME:
3138                 return "Scientific Names";
3139             case TAXONOMY_CODE:
3140                 return "Taxonomy Codes";
3141             case UNKNOWN:
3142                 return "User Selected Data";
3143             default:
3144                 throw new IllegalArgumentException( "unknown data element: "
3145                         + getOptions().getExtDescNodeDataToReturn() );
3146         }
3147     }
3148
3149     final private void openSeqWeb( final PhylogenyNode node ) {
3150         if ( !isCanOpenSeqWeb( node ) ) {
3151             cannotOpenBrowserWarningMessage( "sequence" );
3152             return;
3153         }
3154         String uri_str = null;
3155         final Sequence seq = node.getNodeData().getSequence();
3156         final String source = seq.getAccession().getSource().toLowerCase();
3157         String url;
3158         if ( source.toLowerCase().equals( "ncbi" ) ) {
3159             url = Constants.NCBI_ALL_DATABASE_SEARCH;
3160         }
3161         else {
3162             final WebLink weblink = getConfiguration().getWebLink( source );
3163             url = weblink.getUrl().toString();
3164         }
3165         try {
3166             uri_str = url + URLEncoder.encode( seq.getAccession().getValue(), ForesterConstants.UTF8 );
3167         }
3168         catch ( final UnsupportedEncodingException e ) {
3169             AptxUtil.showErrorMessage( this, e.toString() );
3170             e.printStackTrace();
3171         }
3172         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3173             try {
3174                 JApplet applet = null;
3175                 if ( isApplet() ) {
3176                     applet = obtainApplet();
3177                 }
3178                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_seq" );
3179             }
3180             catch ( final IOException e ) {
3181                 AptxUtil.showErrorMessage( this, e.toString() );
3182                 e.printStackTrace();
3183             }
3184             catch ( final URISyntaxException e ) {
3185                 AptxUtil.showErrorMessage( this, e.toString() );
3186                 e.printStackTrace();
3187             }
3188         }
3189         else {
3190             cannotOpenBrowserWarningMessage( "sequence" );
3191         }
3192     }
3193
3194     final private void openTaxWeb( final PhylogenyNode node ) {
3195         if ( !isCanOpenTaxWeb( node ) ) {
3196             cannotOpenBrowserWarningMessage( "taxonomic" );
3197             return;
3198         }
3199         String uri_str = null;
3200         final Taxonomy tax = node.getNodeData().getTaxonomy();
3201         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
3202                 && getConfiguration().isHasWebLink( tax.getIdentifier().getProvider().toLowerCase() ) ) {
3203             final String type = tax.getIdentifier().getProvider().toLowerCase();
3204             final WebLink weblink = getConfiguration().getWebLink( type );
3205             try {
3206                 uri_str = weblink.getUrl() + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
3207             }
3208             catch ( final UnsupportedEncodingException e ) {
3209                 AptxUtil.showErrorMessage( this, e.toString() );
3210                 e.printStackTrace();
3211             }
3212         }
3213         else if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3214                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
3215             try {
3216                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
3217             }
3218             catch ( final URISyntaxException e ) {
3219                 AptxUtil.showErrorMessage( this, e.toString() );
3220                 uri_str = null;
3221                 e.printStackTrace();
3222             }
3223         }
3224         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
3225             try {
3226                 uri_str = "http://www.eol.org/search?q="
3227                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
3228             }
3229             catch ( final UnsupportedEncodingException e ) {
3230                 AptxUtil.showErrorMessage( this, e.toString() );
3231                 e.printStackTrace();
3232             }
3233         }
3234         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
3235             try {
3236                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3237                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
3238             }
3239             catch ( final UnsupportedEncodingException e ) {
3240                 AptxUtil.showErrorMessage( this, e.toString() );
3241                 e.printStackTrace();
3242             }
3243         }
3244         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
3245             try {
3246                 uri_str = "http://www.eol.org/search?q="
3247                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
3248             }
3249             catch ( final UnsupportedEncodingException e ) {
3250                 AptxUtil.showErrorMessage( this, e.toString() );
3251                 e.printStackTrace();
3252             }
3253         }
3254         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3255             try {
3256                 JApplet applet = null;
3257                 if ( isApplet() ) {
3258                     applet = obtainApplet();
3259                 }
3260                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_tax" );
3261             }
3262             catch ( final IOException e ) {
3263                 AptxUtil.showErrorMessage( this, e.toString() );
3264                 e.printStackTrace();
3265             }
3266             catch ( final URISyntaxException e ) {
3267                 AptxUtil.showErrorMessage( this, e.toString() );
3268                 e.printStackTrace();
3269             }
3270         }
3271         else {
3272             cannotOpenBrowserWarningMessage( "taxonomic" );
3273         }
3274     }
3275
3276     final private void paintBranchLength( final Graphics2D g,
3277                                           final PhylogenyNode node,
3278                                           final boolean to_pdf,
3279                                           final boolean to_graphics_file ) {
3280         g.setFont( getTreeFontSet().getSmallFont() );
3281         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3282             g.setColor( Color.BLACK );
3283         }
3284         else {
3285             g.setColor( getTreeColorSet().getBranchLengthColor() );
3286         }
3287         if ( !node.isRoot() ) {
3288             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3289                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3290                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3291             }
3292             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3293                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3294                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3295             }
3296             else {
3297                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3298                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3299             }
3300         }
3301         else {
3302             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
3303                     - getTreeFontSet()._small_max_descent, g );
3304         }
3305     }
3306
3307     final private void paintBranchLite( final Graphics2D g,
3308                                         final float x1,
3309                                         final float x2,
3310                                         final float y1,
3311                                         final float y2,
3312                                         final PhylogenyNode node ) {
3313         g.setColor( getTreeColorSet().getOvColor() );
3314         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3315             drawLine( x1, y1, x2, y2, g );
3316         }
3317         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3318             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3319             ( g ).draw( _quad_curve );
3320         }
3321         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3322             final float dx = x2 - x1;
3323             final float dy = y2 - y1;
3324             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3325                     + ( dy * 0.8f ), x2, y2 );
3326             ( g ).draw( _cubic_curve );
3327         }
3328         else {
3329             final float x2a = x2;
3330             final float x1a = x1;
3331             // draw the vertical line
3332             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
3333                 drawLine( x1, y1, x1, y2, g );
3334             }
3335             // draw the horizontal line
3336             drawLine( x1a, y2, x2a, y2, g );
3337         }
3338     }
3339
3340     /**
3341      * Paint a branch which consists of a vertical and a horizontal bar
3342      * @param is_ind_found_nodes 
3343      */
3344     final private void paintBranchRectangular( final Graphics2D g,
3345                                                final float x1,
3346                                                final float x2,
3347                                                final float y1,
3348                                                final float y2,
3349                                                final PhylogenyNode node,
3350                                                final boolean to_pdf,
3351                                                final boolean to_graphics_file ) {
3352         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
3353         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3354             drawLine( x1, y1, x2, y2, g );
3355         }
3356         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3357             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3358             g.draw( _quad_curve );
3359         }
3360         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3361             final float dx = x2 - x1;
3362             final float dy = y2 - y1;
3363             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3364                     + ( dy * 0.8f ), x2, y2 );
3365             g.draw( _cubic_curve );
3366         }
3367         else {
3368             final float x2a = x2;
3369             final float x1a = x1;
3370             float y2_r = 0;
3371             if ( node.isFirstChildNode() || node.isLastChildNode()
3372                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3373                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3374                 if ( !to_graphics_file
3375                         && !to_pdf
3376                         && ( ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) && ( y1 < ( getVisibleRect().getMinY() - 20 ) ) ) || ( ( y2 > ( getVisibleRect()
3377                                 .getMaxY() + 20 ) ) && ( y1 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) ) {
3378                     // Do nothing.
3379                 }
3380                 else {
3381                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3382                         float x2c = x1 + EURO_D;
3383                         if ( x2c > x2a ) {
3384                             x2c = x2a;
3385                         }
3386                         drawLine( x1, y1, x2c, y2, g );
3387                     }
3388                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3389                         if ( y2 > y1 ) {
3390                             y2_r = y2 - ROUNDED_D;
3391                             if ( y2_r < y1 ) {
3392                                 y2_r = y1;
3393                             }
3394                             drawLine( x1, y1, x1, y2_r, g );
3395                         }
3396                         else {
3397                             y2_r = y2 + ROUNDED_D;
3398                             if ( y2_r > y1 ) {
3399                                 y2_r = y1;
3400                             }
3401                             drawLine( x1, y1, x1, y2_r, g );
3402                         }
3403                     }
3404                     else {
3405                         drawLine( x1, y1, x1, y2, g );
3406                     }
3407                 }
3408             }
3409             // draw the horizontal line
3410             if ( !to_graphics_file && !to_pdf
3411                     && ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) || ( y2 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) {
3412                 return;
3413             }
3414             float x1_r = 0;
3415             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
3416                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3417                     x1_r = x1a + ROUNDED_D;
3418                     if ( x1_r < x2a ) {
3419                         drawLine( x1_r, y2, x2a, y2, g );
3420                     }
3421                 }
3422                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3423                     final float x1c = x1a + EURO_D;
3424                     if ( x1c < x2a ) {
3425                         drawLine( x1c, y2, x2a, y2, g );
3426                     }
3427                 }
3428                 else {
3429                     drawLine( x1a, y2, x2a, y2, g );
3430                 }
3431             }
3432             else {
3433                 final double w = PhylogenyMethods.getBranchWidthValue( node );
3434                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3435                     x1_r = x1a + ROUNDED_D;
3436                     if ( x1_r < x2a ) {
3437                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
3438                     }
3439                 }
3440                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3441                     final float x1c = x1a + EURO_D;
3442                     if ( x1c < x2a ) {
3443                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
3444                     }
3445                 }
3446                 else {
3447                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
3448                 }
3449             }
3450             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3451                 if ( x1_r > x2a ) {
3452                     x1_r = x2a;
3453                 }
3454                 if ( y2 > y2_r ) {
3455                     final double diff = y2 - y2_r;
3456                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
3457                 }
3458                 else {
3459                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
3460                 }
3461                 g.draw( _arc );
3462             }
3463         }
3464         if ( node.isExternal() ) {
3465             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
3466         }
3467     }
3468
3469     final private double paintCirculars( final PhylogenyNode n,
3470                                          final Phylogeny phy,
3471                                          final float center_x,
3472                                          final float center_y,
3473                                          final double radius,
3474                                          final boolean radial_labels,
3475                                          final Graphics2D g,
3476                                          final boolean to_pdf,
3477                                          final boolean to_graphics_file ) {
3478         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
3479             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
3480                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
3481             }
3482             return _urt_nodeid_angle_map.get( n.getId() );
3483         }
3484         else {
3485             final List<PhylogenyNode> descs = n.getDescendants();
3486             double sum = 0;
3487             for( final PhylogenyNode desc : descs ) {
3488                 sum += paintCirculars( desc,
3489                                        phy,
3490                                        center_x,
3491                                        center_y,
3492                                        radius,
3493                                        radial_labels,
3494                                        g,
3495                                        to_pdf,
3496                                        to_graphics_file );
3497             }
3498             double r = 0;
3499             if ( !n.isRoot() ) {
3500                 r = 1 - ( ( ( double ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3501             }
3502             final double theta = sum / descs.size();
3503             n.setXcoord( ( float ) ( center_x + ( r * radius * Math.cos( theta ) ) ) );
3504             n.setYcoord( ( float ) ( center_y + ( r * radius * Math.sin( theta ) ) ) );
3505             _urt_nodeid_angle_map.put( n.getId(), theta );
3506             for( final PhylogenyNode desc : descs ) {
3507                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
3508             }
3509             return theta;
3510         }
3511     }
3512
3513     final private void paintCircularsLite( final PhylogenyNode n,
3514                                            final Phylogeny phy,
3515                                            final int center_x,
3516                                            final int center_y,
3517                                            final int radius,
3518                                            final Graphics2D g ) {
3519         if ( n.isExternal() ) {
3520             return;
3521         }
3522         else {
3523             final List<PhylogenyNode> descs = n.getDescendants();
3524             for( final PhylogenyNode desc : descs ) {
3525                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
3526             }
3527             float r = 0;
3528             if ( !n.isRoot() ) {
3529                 r = 1 - ( ( ( float ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3530             }
3531             final double theta = _urt_nodeid_angle_map.get( n.getId() );
3532             n.setXSecondary( ( float ) ( center_x + ( radius * r * Math.cos( theta ) ) ) );
3533             n.setYSecondary( ( float ) ( center_y + ( radius * r * Math.sin( theta ) ) ) );
3534             for( final PhylogenyNode desc : descs ) {
3535                 paintBranchCircularLite( n, desc, g );
3536             }
3537         }
3538     }
3539
3540     final private void paintCollapsedNode( final Graphics2D g,
3541                                            final PhylogenyNode node,
3542                                            final boolean to_graphics_file,
3543                                            final boolean to_pdf,
3544                                            final boolean is_in_found_nodes ) {
3545         Color c = null;
3546         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3547             c = Color.BLACK;
3548         }
3549         else if ( is_in_found_nodes ) {
3550             c = getTreeColorSet().getFoundColor();
3551         }
3552         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3553             c = getTaxonomyBasedColor( node );
3554         }
3555         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3556                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3557             c = PhylogenyMethods.getBranchColorValue( node );
3558         }
3559         else {
3560             c = getTreeColorSet().getCollapseFillColor();
3561         }
3562         double d = node.getAllExternalDescendants().size();
3563         if ( d > 1000 ) {
3564             d = ( 3 * _y_distance ) / 3;
3565         }
3566         else {
3567             d = ( Math.log10( d ) * _y_distance ) / 2.5;
3568         }
3569         final int box_size = getOptions().getDefaultNodeShapeSize();
3570         if ( d < box_size ) {
3571             d = box_size;
3572         }
3573         _polygon.reset();
3574         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() - box_size ),
3575                            ForesterUtil.roundToInt( node.getYcoord() ) );
3576         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
3577                            ForesterUtil.roundToInt( node.getYcoord() - d ) );
3578         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
3579                            ForesterUtil.roundToInt( node.getYcoord() + d ) );
3580         if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3581             g.setColor( c );
3582             g.fillPolygon( _polygon );
3583         }
3584         else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
3585             g.setColor( getBackground() );
3586             g.fillPolygon( _polygon );
3587             g.setColor( c );
3588             g.drawPolygon( _polygon );
3589         }
3590         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3591             g.setPaint( new GradientPaint( node.getXcoord() - box_size, node.getYcoord(), getBackground(), ( node
3592                     .getXcoord() + box_size ), ( float ) ( node.getYcoord() - d ), c, false ) );
3593             g.fill( _polygon );
3594             g.setPaint( c );
3595             g.draw( _polygon );
3596         }
3597         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3598     }
3599
3600     final private void paintConfidenceValues( final Graphics2D g,
3601                                               final PhylogenyNode node,
3602                                               final boolean to_pdf,
3603                                               final boolean to_graphics_file ) {
3604         final List<Confidence> confidences = node.getBranchData().getConfidences();
3605         //        if ( confidences.size() == 1 ) {
3606         //            final double value = node.getBranchData().getConfidence( 0 ).getValue();
3607         //            if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
3608         //                return;
3609         //            }
3610         //            conf_str = FORMATTER_CONFIDENCE.format( value );
3611         //        }
3612         //        else if ( confidences.size() > 1 ) {
3613         boolean one_ok = false;
3614         boolean not_first = false;
3615         Collections.sort( confidences );
3616         final StringBuilder sb = new StringBuilder();
3617         String conf_str = "";
3618         for( final Confidence confidence : confidences ) {
3619             final double value = confidence.getValue();
3620             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
3621                 if ( value >= getOptions().getMinConfidenceValue() ) {
3622                     one_ok = true;
3623                 }
3624                 if ( not_first ) {
3625                     sb.append( "/" );
3626                 }
3627                 else {
3628                     not_first = true;
3629                 }
3630                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
3631                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
3632                 if ( getOptions().isShowConfidenceStddev() ) {
3633                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
3634                         sb.append( "(" );
3635                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
3636                                                                                     getOptions()
3637                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
3638                         sb.append( ")" );
3639                     }
3640                 }
3641             }
3642             //}
3643             if ( one_ok ) {
3644                 conf_str = sb.toString();
3645             }
3646         }
3647         if ( conf_str.length() > 0 ) {
3648             final double parent_x = node.getParent().getXcoord();
3649             double x = node.getXcoord();
3650             g.setFont( getTreeFontSet().getSmallFont() );
3651             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3652                 x += EURO_D;
3653             }
3654             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3655                 x += ROUNDED_D;
3656             }
3657             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3658                 g.setColor( Color.BLACK );
3659             }
3660             else {
3661                 g.setColor( getTreeColorSet().getConfidenceColor() );
3662             }
3663             TreePanel
3664                     .drawString( conf_str,
3665                                  parent_x
3666                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
3667                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
3668                                  g );
3669         }
3670     }
3671
3672     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
3673         final int box_size = getOptions().getDefaultNodeShapeSize();
3674         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
3675         g.setColor( getTreeColorSet().getFoundColor() );
3676         g.fillRect( x - half_box_size, y - half_box_size, box_size, box_size );
3677     }
3678
3679     final private void paintGainedAndLostCharacters( final Graphics2D g,
3680                                                      final PhylogenyNode node,
3681                                                      final String gained,
3682                                                      final String lost ) {
3683         if ( node.getParent() != null ) {
3684             final double parent_x = node.getParent().getXcoord();
3685             final double x = node.getXcoord();
3686             g.setFont( getTreeFontSet().getLargeFont() );
3687             g.setColor( getTreeColorSet().getGainedCharactersColor() );
3688             if ( Constants.SPECIAL_CUSTOM ) {
3689                 g.setColor( Color.BLUE );
3690             }
3691             TreePanel
3692                     .drawString( gained,
3693                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
3694                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
3695                                  g );
3696             g.setColor( getTreeColorSet().getLostCharactersColor() );
3697             TreePanel.drawString( lost,
3698                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
3699                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
3700                                   g );
3701         }
3702     }
3703
3704     /**
3705      * Draw a box at the indicated node.
3706      * 
3707      * @param x
3708      * @param y
3709      * @param node
3710      * @param g
3711      */
3712     final private void paintNodeBox( final double x,
3713                                      final double y,
3714                                      final PhylogenyNode node,
3715                                      final Graphics2D g,
3716                                      final boolean to_pdf,
3717                                      final boolean to_graphics_file,
3718                                      final boolean is_in_found_nodes ) {
3719         if ( node.isCollapse() ) {
3720             return;
3721         }
3722         // if this node should be highlighted, do so
3723         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
3724             g.setColor( getTreeColorSet().getFoundColor() );
3725             drawOval( x - 8, y - 8, 16, 16, g );
3726             drawOval( x - 9, y - 8, 17, 17, g );
3727             drawOval( x - 9, y - 9, 18, 18, g );
3728         }
3729         if ( is_in_found_nodes ) {
3730             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
3731         }
3732         else {
3733             Color outline_color = null;
3734             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3735                 outline_color = Color.BLACK;
3736             }
3737             else if ( getControlPanel().isEvents() && AptxUtil.isHasAssignedEvent( node ) ) {
3738                 final Event event = node.getNodeData().getEvent();
3739                 if ( event.isDuplication() ) {
3740                     outline_color = getTreeColorSet().getDuplicationBoxColor();
3741                 }
3742                 else if ( event.isSpeciation() ) {
3743                     outline_color = getTreeColorSet().getSpecBoxColor();
3744                 }
3745                 else if ( event.isSpeciationOrDuplication() ) {
3746                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
3747                 }
3748             }
3749             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
3750                 outline_color = getTaxonomyBasedColor( node );
3751             }
3752             else {
3753                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
3754                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
3755                     outline_color = getTreeColorSet().getBranchColorForPdf();
3756                 }
3757             }
3758             final int box_size = getOptions().getDefaultNodeShapeSize();
3759             final int half_box_size = box_size / 2;
3760             if ( getOptions().isShowDefaultNodeShapes() || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
3761                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
3762                     if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3763                         drawOvalGradient( x - half_box_size,
3764                                           y - half_box_size,
3765                                           box_size,
3766                                           box_size,
3767                                           g,
3768                                           to_pdf ? Color.WHITE : outline_color,
3769                                           to_pdf ? outline_color : getBackground(),
3770                                           outline_color );
3771                     }
3772                     else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
3773                         Color background = getBackground();
3774                         if ( to_pdf ) {
3775                             background = Color.WHITE;
3776                         }
3777                         drawOvalGradient( x - half_box_size,
3778                                           y - half_box_size,
3779                                           box_size,
3780                                           box_size,
3781                                           g,
3782                                           background,
3783                                           background,
3784                                           outline_color );
3785                     }
3786                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3787                         g.setColor( outline_color );
3788                         drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
3789                     }
3790                 }
3791                 else if ( getOptions().getDefaultNodeShape() == NodeVisualization.NodeShape.RECTANGLE ) {
3792                     if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.GRADIENT ) {
3793                         drawRectGradient( x - half_box_size,
3794                                           y - half_box_size,
3795                                           box_size,
3796                                           box_size,
3797                                           g,
3798                                           to_pdf ? Color.WHITE : outline_color,
3799                                           to_pdf ? outline_color : getBackground(),
3800                                           outline_color );
3801                     }
3802                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
3803                         Color background = getBackground();
3804                         if ( to_pdf ) {
3805                             background = Color.WHITE;
3806                         }
3807                         drawRectGradient( x - half_box_size,
3808                                           y - half_box_size,
3809                                           box_size,
3810                                           box_size,
3811                                           g,
3812                                           background,
3813                                           background,
3814                                           outline_color );
3815                     }
3816                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3817                         g.setColor( outline_color );
3818                         drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
3819                     }
3820                 }
3821             }
3822         }
3823     }
3824
3825     final private void paintNodeData( final Graphics2D g,
3826                                       final PhylogenyNode node,
3827                                       final boolean to_graphics_file,
3828                                       final boolean to_pdf,
3829                                       final boolean is_in_found_nodes ) {
3830         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
3831             return;
3832         }
3833         if ( getOptions().isShowBranchLengthValues()
3834                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3835                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3836                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
3837             paintBranchLength( g, node, to_pdf, to_graphics_file );
3838         }
3839         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
3840             return;
3841         }
3842         int x = 0;
3843         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
3844         if ( getControlPanel().isShowTaxonomyImages()
3845                 && ( getImageMap() != null )
3846                 && !getImageMap().isEmpty()
3847                 && node.getNodeData().isHasTaxonomy()
3848                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
3849                         .getUris().isEmpty() ) ) {
3850             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
3851         }
3852         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3853                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
3854             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
3855         }
3856         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3857             g.setColor( Color.BLACK );
3858         }
3859         else if ( is_in_found_nodes ) {
3860             g.setColor( getTreeColorSet().getFoundColor() );
3861         }
3862         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3863             g.setColor( getTaxonomyBasedColor( node ) );
3864         }
3865         else if ( getControlPanel().isColorAccordingToAnnotation()
3866                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
3867                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
3868             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
3869         }
3870         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3871                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3872             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3873         }
3874         else if ( to_pdf ) {
3875             g.setColor( Color.BLACK );
3876         }
3877         else {
3878             g.setColor( getTreeColorSet().getSequenceColor() );
3879         }
3880         _sb.setLength( 0 );
3881         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3882             _sb.append( " [" );
3883             _sb.append( node.getAllExternalDescendants().size() );
3884             _sb.append( "]" );
3885         }
3886         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3887             if ( _sb.length() > 0 ) {
3888                 _sb.append( " " );
3889             }
3890             _sb.append( node.getName() );
3891         }
3892         if ( node.getNodeData().isHasSequence() ) {
3893             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3894                 if ( _sb.length() > 0 ) {
3895                     _sb.append( " " );
3896                 }
3897                 _sb.append( node.getNodeData().getSequence().getSymbol() );
3898             }
3899             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3900                 if ( _sb.length() > 0 ) {
3901                     _sb.append( " " );
3902                 }
3903                 _sb.append( node.getNodeData().getSequence().getName() );
3904             }
3905             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3906                 if ( _sb.length() > 0 ) {
3907                     _sb.append( " " );
3908                 }
3909                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3910                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3911                     _sb.append( ":" );
3912                 }
3913                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3914             }
3915         }
3916         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
3917             if ( _sb.length() > 0 ) {
3918                 _sb.append( " " );
3919             }
3920             _sb.append( propertiesToString( node ) );
3921         }
3922         g.setFont( getTreeFontSet().getLargeFont() );
3923         if ( is_in_found_nodes ) {
3924             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3925         }
3926         double down_shift_factor = 3.0;
3927         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
3928             down_shift_factor = 1;
3929         }
3930         final double pos_x = node.getXcoord() + x + 2 + half_box_size;
3931         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
3932         final String sb_str = _sb.toString();
3933         // GUILHEM_BEG ______________
3934         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
3935                 && ( _query_sequence != null ) ) {
3936             int nodeTextBoundsWidth = 0;
3937             if ( sb_str.length() > 0 ) {
3938                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
3939                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
3940             }
3941             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
3942                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
3943                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
3944                     g.setColor( getTreeColorSet().getBackgroundColor() );
3945                 }
3946             }
3947             else {
3948                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
3949                 for( final SequenceRelation seqRelation : seqRelations ) {
3950                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
3951                             .getRef1().isEqual( _query_sequence ) )
3952                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
3953                                     .getSelectedItem() );
3954                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
3955                         final double linePosX = node.getXcoord() + 2 + half_box_size;
3956                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
3957                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
3958                                 + ")";
3959                         if ( sConfidence != null ) {
3960                             double confidenceX = pos_x;
3961                             if ( sb_str.length() > 0 ) {
3962                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
3963                                         + CONFIDENCE_LEFT_MARGIN;
3964                             }
3965                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
3966                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
3967                                         .getBounds().getWidth();
3968                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
3969                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
3970                             }
3971                         }
3972                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
3973                         {
3974                             if ( nodeTextBoundsWidth == 0 ) {
3975                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
3976                             }
3977                             else {
3978                                 nodeTextBoundsWidth += 2;
3979                             }
3980                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
3981                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
3982                             break;
3983                         }
3984                     }
3985                 }
3986             }
3987         }
3988         if ( sb_str.length() > 0 ) {
3989             TreePanel.drawString( sb_str, pos_x, pos_y, g );
3990         }
3991         // GUILHEM_END _____________
3992         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
3993         // TODO FIXME need to check this one!
3994         //if ( _sb.length() > 0 ) {
3995         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
3996         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3997         //}
3998         // COMMENTED_OUT_BY_GUILHEM_END ________________
3999         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
4000                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4001                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4002             if ( _sb.length() > 0 ) {
4003                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
4004             }
4005             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
4006             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4007                 g.setColor( Color.BLACK );
4008             }
4009             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
4010                 g.setColor( calculateColorForAnnotation( ann ) );
4011             }
4012             final String ann_str = createAnnotationString( ann );
4013             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
4014                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4015             _sb.setLength( 0 );
4016             _sb.append( ann_str );
4017         }
4018         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4019                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
4020                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
4021             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
4022                     && node.getNodeData().isHasBinaryCharacters() ) {
4023                 if ( _sb.length() > 0 ) {
4024                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
4025                 }
4026                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4027                     g.setColor( Color.BLACK );
4028                 }
4029                 else {
4030                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
4031                 }
4032                 if ( getControlPanel().isShowBinaryCharacters() ) {
4033                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
4034                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
4035                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4036                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
4037                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
4038                             .getLostCharactersAsStringBuffer().toString() );
4039                 }
4040                 else {
4041                     TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
4042                                           node.getXcoord() + x + 4 + half_box_size,
4043                                           node.getYcoord()
4044                                                   + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
4045                                           g );
4046                     paintGainedAndLostCharacters( g, node, "+"
4047                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
4048                             + node.getNodeData().getBinaryCharacters().getLostCount() );
4049                 }
4050             }
4051         }
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                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
4996             final StringBuilder sb = new StringBuilder();
4997             for( final String d : data ) {
4998                 if ( !ForesterUtil.isEmpty( d ) ) {
4999                     if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5000                         System.out.println( d );
5001                     }
5002                     sb.append( d );
5003                     sb.append( ForesterUtil.LINE_SEPARATOR );
5004                 }
5005             }
5006             if ( sb.length() < 1 ) {
5007                 if ( getMainPanel().getMainFrame() == null ) {
5008                     // Must be "E" applet version.
5009                     ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5010                             .setCurrentExternalNodesDataBuffer( "" );
5011                 }
5012                 else {
5013                     getMainPanel().getMainFrame().setCurrentExternalNodesDataBuffer( "" );
5014                 }
5015             }
5016             else {
5017                 final String s = sb.toString().trim();
5018                 if ( getMainPanel().getMainFrame() == null ) {
5019                     // Must be "E" applet version.
5020                     ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5021                             .setCurrentExternalNodesDataBuffer( s );
5022                 }
5023                 else {
5024                     getMainPanel().getMainFrame().setCurrentExternalNodesDataBuffer( s );
5025                 }
5026             }
5027         }
5028         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5029             final StringBuilder sb = new StringBuilder();
5030             for( final String d : data ) {
5031                 if ( !ForesterUtil.isEmpty( d ) ) {
5032                     sb.append( d );
5033                     sb.append( ForesterUtil.LINE_SEPARATOR );
5034                 }
5035             }
5036             if ( sb.length() < 1 ) {
5037                 AptxUtil.showInformationMessage( this,
5038                                                  "No Appropriate Data (" + obtainTitleForExtDescNodeData() + ")",
5039                                                  "Descendants of selected node do not contain selected data" );
5040                 if ( getMainPanel().getMainFrame() == null ) {
5041                     // Must be "E" applet version.
5042                     ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5043                             .setCurrentExternalNodesDataBuffer( "" );
5044                 }
5045                 else {
5046                     getMainPanel().getMainFrame().setCurrentExternalNodesDataBuffer( "" );
5047                 }
5048             }
5049             else {
5050                 final String title = "External Descendants "
5051                         + ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5052                                 : obtainTitleForExtDescNodeData() ) + " (" + data.size() + "/"
5053                         + node.getNumberOfExternalNodes() + ") For Node " + node;
5054                 final String s = sb.toString().trim();
5055                 if ( getMainPanel().getMainFrame() == null ) {
5056                     // Must be "E" applet version.
5057                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5058                     ae.showTextFrame( s, title );
5059                     ae.setCurrentExternalNodesDataBuffer( s );
5060                 }
5061                 else {
5062                     getMainPanel().getMainFrame().showTextFrame( s, title );
5063                     getMainPanel().getMainFrame().setCurrentExternalNodesDataBuffer( s );
5064                 }
5065             }
5066         }
5067     }
5068
5069     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5070         try {
5071             if ( ( node.getName().length() > 0 )
5072                     || ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
5073                     || ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) )
5074                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
5075                     || node.getBranchData().isHasConfidences() ) {
5076                 _popup_buffer.setLength( 0 );
5077                 short lines = 0;
5078                 if ( node.getName().length() > 0 ) {
5079                     lines++;
5080                     _popup_buffer.append( node.getName() );
5081                 }
5082                 if ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5083                     lines++;
5084                     boolean enc_data = false;
5085                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5086                     if ( _popup_buffer.length() > 0 ) {
5087                         _popup_buffer.append( "\n" );
5088                     }
5089                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5090                         _popup_buffer.append( "[" );
5091                         _popup_buffer.append( tax.getTaxonomyCode() );
5092                         _popup_buffer.append( "]" );
5093                         enc_data = true;
5094                     }
5095                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5096                         if ( enc_data ) {
5097                             _popup_buffer.append( " " );
5098                         }
5099                         _popup_buffer.append( tax.getScientificName() );
5100                         enc_data = true;
5101                     }
5102                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5103                         if ( enc_data ) {
5104                             _popup_buffer.append( " (" );
5105                         }
5106                         else {
5107                             _popup_buffer.append( "(" );
5108                         }
5109                         _popup_buffer.append( tax.getCommonName() );
5110                         _popup_buffer.append( ")" );
5111                         enc_data = true;
5112                     }
5113                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5114                         if ( enc_data ) {
5115                             _popup_buffer.append( " (" );
5116                         }
5117                         else {
5118                             _popup_buffer.append( "(" );
5119                         }
5120                         _popup_buffer.append( tax.getAuthority() );
5121                         _popup_buffer.append( ")" );
5122                         enc_data = true;
5123                     }
5124                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5125                         if ( enc_data ) {
5126                             _popup_buffer.append( " [" );
5127                         }
5128                         else {
5129                             _popup_buffer.append( "[" );
5130                         }
5131                         _popup_buffer.append( tax.getRank() );
5132                         _popup_buffer.append( "]" );
5133                         enc_data = true;
5134                     }
5135                     if ( tax.getSynonyms().size() > 0 ) {
5136                         if ( enc_data ) {
5137                             _popup_buffer.append( " " );
5138                         }
5139                         _popup_buffer.append( "[" );
5140                         int counter = 1;
5141                         for( final String syn : tax.getSynonyms() ) {
5142                             if ( !ForesterUtil.isEmpty( syn ) ) {
5143                                 enc_data = true;
5144                                 _popup_buffer.append( syn );
5145                                 if ( counter < tax.getSynonyms().size() ) {
5146                                     _popup_buffer.append( ", " );
5147                                 }
5148                             }
5149                             counter++;
5150                         }
5151                         _popup_buffer.append( "]" );
5152                     }
5153                     if ( !enc_data ) {
5154                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5155                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5156                                 _popup_buffer.append( "[" );
5157                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5158                                 _popup_buffer.append( "] " );
5159                             }
5160                             _popup_buffer.append( tax.getIdentifier().getValue() );
5161                         }
5162                     }
5163                 }
5164                 if ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5165                     lines++;
5166                     boolean enc_data = false;
5167                     if ( _popup_buffer.length() > 0 ) {
5168                         _popup_buffer.append( "\n" );
5169                     }
5170                     final Sequence seq = node.getNodeData().getSequence();
5171                     if ( seq.getAccession() != null ) {
5172                         _popup_buffer.append( "[" );
5173                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5174                             _popup_buffer.append( seq.getAccession().getSource() );
5175                             _popup_buffer.append( ":" );
5176                         }
5177                         _popup_buffer.append( seq.getAccession().getValue() );
5178                         _popup_buffer.append( "]" );
5179                         enc_data = true;
5180                     }
5181                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5182                         if ( enc_data ) {
5183                             _popup_buffer.append( " [" );
5184                         }
5185                         else {
5186                             _popup_buffer.append( "[" );
5187                         }
5188                         _popup_buffer.append( seq.getSymbol() );
5189                         _popup_buffer.append( "]" );
5190                         enc_data = true;
5191                     }
5192                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5193                         if ( enc_data ) {
5194                             _popup_buffer.append( " " );
5195                         }
5196                         _popup_buffer.append( seq.getName() );
5197                     }
5198                 }
5199                 if ( node.getNodeData().isHasDate() ) {
5200                     lines++;
5201                     if ( _popup_buffer.length() > 0 ) {
5202                         _popup_buffer.append( "\n" );
5203                     }
5204                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5205                 }
5206                 if ( node.getNodeData().isHasDistribution() ) {
5207                     lines++;
5208                     if ( _popup_buffer.length() > 0 ) {
5209                         _popup_buffer.append( "\n" );
5210                     }
5211                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5212                 }
5213                 if ( node.getBranchData().isHasConfidences() ) {
5214                     final List<Confidence> confs = node.getBranchData().getConfidences();
5215                     for( final Confidence confidence : confs ) {
5216                         lines++;
5217                         if ( _popup_buffer.length() > 0 ) {
5218                             _popup_buffer.append( "\n" );
5219                         }
5220                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5221                             _popup_buffer.append( "[" );
5222                             _popup_buffer.append( confidence.getType() );
5223                             _popup_buffer.append( "] " );
5224                         }
5225                         _popup_buffer
5226                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5227                                                                                           getOptions()
5228                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5229                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5230                             _popup_buffer.append( " (sd=" );
5231                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5232                                     .getStandardDeviation(), getOptions()
5233                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5234                             _popup_buffer.append( ")" );
5235                         }
5236                     }
5237                 }
5238                 if ( node.getNodeData().isHasProperties() ) {
5239                     final PropertiesMap properties = node.getNodeData().getProperties();
5240                     for( final String ref : properties.getPropertyRefs() ) {
5241                         _popup_buffer.append( "\n" );
5242                         final Property p = properties.getProperty( ref );
5243                         _popup_buffer.append( getPartAfterColon( p.getRef() ) );
5244                         _popup_buffer.append( "=" );
5245                         _popup_buffer.append( p.getValue() );
5246                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5247                             _popup_buffer.append( getPartAfterColon( p.getUnit() ) );
5248                         }
5249                     }
5250                 }
5251                 if ( _popup_buffer.length() > 0 ) {
5252                     if ( !getConfiguration().isUseNativeUI() ) {
5253                         _rollover_popup
5254                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5255                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5256                         if ( isInFoundNodes( node ) ) {
5257                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
5258                         }
5259                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5260                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
5261                         }
5262                         else {
5263                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5264                         }
5265                     }
5266                     else {
5267                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5268                     }
5269                     _rollover_popup.setText( _popup_buffer.toString() );
5270                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5271                                                                                   _rollover_popup,
5272                                                                                   e.getLocationOnScreen().x + 10,
5273                                                                                   e.getLocationOnScreen().y
5274                                                                                           - ( lines * 20 ) );
5275                     _node_desc_popup.show();
5276                 }
5277             }
5278         }
5279         catch ( final Exception ex ) {
5280             // Do nothing.
5281         }
5282     }
5283
5284     final private void showNodeEditFrame( final PhylogenyNode n ) {
5285         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5286             // pop up edit box for single node
5287             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5288             _node_frame_index++;
5289         }
5290         else {
5291             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5292         }
5293     }
5294
5295     final private void showNodeFrame( final PhylogenyNode n ) {
5296         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5297             // pop up edit box for single node
5298             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5299             _node_frame_index++;
5300         }
5301         else {
5302             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5303         }
5304     }
5305
5306     final private void switchDisplaygetPhylogenyGraphicsType() {
5307         switch ( getPhylogenyGraphicsType() ) {
5308             case RECTANGULAR:
5309                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5310                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5311                 break;
5312             case EURO_STYLE:
5313                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5314                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5315                 break;
5316             case ROUNDED:
5317                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5318                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5319                 break;
5320             case CURVED:
5321                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5322                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5323                 break;
5324             case TRIANGULAR:
5325                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5326                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5327                 break;
5328             case CONVEX:
5329                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5330                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5331                 break;
5332             case UNROOTED:
5333                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5334                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5335                 break;
5336             case CIRCULAR:
5337                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5338                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5339                 break;
5340             default:
5341                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5342         }
5343         if ( getControlPanel().getDynamicallyHideData() != null ) {
5344             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5345                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5346             }
5347             else {
5348                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5349             }
5350         }
5351         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5352             getControlPanel().setDrawPhylogramEnabled( true );
5353         }
5354         else {
5355             getControlPanel().setDrawPhylogramEnabled( false );
5356         }
5357         if ( getMainPanel().getMainFrame() == null ) {
5358             // Must be "E" applet version.
5359             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5360                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5361         }
5362         else {
5363             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5364         }
5365     }
5366
5367     final private static void drawString( final int i, final double x, final double y, final Graphics2D g ) {
5368         g.drawString( String.valueOf( i ), ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5369     }
5370
5371     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5372         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5373     }
5374
5375     final private static String getPartAfterColon( final String s ) {
5376         final int i = s.indexOf( ':' );
5377         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
5378             return s;
5379         }
5380         return s.substring( i + 1, s.length() );
5381     }
5382
5383     final private static boolean isSequenceEmpty( final Sequence seq ) {
5384         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
5385                 && ForesterUtil.isEmpty( seq.getSymbol() );
5386     }
5387
5388     final private static boolean isTaxonomyEmpty( final Taxonomy tax ) {
5389         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
5390                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
5391                 .getSynonyms().isEmpty() );
5392     }
5393
5394     final private static boolean plusPressed( final int key_code ) {
5395         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5396                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5397     }
5398
5399     final private static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
5400         final Phylogeny new_phy = new Phylogeny();
5401         new_phy.setRooted( true );
5402         new_phy.setName( source_phy.getName() );
5403         new_phy.setDescription( source_phy.getDescription() );
5404         new_phy.setType( source_phy.getType() );
5405         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
5406         new_phy.setConfidence( source_phy.getConfidence() );
5407         new_phy.setIdentifier( source_phy.getIdentifier() );
5408         new_phy.setRoot( new_root.copyNodeDataShallow() );
5409         int i = 0;
5410         for( final PhylogenyNode n : new_root.getDescendants() ) {
5411             new_phy.getRoot().setChildNode( i++, n );
5412         }
5413         return new_phy;
5414     }
5415
5416     final private class SubtreeColorizationActionListener implements ActionListener {
5417
5418         JColorChooser _chooser;
5419         PhylogenyNode _node;
5420
5421         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5422             _chooser = chooser;
5423             _node = node;
5424         }
5425
5426         @Override
5427         public void actionPerformed( final ActionEvent e ) {
5428             final Color c = _chooser.getColor();
5429             if ( c != null ) {
5430                 colorizeSubtree( c, _node );
5431             }
5432         }
5433     }
5434 }