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