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