inprogress
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanel.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.BasicStroke;
29 import java.awt.Color;
30 import java.awt.Cursor;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.GradientPaint;
34 import java.awt.Graphics;
35 import java.awt.Graphics2D;
36 import java.awt.Point;
37 import java.awt.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( TreePanelUtil.createAnnotationString( node
621                             .getNodeData().getSequence().getAnnotations(), getOptions().isShowAnnotationRefSource() )
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 = TreePanelUtil.calculateColorFromString( tax.getTaxonomyCode(), true );
712                 getControlPanel().getSpeciesColors().put( tax.getTaxonomyCode(), c );
713             }
714             else {
715                 c = TreePanelUtil.calculateColorFromString( tax.getScientificName(), true );
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             TreePanelUtil.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         TreePanelUtil.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 = TreePanelUtil.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         TreePanelUtil.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 = TreePanelUtil.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         TreePanelUtil.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.getRefValue() ) ? a.getRefValue() : 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 = TreePanelUtil.calculateColorFromString( ann_str, false );
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,
2374                                         final PhylogenyNode node,
2375                                         final List<PhylogenyNode> additional_nodes ) {
2376         _control_panel.setColorBranches( true );
2377         if ( _control_panel.getColorBranchesCb() != null ) {
2378             _control_panel.getColorBranchesCb().setSelected( true );
2379         }
2380         if ( node != null ) {
2381             for( final PreorderTreeIterator it = new PreorderTreeIterator( node ); it.hasNext(); ) {
2382                 it.next().getBranchData().setBranchColor( new BranchColor( c ) );
2383             }
2384         }
2385         if ( additional_nodes != null ) {
2386             for( final PhylogenyNode n : additional_nodes ) {
2387                 n.getBranchData().setBranchColor( new BranchColor( c ) );
2388             }
2389         }
2390         repaint();
2391     }
2392
2393     final private void colorSubtree( final PhylogenyNode node ) {
2394         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2395             JOptionPane.showMessageDialog( this,
2396                                            "Cannot colorize subtree in unrooted display type",
2397                                            "Attempt to colorize subtree in unrooted display",
2398                                            JOptionPane.WARNING_MESSAGE );
2399             return;
2400         }
2401         _color_chooser.setPreviewPanel( new JPanel() );
2402         SubtreeColorizationActionListener al;
2403         if ( ( getFoundNodes() != null ) && !getFoundNodes().isEmpty() ) {
2404             final List<PhylogenyNode> additional_nodes = getFoundNodesAsListOfPhylogenyNodes();
2405             al = new SubtreeColorizationActionListener( _color_chooser, node, additional_nodes );
2406         }
2407         else {
2408             al = new SubtreeColorizationActionListener( _color_chooser, node );
2409         }
2410         final JDialog dialog = JColorChooser
2411                 .createDialog( this, "Subtree colorization", true, _color_chooser, al, null );
2412         dialog.setVisible( true );
2413     }
2414
2415     private List<PhylogenyNode> getFoundNodesAsListOfPhylogenyNodes() {
2416         final List<PhylogenyNode> additional_nodes = new ArrayList<PhylogenyNode>();
2417         for( final Long id : getFoundNodes() ) {
2418             additional_nodes.add( _phylogeny.getNode( id ) );
2419         }
2420         return additional_nodes;
2421     }
2422
2423     final private void copySubtree( final PhylogenyNode node ) {
2424         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2425             errorMessageNoCutCopyPasteInUnrootedDisplay();
2426             return;
2427         }
2428         setNodeInPreorderToNull();
2429         setCutOrCopiedTree( _phylogeny.copy( node ) );
2430         final List<PhylogenyNode> nodes = PhylogenyMethods.getAllDescendants( node );
2431         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
2432         for( final PhylogenyNode n : nodes ) {
2433             node_ids.add( n.getId() );
2434         }
2435         node_ids.add( node.getId() );
2436         setCopiedAndPastedNodes( node_ids );
2437         repaint();
2438     }
2439
2440     final private String createASimpleTextRepresentationOfANode( final PhylogenyNode node ) {
2441         final String tax = PhylogenyMethods.getSpecies( node );
2442         String label = node.getName();
2443         if ( !ForesterUtil.isEmpty( label ) && !ForesterUtil.isEmpty( tax ) ) {
2444             label = label + " " + tax;
2445         }
2446         else if ( !ForesterUtil.isEmpty( tax ) ) {
2447             label = tax;
2448         }
2449         else {
2450             label = "";
2451         }
2452         if ( !ForesterUtil.isEmpty( label ) ) {
2453             label = " [" + label + "]";
2454         }
2455         return label;
2456     }
2457
2458     final private void cutSubtree( final PhylogenyNode node ) {
2459         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2460             errorMessageNoCutCopyPasteInUnrootedDisplay();
2461             return;
2462         }
2463         if ( node.isRoot() ) {
2464             JOptionPane.showMessageDialog( this,
2465                                            "Cannot cut entire tree as subtree",
2466                                            "Attempt to cut entire tree",
2467                                            JOptionPane.ERROR_MESSAGE );
2468             return;
2469         }
2470         final String label = createASimpleTextRepresentationOfANode( node );
2471         final int r = JOptionPane.showConfirmDialog( null,
2472                                                      "Cut subtree" + label + "?",
2473                                                      "Confirm Cutting of Subtree",
2474                                                      JOptionPane.YES_NO_OPTION );
2475         if ( r != JOptionPane.OK_OPTION ) {
2476             return;
2477         }
2478         setNodeInPreorderToNull();
2479         setCopiedAndPastedNodes( null );
2480         setCutOrCopiedTree( _phylogeny.copy( node ) );
2481         _phylogeny.deleteSubtree( node, true );
2482         _phylogeny.clearHashIdToNodeMap();
2483         _phylogeny.recalculateNumberOfExternalDescendants( true );
2484         resetNodeIdToDistToLeafMap();
2485         setEdited( true );
2486         repaint();
2487     }
2488
2489     final private void cycleColors() {
2490         getMainPanel().getTreeColorSet().cycleColorScheme();
2491         for( final TreePanel tree_panel : getMainPanel().getTreePanels() ) {
2492             tree_panel.setBackground( getMainPanel().getTreeColorSet().getBackgroundColor() );
2493         }
2494     }
2495
2496     final private void decreaseOvSize() {
2497         if ( ( getOvMaxWidth() > 20 ) && ( getOvMaxHeight() > 20 ) ) {
2498             setOvMaxWidth( getOvMaxWidth() - 5 );
2499             setOvMaxHeight( getOvMaxHeight() - 5 );
2500             updateOvSettings();
2501             getControlPanel().displayedPhylogenyMightHaveChanged( false );
2502         }
2503     }
2504
2505     final private void deleteNodeOrSubtree( final PhylogenyNode node ) {
2506         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2507             errorMessageNoCutCopyPasteInUnrootedDisplay();
2508             return;
2509         }
2510         if ( node.isRoot() && ( node.getNumberOfDescendants() != 1 ) ) {
2511             JOptionPane.showMessageDialog( this,
2512                                            "Cannot delete entire tree",
2513                                            "Attempt to delete entire tree",
2514                                            JOptionPane.ERROR_MESSAGE );
2515             return;
2516         }
2517         final String label = createASimpleTextRepresentationOfANode( node );
2518         final Object[] options = { "Node only", "Entire subtree", "Cancel" };
2519         final int r = JOptionPane.showOptionDialog( this,
2520                                                     "Delete" + label + "?",
2521                                                     "Delete Node/Subtree",
2522                                                     JOptionPane.CLOSED_OPTION,
2523                                                     JOptionPane.QUESTION_MESSAGE,
2524                                                     null,
2525                                                     options,
2526                                                     options[ 2 ] );
2527         setNodeInPreorderToNull();
2528         boolean node_only = true;
2529         if ( r == 1 ) {
2530             node_only = false;
2531         }
2532         else if ( r != 0 ) {
2533             return;
2534         }
2535         if ( node_only ) {
2536             PhylogenyMethods.removeNode( node, _phylogeny );
2537         }
2538         else {
2539             _phylogeny.deleteSubtree( node, true );
2540         }
2541         _phylogeny.externalNodesHaveChanged();
2542         _phylogeny.clearHashIdToNodeMap();
2543         _phylogeny.recalculateNumberOfExternalDescendants( true );
2544         resetNodeIdToDistToLeafMap();
2545         setEdited( true );
2546         repaint();
2547     }
2548
2549     final private void displayNodePopupMenu( final PhylogenyNode node, final int x, final int y ) {
2550         makePopupMenus( node );
2551         _node_popup_menu.putClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY, node );
2552         _node_popup_menu.show( this, x, y );
2553     }
2554
2555     final private void drawArc( final double x,
2556                                 final double y,
2557                                 final double width,
2558                                 final double heigth,
2559                                 final double start_angle,
2560                                 final double arc_angle,
2561                                 final Graphics2D g ) {
2562         _arc.setArc( x, y, width, heigth, _180_OVER_PI * start_angle, _180_OVER_PI * arc_angle, Arc2D.OPEN );
2563         g.draw( _arc );
2564     }
2565
2566     final private void drawLine( final double x1, final double y1, final double x2, final double y2, final Graphics2D g ) {
2567         if ( ( x1 == x2 ) && ( y1 == y2 ) ) {
2568             return;
2569         }
2570         _line.setLine( x1, y1, x2, y2 );
2571         g.draw( _line );
2572     }
2573
2574     final private void drawOval( final double x,
2575                                  final double y,
2576                                  final double width,
2577                                  final double heigth,
2578                                  final Graphics2D g ) {
2579         _ellipse.setFrame( x, y, width, heigth );
2580         g.draw( _ellipse );
2581     }
2582
2583     final private void drawOvalFilled( final double x,
2584                                        final double y,
2585                                        final double width,
2586                                        final double heigth,
2587                                        final Graphics2D g ) {
2588         _ellipse.setFrame( x, y, width, heigth );
2589         g.fill( _ellipse );
2590     }
2591
2592     final private void drawOvalGradient( final double x,
2593                                          final double y,
2594                                          final double width,
2595                                          final double heigth,
2596                                          final Graphics2D g,
2597                                          final Color color_1,
2598                                          final Color color_2,
2599                                          final Color color_border ) {
2600         _ellipse.setFrame( x, y, width, heigth );
2601         g.setPaint( new GradientPaint( ( float ) x,
2602                                        ( float ) y,
2603                                        color_1,
2604                                        ( float ) ( x + width ),
2605                                        ( float ) ( y + heigth ),
2606                                        color_2,
2607                                        false ) );
2608         g.fill( _ellipse );
2609         if ( color_border != null ) {
2610             g.setPaint( color_border );
2611             g.draw( _ellipse );
2612         }
2613     }
2614
2615     final private void drawRect( final float x, final float y, final float width, final float heigth, final Graphics2D g ) {
2616         _rectangle.setFrame( x, y, width, heigth );
2617         g.draw( _rectangle );
2618     }
2619
2620     final private void drawRectFilled( final double x,
2621                                        final double y,
2622                                        final double width,
2623                                        final double heigth,
2624                                        final Graphics2D g ) {
2625         _rectangle.setFrame( x, y, width, heigth );
2626         g.fill( _rectangle );
2627     }
2628
2629     final private void drawRectGradient( final double x,
2630                                          final double y,
2631                                          final double width,
2632                                          final double heigth,
2633                                          final Graphics2D g,
2634                                          final Color color_1,
2635                                          final Color color_2,
2636                                          final Color color_border ) {
2637         _rectangle.setFrame( x, y, width, heigth );
2638         g.setPaint( new GradientPaint( ( float ) x,
2639                                        ( float ) y,
2640                                        color_1,
2641                                        ( float ) ( x + width ),
2642                                        ( float ) ( y + heigth ),
2643                                        color_2,
2644                                        false ) );
2645         g.fill( _rectangle );
2646         if ( color_border != null ) {
2647             g.setPaint( color_border );
2648             g.draw( _rectangle );
2649         }
2650     }
2651
2652     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
2653         final List<Uri> us = new ArrayList<Uri>();
2654         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
2655             for( final Uri uri : t.getUris() ) {
2656                 us.add( uri );
2657             }
2658         }
2659         double offset = 0;
2660         for( final Uri uri : us ) {
2661             if ( uri != null ) {
2662                 final String uri_str = uri.getValue().toString().toLowerCase();
2663                 if ( getImageMap().containsKey( uri_str ) ) {
2664                     final BufferedImage bi = getImageMap().get( uri_str );
2665                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
2666                         double scaling_factor = 1;
2667                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
2668                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
2669                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
2670                         }
2671                         // y = y - ( 0.9 * getYdistance() );
2672                         final double hs = bi.getHeight() * scaling_factor;
2673                         double ws = ( bi.getWidth() * scaling_factor ) + offset;
2674                         final double my_y = y - ( 0.5 * hs );
2675                         final int x_w = ( int ) ( x + ws + 0.5 );
2676                         final int y_h = ( int ) ( my_y + hs + 0.5 );
2677                         if ( ( ( x_w - x ) > 7 ) && ( ( y_h - my_y ) > 7 ) ) {
2678                             g.drawImage( bi,
2679                                          ( int ) ( x + 0.5 + offset ),
2680                                          ( int ) ( my_y + 0.5 ),
2681                                          x_w,
2682                                          y_h,
2683                                          0,
2684                                          0,
2685                                          bi.getWidth(),
2686                                          bi.getHeight(),
2687                                          null );
2688                             ws += 8;
2689                         }
2690                         else {
2691                             ws = 0.0;
2692                         }
2693                         offset = ws;
2694                     }
2695                 }
2696             }
2697         }
2698         return offset;
2699     }
2700
2701     final private void errorMessageNoCutCopyPasteInUnrootedDisplay() {
2702         JOptionPane.showMessageDialog( this,
2703                                        "Cannot cut, copy, paste, add, or delete subtrees/nodes in unrooted display",
2704                                        "Attempt to cut/copy/paste/add/delete in unrooted display",
2705                                        JOptionPane.ERROR_MESSAGE );
2706     }
2707
2708     final private Set<Long> getCopiedAndPastedNodes() {
2709         return getMainPanel().getCopiedAndPastedNodes();
2710     }
2711
2712     final private Set<Long> getCurrentExternalNodes() {
2713         return _current_external_nodes;
2714     }
2715
2716     final private Phylogeny getCutOrCopiedTree() {
2717         return getMainPanel().getCutOrCopiedTree();
2718     }
2719
2720     final private float getLastDragPointX() {
2721         return _last_drag_point_x;
2722     }
2723
2724     final private float getLastDragPointY() {
2725         return _last_drag_point_y;
2726     }
2727
2728     final private short getMaxBranchesToLeaf( final PhylogenyNode node ) {
2729         if ( !_nodeid_dist_to_leaf.containsKey( node.getId() ) ) {
2730             final short m = PhylogenyMethods.calculateMaxBranchesToLeaf( node );
2731             _nodeid_dist_to_leaf.put( node.getId(), m );
2732             return m;
2733         }
2734         else {
2735             return _nodeid_dist_to_leaf.get( node.getId() );
2736         }
2737     }
2738
2739     final private double getMaxDistanceToRoot() {
2740         if ( _max_distance_to_root < 0 ) {
2741             recalculateMaxDistanceToRoot();
2742         }
2743         return _max_distance_to_root;
2744     }
2745
2746     final private float getOvMaxHeight() {
2747         return _ov_max_height;
2748     }
2749
2750     final private float getOvMaxWidth() {
2751         return _ov_max_width;
2752     }
2753
2754     final private float getOvXcorrectionFactor() {
2755         return _ov_x_correction_factor;
2756     }
2757
2758     final private float getOvXDistance() {
2759         return _ov_x_distance;
2760     }
2761
2762     final private int getOvXPosition() {
2763         return _ov_x_position;
2764     }
2765
2766     final private float getOvYDistance() {
2767         return _ov_y_distance;
2768     }
2769
2770     final private int getOvYPosition() {
2771         return _ov_y_position;
2772     }
2773
2774     final private int getOvYStart() {
2775         return _ov_y_start;
2776     }
2777
2778     final private double getScaleDistance() {
2779         return _scale_distance;
2780     }
2781
2782     final private String getScaleLabel() {
2783         return _scale_label;
2784     }
2785
2786     final private TreeFontSet getTreeFontSet() {
2787         return getMainPanel().getTreeFontSet();
2788     }
2789
2790     final private float getUrtFactor() {
2791         return _urt_factor;
2792     }
2793
2794     final private float getUrtFactorOv() {
2795         return _urt_factor_ov;
2796     }
2797
2798     final private void handleClickToAction( final NodeClickAction action, final PhylogenyNode node ) {
2799         switch ( action ) {
2800             case SHOW_DATA:
2801                 showNodeFrame( node );
2802                 break;
2803             case COLLAPSE:
2804                 collapse( node );
2805                 break;
2806             case REROOT:
2807                 reRoot( node );
2808                 break;
2809             case SUBTREE:
2810                 subTree( node );
2811                 break;
2812             case SWAP:
2813                 swap( node );
2814                 break;
2815             case COLOR_SUBTREE:
2816                 colorSubtree( node );
2817                 break;
2818             case OPEN_SEQ_WEB:
2819                 openSeqWeb( node );
2820                 break;
2821             case BLAST:
2822                 blast( node );
2823                 break;
2824             case OPEN_TAX_WEB:
2825                 openTaxWeb( node );
2826                 break;
2827             case CUT_SUBTREE:
2828                 cutSubtree( node );
2829                 break;
2830             case COPY_SUBTREE:
2831                 copySubtree( node );
2832                 break;
2833             case PASTE_SUBTREE:
2834                 pasteSubtree( node );
2835                 break;
2836             case DELETE_NODE_OR_SUBTREE:
2837                 deleteNodeOrSubtree( node );
2838                 break;
2839             case ADD_NEW_NODE:
2840                 addEmptyNode( node );
2841                 break;
2842             case EDIT_NODE_DATA:
2843                 showNodeEditFrame( node );
2844                 break;
2845             case SELECT_NODES:
2846                 selectNode( node );
2847                 break;
2848             case SORT_DESCENDENTS:
2849                 sortDescendants( node );
2850                 break;
2851             case GET_EXT_DESC_DATA:
2852                 showExtDescNodeData( node );
2853                 break;
2854             default:
2855                 throw new IllegalArgumentException( "unknown action: " + action );
2856         }
2857     }
2858
2859     final private void increaseCurrentExternalNodesDataBufferChangeCounter() {
2860         _current_external_nodes_data_buffer_change_counter++;
2861     }
2862
2863     final private void increaseOvSize() {
2864         if ( ( getOvMaxWidth() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth() / 2 ) )
2865                 && ( getOvMaxHeight() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
2866                         .getHeight() / 2 ) ) ) {
2867             setOvMaxWidth( getOvMaxWidth() + 5 );
2868             setOvMaxHeight( getOvMaxHeight() + 5 );
2869             updateOvSettings();
2870             getControlPanel().displayedPhylogenyMightHaveChanged( false );
2871         }
2872     }
2873
2874     final private void init() {
2875         _color_chooser = new JColorChooser();
2876         _rollover_popup = new JTextArea();
2877         _rollover_popup.setFont( POPUP_FONT );
2878         resetNodeIdToDistToLeafMap();
2879         setTextAntialias();
2880         setTreeFile( null );
2881         setEdited( false );
2882         initializeOvSettings();
2883         setStartingAngle( ( TWO_PI * 3 ) / 4 );
2884         final ImageLoader il = new ImageLoader( this );
2885         new Thread( il ).start();
2886     }
2887
2888     final private void initializeOvSettings() {
2889         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
2890         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
2891     }
2892
2893     final private boolean inOvVirtualRectangle( final int x, final int y ) {
2894         return ( ( x >= ( getOvVirtualRectangle().x - 1 ) )
2895                 && ( x <= ( getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 ) )
2896                 && ( y >= ( getOvVirtualRectangle().y - 1 ) ) && ( y <= ( getOvVirtualRectangle().y
2897                 + getOvVirtualRectangle().height + 1 ) ) );
2898     }
2899
2900     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
2901         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
2902     }
2903
2904     final private boolean isCanBlast( final PhylogenyNode node ) {
2905         if ( !node.getNodeData().isHasSequence() && ForesterUtil.isEmpty( node.getName() ) ) {
2906             return false;
2907         }
2908         return Blast.isContainsQueryForBlast( node );
2909     }
2910
2911     final private String isCanOpenSeqWeb( final PhylogenyNode node ) {
2912         String v = ForesterUtil.extractUniProtKbProteinSeqIdentifier( node );
2913         if ( ForesterUtil.isEmpty( v ) ) {
2914             v = ForesterUtil.extractGenbankAccessor( node );
2915         }
2916         if ( ForesterUtil.isEmpty( v ) ) {
2917             v = ForesterUtil.extractRefSeqAccessorAccessor( node );
2918         }
2919         if ( ForesterUtil.isEmpty( v ) ) {
2920             v = ForesterUtil.extractGInumber( node );
2921         }
2922         return v;
2923     }
2924
2925     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
2926         if ( node.getNodeData().isHasTaxonomy()
2927                 && ( ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
2928                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
2929                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) || ( ( node
2930                         .getNodeData().getTaxonomy().getIdentifier() != null ) && !ForesterUtil.isEmpty( node
2931                         .getNodeData().getTaxonomy().getIdentifier().getValue() ) ) ) ) {
2932             return true;
2933         }
2934         else {
2935             return false;
2936         }
2937     }
2938
2939     final private boolean isInCurrentExternalNodes( final PhylogenyNode node ) {
2940         return ( ( getCurrentExternalNodes() != null ) && getCurrentExternalNodes().contains( node.getId() ) );
2941     }
2942
2943     final private boolean isInFoundNodes( final PhylogenyNode node ) {
2944         return ( ( getFoundNodes() != null ) && getFoundNodes().contains( node.getId() ) );
2945     }
2946
2947     final private boolean isInOv() {
2948         return _in_ov;
2949     }
2950
2951     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
2952         int y_dist = 40;
2953         if ( getControlPanel().isShowTaxonomyImages() ) {
2954             y_dist = 40 + ( int ) getYdistance();
2955         }
2956         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - y_dist ) )
2957                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + y_dist ) ) || ( ( node.getParent() != null ) && ( node
2958                 .getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
2959     }
2960
2961     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
2962         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - 20 ) )
2963                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + 20 ) )
2964                 || ( node.getXcoord() < ( getVisibleRect().getMinX() - 20 ) ) || ( node.getXcoord() > ( getVisibleRect()
2965                 .getMaxX() + 20 ) ) );
2966     }
2967
2968     final private boolean isNonLinedUpCladogram() {
2969         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
2970     }
2971
2972     final private boolean isUniformBranchLengthsForCladogram() {
2973         return getOptions().getCladogramType() == CLADOGRAM_TYPE.TOTAL_NODE_SUM_DEP;
2974     }
2975
2976     final private void keyPressedCalls( final KeyEvent e ) {
2977         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
2978             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
2979                 if ( !isInOvRect() ) {
2980                     setInOvRect( true );
2981                 }
2982             }
2983             else if ( isInOvRect() ) {
2984                 setInOvRect( false );
2985             }
2986         }
2987         if ( e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK ) {
2988             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
2989                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
2990                 getMainPanel().getTreeFontSet().mediumFonts();
2991                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
2992             }
2993             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
2994                 getMainPanel().getTreeFontSet().decreaseFontSize( 1, false );
2995                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
2996             }
2997             else if ( plusPressed( e.getKeyCode() ) ) {
2998                 getMainPanel().getTreeFontSet().increaseFontSize();
2999                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
3000             }
3001         }
3002         else {
3003             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
3004                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
3005                 getControlPanel().showWhole();
3006             }
3007             else if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
3008                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
3009                 if ( e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK ) {
3010                     if ( e.getKeyCode() == KeyEvent.VK_UP ) {
3011                         getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
3012                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3013                     }
3014                     else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
3015                         getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
3016                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3017                     }
3018                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
3019                         getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
3020                                                                    Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
3021                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3022                     }
3023                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
3024                         getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
3025                                                                   Constants.WHEEL_ZOOM_IN_FACTOR );
3026                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3027                     }
3028                 }
3029                 else {
3030                     final int d = 80;
3031                     int dx = 0;
3032                     int dy = -d;
3033                     if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
3034                         dy = d;
3035                     }
3036                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
3037                         dx = -d;
3038                         dy = 0;
3039                     }
3040                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
3041                         dx = d;
3042                         dy = 0;
3043                     }
3044                     final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
3045                     scroll_position.x = scroll_position.x + dx;
3046                     scroll_position.y = scroll_position.y + dy;
3047                     if ( scroll_position.x <= 0 ) {
3048                         scroll_position.x = 0;
3049                     }
3050                     else {
3051                         final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
3052                                 - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
3053                         if ( scroll_position.x >= max_x ) {
3054                             scroll_position.x = max_x;
3055                         }
3056                     }
3057                     if ( scroll_position.y <= 0 ) {
3058                         scroll_position.y = 0;
3059                     }
3060                     else {
3061                         final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
3062                                 - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
3063                         if ( scroll_position.y >= max_y ) {
3064                             scroll_position.y = max_y;
3065                         }
3066                     }
3067                     repaint();
3068                     getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
3069                 }
3070             }
3071             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
3072                 getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
3073                 getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
3074                                                            Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
3075                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3076             }
3077             else if ( plusPressed( e.getKeyCode() ) ) {
3078                 getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
3079                                                           Constants.WHEEL_ZOOM_IN_FACTOR );
3080                 getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
3081                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3082             }
3083             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
3084                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3085                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3086                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
3087                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3088                 }
3089             }
3090             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
3091                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3092                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3093                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
3094                     if ( getStartingAngle() < 0 ) {
3095                         setStartingAngle( TWO_PI + getStartingAngle() );
3096                     }
3097                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3098                 }
3099             }
3100             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
3101                 boolean selected = false;
3102                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
3103                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
3104                     selected = true;
3105                 }
3106                 else {
3107                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
3108                 }
3109                 if ( getMainPanel().getMainFrame() == null ) {
3110                     // Must be "E" applet version.
3111                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
3112                     if ( ae.getlabelDirectionCbmi() != null ) {
3113                         ae.getlabelDirectionCbmi().setSelected( selected );
3114                     }
3115                 }
3116                 else {
3117                     getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
3118                 }
3119                 repaint();
3120             }
3121             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
3122                 switchDisplaygetPhylogenyGraphicsType();
3123                 repaint();
3124             }
3125             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
3126                 cycleColors();
3127                 repaint();
3128             }
3129             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
3130                 MainFrame.cycleOverview( getOptions(), this );
3131                 repaint();
3132             }
3133             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
3134                 increaseOvSize();
3135             }
3136             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
3137                 decreaseOvSize();
3138             }
3139             e.consume();
3140         }
3141     }
3142
3143     final private void makePopupMenus( final PhylogenyNode node ) {
3144         _node_popup_menu = new JPopupMenu();
3145         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
3146         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
3147         for( int i = 0; i < clickto_names.size(); i++ ) {
3148             final String title = clickto_names.get( i );
3149             _node_popup_menu_items[ i ] = new JMenuItem( title );
3150             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
3151                 final String id = isCanOpenSeqWeb( node );
3152                 if ( !ForesterUtil.isEmpty( id ) ) {
3153                     _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " [" + id + "]" );
3154                     _node_popup_menu_items[ i ].setEnabled( true );
3155                 }
3156                 else {
3157                     _node_popup_menu_items[ i ].setEnabled( false );
3158                 }
3159             }
3160             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
3161                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
3162             }
3163             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
3164                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
3165             }
3166             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
3167                 if ( !getOptions().isEditable() ) {
3168                     continue;
3169                 }
3170                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
3171             }
3172             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
3173                 if ( !getOptions().isEditable() ) {
3174                     continue;
3175                 }
3176                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
3177             }
3178             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
3179                 if ( !getOptions().isEditable() ) {
3180                     continue;
3181                 }
3182                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
3183             }
3184             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
3185                 if ( !getOptions().isEditable() ) {
3186                     continue;
3187                 }
3188                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
3189             }
3190             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
3191                 if ( !getOptions().isEditable() ) {
3192                     continue;
3193                 }
3194             }
3195             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
3196                 if ( !getOptions().isEditable() ) {
3197                     continue;
3198                 }
3199             }
3200             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
3201                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
3202             }
3203             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
3204                 _node_popup_menu_items[ i ].setEnabled( ( isCanCollapse() && !node.isExternal() ) );
3205             }
3206             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
3207                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
3208             }
3209             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
3210                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
3211             }
3212             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
3213                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
3214             }
3215             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
3216                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
3217             }
3218             _node_popup_menu_items[ i ].addActionListener( this );
3219             _node_popup_menu.add( _node_popup_menu_items[ i ] );
3220         }
3221     }
3222
3223     private final String obtainTitleForExtDescNodeData() {
3224         switch ( getOptions().getExtDescNodeDataToReturn() ) {
3225             case NODE_NAME:
3226                 return "Node Names";
3227             case SEQUENCE_NAME:
3228                 return "Sequence Names";
3229             case SEQUENCE_SYMBOL:
3230                 return "Sequence Symbols";
3231             case SEQUENCE_MOL_SEQ:
3232                 return "Molecular Sequences";
3233             case SEQUENCE_MOL_SEQ_FASTA:
3234                 return "Molecular Sequences (Fasta)";
3235             case SEQUENCE_ACC:
3236                 return "Sequence Accessors";
3237             case TAXONOMY_SCIENTIFIC_NAME:
3238                 return "Scientific Names";
3239             case TAXONOMY_CODE:
3240                 return "Taxonomy Codes";
3241             case TAXONOMY_COMM0N_NAME:
3242                 return "Taxonomy Common Names";
3243             case UNKNOWN:
3244                 return "User Selected Data";
3245             default:
3246                 throw new IllegalArgumentException( "unknown data element: "
3247                         + getOptions().getExtDescNodeDataToReturn() );
3248         }
3249     }
3250
3251     final private void openSeqWeb( final PhylogenyNode node ) {
3252         if ( ForesterUtil.isEmpty( isCanOpenSeqWeb( node ) ) ) {
3253             cannotOpenBrowserWarningMessage( "sequence" );
3254             return;
3255         }
3256         final String uri_str = TreePanelUtil.createUriForSeqWeb( node, getConfiguration(), this );
3257         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3258             try {
3259                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3260                                            isApplet(),
3261                                            isApplet() ? obtainApplet() : null,
3262                                            "_aptx_seq" );
3263             }
3264             catch ( final IOException e ) {
3265                 AptxUtil.showErrorMessage( this, e.toString() );
3266                 e.printStackTrace();
3267             }
3268             catch ( final URISyntaxException e ) {
3269                 AptxUtil.showErrorMessage( this, e.toString() );
3270                 e.printStackTrace();
3271             }
3272         }
3273         else {
3274             cannotOpenBrowserWarningMessage( "sequence" );
3275         }
3276     }
3277
3278     final private void openTaxWeb( final PhylogenyNode node ) {
3279         if ( !isCanOpenTaxWeb( node ) ) {
3280             cannotOpenBrowserWarningMessage( "taxonomic" );
3281             return;
3282         }
3283         String uri_str = null;
3284         final Taxonomy tax = node.getNodeData().getTaxonomy();
3285         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3286                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
3287             try {
3288                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
3289             }
3290             catch ( final URISyntaxException e ) {
3291                 AptxUtil.showErrorMessage( this, e.toString() );
3292                 uri_str = null;
3293                 e.printStackTrace();
3294             }
3295         }
3296         else if ( ( tax.getIdentifier() != null )
3297                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3298                 && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
3299                 && ( tax.getIdentifier().getProvider().equalsIgnoreCase( "ncbi" ) || tax.getIdentifier().getProvider()
3300                         .equalsIgnoreCase( "uniprot" ) ) ) {
3301             try {
3302                 uri_str = "http://www.uniprot.org/taxonomy/"
3303                         + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
3304             }
3305             catch ( final UnsupportedEncodingException e ) {
3306                 AptxUtil.showErrorMessage( this, e.toString() );
3307                 e.printStackTrace();
3308             }
3309         }
3310         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
3311             try {
3312                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3313                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
3314             }
3315             catch ( final UnsupportedEncodingException e ) {
3316                 AptxUtil.showErrorMessage( this, e.toString() );
3317                 e.printStackTrace();
3318             }
3319         }
3320         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
3321             try {
3322                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3323                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
3324             }
3325             catch ( final UnsupportedEncodingException e ) {
3326                 AptxUtil.showErrorMessage( this, e.toString() );
3327                 e.printStackTrace();
3328             }
3329         }
3330         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
3331             try {
3332                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3333                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
3334             }
3335             catch ( final UnsupportedEncodingException e ) {
3336                 AptxUtil.showErrorMessage( this, e.toString() );
3337                 e.printStackTrace();
3338             }
3339         }
3340         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3341             try {
3342                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3343                                            isApplet(),
3344                                            isApplet() ? obtainApplet() : null,
3345                                            "_aptx_tax" );
3346             }
3347             catch ( final IOException e ) {
3348                 AptxUtil.showErrorMessage( this, e.toString() );
3349                 e.printStackTrace();
3350             }
3351             catch ( final URISyntaxException e ) {
3352                 AptxUtil.showErrorMessage( this, e.toString() );
3353                 e.printStackTrace();
3354             }
3355         }
3356         else {
3357             cannotOpenBrowserWarningMessage( "taxonomic" );
3358         }
3359     }
3360
3361     final private void paintBranchLength( final Graphics2D g,
3362                                           final PhylogenyNode node,
3363                                           final boolean to_pdf,
3364                                           final boolean to_graphics_file ) {
3365         g.setFont( getTreeFontSet().getSmallFont() );
3366         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3367             g.setColor( Color.BLACK );
3368         }
3369         else {
3370             g.setColor( getTreeColorSet().getBranchLengthColor() );
3371         }
3372         if ( !node.isRoot() ) {
3373             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3374                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3375                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3376             }
3377             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3378                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3379                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3380             }
3381             else {
3382                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3383                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3384             }
3385         }
3386         else {
3387             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
3388                     - getTreeFontSet()._small_max_descent, g );
3389         }
3390     }
3391
3392     final private void paintBranchLite( final Graphics2D g,
3393                                         final float x1,
3394                                         final float x2,
3395                                         final float y1,
3396                                         final float y2,
3397                                         final PhylogenyNode node ) {
3398         g.setColor( getTreeColorSet().getOvColor() );
3399         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3400             drawLine( x1, y1, x2, y2, g );
3401         }
3402         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3403             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3404             ( g ).draw( _quad_curve );
3405         }
3406         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3407             final float dx = x2 - x1;
3408             final float dy = y2 - y1;
3409             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3410                     + ( dy * 0.8f ), x2, y2 );
3411             ( g ).draw( _cubic_curve );
3412         }
3413         else {
3414             final float x2a = x2;
3415             final float x1a = x1;
3416             // draw the vertical line
3417             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
3418                 drawLine( x1, y1, x1, y2, g );
3419             }
3420             // draw the horizontal line
3421             drawLine( x1a, y2, x2a, y2, g );
3422         }
3423     }
3424
3425     /**
3426      * Paint a branch which consists of a vertical and a horizontal bar
3427      * @param is_ind_found_nodes 
3428      */
3429     final private void paintBranchRectangular( final Graphics2D g,
3430                                                final float x1,
3431                                                final float x2,
3432                                                final float y1,
3433                                                final float y2,
3434                                                final PhylogenyNode node,
3435                                                final boolean to_pdf,
3436                                                final boolean to_graphics_file ) {
3437         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
3438         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3439             drawLine( x1, y1, x2, y2, g );
3440         }
3441         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3442             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3443             g.draw( _quad_curve );
3444         }
3445         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3446             final float dx = x2 - x1;
3447             final float dy = y2 - y1;
3448             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3449                     + ( dy * 0.8f ), x2, y2 );
3450             g.draw( _cubic_curve );
3451         }
3452         else {
3453             final float x2a = x2;
3454             final float x1a = x1;
3455             float y2_r = 0;
3456             if ( node.isFirstChildNode() || node.isLastChildNode()
3457                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3458                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3459                 if ( !to_graphics_file
3460                         && !to_pdf
3461                         && ( ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) && ( y1 < ( getVisibleRect().getMinY() - 20 ) ) ) || ( ( y2 > ( getVisibleRect()
3462                                 .getMaxY() + 20 ) ) && ( y1 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) ) {
3463                     // Do nothing.
3464                 }
3465                 else {
3466                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3467                         float x2c = x1 + EURO_D;
3468                         if ( x2c > x2a ) {
3469                             x2c = x2a;
3470                         }
3471                         drawLine( x1, y1, x2c, y2, g );
3472                     }
3473                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3474                         if ( y2 > y1 ) {
3475                             y2_r = y2 - ROUNDED_D;
3476                             if ( y2_r < y1 ) {
3477                                 y2_r = y1;
3478                             }
3479                             drawLine( x1, y1, x1, y2_r, g );
3480                         }
3481                         else {
3482                             y2_r = y2 + ROUNDED_D;
3483                             if ( y2_r > y1 ) {
3484                                 y2_r = y1;
3485                             }
3486                             drawLine( x1, y1, x1, y2_r, g );
3487                         }
3488                     }
3489                     else {
3490                         drawLine( x1, y1, x1, y2, g );
3491                     }
3492                 }
3493             }
3494             // draw the horizontal line
3495             if ( !to_graphics_file && !to_pdf
3496                     && ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) || ( y2 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) {
3497                 return;
3498             }
3499             float x1_r = 0;
3500             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
3501                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3502                     x1_r = x1a + ROUNDED_D;
3503                     if ( x1_r < x2a ) {
3504                         drawLine( x1_r, y2, x2a, y2, g );
3505                     }
3506                 }
3507                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3508                     final float x1c = x1a + EURO_D;
3509                     if ( x1c < x2a ) {
3510                         drawLine( x1c, y2, x2a, y2, g );
3511                     }
3512                 }
3513                 else {
3514                     drawLine( x1a, y2, x2a, y2, g );
3515                 }
3516             }
3517             else {
3518                 final double w = PhylogenyMethods.getBranchWidthValue( node );
3519                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3520                     x1_r = x1a + ROUNDED_D;
3521                     if ( x1_r < x2a ) {
3522                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
3523                     }
3524                 }
3525                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3526                     final float x1c = x1a + EURO_D;
3527                     if ( x1c < x2a ) {
3528                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
3529                     }
3530                 }
3531                 else {
3532                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
3533                 }
3534             }
3535             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3536                 if ( x1_r > x2a ) {
3537                     x1_r = x2a;
3538                 }
3539                 if ( y2 > y2_r ) {
3540                     final double diff = y2 - y2_r;
3541                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
3542                 }
3543                 else {
3544                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
3545                 }
3546                 g.draw( _arc );
3547             }
3548         }
3549         if ( node.isExternal() ) {
3550             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file, isInFoundNodes( node )
3551                     || isInCurrentExternalNodes( node ) );
3552         }
3553     }
3554
3555     final private double paintCirculars( final PhylogenyNode n,
3556                                          final Phylogeny phy,
3557                                          final float center_x,
3558                                          final float center_y,
3559                                          final double radius,
3560                                          final boolean radial_labels,
3561                                          final Graphics2D g,
3562                                          final boolean to_pdf,
3563                                          final boolean to_graphics_file ) {
3564         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
3565             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
3566                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
3567             }
3568             return _urt_nodeid_angle_map.get( n.getId() );
3569         }
3570         else {
3571             final List<PhylogenyNode> descs = n.getDescendants();
3572             double sum = 0;
3573             for( final PhylogenyNode desc : descs ) {
3574                 sum += paintCirculars( desc,
3575                                        phy,
3576                                        center_x,
3577                                        center_y,
3578                                        radius,
3579                                        radial_labels,
3580                                        g,
3581                                        to_pdf,
3582                                        to_graphics_file );
3583             }
3584             double r = 0;
3585             if ( !n.isRoot() ) {
3586                 r = 1 - ( ( ( double ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3587             }
3588             final double theta = sum / descs.size();
3589             n.setXcoord( ( float ) ( center_x + ( r * radius * Math.cos( theta ) ) ) );
3590             n.setYcoord( ( float ) ( center_y + ( r * radius * Math.sin( theta ) ) ) );
3591             _urt_nodeid_angle_map.put( n.getId(), theta );
3592             for( final PhylogenyNode desc : descs ) {
3593                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
3594             }
3595             return theta;
3596         }
3597     }
3598
3599     final private void paintCircularsLite( final PhylogenyNode n,
3600                                            final Phylogeny phy,
3601                                            final int center_x,
3602                                            final int center_y,
3603                                            final int radius,
3604                                            final Graphics2D g ) {
3605         if ( n.isExternal() ) {
3606             return;
3607         }
3608         else {
3609             final List<PhylogenyNode> descs = n.getDescendants();
3610             for( final PhylogenyNode desc : descs ) {
3611                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
3612             }
3613             float r = 0;
3614             if ( !n.isRoot() ) {
3615                 r = 1 - ( ( ( float ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3616             }
3617             final double theta = _urt_nodeid_angle_map.get( n.getId() );
3618             n.setXSecondary( ( float ) ( center_x + ( radius * r * Math.cos( theta ) ) ) );
3619             n.setYSecondary( ( float ) ( center_y + ( radius * r * Math.sin( theta ) ) ) );
3620             for( final PhylogenyNode desc : descs ) {
3621                 paintBranchCircularLite( n, desc, g );
3622             }
3623         }
3624     }
3625
3626     final private void paintCollapsedNode( final Graphics2D g,
3627                                            final PhylogenyNode node,
3628                                            final boolean to_graphics_file,
3629                                            final boolean to_pdf,
3630                                            final boolean is_in_found_nodes ) {
3631         Color c = null;
3632         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3633             c = Color.BLACK;
3634         }
3635         else if ( is_in_found_nodes ) {
3636             c = getTreeColorSet().getFoundColor();
3637         }
3638         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3639             c = getTaxonomyBasedColor( node );
3640         }
3641         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3642                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3643             c = PhylogenyMethods.getBranchColorValue( node );
3644         }
3645         else {
3646             c = getTreeColorSet().getCollapseFillColor();
3647         }
3648         double d = node.getAllExternalDescendants().size();
3649         if ( d > 1000 ) {
3650             d = ( 3 * _y_distance ) / 3;
3651         }
3652         else {
3653             d = ( Math.log10( d ) * _y_distance ) / 2.5;
3654         }
3655         final int box_size = getOptions().getDefaultNodeShapeSize() + 1;
3656         if ( d < box_size ) {
3657             d = box_size;
3658         }
3659         final float xx = node.getXcoord() - ( 2 * box_size );
3660         final float xxx = xx > node.getParent().getXcoord() + 1 ? xx : node.getParent().getXcoord() + 1;
3661         _polygon.reset();
3662         _polygon.moveTo( xxx, node.getYcoord() );
3663         _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() - d );
3664         _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() + d );
3665         _polygon.closePath();
3666         if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3667             g.setColor( c );
3668             g.fill( _polygon );
3669         }
3670         else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
3671             g.setColor( getBackground() );
3672             g.fill( _polygon );
3673             g.setColor( c );
3674             g.draw( _polygon );
3675         }
3676         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3677             g.setPaint( new GradientPaint( xxx, node.getYcoord(), getBackground(), node.getXcoord(), ( float ) ( node
3678                     .getYcoord() - d ), c, false ) );
3679             g.fill( _polygon );
3680             g.setPaint( c );
3681             g.draw( _polygon );
3682         }
3683         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3684     }
3685
3686     final private void paintConfidenceValues( final Graphics2D g,
3687                                               final PhylogenyNode node,
3688                                               final boolean to_pdf,
3689                                               final boolean to_graphics_file ) {
3690         final List<Confidence> confidences = node.getBranchData().getConfidences();
3691         //        if ( confidences.size() == 1 ) {
3692         //            final double value = node.getBranchData().getConfidence( 0 ).getValue();
3693         //            if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
3694         //                return;
3695         //            }
3696         //            conf_str = FORMATTER_CONFIDENCE.format( value );
3697         //        }
3698         //        else if ( confidences.size() > 1 ) {
3699         boolean one_ok = false;
3700         boolean not_first = false;
3701         Collections.sort( confidences );
3702         final StringBuilder sb = new StringBuilder();
3703         String conf_str = "";
3704         for( final Confidence confidence : confidences ) {
3705             final double value = confidence.getValue();
3706             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
3707                 if ( value >= getOptions().getMinConfidenceValue() ) {
3708                     one_ok = true;
3709                 }
3710                 if ( not_first ) {
3711                     sb.append( "/" );
3712                 }
3713                 else {
3714                     not_first = true;
3715                 }
3716                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
3717                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
3718                 if ( getOptions().isShowConfidenceStddev() ) {
3719                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
3720                         sb.append( "(" );
3721                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
3722                                                                                     getOptions()
3723                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
3724                         sb.append( ")" );
3725                     }
3726                 }
3727             }
3728             //}
3729             if ( one_ok ) {
3730                 conf_str = sb.toString();
3731             }
3732         }
3733         if ( conf_str.length() > 0 ) {
3734             final double parent_x = node.getParent().getXcoord();
3735             double x = node.getXcoord();
3736             g.setFont( getTreeFontSet().getSmallFont() );
3737             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3738                 x += EURO_D;
3739             }
3740             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3741                 x += ROUNDED_D;
3742             }
3743             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3744                 g.setColor( Color.BLACK );
3745             }
3746             else {
3747                 g.setColor( getTreeColorSet().getConfidenceColor() );
3748             }
3749             TreePanel
3750                     .drawString( conf_str,
3751                                  parent_x
3752                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
3753                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
3754                                  g );
3755         }
3756     }
3757
3758     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
3759         final int box_size = getOptions().getDefaultNodeShapeSize();
3760         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
3761         g.setColor( getTreeColorSet().getFoundColor() );
3762         g.fillRect( x - half_box_size, y - half_box_size, box_size, box_size );
3763     }
3764
3765     final private void paintGainedAndLostCharacters( final Graphics2D g,
3766                                                      final PhylogenyNode node,
3767                                                      final String gained,
3768                                                      final String lost ) {
3769         if ( node.getParent() != null ) {
3770             final double parent_x = node.getParent().getXcoord();
3771             final double x = node.getXcoord();
3772             g.setFont( getTreeFontSet().getLargeFont() );
3773             g.setColor( getTreeColorSet().getGainedCharactersColor() );
3774             if ( Constants.SPECIAL_CUSTOM ) {
3775                 g.setColor( Color.BLUE );
3776             }
3777             TreePanel
3778                     .drawString( gained,
3779                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
3780                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
3781                                  g );
3782             g.setColor( getTreeColorSet().getLostCharactersColor() );
3783             TreePanel.drawString( lost,
3784                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
3785                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
3786                                   g );
3787         }
3788     }
3789
3790     /**
3791      * Draw a box at the indicated node.
3792      * 
3793      * @param x
3794      * @param y
3795      * @param node
3796      * @param g
3797      */
3798     final private void paintNodeBox( final double x,
3799                                      final double y,
3800                                      final PhylogenyNode node,
3801                                      final Graphics2D g,
3802                                      final boolean to_pdf,
3803                                      final boolean to_graphics_file,
3804                                      final boolean is_in_found_nodes ) {
3805         if ( node.isCollapse() ) {
3806             return;
3807         }
3808         // if this node should be highlighted, do so
3809         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
3810             g.setColor( getTreeColorSet().getFoundColor() );
3811             drawOval( x - 8, y - 8, 16, 16, g );
3812             drawOval( x - 9, y - 8, 17, 17, g );
3813             drawOval( x - 9, y - 9, 18, 18, g );
3814         }
3815         if ( is_in_found_nodes ) {
3816             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
3817         }
3818         else {
3819             Color outline_color = null;
3820             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3821                 outline_color = Color.BLACK;
3822             }
3823             else if ( getControlPanel().isEvents() && TreePanelUtil.isHasAssignedEvent( node ) ) {
3824                 final Event event = node.getNodeData().getEvent();
3825                 if ( event.isDuplication() ) {
3826                     outline_color = getTreeColorSet().getDuplicationBoxColor();
3827                 }
3828                 else if ( event.isSpeciation() ) {
3829                     outline_color = getTreeColorSet().getSpecBoxColor();
3830                 }
3831                 else if ( event.isSpeciationOrDuplication() ) {
3832                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
3833                 }
3834             }
3835             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
3836                 outline_color = getTaxonomyBasedColor( node );
3837             }
3838             else {
3839                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
3840                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
3841                     outline_color = getTreeColorSet().getBranchColorForPdf();
3842                 }
3843             }
3844             final int box_size = getOptions().getDefaultNodeShapeSize();
3845             final int half_box_size = box_size / 2;
3846             if ( ( getOptions().isShowDefaultNodeShapesExternal() && node.isExternal() )
3847                     || ( getOptions().isShowDefaultNodeShapesInternal() && node.isInternal() )
3848                     || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
3849                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
3850                     if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3851                         drawOvalGradient( x - half_box_size,
3852                                           y - half_box_size,
3853                                           box_size,
3854                                           box_size,
3855                                           g,
3856                                           to_pdf ? Color.WHITE : outline_color,
3857                                           to_pdf ? outline_color : getBackground(),
3858                                           outline_color );
3859                     }
3860                     else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
3861                         Color background = getBackground();
3862                         if ( to_pdf ) {
3863                             background = Color.WHITE;
3864                         }
3865                         drawOvalGradient( x - half_box_size,
3866                                           y - half_box_size,
3867                                           box_size,
3868                                           box_size,
3869                                           g,
3870                                           background,
3871                                           background,
3872                                           outline_color );
3873                     }
3874                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3875                         g.setColor( outline_color );
3876                         drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
3877                     }
3878                 }
3879                 else if ( getOptions().getDefaultNodeShape() == NodeVisualization.NodeShape.RECTANGLE ) {
3880                     if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.GRADIENT ) {
3881                         drawRectGradient( x - half_box_size,
3882                                           y - half_box_size,
3883                                           box_size,
3884                                           box_size,
3885                                           g,
3886                                           to_pdf ? Color.WHITE : outline_color,
3887                                           to_pdf ? outline_color : getBackground(),
3888                                           outline_color );
3889                     }
3890                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
3891                         Color background = getBackground();
3892                         if ( to_pdf ) {
3893                             background = Color.WHITE;
3894                         }
3895                         drawRectGradient( x - half_box_size,
3896                                           y - half_box_size,
3897                                           box_size,
3898                                           box_size,
3899                                           g,
3900                                           background,
3901                                           background,
3902                                           outline_color );
3903                     }
3904                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3905                         g.setColor( outline_color );
3906                         drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
3907                     }
3908                 }
3909             }
3910         }
3911     }
3912
3913     final private void paintNodeData( final Graphics2D g,
3914                                       final PhylogenyNode node,
3915                                       final boolean to_graphics_file,
3916                                       final boolean to_pdf,
3917                                       final boolean is_in_found_nodes ) {
3918         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
3919             return;
3920         }
3921         if ( getOptions().isShowBranchLengthValues()
3922                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3923                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3924                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
3925             paintBranchLength( g, node, to_pdf, to_graphics_file );
3926         }
3927         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
3928             return;
3929         }
3930         _sb.setLength( 0 );
3931         int x = 0;
3932         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
3933         if ( getControlPanel().isShowTaxonomyImages()
3934                 && ( getImageMap() != null )
3935                 && !getImageMap().isEmpty()
3936                 && node.getNodeData().isHasTaxonomy()
3937                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
3938                         .getUris().isEmpty() ) ) {
3939             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
3940         }
3941         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3942                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
3943             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
3944         }
3945         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3946             g.setColor( Color.BLACK );
3947         }
3948         else if ( is_in_found_nodes ) {
3949             g.setColor( getTreeColorSet().getFoundColor() );
3950         }
3951         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3952             g.setColor( getTaxonomyBasedColor( node ) );
3953         }
3954         else if ( getControlPanel().isColorAccordingToAnnotation()
3955                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
3956                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
3957             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
3958         }
3959         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3960                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3961             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3962         }
3963         else if ( to_pdf ) {
3964             g.setColor( Color.BLACK );
3965         }
3966         else {
3967             g.setColor( getTreeColorSet().getSequenceColor() );
3968         }
3969         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3970             if ( _sb.length() > 0 ) {
3971                 _sb.setLength( 0 );
3972                 _sb.append( "(" );
3973                 _sb.append( node.getAllExternalDescendants().size() );
3974                 _sb.append( ")" );
3975             }
3976         }
3977         else {
3978             _sb.setLength( 0 );
3979         }
3980         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3981             if ( _sb.length() > 0 ) {
3982                 _sb.append( " " );
3983             }
3984             _sb.append( node.getName() );
3985         }
3986         if ( node.getNodeData().isHasSequence() ) {
3987             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3988                 if ( _sb.length() > 0 ) {
3989                     _sb.append( " " );
3990                 }
3991                 _sb.append( node.getNodeData().getSequence().getSymbol() );
3992             }
3993             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3994                 if ( _sb.length() > 0 ) {
3995                     _sb.append( " " );
3996                 }
3997                 _sb.append( node.getNodeData().getSequence().getName() );
3998             }
3999             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4000                 if ( _sb.length() > 0 ) {
4001                     _sb.append( " " );
4002                 }
4003                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4004                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4005                     _sb.append( ":" );
4006                 }
4007                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4008             }
4009         }
4010         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
4011             if ( _sb.length() > 0 ) {
4012                 _sb.append( " " );
4013             }
4014             _sb.append( propertiesToString( node ) );
4015         }
4016         g.setFont( getTreeFontSet().getLargeFont() );
4017         if ( is_in_found_nodes ) {
4018             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4019         }
4020         double down_shift_factor = 3.0;
4021         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
4022             down_shift_factor = 1;
4023         }
4024         final double pos_x = node.getXcoord() + x + 2 + half_box_size;
4025         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
4026         final String sb_str = _sb.toString();
4027         // GUILHEM_BEG ______________
4028         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
4029                 && ( _query_sequence != null ) ) {
4030             int nodeTextBoundsWidth = 0;
4031             if ( sb_str.length() > 0 ) {
4032                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
4033                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
4034             }
4035             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
4036                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
4037                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
4038                     g.setColor( getTreeColorSet().getBackgroundColor() );
4039                 }
4040             }
4041             else {
4042                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
4043                 for( final SequenceRelation seqRelation : seqRelations ) {
4044                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
4045                             .getRef1().isEqual( _query_sequence ) )
4046                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
4047                                     .getSelectedItem() );
4048                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
4049                         final double linePosX = node.getXcoord() + 2 + half_box_size;
4050                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
4051                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
4052                                 + ")";
4053                         if ( sConfidence != null ) {
4054                             double confidenceX = pos_x;
4055                             if ( sb_str.length() > 0 ) {
4056                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
4057                                         + CONFIDENCE_LEFT_MARGIN;
4058                             }
4059                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
4060                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
4061                                         .getBounds().getWidth();
4062                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
4063                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
4064                             }
4065                         }
4066                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
4067                         {
4068                             if ( nodeTextBoundsWidth == 0 ) {
4069                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
4070                             }
4071                             else {
4072                                 nodeTextBoundsWidth += 2;
4073                             }
4074                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
4075                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
4076                             break;
4077                         }
4078                     }
4079                 }
4080             }
4081         }
4082         if ( sb_str.length() > 0 ) {
4083             TreePanel.drawString( sb_str, pos_x, pos_y, g );
4084         }
4085         // GUILHEM_END _____________
4086         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
4087         // TODO FIXME need to check this one!
4088         //if ( _sb.length() > 0 ) {
4089         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
4090         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4091         //}
4092         // COMMENTED_OUT_BY_GUILHEM_END ________________
4093         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
4094                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4095                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4096             if ( _sb.length() > 0 ) {
4097                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
4098             }
4099             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
4100             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4101                 g.setColor( Color.BLACK );
4102             }
4103             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
4104                 g.setColor( calculateColorForAnnotation( ann ) );
4105             }
4106             final String ann_str = TreePanelUtil.createAnnotationString( ann, getOptions().isShowAnnotationRefSource() );
4107             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
4108                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4109             _sb.setLength( 0 );
4110             _sb.append( ann_str );
4111         }
4112         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4113                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
4114                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
4115             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
4116                     && node.getNodeData().isHasBinaryCharacters() ) {
4117                 if ( _sb.length() > 0 ) {
4118                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
4119                 }
4120                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4121                     g.setColor( Color.BLACK );
4122                 }
4123                 else {
4124                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
4125                 }
4126                 if ( getControlPanel().isShowBinaryCharacters() ) {
4127                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
4128                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
4129                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4130                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
4131                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
4132                             .getLostCharactersAsStringBuffer().toString() );
4133                 }
4134                 else {
4135                     TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
4136                                           node.getXcoord() + x + 4 + half_box_size,
4137                                           node.getYcoord()
4138                                                   + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
4139                                           g );
4140                     paintGainedAndLostCharacters( g, node, "+"
4141                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
4142                             + node.getNodeData().getBinaryCharacters().getLostCount() );
4143                 }
4144             }
4145         }
4146     }
4147
4148     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
4149                                                   final PhylogenyNode node,
4150                                                   final boolean to_pdf,
4151                                                   final boolean to_graphics_file,
4152                                                   final boolean radial_labels,
4153                                                   final double ur_angle,
4154                                                   final boolean is_in_found_nodes ) {
4155         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
4156             return;
4157         }
4158         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4159             g.setColor( Color.BLACK );
4160         }
4161         else if ( is_in_found_nodes ) {
4162             g.setColor( getTreeColorSet().getFoundColor() );
4163         }
4164         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4165             g.setColor( getTaxonomyBasedColor( node ) );
4166         }
4167         else if ( getControlPanel().isColorAccordingToAnnotation()
4168                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
4169                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
4170             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
4171         }
4172         else {
4173             g.setColor( getTreeColorSet().getSequenceColor() );
4174         }
4175         _sb.setLength( 0 );
4176         _sb.append( " " );
4177         if ( node.getNodeData().isHasTaxonomy()
4178                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4179                         .isShowTaxonomyCommonNames() ) ) {
4180             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4181             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4182                 _sb.append( taxonomy.getTaxonomyCode() );
4183                 _sb.append( " " );
4184             }
4185             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4186                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4187                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4188                     _sb.append( taxonomy.getScientificName() );
4189                     _sb.append( " (" );
4190                     _sb.append( taxonomy.getCommonName() );
4191                     _sb.append( ") " );
4192                 }
4193                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4194                     _sb.append( taxonomy.getScientificName() );
4195                     _sb.append( " " );
4196                 }
4197                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4198                     _sb.append( taxonomy.getCommonName() );
4199                     _sb.append( " " );
4200                 }
4201             }
4202             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4203                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4204                     _sb.append( taxonomy.getScientificName() );
4205                     _sb.append( " " );
4206                 }
4207             }
4208             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4209                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4210                     _sb.append( taxonomy.getCommonName() );
4211                     _sb.append( " " );
4212                 }
4213             }
4214         }
4215         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4216             _sb.append( " [" );
4217             _sb.append( node.getAllExternalDescendants().size() );
4218             _sb.append( "]" );
4219         }
4220         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
4221             if ( _sb.length() > 0 ) {
4222                 _sb.append( " " );
4223             }
4224             _sb.append( node.getName() );
4225         }
4226         if ( node.getNodeData().isHasSequence() ) {
4227             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4228                 if ( _sb.length() > 0 ) {
4229                     _sb.append( " " );
4230                 }
4231                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4232                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4233                     _sb.append( ":" );
4234                 }
4235                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4236             }
4237             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
4238                 if ( _sb.length() > 0 ) {
4239                     _sb.append( " " );
4240                 }
4241                 _sb.append( node.getNodeData().getSequence().getName() );
4242             }
4243         }
4244         g.setFont( getTreeFontSet().getLargeFont() );
4245         if ( is_in_found_nodes ) {
4246             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4247         }
4248         if ( _sb.length() > 1 ) {
4249             final String sb_str = _sb.toString();
4250             double m = 0;
4251             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4252                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
4253             }
4254             else {
4255                 m = ( float ) ( ur_angle % TWO_PI );
4256             }
4257             _at = g.getTransform();
4258             boolean need_to_reset = false;
4259             final float x_coord = node.getXcoord();
4260             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
4261             if ( radial_labels ) {
4262                 need_to_reset = true;
4263                 boolean left = false;
4264                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4265                     m -= PI;
4266                     left = true;
4267                 }
4268                 g.rotate( m, x_coord, node.getYcoord() );
4269                 if ( left ) {
4270                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
4271                 }
4272             }
4273             else {
4274                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4275                     need_to_reset = true;
4276                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
4277                 }
4278             }
4279             TreePanel.drawString( sb_str, x_coord, y_coord, g );
4280             if ( need_to_reset ) {
4281                 g.setTransform( _at );
4282             }
4283         }
4284     }
4285
4286     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
4287         if ( node.isCollapse() ) {
4288             if ( !node.isRoot() && !node.getParent().isCollapse() ) {
4289                 paintCollapsedNode( g, node, false, false, false );
4290             }
4291             return;
4292         }
4293         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4294             g.setColor( getTreeColorSet().getFoundColor() );
4295             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
4296         }
4297         float new_x = 0;
4298         if ( !node.isExternal() && !node.isCollapse() ) {
4299             boolean first_child = true;
4300             float y2 = 0.0f;
4301             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4302             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4303                 final PhylogenyNode child_node = node.getChildNode( i );
4304                 int factor_x;
4305                 if ( !isUniformBranchLengthsForCladogram() ) {
4306                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4307                 }
4308                 else {
4309                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4310                 }
4311                 if ( first_child ) {
4312                     first_child = false;
4313                     y2 = node.getYSecondary()
4314                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
4315                                     .getNumberOfExternalNodes() ) );
4316                 }
4317                 else {
4318                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4319                 }
4320                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
4321                 new_x = x2 + node.getXSecondary();
4322                 final float diff_y = node.getYSecondary() - y2;
4323                 final float diff_x = node.getXSecondary() - new_x;
4324                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
4325                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
4326                 }
4327                 child_node.setXSecondary( new_x );
4328                 child_node.setYSecondary( y2 );
4329                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4330             }
4331         }
4332     }
4333
4334     final private void paintNodeRectangular( final Graphics2D g,
4335                                              final PhylogenyNode node,
4336                                              final boolean to_pdf,
4337                                              final boolean dynamically_hide,
4338                                              final int dynamic_hiding_factor,
4339                                              final boolean to_graphics_file ) {
4340         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
4341         if ( node.isCollapse() ) {
4342             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) ) {
4343                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4344             }
4345             return;
4346         }
4347         if ( node.isExternal() ) {
4348             ++_external_node_index;
4349         }
4350         // Confidence values
4351         if ( getControlPanel().isShowConfidenceValues()
4352                 && !node.isExternal()
4353                 && !node.isRoot()
4354                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
4355                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4356                 && node.getBranchData().isHasConfidences() ) {
4357             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
4358         }
4359         // Draw a line to root:
4360         if ( node.isRoot() && _phylogeny.isRooted() ) {
4361             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
4362         }
4363         float new_x = 0;
4364         float new_x_min = Float.MAX_VALUE;
4365         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
4366         float min_dist = 1.5f;
4367         if ( !disallow_shortcutting ) {
4368             //   System.out.println( dynamic_hiding_factor );
4369             if ( dynamic_hiding_factor > 4000 ) {
4370                 min_dist = 4;
4371             }
4372             else if ( dynamic_hiding_factor > 1000 ) {
4373                 min_dist = 3;
4374             }
4375             else if ( dynamic_hiding_factor > 100 ) {
4376                 min_dist = 2;
4377             }
4378         }
4379         if ( !node.isExternal() && !node.isCollapse() ) {
4380             boolean first_child = true;
4381             float y2 = 0.0f;
4382             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4383             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4384                 final PhylogenyNode child_node = node.getChildNode( i );
4385                 int factor_x;
4386                 if ( !isUniformBranchLengthsForCladogram() ) {
4387                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4388                 }
4389                 else {
4390                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4391                 }
4392                 if ( first_child ) {
4393                     first_child = false;
4394                     y2 = node.getYcoord()
4395                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
4396                 }
4397                 else {
4398                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
4399                 }
4400                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
4401                 new_x = x2 + node.getXcoord();
4402                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
4403                     new_x_min = x2;
4404                 }
4405                 final float diff_y = node.getYcoord() - y2;
4406                 final float diff_x = node.getXcoord() - new_x;
4407                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
4408                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
4409                     paintBranchRectangular( g,
4410                                             node.getXcoord(),
4411                                             new_x,
4412                                             node.getYcoord(),
4413                                             y2,
4414                                             child_node,
4415                                             to_pdf,
4416                                             to_graphics_file );
4417                 }
4418                 child_node.setXcoord( new_x );
4419                 child_node.setYcoord( y2 );
4420                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
4421             }
4422             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node )
4423                     || isInCurrentExternalNodes( node ) );
4424         }
4425         if ( dynamically_hide
4426                 && !is_in_found_nodes
4427                 && ( ( node.isExternal() && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) ) || ( !node
4428                         .isExternal() && ( ( new_x_min < 20 ) || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getTreeFontSet()._fm_large
4429                         .getHeight() ) ) ) ) ) {
4430             return;
4431         }
4432         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4433         paintNodeWithRenderableData( g, node, to_graphics_file, to_pdf );
4434     }
4435
4436     final private void paintNodeWithRenderableData( final Graphics2D g,
4437                                                     final PhylogenyNode node,
4438                                                     final boolean to_graphics_file,
4439                                                     final boolean to_pdf ) {
4440         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
4441             return;
4442         }
4443         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
4444             return;
4445         }
4446         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4447                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4448             RenderableDomainArchitecture rds = null;
4449             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
4450                 try {
4451                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
4452                 }
4453                 catch ( final ClassCastException cce ) {
4454                     cce.printStackTrace();
4455                 }
4456                 if ( rds != null ) {
4457                     rds.setRenderingHeight( 6 );
4458                     int x = 0;
4459                     if ( node.getNodeData().isHasTaxonomy() ) {
4460                         if ( getControlPanel().isShowTaxonomyCode()
4461                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
4462                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4463                                     .getTaxonomyCode()
4464                                     + " " );
4465                         }
4466                         if ( getControlPanel().isShowTaxonomyScientificNames()
4467                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) ) {
4468                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4469                                     .getScientificName()
4470                                     + " " );
4471                         }
4472                         if ( getControlPanel().isShowTaxonomyCommonNames()
4473                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) ) {
4474                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4475                                     .getCommonName()
4476                                     + " " );
4477                         }
4478                     }
4479                     if ( node.getNodeData().isHasSequence() ) {
4480                         if ( getControlPanel().isShowGeneNames()
4481                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) ) {
4482                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName()
4483                                     + " " );
4484                         }
4485                         if ( getControlPanel().isShowGeneSymbols()
4486                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) ) {
4487                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getSymbol()
4488                                     + " " );
4489                         }
4490                         if ( getControlPanel().isShowSequenceAcc()
4491                                 && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4492                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence()
4493                                     .getAccession().toString()
4494                                     + " " );
4495                         }
4496                         if ( getControlPanel().isShowAnnotation()
4497                                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4498                                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4499                             x += getTreeFontSet()._fm_large.stringWidth( TreePanelUtil.createAnnotationString( node
4500                                     .getNodeData().getSequence().getAnnotations(), getOptions()
4501                                     .isShowAnnotationRefSource() )
4502                                     + " " );
4503                         }
4504                     }
4505                     if ( getControlPanel().isShowNodeNames() && !ForesterUtil.isEmpty( node.getName() ) ) {
4506                         x += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
4507                     }
4508                     rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
4509                 }
4510             }
4511         }
4512         //////////////
4513         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4514                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
4515             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
4516                                                                          getStatisticsForExpressionValues(),
4517                                                                          getConfiguration() );
4518             if ( rv != null ) {
4519                 int x = 0;
4520                 PhylogenyNode my_node = node;
4521                 if ( !getControlPanel().isDrawPhylogram() ) {
4522                     my_node = getPhylogeny().getFirstExternalNode();
4523                 }
4524                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
4525                     x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
4526                 }
4527                 if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
4528                     x += getTreeFontSet()._fm_large.stringWidth( my_node.getName() + " " );
4529                 }
4530                 rv.render( my_node.getXcoord() + x, node.getYcoord() - 5, g, this, to_pdf );
4531             }
4532         }
4533         //////////////
4534     }
4535
4536     final private void paintOvRectangle( final Graphics2D g ) {
4537         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
4538         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
4539         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
4540         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
4541         final float width = getOvMaxWidth() / w_ratio;
4542         final float height = getOvMaxHeight() / h_ratio;
4543         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
4544         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
4545         g.setColor( getTreeColorSet().getFoundColor() );
4546         getOvRectangle().setRect( x, y, width, height );
4547         final Stroke s = g.getStroke();
4548         g.setStroke( STROKE_1 );
4549         if ( ( width < 6 ) && ( height < 6 ) ) {
4550             drawRectFilled( x, y, 6, 6, g );
4551             getOvVirtualRectangle().setRect( x, y, 6, 6 );
4552         }
4553         else if ( width < 6 ) {
4554             drawRectFilled( x, y, 6, height, g );
4555             getOvVirtualRectangle().setRect( x, y, 6, height );
4556         }
4557         else if ( height < 6 ) {
4558             drawRectFilled( x, y, width, 6, g );
4559             getOvVirtualRectangle().setRect( x, y, width, 6 );
4560         }
4561         else {
4562             drawRect( x, y, width, height, g );
4563             if ( isInOvRect() ) {
4564                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
4565             }
4566             getOvVirtualRectangle().setRect( x, y, width, height );
4567         }
4568         g.setStroke( s );
4569     }
4570
4571     final private void paintPhylogenyLite( final Graphics2D g ) {
4572         _phylogeny
4573                 .getRoot()
4574                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
4575                         .getWidth() ) ) ) );
4576         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
4577         final Stroke s = g.getStroke();
4578         g.setStroke( STROKE_05 );
4579         for( final PhylogenyNode element : _nodes_in_preorder ) {
4580             paintNodeLite( g, element );
4581         }
4582         g.setStroke( s );
4583         paintOvRectangle( g );
4584     }
4585
4586     /**
4587      * Paint the root branch. (Differs from others because it will always be a
4588      * single horizontal line).
4589      * @param to_graphics_file 
4590      * 
4591      * @return new x1 value
4592      */
4593     final private void paintRootBranch( final Graphics2D g,
4594                                         final float x1,
4595                                         final float y1,
4596                                         final PhylogenyNode root,
4597                                         final boolean to_pdf,
4598                                         final boolean to_graphics_file ) {
4599         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
4600         float d = getXdistance();
4601         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
4602             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
4603         }
4604         if ( d < MIN_ROOT_LENGTH ) {
4605             d = MIN_ROOT_LENGTH;
4606         }
4607         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
4608             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
4609         }
4610         else {
4611             final double w = PhylogenyMethods.getBranchWidthValue( root );
4612             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
4613         }
4614         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file, isInFoundNodes( root ) );
4615     }
4616
4617     final private void paintScale( final Graphics2D g,
4618                                    int x1,
4619                                    int y1,
4620                                    final boolean to_pdf,
4621                                    final boolean to_graphics_file ) {
4622         x1 += MOVE;
4623         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
4624         y1 -= 12;
4625         final int y2 = y1 - 8;
4626         final int y3 = y1 - 4;
4627         g.setFont( getTreeFontSet().getSmallFont() );
4628         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4629             g.setColor( Color.BLACK );
4630         }
4631         else {
4632             g.setColor( getTreeColorSet().getBranchLengthColor() );
4633         }
4634         final Stroke s = g.getStroke();
4635         g.setStroke( STROKE_1 );
4636         drawLine( x1, y1, x1, y2, g );
4637         drawLine( x2, y1, x2, y2, g );
4638         drawLine( x1, y3, x2, y3, g );
4639         if ( getScaleLabel() != null ) {
4640             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
4641         }
4642         g.setStroke( s );
4643     }
4644
4645     final private int paintTaxonomy( final Graphics2D g,
4646                                      final PhylogenyNode node,
4647                                      final boolean is_in_found_nodes,
4648                                      final boolean to_pdf,
4649                                      final boolean to_graphics_file,
4650                                      final double x_shift ) {
4651         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4652         g.setFont( getTreeFontSet().getLargeItalicFont() );
4653         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4654             g.setColor( Color.BLACK );
4655         }
4656         else if ( is_in_found_nodes ) {
4657             g.setFont( getTreeFontSet().getLargeItalicFont().deriveFont( TreeFontSet.BOLD_AND_ITALIC ) );
4658             g.setColor( getTreeColorSet().getFoundColor() );
4659         }
4660         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4661             g.setColor( getTaxonomyBasedColor( node ) );
4662         }
4663         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
4664                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
4665             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
4666         }
4667         else if ( to_pdf ) {
4668             g.setColor( Color.BLACK );
4669         }
4670         else {
4671             g.setColor( getTreeColorSet().getTaxonomyColor() );
4672         }
4673         final double start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
4674         final double start_y = node.getYcoord()
4675                 + ( getTreeFontSet()._fm_large.getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0 ) );
4676         _sb.setLength( 0 );
4677         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4678             _sb.append( taxonomy.getTaxonomyCode() );
4679             _sb.append( " " );
4680         }
4681         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4682             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4683                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4684                 if ( getOptions().isAbbreviateScientificTaxonNames()
4685                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4686                     abbreviateScientificName( taxonomy.getScientificName() );
4687                 }
4688                 else {
4689                     _sb.append( taxonomy.getScientificName() );
4690                 }
4691                 _sb.append( " (" );
4692                 _sb.append( taxonomy.getCommonName() );
4693                 _sb.append( ") " );
4694             }
4695             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4696                 if ( getOptions().isAbbreviateScientificTaxonNames()
4697                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4698                     abbreviateScientificName( taxonomy.getScientificName() );
4699                 }
4700                 else {
4701                     _sb.append( taxonomy.getScientificName() );
4702                 }
4703                 _sb.append( " " );
4704             }
4705             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4706                 _sb.append( taxonomy.getCommonName() );
4707                 _sb.append( " " );
4708             }
4709         }
4710         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4711             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4712                 if ( getOptions().isAbbreviateScientificTaxonNames()
4713                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4714                     abbreviateScientificName( taxonomy.getScientificName() );
4715                 }
4716                 else {
4717                     _sb.append( taxonomy.getScientificName() );
4718                 }
4719                 _sb.append( " " );
4720             }
4721         }
4722         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4723             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4724                 _sb.append( taxonomy.getCommonName() );
4725                 _sb.append( " " );
4726             }
4727         }
4728         final String label = _sb.toString();
4729         /* GUILHEM_BEG */
4730         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
4731                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
4732             // invert font color and background color to show that this is the query sequence
4733             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
4734                                                                                                           false,
4735                                                                                                           false ) )
4736                     .getBounds();
4737             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
4738             g.setColor( getTreeColorSet().getBackgroundColor() );
4739         }
4740         /* GUILHEM_END */
4741         TreePanel.drawString( label, start_x, start_y, g );
4742         if ( is_in_found_nodes ) {
4743             return getTreeFontSet()._fm_large_italic_bold.stringWidth( label );
4744         }
4745         else {
4746             return getTreeFontSet()._fm_large_italic.stringWidth( label );
4747         }
4748     }
4749
4750     final private void paintUnrooted( final PhylogenyNode n,
4751                                       final double low_angle,
4752                                       final double high_angle,
4753                                       final boolean radial_labels,
4754                                       final Graphics2D g,
4755                                       final boolean to_pdf,
4756                                       final boolean to_graphics_file ) {
4757         if ( n.isRoot() ) {
4758             n.setXcoord( getWidth() / 2 );
4759             n.setYcoord( getHeight() / 2 );
4760         }
4761         if ( n.isExternal() ) {
4762             paintNodeDataUnrootedCirc( g,
4763                                        n,
4764                                        to_pdf,
4765                                        to_graphics_file,
4766                                        radial_labels,
4767                                        ( high_angle + low_angle ) / 2,
4768                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
4769             return;
4770         }
4771         final float num_enclosed = n.getNumberOfExternalNodes();
4772         final float x = n.getXcoord();
4773         final float y = n.getYcoord();
4774         double current_angle = low_angle;
4775         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4776         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4777         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4778         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4779         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4780             final PhylogenyNode desc = n.getChildNode( i );
4781             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4782             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4783             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4784             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4785             //     continue;
4786             // }
4787             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4788             //    continue;
4789             //}
4790             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4791             //    continue;
4792             // }
4793             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4794             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4795             float length;
4796             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4797                 if ( desc.getDistanceToParent() < 0 ) {
4798                     length = 0;
4799                 }
4800                 else {
4801                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4802                 }
4803             }
4804             else {
4805                 length = getUrtFactor();
4806             }
4807             final double mid_angle = current_angle + ( arc_size / 2 );
4808             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
4809             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
4810             desc.setXcoord( new_x );
4811             desc.setYcoord( new_y );
4812             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
4813             current_angle += arc_size;
4814             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
4815             drawLine( x, y, new_x, new_y, g );
4816             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file, isInFoundNodes( desc )
4817                     || isInCurrentExternalNodes( desc ) );
4818         }
4819         if ( n.isRoot() ) {
4820             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file, isInFoundNodes( n ) );
4821         }
4822     }
4823
4824     final private void paintUnrootedLite( final PhylogenyNode n,
4825                                           final double low_angle,
4826                                           final double high_angle,
4827                                           final Graphics2D g,
4828                                           final float urt_ov_factor ) {
4829         if ( n.isRoot() ) {
4830             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
4831             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
4832             n.setXSecondary( x_pos );
4833             n.setYSecondary( y_pos );
4834         }
4835         if ( n.isExternal() ) {
4836             return;
4837         }
4838         final float num_enclosed = n.getNumberOfExternalNodes();
4839         final float x = n.getXSecondary();
4840         final float y = n.getYSecondary();
4841         double current_angle = low_angle;
4842         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4843             final PhylogenyNode desc = n.getChildNode( i );
4844             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4845             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4846             float length;
4847             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4848                 if ( desc.getDistanceToParent() < 0 ) {
4849                     length = 0;
4850                 }
4851                 else {
4852                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
4853                 }
4854             }
4855             else {
4856                 length = urt_ov_factor;
4857             }
4858             final double mid_angle = current_angle + ( arc_size / 2 );
4859             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
4860             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
4861             desc.setXSecondary( new_x );
4862             desc.setYSecondary( new_y );
4863             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
4864                 g.setColor( getTreeColorSet().getFoundColor() );
4865                 drawRectFilled( desc.getXSecondary() - 1, desc.getYSecondary() - 1, 3, 3, g );
4866                 g.setColor( getTreeColorSet().getOvColor() );
4867             }
4868             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
4869             current_angle += arc_size;
4870             drawLine( x, y, new_x, new_y, g );
4871         }
4872     }
4873
4874     final private void pasteSubtree( final PhylogenyNode node ) {
4875         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4876             errorMessageNoCutCopyPasteInUnrootedDisplay();
4877             return;
4878         }
4879         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
4880             JOptionPane.showMessageDialog( this,
4881                                            "No tree in buffer (need to copy or cut a subtree first)",
4882                                            "Attempt to paste with empty buffer",
4883                                            JOptionPane.ERROR_MESSAGE );
4884             return;
4885         }
4886         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
4887         final Object[] options = { "As sibling", "As descendant", "Cancel" };
4888         final int r = JOptionPane.showOptionDialog( this,
4889                                                     "How to paste subtree" + label + "?",
4890                                                     "Paste Subtree",
4891                                                     JOptionPane.CLOSED_OPTION,
4892                                                     JOptionPane.QUESTION_MESSAGE,
4893                                                     null,
4894                                                     options,
4895                                                     options[ 2 ] );
4896         boolean paste_as_sibling = true;
4897         if ( r == 1 ) {
4898             paste_as_sibling = false;
4899         }
4900         else if ( r != 0 ) {
4901             return;
4902         }
4903         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
4904         buffer_phy.setAllNodesToNotCollapse();
4905         PhylogenyMethods.preOrderReId( buffer_phy );
4906         buffer_phy.setRooted( true );
4907         boolean need_to_show_whole = false;
4908         if ( paste_as_sibling ) {
4909             if ( node.isRoot() ) {
4910                 JOptionPane.showMessageDialog( this,
4911                                                "Cannot paste sibling to root",
4912                                                "Attempt to paste sibling to root",
4913                                                JOptionPane.ERROR_MESSAGE );
4914                 return;
4915             }
4916             buffer_phy.addAsSibling( node );
4917         }
4918         else {
4919             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
4920                 need_to_show_whole = true;
4921                 _phylogeny = buffer_phy;
4922             }
4923             else {
4924                 buffer_phy.addAsChild( node );
4925             }
4926         }
4927         if ( getCopiedAndPastedNodes() == null ) {
4928             setCopiedAndPastedNodes( new HashSet<Long>() );
4929         }
4930         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
4931         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
4932         for( final PhylogenyNode n : nodes ) {
4933             node_ids.add( n.getId() );
4934         }
4935         node_ids.add( node.getId() );
4936         getCopiedAndPastedNodes().addAll( node_ids );
4937         setNodeInPreorderToNull();
4938         _phylogeny.externalNodesHaveChanged();
4939         _phylogeny.clearHashIdToNodeMap();
4940         _phylogeny.recalculateNumberOfExternalDescendants( true );
4941         resetNodeIdToDistToLeafMap();
4942         setEdited( true );
4943         if ( need_to_show_whole ) {
4944             getControlPanel().showWhole();
4945         }
4946         repaint();
4947     }
4948
4949     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
4950         final PropertiesMap properties = node.getNodeData().getProperties();
4951         final StringBuffer sb = new StringBuffer();
4952         boolean first = true;
4953         for( final String ref : properties.getPropertyRefs() ) {
4954             if ( first ) {
4955                 first = false;
4956             }
4957             else {
4958                 sb.append( " " );
4959             }
4960             final Property p = properties.getProperty( ref );
4961             sb.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
4962             sb.append( "=" );
4963             sb.append( p.getValue() );
4964             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
4965                 sb.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
4966             }
4967         }
4968         return sb;
4969     }
4970
4971     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
4972         getMainPanel().setCopiedAndPastedNodes( nodeIds );
4973     }
4974
4975     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
4976         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
4977     }
4978
4979     final private void setInOv( final boolean in_ov ) {
4980         _in_ov = in_ov;
4981     }
4982
4983     final private void setOvMaxHeight( final float ov_max_height ) {
4984         _ov_max_height = ov_max_height;
4985     }
4986
4987     final private void setOvMaxWidth( final float ov_max_width ) {
4988         _ov_max_width = ov_max_width;
4989     }
4990
4991     final private void setOvXcorrectionFactor( final float f ) {
4992         _ov_x_correction_factor = f;
4993     }
4994
4995     final private void setOvXDistance( final float ov_x_distance ) {
4996         _ov_x_distance = ov_x_distance;
4997     }
4998
4999     final private void setOvXPosition( final int ov_x_position ) {
5000         _ov_x_position = ov_x_position;
5001     }
5002
5003     final private void setOvYDistance( final float ov_y_distance ) {
5004         _ov_y_distance = ov_y_distance;
5005     }
5006
5007     final private void setOvYPosition( final int ov_y_position ) {
5008         _ov_y_position = ov_y_position;
5009     }
5010
5011     final private void setOvYStart( final int ov_y_start ) {
5012         _ov_y_start = ov_y_start;
5013     }
5014
5015     final private void setScaleDistance( final double scale_distance ) {
5016         _scale_distance = scale_distance;
5017     }
5018
5019     final private void setScaleLabel( final String scale_label ) {
5020         _scale_label = scale_label;
5021     }
5022
5023     private final void setupStroke( final Graphics2D g ) {
5024         if ( getYdistance() < 0.001 ) {
5025             g.setStroke( STROKE_005 );
5026         }
5027         else if ( getYdistance() < 0.01 ) {
5028             g.setStroke( STROKE_01 );
5029         }
5030         else if ( getYdistance() < 0.5 ) {
5031             g.setStroke( STROKE_025 );
5032         }
5033         else if ( getYdistance() < 1 ) {
5034             g.setStroke( STROKE_05 );
5035         }
5036         else if ( getYdistance() < 2 ) {
5037             g.setStroke( STROKE_075 );
5038         }
5039         else if ( getYdistance() < 20 ) {
5040             g.setStroke( STROKE_1 );
5041         }
5042         else {
5043             g.setStroke( STROKE_2 );
5044         }
5045     }
5046
5047     final private void setUpUrtFactor() {
5048         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
5049                 : getVisibleRect().height;
5050         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5051             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
5052         }
5053         else {
5054             final int max_depth = _circ_max_depth;
5055             if ( max_depth > 0 ) {
5056                 setUrtFactor( d / ( 2 * max_depth ) );
5057             }
5058             else {
5059                 setUrtFactor( d / 2 );
5060             }
5061         }
5062         setUrtFactorOv( getUrtFactor() );
5063     }
5064
5065     final private void setUrtFactor( final float urt_factor ) {
5066         _urt_factor = urt_factor;
5067     }
5068
5069     final private void setUrtFactorOv( final float urt_factor_ov ) {
5070         _urt_factor_ov = urt_factor_ov;
5071     }
5072
5073     private void showExtDescNodeData( final PhylogenyNode node ) {
5074         final List<String> data = new ArrayList<String>();
5075         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
5076         if ( ( getFoundNodes() != null ) && !getFoundNodes().isEmpty() ) {
5077             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
5078                 if ( !nodes.contains( n ) ) {
5079                     nodes.add( n );
5080                 }
5081             }
5082         }
5083         for( final PhylogenyNode n : nodes ) {
5084             switch ( getOptions().getExtDescNodeDataToReturn() ) {
5085                 case NODE_NAME:
5086                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5087                         data.add( n.getName() );
5088                     }
5089                     break;
5090                 case SEQUENCE_NAME:
5091                     if ( n.getNodeData().isHasSequence()
5092                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5093                         data.add( n.getNodeData().getSequence().getName() );
5094                     }
5095                     break;
5096                 case SEQUENCE_SYMBOL:
5097                     if ( n.getNodeData().isHasSequence()
5098                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5099                         data.add( n.getNodeData().getSequence().getSymbol() );
5100                     }
5101                     break;
5102                 case SEQUENCE_MOL_SEQ:
5103                     if ( n.getNodeData().isHasSequence()
5104                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5105                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
5106                     }
5107                     break;
5108                 case SEQUENCE_MOL_SEQ_FASTA:
5109                     final StringBuilder sb = new StringBuilder();
5110                     if ( n.getNodeData().isHasSequence()
5111                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5112                         final StringBuilder ann = new StringBuilder();
5113                         if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5114                             ann.append( n.getName() );
5115                             ann.append( "|" );
5116                         }
5117                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5118                             ann.append( "SYM=" );
5119                             ann.append( n.getNodeData().getSequence().getSymbol() );
5120                             ann.append( "|" );
5121                         }
5122                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5123                             ann.append( "NAME=" );
5124                             ann.append( n.getNodeData().getSequence().getName() );
5125                             ann.append( "|" );
5126                         }
5127                         if ( n.getNodeData().getSequence().getAccession() != null ) {
5128                             ann.append( "ACC=" );
5129                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
5130                             ann.append( "|" );
5131                         }
5132                         if ( n.getNodeData().isHasTaxonomy() ) {
5133                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5134                                 ann.append( "TAXID=" );
5135                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5136                                 ann.append( "|" );
5137                             }
5138                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5139                                 ann.append( "SN=" );
5140                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
5141                                 ann.append( "|" );
5142                             }
5143                         }
5144                         String ann_str;
5145                         if ( ann.charAt( ann.length() - 1 ) == '|' ) {
5146                             ann_str = ann.substring( 0, ann.length() - 1 );
5147                         }
5148                         else {
5149                             ann_str = ann.toString();
5150                         }
5151                         sb.append( SequenceWriter.toFasta( ann_str, n.getNodeData().getSequence()
5152                                 .getMolecularSequence(), 60 ) );
5153                         data.add( sb.toString() );
5154                     }
5155                     break;
5156                 case SEQUENCE_ACC:
5157                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
5158                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
5159                         data.add( n.getNodeData().getSequence().getAccession().toString() );
5160                     }
5161                     break;
5162                 case TAXONOMY_SCIENTIFIC_NAME:
5163                     if ( n.getNodeData().isHasTaxonomy()
5164                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5165                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
5166                     }
5167                     break;
5168                 case TAXONOMY_COMM0N_NAME:
5169                     if ( n.getNodeData().isHasTaxonomy()
5170                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) ) {
5171                         data.add( n.getNodeData().getTaxonomy().getCommonName() );
5172                     }
5173                     break;
5174                 case TAXONOMY_CODE:
5175                     if ( n.getNodeData().isHasTaxonomy()
5176                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5177                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5178                     }
5179                     break;
5180                 case UNKNOWN:
5181                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
5182                     break;
5183                 default:
5184                     throw new IllegalArgumentException( "unknown data element: "
5185                             + getOptions().getExtDescNodeDataToReturn() );
5186             }
5187         } // for loop
5188         final StringBuilder sb = new StringBuilder();
5189         final int size = TreePanelUtil.makeSB( data, getOptions(), sb );
5190         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
5191                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
5192             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5193                 System.out.println( sb );
5194             }
5195             if ( sb.length() < 1 ) {
5196                 clearCurrentExternalNodesDataBuffer();
5197             }
5198             else {
5199                 setCurrentExternalNodesDataBuffer( sb );
5200             }
5201         }
5202         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5203             if ( sb.length() < 1 ) {
5204                 TreePanelUtil.showInformationMessage( this, "No Appropriate Data (" + obtainTitleForExtDescNodeData()
5205                         + ")", "Descendants of selected node do not contain selected data" );
5206                 clearCurrentExternalNodesDataBuffer();
5207             }
5208             else {
5209                 setCurrentExternalNodesDataBuffer( sb );
5210                 String title;
5211                 if ( ( getFoundNodes() != null ) && !getFoundNodes().isEmpty() ) {
5212                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5213                             : obtainTitleForExtDescNodeData() )
5214                             + " for "
5215                             + data.size()
5216                             + " nodes, unique entries: "
5217                             + size;
5218                 }
5219                 else {
5220                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5221                             : obtainTitleForExtDescNodeData() )
5222                             + " for "
5223                             + data.size()
5224                             + "/"
5225                             + node.getNumberOfExternalNodes()
5226                             + " external descendats of node "
5227                             + node
5228                             + ", unique entries: " + size;
5229                 }
5230                 final String s = sb.toString().trim();
5231                 if ( getMainPanel().getMainFrame() == null ) {
5232                     // Must be "E" applet version.
5233                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5234                     ae.showTextFrame( s, title );
5235                 }
5236                 else {
5237                     getMainPanel().getMainFrame().showTextFrame( s, title );
5238                 }
5239             }
5240         }
5241     }
5242
5243     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5244         try {
5245             if ( ( node.getName().length() > 0 )
5246                     || ( node.getNodeData().isHasTaxonomy() && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData()
5247                             .getTaxonomy() ) )
5248                     || ( node.getNodeData().isHasSequence() && !TreePanelUtil.isSequenceEmpty( node.getNodeData()
5249                             .getSequence() ) ) || ( node.getNodeData().isHasDate() )
5250                     || ( node.getNodeData().isHasDistribution() ) || node.getBranchData().isHasConfidences() ) {
5251                 _popup_buffer.setLength( 0 );
5252                 short lines = 0;
5253                 if ( node.getName().length() > 0 ) {
5254                     lines++;
5255                     _popup_buffer.append( node.getName() );
5256                 }
5257                 if ( node.getNodeData().isHasTaxonomy()
5258                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5259                     lines++;
5260                     boolean enc_data = false;
5261                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5262                     if ( _popup_buffer.length() > 0 ) {
5263                         _popup_buffer.append( "\n" );
5264                     }
5265                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5266                         _popup_buffer.append( "[" );
5267                         _popup_buffer.append( tax.getTaxonomyCode() );
5268                         _popup_buffer.append( "]" );
5269                         enc_data = true;
5270                     }
5271                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5272                         if ( enc_data ) {
5273                             _popup_buffer.append( " " );
5274                         }
5275                         _popup_buffer.append( tax.getScientificName() );
5276                         enc_data = true;
5277                     }
5278                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5279                         if ( enc_data ) {
5280                             _popup_buffer.append( " (" );
5281                         }
5282                         else {
5283                             _popup_buffer.append( "(" );
5284                         }
5285                         _popup_buffer.append( tax.getCommonName() );
5286                         _popup_buffer.append( ")" );
5287                         enc_data = true;
5288                     }
5289                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5290                         if ( enc_data ) {
5291                             _popup_buffer.append( " (" );
5292                         }
5293                         else {
5294                             _popup_buffer.append( "(" );
5295                         }
5296                         _popup_buffer.append( tax.getAuthority() );
5297                         _popup_buffer.append( ")" );
5298                         enc_data = true;
5299                     }
5300                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5301                         if ( enc_data ) {
5302                             _popup_buffer.append( " [" );
5303                         }
5304                         else {
5305                             _popup_buffer.append( "[" );
5306                         }
5307                         _popup_buffer.append( tax.getRank() );
5308                         _popup_buffer.append( "]" );
5309                         enc_data = true;
5310                     }
5311                     if ( tax.getSynonyms().size() > 0 ) {
5312                         if ( enc_data ) {
5313                             _popup_buffer.append( " " );
5314                         }
5315                         _popup_buffer.append( "[" );
5316                         int counter = 1;
5317                         for( final String syn : tax.getSynonyms() ) {
5318                             if ( !ForesterUtil.isEmpty( syn ) ) {
5319                                 enc_data = true;
5320                                 _popup_buffer.append( syn );
5321                                 if ( counter < tax.getSynonyms().size() ) {
5322                                     _popup_buffer.append( ", " );
5323                                 }
5324                             }
5325                             counter++;
5326                         }
5327                         _popup_buffer.append( "]" );
5328                     }
5329                     if ( !enc_data ) {
5330                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5331                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5332                                 _popup_buffer.append( "[" );
5333                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5334                                 _popup_buffer.append( "] " );
5335                             }
5336                             _popup_buffer.append( tax.getIdentifier().getValue() );
5337                         }
5338                     }
5339                 }
5340                 if ( node.getNodeData().isHasSequence()
5341                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5342                     lines++;
5343                     boolean enc_data = false;
5344                     if ( _popup_buffer.length() > 0 ) {
5345                         _popup_buffer.append( "\n" );
5346                     }
5347                     final Sequence seq = node.getNodeData().getSequence();
5348                     if ( seq.getAccession() != null ) {
5349                         _popup_buffer.append( "[" );
5350                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5351                             _popup_buffer.append( seq.getAccession().getSource() );
5352                             _popup_buffer.append( ":" );
5353                         }
5354                         _popup_buffer.append( seq.getAccession().getValue() );
5355                         _popup_buffer.append( "]" );
5356                         enc_data = true;
5357                     }
5358                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5359                         if ( enc_data ) {
5360                             _popup_buffer.append( " [" );
5361                         }
5362                         else {
5363                             _popup_buffer.append( "[" );
5364                         }
5365                         _popup_buffer.append( seq.getSymbol() );
5366                         _popup_buffer.append( "]" );
5367                         enc_data = true;
5368                     }
5369                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5370                         if ( enc_data ) {
5371                             _popup_buffer.append( " " );
5372                         }
5373                         _popup_buffer.append( seq.getName() );
5374                     }
5375                 }
5376                 if ( node.getNodeData().isHasDate() ) {
5377                     lines++;
5378                     if ( _popup_buffer.length() > 0 ) {
5379                         _popup_buffer.append( "\n" );
5380                     }
5381                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5382                 }
5383                 if ( node.getNodeData().isHasDistribution() ) {
5384                     lines++;
5385                     if ( _popup_buffer.length() > 0 ) {
5386                         _popup_buffer.append( "\n" );
5387                     }
5388                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5389                 }
5390                 if ( node.getBranchData().isHasConfidences() ) {
5391                     final List<Confidence> confs = node.getBranchData().getConfidences();
5392                     for( final Confidence confidence : confs ) {
5393                         lines++;
5394                         if ( _popup_buffer.length() > 0 ) {
5395                             _popup_buffer.append( "\n" );
5396                         }
5397                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5398                             _popup_buffer.append( "[" );
5399                             _popup_buffer.append( confidence.getType() );
5400                             _popup_buffer.append( "] " );
5401                         }
5402                         _popup_buffer
5403                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5404                                                                                           getOptions()
5405                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5406                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5407                             _popup_buffer.append( " (sd=" );
5408                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5409                                     .getStandardDeviation(), getOptions()
5410                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5411                             _popup_buffer.append( ")" );
5412                         }
5413                     }
5414                 }
5415                 if ( node.getNodeData().isHasProperties() ) {
5416                     final PropertiesMap properties = node.getNodeData().getProperties();
5417                     for( final String ref : properties.getPropertyRefs() ) {
5418                         _popup_buffer.append( "\n" );
5419                         final Property p = properties.getProperty( ref );
5420                         _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5421                         _popup_buffer.append( "=" );
5422                         _popup_buffer.append( p.getValue() );
5423                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5424                             _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5425                         }
5426                     }
5427                 }
5428                 if ( _popup_buffer.length() > 0 ) {
5429                     if ( !getConfiguration().isUseNativeUI() ) {
5430                         _rollover_popup
5431                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5432                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5433                         if ( isInFoundNodes( node ) ) {
5434                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
5435                         }
5436                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5437                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
5438                         }
5439                         else {
5440                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5441                         }
5442                     }
5443                     else {
5444                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5445                     }
5446                     _rollover_popup.setText( _popup_buffer.toString() );
5447                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5448                                                                                   _rollover_popup,
5449                                                                                   e.getLocationOnScreen().x + 10,
5450                                                                                   e.getLocationOnScreen().y
5451                                                                                           - ( lines * 20 ) );
5452                     _node_desc_popup.show();
5453                 }
5454             }
5455         }
5456         catch ( final Exception ex ) {
5457             // Do nothing.
5458         }
5459     }
5460
5461     final private void showNodeEditFrame( final PhylogenyNode n ) {
5462         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5463             // pop up edit box for single node
5464             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5465             _node_frame_index++;
5466         }
5467         else {
5468             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5469         }
5470     }
5471
5472     final private void showNodeFrame( final PhylogenyNode n ) {
5473         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5474             // pop up edit box for single node
5475             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5476             _node_frame_index++;
5477         }
5478         else {
5479             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5480         }
5481     }
5482
5483     final private void switchDisplaygetPhylogenyGraphicsType() {
5484         switch ( getPhylogenyGraphicsType() ) {
5485             case RECTANGULAR:
5486                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5487                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5488                 break;
5489             case EURO_STYLE:
5490                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5491                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5492                 break;
5493             case ROUNDED:
5494                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5495                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5496                 break;
5497             case CURVED:
5498                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5499                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5500                 break;
5501             case TRIANGULAR:
5502                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5503                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5504                 break;
5505             case CONVEX:
5506                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5507                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5508                 break;
5509             case UNROOTED:
5510                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5511                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5512                 break;
5513             case CIRCULAR:
5514                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5515                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5516                 break;
5517             default:
5518                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5519         }
5520         if ( getControlPanel().getDynamicallyHideData() != null ) {
5521             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5522                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5523             }
5524             else {
5525                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5526             }
5527         }
5528         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5529             getControlPanel().setDrawPhylogramEnabled( true );
5530         }
5531         else {
5532             getControlPanel().setDrawPhylogramEnabled( false );
5533         }
5534         if ( getMainPanel().getMainFrame() == null ) {
5535             // Must be "E" applet version.
5536             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5537                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5538         }
5539         else {
5540             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5541         }
5542     }
5543
5544     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5545         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5546     }
5547
5548     final private static boolean plusPressed( final int key_code ) {
5549         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5550                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5551     }
5552
5553     final private class SubtreeColorizationActionListener implements ActionListener {
5554
5555         JColorChooser       _chooser          = null;
5556         PhylogenyNode       _node             = null;
5557         List<PhylogenyNode> _additional_nodes = null;
5558
5559         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5560             _chooser = chooser;
5561             _node = node;
5562         }
5563
5564         SubtreeColorizationActionListener( final JColorChooser chooser,
5565                                            final PhylogenyNode node,
5566                                            final List<PhylogenyNode> additional_nodes ) {
5567             _chooser = chooser;
5568             _node = node;
5569             _additional_nodes = additional_nodes;
5570         }
5571
5572         @Override
5573         public void actionPerformed( final ActionEvent e ) {
5574             final Color c = _chooser.getColor();
5575             if ( c != null ) {
5576                 colorizeSubtree( c, _node, _additional_nodes );
5577             }
5578         }
5579     }
5580 }