in progress
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanel.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.BasicStroke;
29 import java.awt.Color;
30 import java.awt.Cursor;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.FontMetrics;
34 import java.awt.GradientPaint;
35 import java.awt.Graphics;
36 import java.awt.Graphics2D;
37 import java.awt.Point;
38 import java.awt.Rectangle;
39 import java.awt.RenderingHints;
40 import java.awt.Stroke;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.awt.event.FocusAdapter;
44 import java.awt.event.FocusEvent;
45 import java.awt.event.InputEvent;
46 import java.awt.event.KeyAdapter;
47 import java.awt.event.KeyEvent;
48 import java.awt.event.MouseEvent;
49 import java.awt.event.MouseWheelEvent;
50 import java.awt.event.MouseWheelListener;
51 import java.awt.font.FontRenderContext;
52 import java.awt.font.TextLayout;
53 import java.awt.geom.AffineTransform;
54 import java.awt.geom.Arc2D;
55 import java.awt.geom.CubicCurve2D;
56 import java.awt.geom.Ellipse2D;
57 import java.awt.geom.Line2D;
58 import java.awt.geom.Path2D;
59 import java.awt.geom.QuadCurve2D;
60 import java.awt.geom.Rectangle2D;
61 import java.awt.image.BufferedImage;
62 import java.awt.print.PageFormat;
63 import java.awt.print.Printable;
64 import java.awt.print.PrinterException;
65 import java.io.File;
66 import java.io.IOException;
67 import java.io.UnsupportedEncodingException;
68 import java.net.URI;
69 import java.net.URISyntaxException;
70 import java.net.URLEncoder;
71 import java.text.DecimalFormat;
72 import java.text.DecimalFormatSymbols;
73 import java.text.NumberFormat;
74 import java.util.ArrayList;
75 import java.util.Collections;
76 import java.util.HashMap;
77 import java.util.HashSet;
78 import java.util.Hashtable;
79 import java.util.List;
80 import java.util.Set;
81 import java.util.SortedSet;
82
83 import javax.swing.BorderFactory;
84 import javax.swing.JApplet;
85 import javax.swing.JColorChooser;
86 import javax.swing.JDialog;
87 import javax.swing.JMenuItem;
88 import javax.swing.JOptionPane;
89 import javax.swing.JPanel;
90 import javax.swing.JPopupMenu;
91 import javax.swing.JTextArea;
92 import javax.swing.Popup;
93 import javax.swing.PopupFactory;
94
95 import org.forester.archaeopteryx.Configuration.EXT_NODE_DATA_RETURN_ON;
96 import org.forester.archaeopteryx.ControlPanel.NodeClickAction;
97 import org.forester.archaeopteryx.Options.CLADOGRAM_TYPE;
98 import org.forester.archaeopteryx.Options.NODE_LABEL_DIRECTION;
99 import org.forester.archaeopteryx.Options.PHYLOGENY_GRAPHICS_TYPE;
100 import org.forester.archaeopteryx.phylogeny.data.RenderableDomainArchitecture;
101 import org.forester.archaeopteryx.phylogeny.data.RenderableMsaSequence;
102 import org.forester.archaeopteryx.phylogeny.data.RenderableVector;
103 import org.forester.archaeopteryx.tools.Blast;
104 import org.forester.archaeopteryx.tools.ImageLoader;
105 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
106 import org.forester.io.writers.SequenceWriter;
107 import org.forester.phylogeny.Phylogeny;
108 import org.forester.phylogeny.PhylogenyMethods;
109 import org.forester.phylogeny.PhylogenyMethods.DESCENDANT_SORT_PRIORITY;
110 import org.forester.phylogeny.PhylogenyNode;
111 import org.forester.phylogeny.data.Accession;
112 import org.forester.phylogeny.data.Annotation;
113 import org.forester.phylogeny.data.BranchColor;
114 import org.forester.phylogeny.data.Confidence;
115 import org.forester.phylogeny.data.DomainArchitecture;
116 import org.forester.phylogeny.data.Event;
117 import org.forester.phylogeny.data.NodeData.NODE_DATA;
118 import org.forester.phylogeny.data.NodeVisualData;
119 import org.forester.phylogeny.data.NodeVisualData.NodeFill;
120 import org.forester.phylogeny.data.NodeVisualData.NodeShape;
121 import org.forester.phylogeny.data.PhylogenyDataUtil;
122 import org.forester.phylogeny.data.PropertiesMap;
123 import org.forester.phylogeny.data.Property;
124 import org.forester.phylogeny.data.Sequence;
125 import org.forester.phylogeny.data.SequenceRelation;
126 import org.forester.phylogeny.data.Taxonomy;
127 import org.forester.phylogeny.data.Uri;
128 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
129 import org.forester.phylogeny.iterators.PreorderTreeIterator;
130 import org.forester.util.BasicDescriptiveStatistics;
131 import org.forester.util.DescriptiveStatistics;
132 import org.forester.util.ForesterConstants;
133 import org.forester.util.ForesterUtil;
134 import org.forester.util.SequenceAccessionTools;
135 import org.forester.util.TaxonomyUtil;
136
137 public final class TreePanel extends JPanel implements ActionListener, MouseWheelListener, Printable {
138
139     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 += RenderableMsaSequence.DEFAULT_WIDTH + 30;
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                    
4887                         rs.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
4888                                    node.getYcoord() - ( h / 2 ),
4889                                    g,
4890                                    this,
4891                                    to_pdf );
4892                     
4893                     
4894                 }
4895                 else {
4896                     rs.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
4897                                node.getYcoord() - ( h / 2 ),
4898                                g,
4899                                this,
4900                                to_pdf );
4901                 }
4902             }
4903         }
4904     }
4905
4906     final private int calcLengthOfLongestText() {
4907         final StringBuilder sb = new StringBuilder();
4908         if ( _ext_node_with_longest_txt_info != null ) {
4909             nodeDataAsSB( _ext_node_with_longest_txt_info, sb );
4910             if ( _ext_node_with_longest_txt_info.getNodeData().isHasTaxonomy() ) {
4911                 nodeTaxonomyDataAsSB( _ext_node_with_longest_txt_info.getNodeData().getTaxonomy(), sb );
4912             }
4913         }
4914         return getFontMetricsForLargeDefaultFont().stringWidth( sb.toString() );
4915     }
4916
4917     final private void paintOvRectangle( final Graphics2D g ) {
4918         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
4919         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
4920         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
4921         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
4922         final float width = getOvMaxWidth() / w_ratio;
4923         final float height = getOvMaxHeight() / h_ratio;
4924         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
4925         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
4926         g.setColor( getTreeColorSet().getFoundColor0() );
4927         getOvRectangle().setRect( x, y, width, height );
4928         final Stroke s = g.getStroke();
4929         g.setStroke( STROKE_1 );
4930         if ( ( width < 6 ) && ( height < 6 ) ) {
4931             drawRectFilled( x, y, 6, 6, g );
4932             getOvVirtualRectangle().setRect( x, y, 6, 6 );
4933         }
4934         else if ( width < 6 ) {
4935             drawRectFilled( x, y, 6, height, g );
4936             getOvVirtualRectangle().setRect( x, y, 6, height );
4937         }
4938         else if ( height < 6 ) {
4939             drawRectFilled( x, y, width, 6, g );
4940             getOvVirtualRectangle().setRect( x, y, width, 6 );
4941         }
4942         else {
4943             drawRect( x, y, width, height, g );
4944             if ( isInOvRect() ) {
4945                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
4946             }
4947             getOvVirtualRectangle().setRect( x, y, width, height );
4948         }
4949         g.setStroke( s );
4950     }
4951
4952     final private void paintPhylogenyLite( final Graphics2D g ) {
4953         _phylogeny
4954                 .getRoot()
4955                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
4956                         .getWidth() ) ) ) );
4957         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
4958         final Stroke s = g.getStroke();
4959         g.setStroke( STROKE_05 );
4960         for( final PhylogenyNode element : _nodes_in_preorder ) {
4961             paintNodeLite( g, element );
4962         }
4963         g.setStroke( s );
4964         paintOvRectangle( g );
4965     }
4966
4967     /**
4968      * Paint the root branch. (Differs from others because it will always be a
4969      * single horizontal line).
4970      * @param to_graphics_file 
4971      * 
4972      * @return new x1 value
4973      */
4974     final private void paintRootBranch( final Graphics2D g,
4975                                         final float x1,
4976                                         final float y1,
4977                                         final PhylogenyNode root,
4978                                         final boolean to_pdf,
4979                                         final boolean to_graphics_file ) {
4980         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
4981         float d = getXdistance();
4982         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
4983             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
4984         }
4985         if ( d < MIN_ROOT_LENGTH ) {
4986             d = MIN_ROOT_LENGTH;
4987         }
4988         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
4989             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
4990         }
4991         else {
4992             final double w = PhylogenyMethods.getBranchWidthValue( root );
4993             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
4994         }
4995         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file );
4996     }
4997
4998     final private void paintScale( final Graphics2D g,
4999                                    int x1,
5000                                    int y1,
5001                                    final boolean to_pdf,
5002                                    final boolean to_graphics_file ) {
5003         x1 += MOVE;
5004         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
5005         y1 -= 12;
5006         final int y2 = y1 - 8;
5007         final int y3 = y1 - 4;
5008         g.setFont( getTreeFontSet().getSmallFont() );
5009         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5010             g.setColor( Color.BLACK );
5011         }
5012         else {
5013             g.setColor( getTreeColorSet().getBranchLengthColor() );
5014         }
5015         final Stroke s = g.getStroke();
5016         g.setStroke( STROKE_1 );
5017         drawLine( x1, y1, x1, y2, g );
5018         drawLine( x2, y1, x2, y2, g );
5019         drawLine( x1, y3, x2, y3, g );
5020         if ( getScaleLabel() != null ) {
5021             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
5022         }
5023         g.setStroke( s );
5024     }
5025
5026     final private int paintTaxonomy( final Graphics2D g,
5027                                      final PhylogenyNode node,
5028                                      final boolean is_in_found_nodes,
5029                                      final boolean to_pdf,
5030                                      final boolean to_graphics_file,
5031                                      final float x_shift ) {
5032         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
5033         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
5034         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getTaxonomyColor() );
5035         final float start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
5036         float start_y;
5037         if ( !using_visual_font ) {
5038             start_y = node.getYcoord()
5039                     + ( getFontMetricsForLargeDefaultFont().getAscent() / ( node.getNumberOfDescendants() == 1 ? 1
5040                             : 3.0f ) );
5041         }
5042         else {
5043             start_y = node.getYcoord()
5044                     + ( getFontMetrics( g.getFont() ).getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
5045         }
5046         _sb.setLength( 0 );
5047         nodeTaxonomyDataAsSB( taxonomy, _sb );
5048         final String label = _sb.toString();
5049         /* GUILHEM_BEG */
5050         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
5051                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
5052             // invert font color and background color to show that this is the query sequence
5053             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
5054                                                                                                           false,
5055                                                                                                           false ) )
5056                     .getBounds();
5057             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
5058             g.setColor( getTreeColorSet().getBackgroundColor() );
5059         }
5060         /* GUILHEM_END */
5061         TreePanel.drawString( label, start_x, start_y, g );
5062         if ( !using_visual_font && !is_in_found_nodes ) {
5063             return getFontMetricsForLargeDefaultFont().stringWidth( label );
5064         }
5065         return getFontMetrics( g.getFont() ).stringWidth( label );
5066     }
5067
5068     final private void paintUnrooted( final PhylogenyNode n,
5069                                       final double low_angle,
5070                                       final double high_angle,
5071                                       final boolean radial_labels,
5072                                       final Graphics2D g,
5073                                       final boolean to_pdf,
5074                                       final boolean to_graphics_file ) {
5075         if ( n.isRoot() ) {
5076             n.setXcoord( getWidth() / 2 );
5077             n.setYcoord( getHeight() / 2 );
5078         }
5079         if ( n.isExternal() ) {
5080             paintNodeDataUnrootedCirc( g,
5081                                        n,
5082                                        to_pdf,
5083                                        to_graphics_file,
5084                                        radial_labels,
5085                                        ( high_angle + low_angle ) / 2,
5086                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
5087             return;
5088         }
5089         final float num_enclosed = n.getNumberOfExternalNodes();
5090         final float x = n.getXcoord();
5091         final float y = n.getYcoord();
5092         double current_angle = low_angle;
5093         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
5094         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
5095         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
5096         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
5097         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5098             final PhylogenyNode desc = n.getChildNode( i );
5099             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
5100             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
5101             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
5102             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
5103             //     continue;
5104             // }
5105             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
5106             //    continue;
5107             //}
5108             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
5109             //    continue;
5110             // }
5111             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5112             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5113             float length;
5114             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5115                 if ( desc.getDistanceToParent() < 0 ) {
5116                     length = 0;
5117                 }
5118                 else {
5119                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
5120                 }
5121             }
5122             else {
5123                 length = getUrtFactor();
5124             }
5125             final double mid_angle = current_angle + ( arc_size / 2 );
5126             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5127             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5128             desc.setXcoord( new_x );
5129             desc.setYcoord( new_y );
5130             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
5131             current_angle += arc_size;
5132             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
5133             drawLine( x, y, new_x, new_y, g );
5134             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file );
5135         }
5136         if ( n.isRoot() ) {
5137             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file );
5138         }
5139     }
5140
5141     final private void paintUnrootedLite( final PhylogenyNode n,
5142                                           final double low_angle,
5143                                           final double high_angle,
5144                                           final Graphics2D g,
5145                                           final float urt_ov_factor ) {
5146         if ( n.isRoot() ) {
5147             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
5148             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
5149             n.setXSecondary( x_pos );
5150             n.setYSecondary( y_pos );
5151         }
5152         if ( n.isExternal() ) {
5153             return;
5154         }
5155         final float num_enclosed = n.getNumberOfExternalNodes();
5156         final float x = n.getXSecondary();
5157         final float y = n.getYSecondary();
5158         double current_angle = low_angle;
5159         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5160             final PhylogenyNode desc = n.getChildNode( i );
5161             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5162             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5163             float length;
5164             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5165                 if ( desc.getDistanceToParent() < 0 ) {
5166                     length = 0;
5167                 }
5168                 else {
5169                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
5170                 }
5171             }
5172             else {
5173                 length = urt_ov_factor;
5174             }
5175             final double mid_angle = current_angle + ( arc_size / 2 );
5176             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5177             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5178             desc.setXSecondary( new_x );
5179             desc.setYSecondary( new_y );
5180             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
5181                 g.setColor( getColorForFoundNode( desc ) );
5182                 drawRectFilled( desc.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5183                                 desc.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5184                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5185                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5186                                 g );
5187                 g.setColor( getTreeColorSet().getOvColor() );
5188             }
5189             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
5190             current_angle += arc_size;
5191             drawLine( x, y, new_x, new_y, g );
5192         }
5193     }
5194
5195     final private void pasteSubtree( final PhylogenyNode node ) {
5196         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5197             errorMessageNoCutCopyPasteInUnrootedDisplay();
5198             return;
5199         }
5200         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
5201             JOptionPane.showMessageDialog( this,
5202                                            "No tree in buffer (need to copy or cut a subtree first)",
5203                                            "Attempt to paste with empty buffer",
5204                                            JOptionPane.ERROR_MESSAGE );
5205             return;
5206         }
5207         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
5208         final Object[] options = { "As sibling", "As descendant", "Cancel" };
5209         final int r = JOptionPane.showOptionDialog( this,
5210                                                     "How to paste subtree" + label + "?",
5211                                                     "Paste Subtree",
5212                                                     JOptionPane.CLOSED_OPTION,
5213                                                     JOptionPane.QUESTION_MESSAGE,
5214                                                     null,
5215                                                     options,
5216                                                     options[ 2 ] );
5217         boolean paste_as_sibling = true;
5218         if ( r == 1 ) {
5219             paste_as_sibling = false;
5220         }
5221         else if ( r != 0 ) {
5222             return;
5223         }
5224         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
5225         buffer_phy.setAllNodesToNotCollapse();
5226         PhylogenyMethods.preOrderReId( buffer_phy );
5227         buffer_phy.setRooted( true );
5228         boolean need_to_show_whole = false;
5229         if ( paste_as_sibling ) {
5230             if ( node.isRoot() ) {
5231                 JOptionPane.showMessageDialog( this,
5232                                                "Cannot paste sibling to root",
5233                                                "Attempt to paste sibling to root",
5234                                                JOptionPane.ERROR_MESSAGE );
5235                 return;
5236             }
5237             buffer_phy.addAsSibling( node );
5238         }
5239         else {
5240             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
5241                 need_to_show_whole = true;
5242                 _phylogeny = buffer_phy;
5243             }
5244             else {
5245                 buffer_phy.addAsChild( node );
5246             }
5247         }
5248         if ( getCopiedAndPastedNodes() == null ) {
5249             setCopiedAndPastedNodes( new HashSet<Long>() );
5250         }
5251         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
5252         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
5253         for( final PhylogenyNode n : nodes ) {
5254             node_ids.add( n.getId() );
5255         }
5256         node_ids.add( node.getId() );
5257         getCopiedAndPastedNodes().addAll( node_ids );
5258         setNodeInPreorderToNull();
5259         _phylogeny.externalNodesHaveChanged();
5260         _phylogeny.clearHashIdToNodeMap();
5261         _phylogeny.recalculateNumberOfExternalDescendants( true );
5262         resetNodeIdToDistToLeafMap();
5263         setEdited( true );
5264         if ( need_to_show_whole ) {
5265             getControlPanel().showWhole();
5266         }
5267         repaint();
5268     }
5269
5270     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
5271         final PropertiesMap properties = node.getNodeData().getProperties();
5272         final StringBuffer sb = new StringBuffer();
5273         boolean first = true;
5274         for( final String ref : properties.getPropertyRefs() ) {
5275             if ( first ) {
5276                 first = false;
5277             }
5278             else {
5279                 sb.append( " " );
5280             }
5281             final Property p = properties.getProperty( ref );
5282             sb.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5283             sb.append( "=" );
5284             sb.append( p.getValue() );
5285             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5286                 sb.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5287             }
5288         }
5289         return sb;
5290     }
5291
5292     private void setColor( final Graphics2D g,
5293                            final PhylogenyNode node,
5294                            final boolean to_graphics_file,
5295                            final boolean to_pdf,
5296                            final boolean is_in_found_nodes,
5297                            final Color default_color ) {
5298         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5299             g.setColor( Color.BLACK );
5300         }
5301         else if ( is_in_found_nodes ) {
5302             g.setColor( getColorForFoundNode( node ) );
5303         }
5304         else if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
5305                 && ( node.getNodeData().getNodeVisualData().getFontColor() != null ) ) {
5306             g.setColor( node.getNodeData().getNodeVisualData().getFontColor() );
5307         }
5308         else if ( getControlPanel().isColorAccordingToSequence() ) {
5309             g.setColor( getSequenceBasedColor( node ) );
5310         }
5311         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5312             g.setColor( getTaxonomyBasedColor( node ) );
5313         }
5314         else if ( getControlPanel().isColorAccordingToAnnotation()
5315                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
5316                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
5317             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
5318         }
5319         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
5320                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
5321             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
5322         }
5323         else if ( to_pdf ) {
5324             g.setColor( Color.BLACK );
5325         }
5326         else {
5327             g.setColor( default_color );
5328         }
5329     }
5330
5331     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
5332         getMainPanel().setCopiedAndPastedNodes( nodeIds );
5333     }
5334
5335     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
5336         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
5337     }
5338
5339     private boolean setFont( final Graphics2D g, final PhylogenyNode node, final boolean is_in_found_nodes ) {
5340         Font visual_font = null;
5341         if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null ) ) {
5342             visual_font = node.getNodeData().getNodeVisualData().getFont();
5343             g.setFont( visual_font != null ? visual_font : getTreeFontSet().getLargeFont() );
5344         }
5345         else {
5346             g.setFont( getTreeFontSet().getLargeFont() );
5347         }
5348         if ( is_in_found_nodes ) {
5349             g.setFont( g.getFont().deriveFont( Font.BOLD ) );
5350         }
5351         return visual_font != null;
5352     }
5353
5354     final private void setInOv( final boolean in_ov ) {
5355         _in_ov = in_ov;
5356     }
5357
5358     final private void setOvMaxHeight( final float ov_max_height ) {
5359         _ov_max_height = ov_max_height;
5360     }
5361
5362     final private void setOvMaxWidth( final float ov_max_width ) {
5363         _ov_max_width = ov_max_width;
5364     }
5365
5366     final private void setOvXcorrectionFactor( final float f ) {
5367         _ov_x_correction_factor = f;
5368     }
5369
5370     final private void setOvXDistance( final float ov_x_distance ) {
5371         _ov_x_distance = ov_x_distance;
5372     }
5373
5374     final private void setOvXPosition( final int ov_x_position ) {
5375         _ov_x_position = ov_x_position;
5376     }
5377
5378     final private void setOvYDistance( final float ov_y_distance ) {
5379         _ov_y_distance = ov_y_distance;
5380     }
5381
5382     final private void setOvYPosition( final int ov_y_position ) {
5383         _ov_y_position = ov_y_position;
5384     }
5385
5386     final private void setOvYStart( final int ov_y_start ) {
5387         _ov_y_start = ov_y_start;
5388     }
5389
5390     final private void setScaleDistance( final double scale_distance ) {
5391         _scale_distance = scale_distance;
5392     }
5393
5394     final private void setScaleLabel( final String scale_label ) {
5395         _scale_label = scale_label;
5396     }
5397
5398     private final void setupStroke( final Graphics2D g ) {
5399         if ( getYdistance() < 0.001 ) {
5400             g.setStroke( STROKE_005 );
5401         }
5402         else if ( getYdistance() < 0.01 ) {
5403             g.setStroke( STROKE_01 );
5404         }
5405         else if ( getYdistance() < 0.5 ) {
5406             g.setStroke( STROKE_025 );
5407         }
5408         else if ( getYdistance() < 1 ) {
5409             g.setStroke( STROKE_05 );
5410         }
5411         else if ( getYdistance() < 2 ) {
5412             g.setStroke( STROKE_075 );
5413         }
5414         else if ( getYdistance() < 20 ) {
5415             g.setStroke( STROKE_1 );
5416         }
5417         else {
5418             g.setStroke( STROKE_2 );
5419         }
5420     }
5421
5422     final private void setUpUrtFactor() {
5423         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
5424                 : getVisibleRect().height;
5425         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5426             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
5427         }
5428         else {
5429             final int max_depth = _circ_max_depth;
5430             if ( max_depth > 0 ) {
5431                 setUrtFactor( d / ( 2 * max_depth ) );
5432             }
5433             else {
5434                 setUrtFactor( d / 2 );
5435             }
5436         }
5437         setUrtFactorOv( getUrtFactor() );
5438     }
5439
5440     final private void setUrtFactor( final float urt_factor ) {
5441         _urt_factor = urt_factor;
5442     }
5443
5444     final private void setUrtFactorOv( final float urt_factor_ov ) {
5445         _urt_factor_ov = urt_factor_ov;
5446     }
5447
5448     private void showExtDescNodeData( final PhylogenyNode node ) {
5449         final List<String> data = new ArrayList<String>();
5450         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
5451         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
5452             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
5453                 if ( !nodes.contains( n ) ) {
5454                     nodes.add( n );
5455                 }
5456             }
5457         }
5458         for( final PhylogenyNode n : nodes ) {
5459             switch ( getOptions().getExtDescNodeDataToReturn() ) {
5460                 case NODE_NAME:
5461                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5462                         data.add( n.getName() );
5463                     }
5464                     break;
5465                 case SEQUENCE_NAME:
5466                     if ( n.getNodeData().isHasSequence()
5467                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5468                         data.add( n.getNodeData().getSequence().getName() );
5469                     }
5470                     break;
5471                 case GENE_NAME:
5472                     if ( n.getNodeData().isHasSequence()
5473                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5474                         data.add( n.getNodeData().getSequence().getGeneName() );
5475                     }
5476                     break;
5477                 case SEQUENCE_SYMBOL:
5478                     if ( n.getNodeData().isHasSequence()
5479                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5480                         data.add( n.getNodeData().getSequence().getSymbol() );
5481                     }
5482                     break;
5483                 case SEQUENCE_MOL_SEQ:
5484                     if ( n.getNodeData().isHasSequence()
5485                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5486                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
5487                     }
5488                     break;
5489                 case SEQUENCE_MOL_SEQ_FASTA:
5490                     final StringBuilder sb = new StringBuilder();
5491                     if ( n.getNodeData().isHasSequence()
5492                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5493                         final StringBuilder ann = new StringBuilder();
5494                         if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5495                             ann.append( n.getName() );
5496                             ann.append( "|" );
5497                         }
5498                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5499                             ann.append( "SYM=" );
5500                             ann.append( n.getNodeData().getSequence().getSymbol() );
5501                             ann.append( "|" );
5502                         }
5503                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5504                             ann.append( "NAME=" );
5505                             ann.append( n.getNodeData().getSequence().getName() );
5506                             ann.append( "|" );
5507                         }
5508                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5509                             ann.append( "GN=" );
5510                             ann.append( n.getNodeData().getSequence().getGeneName() );
5511                             ann.append( "|" );
5512                         }
5513                         if ( n.getNodeData().getSequence().getAccession() != null ) {
5514                             ann.append( "ACC=" );
5515                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
5516                             ann.append( "|" );
5517                         }
5518                         if ( n.getNodeData().isHasTaxonomy() ) {
5519                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5520                                 ann.append( "TAXID=" );
5521                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5522                                 ann.append( "|" );
5523                             }
5524                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5525                                 ann.append( "SN=" );
5526                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
5527                                 ann.append( "|" );
5528                             }
5529                         }
5530                         String ann_str;
5531                         if ( ann.charAt( ann.length() - 1 ) == '|' ) {
5532                             ann_str = ann.substring( 0, ann.length() - 1 );
5533                         }
5534                         else {
5535                             ann_str = ann.toString();
5536                         }
5537                         sb.append( SequenceWriter.toFasta( ann_str, n.getNodeData().getSequence()
5538                                 .getMolecularSequence(), 60 ) );
5539                         data.add( sb.toString() );
5540                     }
5541                     break;
5542                 case SEQUENCE_ACC:
5543                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
5544                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
5545                         data.add( n.getNodeData().getSequence().getAccession().toString() );
5546                     }
5547                     break;
5548                 case TAXONOMY_SCIENTIFIC_NAME:
5549                     if ( n.getNodeData().isHasTaxonomy()
5550                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5551                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
5552                     }
5553                     break;
5554                 case TAXONOMY_COMM0N_NAME:
5555                     if ( n.getNodeData().isHasTaxonomy()
5556                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) ) {
5557                         data.add( n.getNodeData().getTaxonomy().getCommonName() );
5558                     }
5559                     break;
5560                 case TAXONOMY_CODE:
5561                     if ( n.getNodeData().isHasTaxonomy()
5562                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5563                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5564                     }
5565                     break;
5566                 case UNKNOWN:
5567                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
5568                     break;
5569                 default:
5570                     throw new IllegalArgumentException( "unknown data element: "
5571                             + getOptions().getExtDescNodeDataToReturn() );
5572             }
5573         } // for loop
5574         final StringBuilder sb = new StringBuilder();
5575         final int size = TreePanelUtil.makeSB( data, getOptions(), sb );
5576         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
5577                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
5578             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5579                 System.out.println( sb );
5580             }
5581             if ( sb.length() < 1 ) {
5582                 clearCurrentExternalNodesDataBuffer();
5583             }
5584             else {
5585                 setCurrentExternalNodesDataBuffer( sb );
5586             }
5587         }
5588         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5589             if ( sb.length() < 1 ) {
5590                 TreePanelUtil.showInformationMessage( this, "No Appropriate Data (" + obtainTitleForExtDescNodeData()
5591                         + ")", "Descendants of selected node do not contain selected data" );
5592                 clearCurrentExternalNodesDataBuffer();
5593             }
5594             else {
5595                 setCurrentExternalNodesDataBuffer( sb );
5596                 String title;
5597                 if ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() ) {
5598                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5599                             : obtainTitleForExtDescNodeData() )
5600                             + " for "
5601                             + data.size()
5602                             + " nodes, unique entries: "
5603                             + size;
5604                 }
5605                 else {
5606                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5607                             : obtainTitleForExtDescNodeData() )
5608                             + " for "
5609                             + data.size()
5610                             + "/"
5611                             + node.getNumberOfExternalNodes()
5612                             + " external descendats of node "
5613                             + node
5614                             + ", unique entries: " + size;
5615                 }
5616                 final String s = sb.toString().trim();
5617                 if ( getMainPanel().getMainFrame() == null ) {
5618                     // Must be "E" applet version.
5619                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5620                     ae.showTextFrame( s, title );
5621                 }
5622                 else {
5623                     getMainPanel().getMainFrame().showTextFrame( s, title );
5624                 }
5625             }
5626         }
5627     }
5628
5629     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5630         try {
5631             if ( ( node.getName().length() > 0 )
5632                     || ( node.getNodeData().isHasTaxonomy() && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData()
5633                             .getTaxonomy() ) )
5634                     || ( node.getNodeData().isHasSequence() && !TreePanelUtil.isSequenceEmpty( node.getNodeData()
5635                             .getSequence() ) ) || ( node.getNodeData().isHasDate() )
5636                     || ( node.getNodeData().isHasDistribution() ) || node.getBranchData().isHasConfidences() ) {
5637                 _popup_buffer.setLength( 0 );
5638                 short lines = 0;
5639                 if ( node.getName().length() > 0 ) {
5640                     lines++;
5641                     _popup_buffer.append( node.getName() );
5642                 }
5643                 if ( node.getNodeData().isHasTaxonomy()
5644                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5645                     lines++;
5646                     boolean enc_data = false;
5647                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5648                     if ( _popup_buffer.length() > 0 ) {
5649                         _popup_buffer.append( "\n" );
5650                     }
5651                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5652                         _popup_buffer.append( "[" );
5653                         _popup_buffer.append( tax.getTaxonomyCode() );
5654                         _popup_buffer.append( "]" );
5655                         enc_data = true;
5656                     }
5657                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5658                         if ( enc_data ) {
5659                             _popup_buffer.append( " " );
5660                         }
5661                         _popup_buffer.append( tax.getScientificName() );
5662                         enc_data = true;
5663                     }
5664                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5665                         if ( enc_data ) {
5666                             _popup_buffer.append( " (" );
5667                         }
5668                         else {
5669                             _popup_buffer.append( "(" );
5670                         }
5671                         _popup_buffer.append( tax.getCommonName() );
5672                         _popup_buffer.append( ")" );
5673                         enc_data = true;
5674                     }
5675                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5676                         if ( enc_data ) {
5677                             _popup_buffer.append( " (" );
5678                         }
5679                         else {
5680                             _popup_buffer.append( "(" );
5681                         }
5682                         _popup_buffer.append( tax.getAuthority() );
5683                         _popup_buffer.append( ")" );
5684                         enc_data = true;
5685                     }
5686                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5687                         if ( enc_data ) {
5688                             _popup_buffer.append( " [" );
5689                         }
5690                         else {
5691                             _popup_buffer.append( "[" );
5692                         }
5693                         _popup_buffer.append( tax.getRank() );
5694                         _popup_buffer.append( "]" );
5695                         enc_data = true;
5696                     }
5697                     if ( tax.getSynonyms().size() > 0 ) {
5698                         if ( enc_data ) {
5699                             _popup_buffer.append( " " );
5700                         }
5701                         _popup_buffer.append( "[" );
5702                         int counter = 1;
5703                         for( final String syn : tax.getSynonyms() ) {
5704                             if ( !ForesterUtil.isEmpty( syn ) ) {
5705                                 enc_data = true;
5706                                 _popup_buffer.append( syn );
5707                                 if ( counter < tax.getSynonyms().size() ) {
5708                                     _popup_buffer.append( ", " );
5709                                 }
5710                             }
5711                             counter++;
5712                         }
5713                         _popup_buffer.append( "]" );
5714                     }
5715                     if ( !enc_data ) {
5716                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5717                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5718                                 _popup_buffer.append( "[" );
5719                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5720                                 _popup_buffer.append( "] " );
5721                             }
5722                             _popup_buffer.append( tax.getIdentifier().getValue() );
5723                         }
5724                     }
5725                 }
5726                 if ( node.getNodeData().isHasSequence()
5727                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5728                     lines++;
5729                     boolean enc_data = false;
5730                     if ( _popup_buffer.length() > 0 ) {
5731                         _popup_buffer.append( "\n" );
5732                     }
5733                     final Sequence seq = node.getNodeData().getSequence();
5734                     if ( seq.getAccession() != null ) {
5735                         _popup_buffer.append( "[" );
5736                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5737                             _popup_buffer.append( seq.getAccession().getSource() );
5738                             _popup_buffer.append( ":" );
5739                         }
5740                         _popup_buffer.append( seq.getAccession().getValue() );
5741                         _popup_buffer.append( "]" );
5742                         enc_data = true;
5743                     }
5744                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5745                         if ( enc_data ) {
5746                             _popup_buffer.append( " [" );
5747                         }
5748                         else {
5749                             _popup_buffer.append( "[" );
5750                         }
5751                         _popup_buffer.append( seq.getSymbol() );
5752                         _popup_buffer.append( "]" );
5753                         enc_data = true;
5754                     }
5755                     if ( !ForesterUtil.isEmpty( seq.getGeneName() ) ) {
5756                         if ( enc_data ) {
5757                             _popup_buffer.append( " [" );
5758                         }
5759                         else {
5760                             _popup_buffer.append( "[" );
5761                         }
5762                         _popup_buffer.append( seq.getGeneName() );
5763                         _popup_buffer.append( "]" );
5764                         enc_data = true;
5765                     }
5766                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5767                         if ( enc_data ) {
5768                             _popup_buffer.append( " " );
5769                         }
5770                         _popup_buffer.append( seq.getName() );
5771                     }
5772                 }
5773                 if ( node.getNodeData().isHasDate() ) {
5774                     lines++;
5775                     if ( _popup_buffer.length() > 0 ) {
5776                         _popup_buffer.append( "\n" );
5777                     }
5778                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5779                 }
5780                 if ( node.getNodeData().isHasDistribution() ) {
5781                     lines++;
5782                     if ( _popup_buffer.length() > 0 ) {
5783                         _popup_buffer.append( "\n" );
5784                     }
5785                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5786                 }
5787                 if ( node.getBranchData().isHasConfidences() ) {
5788                     final List<Confidence> confs = node.getBranchData().getConfidences();
5789                     for( final Confidence confidence : confs ) {
5790                         lines++;
5791                         if ( _popup_buffer.length() > 0 ) {
5792                             _popup_buffer.append( "\n" );
5793                         }
5794                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5795                             _popup_buffer.append( "[" );
5796                             _popup_buffer.append( confidence.getType() );
5797                             _popup_buffer.append( "] " );
5798                         }
5799                         _popup_buffer
5800                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5801                                                                                           getOptions()
5802                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5803                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5804                             _popup_buffer.append( " (sd=" );
5805                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5806                                     .getStandardDeviation(), getOptions()
5807                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5808                             _popup_buffer.append( ")" );
5809                         }
5810                     }
5811                 }
5812                 if ( node.getNodeData().isHasProperties() ) {
5813                     final PropertiesMap properties = node.getNodeData().getProperties();
5814                     for( final String ref : properties.getPropertyRefs() ) {
5815                         _popup_buffer.append( "\n" );
5816                         final Property p = properties.getProperty( ref );
5817                         _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5818                         _popup_buffer.append( "=" );
5819                         _popup_buffer.append( p.getValue() );
5820                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5821                             _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5822                         }
5823                     }
5824                 }
5825                 if ( _popup_buffer.length() > 0 ) {
5826                     if ( !getConfiguration().isUseNativeUI() ) {
5827                         _rollover_popup
5828                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5829                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5830                         if ( isInFoundNodes0( node ) && !isInFoundNodes1( node ) ) {
5831                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0() );
5832                         }
5833                         else if ( !isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5834                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor1() );
5835                         }
5836                         else if ( isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5837                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0and1() );
5838                         }
5839                         else {
5840                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5841                         }
5842                     }
5843                     else {
5844                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5845                     }
5846                     _rollover_popup.setText( _popup_buffer.toString() );
5847                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5848                                                                                   _rollover_popup,
5849                                                                                   e.getLocationOnScreen().x + 10,
5850                                                                                   e.getLocationOnScreen().y
5851                                                                                           - ( lines * 20 ) );
5852                     _node_desc_popup.show();
5853                 }
5854             }
5855         }
5856         catch ( final Exception ex ) {
5857             // Do nothing.
5858         }
5859     }
5860
5861     final private void showNodeEditFrame( final PhylogenyNode n ) {
5862         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5863             // pop up edit box for single node
5864             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5865             _node_frame_index++;
5866         }
5867         else {
5868             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5869         }
5870     }
5871
5872     final private void showNodeFrame( final PhylogenyNode n ) {
5873         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5874             // pop up edit box for single node
5875             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5876             _node_frame_index++;
5877         }
5878         else {
5879             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5880         }
5881     }
5882
5883     final private void switchDisplaygetPhylogenyGraphicsType() {
5884         switch ( getPhylogenyGraphicsType() ) {
5885             case RECTANGULAR:
5886                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5887                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5888                 break;
5889             case EURO_STYLE:
5890                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5891                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5892                 break;
5893             case ROUNDED:
5894                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5895                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5896                 break;
5897             case CURVED:
5898                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5899                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5900                 break;
5901             case TRIANGULAR:
5902                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5903                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5904                 break;
5905             case CONVEX:
5906                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5907                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5908                 break;
5909             case UNROOTED:
5910                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5911                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5912                 break;
5913             case CIRCULAR:
5914                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5915                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5916                 break;
5917             default:
5918                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5919         }
5920         if ( getControlPanel().getDynamicallyHideData() != null ) {
5921             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5922                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5923             }
5924             else {
5925                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5926             }
5927         }
5928         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5929             getControlPanel().setDrawPhylogramEnabled( true );
5930         }
5931         else {
5932             getControlPanel().setDrawPhylogramEnabled( false );
5933         }
5934         if ( getMainPanel().getMainFrame() == null ) {
5935             // Must be "E" applet version.
5936             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5937                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5938         }
5939         else {
5940             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5941         }
5942     }
5943
5944     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
5945         if ( node.getNodeData().getNodeVisualData() == null ) {
5946             node.getNodeData().setNodeVisualData( new NodeVisualData() );
5947         }
5948         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
5949     }
5950
5951     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
5952         g.drawString( str, x, y );
5953     }
5954
5955     final private static boolean plusPressed( final int key_code ) {
5956         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5957                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5958     }
5959
5960     final private class NodeColorizationActionListener implements ActionListener {
5961
5962         List<PhylogenyNode> _additional_nodes = null;
5963         JColorChooser       _chooser          = null;
5964         PhylogenyNode       _node             = null;
5965
5966         NodeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5967             _chooser = chooser;
5968             _node = node;
5969         }
5970
5971         NodeColorizationActionListener( final JColorChooser chooser,
5972                                         final PhylogenyNode node,
5973                                         final List<PhylogenyNode> additional_nodes ) {
5974             _chooser = chooser;
5975             _node = node;
5976             _additional_nodes = additional_nodes;
5977         }
5978
5979         @Override
5980         public void actionPerformed( final ActionEvent e ) {
5981             final Color c = _chooser.getColor();
5982             if ( c != null ) {
5983                 colorizeNodes( c, _node, _additional_nodes );
5984             }
5985         }
5986     }
5987
5988     final private class SubtreeColorizationActionListener implements ActionListener {
5989
5990         List<PhylogenyNode> _additional_nodes = null;
5991         JColorChooser       _chooser          = null;
5992         PhylogenyNode       _node             = null;
5993
5994         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5995             _chooser = chooser;
5996             _node = node;
5997         }
5998
5999         SubtreeColorizationActionListener( final JColorChooser chooser,
6000                                            final PhylogenyNode node,
6001                                            final List<PhylogenyNode> additional_nodes ) {
6002             _chooser = chooser;
6003             _node = node;
6004             _additional_nodes = additional_nodes;
6005         }
6006
6007         @Override
6008         public void actionPerformed( final ActionEvent e ) {
6009             final Color c = _chooser.getColor();
6010             if ( c != null ) {
6011                 colorizeSubtree( c, _node, _additional_nodes );
6012             }
6013         }
6014     }
6015 }