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