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