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