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