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