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