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