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