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