07a99bd5555a32e377e48bfb83cd47664b33613c
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanel.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.BasicStroke;
29 import java.awt.Color;
30 import java.awt.Cursor;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.GradientPaint;
34 import java.awt.Graphics;
35 import java.awt.Graphics2D;
36 import java.awt.Point;
37 import java.awt.Polygon;
38 import java.awt.Rectangle;
39 import java.awt.RenderingHints;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.awt.event.FocusAdapter;
43 import java.awt.event.FocusEvent;
44 import java.awt.event.InputEvent;
45 import java.awt.event.KeyAdapter;
46 import java.awt.event.KeyEvent;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.MouseWheelEvent;
49 import java.awt.event.MouseWheelListener;
50 import java.awt.font.FontRenderContext;
51 import java.awt.font.TextLayout;
52 import java.awt.geom.AffineTransform;
53 import java.awt.geom.Arc2D;
54 import java.awt.geom.CubicCurve2D;
55 import java.awt.geom.Ellipse2D;
56 import java.awt.geom.Line2D;
57 import java.awt.geom.QuadCurve2D;
58 import java.awt.geom.Rectangle2D;
59 import java.awt.image.BufferedImage;
60 import java.awt.print.PageFormat;
61 import java.awt.print.Printable;
62 import java.awt.print.PrinterException;
63 import java.io.File;
64 import java.io.IOException;
65 import java.io.UnsupportedEncodingException;
66 import java.net.URI;
67 import java.net.URISyntaxException;
68 import java.net.URLEncoder;
69 import java.text.DecimalFormat;
70 import java.text.DecimalFormatSymbols;
71 import java.text.NumberFormat;
72 import java.util.ArrayList;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.HashSet;
76 import java.util.Hashtable;
77 import java.util.List;
78 import java.util.Set;
79 import java.util.SortedSet;
80
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                             = 1000;
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 Polygon                _polygon                                           = new Polygon();
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             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
1878                     && !getMainPanel().getOptions().isShowDefaultNodeShapesInternal()
1879                     && !getMainPanel().getOptions().isShowDefaultNodeShapesExternal()
1880                     && ( ( getControlPanel() != null ) && !getControlPanel().isShowDomainArchitectures() ) ) {
1881                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
1882             }
1883             else {
1884                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
1885             }
1886             try {
1887                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING,
1888                                       RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
1889             }
1890             catch ( final Throwable e ) {
1891                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
1892             }
1893         }
1894         else {
1895             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
1896             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
1897         }
1898     }
1899
1900     final void setTinyFonts() {
1901         getTreeFontSet().tinyFonts();
1902     }
1903
1904     final void setTreeFile( final File treefile ) {
1905         _treefile = treefile;
1906     }
1907
1908     final void setXcorrectionFactor( final float f ) {
1909         _x_correction_factor = f;
1910     }
1911
1912     final void setXdistance( final float x ) {
1913         _x_distance = x;
1914     }
1915
1916     final void setYdistance( final float y ) {
1917         _y_distance = y;
1918     }
1919
1920     final void sortDescendants( final PhylogenyNode node ) {
1921         if ( !node.isExternal() ) {
1922             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
1923             if ( ( !getControlPanel().isShowTaxonomyScientificNames() && !getControlPanel().isShowTaxonomyCode() && !getControlPanel()
1924                     .isShowTaxonomyCommonNames() ) ) {
1925                 if ( ( getControlPanel().isShowSequenceAcc() || getControlPanel().isShowGeneNames() || getControlPanel()
1926                         .isShowGeneSymbols() ) ) {
1927                     pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
1928                 }
1929                 else if ( getControlPanel().isShowNodeNames() ) {
1930                     pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
1931                 }
1932             }
1933             PhylogenyMethods.sortNodeDescendents( node, pri );
1934             setNodeInPreorderToNull();
1935             _phylogeny.externalNodesHaveChanged();
1936             _phylogeny.clearHashIdToNodeMap();
1937             _phylogeny.recalculateNumberOfExternalDescendants( true );
1938             resetNodeIdToDistToLeafMap();
1939             setEdited( true );
1940         }
1941         repaint();
1942     }
1943
1944     final void subTree( final PhylogenyNode node ) {
1945         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
1946             JOptionPane.showMessageDialog( this,
1947                                            "Cannot get a sub/super tree in unrooted display",
1948                                            "Attempt to get sub/super tree in unrooted display",
1949                                            JOptionPane.WARNING_MESSAGE );
1950             return;
1951         }
1952         if ( node.isExternal() ) {
1953             JOptionPane.showMessageDialog( this,
1954                                            "Cannot get a subtree of a external node",
1955                                            "Attempt to get subtree of external node",
1956                                            JOptionPane.WARNING_MESSAGE );
1957             return;
1958         }
1959         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
1960             JOptionPane.showMessageDialog( this,
1961                                            "Cannot get a subtree of the root node",
1962                                            "Attempt to get subtree of root node",
1963                                            JOptionPane.WARNING_MESSAGE );
1964             return;
1965         }
1966         setNodeInPreorderToNull();
1967         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
1968             _sub_phylogenies[ _subtree_index ] = _phylogeny;
1969             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
1970             ++_subtree_index;
1971             _phylogeny = subTree( node, _phylogeny );
1972             updateSubSuperTreeButton();
1973         }
1974         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
1975             superTree();
1976         }
1977         _main_panel.getControlPanel().showWhole();
1978         repaint();
1979     }
1980
1981     final void superTree() {
1982         setNodeInPreorderToNull();
1983         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
1984         for( final PhylogenyNode n : temp_root.getDescendants() ) {
1985             n.setParent( temp_root );
1986         }
1987         _sub_phylogenies[ _subtree_index ] = null;
1988         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
1989         _phylogeny = _sub_phylogenies[ --_subtree_index ];
1990         updateSubSuperTreeButton();
1991     }
1992
1993     final void swap( final PhylogenyNode node ) {
1994         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
1995             return;
1996         }
1997         if ( node.getNumberOfDescendants() > 2 ) {
1998             JOptionPane.showMessageDialog( this,
1999                                            "Cannot swap descendants of nodes with more than 2 descendants",
2000                                            "Cannot swap descendants",
2001                                            JOptionPane.ERROR_MESSAGE );
2002             return;
2003         }
2004         if ( !node.isExternal() ) {
2005             node.swapChildren();
2006             setNodeInPreorderToNull();
2007             _phylogeny.externalNodesHaveChanged();
2008             _phylogeny.clearHashIdToNodeMap();
2009             _phylogeny.recalculateNumberOfExternalDescendants( true );
2010             resetNodeIdToDistToLeafMap();
2011             setEdited( true );
2012         }
2013         repaint();
2014     }
2015
2016     final void taxColor() {
2017         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
2018             return;
2019         }
2020         setWaitCursor();
2021         AptxUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
2022         _control_panel.setColorBranches( true );
2023         if ( _control_panel.getColorBranchesCb() != null ) {
2024             _control_panel.getColorBranchesCb().setSelected( true );
2025         }
2026         setEdited( true );
2027         setArrowCursor();
2028         repaint();
2029     }
2030
2031     final void updateOvSettings() {
2032         switch ( getOptions().getOvPlacement() ) {
2033             case LOWER_LEFT:
2034                 setOvXPosition( OV_BORDER );
2035                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
2036                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
2037                 break;
2038             case LOWER_RIGHT:
2039                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
2040                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
2041                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
2042                 break;
2043             case UPPER_RIGHT:
2044                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
2045                 setOvYPosition( OV_BORDER );
2046                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
2047                 break;
2048             default:
2049                 setOvXPosition( OV_BORDER );
2050                 setOvYPosition( OV_BORDER );
2051                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
2052                 break;
2053         }
2054     }
2055
2056     final void updateOvSizes() {
2057         if ( ( getWidth() > ( 1.05 * getVisibleRect().width ) ) || ( getHeight() > ( 1.05 * getVisibleRect().height ) ) ) {
2058             setOvOn( true );
2059             float l = getLongestExtNodeInfo();
2060             final float w_ratio = getOvMaxWidth() / getWidth();
2061             l *= w_ratio;
2062             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
2063             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
2064             float ov_xdist = 0;
2065             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
2066                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
2067             }
2068             else {
2069                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
2070             }
2071             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
2072             if ( ov_xdist < 0.0 ) {
2073                 ov_xdist = 0.0f;
2074             }
2075             if ( ydist < 0.0 ) {
2076                 ydist = 0.0f;
2077             }
2078             setOvXDistance( ov_xdist );
2079             final double height = _phylogeny.getHeight();
2080             if ( height > 0 ) {
2081                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
2082                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
2083             }
2084             else {
2085                 setOvXcorrectionFactor( 0 );
2086             }
2087         }
2088         else {
2089             setOvOn( false );
2090         }
2091     }
2092
2093     void updateSetOfCollapsedExternalNodes() {
2094         final Phylogeny phy = getPhylogeny();
2095         _collapsed_external_nodeid_set.clear();
2096         if ( phy != null ) {
2097             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2098                 final PhylogenyNode ext_node = it.next();
2099                 PhylogenyNode n = ext_node;
2100                 while ( !n.isRoot() ) {
2101                     if ( n.isCollapse() ) {
2102                         _collapsed_external_nodeid_set.add( ext_node.getId() );
2103                         ext_node.setCollapse( true );
2104                         continue E;
2105                     }
2106                     n = n.getParent();
2107                 }
2108             }
2109         }
2110     }
2111
2112     final void updateSubSuperTreeButton() {
2113         if ( _subtree_index < 1 ) {
2114             getControlPanel().deactivateButtonToReturnToSuperTree();
2115         }
2116         else {
2117             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
2118         }
2119     }
2120
2121     final void zoomInDomainStructure() {
2122         if ( _domain_structure_width < 2000 ) {
2123             _domain_structure_width *= 1.2;
2124         }
2125     }
2126
2127     final void zoomOutDomainStructure() {
2128         if ( _domain_structure_width > 20 ) {
2129             _domain_structure_width *= 0.8;
2130         }
2131     }
2132
2133     private void abbreviateScientificName( final String sn ) {
2134         final String[] a = sn.split( "\\s+" );
2135         _sb.append( a[ 0 ].substring( 0, 1 ) );
2136         _sb.append( a[ 1 ].substring( 0, 2 ) );
2137         if ( a.length > 2 ) {
2138             for( int i = 2; i < a.length; i++ ) {
2139                 _sb.append( " " );
2140                 _sb.append( a[ i ] );
2141             }
2142         }
2143     }
2144
2145     final private void addEmptyNode( final PhylogenyNode node ) {
2146         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2147             errorMessageNoCutCopyPasteInUnrootedDisplay();
2148             return;
2149         }
2150         final String label = createASimpleTextRepresentationOfANode( node );
2151         String msg = "";
2152         if ( ForesterUtil.isEmpty( label ) ) {
2153             msg = "How to add the new, empty node?";
2154         }
2155         else {
2156             msg = "How to add the new, empty node to node" + label + "?";
2157         }
2158         final Object[] options = { "As sibling", "As descendant", "Cancel" };
2159         final int r = JOptionPane.showOptionDialog( this,
2160                                                     msg,
2161                                                     "Addition of Empty New Node",
2162                                                     JOptionPane.CLOSED_OPTION,
2163                                                     JOptionPane.QUESTION_MESSAGE,
2164                                                     null,
2165                                                     options,
2166                                                     options[ 2 ] );
2167         boolean add_as_sibling = true;
2168         if ( r == 1 ) {
2169             add_as_sibling = false;
2170         }
2171         else if ( r != 0 ) {
2172             return;
2173         }
2174         final Phylogeny phy = new Phylogeny();
2175         phy.setRoot( new PhylogenyNode() );
2176         phy.setRooted( true );
2177         if ( add_as_sibling ) {
2178             if ( node.isRoot() ) {
2179                 JOptionPane.showMessageDialog( this,
2180                                                "Cannot add sibling to root",
2181                                                "Attempt to add sibling to root",
2182                                                JOptionPane.ERROR_MESSAGE );
2183                 return;
2184             }
2185             phy.addAsSibling( node );
2186         }
2187         else {
2188             phy.addAsChild( node );
2189         }
2190         setNodeInPreorderToNull();
2191         _phylogeny.externalNodesHaveChanged();
2192         _phylogeny.clearHashIdToNodeMap();
2193         _phylogeny.recalculateNumberOfExternalDescendants( true );
2194         resetNodeIdToDistToLeafMap();
2195         setEdited( true );
2196         repaint();
2197     }
2198
2199     final private void addToCurrentExternalNodes( final long i ) {
2200         if ( _current_external_nodes == null ) {
2201             _current_external_nodes = new HashSet<Long>();
2202         }
2203         _current_external_nodes.add( i );
2204     }
2205
2206     final private void assignGraphicsForBranchWithColorForParentBranch( final PhylogenyNode node,
2207                                                                         final boolean is_vertical,
2208                                                                         final Graphics g,
2209                                                                         final boolean to_pdf,
2210                                                                         final boolean to_graphics_file ) {
2211         final NodeClickAction action = _control_panel.getActionWhenNodeClicked();
2212         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2213             g.setColor( Color.BLACK );
2214         }
2215         else if ( ( ( action == NodeClickAction.COPY_SUBTREE ) || ( action == NodeClickAction.CUT_SUBTREE )
2216                 || ( action == NodeClickAction.DELETE_NODE_OR_SUBTREE ) || ( action == NodeClickAction.PASTE_SUBTREE ) || ( action == NodeClickAction.ADD_NEW_NODE ) )
2217                 && ( getCutOrCopiedTree() != null )
2218                 && ( getCopiedAndPastedNodes() != null )
2219                 && !to_pdf
2220                 && !to_graphics_file && getCopiedAndPastedNodes().contains( node.getId() ) ) {
2221             g.setColor( getTreeColorSet().getFoundColor() );
2222         }
2223         else if ( getControlPanel().isColorBranches() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2224             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
2225         }
2226         else if ( to_pdf ) {
2227             g.setColor( getTreeColorSet().getBranchColorForPdf() );
2228         }
2229         else {
2230             g.setColor( getTreeColorSet().getBranchColor() );
2231         }
2232     }
2233
2234     final private void blast( final PhylogenyNode node ) {
2235         if ( !isCanBlast( node ) ) {
2236             JOptionPane.showMessageDialog( this,
2237                                            "Insufficient information present",
2238                                            "Cannot Blast",
2239                                            JOptionPane.INFORMATION_MESSAGE );
2240             return;
2241         }
2242         else {
2243             final String query = Blast.obtainQueryForBlast( node );
2244             System.out.println( "query for BLAST is: " + query );
2245             char type = '?';
2246             if ( !ForesterUtil.isEmpty( query ) ) {
2247                 if ( node.getNodeData().isHasSequence() ) {
2248                     if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getType() ) ) {
2249                         if ( node.getNodeData().getSequence().getType().toLowerCase()
2250                                 .equals( PhyloXmlUtil.SEQ_TYPE_PROTEIN ) ) {
2251                             type = 'p';
2252                         }
2253                         else {
2254                             type = 'n';
2255                         }
2256                     }
2257                     else if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) {
2258                         if ( ForesterUtil.seqIsLikelyToBeAa( node.getNodeData().getSequence().getMolecularSequence() ) ) {
2259                             type = 'p';
2260                         }
2261                         else {
2262                             type = 'n';
2263                         }
2264                     }
2265                 }
2266                 if ( type == '?' ) {
2267                     if ( SequenceIdParser.isProtein( query ) ) {
2268                         type = 'p';
2269                     }
2270                     else {
2271                         type = 'n';
2272                     }
2273                 }
2274                 JApplet applet = null;
2275                 if ( isApplet() ) {
2276                     applet = obtainApplet();
2277                 }
2278                 try {
2279                     Blast.openNcbiBlastWeb( query, type == 'n', applet, this );
2280                 }
2281                 catch ( final Exception e ) {
2282                     e.printStackTrace();
2283                 }
2284                 if ( Constants.ALLOW_DDBJ_BLAST ) {
2285                     try {
2286                         System.out.println( "trying: " + query );
2287                         final Blast s = new Blast();
2288                         s.ddbjBlast( query );
2289                     }
2290                     catch ( final Exception e ) {
2291                         e.printStackTrace();
2292                     }
2293                 }
2294             }
2295         }
2296     }
2297
2298     private final int calcDynamicHidingFactor() {
2299         return ( int ) ( 0.5 + ( getTreeFontSet()._fm_large.getHeight() / ( 1.5 * getYdistance() ) ) );
2300     }
2301
2302     /**
2303      * Calculate the length of the distance between the given node and its
2304      * parent.
2305      * 
2306      * @param node
2307      * @param ext_node_x
2308      * @factor
2309      * @return the distance value
2310      */
2311     final private float calculateBranchLengthToParent( final PhylogenyNode node, final float factor ) {
2312         if ( getControlPanel().isDrawPhylogram() ) {
2313             if ( node.getDistanceToParent() < 0.0 ) {
2314                 return 0.0f;
2315             }
2316             return ( float ) ( getXcorrectionFactor() * node.getDistanceToParent() );
2317         }
2318         else {
2319             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
2320                 return getXdistance();
2321             }
2322             return getXdistance() * factor;
2323         }
2324     }
2325
2326     final private Color calculateColorForAnnotation( final SortedSet<Annotation> ann ) {
2327         Color c = getTreeColorSet().getAnnotationColor();
2328         if ( getControlPanel().isColorAccordingToAnnotation() && ( getControlPanel().getAnnotationColors() != null ) ) {
2329             final StringBuilder sb = new StringBuilder();
2330             for( final Annotation a : ann ) {
2331                 sb.append( !ForesterUtil.isEmpty( a.getRef() ) ? a.getRef() : a.getDesc() );
2332             }
2333             final String ann_str = sb.toString();
2334             if ( !ForesterUtil.isEmpty( ann_str ) ) {
2335                 c = getControlPanel().getAnnotationColors().get( ann_str );
2336                 if ( c == null ) {
2337                     c = AptxUtil.calculateColorFromString( ann_str );
2338                     getControlPanel().getAnnotationColors().put( ann_str, c );
2339                 }
2340                 if ( c == null ) {
2341                     c = getTreeColorSet().getAnnotationColor();
2342                 }
2343             }
2344         }
2345         return c;
2346     }
2347
2348     final private float calculateOvBranchLengthToParent( final PhylogenyNode node, final int factor ) {
2349         if ( getControlPanel().isDrawPhylogram() ) {
2350             if ( node.getDistanceToParent() < 0.0 ) {
2351                 return 0.0f;
2352             }
2353             return ( float ) ( getOvXcorrectionFactor() * node.getDistanceToParent() );
2354         }
2355         else {
2356             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
2357                 return getOvXDistance();
2358             }
2359             return getOvXDistance() * factor;
2360         }
2361     }
2362
2363     final private void cannotOpenBrowserWarningMessage( final String type_type ) {
2364         JOptionPane.showMessageDialog( this,
2365                                        "Cannot launch web browser for " + type_type + " data of this node",
2366                                        "Cannot launch web browser",
2367                                        JOptionPane.WARNING_MESSAGE );
2368     }
2369
2370     final private void colorizeSubtree( final Color c, final PhylogenyNode node ) {
2371         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2372             JOptionPane.showMessageDialog( this,
2373                                            "Cannot colorize subtree in unrooted display type",
2374                                            "Attempt to colorize subtree in unrooted display",
2375                                            JOptionPane.WARNING_MESSAGE );
2376             return;
2377         }
2378         _control_panel.setColorBranches( true );
2379         if ( _control_panel.getColorBranchesCb() != null ) {
2380             _control_panel.getColorBranchesCb().setSelected( true );
2381         }
2382         for( final PreorderTreeIterator it = new PreorderTreeIterator( node ); it.hasNext(); ) {
2383             it.next().getBranchData().setBranchColor( new BranchColor( c ) );
2384         }
2385         repaint();
2386     }
2387
2388     final private void colorSubtree( final PhylogenyNode node ) {
2389         Color intitial_color = null;
2390         if ( getControlPanel().isColorBranches() && ( PhylogenyMethods.getBranchColorValue( node ) != null )
2391                 && ( ( ( !node.isRoot() && ( node.getParent().getNumberOfDescendants() < 3 ) ) ) || ( node.isRoot() ) ) ) {
2392             intitial_color = PhylogenyMethods.getBranchColorValue( node );
2393         }
2394         else {
2395             intitial_color = getTreeColorSet().getBranchColor();
2396         }
2397         _color_chooser.setColor( intitial_color );
2398         _color_chooser.setPreviewPanel( new JPanel() );
2399         final JDialog dialog = JColorChooser
2400                 .createDialog( this,
2401                                "Subtree colorization",
2402                                true,
2403                                _color_chooser,
2404                                new SubtreeColorizationActionListener( _color_chooser, node ),
2405                                null );
2406         dialog.setVisible( true );
2407     }
2408
2409     final private void copySubtree( final PhylogenyNode node ) {
2410         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2411             errorMessageNoCutCopyPasteInUnrootedDisplay();
2412             return;
2413         }
2414         setNodeInPreorderToNull();
2415         setCutOrCopiedTree( _phylogeny.copy( node ) );
2416         final List<PhylogenyNode> nodes = PhylogenyMethods.getAllDescendants( node );
2417         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
2418         for( final PhylogenyNode n : nodes ) {
2419             node_ids.add( n.getId() );
2420         }
2421         node_ids.add( node.getId() );
2422         setCopiedAndPastedNodes( node_ids );
2423         repaint();
2424     }
2425
2426     private String createAnnotationString( final SortedSet<Annotation> ann ) {
2427         final StringBuilder sb = new StringBuilder();
2428         boolean first = true;
2429         for( final Annotation a : ann ) {
2430             if ( !first ) {
2431                 sb.append( "|" );
2432             }
2433             else {
2434                 first = false;
2435             }
2436             sb.append( a.asSimpleText() );
2437         }
2438         final String ann_str = sb.toString();
2439         return ann_str;
2440     }
2441
2442     final private String createASimpleTextRepresentationOfANode( final PhylogenyNode node ) {
2443         final String tax = PhylogenyMethods.getSpecies( node );
2444         String label = node.getName();
2445         if ( !ForesterUtil.isEmpty( label ) && !ForesterUtil.isEmpty( tax ) ) {
2446             label = label + " " + tax;
2447         }
2448         else if ( !ForesterUtil.isEmpty( tax ) ) {
2449             label = tax;
2450         }
2451         else {
2452             label = "";
2453         }
2454         if ( !ForesterUtil.isEmpty( label ) ) {
2455             label = " [" + label + "]";
2456         }
2457         return label;
2458     }
2459
2460     final private void cutSubtree( final PhylogenyNode node ) {
2461         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2462             errorMessageNoCutCopyPasteInUnrootedDisplay();
2463             return;
2464         }
2465         if ( node.isRoot() ) {
2466             JOptionPane.showMessageDialog( this,
2467                                            "Cannot cut entire tree as subtree",
2468                                            "Attempt to cut entire tree",
2469                                            JOptionPane.ERROR_MESSAGE );
2470             return;
2471         }
2472         final String label = createASimpleTextRepresentationOfANode( node );
2473         final int r = JOptionPane.showConfirmDialog( null,
2474                                                      "Cut subtree" + label + "?",
2475                                                      "Confirm Cutting of Subtree",
2476                                                      JOptionPane.YES_NO_OPTION );
2477         if ( r != JOptionPane.OK_OPTION ) {
2478             return;
2479         }
2480         setNodeInPreorderToNull();
2481         setCopiedAndPastedNodes( null );
2482         setCutOrCopiedTree( _phylogeny.copy( node ) );
2483         _phylogeny.deleteSubtree( node, true );
2484         _phylogeny.clearHashIdToNodeMap();
2485         _phylogeny.recalculateNumberOfExternalDescendants( true );
2486         resetNodeIdToDistToLeafMap();
2487         setEdited( true );
2488         repaint();
2489     }
2490
2491     final private void cycleColors() {
2492         getMainPanel().getTreeColorSet().cycleColorScheme();
2493         for( final TreePanel tree_panel : getMainPanel().getTreePanels() ) {
2494             tree_panel.setBackground( getMainPanel().getTreeColorSet().getBackgroundColor() );
2495         }
2496     }
2497
2498     final private void decreaseOvSize() {
2499         if ( ( getOvMaxWidth() > 20 ) && ( getOvMaxHeight() > 20 ) ) {
2500             setOvMaxWidth( getOvMaxWidth() - 5 );
2501             setOvMaxHeight( getOvMaxHeight() - 5 );
2502             updateOvSettings();
2503             getControlPanel().displayedPhylogenyMightHaveChanged( false );
2504         }
2505     }
2506
2507     final private void deleteNodeOrSubtree( final PhylogenyNode node ) {
2508         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2509             errorMessageNoCutCopyPasteInUnrootedDisplay();
2510             return;
2511         }
2512         if ( node.isRoot() && ( node.getNumberOfDescendants() != 1 ) ) {
2513             JOptionPane.showMessageDialog( this,
2514                                            "Cannot delete entire tree",
2515                                            "Attempt to delete entire tree",
2516                                            JOptionPane.ERROR_MESSAGE );
2517             return;
2518         }
2519         final String label = createASimpleTextRepresentationOfANode( node );
2520         final Object[] options = { "Node only", "Entire subtree", "Cancel" };
2521         final int r = JOptionPane.showOptionDialog( this,
2522                                                     "Delete" + label + "?",
2523                                                     "Delete Node/Subtree",
2524                                                     JOptionPane.CLOSED_OPTION,
2525                                                     JOptionPane.QUESTION_MESSAGE,
2526                                                     null,
2527                                                     options,
2528                                                     options[ 2 ] );
2529         setNodeInPreorderToNull();
2530         boolean node_only = true;
2531         if ( r == 1 ) {
2532             node_only = false;
2533         }
2534         else if ( r != 0 ) {
2535             return;
2536         }
2537         if ( node_only ) {
2538             PhylogenyMethods.removeNode( node, _phylogeny );
2539         }
2540         else {
2541             _phylogeny.deleteSubtree( node, true );
2542         }
2543         _phylogeny.externalNodesHaveChanged();
2544         _phylogeny.clearHashIdToNodeMap();
2545         _phylogeny.recalculateNumberOfExternalDescendants( true );
2546         resetNodeIdToDistToLeafMap();
2547         setEdited( true );
2548         repaint();
2549     }
2550
2551     final private void displayNodePopupMenu( final PhylogenyNode node, final int x, final int y ) {
2552         makePopupMenus( node );
2553         _node_popup_menu.putClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY, node );
2554         _node_popup_menu.show( this, x, y );
2555     }
2556
2557     final private void drawArc( final double x,
2558                                 final double y,
2559                                 final double width,
2560                                 final double heigth,
2561                                 final double start_angle,
2562                                 final double arc_angle,
2563                                 final Graphics2D g ) {
2564         _arc.setArc( x, y, width, heigth, _180_OVER_PI * start_angle, _180_OVER_PI * arc_angle, Arc2D.OPEN );
2565         g.draw( _arc );
2566     }
2567
2568     final private void drawLine( final double x1, final double y1, final double x2, final double y2, final Graphics2D g ) {
2569         if ( ( x1 == x2 ) && ( y1 == y2 ) ) {
2570             return;
2571         }
2572         _line.setLine( x1, y1, x2, y2 );
2573         g.draw( _line );
2574     }
2575
2576     final private void drawOval( 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.draw( _ellipse );
2583     }
2584
2585     final private void drawOvalFilled( final double x,
2586                                        final double y,
2587                                        final double width,
2588                                        final double heigth,
2589                                        final Graphics2D g ) {
2590         _ellipse.setFrame( x, y, width, heigth );
2591         g.fill( _ellipse );
2592     }
2593
2594     final private void drawOvalGradient( final double x,
2595                                          final double y,
2596                                          final double width,
2597                                          final double heigth,
2598                                          final Graphics2D g,
2599                                          final Color color_1,
2600                                          final Color color_2,
2601                                          final Color color_border ) {
2602         _ellipse.setFrame( x, y, width, heigth );
2603         g.setPaint( new GradientPaint( ( float ) x,
2604                                        ( float ) y,
2605                                        color_1,
2606                                        ( float ) ( x + width ),
2607                                        ( float ) ( y + heigth ),
2608                                        color_2,
2609                                        false ) );
2610         g.fill( _ellipse );
2611         if ( color_border != null ) {
2612             g.setPaint( color_border );
2613             g.draw( _ellipse );
2614         }
2615     }
2616
2617     final private void drawRect( final float x, final float y, final float width, final float heigth, final Graphics2D g ) {
2618         _rectangle.setFrame( x, y, width, heigth );
2619         g.draw( _rectangle );
2620     }
2621
2622     final private void drawRectFilled( final double x,
2623                                        final double y,
2624                                        final double width,
2625                                        final double heigth,
2626                                        final Graphics2D g ) {
2627         _rectangle.setFrame( x, y, width, heigth );
2628         g.fill( _rectangle );
2629     }
2630
2631     final private void drawRectGradient( final double x,
2632                                          final double y,
2633                                          final double width,
2634                                          final double heigth,
2635                                          final Graphics2D g,
2636                                          final Color color_1,
2637                                          final Color color_2,
2638                                          final Color color_border ) {
2639         _rectangle.setFrame( x, y, width, heigth );
2640         g.setPaint( new GradientPaint( ( float ) x,
2641                                        ( float ) y,
2642                                        color_1,
2643                                        ( float ) ( x + width ),
2644                                        ( float ) ( y + heigth ),
2645                                        color_2,
2646                                        false ) );
2647         g.fill( _rectangle );
2648         if ( color_border != null ) {
2649             g.setPaint( color_border );
2650             g.draw( _rectangle );
2651         }
2652     }
2653
2654     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
2655         final List<Uri> us = new ArrayList<Uri>();
2656         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
2657             for( final Uri uri : t.getUris() ) {
2658                 us.add( uri );
2659             }
2660         }
2661         double offset = 0;
2662         for( final Uri uri : us ) {
2663             if ( uri != null ) {
2664                 final String uri_str = uri.getValue().toString().toLowerCase();
2665                 if ( getImageMap().containsKey( uri_str ) ) {
2666                     final BufferedImage bi = getImageMap().get( uri_str );
2667                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
2668                         double scaling_factor = 1;
2669                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
2670                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
2671                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
2672                         }
2673                         // y = y - ( 0.9 * getYdistance() );
2674                         final double hs = bi.getHeight() * scaling_factor;
2675                         double ws = ( bi.getWidth() * scaling_factor ) + offset;
2676                         final double my_y = y - ( 0.5 * hs );
2677                         final int x_w = ( int ) ( x + ws + 0.5 );
2678                         final int y_h = ( int ) ( my_y + hs + 0.5 );
2679                         if ( ( ( x_w - x ) > 7 ) && ( ( y_h - my_y ) > 7 ) ) {
2680                             g.drawImage( bi,
2681                                          ( int ) ( x + 0.5 + offset ),
2682                                          ( int ) ( my_y + 0.5 ),
2683                                          x_w,
2684                                          y_h,
2685                                          0,
2686                                          0,
2687                                          bi.getWidth(),
2688                                          bi.getHeight(),
2689                                          null );
2690                             ws += 8;
2691                         }
2692                         else {
2693                             ws = 0.0;
2694                         }
2695                         offset = ws;
2696                     }
2697                 }
2698             }
2699         }
2700         return offset;
2701     }
2702
2703     final private void errorMessageNoCutCopyPasteInUnrootedDisplay() {
2704         JOptionPane.showMessageDialog( this,
2705                                        "Cannot cut, copy, paste, add, or delete subtrees/nodes in unrooted display",
2706                                        "Attempt to cut/copy/paste/add/delete in unrooted display",
2707                                        JOptionPane.ERROR_MESSAGE );
2708     }
2709
2710     final private Set<Long> getCopiedAndPastedNodes() {
2711         return getMainPanel().getCopiedAndPastedNodes();
2712     }
2713
2714     final private Set<Long> getCurrentExternalNodes() {
2715         return _current_external_nodes;
2716     }
2717
2718     final private Phylogeny getCutOrCopiedTree() {
2719         return getMainPanel().getCutOrCopiedTree();
2720     }
2721
2722     final private float getLastDragPointX() {
2723         return _last_drag_point_x;
2724     }
2725
2726     final private float getLastDragPointY() {
2727         return _last_drag_point_y;
2728     }
2729
2730     final private short getMaxBranchesToLeaf( final PhylogenyNode node ) {
2731         if ( !_nodeid_dist_to_leaf.containsKey( node.getId() ) ) {
2732             final short m = PhylogenyMethods.calculateMaxBranchesToLeaf( node );
2733             _nodeid_dist_to_leaf.put( node.getId(), m );
2734             return m;
2735         }
2736         else {
2737             return _nodeid_dist_to_leaf.get( node.getId() );
2738         }
2739     }
2740
2741     final private double getMaxDistanceToRoot() {
2742         if ( _max_distance_to_root < 0 ) {
2743             recalculateMaxDistanceToRoot();
2744         }
2745         return _max_distance_to_root;
2746     }
2747
2748     final private float getOvMaxHeight() {
2749         return _ov_max_height;
2750     }
2751
2752     final private float getOvMaxWidth() {
2753         return _ov_max_width;
2754     }
2755
2756     final private float getOvXcorrectionFactor() {
2757         return _ov_x_correction_factor;
2758     }
2759
2760     final private float getOvXDistance() {
2761         return _ov_x_distance;
2762     }
2763
2764     final private int getOvXPosition() {
2765         return _ov_x_position;
2766     }
2767
2768     final private float getOvYDistance() {
2769         return _ov_y_distance;
2770     }
2771
2772     final private int getOvYPosition() {
2773         return _ov_y_position;
2774     }
2775
2776     final private int getOvYStart() {
2777         return _ov_y_start;
2778     }
2779
2780     final private double getScaleDistance() {
2781         return _scale_distance;
2782     }
2783
2784     final private String getScaleLabel() {
2785         return _scale_label;
2786     }
2787
2788     final private TreeFontSet getTreeFontSet() {
2789         return getMainPanel().getTreeFontSet();
2790     }
2791
2792     final private float getUrtFactor() {
2793         return _urt_factor;
2794     }
2795
2796     final private float getUrtFactorOv() {
2797         return _urt_factor_ov;
2798     }
2799
2800     final private void handleClickToAction( final NodeClickAction action, final PhylogenyNode node ) {
2801         switch ( action ) {
2802             case SHOW_DATA:
2803                 showNodeFrame( node );
2804                 break;
2805             case COLLAPSE:
2806                 collapse( node );
2807                 break;
2808             case REROOT:
2809                 reRoot( node );
2810                 break;
2811             case SUBTREE:
2812                 subTree( node );
2813                 break;
2814             case SWAP:
2815                 swap( node );
2816                 break;
2817             case COLOR_SUBTREE:
2818                 colorSubtree( node );
2819                 break;
2820             case OPEN_SEQ_WEB:
2821                 openSeqWeb( node );
2822                 break;
2823             case BLAST:
2824                 blast( node );
2825                 break;
2826             case OPEN_TAX_WEB:
2827                 openTaxWeb( node );
2828                 break;
2829             case CUT_SUBTREE:
2830                 cutSubtree( node );
2831                 break;
2832             case COPY_SUBTREE:
2833                 copySubtree( node );
2834                 break;
2835             case PASTE_SUBTREE:
2836                 pasteSubtree( node );
2837                 break;
2838             case DELETE_NODE_OR_SUBTREE:
2839                 deleteNodeOrSubtree( node );
2840                 break;
2841             case ADD_NEW_NODE:
2842                 addEmptyNode( node );
2843                 break;
2844             case EDIT_NODE_DATA:
2845                 showNodeEditFrame( node );
2846                 break;
2847             case SELECT_NODES:
2848                 selectNode( node );
2849                 break;
2850             case SORT_DESCENDENTS:
2851                 sortDescendants( node );
2852                 break;
2853             case GET_EXT_DESC_DATA:
2854                 showExtDescNodeData( node );
2855                 break;
2856             default:
2857                 throw new IllegalArgumentException( "unknown action: " + action );
2858         }
2859     }
2860
2861     final private void increaseCurrentExternalNodesDataBufferChangeCounter() {
2862         _current_external_nodes_data_buffer_change_counter++;
2863     }
2864
2865     final private void increaseOvSize() {
2866         if ( ( getOvMaxWidth() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth() / 2 ) )
2867                 && ( getOvMaxHeight() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
2868                         .getHeight() / 2 ) ) ) {
2869             setOvMaxWidth( getOvMaxWidth() + 5 );
2870             setOvMaxHeight( getOvMaxHeight() + 5 );
2871             updateOvSettings();
2872             getControlPanel().displayedPhylogenyMightHaveChanged( false );
2873         }
2874     }
2875
2876     final private void init() {
2877         _color_chooser = new JColorChooser();
2878         _rollover_popup = new JTextArea();
2879         _rollover_popup.setFont( POPUP_FONT );
2880         resetNodeIdToDistToLeafMap();
2881         setTextAntialias();
2882         setTreeFile( null );
2883         setEdited( false );
2884         initializeOvSettings();
2885         setStartingAngle( ( TWO_PI * 3 ) / 4 );
2886         final ImageLoader il = new ImageLoader( this );
2887         new Thread( il ).start();
2888     }
2889
2890     final private void initializeOvSettings() {
2891         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
2892         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
2893     }
2894
2895     final private boolean inOvVirtualRectangle( final int x, final int y ) {
2896         return ( ( x >= ( getOvVirtualRectangle().x - 1 ) )
2897                 && ( x <= ( getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 ) )
2898                 && ( y >= ( getOvVirtualRectangle().y - 1 ) ) && ( y <= ( getOvVirtualRectangle().y
2899                 + getOvVirtualRectangle().height + 1 ) ) );
2900     }
2901
2902     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
2903         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
2904     }
2905
2906     final private boolean isCanBlast( final PhylogenyNode node ) {
2907         if ( !node.getNodeData().isHasSequence() && ForesterUtil.isEmpty( node.getName() ) ) {
2908             return false;
2909         }
2910         return Blast.isContainsQueryForBlast( node );
2911     }
2912
2913     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
2914         if ( node.getNodeData().isHasTaxonomy()
2915                 && ( ( ( node.getNodeData().getTaxonomy().getIdentifier() != null )
2916                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getProvider() )
2917                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && getConfiguration()
2918                         .isHasWebLink( node.getNodeData().getTaxonomy().getIdentifier().getProvider().toLowerCase() ) )
2919                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
2920                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
2921                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) || ( ( node
2922                         .getNodeData().getTaxonomy().getIdentifier() != null )
2923                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && node
2924                         .getNodeData().getTaxonomy().getIdentifier().getValue().startsWith( "http://" ) ) ) ) {
2925             return true;
2926         }
2927         else {
2928             return false;
2929         }
2930     }
2931
2932     final private boolean isInCurrentExternalNodes( final PhylogenyNode node ) {
2933         return ( ( getCurrentExternalNodes() != null ) && getCurrentExternalNodes().contains( node.getId() ) );
2934     }
2935
2936     final private boolean isInFoundNodes( final PhylogenyNode node ) {
2937         return ( ( getFoundNodes() != null ) && getFoundNodes().contains( node.getId() ) );
2938     }
2939
2940     final private boolean isInOv() {
2941         return _in_ov;
2942     }
2943
2944     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
2945         int y_dist = 40;
2946         if ( getControlPanel().isShowTaxonomyImages() ) {
2947             y_dist = 40 + ( int ) getYdistance();
2948         }
2949         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - y_dist ) )
2950                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + y_dist ) ) || ( ( node.getParent() != null ) && ( node
2951                 .getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
2952     }
2953
2954     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
2955         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - 20 ) )
2956                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + 20 ) )
2957                 || ( node.getXcoord() < ( getVisibleRect().getMinX() - 20 ) ) || ( node.getXcoord() > ( getVisibleRect()
2958                 .getMaxX() + 20 ) ) );
2959     }
2960
2961     final private boolean isNonLinedUpCladogram() {
2962         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
2963     }
2964
2965     final private boolean isUniformBranchLengthsForCladogram() {
2966         return getOptions().getCladogramType() == CLADOGRAM_TYPE.TOTAL_NODE_SUM_DEP;
2967     }
2968
2969     final private void keyPressedCalls( final KeyEvent e ) {
2970         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
2971             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
2972                 if ( !isInOvRect() ) {
2973                     setInOvRect( true );
2974                 }
2975             }
2976             else if ( isInOvRect() ) {
2977                 setInOvRect( false );
2978             }
2979         }
2980         if ( e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK ) {
2981             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
2982                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
2983                 getMainPanel().getTreeFontSet().mediumFonts();
2984                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
2985             }
2986             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
2987                 getMainPanel().getTreeFontSet().decreaseFontSize( 1, false );
2988                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
2989             }
2990             else if ( plusPressed( e.getKeyCode() ) ) {
2991                 getMainPanel().getTreeFontSet().increaseFontSize();
2992                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
2993             }
2994         }
2995         else {
2996             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
2997                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
2998                 getControlPanel().showWhole();
2999             }
3000             else if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
3001                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
3002                 if ( e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK ) {
3003                     if ( e.getKeyCode() == KeyEvent.VK_UP ) {
3004                         getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
3005                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3006                     }
3007                     else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
3008                         getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
3009                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3010                     }
3011                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
3012                         getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
3013                                                                    Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
3014                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3015                     }
3016                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
3017                         getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
3018                                                                   Constants.WHEEL_ZOOM_IN_FACTOR );
3019                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3020                     }
3021                 }
3022                 else {
3023                     final int d = 80;
3024                     int dx = 0;
3025                     int dy = -d;
3026                     if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
3027                         dy = d;
3028                     }
3029                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
3030                         dx = -d;
3031                         dy = 0;
3032                     }
3033                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
3034                         dx = d;
3035                         dy = 0;
3036                     }
3037                     final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
3038                     scroll_position.x = scroll_position.x + dx;
3039                     scroll_position.y = scroll_position.y + dy;
3040                     if ( scroll_position.x <= 0 ) {
3041                         scroll_position.x = 0;
3042                     }
3043                     else {
3044                         final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
3045                                 - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
3046                         if ( scroll_position.x >= max_x ) {
3047                             scroll_position.x = max_x;
3048                         }
3049                     }
3050                     if ( scroll_position.y <= 0 ) {
3051                         scroll_position.y = 0;
3052                     }
3053                     else {
3054                         final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
3055                                 - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
3056                         if ( scroll_position.y >= max_y ) {
3057                             scroll_position.y = max_y;
3058                         }
3059                     }
3060                     repaint();
3061                     getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
3062                 }
3063             }
3064             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
3065                 getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
3066                 getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
3067                                                            Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
3068                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3069             }
3070             else if ( plusPressed( e.getKeyCode() ) ) {
3071                 getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
3072                                                           Constants.WHEEL_ZOOM_IN_FACTOR );
3073                 getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
3074                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3075             }
3076             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
3077                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3078                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3079                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
3080                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3081                 }
3082             }
3083             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
3084                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3085                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3086                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
3087                     if ( getStartingAngle() < 0 ) {
3088                         setStartingAngle( TWO_PI + getStartingAngle() );
3089                     }
3090                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3091                 }
3092             }
3093             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
3094                 boolean selected = false;
3095                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
3096                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
3097                     selected = true;
3098                 }
3099                 else {
3100                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
3101                 }
3102                 if ( getMainPanel().getMainFrame() == null ) {
3103                     // Must be "E" applet version.
3104                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
3105                     if ( ae.getlabelDirectionCbmi() != null ) {
3106                         ae.getlabelDirectionCbmi().setSelected( selected );
3107                     }
3108                 }
3109                 else {
3110                     getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
3111                 }
3112                 repaint();
3113             }
3114             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
3115                 switchDisplaygetPhylogenyGraphicsType();
3116                 repaint();
3117             }
3118             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
3119                 cycleColors();
3120                 repaint();
3121             }
3122             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
3123                 MainFrame.cycleOverview( getOptions(), this );
3124                 repaint();
3125             }
3126             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
3127                 increaseOvSize();
3128             }
3129             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
3130                 decreaseOvSize();
3131             }
3132             e.consume();
3133         }
3134     }
3135
3136     final private void makePopupMenus( final PhylogenyNode node ) {
3137         _node_popup_menu = new JPopupMenu();
3138         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
3139         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
3140         for( int i = 0; i < clickto_names.size(); i++ ) {
3141             final String title = clickto_names.get( i );
3142             _node_popup_menu_items[ i ] = new JMenuItem( title );
3143             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
3144                 final String id = isCanOpenSeqWeb( node );
3145                 if ( !ForesterUtil.isEmpty( id ) ) {
3146                     _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " [" + id + "]" );
3147                     _node_popup_menu_items[ i ].setEnabled( true );
3148                 }
3149                 else {
3150                     _node_popup_menu_items[ i ].setEnabled( false );
3151                 }
3152             }
3153             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
3154                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
3155             }
3156             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
3157                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
3158             }
3159             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
3160                 if ( !getOptions().isEditable() ) {
3161                     continue;
3162                 }
3163                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
3164             }
3165             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
3166                 if ( !getOptions().isEditable() ) {
3167                     continue;
3168                 }
3169                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
3170             }
3171             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
3172                 if ( !getOptions().isEditable() ) {
3173                     continue;
3174                 }
3175                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
3176             }
3177             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
3178                 if ( !getOptions().isEditable() ) {
3179                     continue;
3180                 }
3181                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
3182             }
3183             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
3184                 if ( !getOptions().isEditable() ) {
3185                     continue;
3186                 }
3187             }
3188             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
3189                 if ( !getOptions().isEditable() ) {
3190                     continue;
3191                 }
3192             }
3193             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
3194                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
3195             }
3196             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
3197                 _node_popup_menu_items[ i ].setEnabled( ( isCanCollapse() && !node.isExternal() ) );
3198             }
3199             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
3200                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
3201             }
3202             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
3203                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
3204             }
3205             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
3206                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
3207             }
3208             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
3209                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
3210             }
3211             _node_popup_menu_items[ i ].addActionListener( this );
3212             _node_popup_menu.add( _node_popup_menu_items[ i ] );
3213         }
3214     }
3215
3216     private final String obtainTitleForExtDescNodeData() {
3217         switch ( getOptions().getExtDescNodeDataToReturn() ) {
3218             case NODE_NAME:
3219                 return "Node Names";
3220             case SEQUENCE_NAME:
3221                 return "Sequence Names";
3222             case SEQUENCE_SYMBOL:
3223                 return "Sequence Symbols";
3224             case SEQUENCE_MOL_SEQ:
3225                 return "Molecular Sequences";
3226             case SEQUENCE_MOL_SEQ_FASTA:
3227                 return "Molecular Sequences (Fasta)";
3228             case SEQUENCE_ACC:
3229                 return "Sequence Accessors";
3230             case TAXONOMY_SCIENTIFIC_NAME:
3231                 return "Scientific Names";
3232             case TAXONOMY_CODE:
3233                 return "Taxonomy Codes";
3234             case UNKNOWN:
3235                 return "User Selected Data";
3236             default:
3237                 throw new IllegalArgumentException( "unknown data element: "
3238                         + getOptions().getExtDescNodeDataToReturn() );
3239         }
3240     }
3241
3242     final private String isCanOpenSeqWeb( final PhylogenyNode node ) {
3243         if ( node.getNodeData().isHasSequence()
3244                 && ( node.getNodeData().getSequence().getAccession() != null )
3245                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() )
3246                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getValue() )
3247                 && getConfiguration().isHasWebLink( node.getNodeData().getSequence().getAccession().getSource()
3248                         .toLowerCase() ) ) {
3249             return node.getNodeData().getSequence().getAccession().getSource();
3250         }
3251         String v = ForesterUtil.extractUniProtKbProteinSeqIdentifier( node );
3252         if ( ForesterUtil.isEmpty( v ) ) {
3253             v = ForesterUtil.extractGenbankAccessor( node );
3254         }
3255         if ( ForesterUtil.isEmpty( v ) ) {
3256             v = ForesterUtil.extractRefSeqAccessorAccessor( node );
3257         }
3258         return v;
3259     }
3260
3261     final private void openSeqWeb( final PhylogenyNode node ) {
3262         if ( ForesterUtil.isEmpty( isCanOpenSeqWeb( node ) ) ) {
3263             cannotOpenBrowserWarningMessage( "sequence" );
3264             return;
3265         }
3266         final String uri_str = AptxUtil.createUriForSeqWeb( node, getConfiguration(), this );
3267         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3268             try {
3269                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3270                                            isApplet(),
3271                                            isApplet() ? obtainApplet() : null,
3272                                            "_aptx_seq" );
3273             }
3274             catch ( final IOException e ) {
3275                 AptxUtil.showErrorMessage( this, e.toString() );
3276                 e.printStackTrace();
3277             }
3278             catch ( final URISyntaxException e ) {
3279                 AptxUtil.showErrorMessage( this, e.toString() );
3280                 e.printStackTrace();
3281             }
3282         }
3283         else {
3284             cannotOpenBrowserWarningMessage( "sequence" );
3285         }
3286     }
3287
3288     final private void openTaxWeb( final PhylogenyNode node ) {
3289         if ( !isCanOpenTaxWeb( node ) ) {
3290             cannotOpenBrowserWarningMessage( "taxonomic" );
3291             return;
3292         }
3293         String uri_str = null;
3294         final Taxonomy tax = node.getNodeData().getTaxonomy();
3295         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
3296                 && getConfiguration().isHasWebLink( tax.getIdentifier().getProvider().toLowerCase() ) ) {
3297             final String type = tax.getIdentifier().getProvider().toLowerCase();
3298             final WebLink weblink = getConfiguration().getWebLink( type );
3299             try {
3300                 uri_str = weblink.getUrl() + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
3301             }
3302             catch ( final UnsupportedEncodingException e ) {
3303                 AptxUtil.showErrorMessage( this, e.toString() );
3304                 e.printStackTrace();
3305             }
3306         }
3307         else if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3308                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
3309             try {
3310                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
3311             }
3312             catch ( final URISyntaxException e ) {
3313                 AptxUtil.showErrorMessage( this, e.toString() );
3314                 uri_str = null;
3315                 e.printStackTrace();
3316             }
3317         }
3318         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
3319             try {
3320                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3321                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
3322             }
3323             catch ( final UnsupportedEncodingException e ) {
3324                 AptxUtil.showErrorMessage( this, e.toString() );
3325                 e.printStackTrace();
3326             }
3327         }
3328         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
3329             try {
3330                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3331                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
3332             }
3333             catch ( final UnsupportedEncodingException e ) {
3334                 AptxUtil.showErrorMessage( this, e.toString() );
3335                 e.printStackTrace();
3336             }
3337         }
3338         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
3339             try {
3340                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3341                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
3342             }
3343             catch ( final UnsupportedEncodingException e ) {
3344                 AptxUtil.showErrorMessage( this, e.toString() );
3345                 e.printStackTrace();
3346             }
3347         }
3348         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3349             try {
3350                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3351                                            isApplet(),
3352                                            isApplet() ? obtainApplet() : null,
3353                                            "_aptx_tax" );
3354             }
3355             catch ( final IOException e ) {
3356                 AptxUtil.showErrorMessage( this, e.toString() );
3357                 e.printStackTrace();
3358             }
3359             catch ( final URISyntaxException e ) {
3360                 AptxUtil.showErrorMessage( this, e.toString() );
3361                 e.printStackTrace();
3362             }
3363         }
3364         else {
3365             cannotOpenBrowserWarningMessage( "taxonomic" );
3366         }
3367     }
3368
3369     final private void paintBranchLength( final Graphics2D g,
3370                                           final PhylogenyNode node,
3371                                           final boolean to_pdf,
3372                                           final boolean to_graphics_file ) {
3373         g.setFont( getTreeFontSet().getSmallFont() );
3374         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3375             g.setColor( Color.BLACK );
3376         }
3377         else {
3378             g.setColor( getTreeColorSet().getBranchLengthColor() );
3379         }
3380         if ( !node.isRoot() ) {
3381             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3382                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3383                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3384             }
3385             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3386                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3387                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3388             }
3389             else {
3390                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3391                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
3392             }
3393         }
3394         else {
3395             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
3396                     - getTreeFontSet()._small_max_descent, g );
3397         }
3398     }
3399
3400     final private void paintBranchLite( final Graphics2D g,
3401                                         final float x1,
3402                                         final float x2,
3403                                         final float y1,
3404                                         final float y2,
3405                                         final PhylogenyNode node ) {
3406         g.setColor( getTreeColorSet().getOvColor() );
3407         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3408             drawLine( x1, y1, x2, y2, g );
3409         }
3410         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3411             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3412             ( g ).draw( _quad_curve );
3413         }
3414         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3415             final float dx = x2 - x1;
3416             final float dy = y2 - y1;
3417             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3418                     + ( dy * 0.8f ), x2, y2 );
3419             ( g ).draw( _cubic_curve );
3420         }
3421         else {
3422             final float x2a = x2;
3423             final float x1a = x1;
3424             // draw the vertical line
3425             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
3426                 drawLine( x1, y1, x1, y2, g );
3427             }
3428             // draw the horizontal line
3429             drawLine( x1a, y2, x2a, y2, g );
3430         }
3431     }
3432
3433     /**
3434      * Paint a branch which consists of a vertical and a horizontal bar
3435      * @param is_ind_found_nodes 
3436      */
3437     final private void paintBranchRectangular( final Graphics2D g,
3438                                                final float x1,
3439                                                final float x2,
3440                                                final float y1,
3441                                                final float y2,
3442                                                final PhylogenyNode node,
3443                                                final boolean to_pdf,
3444                                                final boolean to_graphics_file ) {
3445         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
3446         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3447             drawLine( x1, y1, x2, y2, g );
3448         }
3449         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3450             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3451             g.draw( _quad_curve );
3452         }
3453         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3454             final float dx = x2 - x1;
3455             final float dy = y2 - y1;
3456             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3457                     + ( dy * 0.8f ), x2, y2 );
3458             g.draw( _cubic_curve );
3459         }
3460         else {
3461             final float x2a = x2;
3462             final float x1a = x1;
3463             float y2_r = 0;
3464             if ( node.isFirstChildNode() || node.isLastChildNode()
3465                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3466                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3467                 if ( !to_graphics_file
3468                         && !to_pdf
3469                         && ( ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) && ( y1 < ( getVisibleRect().getMinY() - 20 ) ) ) || ( ( y2 > ( getVisibleRect()
3470                                 .getMaxY() + 20 ) ) && ( y1 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) ) {
3471                     // Do nothing.
3472                 }
3473                 else {
3474                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3475                         float x2c = x1 + EURO_D;
3476                         if ( x2c > x2a ) {
3477                             x2c = x2a;
3478                         }
3479                         drawLine( x1, y1, x2c, y2, g );
3480                     }
3481                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3482                         if ( y2 > y1 ) {
3483                             y2_r = y2 - ROUNDED_D;
3484                             if ( y2_r < y1 ) {
3485                                 y2_r = y1;
3486                             }
3487                             drawLine( x1, y1, x1, y2_r, g );
3488                         }
3489                         else {
3490                             y2_r = y2 + ROUNDED_D;
3491                             if ( y2_r > y1 ) {
3492                                 y2_r = y1;
3493                             }
3494                             drawLine( x1, y1, x1, y2_r, g );
3495                         }
3496                     }
3497                     else {
3498                         drawLine( x1, y1, x1, y2, g );
3499                     }
3500                 }
3501             }
3502             // draw the horizontal line
3503             if ( !to_graphics_file && !to_pdf
3504                     && ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) || ( y2 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) {
3505                 return;
3506             }
3507             float x1_r = 0;
3508             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
3509                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3510                     x1_r = x1a + ROUNDED_D;
3511                     if ( x1_r < x2a ) {
3512                         drawLine( x1_r, y2, x2a, y2, g );
3513                     }
3514                 }
3515                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3516                     final float x1c = x1a + EURO_D;
3517                     if ( x1c < x2a ) {
3518                         drawLine( x1c, y2, x2a, y2, g );
3519                     }
3520                 }
3521                 else {
3522                     drawLine( x1a, y2, x2a, y2, g );
3523                 }
3524             }
3525             else {
3526                 final double w = PhylogenyMethods.getBranchWidthValue( node );
3527                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3528                     x1_r = x1a + ROUNDED_D;
3529                     if ( x1_r < x2a ) {
3530                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
3531                     }
3532                 }
3533                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3534                     final float x1c = x1a + EURO_D;
3535                     if ( x1c < x2a ) {
3536                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
3537                     }
3538                 }
3539                 else {
3540                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
3541                 }
3542             }
3543             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3544                 if ( x1_r > x2a ) {
3545                     x1_r = x2a;
3546                 }
3547                 if ( y2 > y2_r ) {
3548                     final double diff = y2 - y2_r;
3549                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
3550                 }
3551                 else {
3552                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
3553                 }
3554                 g.draw( _arc );
3555             }
3556         }
3557         if ( node.isExternal() ) {
3558             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file, isInFoundNodes( node )
3559                     || isInCurrentExternalNodes( node ) );
3560         }
3561     }
3562
3563     final private double paintCirculars( final PhylogenyNode n,
3564                                          final Phylogeny phy,
3565                                          final float center_x,
3566                                          final float center_y,
3567                                          final double radius,
3568                                          final boolean radial_labels,
3569                                          final Graphics2D g,
3570                                          final boolean to_pdf,
3571                                          final boolean to_graphics_file ) {
3572         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
3573             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
3574                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
3575             }
3576             return _urt_nodeid_angle_map.get( n.getId() );
3577         }
3578         else {
3579             final List<PhylogenyNode> descs = n.getDescendants();
3580             double sum = 0;
3581             for( final PhylogenyNode desc : descs ) {
3582                 sum += paintCirculars( desc,
3583                                        phy,
3584                                        center_x,
3585                                        center_y,
3586                                        radius,
3587                                        radial_labels,
3588                                        g,
3589                                        to_pdf,
3590                                        to_graphics_file );
3591             }
3592             double r = 0;
3593             if ( !n.isRoot() ) {
3594                 r = 1 - ( ( ( double ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3595             }
3596             final double theta = sum / descs.size();
3597             n.setXcoord( ( float ) ( center_x + ( r * radius * Math.cos( theta ) ) ) );
3598             n.setYcoord( ( float ) ( center_y + ( r * radius * Math.sin( theta ) ) ) );
3599             _urt_nodeid_angle_map.put( n.getId(), theta );
3600             for( final PhylogenyNode desc : descs ) {
3601                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
3602             }
3603             return theta;
3604         }
3605     }
3606
3607     final private void paintCircularsLite( final PhylogenyNode n,
3608                                            final Phylogeny phy,
3609                                            final int center_x,
3610                                            final int center_y,
3611                                            final int radius,
3612                                            final Graphics2D g ) {
3613         if ( n.isExternal() ) {
3614             return;
3615         }
3616         else {
3617             final List<PhylogenyNode> descs = n.getDescendants();
3618             for( final PhylogenyNode desc : descs ) {
3619                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
3620             }
3621             float r = 0;
3622             if ( !n.isRoot() ) {
3623                 r = 1 - ( ( ( float ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3624             }
3625             final double theta = _urt_nodeid_angle_map.get( n.getId() );
3626             n.setXSecondary( ( float ) ( center_x + ( radius * r * Math.cos( theta ) ) ) );
3627             n.setYSecondary( ( float ) ( center_y + ( radius * r * Math.sin( theta ) ) ) );
3628             for( final PhylogenyNode desc : descs ) {
3629                 paintBranchCircularLite( n, desc, g );
3630             }
3631         }
3632     }
3633
3634     final private void paintCollapsedNode( final Graphics2D g,
3635                                            final PhylogenyNode node,
3636                                            final boolean to_graphics_file,
3637                                            final boolean to_pdf,
3638                                            final boolean is_in_found_nodes ) {
3639         Color c = null;
3640         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3641             c = Color.BLACK;
3642         }
3643         else if ( is_in_found_nodes ) {
3644             c = getTreeColorSet().getFoundColor();
3645         }
3646         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3647             c = getTaxonomyBasedColor( node );
3648         }
3649         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3650                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3651             c = PhylogenyMethods.getBranchColorValue( node );
3652         }
3653         else {
3654             c = getTreeColorSet().getCollapseFillColor();
3655         }
3656         double d = node.getAllExternalDescendants().size();
3657         if ( d > 1000 ) {
3658             d = ( 3 * _y_distance ) / 3;
3659         }
3660         else {
3661             d = ( Math.log10( d ) * _y_distance ) / 2.5;
3662         }
3663         final int box_size = getOptions().getDefaultNodeShapeSize();
3664         if ( d < box_size ) {
3665             d = box_size;
3666         }
3667         _polygon.reset();
3668         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() - box_size ),
3669                            ForesterUtil.roundToInt( node.getYcoord() ) );
3670         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
3671                            ForesterUtil.roundToInt( node.getYcoord() - d ) );
3672         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
3673                            ForesterUtil.roundToInt( node.getYcoord() + d ) );
3674         if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3675             g.setColor( c );
3676             g.fillPolygon( _polygon );
3677         }
3678         else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
3679             g.setColor( getBackground() );
3680             g.fillPolygon( _polygon );
3681             g.setColor( c );
3682             g.drawPolygon( _polygon );
3683         }
3684         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3685             g.setPaint( new GradientPaint( node.getXcoord() - box_size, node.getYcoord(), getBackground(), ( node
3686                     .getXcoord() + box_size ), ( float ) ( node.getYcoord() - d ), c, false ) );
3687             g.fill( _polygon );
3688             g.setPaint( c );
3689             g.draw( _polygon );
3690         }
3691         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3692     }
3693
3694     final private void paintConfidenceValues( final Graphics2D g,
3695                                               final PhylogenyNode node,
3696                                               final boolean to_pdf,
3697                                               final boolean to_graphics_file ) {
3698         final List<Confidence> confidences = node.getBranchData().getConfidences();
3699         //        if ( confidences.size() == 1 ) {
3700         //            final double value = node.getBranchData().getConfidence( 0 ).getValue();
3701         //            if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
3702         //                return;
3703         //            }
3704         //            conf_str = FORMATTER_CONFIDENCE.format( value );
3705         //        }
3706         //        else if ( confidences.size() > 1 ) {
3707         boolean one_ok = false;
3708         boolean not_first = false;
3709         Collections.sort( confidences );
3710         final StringBuilder sb = new StringBuilder();
3711         String conf_str = "";
3712         for( final Confidence confidence : confidences ) {
3713             final double value = confidence.getValue();
3714             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
3715                 if ( value >= getOptions().getMinConfidenceValue() ) {
3716                     one_ok = true;
3717                 }
3718                 if ( not_first ) {
3719                     sb.append( "/" );
3720                 }
3721                 else {
3722                     not_first = true;
3723                 }
3724                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
3725                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
3726                 if ( getOptions().isShowConfidenceStddev() ) {
3727                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
3728                         sb.append( "(" );
3729                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
3730                                                                                     getOptions()
3731                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
3732                         sb.append( ")" );
3733                     }
3734                 }
3735             }
3736             //}
3737             if ( one_ok ) {
3738                 conf_str = sb.toString();
3739             }
3740         }
3741         if ( conf_str.length() > 0 ) {
3742             final double parent_x = node.getParent().getXcoord();
3743             double x = node.getXcoord();
3744             g.setFont( getTreeFontSet().getSmallFont() );
3745             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3746                 x += EURO_D;
3747             }
3748             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3749                 x += ROUNDED_D;
3750             }
3751             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3752                 g.setColor( Color.BLACK );
3753             }
3754             else {
3755                 g.setColor( getTreeColorSet().getConfidenceColor() );
3756             }
3757             TreePanel
3758                     .drawString( conf_str,
3759                                  parent_x
3760                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
3761                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
3762                                  g );
3763         }
3764     }
3765
3766     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
3767         final int box_size = getOptions().getDefaultNodeShapeSize();
3768         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
3769         g.setColor( getTreeColorSet().getFoundColor() );
3770         g.fillRect( x - half_box_size, y - half_box_size, box_size, box_size );
3771     }
3772
3773     final private void paintGainedAndLostCharacters( final Graphics2D g,
3774                                                      final PhylogenyNode node,
3775                                                      final String gained,
3776                                                      final String lost ) {
3777         if ( node.getParent() != null ) {
3778             final double parent_x = node.getParent().getXcoord();
3779             final double x = node.getXcoord();
3780             g.setFont( getTreeFontSet().getLargeFont() );
3781             g.setColor( getTreeColorSet().getGainedCharactersColor() );
3782             if ( Constants.SPECIAL_CUSTOM ) {
3783                 g.setColor( Color.BLUE );
3784             }
3785             TreePanel
3786                     .drawString( gained,
3787                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
3788                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
3789                                  g );
3790             g.setColor( getTreeColorSet().getLostCharactersColor() );
3791             TreePanel.drawString( lost,
3792                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
3793                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
3794                                   g );
3795         }
3796     }
3797
3798     /**
3799      * Draw a box at the indicated node.
3800      * 
3801      * @param x
3802      * @param y
3803      * @param node
3804      * @param g
3805      */
3806     final private void paintNodeBox( final double x,
3807                                      final double y,
3808                                      final PhylogenyNode node,
3809                                      final Graphics2D g,
3810                                      final boolean to_pdf,
3811                                      final boolean to_graphics_file,
3812                                      final boolean is_in_found_nodes ) {
3813         if ( node.isCollapse() ) {
3814             return;
3815         }
3816         // if this node should be highlighted, do so
3817         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
3818             g.setColor( getTreeColorSet().getFoundColor() );
3819             drawOval( x - 8, y - 8, 16, 16, g );
3820             drawOval( x - 9, y - 8, 17, 17, g );
3821             drawOval( x - 9, y - 9, 18, 18, g );
3822         }
3823         if ( is_in_found_nodes ) {
3824             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
3825         }
3826         else {
3827             Color outline_color = null;
3828             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3829                 outline_color = Color.BLACK;
3830             }
3831             else if ( getControlPanel().isEvents() && AptxUtil.isHasAssignedEvent( node ) ) {
3832                 final Event event = node.getNodeData().getEvent();
3833                 if ( event.isDuplication() ) {
3834                     outline_color = getTreeColorSet().getDuplicationBoxColor();
3835                 }
3836                 else if ( event.isSpeciation() ) {
3837                     outline_color = getTreeColorSet().getSpecBoxColor();
3838                 }
3839                 else if ( event.isSpeciationOrDuplication() ) {
3840                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
3841                 }
3842             }
3843             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
3844                 outline_color = getTaxonomyBasedColor( node );
3845             }
3846             else {
3847                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
3848                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
3849                     outline_color = getTreeColorSet().getBranchColorForPdf();
3850                 }
3851             }
3852             final int box_size = getOptions().getDefaultNodeShapeSize();
3853             final int half_box_size = box_size / 2;
3854             if ( ( getOptions().isShowDefaultNodeShapesExternal() && node.isExternal() )
3855                     || ( getOptions().isShowDefaultNodeShapesInternal() && node.isInternal() )
3856                     || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
3857                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
3858                     if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3859                         drawOvalGradient( x - half_box_size,
3860                                           y - half_box_size,
3861                                           box_size,
3862                                           box_size,
3863                                           g,
3864                                           to_pdf ? Color.WHITE : outline_color,
3865                                           to_pdf ? outline_color : getBackground(),
3866                                           outline_color );
3867                     }
3868                     else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
3869                         Color background = getBackground();
3870                         if ( to_pdf ) {
3871                             background = Color.WHITE;
3872                         }
3873                         drawOvalGradient( x - half_box_size,
3874                                           y - half_box_size,
3875                                           box_size,
3876                                           box_size,
3877                                           g,
3878                                           background,
3879                                           background,
3880                                           outline_color );
3881                     }
3882                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3883                         g.setColor( outline_color );
3884                         drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
3885                     }
3886                 }
3887                 else if ( getOptions().getDefaultNodeShape() == NodeVisualization.NodeShape.RECTANGLE ) {
3888                     if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.GRADIENT ) {
3889                         drawRectGradient( x - half_box_size,
3890                                           y - half_box_size,
3891                                           box_size,
3892                                           box_size,
3893                                           g,
3894                                           to_pdf ? Color.WHITE : outline_color,
3895                                           to_pdf ? outline_color : getBackground(),
3896                                           outline_color );
3897                     }
3898                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
3899                         Color background = getBackground();
3900                         if ( to_pdf ) {
3901                             background = Color.WHITE;
3902                         }
3903                         drawRectGradient( x - half_box_size,
3904                                           y - half_box_size,
3905                                           box_size,
3906                                           box_size,
3907                                           g,
3908                                           background,
3909                                           background,
3910                                           outline_color );
3911                     }
3912                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3913                         g.setColor( outline_color );
3914                         drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
3915                     }
3916                 }
3917             }
3918         }
3919     }
3920
3921     final private void paintNodeData( final Graphics2D g,
3922                                       final PhylogenyNode node,
3923                                       final boolean to_graphics_file,
3924                                       final boolean to_pdf,
3925                                       final boolean is_in_found_nodes ) {
3926         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
3927             return;
3928         }
3929         if ( getOptions().isShowBranchLengthValues()
3930                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3931                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3932                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
3933             paintBranchLength( g, node, to_pdf, to_graphics_file );
3934         }
3935         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
3936             return;
3937         }
3938         int x = 0;
3939         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
3940         if ( getControlPanel().isShowTaxonomyImages()
3941                 && ( getImageMap() != null )
3942                 && !getImageMap().isEmpty()
3943                 && node.getNodeData().isHasTaxonomy()
3944                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
3945                         .getUris().isEmpty() ) ) {
3946             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
3947         }
3948         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3949                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
3950             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
3951         }
3952         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3953             g.setColor( Color.BLACK );
3954         }
3955         else if ( is_in_found_nodes ) {
3956             g.setColor( getTreeColorSet().getFoundColor() );
3957         }
3958         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3959             g.setColor( getTaxonomyBasedColor( node ) );
3960         }
3961         else if ( getControlPanel().isColorAccordingToAnnotation()
3962                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
3963                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
3964             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
3965         }
3966         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3967                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3968             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3969         }
3970         else if ( to_pdf ) {
3971             g.setColor( Color.BLACK );
3972         }
3973         else {
3974             g.setColor( getTreeColorSet().getSequenceColor() );
3975         }
3976         _sb.setLength( 0 );
3977         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3978             _sb.append( " [" );
3979             _sb.append( node.getAllExternalDescendants().size() );
3980             _sb.append( "]" );
3981         }
3982         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3983             if ( _sb.length() > 0 ) {
3984                 _sb.append( " " );
3985             }
3986             _sb.append( node.getName() );
3987         }
3988         if ( node.getNodeData().isHasSequence() ) {
3989             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3990                 if ( _sb.length() > 0 ) {
3991                     _sb.append( " " );
3992                 }
3993                 _sb.append( node.getNodeData().getSequence().getSymbol() );
3994             }
3995             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3996                 if ( _sb.length() > 0 ) {
3997                     _sb.append( " " );
3998                 }
3999                 _sb.append( node.getNodeData().getSequence().getName() );
4000             }
4001             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4002                 if ( _sb.length() > 0 ) {
4003                     _sb.append( " " );
4004                 }
4005                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4006                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4007                     _sb.append( ":" );
4008                 }
4009                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4010             }
4011         }
4012         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
4013             if ( _sb.length() > 0 ) {
4014                 _sb.append( " " );
4015             }
4016             _sb.append( propertiesToString( node ) );
4017         }
4018         g.setFont( getTreeFontSet().getLargeFont() );
4019         if ( is_in_found_nodes ) {
4020             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4021         }
4022         double down_shift_factor = 3.0;
4023         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
4024             down_shift_factor = 1;
4025         }
4026         final double pos_x = node.getXcoord() + x + 2 + half_box_size;
4027         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
4028         final String sb_str = _sb.toString();
4029         // GUILHEM_BEG ______________
4030         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
4031                 && ( _query_sequence != null ) ) {
4032             int nodeTextBoundsWidth = 0;
4033             if ( sb_str.length() > 0 ) {
4034                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
4035                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
4036             }
4037             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
4038                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
4039                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
4040                     g.setColor( getTreeColorSet().getBackgroundColor() );
4041                 }
4042             }
4043             else {
4044                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
4045                 for( final SequenceRelation seqRelation : seqRelations ) {
4046                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
4047                             .getRef1().isEqual( _query_sequence ) )
4048                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
4049                                     .getSelectedItem() );
4050                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
4051                         final double linePosX = node.getXcoord() + 2 + half_box_size;
4052                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
4053                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
4054                                 + ")";
4055                         if ( sConfidence != null ) {
4056                             double confidenceX = pos_x;
4057                             if ( sb_str.length() > 0 ) {
4058                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
4059                                         + CONFIDENCE_LEFT_MARGIN;
4060                             }
4061                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
4062                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
4063                                         .getBounds().getWidth();
4064                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
4065                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
4066                             }
4067                         }
4068                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
4069                         {
4070                             if ( nodeTextBoundsWidth == 0 ) {
4071                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
4072                             }
4073                             else {
4074                                 nodeTextBoundsWidth += 2;
4075                             }
4076                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
4077                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
4078                             break;
4079                         }
4080                     }
4081                 }
4082             }
4083         }
4084         if ( sb_str.length() > 0 ) {
4085             TreePanel.drawString( sb_str, pos_x, pos_y, g );
4086         }
4087         // GUILHEM_END _____________
4088         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
4089         // TODO FIXME need to check this one!
4090         //if ( _sb.length() > 0 ) {
4091         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
4092         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4093         //}
4094         // COMMENTED_OUT_BY_GUILHEM_END ________________
4095         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
4096                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4097                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4098             if ( _sb.length() > 0 ) {
4099                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
4100             }
4101             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
4102             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4103                 g.setColor( Color.BLACK );
4104             }
4105             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
4106                 g.setColor( calculateColorForAnnotation( ann ) );
4107             }
4108             final String ann_str = createAnnotationString( ann );
4109             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
4110                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4111             _sb.setLength( 0 );
4112             _sb.append( ann_str );
4113         }
4114         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4115                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
4116                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
4117             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
4118                     && node.getNodeData().isHasBinaryCharacters() ) {
4119                 if ( _sb.length() > 0 ) {
4120                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
4121                 }
4122                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4123                     g.setColor( Color.BLACK );
4124                 }
4125                 else {
4126                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
4127                 }
4128                 if ( getControlPanel().isShowBinaryCharacters() ) {
4129                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
4130                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
4131                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
4132                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
4133                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
4134                             .getLostCharactersAsStringBuffer().toString() );
4135                 }
4136                 else {
4137                     TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
4138                                           node.getXcoord() + x + 4 + half_box_size,
4139                                           node.getYcoord()
4140                                                   + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
4141                                           g );
4142                     paintGainedAndLostCharacters( g, node, "+"
4143                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
4144                             + node.getNodeData().getBinaryCharacters().getLostCount() );
4145                 }
4146             }
4147         }
4148     }
4149
4150     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
4151                                                   final PhylogenyNode node,
4152                                                   final boolean to_pdf,
4153                                                   final boolean to_graphics_file,
4154                                                   final boolean radial_labels,
4155                                                   final double ur_angle,
4156                                                   final boolean is_in_found_nodes ) {
4157         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
4158             return;
4159         }
4160         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4161             g.setColor( Color.BLACK );
4162         }
4163         else if ( is_in_found_nodes ) {
4164             g.setColor( getTreeColorSet().getFoundColor() );
4165         }
4166         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4167             g.setColor( getTaxonomyBasedColor( node ) );
4168         }
4169         else if ( getControlPanel().isColorAccordingToAnnotation()
4170                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
4171                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
4172             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
4173         }
4174         else {
4175             g.setColor( getTreeColorSet().getSequenceColor() );
4176         }
4177         _sb.setLength( 0 );
4178         _sb.append( " " );
4179         if ( node.getNodeData().isHasTaxonomy()
4180                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4181                         .isShowTaxonomyCommonNames() ) ) {
4182             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4183             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4184                 _sb.append( taxonomy.getTaxonomyCode() );
4185                 _sb.append( " " );
4186             }
4187             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4188                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4189                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4190                     _sb.append( taxonomy.getScientificName() );
4191                     _sb.append( " (" );
4192                     _sb.append( taxonomy.getCommonName() );
4193                     _sb.append( ") " );
4194                 }
4195                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4196                     _sb.append( taxonomy.getScientificName() );
4197                     _sb.append( " " );
4198                 }
4199                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4200                     _sb.append( taxonomy.getCommonName() );
4201                     _sb.append( " " );
4202                 }
4203             }
4204             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4205                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4206                     _sb.append( taxonomy.getScientificName() );
4207                     _sb.append( " " );
4208                 }
4209             }
4210             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4211                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4212                     _sb.append( taxonomy.getCommonName() );
4213                     _sb.append( " " );
4214                 }
4215             }
4216         }
4217         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4218             _sb.append( " [" );
4219             _sb.append( node.getAllExternalDescendants().size() );
4220             _sb.append( "]" );
4221         }
4222         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
4223             if ( _sb.length() > 0 ) {
4224                 _sb.append( " " );
4225             }
4226             _sb.append( node.getName() );
4227         }
4228         if ( node.getNodeData().isHasSequence() ) {
4229             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4230                 if ( _sb.length() > 0 ) {
4231                     _sb.append( " " );
4232                 }
4233                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4234                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4235                     _sb.append( ":" );
4236                 }
4237                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4238             }
4239             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
4240                 if ( _sb.length() > 0 ) {
4241                     _sb.append( " " );
4242                 }
4243                 _sb.append( node.getNodeData().getSequence().getName() );
4244             }
4245         }
4246         g.setFont( getTreeFontSet().getLargeFont() );
4247         if ( is_in_found_nodes ) {
4248             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4249         }
4250         if ( _sb.length() > 1 ) {
4251             final String sb_str = _sb.toString();
4252             double m = 0;
4253             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4254                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
4255             }
4256             else {
4257                 m = ( float ) ( ur_angle % TWO_PI );
4258             }
4259             _at = g.getTransform();
4260             boolean need_to_reset = false;
4261             final float x_coord = node.getXcoord();
4262             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
4263             if ( radial_labels ) {
4264                 need_to_reset = true;
4265                 boolean left = false;
4266                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4267                     m -= PI;
4268                     left = true;
4269                 }
4270                 g.rotate( m, x_coord, node.getYcoord() );
4271                 if ( left ) {
4272                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
4273                 }
4274             }
4275             else {
4276                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4277                     need_to_reset = true;
4278                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
4279                 }
4280             }
4281             TreePanel.drawString( sb_str, x_coord, y_coord, g );
4282             if ( need_to_reset ) {
4283                 g.setTransform( _at );
4284             }
4285         }
4286     }
4287
4288     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
4289         if ( node.isCollapse() ) {
4290             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
4291                 paintCollapsedNode( g, node, false, false, false );
4292             }
4293             return;
4294         }
4295         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4296             g.setColor( getTreeColorSet().getFoundColor() );
4297             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
4298         }
4299         float new_x = 0;
4300         if ( !node.isExternal() && !node.isCollapse() ) {
4301             boolean first_child = true;
4302             float y2 = 0.0f;
4303             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4304             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4305                 final PhylogenyNode child_node = node.getChildNode( i );
4306                 int factor_x;
4307                 if ( !isUniformBranchLengthsForCladogram() ) {
4308                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4309                 }
4310                 else {
4311                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4312                 }
4313                 if ( first_child ) {
4314                     first_child = false;
4315                     y2 = node.getYSecondary()
4316                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
4317                                     .getNumberOfExternalNodes() ) );
4318                 }
4319                 else {
4320                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4321                 }
4322                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
4323                 new_x = x2 + node.getXSecondary();
4324                 final float diff_y = node.getYSecondary() - y2;
4325                 final float diff_x = node.getXSecondary() - new_x;
4326                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
4327                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
4328                 }
4329                 child_node.setXSecondary( new_x );
4330                 child_node.setYSecondary( y2 );
4331                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4332             }
4333         }
4334     }
4335
4336     final private void paintNodeRectangular( final Graphics2D g,
4337                                              final PhylogenyNode node,
4338                                              final boolean to_pdf,
4339                                              final boolean dynamically_hide,
4340                                              final int dynamic_hiding_factor,
4341                                              final boolean to_graphics_file ) {
4342         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
4343         if ( node.isCollapse() ) {
4344             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
4345                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4346             }
4347             return;
4348         }
4349         if ( node.isExternal() ) {
4350             ++_external_node_index;
4351         }
4352         // Confidence values
4353         if ( getControlPanel().isShowConfidenceValues()
4354                 && !node.isExternal()
4355                 && !node.isRoot()
4356                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
4357                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4358                 && node.getBranchData().isHasConfidences() ) {
4359             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
4360         }
4361         // Draw a line to root:
4362         if ( node.isRoot() && _phylogeny.isRooted() ) {
4363             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
4364         }
4365         float new_x = 0;
4366         float new_x_min = Float.MAX_VALUE;
4367         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
4368         float min_dist = 1.5f;
4369         if ( !disallow_shortcutting ) {
4370             //   System.out.println( dynamic_hiding_factor );
4371             if ( dynamic_hiding_factor > 4000 ) {
4372                 min_dist = 4;
4373             }
4374             else if ( dynamic_hiding_factor > 1000 ) {
4375                 min_dist = 3;
4376             }
4377             else if ( dynamic_hiding_factor > 100 ) {
4378                 min_dist = 2;
4379             }
4380         }
4381         if ( !node.isExternal() && !node.isCollapse() ) {
4382             boolean first_child = true;
4383             float y2 = 0.0f;
4384             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4385             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4386                 final PhylogenyNode child_node = node.getChildNode( i );
4387                 int factor_x;
4388                 if ( !isUniformBranchLengthsForCladogram() ) {
4389                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4390                 }
4391                 else {
4392                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4393                 }
4394                 if ( first_child ) {
4395                     first_child = false;
4396                     y2 = node.getYcoord()
4397                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
4398                 }
4399                 else {
4400                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
4401                 }
4402                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
4403                 new_x = x2 + node.getXcoord();
4404                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
4405                     new_x_min = x2;
4406                 }
4407                 final float diff_y = node.getYcoord() - y2;
4408                 final float diff_x = node.getXcoord() - new_x;
4409                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
4410                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
4411                     paintBranchRectangular( g,
4412                                             node.getXcoord(),
4413                                             new_x,
4414                                             node.getYcoord(),
4415                                             y2,
4416                                             child_node,
4417                                             to_pdf,
4418                                             to_graphics_file );
4419                 }
4420                 child_node.setXcoord( new_x );
4421                 child_node.setYcoord( y2 );
4422                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
4423             }
4424             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node )
4425                     || isInCurrentExternalNodes( node ) );
4426         }
4427         if ( dynamically_hide
4428                 && !is_in_found_nodes
4429                 && ( ( node.isExternal() && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) ) || ( !node
4430                         .isExternal() && ( ( new_x_min < 20 ) || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getTreeFontSet()._fm_large
4431                         .getHeight() ) ) ) ) ) {
4432             return;
4433         }
4434         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4435         paintNodeWithRenderableData( g, node, to_graphics_file, to_pdf );
4436     }
4437
4438     final private void paintNodeWithRenderableData( final Graphics2D g,
4439                                                     final PhylogenyNode node,
4440                                                     final boolean to_graphics_file,
4441                                                     final boolean to_pdf ) {
4442         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
4443             return;
4444         }
4445         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
4446             return;
4447         }
4448         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4449                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4450             RenderableDomainArchitecture rds = null;
4451             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
4452                 try {
4453                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
4454                 }
4455                 catch ( final ClassCastException cce ) {
4456                     cce.printStackTrace();
4457                 }
4458                 if ( rds != null ) {
4459                     rds.setRenderingHeight( 6 );
4460                     int x = 0;
4461                     if ( node.getNodeData().isHasTaxonomy() ) {
4462                         if ( getControlPanel().isShowTaxonomyCode()
4463                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
4464                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4465                                     .getTaxonomyCode()
4466                                     + " " );
4467                         }
4468                         if ( getControlPanel().isShowTaxonomyScientificNames()
4469                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) ) {
4470                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4471                                     .getScientificName()
4472                                     + " " );
4473                         }
4474                         if ( getControlPanel().isShowTaxonomyCommonNames()
4475                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) ) {
4476                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
4477                                     .getCommonName()
4478                                     + " " );
4479                         }
4480                     }
4481                     if ( node.getNodeData().isHasSequence() ) {
4482                         if ( getControlPanel().isShowGeneNames()
4483                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) ) {
4484                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName()
4485                                     + " " );
4486                         }
4487                         if ( getControlPanel().isShowGeneSymbols()
4488                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) ) {
4489                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getSymbol()
4490                                     + " " );
4491                         }
4492                         if ( getControlPanel().isShowSequenceAcc()
4493                                 && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4494                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence()
4495                                     .getAccession().toString()
4496                                     + " " );
4497                         }
4498                     }
4499                     if ( getControlPanel().isShowNodeNames() && !ForesterUtil.isEmpty( node.getName() ) ) {
4500                         x += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
4501                     }
4502                     rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
4503                 }
4504             }
4505         }
4506         //////////////
4507         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4508                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
4509             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
4510                                                                          getStatisticsForExpressionValues(),
4511                                                                          getConfiguration() );
4512             if ( rv != null ) {
4513                 int x = 0;
4514                 PhylogenyNode my_node = node;
4515                 if ( !getControlPanel().isDrawPhylogram() ) {
4516                     my_node = getPhylogeny().getFirstExternalNode();
4517                 }
4518                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
4519                     x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
4520                 }
4521                 if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
4522                     x += getTreeFontSet()._fm_large.stringWidth( my_node.getName() + " " );
4523                 }
4524                 rv.render( my_node.getXcoord() + x, node.getYcoord() - 5, g, this, to_pdf );
4525             }
4526         }
4527         //////////////
4528     }
4529
4530     final private void paintOvRectangle( final Graphics2D g ) {
4531         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
4532         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
4533         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
4534         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
4535         final float width = getOvMaxWidth() / w_ratio;
4536         final float height = getOvMaxHeight() / h_ratio;
4537         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
4538         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
4539         g.setColor( getTreeColorSet().getFoundColor() );
4540         getOvRectangle().setRect( x, y, width, height );
4541         if ( ( width < 6 ) && ( height < 6 ) ) {
4542             drawRectFilled( x, y, 6, 6, g );
4543             getOvVirtualRectangle().setRect( x, y, 6, 6 );
4544         }
4545         else if ( width < 6 ) {
4546             drawRectFilled( x, y, 6, height, g );
4547             getOvVirtualRectangle().setRect( x, y, 6, height );
4548         }
4549         else if ( height < 6 ) {
4550             drawRectFilled( x, y, width, 6, g );
4551             getOvVirtualRectangle().setRect( x, y, width, 6 );
4552         }
4553         else {
4554             drawRect( x, y, width, height, g );
4555             if ( isInOvRect() ) {
4556                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
4557             }
4558             getOvVirtualRectangle().setRect( x, y, width, height );
4559         }
4560     }
4561
4562     final private void paintPhylogenyLite( final Graphics2D g ) {
4563         _phylogeny
4564                 .getRoot()
4565                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
4566                         .getWidth() ) ) ) );
4567         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
4568         for( final PhylogenyNode element : _nodes_in_preorder ) {
4569             paintNodeLite( g, element );
4570         }
4571         paintOvRectangle( g );
4572     }
4573
4574     /**
4575      * Paint the root branch. (Differs from others because it will always be a
4576      * single horizontal line).
4577      * @param to_graphics_file 
4578      * 
4579      * @return new x1 value
4580      */
4581     final private void paintRootBranch( final Graphics2D g,
4582                                         final float x1,
4583                                         final float y1,
4584                                         final PhylogenyNode root,
4585                                         final boolean to_pdf,
4586                                         final boolean to_graphics_file ) {
4587         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
4588         float d = getXdistance();
4589         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
4590             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
4591         }
4592         if ( d < MIN_ROOT_LENGTH ) {
4593             d = MIN_ROOT_LENGTH;
4594         }
4595         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
4596             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
4597         }
4598         else {
4599             final double w = PhylogenyMethods.getBranchWidthValue( root );
4600             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
4601         }
4602         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file, isInFoundNodes( root ) );
4603     }
4604
4605     final private void paintScale( final Graphics2D g,
4606                                    int x1,
4607                                    int y1,
4608                                    final boolean to_pdf,
4609                                    final boolean to_graphics_file ) {
4610         x1 += MOVE;
4611         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
4612         y1 -= 12;
4613         final int y2 = y1 - 8;
4614         final int y3 = y1 - 4;
4615         g.setFont( getTreeFontSet().getSmallFont() );
4616         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4617             g.setColor( Color.BLACK );
4618         }
4619         else {
4620             g.setColor( getTreeColorSet().getBranchLengthColor() );
4621         }
4622         drawLine( x1, y1, x1, y2, g );
4623         drawLine( x2, y1, x2, y2, g );
4624         drawLine( x1, y3, x2, y3, g );
4625         if ( getScaleLabel() != null ) {
4626             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
4627         }
4628     }
4629
4630     final private int paintTaxonomy( final Graphics2D g,
4631                                      final PhylogenyNode node,
4632                                      final boolean is_in_found_nodes,
4633                                      final boolean to_pdf,
4634                                      final boolean to_graphics_file,
4635                                      final double x_shift ) {
4636         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4637         g.setFont( getTreeFontSet().getLargeItalicFont() );
4638         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4639             g.setColor( Color.BLACK );
4640         }
4641         else if ( is_in_found_nodes ) {
4642             g.setFont( getTreeFontSet().getLargeItalicFont().deriveFont( TreeFontSet.BOLD_AND_ITALIC ) );
4643             g.setColor( getTreeColorSet().getFoundColor() );
4644         }
4645         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4646             g.setColor( getTaxonomyBasedColor( node ) );
4647         }
4648         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
4649                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
4650             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
4651         }
4652         else if ( to_pdf ) {
4653             g.setColor( Color.BLACK );
4654         }
4655         else {
4656             g.setColor( getTreeColorSet().getTaxonomyColor() );
4657         }
4658         final double start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
4659         final double start_y = node.getYcoord()
4660                 + ( getTreeFontSet()._fm_large.getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0 ) );
4661         _sb.setLength( 0 );
4662         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4663             _sb.append( taxonomy.getTaxonomyCode() );
4664             _sb.append( " " );
4665         }
4666         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4667             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4668                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4669                 if ( getOptions().isAbbreviateScientificTaxonNames()
4670                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4671                     abbreviateScientificName( taxonomy.getScientificName() );
4672                 }
4673                 else {
4674                     _sb.append( taxonomy.getScientificName() );
4675                 }
4676                 _sb.append( " (" );
4677                 _sb.append( taxonomy.getCommonName() );
4678                 _sb.append( ") " );
4679             }
4680             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4681                 if ( getOptions().isAbbreviateScientificTaxonNames()
4682                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4683                     abbreviateScientificName( taxonomy.getScientificName() );
4684                 }
4685                 else {
4686                     _sb.append( taxonomy.getScientificName() );
4687                 }
4688                 _sb.append( " " );
4689             }
4690             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4691                 _sb.append( taxonomy.getCommonName() );
4692                 _sb.append( " " );
4693             }
4694         }
4695         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4696             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4697                 if ( getOptions().isAbbreviateScientificTaxonNames()
4698                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4699                     abbreviateScientificName( taxonomy.getScientificName() );
4700                 }
4701                 else {
4702                     _sb.append( taxonomy.getScientificName() );
4703                 }
4704                 _sb.append( " " );
4705             }
4706         }
4707         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4708             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4709                 _sb.append( taxonomy.getCommonName() );
4710                 _sb.append( " " );
4711             }
4712         }
4713         final String label = _sb.toString();
4714         /* GUILHEM_BEG */
4715         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
4716                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
4717             // invert font color and background color to show that this is the query sequence
4718             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
4719                                                                                                           false,
4720                                                                                                           false ) )
4721                     .getBounds();
4722             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
4723             g.setColor( getTreeColorSet().getBackgroundColor() );
4724         }
4725         /* GUILHEM_END */
4726         TreePanel.drawString( label, start_x, start_y, g );
4727         if ( is_in_found_nodes ) {
4728             return getTreeFontSet()._fm_large_italic_bold.stringWidth( label );
4729         }
4730         else {
4731             return getTreeFontSet()._fm_large_italic.stringWidth( label );
4732         }
4733     }
4734
4735     final private void paintUnrooted( final PhylogenyNode n,
4736                                       final double low_angle,
4737                                       final double high_angle,
4738                                       final boolean radial_labels,
4739                                       final Graphics2D g,
4740                                       final boolean to_pdf,
4741                                       final boolean to_graphics_file ) {
4742         if ( n.isRoot() ) {
4743             n.setXcoord( getWidth() / 2 );
4744             n.setYcoord( getHeight() / 2 );
4745         }
4746         if ( n.isExternal() ) {
4747             paintNodeDataUnrootedCirc( g,
4748                                        n,
4749                                        to_pdf,
4750                                        to_graphics_file,
4751                                        radial_labels,
4752                                        ( high_angle + low_angle ) / 2,
4753                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
4754             return;
4755         }
4756         final float num_enclosed = n.getNumberOfExternalNodes();
4757         final float x = n.getXcoord();
4758         final float y = n.getYcoord();
4759         double current_angle = low_angle;
4760         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4761         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4762         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4763         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4764         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4765             final PhylogenyNode desc = n.getChildNode( i );
4766             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4767             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4768             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4769             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4770             //     continue;
4771             // }
4772             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4773             //    continue;
4774             //}
4775             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4776             //    continue;
4777             // }
4778             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4779             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4780             float length;
4781             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4782                 if ( desc.getDistanceToParent() < 0 ) {
4783                     length = 0;
4784                 }
4785                 else {
4786                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4787                 }
4788             }
4789             else {
4790                 length = getUrtFactor();
4791             }
4792             final double mid_angle = current_angle + ( arc_size / 2 );
4793             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
4794             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
4795             desc.setXcoord( new_x );
4796             desc.setYcoord( new_y );
4797             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
4798             current_angle += arc_size;
4799             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
4800             drawLine( x, y, new_x, new_y, g );
4801             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file, isInFoundNodes( desc )
4802                     || isInCurrentExternalNodes( desc ) );
4803         }
4804         if ( n.isRoot() ) {
4805             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file, isInFoundNodes( n ) );
4806         }
4807     }
4808
4809     final private void paintUnrootedLite( final PhylogenyNode n,
4810                                           final double low_angle,
4811                                           final double high_angle,
4812                                           final Graphics2D g,
4813                                           final float urt_ov_factor ) {
4814         if ( n.isRoot() ) {
4815             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
4816             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
4817             n.setXSecondary( x_pos );
4818             n.setYSecondary( y_pos );
4819         }
4820         if ( n.isExternal() ) {
4821             return;
4822         }
4823         final float num_enclosed = n.getNumberOfExternalNodes();
4824         final float x = n.getXSecondary();
4825         final float y = n.getYSecondary();
4826         double current_angle = low_angle;
4827         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4828             final PhylogenyNode desc = n.getChildNode( i );
4829             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4830             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4831             float length;
4832             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4833                 if ( desc.getDistanceToParent() < 0 ) {
4834                     length = 0;
4835                 }
4836                 else {
4837                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
4838                 }
4839             }
4840             else {
4841                 length = urt_ov_factor;
4842             }
4843             final double mid_angle = current_angle + ( arc_size / 2 );
4844             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
4845             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
4846             desc.setXSecondary( new_x );
4847             desc.setYSecondary( new_y );
4848             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
4849                 g.setColor( getTreeColorSet().getFoundColor() );
4850                 drawRectFilled( desc.getXSecondary() - 1, desc.getYSecondary() - 1, 3, 3, g );
4851                 g.setColor( getTreeColorSet().getOvColor() );
4852             }
4853             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
4854             current_angle += arc_size;
4855             drawLine( x, y, new_x, new_y, g );
4856         }
4857     }
4858
4859     final private void pasteSubtree( final PhylogenyNode node ) {
4860         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4861             errorMessageNoCutCopyPasteInUnrootedDisplay();
4862             return;
4863         }
4864         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
4865             JOptionPane.showMessageDialog( this,
4866                                            "No tree in buffer (need to copy or cut a subtree first)",
4867                                            "Attempt to paste with empty buffer",
4868                                            JOptionPane.ERROR_MESSAGE );
4869             return;
4870         }
4871         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
4872         final Object[] options = { "As sibling", "As descendant", "Cancel" };
4873         final int r = JOptionPane.showOptionDialog( this,
4874                                                     "How to paste subtree" + label + "?",
4875                                                     "Paste Subtree",
4876                                                     JOptionPane.CLOSED_OPTION,
4877                                                     JOptionPane.QUESTION_MESSAGE,
4878                                                     null,
4879                                                     options,
4880                                                     options[ 2 ] );
4881         boolean paste_as_sibling = true;
4882         if ( r == 1 ) {
4883             paste_as_sibling = false;
4884         }
4885         else if ( r != 0 ) {
4886             return;
4887         }
4888         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
4889         buffer_phy.setAllNodesToNotCollapse();
4890         PhylogenyMethods.preOrderReId( buffer_phy );
4891         buffer_phy.setRooted( true );
4892         boolean need_to_show_whole = false;
4893         if ( paste_as_sibling ) {
4894             if ( node.isRoot() ) {
4895                 JOptionPane.showMessageDialog( this,
4896                                                "Cannot paste sibling to root",
4897                                                "Attempt to paste sibling to root",
4898                                                JOptionPane.ERROR_MESSAGE );
4899                 return;
4900             }
4901             buffer_phy.addAsSibling( node );
4902         }
4903         else {
4904             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
4905                 need_to_show_whole = true;
4906                 _phylogeny = buffer_phy;
4907             }
4908             else {
4909                 buffer_phy.addAsChild( node );
4910             }
4911         }
4912         if ( getCopiedAndPastedNodes() == null ) {
4913             setCopiedAndPastedNodes( new HashSet<Long>() );
4914         }
4915         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
4916         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
4917         for( final PhylogenyNode n : nodes ) {
4918             node_ids.add( n.getId() );
4919         }
4920         node_ids.add( node.getId() );
4921         getCopiedAndPastedNodes().addAll( node_ids );
4922         setNodeInPreorderToNull();
4923         _phylogeny.externalNodesHaveChanged();
4924         _phylogeny.clearHashIdToNodeMap();
4925         _phylogeny.recalculateNumberOfExternalDescendants( true );
4926         resetNodeIdToDistToLeafMap();
4927         setEdited( true );
4928         if ( need_to_show_whole ) {
4929             getControlPanel().showWhole();
4930         }
4931         repaint();
4932     }
4933
4934     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
4935         final PropertiesMap properties = node.getNodeData().getProperties();
4936         final StringBuffer sb = new StringBuffer();
4937         boolean first = true;
4938         for( final String ref : properties.getPropertyRefs() ) {
4939             if ( first ) {
4940                 first = false;
4941             }
4942             else {
4943                 sb.append( " " );
4944             }
4945             final Property p = properties.getProperty( ref );
4946             sb.append( getPartAfterColon( p.getRef() ) );
4947             sb.append( "=" );
4948             sb.append( p.getValue() );
4949             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
4950                 sb.append( getPartAfterColon( p.getUnit() ) );
4951             }
4952         }
4953         return sb;
4954     }
4955
4956     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
4957         getMainPanel().setCopiedAndPastedNodes( nodeIds );
4958     }
4959
4960     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
4961         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
4962     }
4963
4964     final private void setInOv( final boolean in_ov ) {
4965         _in_ov = in_ov;
4966     }
4967
4968     final private void setOvMaxHeight( final float ov_max_height ) {
4969         _ov_max_height = ov_max_height;
4970     }
4971
4972     final private void setOvMaxWidth( final float ov_max_width ) {
4973         _ov_max_width = ov_max_width;
4974     }
4975
4976     final private void setOvXcorrectionFactor( final float f ) {
4977         _ov_x_correction_factor = f;
4978     }
4979
4980     final private void setOvXDistance( final float ov_x_distance ) {
4981         _ov_x_distance = ov_x_distance;
4982     }
4983
4984     final private void setOvXPosition( final int ov_x_position ) {
4985         _ov_x_position = ov_x_position;
4986     }
4987
4988     final private void setOvYDistance( final float ov_y_distance ) {
4989         _ov_y_distance = ov_y_distance;
4990     }
4991
4992     final private void setOvYPosition( final int ov_y_position ) {
4993         _ov_y_position = ov_y_position;
4994     }
4995
4996     final private void setOvYStart( final int ov_y_start ) {
4997         _ov_y_start = ov_y_start;
4998     }
4999
5000     final private void setScaleDistance( final double scale_distance ) {
5001         _scale_distance = scale_distance;
5002     }
5003
5004     final private void setScaleLabel( final String scale_label ) {
5005         _scale_label = scale_label;
5006     }
5007
5008     final private void setUpUrtFactor() {
5009         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
5010                 : getVisibleRect().height;
5011         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5012             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
5013         }
5014         else {
5015             final int max_depth = _circ_max_depth;
5016             if ( max_depth > 0 ) {
5017                 setUrtFactor( d / ( 2 * max_depth ) );
5018             }
5019             else {
5020                 setUrtFactor( d / 2 );
5021             }
5022         }
5023         setUrtFactorOv( getUrtFactor() );
5024     }
5025
5026     final private void setUrtFactor( final float urt_factor ) {
5027         _urt_factor = urt_factor;
5028     }
5029
5030     final private void setUrtFactorOv( final float urt_factor_ov ) {
5031         _urt_factor_ov = urt_factor_ov;
5032     }
5033
5034     private void showExtDescNodeData( final PhylogenyNode node ) {
5035         final List<String> data = new ArrayList<String>();
5036         for( final PhylogenyNode n : node.getAllExternalDescendants() ) {
5037             switch ( getOptions().getExtDescNodeDataToReturn() ) {
5038                 case NODE_NAME:
5039                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5040                         data.add( n.getName() );
5041                     }
5042                     break;
5043                 case SEQUENCE_NAME:
5044                     if ( n.getNodeData().isHasSequence()
5045                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5046                         data.add( n.getNodeData().getSequence().getName() );
5047                     }
5048                     break;
5049                 case SEQUENCE_SYMBOL:
5050                     if ( n.getNodeData().isHasSequence()
5051                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5052                         data.add( n.getNodeData().getSequence().getSymbol() );
5053                     }
5054                     break;
5055                 case SEQUENCE_MOL_SEQ:
5056                     if ( n.getNodeData().isHasSequence()
5057                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5058                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
5059                     }
5060                     break;
5061                 case SEQUENCE_MOL_SEQ_FASTA:
5062                     final StringBuilder sb = new StringBuilder();
5063                     if ( n.getNodeData().isHasSequence()
5064                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5065                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5066                             sb.append( SequenceWriter.toFasta( n.getNodeData().getSequence().getName(), n.getNodeData()
5067                                     .getSequence().getMolecularSequence(), 60 ) );
5068                         }
5069                         else {
5070                             sb.append( SequenceWriter.toFasta( n.getName(), n.getNodeData().getSequence()
5071                                     .getMolecularSequence(), 60 ) );
5072                         }
5073                         data.add( sb.toString() );
5074                     }
5075                     break;
5076                 case SEQUENCE_ACC:
5077                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
5078                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
5079                         data.add( n.getNodeData().getSequence().getAccession().toString() );
5080                     }
5081                     break;
5082                 case TAXONOMY_SCIENTIFIC_NAME:
5083                     if ( n.getNodeData().isHasTaxonomy()
5084                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5085                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
5086                     }
5087                     break;
5088                 case TAXONOMY_CODE:
5089                     if ( n.getNodeData().isHasTaxonomy()
5090                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5091                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5092                     }
5093                     break;
5094                 case UNKNOWN:
5095                     AptxUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
5096                     break;
5097                 default:
5098                     throw new IllegalArgumentException( "unknown data element: "
5099                             + getOptions().getExtDescNodeDataToReturn() );
5100             }
5101         } // for loop
5102         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
5103                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
5104             final StringBuilder sb = new StringBuilder();
5105             for( final String d : data ) {
5106                 if ( !ForesterUtil.isEmpty( d ) ) {
5107                     if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5108                         System.out.println( d );
5109                     }
5110                     sb.append( d );
5111                     sb.append( ForesterUtil.LINE_SEPARATOR );
5112                 }
5113             }
5114             if ( sb.length() < 1 ) {
5115                 clearCurrentExternalNodesDataBuffer();
5116             }
5117             else {
5118                 setCurrentExternalNodesDataBuffer( sb );
5119             }
5120         }
5121         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5122             final StringBuilder sb = new StringBuilder();
5123             for( final String d : data ) {
5124                 if ( !ForesterUtil.isEmpty( d ) ) {
5125                     sb.append( d );
5126                     sb.append( ForesterUtil.LINE_SEPARATOR );
5127                 }
5128             }
5129             if ( sb.length() < 1 ) {
5130                 AptxUtil.showInformationMessage( this,
5131                                                  "No Appropriate Data (" + obtainTitleForExtDescNodeData() + ")",
5132                                                  "Descendants of selected node do not contain selected data" );
5133                 clearCurrentExternalNodesDataBuffer();
5134             }
5135             else {
5136                 setCurrentExternalNodesDataBuffer( sb );
5137                 final String title = "External Descendants "
5138                         + ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5139                                 : obtainTitleForExtDescNodeData() ) + " (" + data.size() + "/"
5140                         + node.getNumberOfExternalNodes() + ") For Node " + node;
5141                 final String s = sb.toString().trim();
5142                 if ( getMainPanel().getMainFrame() == null ) {
5143                     // Must be "E" applet version.
5144                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5145                     ae.showTextFrame( s, title );
5146                 }
5147                 else {
5148                     getMainPanel().getMainFrame().showTextFrame( s, title );
5149                 }
5150             }
5151         }
5152     }
5153
5154     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5155         try {
5156             if ( ( node.getName().length() > 0 )
5157                     || ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
5158                     || ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) )
5159                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
5160                     || node.getBranchData().isHasConfidences() ) {
5161                 _popup_buffer.setLength( 0 );
5162                 short lines = 0;
5163                 if ( node.getName().length() > 0 ) {
5164                     lines++;
5165                     _popup_buffer.append( node.getName() );
5166                 }
5167                 if ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5168                     lines++;
5169                     boolean enc_data = false;
5170                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5171                     if ( _popup_buffer.length() > 0 ) {
5172                         _popup_buffer.append( "\n" );
5173                     }
5174                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5175                         _popup_buffer.append( "[" );
5176                         _popup_buffer.append( tax.getTaxonomyCode() );
5177                         _popup_buffer.append( "]" );
5178                         enc_data = true;
5179                     }
5180                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5181                         if ( enc_data ) {
5182                             _popup_buffer.append( " " );
5183                         }
5184                         _popup_buffer.append( tax.getScientificName() );
5185                         enc_data = true;
5186                     }
5187                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5188                         if ( enc_data ) {
5189                             _popup_buffer.append( " (" );
5190                         }
5191                         else {
5192                             _popup_buffer.append( "(" );
5193                         }
5194                         _popup_buffer.append( tax.getCommonName() );
5195                         _popup_buffer.append( ")" );
5196                         enc_data = true;
5197                     }
5198                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5199                         if ( enc_data ) {
5200                             _popup_buffer.append( " (" );
5201                         }
5202                         else {
5203                             _popup_buffer.append( "(" );
5204                         }
5205                         _popup_buffer.append( tax.getAuthority() );
5206                         _popup_buffer.append( ")" );
5207                         enc_data = true;
5208                     }
5209                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5210                         if ( enc_data ) {
5211                             _popup_buffer.append( " [" );
5212                         }
5213                         else {
5214                             _popup_buffer.append( "[" );
5215                         }
5216                         _popup_buffer.append( tax.getRank() );
5217                         _popup_buffer.append( "]" );
5218                         enc_data = true;
5219                     }
5220                     if ( tax.getSynonyms().size() > 0 ) {
5221                         if ( enc_data ) {
5222                             _popup_buffer.append( " " );
5223                         }
5224                         _popup_buffer.append( "[" );
5225                         int counter = 1;
5226                         for( final String syn : tax.getSynonyms() ) {
5227                             if ( !ForesterUtil.isEmpty( syn ) ) {
5228                                 enc_data = true;
5229                                 _popup_buffer.append( syn );
5230                                 if ( counter < tax.getSynonyms().size() ) {
5231                                     _popup_buffer.append( ", " );
5232                                 }
5233                             }
5234                             counter++;
5235                         }
5236                         _popup_buffer.append( "]" );
5237                     }
5238                     if ( !enc_data ) {
5239                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5240                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5241                                 _popup_buffer.append( "[" );
5242                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5243                                 _popup_buffer.append( "] " );
5244                             }
5245                             _popup_buffer.append( tax.getIdentifier().getValue() );
5246                         }
5247                     }
5248                 }
5249                 if ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5250                     lines++;
5251                     boolean enc_data = false;
5252                     if ( _popup_buffer.length() > 0 ) {
5253                         _popup_buffer.append( "\n" );
5254                     }
5255                     final Sequence seq = node.getNodeData().getSequence();
5256                     if ( seq.getAccession() != null ) {
5257                         _popup_buffer.append( "[" );
5258                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5259                             _popup_buffer.append( seq.getAccession().getSource() );
5260                             _popup_buffer.append( ":" );
5261                         }
5262                         _popup_buffer.append( seq.getAccession().getValue() );
5263                         _popup_buffer.append( "]" );
5264                         enc_data = true;
5265                     }
5266                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5267                         if ( enc_data ) {
5268                             _popup_buffer.append( " [" );
5269                         }
5270                         else {
5271                             _popup_buffer.append( "[" );
5272                         }
5273                         _popup_buffer.append( seq.getSymbol() );
5274                         _popup_buffer.append( "]" );
5275                         enc_data = true;
5276                     }
5277                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5278                         if ( enc_data ) {
5279                             _popup_buffer.append( " " );
5280                         }
5281                         _popup_buffer.append( seq.getName() );
5282                     }
5283                 }
5284                 if ( node.getNodeData().isHasDate() ) {
5285                     lines++;
5286                     if ( _popup_buffer.length() > 0 ) {
5287                         _popup_buffer.append( "\n" );
5288                     }
5289                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5290                 }
5291                 if ( node.getNodeData().isHasDistribution() ) {
5292                     lines++;
5293                     if ( _popup_buffer.length() > 0 ) {
5294                         _popup_buffer.append( "\n" );
5295                     }
5296                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5297                 }
5298                 if ( node.getBranchData().isHasConfidences() ) {
5299                     final List<Confidence> confs = node.getBranchData().getConfidences();
5300                     for( final Confidence confidence : confs ) {
5301                         lines++;
5302                         if ( _popup_buffer.length() > 0 ) {
5303                             _popup_buffer.append( "\n" );
5304                         }
5305                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5306                             _popup_buffer.append( "[" );
5307                             _popup_buffer.append( confidence.getType() );
5308                             _popup_buffer.append( "] " );
5309                         }
5310                         _popup_buffer
5311                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5312                                                                                           getOptions()
5313                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5314                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5315                             _popup_buffer.append( " (sd=" );
5316                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5317                                     .getStandardDeviation(), getOptions()
5318                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5319                             _popup_buffer.append( ")" );
5320                         }
5321                     }
5322                 }
5323                 if ( node.getNodeData().isHasProperties() ) {
5324                     final PropertiesMap properties = node.getNodeData().getProperties();
5325                     for( final String ref : properties.getPropertyRefs() ) {
5326                         _popup_buffer.append( "\n" );
5327                         final Property p = properties.getProperty( ref );
5328                         _popup_buffer.append( getPartAfterColon( p.getRef() ) );
5329                         _popup_buffer.append( "=" );
5330                         _popup_buffer.append( p.getValue() );
5331                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5332                             _popup_buffer.append( getPartAfterColon( p.getUnit() ) );
5333                         }
5334                     }
5335                 }
5336                 if ( _popup_buffer.length() > 0 ) {
5337                     if ( !getConfiguration().isUseNativeUI() ) {
5338                         _rollover_popup
5339                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5340                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5341                         if ( isInFoundNodes( node ) ) {
5342                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
5343                         }
5344                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5345                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
5346                         }
5347                         else {
5348                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5349                         }
5350                     }
5351                     else {
5352                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5353                     }
5354                     _rollover_popup.setText( _popup_buffer.toString() );
5355                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5356                                                                                   _rollover_popup,
5357                                                                                   e.getLocationOnScreen().x + 10,
5358                                                                                   e.getLocationOnScreen().y
5359                                                                                           - ( lines * 20 ) );
5360                     _node_desc_popup.show();
5361                 }
5362             }
5363         }
5364         catch ( final Exception ex ) {
5365             // Do nothing.
5366         }
5367     }
5368
5369     final private void showNodeEditFrame( final PhylogenyNode n ) {
5370         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5371             // pop up edit box for single node
5372             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5373             _node_frame_index++;
5374         }
5375         else {
5376             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5377         }
5378     }
5379
5380     final private void showNodeFrame( final PhylogenyNode n ) {
5381         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5382             // pop up edit box for single node
5383             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5384             _node_frame_index++;
5385         }
5386         else {
5387             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5388         }
5389     }
5390
5391     final private void switchDisplaygetPhylogenyGraphicsType() {
5392         switch ( getPhylogenyGraphicsType() ) {
5393             case RECTANGULAR:
5394                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5395                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5396                 break;
5397             case EURO_STYLE:
5398                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5399                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5400                 break;
5401             case ROUNDED:
5402                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5403                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5404                 break;
5405             case CURVED:
5406                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5407                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5408                 break;
5409             case TRIANGULAR:
5410                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5411                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5412                 break;
5413             case CONVEX:
5414                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5415                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5416                 break;
5417             case UNROOTED:
5418                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5419                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5420                 break;
5421             case CIRCULAR:
5422                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5423                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5424                 break;
5425             default:
5426                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5427         }
5428         if ( getControlPanel().getDynamicallyHideData() != null ) {
5429             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5430                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5431             }
5432             else {
5433                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5434             }
5435         }
5436         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5437             getControlPanel().setDrawPhylogramEnabled( true );
5438         }
5439         else {
5440             getControlPanel().setDrawPhylogramEnabled( false );
5441         }
5442         if ( getMainPanel().getMainFrame() == null ) {
5443             // Must be "E" applet version.
5444             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5445                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5446         }
5447         else {
5448             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5449         }
5450     }
5451
5452     final private static void drawString( final int i, final double x, final double y, final Graphics2D g ) {
5453         g.drawString( String.valueOf( i ), ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5454     }
5455
5456     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5457         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5458     }
5459
5460     final private static String getPartAfterColon( final String s ) {
5461         final int i = s.indexOf( ':' );
5462         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
5463             return s;
5464         }
5465         return s.substring( i + 1, s.length() );
5466     }
5467
5468     final private static boolean isSequenceEmpty( final Sequence seq ) {
5469         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
5470                 && ForesterUtil.isEmpty( seq.getSymbol() );
5471     }
5472
5473     final private static boolean isTaxonomyEmpty( final Taxonomy tax ) {
5474         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
5475                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
5476                 .getSynonyms().isEmpty() );
5477     }
5478
5479     final private static boolean plusPressed( final int key_code ) {
5480         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5481                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5482     }
5483
5484     final private static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
5485         final Phylogeny new_phy = new Phylogeny();
5486         new_phy.setRooted( true );
5487         new_phy.setName( source_phy.getName() );
5488         new_phy.setDescription( source_phy.getDescription() );
5489         new_phy.setType( source_phy.getType() );
5490         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
5491         new_phy.setConfidence( source_phy.getConfidence() );
5492         new_phy.setIdentifier( source_phy.getIdentifier() );
5493         new_phy.setRoot( new_root.copyNodeDataShallow() );
5494         int i = 0;
5495         for( final PhylogenyNode n : new_root.getDescendants() ) {
5496             new_phy.getRoot().setChildNode( i++, n );
5497         }
5498         return new_phy;
5499     }
5500
5501     final private class SubtreeColorizationActionListener implements ActionListener {
5502
5503         JColorChooser _chooser;
5504         PhylogenyNode _node;
5505
5506         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5507             _chooser = chooser;
5508             _node = node;
5509         }
5510
5511         @Override
5512         public void actionPerformed( final ActionEvent e ) {
5513             final Color c = _chooser.getColor();
5514             if ( c != null ) {
5515                 colorizeSubtree( c, _node );
5516             }
5517         }
5518     }
5519 }