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