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