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