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