b94080f8c617e0e689153caa68785036dfbc2aae
[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() || AptxUtil.UNIPROT_KB_PATTERN_2
3247                         .matcher( node.getName() ).find() ) ) {
3248             return true;
3249         }
3250         if ( node.getNodeData().isHasSequence() ) {
3251             Sequence seq = node.getNodeData().getSequence();
3252             if ( !ForesterUtil.isEmpty( seq.getName() )
3253                     && ( AptxUtil.UNIPROT_KB_PATTERN_1.matcher( seq.getName() ).find() || AptxUtil.UNIPROT_KB_PATTERN_2
3254                             .matcher( seq.getName() ).find() ) ) {
3255                 return true;
3256             }
3257             if ( !ForesterUtil.isEmpty( seq.getSymbol() )
3258                     && ( AptxUtil.UNIPROT_KB_PATTERN_1.matcher( seq.getSymbol() ).find() || AptxUtil.UNIPROT_KB_PATTERN_2
3259                             .matcher( seq.getSymbol() ).find() ) ) {
3260                 return true;
3261             }
3262             if ( ( node.getNodeData().getSequence().getAccession() != null )
3263                     && !ForesterUtil.isEmpty( seq.getAccession().getValue() )
3264                     && ( AptxUtil.UNIPROT_KB_PATTERN_1.matcher( seq.getAccession().getValue() ).find() || AptxUtil.UNIPROT_KB_PATTERN_2
3265                             .matcher( seq.getAccession().getValue() ).find() ) ) {
3266                 return true;
3267             }
3268         }
3269         return false;
3270     }
3271
3272     final private void openSeqWeb( final PhylogenyNode node ) {
3273         if ( !isCanOpenSeqWeb( node ) ) {
3274             cannotOpenBrowserWarningMessage( "sequence" );
3275             return;
3276         }
3277         String uri_str = null;
3278         if ( node.getNodeData().isHasSequence()
3279                 && ( node.getNodeData().getSequence().getAccession() != null )
3280                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() )
3281                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getValue() )
3282                 && getConfiguration().isHasWebLink( node.getNodeData().getSequence().getAccession().getSource()
3283                         .toLowerCase() ) ) {
3284             final Sequence seq = node.getNodeData().getSequence();
3285             final String source = seq.getAccession().getSource().toLowerCase();
3286             String url;
3287             if ( source.toLowerCase().equals( "ncbi" ) ) {
3288                 url = Constants.NCBI_ALL_DATABASE_SEARCH;
3289             }
3290             else {
3291                 final WebLink weblink = getConfiguration().getWebLink( source );
3292                 url = weblink.getUrl().toString();
3293             }
3294             try {
3295                 uri_str = url + URLEncoder.encode( seq.getAccession().getValue(), ForesterConstants.UTF8 );
3296             }
3297             catch ( final UnsupportedEncodingException e ) {
3298                 AptxUtil.showErrorMessage( this, e.toString() );
3299                 e.printStackTrace();
3300             }
3301         }
3302         else {
3303             String upkb = null;
3304             if ( node.getNodeData().isHasSequence() ) {
3305                 Sequence seq = node.getNodeData().getSequence();
3306                 Matcher m;
3307                 if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
3308                     m = AptxUtil.UNIPROT_KB_PATTERN_1.matcher( seq.getSymbol() );
3309                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
3310                         if ( m.find() ) {
3311                             upkb = m.group( 2 );
3312                         }
3313                         else {
3314                             m = AptxUtil.UNIPROT_KB_PATTERN_2.matcher( seq.getSymbol() );
3315                             if ( m.find() ) {
3316                                 upkb = m.group();
3317                             }
3318                         }
3319                     }
3320                 }
3321                 if ( ForesterUtil.isEmpty( upkb ) && !ForesterUtil.isEmpty( seq.getName() ) ) {
3322                     m = AptxUtil.UNIPROT_KB_PATTERN_1.matcher( seq.getName() );
3323                     if ( m.find() ) {
3324                         upkb = m.group( 2 );
3325                     }
3326                     else {
3327                         m = AptxUtil.UNIPROT_KB_PATTERN_2.matcher( seq.getName() );
3328                         if ( m.find() ) {
3329                             upkb = m.group();
3330                         }
3331                     }
3332                 }
3333                 if ( ForesterUtil.isEmpty( upkb ) && ( node.getNodeData().getSequence().getAccession() != null )
3334                         && !ForesterUtil.isEmpty( seq.getAccession().getValue() ) ) {
3335                     m = AptxUtil.UNIPROT_KB_PATTERN_1.matcher( seq.getAccession().getValue() );
3336                     if ( m.find() ) {
3337                         upkb = m.group( 2 );
3338                     }
3339                     else {
3340                         m = AptxUtil.UNIPROT_KB_PATTERN_2.matcher( seq.getAccession().getValue() );
3341                         if ( m.find() ) {
3342                             upkb = m.group();
3343                         }
3344                     }
3345                 }
3346             }
3347             if ( ForesterUtil.isEmpty( upkb ) && !ForesterUtil.isEmpty( node.getName() ) ) {
3348                 final Matcher m1 = AptxUtil.UNIPROT_KB_PATTERN_1.matcher( node.getName() );
3349                 if ( m1.find() ) {
3350                     upkb = m1.group( 2 );
3351                 }
3352                 else {
3353                     final Matcher m2 = AptxUtil.UNIPROT_KB_PATTERN_2.matcher( node.getName() );
3354                     if ( m2.find() ) {
3355                         upkb = m2.group();
3356                     }
3357                 }
3358             }
3359             try {
3360                 uri_str = AptxUtil.UNIPROT_KB + URLEncoder.encode( upkb, ForesterConstants.UTF8 );
3361             }
3362             catch ( final UnsupportedEncodingException e ) {
3363                 AptxUtil.showErrorMessage( this, e.toString() );
3364                 e.printStackTrace();
3365             }
3366         }
3367         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3368             try {
3369                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3370                                            isApplet(),
3371                                            isApplet() ? obtainApplet() : null,
3372                                            "_aptx_seq" );
3373             }
3374             catch ( final IOException e ) {
3375                 AptxUtil.showErrorMessage( this, e.toString() );
3376                 e.printStackTrace();
3377             }
3378             catch ( final URISyntaxException e ) {
3379                 AptxUtil.showErrorMessage( this, e.toString() );
3380                 e.printStackTrace();
3381             }
3382         }
3383         else {
3384             cannotOpenBrowserWarningMessage( "sequence" );
3385         }
3386     }
3387
3388     final private void openTaxWeb( final PhylogenyNode node ) {
3389         if ( !isCanOpenTaxWeb( node ) ) {
3390             cannotOpenBrowserWarningMessage( "taxonomic" );
3391             return;
3392         }
3393         String uri_str = null;
3394         final Taxonomy tax = node.getNodeData().getTaxonomy();
3395         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
3396                 && getConfiguration().isHasWebLink( tax.getIdentifier().getProvider().toLowerCase() ) ) {
3397             final String type = tax.getIdentifier().getProvider().toLowerCase();
3398             final WebLink weblink = getConfiguration().getWebLink( type );
3399             try {
3400                 uri_str = weblink.getUrl() + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
3401             }
3402             catch ( final UnsupportedEncodingException e ) {
3403                 AptxUtil.showErrorMessage( this, e.toString() );
3404                 e.printStackTrace();
3405             }
3406         }
3407         else if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3408                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
3409             try {
3410                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
3411             }
3412             catch ( final URISyntaxException e ) {
3413                 AptxUtil.showErrorMessage( this, e.toString() );
3414                 uri_str = null;
3415                 e.printStackTrace();
3416             }
3417         }
3418         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
3419             try {
3420                 uri_str = "http://www.eol.org/search?q="
3421                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
3422             }
3423             catch ( final UnsupportedEncodingException e ) {
3424                 AptxUtil.showErrorMessage( this, e.toString() );
3425                 e.printStackTrace();
3426             }
3427         }
3428         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
3429             try {
3430                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3431                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
3432             }
3433             catch ( final UnsupportedEncodingException e ) {
3434                 AptxUtil.showErrorMessage( this, e.toString() );
3435                 e.printStackTrace();
3436             }
3437         }
3438         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
3439             try {
3440                 uri_str = "http://www.eol.org/search?q="
3441                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
3442             }
3443             catch ( final UnsupportedEncodingException e ) {
3444                 AptxUtil.showErrorMessage( this, e.toString() );
3445                 e.printStackTrace();
3446             }
3447         }
3448         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3449             try {
3450                 JApplet applet = null;
3451                 if ( isApplet() ) {
3452                     applet = obtainApplet();
3453                 }
3454                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_tax" );
3455             }
3456             catch ( final IOException e ) {
3457                 AptxUtil.showErrorMessage( this, e.toString() );
3458                 e.printStackTrace();
3459             }
3460             catch ( final URISyntaxException e ) {
3461                 AptxUtil.showErrorMessage( this, e.toString() );
3462                 e.printStackTrace();
3463             }
3464         }
3465         else {
3466             cannotOpenBrowserWarningMessage( "taxonomic" );
3467         }
3468     }
3469
3470     final private void paintBranchLength( final Graphics2D g,
3471                                           final PhylogenyNode node,
3472                                           final boolean to_pdf,
3473                                           final boolean to_graphics_file ) {
3474         g.setFont( getTreeFontSet().getSmallFont() );
3475         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3476             g.setColor( Color.BLACK );
3477         }
3478         else {
3479             g.setColor( getTreeColorSet().getBranchLengthColor() );
3480         }
3481         if ( !node.isRoot() ) {
3482             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3483                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3484                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3485             }
3486             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3487                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3488                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3489             }
3490             else {
3491                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3492                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3493             }
3494         }
3495         else {
3496             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
3497                     - getTreeFontSet()._small_max_descent, g );
3498         }
3499     }
3500
3501     final private void paintBranchLite( final Graphics2D g,
3502                                         final float x1,
3503                                         final float x2,
3504                                         final float y1,
3505                                         final float y2,
3506                                         final PhylogenyNode node ) {
3507         g.setColor( getTreeColorSet().getOvColor() );
3508         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3509             drawLine( x1, y1, x2, y2, g );
3510         }
3511         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3512             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3513             ( g ).draw( _quad_curve );
3514         }
3515         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3516             final float dx = x2 - x1;
3517             final float dy = y2 - y1;
3518             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3519                     + ( dy * 0.8f ), x2, y2 );
3520             ( g ).draw( _cubic_curve );
3521         }
3522         else {
3523             final float x2a = x2;
3524             final float x1a = x1;
3525             // draw the vertical line
3526             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
3527                 drawLine( x1, y1, x1, y2, g );
3528             }
3529             // draw the horizontal line
3530             drawLine( x1a, y2, x2a, y2, g );
3531         }
3532     }
3533
3534     /**
3535      * Paint a branch which consists of a vertical and a horizontal bar
3536      * @param is_ind_found_nodes 
3537      */
3538     final private void paintBranchRectangular( final Graphics2D g,
3539                                                final float x1,
3540                                                final float x2,
3541                                                final float y1,
3542                                                final float y2,
3543                                                final PhylogenyNode node,
3544                                                final boolean to_pdf,
3545                                                final boolean to_graphics_file ) {
3546         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
3547         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3548             drawLine( x1, y1, x2, y2, g );
3549         }
3550         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3551             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3552             g.draw( _quad_curve );
3553         }
3554         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3555             final float dx = x2 - x1;
3556             final float dy = y2 - y1;
3557             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3558                     + ( dy * 0.8f ), x2, y2 );
3559             g.draw( _cubic_curve );
3560         }
3561         else {
3562             final float x2a = x2;
3563             final float x1a = x1;
3564             float y2_r = 0;
3565             if ( node.isFirstChildNode() || node.isLastChildNode()
3566                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3567                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3568                 if ( !to_graphics_file
3569                         && !to_pdf
3570                         && ( ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) && ( y1 < ( getVisibleRect().getMinY() - 20 ) ) ) || ( ( y2 > ( getVisibleRect()
3571                                 .getMaxY() + 20 ) ) && ( y1 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) ) {
3572                     // Do nothing.
3573                 }
3574                 else {
3575                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3576                         float x2c = x1 + EURO_D;
3577                         if ( x2c > x2a ) {
3578                             x2c = x2a;
3579                         }
3580                         drawLine( x1, y1, x2c, y2, g );
3581                     }
3582                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3583                         if ( y2 > y1 ) {
3584                             y2_r = y2 - ROUNDED_D;
3585                             if ( y2_r < y1 ) {
3586                                 y2_r = y1;
3587                             }
3588                             drawLine( x1, y1, x1, y2_r, g );
3589                         }
3590                         else {
3591                             y2_r = y2 + ROUNDED_D;
3592                             if ( y2_r > y1 ) {
3593                                 y2_r = y1;
3594                             }
3595                             drawLine( x1, y1, x1, y2_r, g );
3596                         }
3597                     }
3598                     else {
3599                         drawLine( x1, y1, x1, y2, g );
3600                     }
3601                 }
3602             }
3603             // draw the horizontal line
3604             if ( !to_graphics_file && !to_pdf
3605                     && ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) || ( y2 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) {
3606                 return;
3607             }
3608             float x1_r = 0;
3609             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
3610                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3611                     x1_r = x1a + ROUNDED_D;
3612                     if ( x1_r < x2a ) {
3613                         drawLine( x1_r, y2, x2a, y2, g );
3614                     }
3615                 }
3616                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3617                     final float x1c = x1a + EURO_D;
3618                     if ( x1c < x2a ) {
3619                         drawLine( x1c, y2, x2a, y2, g );
3620                     }
3621                 }
3622                 else {
3623                     drawLine( x1a, y2, x2a, y2, g );
3624                 }
3625             }
3626             else {
3627                 final double w = PhylogenyMethods.getBranchWidthValue( node );
3628                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3629                     x1_r = x1a + ROUNDED_D;
3630                     if ( x1_r < x2a ) {
3631                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
3632                     }
3633                 }
3634                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3635                     final float x1c = x1a + EURO_D;
3636                     if ( x1c < x2a ) {
3637                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
3638                     }
3639                 }
3640                 else {
3641                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
3642                 }
3643             }
3644             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3645                 if ( x1_r > x2a ) {
3646                     x1_r = x2a;
3647                 }
3648                 if ( y2 > y2_r ) {
3649                     final double diff = y2 - y2_r;
3650                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
3651                 }
3652                 else {
3653                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
3654                 }
3655                 g.draw( _arc );
3656             }
3657         }
3658         if ( node.isExternal() ) {
3659             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file, isInFoundNodes( node )
3660                     || isInCurrentExternalNodes( node ) );
3661         }
3662     }
3663
3664     final private double paintCirculars( final PhylogenyNode n,
3665                                          final Phylogeny phy,
3666                                          final float center_x,
3667                                          final float center_y,
3668                                          final double radius,
3669                                          final boolean radial_labels,
3670                                          final Graphics2D g,
3671                                          final boolean to_pdf,
3672                                          final boolean to_graphics_file ) {
3673         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
3674             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
3675                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
3676             }
3677             return _urt_nodeid_angle_map.get( n.getId() );
3678         }
3679         else {
3680             final List<PhylogenyNode> descs = n.getDescendants();
3681             double sum = 0;
3682             for( final PhylogenyNode desc : descs ) {
3683                 sum += paintCirculars( desc,
3684                                        phy,
3685                                        center_x,
3686                                        center_y,
3687                                        radius,
3688                                        radial_labels,
3689                                        g,
3690                                        to_pdf,
3691                                        to_graphics_file );
3692             }
3693             double r = 0;
3694             if ( !n.isRoot() ) {
3695                 r = 1 - ( ( ( double ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3696             }
3697             final double theta = sum / descs.size();
3698             n.setXcoord( ( float ) ( center_x + ( r * radius * Math.cos( theta ) ) ) );
3699             n.setYcoord( ( float ) ( center_y + ( r * radius * Math.sin( theta ) ) ) );
3700             _urt_nodeid_angle_map.put( n.getId(), theta );
3701             for( final PhylogenyNode desc : descs ) {
3702                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
3703             }
3704             return theta;
3705         }
3706     }
3707
3708     final private void paintCircularsLite( final PhylogenyNode n,
3709                                            final Phylogeny phy,
3710                                            final int center_x,
3711                                            final int center_y,
3712                                            final int radius,
3713                                            final Graphics2D g ) {
3714         if ( n.isExternal() ) {
3715             return;
3716         }
3717         else {
3718             final List<PhylogenyNode> descs = n.getDescendants();
3719             for( final PhylogenyNode desc : descs ) {
3720                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
3721             }
3722             float r = 0;
3723             if ( !n.isRoot() ) {
3724                 r = 1 - ( ( ( float ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3725             }
3726             final double theta = _urt_nodeid_angle_map.get( n.getId() );
3727             n.setXSecondary( ( float ) ( center_x + ( radius * r * Math.cos( theta ) ) ) );
3728             n.setYSecondary( ( float ) ( center_y + ( radius * r * Math.sin( theta ) ) ) );
3729             for( final PhylogenyNode desc : descs ) {
3730                 paintBranchCircularLite( n, desc, g );
3731             }
3732         }
3733     }
3734
3735     final private void paintCollapsedNode( final Graphics2D g,
3736                                            final PhylogenyNode node,
3737                                            final boolean to_graphics_file,
3738                                            final boolean to_pdf,
3739                                            final boolean is_in_found_nodes ) {
3740         Color c = null;
3741         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3742             c = Color.BLACK;
3743         }
3744         else if ( is_in_found_nodes ) {
3745             c = getTreeColorSet().getFoundColor();
3746         }
3747         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3748             c = getTaxonomyBasedColor( node );
3749         }
3750         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3751                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3752             c = PhylogenyMethods.getBranchColorValue( node );
3753         }
3754         else {
3755             c = getTreeColorSet().getCollapseFillColor();
3756         }
3757         double d = node.getAllExternalDescendants().size();
3758         if ( d > 1000 ) {
3759             d = ( 3 * _y_distance ) / 3;
3760         }
3761         else {
3762             d = ( Math.log10( d ) * _y_distance ) / 2.5;
3763         }
3764         final int box_size = getOptions().getDefaultNodeShapeSize();
3765         if ( d < box_size ) {
3766             d = box_size;
3767         }
3768         _polygon.reset();
3769         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() - box_size ),
3770                            ForesterUtil.roundToInt( node.getYcoord() ) );
3771         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
3772                            ForesterUtil.roundToInt( node.getYcoord() - d ) );
3773         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
3774                            ForesterUtil.roundToInt( node.getYcoord() + d ) );
3775         if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3776             g.setColor( c );
3777             g.fillPolygon( _polygon );
3778         }
3779         else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
3780             g.setColor( getBackground() );
3781             g.fillPolygon( _polygon );
3782             g.setColor( c );
3783             g.drawPolygon( _polygon );
3784         }
3785         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3786             g.setPaint( new GradientPaint( node.getXcoord() - box_size, node.getYcoord(), getBackground(), ( node
3787                     .getXcoord() + box_size ), ( float ) ( node.getYcoord() - d ), c, false ) );
3788             g.fill( _polygon );
3789             g.setPaint( c );
3790             g.draw( _polygon );
3791         }
3792         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3793     }
3794
3795     final private void paintConfidenceValues( final Graphics2D g,
3796                                               final PhylogenyNode node,
3797                                               final boolean to_pdf,
3798                                               final boolean to_graphics_file ) {
3799         final List<Confidence> confidences = node.getBranchData().getConfidences();
3800         //        if ( confidences.size() == 1 ) {
3801         //            final double value = node.getBranchData().getConfidence( 0 ).getValue();
3802         //            if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
3803         //                return;
3804         //            }
3805         //            conf_str = FORMATTER_CONFIDENCE.format( value );
3806         //        }
3807         //        else if ( confidences.size() > 1 ) {
3808         boolean one_ok = false;
3809         boolean not_first = false;
3810         Collections.sort( confidences );
3811         final StringBuilder sb = new StringBuilder();
3812         String conf_str = "";
3813         for( final Confidence confidence : confidences ) {
3814             final double value = confidence.getValue();
3815             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
3816                 if ( value >= getOptions().getMinConfidenceValue() ) {
3817                     one_ok = true;
3818                 }
3819                 if ( not_first ) {
3820                     sb.append( "/" );
3821                 }
3822                 else {
3823                     not_first = true;
3824                 }
3825                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
3826                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
3827                 if ( getOptions().isShowConfidenceStddev() ) {
3828                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
3829                         sb.append( "(" );
3830                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
3831                                                                                     getOptions()
3832                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
3833                         sb.append( ")" );
3834                     }
3835                 }
3836             }
3837             //}
3838             if ( one_ok ) {
3839                 conf_str = sb.toString();
3840             }
3841         }
3842         if ( conf_str.length() > 0 ) {
3843             final double parent_x = node.getParent().getXcoord();
3844             double x = node.getXcoord();
3845             g.setFont( getTreeFontSet().getSmallFont() );
3846             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3847                 x += EURO_D;
3848             }
3849             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3850                 x += ROUNDED_D;
3851             }
3852             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3853                 g.setColor( Color.BLACK );
3854             }
3855             else {
3856                 g.setColor( getTreeColorSet().getConfidenceColor() );
3857             }
3858             TreePanel
3859                     .drawString( conf_str,
3860                                  parent_x
3861                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
3862                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
3863                                  g );
3864         }
3865     }
3866
3867     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
3868         final int box_size = getOptions().getDefaultNodeShapeSize();
3869         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
3870         g.setColor( getTreeColorSet().getFoundColor() );
3871         g.fillRect( x - half_box_size, y - half_box_size, box_size, box_size );
3872     }
3873
3874     final private void paintGainedAndLostCharacters( final Graphics2D g,
3875                                                      final PhylogenyNode node,
3876                                                      final String gained,
3877                                                      final String lost ) {
3878         if ( node.getParent() != null ) {
3879             final double parent_x = node.getParent().getXcoord();
3880             final double x = node.getXcoord();
3881             g.setFont( getTreeFontSet().getLargeFont() );
3882             g.setColor( getTreeColorSet().getGainedCharactersColor() );
3883             if ( Constants.SPECIAL_CUSTOM ) {
3884                 g.setColor( Color.BLUE );
3885             }
3886             TreePanel
3887                     .drawString( gained,
3888                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
3889                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
3890                                  g );
3891             g.setColor( getTreeColorSet().getLostCharactersColor() );
3892             TreePanel.drawString( lost,
3893                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
3894                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
3895                                   g );
3896         }
3897     }
3898
3899     /**
3900      * Draw a box at the indicated node.
3901      * 
3902      * @param x
3903      * @param y
3904      * @param node
3905      * @param g
3906      */
3907     final private void paintNodeBox( final double x,
3908                                      final double y,
3909                                      final PhylogenyNode node,
3910                                      final Graphics2D g,
3911                                      final boolean to_pdf,
3912                                      final boolean to_graphics_file,
3913                                      final boolean is_in_found_nodes ) {
3914         if ( node.isCollapse() ) {
3915             return;
3916         }
3917         // if this node should be highlighted, do so
3918         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
3919             g.setColor( getTreeColorSet().getFoundColor() );
3920             drawOval( x - 8, y - 8, 16, 16, g );
3921             drawOval( x - 9, y - 8, 17, 17, g );
3922             drawOval( x - 9, y - 9, 18, 18, g );
3923         }
3924         if ( is_in_found_nodes ) {
3925             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
3926         }
3927         else {
3928             Color outline_color = null;
3929             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3930                 outline_color = Color.BLACK;
3931             }
3932             else if ( getControlPanel().isEvents() && AptxUtil.isHasAssignedEvent( node ) ) {
3933                 final Event event = node.getNodeData().getEvent();
3934                 if ( event.isDuplication() ) {
3935                     outline_color = getTreeColorSet().getDuplicationBoxColor();
3936                 }
3937                 else if ( event.isSpeciation() ) {
3938                     outline_color = getTreeColorSet().getSpecBoxColor();
3939                 }
3940                 else if ( event.isSpeciationOrDuplication() ) {
3941                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
3942                 }
3943             }
3944             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
3945                 outline_color = getTaxonomyBasedColor( node );
3946             }
3947             else {
3948                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
3949                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
3950                     outline_color = getTreeColorSet().getBranchColorForPdf();
3951                 }
3952             }
3953             final int box_size = getOptions().getDefaultNodeShapeSize();
3954             final int half_box_size = box_size / 2;
3955             if ( ( getOptions().isShowDefaultNodeShapesExternal() && node.isExternal() )
3956                     || ( getOptions().isShowDefaultNodeShapesInternal() && node.isInternal() )
3957                     || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
3958                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
3959                     if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3960                         drawOvalGradient( x - half_box_size,
3961                                           y - half_box_size,
3962                                           box_size,
3963                                           box_size,
3964                                           g,
3965                                           to_pdf ? Color.WHITE : outline_color,
3966                                           to_pdf ? outline_color : getBackground(),
3967                                           outline_color );
3968                     }
3969                     else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
3970                         Color background = getBackground();
3971                         if ( to_pdf ) {
3972                             background = Color.WHITE;
3973                         }
3974                         drawOvalGradient( x - half_box_size,
3975                                           y - half_box_size,
3976                                           box_size,
3977                                           box_size,
3978                                           g,
3979                                           background,
3980                                           background,
3981                                           outline_color );
3982                     }
3983                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3984                         g.setColor( outline_color );
3985                         drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
3986                     }
3987                 }
3988                 else if ( getOptions().getDefaultNodeShape() == NodeVisualization.NodeShape.RECTANGLE ) {
3989                     if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.GRADIENT ) {
3990                         drawRectGradient( x - half_box_size,
3991                                           y - half_box_size,
3992                                           box_size,
3993                                           box_size,
3994                                           g,
3995                                           to_pdf ? Color.WHITE : outline_color,
3996                                           to_pdf ? outline_color : getBackground(),
3997                                           outline_color );
3998                     }
3999                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
4000                         Color background = getBackground();
4001                         if ( to_pdf ) {
4002                             background = Color.WHITE;
4003                         }
4004                         drawRectGradient( x - half_box_size,
4005                                           y - half_box_size,
4006                                           box_size,
4007                                           box_size,
4008                                           g,
4009                                           background,
4010                                           background,
4011                                           outline_color );
4012                     }
4013                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
4014                         g.setColor( outline_color );
4015                         drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4016                     }
4017                 }
4018             }
4019         }
4020     }
4021
4022     final private void paintNodeData( final Graphics2D g,
4023                                       final PhylogenyNode node,
4024                                       final boolean to_graphics_file,
4025                                       final boolean to_pdf,
4026                                       final boolean is_in_found_nodes ) {
4027         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
4028             return;
4029         }
4030         if ( getOptions().isShowBranchLengthValues()
4031                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4032                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4033                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
4034             paintBranchLength( g, node, to_pdf, to_graphics_file );
4035         }
4036         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
4037             return;
4038         }
4039         int x = 0;
4040         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
4041         if ( getControlPanel().isShowTaxonomyImages()
4042                 && ( getImageMap() != null )
4043                 && !getImageMap().isEmpty()
4044                 && node.getNodeData().isHasTaxonomy()
4045                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
4046                         .getUris().isEmpty() ) ) {
4047             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
4048         }
4049         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4050                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
4051             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
4052         }
4053         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4054             g.setColor( Color.BLACK );
4055         }
4056         else if ( is_in_found_nodes ) {
4057             g.setColor( getTreeColorSet().getFoundColor() );
4058         }
4059         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4060             g.setColor( getTaxonomyBasedColor( node ) );
4061         }
4062         else if ( getControlPanel().isColorAccordingToAnnotation()
4063                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
4064                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
4065             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
4066         }
4067         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
4068                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
4069             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
4070         }
4071         else if ( to_pdf ) {
4072             g.setColor( Color.BLACK );
4073         }
4074         else {
4075             g.setColor( getTreeColorSet().getSequenceColor() );
4076         }
4077         _sb.setLength( 0 );
4078         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4079             _sb.append( " [" );
4080             _sb.append( node.getAllExternalDescendants().size() );
4081             _sb.append( "]" );
4082         }
4083         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
4084             if ( _sb.length() > 0 ) {
4085                 _sb.append( " " );
4086             }
4087             _sb.append( node.getName() );
4088         }
4089         if ( node.getNodeData().isHasSequence() ) {
4090             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
4091                 if ( _sb.length() > 0 ) {
4092                     _sb.append( " " );
4093                 }
4094                 _sb.append( node.getNodeData().getSequence().getSymbol() );
4095             }
4096             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
4097                 if ( _sb.length() > 0 ) {
4098                     _sb.append( " " );
4099                 }
4100                 _sb.append( node.getNodeData().getSequence().getName() );
4101             }
4102             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4103                 if ( _sb.length() > 0 ) {
4104                     _sb.append( " " );
4105                 }
4106                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4107                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4108                     _sb.append( ":" );
4109                 }
4110                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4111             }
4112         }
4113         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
4114             if ( _sb.length() > 0 ) {
4115                 _sb.append( " " );
4116             }
4117             _sb.append( propertiesToString( node ) );
4118         }
4119         g.setFont( getTreeFontSet().getLargeFont() );
4120         if ( is_in_found_nodes ) {
4121             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4122         }
4123         double down_shift_factor = 3.0;
4124         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
4125             down_shift_factor = 1;
4126         }
4127         final double pos_x = node.getXcoord() + x + 2 + half_box_size;
4128         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
4129         final String sb_str = _sb.toString();
4130         // GUILHEM_BEG ______________
4131         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
4132                 && ( _query_sequence != null ) ) {
4133             int nodeTextBoundsWidth = 0;
4134             if ( sb_str.length() > 0 ) {
4135                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
4136                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
4137             }
4138             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
4139                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
4140                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
4141                     g.setColor( getTreeColorSet().getBackgroundColor() );
4142                 }
4143             }
4144             else {
4145                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
4146                 for( final SequenceRelation seqRelation : seqRelations ) {
4147                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
4148                             .getRef1().isEqual( _query_sequence ) )
4149                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
4150                                     .getSelectedItem() );
4151                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
4152                         final double linePosX = node.getXcoord() + 2 + half_box_size;
4153                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
4154                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
4155                                 + ")";
4156                         if ( sConfidence != null ) {
4157                             double confidenceX = pos_x;
4158                             if ( sb_str.length() > 0 ) {
4159                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
4160                                         + CONFIDENCE_LEFT_MARGIN;
4161                             }
4162                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
4163                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
4164                                         .getBounds().getWidth();
4165                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
4166                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
4167                             }
4168                         }
4169                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
4170                         {
4171                             if ( nodeTextBoundsWidth == 0 ) {
4172                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
4173                             }
4174                             else {
4175                                 nodeTextBoundsWidth += 2;
4176                             }
4177                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
4178                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
4179                             break;
4180                         }
4181                     }
4182                 }
4183             }
4184         }
4185         if ( sb_str.length() > 0 ) {
4186             TreePanel.drawString( sb_str, pos_x, pos_y, g );
4187         }
4188         // GUILHEM_END _____________
4189         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
4190         // TODO FIXME need to check this one!
4191         //if ( _sb.length() > 0 ) {
4192         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
4193         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4194         //}
4195         // COMMENTED_OUT_BY_GUILHEM_END ________________
4196         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
4197                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4198                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4199             if ( _sb.length() > 0 ) {
4200                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
4201             }
4202             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
4203             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4204                 g.setColor( Color.BLACK );
4205             }
4206             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
4207                 g.setColor( calculateColorForAnnotation( ann ) );
4208             }
4209             final String ann_str = createAnnotationString( ann );
4210             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
4211                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4212             _sb.setLength( 0 );
4213             _sb.append( ann_str );
4214         }
4215         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4216                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
4217                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
4218             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
4219                     && node.getNodeData().isHasBinaryCharacters() ) {
4220                 if ( _sb.length() > 0 ) {
4221                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
4222                 }
4223                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4224                     g.setColor( Color.BLACK );
4225                 }
4226                 else {
4227                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
4228                 }
4229                 if ( getControlPanel().isShowBinaryCharacters() ) {
4230                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
4231                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
4232                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4233                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
4234                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
4235                             .getLostCharactersAsStringBuffer().toString() );
4236                 }
4237                 else {
4238                     TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
4239                                           node.getXcoord() + x + 4 + half_box_size,
4240                                           node.getYcoord()
4241                                                   + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
4242                                           g );
4243                     paintGainedAndLostCharacters( g, node, "+"
4244                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
4245                             + node.getNodeData().getBinaryCharacters().getLostCount() );
4246                 }
4247             }
4248         }
4249     }
4250
4251     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
4252                                                   final PhylogenyNode node,
4253                                                   final boolean to_pdf,
4254                                                   final boolean to_graphics_file,
4255                                                   final boolean radial_labels,
4256                                                   final double ur_angle,
4257                                                   final boolean is_in_found_nodes ) {
4258         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
4259             return;
4260         }
4261         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4262             g.setColor( Color.BLACK );
4263         }
4264         else if ( is_in_found_nodes ) {
4265             g.setColor( getTreeColorSet().getFoundColor() );
4266         }
4267         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4268             g.setColor( getTaxonomyBasedColor( node ) );
4269         }
4270         else if ( getControlPanel().isColorAccordingToAnnotation()
4271                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
4272                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
4273             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
4274         }
4275         else {
4276             g.setColor( getTreeColorSet().getSequenceColor() );
4277         }
4278         _sb.setLength( 0 );
4279         _sb.append( " " );
4280         if ( node.getNodeData().isHasTaxonomy()
4281                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4282                         .isShowTaxonomyCommonNames() ) ) {
4283             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4284             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4285                 _sb.append( taxonomy.getTaxonomyCode() );
4286                 _sb.append( " " );
4287             }
4288             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4289                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4290                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4291                     _sb.append( taxonomy.getScientificName() );
4292                     _sb.append( " (" );
4293                     _sb.append( taxonomy.getCommonName() );
4294                     _sb.append( ") " );
4295                 }
4296                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4297                     _sb.append( taxonomy.getScientificName() );
4298                     _sb.append( " " );
4299                 }
4300                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4301                     _sb.append( taxonomy.getCommonName() );
4302                     _sb.append( " " );
4303                 }
4304             }
4305             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4306                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4307                     _sb.append( taxonomy.getScientificName() );
4308                     _sb.append( " " );
4309                 }
4310             }
4311             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4312                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4313                     _sb.append( taxonomy.getCommonName() );
4314                     _sb.append( " " );
4315                 }
4316             }
4317         }
4318         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4319             _sb.append( " [" );
4320             _sb.append( node.getAllExternalDescendants().size() );
4321             _sb.append( "]" );
4322         }
4323         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
4324             if ( _sb.length() > 0 ) {
4325                 _sb.append( " " );
4326             }
4327             _sb.append( node.getName() );
4328         }
4329         if ( node.getNodeData().isHasSequence() ) {
4330             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4331                 if ( _sb.length() > 0 ) {
4332                     _sb.append( " " );
4333                 }
4334                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4335                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4336                     _sb.append( ":" );
4337                 }
4338                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4339             }
4340             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
4341                 if ( _sb.length() > 0 ) {
4342                     _sb.append( " " );
4343                 }
4344                 _sb.append( node.getNodeData().getSequence().getName() );
4345             }
4346         }
4347         g.setFont( getTreeFontSet().getLargeFont() );
4348         if ( is_in_found_nodes ) {
4349             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4350         }
4351         if ( _sb.length() > 1 ) {
4352             final String sb_str = _sb.toString();
4353             double m = 0;
4354             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4355                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
4356             }
4357             else {
4358                 m = ( float ) ( ur_angle % TWO_PI );
4359             }
4360             _at = g.getTransform();
4361             boolean need_to_reset = false;
4362             final float x_coord = node.getXcoord();
4363             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
4364             if ( radial_labels ) {
4365                 need_to_reset = true;
4366                 boolean left = false;
4367                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4368                     m -= PI;
4369                     left = true;
4370                 }
4371                 g.rotate( m, x_coord, node.getYcoord() );
4372                 if ( left ) {
4373                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
4374                 }
4375             }
4376             else {
4377                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4378                     need_to_reset = true;
4379                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
4380                 }
4381             }
4382             TreePanel.drawString( sb_str, x_coord, y_coord, g );
4383             if ( need_to_reset ) {
4384                 g.setTransform( _at );
4385             }
4386         }
4387     }
4388
4389     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
4390         if ( node.isCollapse() ) {
4391             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
4392                 paintCollapsedNode( g, node, false, false, false );
4393             }
4394             return;
4395         }
4396         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4397             g.setColor( getTreeColorSet().getFoundColor() );
4398             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
4399         }
4400         float new_x = 0;
4401         if ( !node.isExternal() && !node.isCollapse() ) {
4402             boolean first_child = true;
4403             float y2 = 0.0f;
4404             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4405             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4406                 final PhylogenyNode child_node = node.getChildNode( i );
4407                 int factor_x;
4408                 if ( !isUniformBranchLengthsForCladogram() ) {
4409                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4410                 }
4411                 else {
4412                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4413                 }
4414                 if ( first_child ) {
4415                     first_child = false;
4416                     y2 = node.getYSecondary()
4417                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
4418                                     .getNumberOfExternalNodes() ) );
4419                 }
4420                 else {
4421                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4422                 }
4423                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
4424                 new_x = x2 + node.getXSecondary();
4425                 final float diff_y = node.getYSecondary() - y2;
4426                 final float diff_x = node.getXSecondary() - new_x;
4427                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
4428                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
4429                 }
4430                 child_node.setXSecondary( new_x );
4431                 child_node.setYSecondary( y2 );
4432                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4433             }
4434         }
4435     }
4436
4437     final private void paintNodeRectangular( final Graphics2D g,
4438                                              final PhylogenyNode node,
4439                                              final boolean to_pdf,
4440                                              final boolean dynamically_hide,
4441                                              final int dynamic_hiding_factor,
4442                                              final boolean to_graphics_file ) {
4443         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
4444         if ( node.isCollapse() ) {
4445             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
4446                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4447             }
4448             return;
4449         }
4450         if ( node.isExternal() ) {
4451             ++_external_node_index;
4452         }
4453         // Confidence values
4454         if ( getControlPanel().isShowConfidenceValues()
4455                 && !node.isExternal()
4456                 && !node.isRoot()
4457                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
4458                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4459                 && node.getBranchData().isHasConfidences() ) {
4460             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
4461         }
4462         // Draw a line to root:
4463         if ( node.isRoot() && _phylogeny.isRooted() ) {
4464             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
4465         }
4466         float new_x = 0;
4467         float new_x_min = Float.MAX_VALUE;
4468         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
4469         float min_dist = 1.5f;
4470         if ( !disallow_shortcutting ) {
4471             //   System.out.println( dynamic_hiding_factor );
4472             if ( dynamic_hiding_factor > 4000 ) {
4473                 min_dist = 4;
4474             }
4475             else if ( dynamic_hiding_factor > 1000 ) {
4476                 min_dist = 3;
4477             }
4478             else if ( dynamic_hiding_factor > 100 ) {
4479                 min_dist = 2;
4480             }
4481         }
4482         if ( !node.isExternal() && !node.isCollapse() ) {
4483             boolean first_child = true;
4484             float y2 = 0.0f;
4485             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4486             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4487                 final PhylogenyNode child_node = node.getChildNode( i );
4488                 int factor_x;
4489                 if ( !isUniformBranchLengthsForCladogram() ) {
4490                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4491                 }
4492                 else {
4493                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4494                 }
4495                 if ( first_child ) {
4496                     first_child = false;
4497                     y2 = node.getYcoord()
4498                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
4499                 }
4500                 else {
4501                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
4502                 }
4503                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
4504                 new_x = x2 + node.getXcoord();
4505                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
4506                     new_x_min = x2;
4507                 }
4508                 final float diff_y = node.getYcoord() - y2;
4509                 final float diff_x = node.getXcoord() - new_x;
4510                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
4511                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
4512                     paintBranchRectangular( g,
4513                                             node.getXcoord(),
4514                                             new_x,
4515                                             node.getYcoord(),
4516                                             y2,
4517                                             child_node,
4518                                             to_pdf,
4519                                             to_graphics_file );
4520                 }
4521                 child_node.setXcoord( new_x );
4522                 child_node.setYcoord( y2 );
4523                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
4524             }
4525             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node )
4526                     || isInCurrentExternalNodes( node ) );
4527         }
4528         if ( dynamically_hide
4529                 && !is_in_found_nodes
4530                 && ( ( node.isExternal() && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) ) || ( !node
4531                         .isExternal() && ( ( new_x_min < 20 ) || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getTreeFontSet()._fm_large
4532                         .getHeight() ) ) ) ) ) {
4533             return;
4534         }
4535         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4536         paintNodeWithRenderableData( g, node, to_graphics_file, to_pdf );
4537     }
4538
4539     final private void paintNodeWithRenderableData( final Graphics2D g,
4540                                                     final PhylogenyNode node,
4541                                                     final boolean to_graphics_file,
4542                                                     final boolean to_pdf ) {
4543         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
4544             return;
4545         }
4546         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
4547             return;
4548         }
4549         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4550                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4551             RenderableDomainArchitecture rds = null;
4552             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
4553                 try {
4554                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
4555                 }
4556                 catch ( final ClassCastException cce ) {
4557                     cce.printStackTrace();
4558                 }
4559                 if ( rds != null ) {
4560                     rds.setRenderingHeight( 6 );
4561                     int x = 0;
4562                     if ( node.getNodeData().isHasTaxonomy() ) {
4563                         if ( getControlPanel().isShowTaxonomyCode()
4564                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
4565                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4566                                     .getTaxonomyCode()
4567                                     + " " );
4568                         }
4569                         if ( getControlPanel().isShowTaxonomyScientificNames()
4570                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) ) {
4571                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4572                                     .getScientificName()
4573                                     + " " );
4574                         }
4575                         if ( getControlPanel().isShowTaxonomyCommonNames()
4576                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) ) {
4577                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4578                                     .getCommonName()
4579                                     + " " );
4580                         }
4581                     }
4582                     if ( node.getNodeData().isHasSequence() ) {
4583                         if ( getControlPanel().isShowGeneNames()
4584                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) ) {
4585                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName()
4586                                     + " " );
4587                         }
4588                         if ( getControlPanel().isShowGeneSymbols()
4589                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) ) {
4590                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getSymbol()
4591                                     + " " );
4592                         }
4593                         if ( getControlPanel().isShowSequenceAcc()
4594                                 && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4595                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence()
4596                                     .getAccession().toString()
4597                                     + " " );
4598                         }
4599                     }
4600                     if ( getControlPanel().isShowNodeNames() && !ForesterUtil.isEmpty( node.getName() ) ) {
4601                         x += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
4602                     }
4603                     rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
4604                 }
4605             }
4606         }
4607         //////////////
4608         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4609                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
4610             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
4611                                                                          getStatisticsForExpressionValues(),
4612                                                                          getConfiguration() );
4613             if ( rv != null ) {
4614                 int x = 0;
4615                 PhylogenyNode my_node = node;
4616                 if ( !getControlPanel().isDrawPhylogram() ) {
4617                     my_node = getPhylogeny().getFirstExternalNode();
4618                 }
4619                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
4620                     x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
4621                 }
4622                 if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
4623                     x += getTreeFontSet()._fm_large.stringWidth( my_node.getName() + " " );
4624                 }
4625                 rv.render( my_node.getXcoord() + x, node.getYcoord() - 5, g, this, to_pdf );
4626             }
4627         }
4628         //////////////
4629     }
4630
4631     final private void paintOvRectangle( final Graphics2D g ) {
4632         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
4633         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
4634         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
4635         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
4636         final float width = getOvMaxWidth() / w_ratio;
4637         final float height = getOvMaxHeight() / h_ratio;
4638         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
4639         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
4640         g.setColor( getTreeColorSet().getFoundColor() );
4641         getOvRectangle().setRect( x, y, width, height );
4642         if ( ( width < 6 ) && ( height < 6 ) ) {
4643             drawRectFilled( x, y, 6, 6, g );
4644             getOvVirtualRectangle().setRect( x, y, 6, 6 );
4645         }
4646         else if ( width < 6 ) {
4647             drawRectFilled( x, y, 6, height, g );
4648             getOvVirtualRectangle().setRect( x, y, 6, height );
4649         }
4650         else if ( height < 6 ) {
4651             drawRectFilled( x, y, width, 6, g );
4652             getOvVirtualRectangle().setRect( x, y, width, 6 );
4653         }
4654         else {
4655             drawRect( x, y, width, height, g );
4656             if ( isInOvRect() ) {
4657                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
4658             }
4659             getOvVirtualRectangle().setRect( x, y, width, height );
4660         }
4661     }
4662
4663     final private void paintPhylogenyLite( final Graphics2D g ) {
4664         _phylogeny
4665                 .getRoot()
4666                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
4667                         .getWidth() ) ) ) );
4668         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
4669         for( final PhylogenyNode element : _nodes_in_preorder ) {
4670             paintNodeLite( g, element );
4671         }
4672         paintOvRectangle( g );
4673     }
4674
4675     /**
4676      * Paint the root branch. (Differs from others because it will always be a
4677      * single horizontal line).
4678      * @param to_graphics_file 
4679      * 
4680      * @return new x1 value
4681      */
4682     final private void paintRootBranch( final Graphics2D g,
4683                                         final float x1,
4684                                         final float y1,
4685                                         final PhylogenyNode root,
4686                                         final boolean to_pdf,
4687                                         final boolean to_graphics_file ) {
4688         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
4689         float d = getXdistance();
4690         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
4691             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
4692         }
4693         if ( d < MIN_ROOT_LENGTH ) {
4694             d = MIN_ROOT_LENGTH;
4695         }
4696         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
4697             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
4698         }
4699         else {
4700             final double w = PhylogenyMethods.getBranchWidthValue( root );
4701             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
4702         }
4703         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file, isInFoundNodes( root ) );
4704     }
4705
4706     final private void paintScale( final Graphics2D g,
4707                                    int x1,
4708                                    int y1,
4709                                    final boolean to_pdf,
4710                                    final boolean to_graphics_file ) {
4711         x1 += MOVE;
4712         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
4713         y1 -= 12;
4714         final int y2 = y1 - 8;
4715         final int y3 = y1 - 4;
4716         g.setFont( getTreeFontSet().getSmallFont() );
4717         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4718             g.setColor( Color.BLACK );
4719         }
4720         else {
4721             g.setColor( getTreeColorSet().getBranchLengthColor() );
4722         }
4723         drawLine( x1, y1, x1, y2, g );
4724         drawLine( x2, y1, x2, y2, g );
4725         drawLine( x1, y3, x2, y3, g );
4726         if ( getScaleLabel() != null ) {
4727             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
4728         }
4729     }
4730
4731     final private int paintTaxonomy( final Graphics2D g,
4732                                      final PhylogenyNode node,
4733                                      final boolean is_in_found_nodes,
4734                                      final boolean to_pdf,
4735                                      final boolean to_graphics_file,
4736                                      final double x_shift ) {
4737         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4738         g.setFont( getTreeFontSet().getLargeItalicFont() );
4739         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4740             g.setColor( Color.BLACK );
4741         }
4742         else if ( is_in_found_nodes ) {
4743             g.setFont( getTreeFontSet().getLargeItalicFont().deriveFont( TreeFontSet.BOLD_AND_ITALIC ) );
4744             g.setColor( getTreeColorSet().getFoundColor() );
4745         }
4746         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4747             g.setColor( getTaxonomyBasedColor( node ) );
4748         }
4749         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
4750                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
4751             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
4752         }
4753         else if ( to_pdf ) {
4754             g.setColor( Color.BLACK );
4755         }
4756         else {
4757             g.setColor( getTreeColorSet().getTaxonomyColor() );
4758         }
4759         final double start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
4760         final double start_y = node.getYcoord()
4761                 + ( getTreeFontSet()._fm_large.getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0 ) );
4762         _sb.setLength( 0 );
4763         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4764             _sb.append( taxonomy.getTaxonomyCode() );
4765             _sb.append( " " );
4766         }
4767         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4768             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4769                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4770                 if ( getOptions().isAbbreviateScientificTaxonNames()
4771                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4772                     abbreviateScientificName( taxonomy.getScientificName() );
4773                 }
4774                 else {
4775                     _sb.append( taxonomy.getScientificName() );
4776                 }
4777                 _sb.append( " (" );
4778                 _sb.append( taxonomy.getCommonName() );
4779                 _sb.append( ") " );
4780             }
4781             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4782                 if ( getOptions().isAbbreviateScientificTaxonNames()
4783                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4784                     abbreviateScientificName( taxonomy.getScientificName() );
4785                 }
4786                 else {
4787                     _sb.append( taxonomy.getScientificName() );
4788                 }
4789                 _sb.append( " " );
4790             }
4791             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4792                 _sb.append( taxonomy.getCommonName() );
4793                 _sb.append( " " );
4794             }
4795         }
4796         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4797             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4798                 if ( getOptions().isAbbreviateScientificTaxonNames()
4799                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4800                     abbreviateScientificName( taxonomy.getScientificName() );
4801                 }
4802                 else {
4803                     _sb.append( taxonomy.getScientificName() );
4804                 }
4805                 _sb.append( " " );
4806             }
4807         }
4808         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4809             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4810                 _sb.append( taxonomy.getCommonName() );
4811                 _sb.append( " " );
4812             }
4813         }
4814         final String label = _sb.toString();
4815         /* GUILHEM_BEG */
4816         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
4817                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
4818             // invert font color and background color to show that this is the query sequence
4819             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
4820                                                                                                           false,
4821                                                                                                           false ) )
4822                     .getBounds();
4823             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
4824             g.setColor( getTreeColorSet().getBackgroundColor() );
4825         }
4826         /* GUILHEM_END */
4827         TreePanel.drawString( label, start_x, start_y, g );
4828         if ( is_in_found_nodes ) {
4829             return getTreeFontSet()._fm_large_italic_bold.stringWidth( label );
4830         }
4831         else {
4832             return getTreeFontSet()._fm_large_italic.stringWidth( label );
4833         }
4834     }
4835
4836     final private void paintUnrooted( final PhylogenyNode n,
4837                                       final double low_angle,
4838                                       final double high_angle,
4839                                       final boolean radial_labels,
4840                                       final Graphics2D g,
4841                                       final boolean to_pdf,
4842                                       final boolean to_graphics_file ) {
4843         if ( n.isRoot() ) {
4844             n.setXcoord( getWidth() / 2 );
4845             n.setYcoord( getHeight() / 2 );
4846         }
4847         if ( n.isExternal() ) {
4848             paintNodeDataUnrootedCirc( g,
4849                                        n,
4850                                        to_pdf,
4851                                        to_graphics_file,
4852                                        radial_labels,
4853                                        ( high_angle + low_angle ) / 2,
4854                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
4855             return;
4856         }
4857         final float num_enclosed = n.getNumberOfExternalNodes();
4858         final float x = n.getXcoord();
4859         final float y = n.getYcoord();
4860         double current_angle = low_angle;
4861         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4862         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4863         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4864         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4865         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4866             final PhylogenyNode desc = n.getChildNode( i );
4867             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4868             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4869             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4870             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4871             //     continue;
4872             // }
4873             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4874             //    continue;
4875             //}
4876             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4877             //    continue;
4878             // }
4879             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4880             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4881             float length;
4882             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4883                 if ( desc.getDistanceToParent() < 0 ) {
4884                     length = 0;
4885                 }
4886                 else {
4887                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4888                 }
4889             }
4890             else {
4891                 length = getUrtFactor();
4892             }
4893             final double mid_angle = current_angle + ( arc_size / 2 );
4894             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
4895             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
4896             desc.setXcoord( new_x );
4897             desc.setYcoord( new_y );
4898             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
4899             current_angle += arc_size;
4900             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
4901             drawLine( x, y, new_x, new_y, g );
4902             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file, isInFoundNodes( desc )
4903                     || isInCurrentExternalNodes( desc ) );
4904         }
4905         if ( n.isRoot() ) {
4906             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file, isInFoundNodes( n ) );
4907         }
4908     }
4909
4910     final private void paintUnrootedLite( final PhylogenyNode n,
4911                                           final double low_angle,
4912                                           final double high_angle,
4913                                           final Graphics2D g,
4914                                           final float urt_ov_factor ) {
4915         if ( n.isRoot() ) {
4916             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
4917             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
4918             n.setXSecondary( x_pos );
4919             n.setYSecondary( y_pos );
4920         }
4921         if ( n.isExternal() ) {
4922             return;
4923         }
4924         final float num_enclosed = n.getNumberOfExternalNodes();
4925         final float x = n.getXSecondary();
4926         final float y = n.getYSecondary();
4927         double current_angle = low_angle;
4928         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4929             final PhylogenyNode desc = n.getChildNode( i );
4930             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4931             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4932             float length;
4933             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4934                 if ( desc.getDistanceToParent() < 0 ) {
4935                     length = 0;
4936                 }
4937                 else {
4938                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
4939                 }
4940             }
4941             else {
4942                 length = urt_ov_factor;
4943             }
4944             final double mid_angle = current_angle + ( arc_size / 2 );
4945             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
4946             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
4947             desc.setXSecondary( new_x );
4948             desc.setYSecondary( new_y );
4949             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
4950                 g.setColor( getTreeColorSet().getFoundColor() );
4951                 drawRectFilled( desc.getXSecondary() - 1, desc.getYSecondary() - 1, 3, 3, g );
4952                 g.setColor( getTreeColorSet().getOvColor() );
4953             }
4954             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
4955             current_angle += arc_size;
4956             drawLine( x, y, new_x, new_y, g );
4957         }
4958     }
4959
4960     final private void pasteSubtree( final PhylogenyNode node ) {
4961         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4962             errorMessageNoCutCopyPasteInUnrootedDisplay();
4963             return;
4964         }
4965         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
4966             JOptionPane.showMessageDialog( this,
4967                                            "No tree in buffer (need to copy or cut a subtree first)",
4968                                            "Attempt to paste with empty buffer",
4969                                            JOptionPane.ERROR_MESSAGE );
4970             return;
4971         }
4972         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
4973         final Object[] options = { "As sibling", "As descendant", "Cancel" };
4974         final int r = JOptionPane.showOptionDialog( this,
4975                                                     "How to paste subtree" + label + "?",
4976                                                     "Paste Subtree",
4977                                                     JOptionPane.CLOSED_OPTION,
4978                                                     JOptionPane.QUESTION_MESSAGE,
4979                                                     null,
4980                                                     options,
4981                                                     options[ 2 ] );
4982         boolean paste_as_sibling = true;
4983         if ( r == 1 ) {
4984             paste_as_sibling = false;
4985         }
4986         else if ( r != 0 ) {
4987             return;
4988         }
4989         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
4990         buffer_phy.setAllNodesToNotCollapse();
4991         PhylogenyMethods.preOrderReId( buffer_phy );
4992         buffer_phy.setRooted( true );
4993         boolean need_to_show_whole = false;
4994         if ( paste_as_sibling ) {
4995             if ( node.isRoot() ) {
4996                 JOptionPane.showMessageDialog( this,
4997                                                "Cannot paste sibling to root",
4998                                                "Attempt to paste sibling to root",
4999                                                JOptionPane.ERROR_MESSAGE );
5000                 return;
5001             }
5002             buffer_phy.addAsSibling( node );
5003         }
5004         else {
5005             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
5006                 need_to_show_whole = true;
5007                 _phylogeny = buffer_phy;
5008             }
5009             else {
5010                 buffer_phy.addAsChild( node );
5011             }
5012         }
5013         if ( getCopiedAndPastedNodes() == null ) {
5014             setCopiedAndPastedNodes( new HashSet<Long>() );
5015         }
5016         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
5017         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
5018         for( final PhylogenyNode n : nodes ) {
5019             node_ids.add( n.getId() );
5020         }
5021         node_ids.add( node.getId() );
5022         getCopiedAndPastedNodes().addAll( node_ids );
5023         setNodeInPreorderToNull();
5024         _phylogeny.externalNodesHaveChanged();
5025         _phylogeny.clearHashIdToNodeMap();
5026         _phylogeny.recalculateNumberOfExternalDescendants( true );
5027         resetNodeIdToDistToLeafMap();
5028         setEdited( true );
5029         if ( need_to_show_whole ) {
5030             getControlPanel().showWhole();
5031         }
5032         repaint();
5033     }
5034
5035     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
5036         final PropertiesMap properties = node.getNodeData().getProperties();
5037         final StringBuffer sb = new StringBuffer();
5038         boolean first = true;
5039         for( final String ref : properties.getPropertyRefs() ) {
5040             if ( first ) {
5041                 first = false;
5042             }
5043             else {
5044                 sb.append( " " );
5045             }
5046             final Property p = properties.getProperty( ref );
5047             sb.append( getPartAfterColon( p.getRef() ) );
5048             sb.append( "=" );
5049             sb.append( p.getValue() );
5050             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5051                 sb.append( getPartAfterColon( p.getUnit() ) );
5052             }
5053         }
5054         return sb;
5055     }
5056
5057     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
5058         getMainPanel().setCopiedAndPastedNodes( nodeIds );
5059     }
5060
5061     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
5062         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
5063     }
5064
5065     final private void setInOv( final boolean in_ov ) {
5066         _in_ov = in_ov;
5067     }
5068
5069     final private void setOvMaxHeight( final float ov_max_height ) {
5070         _ov_max_height = ov_max_height;
5071     }
5072
5073     final private void setOvMaxWidth( final float ov_max_width ) {
5074         _ov_max_width = ov_max_width;
5075     }
5076
5077     final private void setOvXcorrectionFactor( final float f ) {
5078         _ov_x_correction_factor = f;
5079     }
5080
5081     final private void setOvXDistance( final float ov_x_distance ) {
5082         _ov_x_distance = ov_x_distance;
5083     }
5084
5085     final private void setOvXPosition( final int ov_x_position ) {
5086         _ov_x_position = ov_x_position;
5087     }
5088
5089     final private void setOvYDistance( final float ov_y_distance ) {
5090         _ov_y_distance = ov_y_distance;
5091     }
5092
5093     final private void setOvYPosition( final int ov_y_position ) {
5094         _ov_y_position = ov_y_position;
5095     }
5096
5097     final private void setOvYStart( final int ov_y_start ) {
5098         _ov_y_start = ov_y_start;
5099     }
5100
5101     final private void setScaleDistance( final double scale_distance ) {
5102         _scale_distance = scale_distance;
5103     }
5104
5105     final private void setScaleLabel( final String scale_label ) {
5106         _scale_label = scale_label;
5107     }
5108
5109     final private void setUpUrtFactor() {
5110         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
5111                 : getVisibleRect().height;
5112         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5113             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
5114         }
5115         else {
5116             final int max_depth = _circ_max_depth;
5117             if ( max_depth > 0 ) {
5118                 setUrtFactor( d / ( 2 * max_depth ) );
5119             }
5120             else {
5121                 setUrtFactor( d / 2 );
5122             }
5123         }
5124         setUrtFactorOv( getUrtFactor() );
5125     }
5126
5127     final private void setUrtFactor( final float urt_factor ) {
5128         _urt_factor = urt_factor;
5129     }
5130
5131     final private void setUrtFactorOv( final float urt_factor_ov ) {
5132         _urt_factor_ov = urt_factor_ov;
5133     }
5134
5135     private void showExtDescNodeData( final PhylogenyNode node ) {
5136         final List<String> data = new ArrayList<String>();
5137         for( final PhylogenyNode n : node.getAllExternalDescendants() ) {
5138             switch ( getOptions().getExtDescNodeDataToReturn() ) {
5139                 case NODE_NAME:
5140                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5141                         data.add( n.getName() );
5142                     }
5143                     break;
5144                 case SEQUENCE_NAME:
5145                     if ( n.getNodeData().isHasSequence()
5146                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5147                         data.add( n.getNodeData().getSequence().getName() );
5148                     }
5149                     break;
5150                 case SEQUENCE_SYMBOL:
5151                     if ( n.getNodeData().isHasSequence()
5152                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5153                         data.add( n.getNodeData().getSequence().getSymbol() );
5154                     }
5155                     break;
5156                 case SEQUENCE_MOL_SEQ:
5157                     if ( n.getNodeData().isHasSequence()
5158                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5159                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
5160                     }
5161                     break;
5162                 case SEQUENCE_MOL_SEQ_FASTA:
5163                     final StringBuilder sb = new StringBuilder();
5164                     if ( n.getNodeData().isHasSequence()
5165                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5166                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5167                             sb.append( SequenceWriter.toFasta( n.getNodeData().getSequence().getName(), n.getNodeData()
5168                                     .getSequence().getMolecularSequence(), 60 ) );
5169                         }
5170                         else {
5171                             sb.append( SequenceWriter.toFasta( n.getName(), n.getNodeData().getSequence()
5172                                     .getMolecularSequence(), 60 ) );
5173                         }
5174                         data.add( sb.toString() );
5175                     }
5176                     break;
5177                 case SEQUENCE_ACC:
5178                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
5179                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
5180                         data.add( n.getNodeData().getSequence().getAccession().toString() );
5181                     }
5182                     break;
5183                 case TAXONOMY_SCIENTIFIC_NAME:
5184                     if ( n.getNodeData().isHasTaxonomy()
5185                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5186                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
5187                     }
5188                     break;
5189                 case TAXONOMY_CODE:
5190                     if ( n.getNodeData().isHasTaxonomy()
5191                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5192                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5193                     }
5194                     break;
5195                 case UNKNOWN:
5196                     AptxUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
5197                     break;
5198                 default:
5199                     throw new IllegalArgumentException( "unknown data element: "
5200                             + getOptions().getExtDescNodeDataToReturn() );
5201             }
5202         } // for loop
5203         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
5204                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
5205             final StringBuilder sb = new StringBuilder();
5206             for( final String d : data ) {
5207                 if ( !ForesterUtil.isEmpty( d ) ) {
5208                     if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5209                         System.out.println( d );
5210                     }
5211                     sb.append( d );
5212                     sb.append( ForesterUtil.LINE_SEPARATOR );
5213                 }
5214             }
5215             if ( sb.length() < 1 ) {
5216                 clearCurrentExternalNodesDataBuffer();
5217             }
5218             else {
5219                 setCurrentExternalNodesDataBuffer( sb );
5220             }
5221         }
5222         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5223             final StringBuilder sb = new StringBuilder();
5224             for( final String d : data ) {
5225                 if ( !ForesterUtil.isEmpty( d ) ) {
5226                     sb.append( d );
5227                     sb.append( ForesterUtil.LINE_SEPARATOR );
5228                 }
5229             }
5230             if ( sb.length() < 1 ) {
5231                 AptxUtil.showInformationMessage( this,
5232                                                  "No Appropriate Data (" + obtainTitleForExtDescNodeData() + ")",
5233                                                  "Descendants of selected node do not contain selected data" );
5234                 clearCurrentExternalNodesDataBuffer();
5235             }
5236             else {
5237                 setCurrentExternalNodesDataBuffer( sb );
5238                 final String title = "External Descendants "
5239                         + ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5240                                 : obtainTitleForExtDescNodeData() ) + " (" + data.size() + "/"
5241                         + node.getNumberOfExternalNodes() + ") For Node " + node;
5242                 final String s = sb.toString().trim();
5243                 if ( getMainPanel().getMainFrame() == null ) {
5244                     // Must be "E" applet version.
5245                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5246                     ae.showTextFrame( s, title );
5247                 }
5248                 else {
5249                     getMainPanel().getMainFrame().showTextFrame( s, title );
5250                 }
5251             }
5252         }
5253     }
5254
5255     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5256         try {
5257             if ( ( node.getName().length() > 0 )
5258                     || ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
5259                     || ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) )
5260                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
5261                     || node.getBranchData().isHasConfidences() ) {
5262                 _popup_buffer.setLength( 0 );
5263                 short lines = 0;
5264                 if ( node.getName().length() > 0 ) {
5265                     lines++;
5266                     _popup_buffer.append( node.getName() );
5267                 }
5268                 if ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5269                     lines++;
5270                     boolean enc_data = false;
5271                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5272                     if ( _popup_buffer.length() > 0 ) {
5273                         _popup_buffer.append( "\n" );
5274                     }
5275                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5276                         _popup_buffer.append( "[" );
5277                         _popup_buffer.append( tax.getTaxonomyCode() );
5278                         _popup_buffer.append( "]" );
5279                         enc_data = true;
5280                     }
5281                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5282                         if ( enc_data ) {
5283                             _popup_buffer.append( " " );
5284                         }
5285                         _popup_buffer.append( tax.getScientificName() );
5286                         enc_data = true;
5287                     }
5288                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5289                         if ( enc_data ) {
5290                             _popup_buffer.append( " (" );
5291                         }
5292                         else {
5293                             _popup_buffer.append( "(" );
5294                         }
5295                         _popup_buffer.append( tax.getCommonName() );
5296                         _popup_buffer.append( ")" );
5297                         enc_data = true;
5298                     }
5299                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5300                         if ( enc_data ) {
5301                             _popup_buffer.append( " (" );
5302                         }
5303                         else {
5304                             _popup_buffer.append( "(" );
5305                         }
5306                         _popup_buffer.append( tax.getAuthority() );
5307                         _popup_buffer.append( ")" );
5308                         enc_data = true;
5309                     }
5310                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5311                         if ( enc_data ) {
5312                             _popup_buffer.append( " [" );
5313                         }
5314                         else {
5315                             _popup_buffer.append( "[" );
5316                         }
5317                         _popup_buffer.append( tax.getRank() );
5318                         _popup_buffer.append( "]" );
5319                         enc_data = true;
5320                     }
5321                     if ( tax.getSynonyms().size() > 0 ) {
5322                         if ( enc_data ) {
5323                             _popup_buffer.append( " " );
5324                         }
5325                         _popup_buffer.append( "[" );
5326                         int counter = 1;
5327                         for( final String syn : tax.getSynonyms() ) {
5328                             if ( !ForesterUtil.isEmpty( syn ) ) {
5329                                 enc_data = true;
5330                                 _popup_buffer.append( syn );
5331                                 if ( counter < tax.getSynonyms().size() ) {
5332                                     _popup_buffer.append( ", " );
5333                                 }
5334                             }
5335                             counter++;
5336                         }
5337                         _popup_buffer.append( "]" );
5338                     }
5339                     if ( !enc_data ) {
5340                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5341                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5342                                 _popup_buffer.append( "[" );
5343                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5344                                 _popup_buffer.append( "] " );
5345                             }
5346                             _popup_buffer.append( tax.getIdentifier().getValue() );
5347                         }
5348                     }
5349                 }
5350                 if ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5351                     lines++;
5352                     boolean enc_data = false;
5353                     if ( _popup_buffer.length() > 0 ) {
5354                         _popup_buffer.append( "\n" );
5355                     }
5356                     final Sequence seq = node.getNodeData().getSequence();
5357                     if ( seq.getAccession() != null ) {
5358                         _popup_buffer.append( "[" );
5359                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5360                             _popup_buffer.append( seq.getAccession().getSource() );
5361                             _popup_buffer.append( ":" );
5362                         }
5363                         _popup_buffer.append( seq.getAccession().getValue() );
5364                         _popup_buffer.append( "]" );
5365                         enc_data = true;
5366                     }
5367                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5368                         if ( enc_data ) {
5369                             _popup_buffer.append( " [" );
5370                         }
5371                         else {
5372                             _popup_buffer.append( "[" );
5373                         }
5374                         _popup_buffer.append( seq.getSymbol() );
5375                         _popup_buffer.append( "]" );
5376                         enc_data = true;
5377                     }
5378                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5379                         if ( enc_data ) {
5380                             _popup_buffer.append( " " );
5381                         }
5382                         _popup_buffer.append( seq.getName() );
5383                     }
5384                 }
5385                 if ( node.getNodeData().isHasDate() ) {
5386                     lines++;
5387                     if ( _popup_buffer.length() > 0 ) {
5388                         _popup_buffer.append( "\n" );
5389                     }
5390                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5391                 }
5392                 if ( node.getNodeData().isHasDistribution() ) {
5393                     lines++;
5394                     if ( _popup_buffer.length() > 0 ) {
5395                         _popup_buffer.append( "\n" );
5396                     }
5397                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5398                 }
5399                 if ( node.getBranchData().isHasConfidences() ) {
5400                     final List<Confidence> confs = node.getBranchData().getConfidences();
5401                     for( final Confidence confidence : confs ) {
5402                         lines++;
5403                         if ( _popup_buffer.length() > 0 ) {
5404                             _popup_buffer.append( "\n" );
5405                         }
5406                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5407                             _popup_buffer.append( "[" );
5408                             _popup_buffer.append( confidence.getType() );
5409                             _popup_buffer.append( "] " );
5410                         }
5411                         _popup_buffer
5412                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5413                                                                                           getOptions()
5414                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5415                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5416                             _popup_buffer.append( " (sd=" );
5417                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5418                                     .getStandardDeviation(), getOptions()
5419                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5420                             _popup_buffer.append( ")" );
5421                         }
5422                     }
5423                 }
5424                 if ( node.getNodeData().isHasProperties() ) {
5425                     final PropertiesMap properties = node.getNodeData().getProperties();
5426                     for( final String ref : properties.getPropertyRefs() ) {
5427                         _popup_buffer.append( "\n" );
5428                         final Property p = properties.getProperty( ref );
5429                         _popup_buffer.append( getPartAfterColon( p.getRef() ) );
5430                         _popup_buffer.append( "=" );
5431                         _popup_buffer.append( p.getValue() );
5432                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5433                             _popup_buffer.append( getPartAfterColon( p.getUnit() ) );
5434                         }
5435                     }
5436                 }
5437                 if ( _popup_buffer.length() > 0 ) {
5438                     if ( !getConfiguration().isUseNativeUI() ) {
5439                         _rollover_popup
5440                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5441                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5442                         if ( isInFoundNodes( node ) ) {
5443                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
5444                         }
5445                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5446                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
5447                         }
5448                         else {
5449                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5450                         }
5451                     }
5452                     else {
5453                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5454                     }
5455                     _rollover_popup.setText( _popup_buffer.toString() );
5456                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5457                                                                                   _rollover_popup,
5458                                                                                   e.getLocationOnScreen().x + 10,
5459                                                                                   e.getLocationOnScreen().y
5460                                                                                           - ( lines * 20 ) );
5461                     _node_desc_popup.show();
5462                 }
5463             }
5464         }
5465         catch ( final Exception ex ) {
5466             // Do nothing.
5467         }
5468     }
5469
5470     final private void showNodeEditFrame( final PhylogenyNode n ) {
5471         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5472             // pop up edit box for single node
5473             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5474             _node_frame_index++;
5475         }
5476         else {
5477             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5478         }
5479     }
5480
5481     final private void showNodeFrame( final PhylogenyNode n ) {
5482         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5483             // pop up edit box for single node
5484             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5485             _node_frame_index++;
5486         }
5487         else {
5488             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5489         }
5490     }
5491
5492     final private void switchDisplaygetPhylogenyGraphicsType() {
5493         switch ( getPhylogenyGraphicsType() ) {
5494             case RECTANGULAR:
5495                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5496                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5497                 break;
5498             case EURO_STYLE:
5499                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5500                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5501                 break;
5502             case ROUNDED:
5503                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5504                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5505                 break;
5506             case CURVED:
5507                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5508                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5509                 break;
5510             case TRIANGULAR:
5511                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5512                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5513                 break;
5514             case CONVEX:
5515                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5516                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5517                 break;
5518             case UNROOTED:
5519                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5520                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5521                 break;
5522             case CIRCULAR:
5523                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5524                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5525                 break;
5526             default:
5527                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5528         }
5529         if ( getControlPanel().getDynamicallyHideData() != null ) {
5530             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5531                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5532             }
5533             else {
5534                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5535             }
5536         }
5537         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5538             getControlPanel().setDrawPhylogramEnabled( true );
5539         }
5540         else {
5541             getControlPanel().setDrawPhylogramEnabled( false );
5542         }
5543         if ( getMainPanel().getMainFrame() == null ) {
5544             // Must be "E" applet version.
5545             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5546                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5547         }
5548         else {
5549             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5550         }
5551     }
5552
5553     final private static void drawString( final int i, final double x, final double y, final Graphics2D g ) {
5554         g.drawString( String.valueOf( i ), ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5555     }
5556
5557     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5558         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5559     }
5560
5561     final private static String getPartAfterColon( final String s ) {
5562         final int i = s.indexOf( ':' );
5563         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
5564             return s;
5565         }
5566         return s.substring( i + 1, s.length() );
5567     }
5568
5569     final private static boolean isSequenceEmpty( final Sequence seq ) {
5570         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
5571                 && ForesterUtil.isEmpty( seq.getSymbol() );
5572     }
5573
5574     final private static boolean isTaxonomyEmpty( final Taxonomy tax ) {
5575         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
5576                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
5577                 .getSynonyms().isEmpty() );
5578     }
5579
5580     final private static boolean plusPressed( final int key_code ) {
5581         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5582                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5583     }
5584
5585     final private static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
5586         final Phylogeny new_phy = new Phylogeny();
5587         new_phy.setRooted( true );
5588         new_phy.setName( source_phy.getName() );
5589         new_phy.setDescription( source_phy.getDescription() );
5590         new_phy.setType( source_phy.getType() );
5591         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
5592         new_phy.setConfidence( source_phy.getConfidence() );
5593         new_phy.setIdentifier( source_phy.getIdentifier() );
5594         new_phy.setRoot( new_root.copyNodeDataShallow() );
5595         int i = 0;
5596         for( final PhylogenyNode n : new_root.getDescendants() ) {
5597             new_phy.getRoot().setChildNode( i++, n );
5598         }
5599         return new_phy;
5600     }
5601
5602     final private class SubtreeColorizationActionListener implements ActionListener {
5603
5604         JColorChooser _chooser;
5605         PhylogenyNode _node;
5606
5607         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5608             _chooser = chooser;
5609             _node = node;
5610         }
5611
5612         @Override
5613         public void actionPerformed( final ActionEvent e ) {
5614             final Color c = _chooser.getColor();
5615             if ( c != null ) {
5616                 colorizeSubtree( c, _node );
5617             }
5618         }
5619     }
5620 }