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