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