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