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