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