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