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