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