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