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