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