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