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 decreaseDomainStructureEvalueThresholdExp() {
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 getDomainStructureEvalueThresholdExp() {
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 increaseDomainStructureEvalueThresholdExp() {
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 DOMAINS_ALL:
3603                 return "Domains [E-value threshold: " + Math.pow( 10, getDomainStructureEvalueThresholdExp() ) + "]";
3604             case DOMAINS_COLLAPSED_PER_PROTEIN:
3605                 return "Domains [collapsed per protein, E-value threshold: " + Math.pow( 10, getDomainStructureEvalueThresholdExp() ) + "]";
3606             case SEQ_ANNOTATIONS:
3607                 return "Sequence Annotations";
3608             case GO_TERM_IDS:
3609                 return "GO Term IDs";    
3610             case UNKNOWN:
3611                 return "User Selected Data";
3612             default:
3613                 throw new IllegalArgumentException( "unknown data element: "
3614                         + getOptions().getExtDescNodeDataToReturn() );
3615         }
3616     }
3617
3618     final private void openPdbWeb( final PhylogenyNode node ) {
3619         final List<Accession> pdb_ids = getPdbAccs( node );
3620         if ( ForesterUtil.isEmpty( pdb_ids ) ) {
3621             cannotOpenBrowserWarningMessage( "PDB" );
3622             return;
3623         }
3624         final List<String> uri_strs = TreePanelUtil.createUrisForPdbWeb( node, pdb_ids, getConfiguration(), this );
3625         if ( !ForesterUtil.isEmpty( uri_strs ) ) {
3626             for( final String uri_str : uri_strs ) {
3627                 try {
3628                     AptxUtil.launchWebBrowser( new URI( uri_str ),
3629                                                isApplet(),
3630                                                isApplet() ? obtainApplet() : null,
3631                             "_aptx_seq" );
3632                 }
3633                 catch ( final IOException e ) {
3634                     AptxUtil.showErrorMessage( this, e.toString() );
3635                     e.printStackTrace();
3636                 }
3637                 catch ( final URISyntaxException e ) {
3638                     AptxUtil.showErrorMessage( this, e.toString() );
3639                     e.printStackTrace();
3640                 }
3641             }
3642         }
3643         else {
3644             cannotOpenBrowserWarningMessage( "PDB" );
3645         }
3646     }
3647
3648     final private void openSeqWeb( final PhylogenyNode node ) {
3649         if ( ForesterUtil.isEmpty( isCanOpenSeqWeb( node ) ) ) {
3650             cannotOpenBrowserWarningMessage( "sequence" );
3651             return;
3652         }
3653         final String uri_str = TreePanelUtil.createUriForSeqWeb( node, getConfiguration(), this );
3654         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3655             try {
3656                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3657                                            isApplet(),
3658                                            isApplet() ? obtainApplet() : null,
3659                         "_aptx_seq" );
3660             }
3661             catch ( final IOException e ) {
3662                 AptxUtil.showErrorMessage( this, e.toString() );
3663                 e.printStackTrace();
3664             }
3665             catch ( final URISyntaxException e ) {
3666                 AptxUtil.showErrorMessage( this, e.toString() );
3667                 e.printStackTrace();
3668             }
3669         }
3670         else {
3671             cannotOpenBrowserWarningMessage( "sequence" );
3672         }
3673     }
3674
3675     final private void openTaxWeb( final PhylogenyNode node ) {
3676         if ( !isCanOpenTaxWeb( node ) ) {
3677             cannotOpenBrowserWarningMessage( "taxonomic" );
3678             return;
3679         }
3680         String uri_str = null;
3681         final Taxonomy tax = node.getNodeData().getTaxonomy();
3682         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3683                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
3684             try {
3685                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
3686             }
3687             catch ( final URISyntaxException e ) {
3688                 AptxUtil.showErrorMessage( this, e.toString() );
3689                 uri_str = null;
3690                 e.printStackTrace();
3691             }
3692         }
3693         else if ( ( tax.getIdentifier() != null )
3694                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3695                 && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
3696                 && ( tax.getIdentifier().getProvider().equalsIgnoreCase( "ncbi" ) || tax.getIdentifier().getProvider()
3697                         .equalsIgnoreCase( "uniprot" ) ) ) {
3698             try {
3699                 uri_str = "http://www.uniprot.org/taxonomy/"
3700                         + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
3701             }
3702             catch ( final UnsupportedEncodingException e ) {
3703                 AptxUtil.showErrorMessage( this, e.toString() );
3704                 e.printStackTrace();
3705             }
3706         }
3707         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
3708             try {
3709                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3710                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
3711             }
3712             catch ( final UnsupportedEncodingException e ) {
3713                 AptxUtil.showErrorMessage( this, e.toString() );
3714                 e.printStackTrace();
3715             }
3716         }
3717         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
3718             try {
3719                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3720                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
3721             }
3722             catch ( final UnsupportedEncodingException e ) {
3723                 AptxUtil.showErrorMessage( this, e.toString() );
3724                 e.printStackTrace();
3725             }
3726         }
3727         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
3728             try {
3729                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3730                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
3731             }
3732             catch ( final UnsupportedEncodingException e ) {
3733                 AptxUtil.showErrorMessage( this, e.toString() );
3734                 e.printStackTrace();
3735             }
3736         }
3737         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3738             try {
3739                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3740                                            isApplet(),
3741                                            isApplet() ? obtainApplet() : null,
3742                         "_aptx_tax" );
3743             }
3744             catch ( final IOException e ) {
3745                 AptxUtil.showErrorMessage( this, e.toString() );
3746                 e.printStackTrace();
3747             }
3748             catch ( final URISyntaxException e ) {
3749                 AptxUtil.showErrorMessage( this, e.toString() );
3750                 e.printStackTrace();
3751             }
3752         }
3753         else {
3754             cannotOpenBrowserWarningMessage( "taxonomic" );
3755         }
3756     }
3757
3758     final private void paintBranchLength( final Graphics2D g,
3759                                           final PhylogenyNode node,
3760                                           final boolean to_pdf,
3761                                           final boolean to_graphics_file ) {
3762         g.setFont( getTreeFontSet().getSmallFont() );
3763         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3764             g.setColor( Color.BLACK );
3765         }
3766         else {
3767             g.setColor( getTreeColorSet().getBranchLengthColor() );
3768         }
3769         if ( !node.isRoot() ) {
3770             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3771                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3772                                       .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3773             }
3774             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3775                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3776                                       .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3777             }
3778             else {
3779                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3780                                       .getXcoord() + 3, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3781             }
3782         }
3783         else {
3784             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
3785                                   - getTreeFontSet().getSmallMaxDescent(), g );
3786         }
3787     }
3788
3789     final private void paintBranchLite( final Graphics2D g,
3790                                         final float x1,
3791                                         final float x2,
3792                                         final float y1,
3793                                         final float y2,
3794                                         final PhylogenyNode node ) {
3795         g.setColor( getTreeColorSet().getOvColor() );
3796         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3797             drawLine( x1, y1, x2, y2, g );
3798         }
3799         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3800             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3801             ( g ).draw( _quad_curve );
3802         }
3803         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3804             final float dx = x2 - x1;
3805             final float dy = y2 - y1;
3806             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3807                                    + ( dy * 0.8f ), x2, y2 );
3808             ( g ).draw( _cubic_curve );
3809         }
3810         else {
3811             final float x2a = x2;
3812             final float x1a = x1;
3813             // draw the vertical line
3814             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
3815                 drawLine( x1, y1, x1, y2, g );
3816             }
3817             // draw the horizontal line
3818             drawLine( x1a, y2, x2a, y2, g );
3819         }
3820     }
3821
3822     /**
3823      * Paint a branch which consists of a vertical and a horizontal bar
3824      * @param is_ind_found_nodes
3825      */
3826     final private void paintBranchRectangular( final Graphics2D g,
3827                                                final float x1,
3828                                                final float x2,
3829                                                final float y1,
3830                                                final float y2,
3831                                                final PhylogenyNode node,
3832                                                final boolean to_pdf,
3833                                                final boolean to_graphics_file ) {
3834         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
3835         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3836             drawLine( x1, y1, x2, y2, g );
3837         }
3838         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3839             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3840             g.draw( _quad_curve );
3841         }
3842         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3843             final float dx = x2 - x1;
3844             final float dy = y2 - y1;
3845             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3846                                    + ( dy * 0.8f ), x2, y2 );
3847             g.draw( _cubic_curve );
3848         }
3849         else {
3850             final float x2a = x2;
3851             final float x1a = x1;
3852             float y2_r = 0;
3853             if ( node.isFirstChildNode() || node.isLastChildNode()
3854                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3855                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3856                 if ( !to_graphics_file
3857                         && !to_pdf
3858                         && ( ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) && ( y1 < ( getVisibleRect().getMinY() - 20 ) ) ) || ( ( y2 > ( getVisibleRect()
3859                                 .getMaxY() + 20 ) ) && ( y1 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) ) {
3860                     // Do nothing.
3861                 }
3862                 else {
3863                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3864                         float x2c = x1 + EURO_D;
3865                         if ( x2c > x2a ) {
3866                             x2c = x2a;
3867                         }
3868                         drawLine( x1, y1, x2c, y2, g );
3869                     }
3870                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3871                         if ( y2 > y1 ) {
3872                             y2_r = y2 - ROUNDED_D;
3873                             if ( y2_r < y1 ) {
3874                                 y2_r = y1;
3875                             }
3876                             drawLine( x1, y1, x1, y2_r, g );
3877                         }
3878                         else {
3879                             y2_r = y2 + ROUNDED_D;
3880                             if ( y2_r > y1 ) {
3881                                 y2_r = y1;
3882                             }
3883                             drawLine( x1, y1, x1, y2_r, g );
3884                         }
3885                     }
3886                     else {
3887                         drawLine( x1, y1, x1, y2, g );
3888                     }
3889                 }
3890             }
3891             // draw the horizontal line
3892             if ( !to_graphics_file && !to_pdf
3893                     && ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) || ( y2 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) {
3894                 return;
3895             }
3896             float x1_r = 0;
3897             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
3898                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3899                     x1_r = x1a + ROUNDED_D;
3900                     if ( x1_r < x2a ) {
3901                         drawLine( x1_r, y2, x2a, y2, g );
3902                     }
3903                 }
3904                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3905                     final float x1c = x1a + EURO_D;
3906                     if ( x1c < x2a ) {
3907                         drawLine( x1c, y2, x2a, y2, g );
3908                     }
3909                 }
3910                 else {
3911                     drawLine( x1a, y2, x2a, y2, g );
3912                 }
3913             }
3914             else {
3915                 final double w = PhylogenyMethods.getBranchWidthValue( node );
3916                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3917                     x1_r = x1a + ROUNDED_D;
3918                     if ( x1_r < x2a ) {
3919                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
3920                     }
3921                 }
3922                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3923                     final float x1c = x1a + EURO_D;
3924                     if ( x1c < x2a ) {
3925                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
3926                     }
3927                 }
3928                 else {
3929                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
3930                 }
3931             }
3932             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3933                 if ( x1_r > x2a ) {
3934                     x1_r = x2a;
3935                 }
3936                 if ( y2 > y2_r ) {
3937                     final double diff = y2 - y2_r;
3938                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
3939                 }
3940                 else {
3941                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
3942                 }
3943                 g.draw( _arc );
3944             }
3945         }
3946         if ( node.isExternal() ) {
3947             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file );
3948         }
3949     }
3950
3951     final private double paintCirculars( final PhylogenyNode n,
3952                                          final Phylogeny phy,
3953                                          final float center_x,
3954                                          final float center_y,
3955                                          final double radius,
3956                                          final boolean radial_labels,
3957                                          final Graphics2D g,
3958                                          final boolean to_pdf,
3959                                          final boolean to_graphics_file ) {
3960         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
3961             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
3962                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
3963             }
3964             return _urt_nodeid_angle_map.get( n.getId() );
3965         }
3966         else {
3967             final List<PhylogenyNode> descs = n.getDescendants();
3968             double sum = 0;
3969             for( final PhylogenyNode desc : descs ) {
3970                 sum += paintCirculars( desc,
3971                                        phy,
3972                                        center_x,
3973                                        center_y,
3974                                        radius,
3975                                        radial_labels,
3976                                        g,
3977                                        to_pdf,
3978                                        to_graphics_file );
3979             }
3980             double r = 0;
3981             if ( !n.isRoot() ) {
3982                 r = 1 - ( ( ( double ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3983             }
3984             final double theta = sum / descs.size();
3985             n.setXcoord( ( float ) ( center_x + ( r * radius * Math.cos( theta ) ) ) );
3986             n.setYcoord( ( float ) ( center_y + ( r * radius * Math.sin( theta ) ) ) );
3987             _urt_nodeid_angle_map.put( n.getId(), theta );
3988             for( final PhylogenyNode desc : descs ) {
3989                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
3990             }
3991             return theta;
3992         }
3993     }
3994
3995     final private void paintCircularsLite( final PhylogenyNode n,
3996                                            final Phylogeny phy,
3997                                            final int center_x,
3998                                            final int center_y,
3999                                            final int radius,
4000                                            final Graphics2D g ) {
4001         if ( n.isExternal() ) {
4002             return;
4003         }
4004         else {
4005             final List<PhylogenyNode> descs = n.getDescendants();
4006             for( final PhylogenyNode desc : descs ) {
4007                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
4008             }
4009             float r = 0;
4010             if ( !n.isRoot() ) {
4011                 r = 1 - ( ( ( float ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
4012             }
4013             final double theta = _urt_nodeid_angle_map.get( n.getId() );
4014             n.setXSecondary( ( float ) ( center_x + ( radius * r * Math.cos( theta ) ) ) );
4015             n.setYSecondary( ( float ) ( center_y + ( radius * r * Math.sin( theta ) ) ) );
4016             for( final PhylogenyNode desc : descs ) {
4017                 paintBranchCircularLite( n, desc, g );
4018             }
4019         }
4020     }
4021
4022     final private void paintCollapsedNode( final Graphics2D g,
4023                                            final PhylogenyNode node,
4024                                            final boolean to_graphics_file,
4025                                            final boolean to_pdf,
4026                                            final boolean is_in_found_nodes ) {
4027         Color c = null;
4028         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4029             c = Color.BLACK;
4030         }
4031         else if ( is_in_found_nodes ) {
4032             c = getColorForFoundNode( node );
4033         }
4034         else if ( getControlPanel().isColorAccordingToSequence() ) {
4035             c = getSequenceBasedColor( node );
4036         }
4037         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4038             c = getTaxonomyBasedColor( node );
4039         }
4040         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
4041                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
4042             c = PhylogenyMethods.getBranchColorValue( node );
4043         }
4044         else {
4045             c = getTreeColorSet().getCollapseFillColor();
4046         }
4047         double d = node.getAllExternalDescendants().size();
4048         if ( d > 1000 ) {
4049             d = ( 3 * _y_distance ) / 3;
4050         }
4051         else {
4052             d = ( Math.log10( d ) * _y_distance ) / 2.5;
4053         }
4054         final int box_size = getOptions().getDefaultNodeShapeSize() + 1;
4055         if ( d < box_size ) {
4056             d = box_size;
4057         }
4058         final float xx = node.getXcoord() - ( 2 * box_size );
4059         final float xxx = xx > ( node.getParent().getXcoord() + 1 ) ? xx : node.getParent().getXcoord() + 1;
4060         _polygon.reset();
4061         _polygon.moveTo( xxx, node.getYcoord() );
4062         _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() - d );
4063         _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() + d );
4064         _polygon.closePath();
4065         if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.SOLID ) {
4066             g.setColor( c );
4067             g.fill( _polygon );
4068         }
4069         else if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.NONE ) {
4070             g.setColor( getBackground() );
4071             g.fill( _polygon );
4072             g.setColor( c );
4073             g.draw( _polygon );
4074         }
4075         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
4076             g.setPaint( new GradientPaint( xxx, node.getYcoord(), getBackground(), node.getXcoord(), ( float ) ( node
4077                     .getYcoord() - d ), c, false ) );
4078             g.fill( _polygon );
4079             g.setPaint( c );
4080             g.draw( _polygon );
4081         }
4082         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4083     }
4084
4085     final private void paintConfidenceValues( final Graphics2D g,
4086                                               final PhylogenyNode node,
4087                                               final boolean to_pdf,
4088                                               final boolean to_graphics_file ) {
4089         final List<Confidence> confidences = node.getBranchData().getConfidences();
4090         boolean not_first = false;
4091         Collections.sort( confidences );
4092         final StringBuilder sb = new StringBuilder();
4093         for( final Confidence confidence : confidences ) {
4094             if ( ForesterUtil.isEmpty( SHOW_ONLY_THIS_CONF_TYPE )
4095                     || ( !ForesterUtil.isEmpty( confidence.getType() ) && confidence.getType()
4096                             .equalsIgnoreCase( SHOW_ONLY_THIS_CONF_TYPE ) ) ) {
4097                 final double value = confidence.getValue();
4098                 if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4099                     if ( value < getOptions().getMinConfidenceValue() ) {
4100                         return;
4101                     }
4102                     if ( not_first ) {
4103                         sb.append( "/" );
4104                     }
4105                     else {
4106                         not_first = true;
4107                     }
4108                     sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
4109                                                                                 .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4110                     if ( getOptions().isShowConfidenceStddev() ) {
4111                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4112                             sb.append( "(" );
4113                             sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
4114                                                                                         .getStandardDeviation(), getOptions()
4115                                                                                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4116                             sb.append( ")" );
4117                         }
4118                     }
4119                 }
4120             }
4121         }
4122         if ( sb.length() > 0 ) {
4123             final float parent_x = node.getParent().getXcoord();
4124             float x = node.getXcoord();
4125             g.setFont( getTreeFontSet().getSmallFont() );
4126             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
4127                 x += EURO_D;
4128             }
4129             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
4130                 x += ROUNDED_D;
4131             }
4132             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4133                 g.setColor( Color.BLACK );
4134             }
4135             else {
4136                 g.setColor( getTreeColorSet().getConfidenceColor() );
4137             }
4138             final String conf_str = sb.toString();
4139             TreePanel.drawString( conf_str,
4140                                   parent_x
4141                                   + ( ( x - parent_x - getTreeFontSet().getFontMetricsSmall()
4142                                           .stringWidth( conf_str ) ) / 2 ),
4143                                           ( node.getYcoord() + getTreeFontSet().getSmallMaxAscent() ) - 1,
4144                                           g );
4145         }
4146     }
4147
4148     final private void paintGainedAndLostCharacters( final Graphics2D g,
4149                                                      final PhylogenyNode node,
4150                                                      final String gained,
4151                                                      final String lost ) {
4152         if ( node.getParent() != null ) {
4153             final float parent_x = node.getParent().getXcoord();
4154             final float x = node.getXcoord();
4155             g.setFont( getTreeFontSet().getLargeFont() );
4156             g.setColor( getTreeColorSet().getGainedCharactersColor() );
4157             if ( Constants.SPECIAL_CUSTOM ) {
4158                 g.setColor( Color.BLUE );
4159             }
4160             TreePanel
4161             .drawString( gained,
4162                          parent_x
4163                          + ( ( x - parent_x - getFontMetricsForLargeDefaultFont().stringWidth( gained ) ) / 2 ),
4164                          ( node.getYcoord() - getFontMetricsForLargeDefaultFont().getMaxDescent() ),
4165                          g );
4166             g.setColor( getTreeColorSet().getLostCharactersColor() );
4167             TreePanel
4168             .drawString( lost,
4169                          parent_x
4170                          + ( ( x - parent_x - getFontMetricsForLargeDefaultFont().stringWidth( lost ) ) / 2 ),
4171                          ( node.getYcoord() + getFontMetricsForLargeDefaultFont().getMaxAscent() ),
4172                          g );
4173         }
4174     }
4175
4176     /**
4177      * Draw a box at the indicated node.
4178      *
4179      * @param x
4180      * @param y
4181      * @param node
4182      * @param g
4183      */
4184     final private void paintNodeBox( final float x,
4185                                      final float y,
4186                                      final PhylogenyNode node,
4187                                      final Graphics2D g,
4188                                      final boolean to_pdf,
4189                                      final boolean to_graphics_file ) {
4190         if ( node.isCollapse() ) {
4191             return;
4192         }
4193         // if this node should be highlighted, do so
4194         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
4195             g.setColor( getTreeColorSet().getFoundColor0() );
4196             drawOval( x - 8, y - 8, 16, 16, g );
4197             drawOval( x - 9, y - 8, 17, 17, g );
4198             drawOval( x - 9, y - 9, 18, 18, g );
4199         }
4200         if ( ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) )
4201                 || ( getOptions().isShowDefaultNodeShapesExternal() && node.isExternal() )
4202                 || ( getOptions().isShowDefaultNodeShapesInternal() && node.isInternal() )
4203                 || ( getOptions().isShowDefaultNodeShapesForMarkedNodes()
4204                         && ( node.getNodeData().getNodeVisualData() != null ) && ( !node.getNodeData()
4205                                 .getNodeVisualData().isEmpty() ) )
4206                                 || ( getControlPanel().isUseVisualStyles() && ( ( node.getNodeData().getNodeVisualData() != null ) && ( ( node
4207                                         .getNodeData().getNodeVisualData().getNodeColor() != null )
4208                                         || ( node.getNodeData().getNodeVisualData().getSize() != NodeVisualData.DEFAULT_SIZE )
4209                                         || ( node.getNodeData().getNodeVisualData().getFillType() != NodeFill.DEFAULT ) || ( node
4210                                                 .getNodeData().getNodeVisualData().getShape() != NodeShape.DEFAULT ) ) ) )
4211                                                 || ( getControlPanel().isEvents() && node.isHasAssignedEvent() && ( node.getNodeData().getEvent()
4212                                                         .isDuplication()
4213                                                         || node.getNodeData().getEvent().isSpeciation() || node.getNodeData().getEvent()
4214                                                         .isSpeciationOrDuplication() ) ) ) {
4215             NodeVisualData vis = null;
4216             if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
4217                     && ( !node.getNodeData().getNodeVisualData().isEmpty() ) ) {
4218                 vis = node.getNodeData().getNodeVisualData();
4219             }
4220             float box_size = getOptions().getDefaultNodeShapeSize();
4221             if ( ( vis != null ) && ( vis.getSize() != NodeVisualData.DEFAULT_SIZE ) ) {
4222                 box_size = vis.getSize();
4223             }
4224             final float half_box_size = box_size / 2.0f;
4225             Color outline_color = null;
4226             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4227                 outline_color = Color.BLACK;
4228             }
4229             else if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4230                 outline_color = getColorForFoundNode( node );
4231             }
4232             else if ( vis != null ) {
4233                 if ( vis.getNodeColor() != null ) {
4234                     outline_color = vis.getNodeColor();
4235                 }
4236                 else if ( vis.getFontColor() != null ) {
4237                     outline_color = vis.getFontColor();
4238                 }
4239             }
4240             else if ( getControlPanel().isEvents() && TreePanelUtil.isHasAssignedEvent( node ) ) {
4241                 final Event event = node.getNodeData().getEvent();
4242                 if ( event.isDuplication() ) {
4243                     outline_color = getTreeColorSet().getDuplicationBoxColor();
4244                 }
4245                 else if ( event.isSpeciation() ) {
4246                     outline_color = getTreeColorSet().getSpecBoxColor();
4247                 }
4248                 else if ( event.isSpeciationOrDuplication() ) {
4249                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
4250                 }
4251             }
4252             if ( outline_color == null ) {
4253                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
4254                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
4255                     outline_color = getTreeColorSet().getBranchColorForPdf();
4256                 }
4257             }
4258             NodeShape shape = null;
4259             if ( vis != null ) {
4260                 if ( vis.getShape() == NodeShape.CIRCLE ) {
4261                     shape = NodeShape.CIRCLE;
4262                 }
4263                 else if ( vis.getShape() == NodeShape.RECTANGLE ) {
4264                     shape = NodeShape.RECTANGLE;
4265                 }
4266             }
4267             if ( shape == null ) {
4268                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
4269                     shape = NodeShape.CIRCLE;
4270                 }
4271                 else if ( getOptions().getDefaultNodeShape() == NodeShape.RECTANGLE ) {
4272                     shape = NodeShape.RECTANGLE;
4273                 }
4274             }
4275             NodeFill fill = null;
4276             if ( vis != null ) {
4277                 if ( vis.getFillType() == NodeFill.SOLID ) {
4278                     fill = NodeFill.SOLID;
4279                 }
4280                 else if ( vis.getFillType() == NodeFill.NONE ) {
4281                     fill = NodeFill.NONE;
4282                 }
4283                 else if ( vis.getFillType() == NodeFill.GRADIENT ) {
4284                     fill = NodeFill.GRADIENT;
4285                 }
4286             }
4287             if ( fill == null ) {
4288                 if ( getOptions().getDefaultNodeFill() == NodeFill.SOLID ) {
4289                     fill = NodeFill.SOLID;
4290                 }
4291                 else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
4292                     fill = NodeFill.NONE;
4293                 }
4294                 else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
4295                     fill = NodeFill.GRADIENT;
4296                 }
4297             }
4298             Color vis_fill_color = null;
4299             if ( ( vis != null ) && ( vis.getNodeColor() != null ) ) {
4300                 vis_fill_color = vis.getNodeColor();
4301             }
4302             if ( shape == NodeShape.CIRCLE ) {
4303                 if ( fill == NodeFill.GRADIENT ) {
4304                     drawOvalGradient( x - half_box_size, y - half_box_size, box_size, box_size, g, to_pdf ? Color.WHITE
4305                             : outline_color, to_pdf ? outline_color : getBackground(), outline_color );
4306                 }
4307                 else if ( fill == NodeFill.NONE ) {
4308                     Color background = getBackground();
4309                     if ( to_pdf ) {
4310                         background = Color.WHITE;
4311                     }
4312                     drawOvalGradient( x - half_box_size,
4313                                       y - half_box_size,
4314                                       box_size,
4315                                       box_size,
4316                                       g,
4317                                       background,
4318                                       background,
4319                                       outline_color );
4320                 }
4321                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
4322                     if ( vis_fill_color != null ) {
4323                         g.setColor( vis_fill_color );
4324                     }
4325                     else {
4326                         g.setColor( outline_color );
4327                     }
4328                     drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4329                 }
4330             }
4331             else if ( shape == NodeVisualData.NodeShape.RECTANGLE ) {
4332                 if ( fill == NodeVisualData.NodeFill.GRADIENT ) {
4333                     drawRectGradient( x - half_box_size, y - half_box_size, box_size, box_size, g, to_pdf ? Color.WHITE
4334                             : outline_color, to_pdf ? outline_color : getBackground(), outline_color );
4335                 }
4336                 else if ( fill == NodeVisualData.NodeFill.NONE ) {
4337                     Color background = getBackground();
4338                     if ( to_pdf ) {
4339                         background = Color.WHITE;
4340                     }
4341                     drawRectGradient( x - half_box_size,
4342                                       y - half_box_size,
4343                                       box_size,
4344                                       box_size,
4345                                       g,
4346                                       background,
4347                                       background,
4348                                       outline_color );
4349                 }
4350                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
4351                     if ( vis_fill_color != null ) {
4352                         g.setColor( vis_fill_color );
4353                     }
4354                     else {
4355                         g.setColor( outline_color );
4356                     }
4357                     drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4358                 }
4359             }
4360         }
4361     }
4362
4363     final private int paintNodeData( final Graphics2D g,
4364                                      final PhylogenyNode node,
4365                                      final boolean to_graphics_file,
4366                                      final boolean to_pdf,
4367                                      final boolean is_in_found_nodes ) {
4368         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
4369             return 0;
4370         }
4371         if ( getControlPanel().isWriteBranchLengthValues()
4372                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4373                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4374                         && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
4375             paintBranchLength( g, node, to_pdf, to_graphics_file );
4376         }
4377         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
4378             return 0;
4379         }
4380         _sb.setLength( 0 );
4381         int x = 0;
4382         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
4383         if ( getControlPanel().isShowTaxonomyImages()
4384                 && ( getImageMap() != null )
4385                 && !getImageMap().isEmpty()
4386                 && node.getNodeData().isHasTaxonomy()
4387                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
4388                         .getUris().isEmpty() ) ) {
4389             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
4390         }
4391         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4392                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
4393             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
4394         }
4395         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
4396         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4397             if ( _sb.length() > 0 ) {
4398                 _sb.setLength( 0 );
4399                 _sb.append( " (" );
4400                 _sb.append( node.getAllExternalDescendants().size() );
4401                 _sb.append( ")" );
4402             }
4403         }
4404         else {
4405             _sb.setLength( 0 );
4406         }
4407         nodeDataAsSB( node, _sb );
4408         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4409         float down_shift_factor = 3.0f;
4410         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
4411             down_shift_factor = 1;
4412         }
4413         final float pos_x = node.getXcoord() + x + 2 + half_box_size;
4414         float pos_y;
4415         if ( !using_visual_font ) {
4416             pos_y = ( node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ) );
4417         }
4418         else {
4419             pos_y = ( node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / down_shift_factor ) );
4420         }
4421         final String sb_str = _sb.toString();
4422         // GUILHEM_BEG ______________
4423         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
4424                 && ( _query_sequence != null ) ) {
4425             int nodeTextBoundsWidth = 0;
4426             if ( sb_str.length() > 0 ) {
4427                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
4428                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
4429             }
4430             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
4431                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
4432                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
4433                     g.setColor( getTreeColorSet().getBackgroundColor() );
4434                 }
4435             }
4436             else {
4437                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
4438                 for( final SequenceRelation seqRelation : seqRelations ) {
4439                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
4440                             .getRef1().isEqual( _query_sequence ) )
4441                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
4442                                                              .getSelectedItem() );
4443                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
4444                         final double linePosX = node.getXcoord() + 2 + half_box_size;
4445                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
4446                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
4447                                         + ")";
4448                         if ( sConfidence != null ) {
4449                             float confidenceX = pos_x;
4450                             if ( sb_str.length() > 0 ) {
4451                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
4452                                         + CONFIDENCE_LEFT_MARGIN;
4453                             }
4454                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code
4455                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
4456                                 .getBounds().getWidth();
4457                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
4458                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
4459                             }
4460                         }
4461                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
4462                         {
4463                             if ( nodeTextBoundsWidth == 0 ) {
4464                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
4465                             }
4466                             else {
4467                                 nodeTextBoundsWidth += 2;
4468                             }
4469                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
4470                                         + nodeTextBoundsWidth, 3 + ( int ) pos_y );
4471                             break;
4472                         }
4473                     }
4474                 }
4475             }
4476         }
4477         if ( sb_str.length() > 0 ) {
4478             TreePanel.drawString( sb_str, pos_x, pos_y, g );
4479         }
4480         // GUILHEM_END _____________
4481         if ( _sb.length() > 0 ) {
4482             if ( !using_visual_font && !is_in_found_nodes ) {
4483                 x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
4484             }
4485             else {
4486                 x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
4487             }
4488         }
4489         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
4490                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4491                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4492             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
4493             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4494                 g.setColor( Color.BLACK );
4495             }
4496             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
4497                 g.setColor( calculateColorForAnnotation( ann ) );
4498             }
4499             final String ann_str = TreePanelUtil.createAnnotationString( ann, getOptions().isShowAnnotationRefSource() );
4500             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
4501                                   + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ), g );
4502             _sb.setLength( 0 );
4503             _sb.append( ann_str );
4504             if ( _sb.length() > 0 ) {
4505                 if ( !using_visual_font && !is_in_found_nodes ) {
4506                     x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
4507                 }
4508                 else {
4509                     x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
4510                 }
4511             }
4512         }
4513         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4514                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
4515                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
4516             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
4517                     && node.getNodeData().isHasBinaryCharacters() ) {
4518                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4519                     g.setColor( Color.BLACK );
4520                 }
4521                 else {
4522                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
4523                 }
4524                 if ( getControlPanel().isShowBinaryCharacters() ) {
4525                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
4526                                           .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
4527                                           + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ), g );
4528                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
4529                                                   .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
4530                                                   .getLostCharactersAsStringBuffer().toString() );
4531                 }
4532                 else {
4533                     TreePanel
4534                     .drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
4535                                  node.getXcoord() + x + 4 + half_box_size,
4536                                  node.getYcoord()
4537                                  + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ),
4538                                  g );
4539                     paintGainedAndLostCharacters( g, node, "+"
4540                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
4541                                     + node.getNodeData().getBinaryCharacters().getLostCount() );
4542                 }
4543             }
4544         }
4545         return x;
4546     }
4547
4548     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
4549                                                   final PhylogenyNode node,
4550                                                   final boolean to_pdf,
4551                                                   final boolean to_graphics_file,
4552                                                   final boolean radial_labels,
4553                                                   final double ur_angle,
4554                                                   final boolean is_in_found_nodes ) {
4555         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
4556             return;
4557         }
4558         _sb.setLength( 0 );
4559         _sb.append( " " );
4560         if ( node.getNodeData().isHasTaxonomy()
4561                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4562                         .isShowTaxonomyCommonNames() ) ) {
4563             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4564             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4565                 _sb.append( taxonomy.getTaxonomyCode() );
4566                 _sb.append( " " );
4567             }
4568             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4569                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4570                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4571                     _sb.append( taxonomy.getScientificName() );
4572                     _sb.append( " (" );
4573                     _sb.append( taxonomy.getCommonName() );
4574                     _sb.append( ") " );
4575                 }
4576                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4577                     _sb.append( taxonomy.getScientificName() );
4578                     _sb.append( " " );
4579                 }
4580                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4581                     _sb.append( taxonomy.getCommonName() );
4582                     _sb.append( " " );
4583                 }
4584             }
4585             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4586                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4587                     _sb.append( taxonomy.getScientificName() );
4588                     _sb.append( " " );
4589                 }
4590             }
4591             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4592                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4593                     _sb.append( taxonomy.getCommonName() );
4594                     _sb.append( " " );
4595                 }
4596             }
4597         }
4598         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4599             _sb.append( " [" );
4600             _sb.append( node.getAllExternalDescendants().size() );
4601             _sb.append( "]" );
4602         }
4603         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
4604             if ( _sb.length() > 0 ) {
4605                 _sb.append( " " );
4606             }
4607             _sb.append( node.getName() );
4608         }
4609         if ( node.getNodeData().isHasSequence() ) {
4610             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4611                 if ( _sb.length() > 0 ) {
4612                     _sb.append( " " );
4613                 }
4614                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4615                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4616                     _sb.append( ":" );
4617                 }
4618                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4619             }
4620             if ( getControlPanel().isShowSeqNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
4621                 if ( _sb.length() > 0 ) {
4622                     _sb.append( " " );
4623                 }
4624                 _sb.append( node.getNodeData().getSequence().getName() );
4625             }
4626         }
4627         //g.setFont( getTreeFontSet().getLargeFont() );
4628         //if ( is_in_found_nodes ) {
4629         //    g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4630         // }
4631         if ( _sb.length() > 1 ) {
4632             setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
4633             final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4634             final String sb_str = _sb.toString();
4635             double m = 0;
4636             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4637                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
4638             }
4639             else {
4640                 m = ( float ) ( ur_angle % TWO_PI );
4641             }
4642             _at = g.getTransform();
4643             boolean need_to_reset = false;
4644             final float x_coord = node.getXcoord();
4645             float y_coord;
4646             if ( !using_visual_font ) {
4647                 y_coord = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / 3.0f );
4648             }
4649             else {
4650                 y_coord = node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / 3.0f );
4651             }
4652             if ( radial_labels ) {
4653                 need_to_reset = true;
4654                 boolean left = false;
4655                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4656                     m -= PI;
4657                     left = true;
4658                 }
4659                 g.rotate( m, x_coord, node.getYcoord() );
4660                 if ( left ) {
4661                     if ( !using_visual_font ) {
4662                         g.translate( -( getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth() ),
4663                                      0 );
4664                     }
4665                     else {
4666                         g.translate( -( getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth() ), 0 );
4667                     }
4668                 }
4669             }
4670             else {
4671                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4672                     need_to_reset = true;
4673                     if ( !using_visual_font ) {
4674                         g.translate( -getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth(), 0 );
4675                     }
4676                     else {
4677                         g.translate( -getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth(), 0 );
4678                     }
4679                 }
4680             }
4681             TreePanel.drawString( sb_str, x_coord, y_coord, g );
4682             if ( need_to_reset ) {
4683                 g.setTransform( _at );
4684             }
4685         }
4686     }
4687
4688     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
4689         if ( node.isCollapse() ) {
4690             if ( !node.isRoot() && !node.getParent().isCollapse() ) {
4691                 paintCollapsedNode( g, node, false, false, false );
4692             }
4693             return;
4694         }
4695         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4696             g.setColor( getColorForFoundNode( node ) );
4697             drawRectFilled( node.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, node.getYSecondary()
4698                             - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, OVERVIEW_FOUND_NODE_BOX_SIZE, OVERVIEW_FOUND_NODE_BOX_SIZE, g );
4699         }
4700         float new_x = 0;
4701         if ( !node.isExternal() && !node.isCollapse() ) {
4702             boolean first_child = true;
4703             float y2 = 0.0f;
4704             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4705             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4706                 final PhylogenyNode child_node = node.getChildNode( i );
4707                 int factor_x;
4708                 if ( !isUniformBranchLengthsForCladogram() ) {
4709                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4710                 }
4711                 else {
4712                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4713                 }
4714                 if ( first_child ) {
4715                     first_child = false;
4716                     y2 = node.getYSecondary()
4717                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
4718                                     .getNumberOfExternalNodes() ) );
4719                 }
4720                 else {
4721                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4722                 }
4723                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
4724                 new_x = x2 + node.getXSecondary();
4725                 final float diff_y = node.getYSecondary() - y2;
4726                 final float diff_x = node.getXSecondary() - new_x;
4727                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
4728                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
4729                 }
4730                 child_node.setXSecondary( new_x );
4731                 child_node.setYSecondary( y2 );
4732                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4733             }
4734         }
4735     }
4736
4737     final private void paintNodeRectangular( final Graphics2D g,
4738                                              final PhylogenyNode node,
4739                                              final boolean to_pdf,
4740                                              final boolean dynamically_hide,
4741                                              final int dynamic_hiding_factor,
4742                                              final boolean to_graphics_file,
4743                                              final boolean disallow_shortcutting ) {
4744         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
4745         if ( node.isCollapse() ) {
4746             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) ) {
4747                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4748             }
4749             return;
4750         }
4751         if ( node.isExternal() ) {
4752             ++_external_node_index;
4753         }
4754         // Confidence values
4755         if ( getControlPanel().isShowConfidenceValues()
4756                 && !node.isExternal()
4757                 && !node.isRoot()
4758                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
4759                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4760                         && node.getBranchData().isHasConfidences() ) {
4761             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
4762         }
4763         // Draw a line to root:
4764         if ( node.isRoot() && _phylogeny.isRooted() ) {
4765             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
4766         }
4767         float new_x = 0;
4768         float new_x_min = Float.MAX_VALUE;
4769         float min_dist = 1.5f;
4770         if ( !disallow_shortcutting ) {
4771             if ( dynamic_hiding_factor > 4000 ) {
4772                 min_dist = 4;
4773             }
4774             else if ( dynamic_hiding_factor > 1000 ) {
4775                 min_dist = 3;
4776             }
4777             else if ( dynamic_hiding_factor > 100 ) {
4778                 min_dist = 2;
4779             }
4780         }
4781         if ( !node.isExternal() && !node.isCollapse() ) {
4782             boolean first_child = true;
4783             float y2 = 0.0f;
4784             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4785             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4786                 final PhylogenyNode child_node = node.getChildNode( i );
4787                 int factor_x;
4788                 if ( !isUniformBranchLengthsForCladogram() ) {
4789                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4790                 }
4791                 else {
4792                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4793                 }
4794                 if ( first_child ) {
4795                     first_child = false;
4796                     y2 = node.getYcoord()
4797                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
4798                 }
4799                 else {
4800                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
4801                 }
4802                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
4803                 new_x = x2 + node.getXcoord();
4804                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
4805                     new_x_min = x2;
4806                 }
4807                 final float diff_y = node.getYcoord() - y2;
4808                 final float diff_x = node.getXcoord() - new_x;
4809                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
4810                         || ( diff_x < -min_dist ) ) {
4811                     paintBranchRectangular( g,
4812                                             node.getXcoord(),
4813                                             new_x,
4814                                             node.getYcoord(),
4815                                             y2,
4816                                             child_node,
4817                                             to_pdf,
4818                                             to_graphics_file );
4819                 }
4820                 child_node.setXcoord( new_x );
4821                 child_node.setYcoord( y2 );
4822                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
4823             }
4824             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file );
4825         }
4826         if ( dynamically_hide
4827                 && !is_in_found_nodes
4828                 && ( ( node.isExternal() && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) ) || ( !node
4829                         .isExternal() && ( ( new_x_min < 20 ) || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getFontMetricsForLargeDefaultFont()
4830                                 .getHeight() ) ) ) ) ) {
4831             return;
4832         }
4833         final int x = paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4834         paintNodeWithRenderableData( x, g, node, to_graphics_file, to_pdf );
4835     }
4836
4837     final private void paintNodeWithRenderableData( final int x,
4838                                                     final Graphics2D g,
4839                                                     final PhylogenyNode node,
4840                                                     final boolean to_graphics_file,
4841                                                     final boolean to_pdf ) {
4842         if ( isNodeDataInvisible( node ) && !( to_graphics_file || to_pdf ) ) {
4843             return;
4844         }
4845         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
4846             return;
4847         }
4848         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4849                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null )
4850                 && ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
4851             RenderableDomainArchitecture rds = null;
4852             try {
4853                 rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
4854             }
4855             catch ( final ClassCastException cce ) {
4856                 cce.printStackTrace();
4857             }
4858             if ( rds != null ) {
4859                 final int default_height = 7;
4860                 float y = getYdistance();
4861                 if ( getControlPanel().isDynamicallyHideData() ) {
4862                     y = getTreeFontSet().getFontMetricsLarge().getHeight();
4863                 }
4864                 final int h = y < default_height ? ForesterUtil.roundToInt( y ) : default_height;
4865                 rds.setRenderingHeight( h > 1 ? h : 2 );
4866                 if ( getControlPanel().isDrawPhylogram() ) {
4867                     if ( getOptions().isLineUpRendarableNodeData() ) {
4868                         if ( getOptions().isRightLineUpDomains() ) {
4869                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
4870                                     + _length_of_longest_text + ( ( _longest_domain - rds.getTotalLength() ) * rds
4871                                             .getRenderingFactorWidth() ) ), node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
4872                         }
4873                         else {
4874                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
4875                                         node.getYcoord() - ( h / 2.0f ),
4876                                         g,
4877                                         this,
4878                                         to_pdf );
4879                         }
4880                     }
4881                     else {
4882                         rds.render( node.getXcoord() + x, node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
4883                     }
4884                 }
4885                 else {
4886                     if ( getOptions().isRightLineUpDomains() ) {
4887                         rds.render( ( ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text ) - 20 )
4888                                     + ( ( _longest_domain - rds.getTotalLength() ) * rds
4889                                             .getRenderingFactorWidth() ),
4890                                             node.getYcoord() - ( h / 2.0f ),
4891                                             g,
4892                                             this,
4893                                             to_pdf );
4894                     }
4895                     else {
4896                         rds.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
4897                                     node.getYcoord() - ( h / 2.0f ),
4898                                     g,
4899                                     this,
4900                                     to_pdf );
4901                     }
4902                 }
4903             }
4904         }
4905         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4906                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
4907             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
4908                                                                          getStatisticsForExpressionValues(),
4909                                                                          getConfiguration() );
4910             if ( rv != null ) {
4911                 double domain_add = 0;
4912                 if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4913                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4914                     domain_add = _domain_structure_width + 10;
4915                 }
4916                 if ( getControlPanel().isDrawPhylogram() ) {
4917                     rv.render( ( float ) ( node.getXcoord() + x + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
4918                 }
4919                 else {
4920                     rv.render( ( float ) ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text + domain_add ),
4921                                node.getYcoord() - 3,
4922                                g,
4923                                this,
4924                                to_pdf );
4925                 }
4926             }
4927         }
4928         if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
4929                 && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
4930                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
4931             final RenderableMsaSequence rs = RenderableMsaSequence.createInstance( node.getNodeData().getSequence()
4932                                                                                    .getMolecularSequence(), node.getNodeData().getSequence().getType(), getConfiguration() );
4933             if ( rs != null ) {
4934                 final int default_height = 7;
4935                 float y = getYdistance();
4936                 if ( getControlPanel().isDynamicallyHideData() ) {
4937                     y = getTreeFontSet().getFontMetricsLarge().getHeight();
4938                 }
4939                 final int h = y < default_height ? ForesterUtil.roundToInt( y ) : default_height;
4940                 rs.setRenderingHeight( h > 1 ? h : 2 );
4941                 if ( getControlPanel().isDrawPhylogram() ) {
4942                     rs.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
4943                                node.getYcoord() - ( h / 2.0f ),
4944                                g,
4945                                this,
4946                                to_pdf );
4947                 }
4948                 else {
4949                     rs.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
4950                                node.getYcoord() - ( h / 2.0f ),
4951                                g,
4952                                this,
4953                                to_pdf );
4954                 }
4955             }
4956         }
4957     }
4958
4959     final private int calcLengthOfLongestText() {
4960         final StringBuilder sb = new StringBuilder();
4961         if ( _ext_node_with_longest_txt_info != null ) {
4962             nodeDataAsSB( _ext_node_with_longest_txt_info, sb );
4963             if ( _ext_node_with_longest_txt_info.getNodeData().isHasTaxonomy() ) {
4964                 nodeTaxonomyDataAsSB( _ext_node_with_longest_txt_info.getNodeData().getTaxonomy(), sb );
4965             }
4966         }
4967         return getFontMetricsForLargeDefaultFont().stringWidth( sb.toString() );
4968     }
4969
4970     final private void paintOvRectangle( final Graphics2D g ) {
4971         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
4972         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
4973         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
4974         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
4975         final float width = getOvMaxWidth() / w_ratio;
4976         final float height = getOvMaxHeight() / h_ratio;
4977         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
4978         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
4979         g.setColor( getTreeColorSet().getFoundColor0() );
4980         getOvRectangle().setRect( x, y, width, height );
4981         final Stroke s = g.getStroke();
4982         g.setStroke( STROKE_1 );
4983         if ( ( width < 6 ) && ( height < 6 ) ) {
4984             drawRectFilled( x, y, 6, 6, g );
4985             getOvVirtualRectangle().setRect( x, y, 6, 6 );
4986         }
4987         else if ( width < 6 ) {
4988             drawRectFilled( x, y, 6, height, g );
4989             getOvVirtualRectangle().setRect( x, y, 6, height );
4990         }
4991         else if ( height < 6 ) {
4992             drawRectFilled( x, y, width, 6, g );
4993             getOvVirtualRectangle().setRect( x, y, width, 6 );
4994         }
4995         else {
4996             drawRect( x, y, width, height, g );
4997             if ( isInOvRect() ) {
4998                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
4999             }
5000             getOvVirtualRectangle().setRect( x, y, width, height );
5001         }
5002         g.setStroke( s );
5003     }
5004
5005     final private void paintPhylogenyLite( final Graphics2D g ) {
5006         _phylogeny
5007         .getRoot()
5008         .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
5009                 .getWidth() ) ) ) );
5010         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
5011         final Stroke s = g.getStroke();
5012         g.setStroke( STROKE_05 );
5013         for( final PhylogenyNode element : _nodes_in_preorder ) {
5014             paintNodeLite( g, element );
5015         }
5016         g.setStroke( s );
5017         paintOvRectangle( g );
5018     }
5019
5020     /**
5021      * Paint the root branch. (Differs from others because it will always be a
5022      * single horizontal line).
5023      * @param to_graphics_file
5024      *
5025      * @return new x1 value
5026      */
5027     final private void paintRootBranch( final Graphics2D g,
5028                                         final float x1,
5029                                         final float y1,
5030                                         final PhylogenyNode root,
5031                                         final boolean to_pdf,
5032                                         final boolean to_graphics_file ) {
5033         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
5034         float d = getXdistance();
5035         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
5036             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
5037         }
5038         if ( d < MIN_ROOT_LENGTH ) {
5039             d = MIN_ROOT_LENGTH;
5040         }
5041         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
5042             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
5043         }
5044         else {
5045             final double w = PhylogenyMethods.getBranchWidthValue( root );
5046             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
5047         }
5048         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file );
5049     }
5050
5051     final private void paintScale( final Graphics2D g,
5052                                    int x1,
5053                                    int y1,
5054                                    final boolean to_pdf,
5055                                    final boolean to_graphics_file ) {
5056         x1 += MOVE;
5057         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
5058         y1 -= 12;
5059         final int y2 = y1 - 8;
5060         final int y3 = y1 - 4;
5061         g.setFont( getTreeFontSet().getSmallFont() );
5062         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5063             g.setColor( Color.BLACK );
5064         }
5065         else {
5066             g.setColor( getTreeColorSet().getBranchLengthColor() );
5067         }
5068         final Stroke s = g.getStroke();
5069         g.setStroke( STROKE_1 );
5070         drawLine( x1, y1, x1, y2, g );
5071         drawLine( x2, y1, x2, y2, g );
5072         drawLine( x1, y3, x2, y3, g );
5073         if ( getScaleLabel() != null ) {
5074             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
5075         }
5076         g.setStroke( s );
5077     }
5078
5079     final private int paintTaxonomy( final Graphics2D g,
5080                                      final PhylogenyNode node,
5081                                      final boolean is_in_found_nodes,
5082                                      final boolean to_pdf,
5083                                      final boolean to_graphics_file,
5084                                      final float x_shift ) {
5085         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
5086         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
5087         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getTaxonomyColor() );
5088         final float start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
5089         float start_y;
5090         if ( !using_visual_font ) {
5091             start_y = node.getYcoord()
5092                     + ( getFontMetricsForLargeDefaultFont().getAscent() / ( node.getNumberOfDescendants() == 1 ? 1
5093                             : 3.0f ) );
5094         }
5095         else {
5096             start_y = node.getYcoord()
5097                     + ( getFontMetrics( g.getFont() ).getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
5098         }
5099         _sb.setLength( 0 );
5100         nodeTaxonomyDataAsSB( taxonomy, _sb );
5101         final String label = _sb.toString();
5102         /* GUILHEM_BEG */
5103         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
5104                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
5105             // invert font color and background color to show that this is the query sequence
5106             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
5107                                                                                                           false,
5108                                                                                                           false ) )
5109             .getBounds();
5110             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
5111             g.setColor( getTreeColorSet().getBackgroundColor() );
5112         }
5113         /* GUILHEM_END */
5114         TreePanel.drawString( label, start_x, start_y, g );
5115         if ( !using_visual_font && !is_in_found_nodes ) {
5116             return getFontMetricsForLargeDefaultFont().stringWidth( label );
5117         }
5118         return getFontMetrics( g.getFont() ).stringWidth( label );
5119     }
5120
5121     final private void paintUnrooted( final PhylogenyNode n,
5122                                       final double low_angle,
5123                                       final double high_angle,
5124                                       final boolean radial_labels,
5125                                       final Graphics2D g,
5126                                       final boolean to_pdf,
5127                                       final boolean to_graphics_file ) {
5128         if ( n.isRoot() ) {
5129             n.setXcoord( getWidth() / 2 );
5130             n.setYcoord( getHeight() / 2 );
5131         }
5132         if ( n.isExternal() ) {
5133             paintNodeDataUnrootedCirc( g,
5134                                        n,
5135                                        to_pdf,
5136                                        to_graphics_file,
5137                                        radial_labels,
5138                                        ( high_angle + low_angle ) / 2,
5139                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
5140             return;
5141         }
5142         final float num_enclosed = n.getNumberOfExternalNodes();
5143         final float x = n.getXcoord();
5144         final float y = n.getYcoord();
5145         double current_angle = low_angle;
5146         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
5147         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
5148         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
5149         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
5150         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5151             final PhylogenyNode desc = n.getChildNode( i );
5152             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
5153             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
5154             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
5155             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
5156             //     continue;
5157             // }
5158             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
5159             //    continue;
5160             //}
5161             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
5162             //    continue;
5163             // }
5164             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5165             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5166             float length;
5167             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5168                 if ( desc.getDistanceToParent() < 0 ) {
5169                     length = 0;
5170                 }
5171                 else {
5172                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
5173                 }
5174             }
5175             else {
5176                 length = getUrtFactor();
5177             }
5178             final double mid_angle = current_angle + ( arc_size / 2 );
5179             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5180             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5181             desc.setXcoord( new_x );
5182             desc.setYcoord( new_y );
5183             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
5184             current_angle += arc_size;
5185             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
5186             drawLine( x, y, new_x, new_y, g );
5187             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file );
5188         }
5189         if ( n.isRoot() ) {
5190             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file );
5191         }
5192     }
5193
5194     final private void paintUnrootedLite( final PhylogenyNode n,
5195                                           final double low_angle,
5196                                           final double high_angle,
5197                                           final Graphics2D g,
5198                                           final float urt_ov_factor ) {
5199         if ( n.isRoot() ) {
5200             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
5201             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
5202             n.setXSecondary( x_pos );
5203             n.setYSecondary( y_pos );
5204         }
5205         if ( n.isExternal() ) {
5206             return;
5207         }
5208         final float num_enclosed = n.getNumberOfExternalNodes();
5209         final float x = n.getXSecondary();
5210         final float y = n.getYSecondary();
5211         double current_angle = low_angle;
5212         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5213             final PhylogenyNode desc = n.getChildNode( i );
5214             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5215             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5216             float length;
5217             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5218                 if ( desc.getDistanceToParent() < 0 ) {
5219                     length = 0;
5220                 }
5221                 else {
5222                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
5223                 }
5224             }
5225             else {
5226                 length = urt_ov_factor;
5227             }
5228             final double mid_angle = current_angle + ( arc_size / 2 );
5229             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5230             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5231             desc.setXSecondary( new_x );
5232             desc.setYSecondary( new_y );
5233             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
5234                 g.setColor( getColorForFoundNode( desc ) );
5235                 drawRectFilled( desc.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5236                                 desc.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5237                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5238                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5239                                 g );
5240                 g.setColor( getTreeColorSet().getOvColor() );
5241             }
5242             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
5243             current_angle += arc_size;
5244             drawLine( x, y, new_x, new_y, g );
5245         }
5246     }
5247
5248     final private void pasteSubtree( final PhylogenyNode node ) {
5249         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5250             errorMessageNoCutCopyPasteInUnrootedDisplay();
5251             return;
5252         }
5253         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
5254             JOptionPane.showMessageDialog( this,
5255                                            "No tree in buffer (need to copy or cut a subtree first)",
5256                                            "Attempt to paste with empty buffer",
5257                                            JOptionPane.ERROR_MESSAGE );
5258             return;
5259         }
5260         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
5261         final Object[] options = { "As sibling", "As descendant", "Cancel" };
5262         final int r = JOptionPane.showOptionDialog( this,
5263                                                     "How to paste subtree" + label + "?",
5264                                                     "Paste Subtree",
5265                                                     JOptionPane.CLOSED_OPTION,
5266                                                     JOptionPane.QUESTION_MESSAGE,
5267                                                     null,
5268                                                     options,
5269                                                     options[ 2 ] );
5270         boolean paste_as_sibling = true;
5271         if ( r == 1 ) {
5272             paste_as_sibling = false;
5273         }
5274         else if ( r != 0 ) {
5275             return;
5276         }
5277         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
5278         buffer_phy.setAllNodesToNotCollapse();
5279         PhylogenyMethods.preOrderReId( buffer_phy );
5280         buffer_phy.setRooted( true );
5281         boolean need_to_show_whole = false;
5282         if ( paste_as_sibling ) {
5283             if ( node.isRoot() ) {
5284                 JOptionPane.showMessageDialog( this,
5285                                                "Cannot paste sibling to root",
5286                                                "Attempt to paste sibling to root",
5287                                                JOptionPane.ERROR_MESSAGE );
5288                 return;
5289             }
5290             buffer_phy.addAsSibling( node );
5291         }
5292         else {
5293             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
5294                 need_to_show_whole = true;
5295                 _phylogeny = buffer_phy;
5296             }
5297             else {
5298                 buffer_phy.addAsChild( node );
5299             }
5300         }
5301         if ( getCopiedAndPastedNodes() == null ) {
5302             setCopiedAndPastedNodes( new HashSet<Long>() );
5303         }
5304         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
5305         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
5306         for( final PhylogenyNode n : nodes ) {
5307             node_ids.add( n.getId() );
5308         }
5309         node_ids.add( node.getId() );
5310         getCopiedAndPastedNodes().addAll( node_ids );
5311         setNodeInPreorderToNull();
5312         _phylogeny.externalNodesHaveChanged();
5313         _phylogeny.clearHashIdToNodeMap();
5314         _phylogeny.recalculateNumberOfExternalDescendants( true );
5315         resetNodeIdToDistToLeafMap();
5316         setEdited( true );
5317         if ( need_to_show_whole ) {
5318             getControlPanel().showWhole();
5319         }
5320         repaint();
5321     }
5322
5323     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
5324         final PropertiesMap properties = node.getNodeData().getProperties();
5325         final StringBuffer sb = new StringBuffer();
5326         boolean first = true;
5327         for( final String ref : properties.getPropertyRefs() ) {
5328             if ( first ) {
5329                 first = false;
5330             }
5331             else {
5332                 sb.append( " " );
5333             }
5334             final Property p = properties.getProperty( ref );
5335             sb.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5336             sb.append( "=" );
5337             sb.append( p.getValue() );
5338             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5339                 sb.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5340             }
5341         }
5342         return sb;
5343     }
5344
5345     private void setColor( final Graphics2D g,
5346                            final PhylogenyNode node,
5347                            final boolean to_graphics_file,
5348                            final boolean to_pdf,
5349                            final boolean is_in_found_nodes,
5350                            final Color default_color ) {
5351         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5352             g.setColor( Color.BLACK );
5353         }
5354         else if ( is_in_found_nodes ) {
5355             g.setColor( getColorForFoundNode( node ) );
5356         }
5357         else if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
5358                 && ( node.getNodeData().getNodeVisualData().getFontColor() != null ) ) {
5359             g.setColor( node.getNodeData().getNodeVisualData().getFontColor() );
5360         }
5361         else if ( getControlPanel().isColorAccordingToSequence() ) {
5362             g.setColor( getSequenceBasedColor( node ) );
5363         }
5364         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5365             g.setColor( getTaxonomyBasedColor( node ) );
5366         }
5367         else if ( getControlPanel().isColorAccordingToAnnotation()
5368                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
5369                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
5370             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
5371         }
5372         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
5373                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
5374             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
5375         }
5376         else if ( to_pdf ) {
5377             g.setColor( Color.BLACK );
5378         }
5379         else {
5380             g.setColor( default_color );
5381         }
5382     }
5383
5384     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
5385         getMainPanel().setCopiedAndPastedNodes( nodeIds );
5386     }
5387
5388     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
5389         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
5390     }
5391
5392     private boolean setFont( final Graphics2D g, final PhylogenyNode node, final boolean is_in_found_nodes ) {
5393         Font visual_font = null;
5394         if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null ) ) {
5395             visual_font = node.getNodeData().getNodeVisualData().getFont();
5396             g.setFont( visual_font != null ? visual_font : getTreeFontSet().getLargeFont() );
5397         }
5398         else {
5399             g.setFont( getTreeFontSet().getLargeFont() );
5400         }
5401         if ( is_in_found_nodes ) {
5402             g.setFont( g.getFont().deriveFont( Font.BOLD ) );
5403         }
5404         return visual_font != null;
5405     }
5406
5407     final private void setInOv( final boolean in_ov ) {
5408         _in_ov = in_ov;
5409     }
5410
5411     final private void setOvMaxHeight( final float ov_max_height ) {
5412         _ov_max_height = ov_max_height;
5413     }
5414
5415     final private void setOvMaxWidth( final float ov_max_width ) {
5416         _ov_max_width = ov_max_width;
5417     }
5418
5419     final private void setOvXcorrectionFactor( final float f ) {
5420         _ov_x_correction_factor = f;
5421     }
5422
5423     final private void setOvXDistance( final float ov_x_distance ) {
5424         _ov_x_distance = ov_x_distance;
5425     }
5426
5427     final private void setOvXPosition( final int ov_x_position ) {
5428         _ov_x_position = ov_x_position;
5429     }
5430
5431     final private void setOvYDistance( final float ov_y_distance ) {
5432         _ov_y_distance = ov_y_distance;
5433     }
5434
5435     final private void setOvYPosition( final int ov_y_position ) {
5436         _ov_y_position = ov_y_position;
5437     }
5438
5439     final private void setOvYStart( final int ov_y_start ) {
5440         _ov_y_start = ov_y_start;
5441     }
5442
5443     final private void setScaleDistance( final double scale_distance ) {
5444         _scale_distance = scale_distance;
5445     }
5446
5447     final private void setScaleLabel( final String scale_label ) {
5448         _scale_label = scale_label;
5449     }
5450
5451     private final void setupStroke( final Graphics2D g ) {
5452         if ( getYdistance() < 0.0001 ) {
5453             g.setStroke( STROKE_0025 );
5454         }
5455         if ( getYdistance() < 0.001 ) {
5456             g.setStroke( STROKE_005 );
5457         }
5458         else if ( getYdistance() < 0.01 ) {
5459             g.setStroke( STROKE_01 );
5460         }
5461         else if ( getYdistance() < 0.5 ) {
5462             g.setStroke( STROKE_025 );
5463         }
5464         else if ( getYdistance() < 1 ) {
5465             g.setStroke( STROKE_05 );
5466         }
5467         else if ( getYdistance() < 2 ) {
5468             g.setStroke( STROKE_075 );
5469         }
5470         else if ( ( getYdistance() < 20 ) || !getConfiguration().isAllowThickStrokes() ) {
5471             g.setStroke( STROKE_1 );
5472         }
5473         else {
5474             g.setStroke( STROKE_2 );
5475         }
5476     }
5477
5478     final private void setUpUrtFactor() {
5479         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
5480                 : getVisibleRect().height;
5481         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5482             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
5483         }
5484         else {
5485             final int max_depth = _circ_max_depth;
5486             if ( max_depth > 0 ) {
5487                 setUrtFactor( d / ( 2 * max_depth ) );
5488             }
5489             else {
5490                 setUrtFactor( d / 2 );
5491             }
5492         }
5493         setUrtFactorOv( getUrtFactor() );
5494     }
5495
5496     final private void setUrtFactor( final float urt_factor ) {
5497         _urt_factor = urt_factor;
5498     }
5499
5500     final private void setUrtFactorOv( final float urt_factor_ov ) {
5501         _urt_factor_ov = urt_factor_ov;
5502     }
5503
5504     private void showExtDescNodeData( final PhylogenyNode node ) {
5505         final List<String> data = new ArrayList<String>();
5506         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
5507         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
5508             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
5509                 if ( !nodes.contains( n ) ) {
5510                     nodes.add( n );
5511                 }
5512             }
5513         }
5514         for( final PhylogenyNode n : nodes ) {
5515             switch ( getOptions().getExtDescNodeDataToReturn() ) {
5516                 case NODE_NAME:
5517                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5518                         data.add( n.getName() );
5519                     }
5520                     break;
5521                 case SEQUENCE_NAME:
5522                     if ( n.getNodeData().isHasSequence()
5523                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5524                         data.add( n.getNodeData().getSequence().getName() );
5525                     }
5526                     break;
5527                 case GENE_NAME:
5528                     if ( n.getNodeData().isHasSequence()
5529                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5530                         data.add( n.getNodeData().getSequence().getGeneName() );
5531                     }
5532                     break;
5533                 case SEQUENCE_SYMBOL:
5534                     if ( n.getNodeData().isHasSequence()
5535                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5536                         data.add( n.getNodeData().getSequence().getSymbol() );
5537                     }
5538                     break;
5539                 case SEQUENCE_MOL_SEQ:
5540                     if ( n.getNodeData().isHasSequence()
5541                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5542                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
5543                     }
5544                     break;
5545                 case SEQUENCE_MOL_SEQ_FASTA:
5546                     final StringBuilder sb = new StringBuilder();
5547                     if ( n.getNodeData().isHasSequence()
5548                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5549                         final StringBuilder ann = new StringBuilder();
5550                         if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5551                             ann.append( n.getName() );
5552                             ann.append( "|" );
5553                         }
5554                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5555                             ann.append( "SYM=" );
5556                             ann.append( n.getNodeData().getSequence().getSymbol() );
5557                             ann.append( "|" );
5558                         }
5559                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5560                             ann.append( "NAME=" );
5561                             ann.append( n.getNodeData().getSequence().getName() );
5562                             ann.append( "|" );
5563                         }
5564                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5565                             ann.append( "GN=" );
5566                             ann.append( n.getNodeData().getSequence().getGeneName() );
5567                             ann.append( "|" );
5568                         }
5569                         if ( n.getNodeData().getSequence().getAccession() != null ) {
5570                             ann.append( "ACC=" );
5571                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
5572                             ann.append( "|" );
5573                         }
5574                         if ( n.getNodeData().isHasTaxonomy() ) {
5575                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5576                                 ann.append( "TAXID=" );
5577                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5578                                 ann.append( "|" );
5579                             }
5580                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5581                                 ann.append( "SN=" );
5582                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
5583                                 ann.append( "|" );
5584                             }
5585                         }
5586                         String ann_str;
5587                         if ( ann.charAt( ann.length() - 1 ) == '|' ) {
5588                             ann_str = ann.substring( 0, ann.length() - 1 );
5589                         }
5590                         else {
5591                             ann_str = ann.toString();
5592                         }
5593                         sb.append( SequenceWriter.toFasta( ann_str, n.getNodeData().getSequence()
5594                                                            .getMolecularSequence(), 60 ) );
5595                         data.add( sb.toString() );
5596                     }
5597                     break;
5598                 case SEQUENCE_ACC:
5599                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
5600                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
5601                         data.add( n.getNodeData().getSequence().getAccession().toString() );
5602                     }
5603                     break;
5604                 case TAXONOMY_SCIENTIFIC_NAME:
5605                     if ( n.getNodeData().isHasTaxonomy()
5606                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5607                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
5608                     }
5609                     break;
5610                 case TAXONOMY_COMM0N_NAME:
5611                     if ( n.getNodeData().isHasTaxonomy()
5612                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) ) {
5613                         data.add( n.getNodeData().getTaxonomy().getCommonName() );
5614                     }
5615                     break;
5616                 case TAXONOMY_CODE:
5617                     if ( n.getNodeData().isHasTaxonomy()
5618                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5619                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5620                     }
5621                     break;
5622                 case DOMAINS_ALL:    
5623                 case DOMAINS_COLLAPSED_PER_PROTEIN:
5624                     if ( n.getNodeData().isHasSequence()
5625                             && n.getNodeData().getSequence().getDomainArchitecture() != null ) {
5626                         final DomainArchitecture da = n.getNodeData().getSequence().getDomainArchitecture();
5627                         final Set<String> s = new HashSet<String>();
5628                         for( int i = 0; i < da.getDomains().size(); ++i ) {
5629                             final ProteinDomain d = da.getDomain( i );
5630                             if ( d.getConfidence() <= Math.pow( 10, getDomainStructureEvalueThresholdExp() ) ) {
5631                                 final String name = d.getName();
5632                                 if ( !( s.contains( name ) ) ) {
5633                                     data.add( name );
5634                                     if ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.DOMAINS_COLLAPSED_PER_PROTEIN ) {
5635                                         s.add( name );
5636                                     }
5637                                 }
5638                             }
5639                         }
5640                     }
5641                     break;    
5642                 case SEQ_ANNOTATIONS:
5643                     if ( n.getNodeData().isHasSequence() ) {
5644                         if ( n.getNodeData().isHasSequence()
5645                                 && n.getNodeData().getSequence().getAnnotations() != null ) {
5646                             final SortedSet<Annotation> a = n.getNodeData().getSequence().getAnnotations();
5647                             for( int i = 0; i < a.size(); ++i ) {
5648                                 data.add(  n.getNodeData().getSequence().getAnnotation( i ).toString() );
5649                             }
5650                         }
5651                     }
5652                     break;  
5653                 case GO_TERM_IDS:
5654                     if ( n.getNodeData().isHasSequence() ) {
5655                         if ( n.getNodeData().isHasSequence()
5656                                 && n.getNodeData().getSequence().getAnnotations() != null ) {
5657                             final SortedSet<Annotation> a = n.getNodeData().getSequence().getAnnotations();
5658                             for( int i = 0; i < a.size(); ++i ) {
5659                                 final Annotation ann =  n.getNodeData().getSequence().getAnnotation( i );
5660                                 final String ref = ann.getRef();
5661                                 if ( ref.toUpperCase().startsWith( "GO:" ) ) {
5662                                     data.add( ref );
5663                                 }
5664                             }
5665                         }
5666                     }
5667                     break;      
5668                 case UNKNOWN:
5669                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
5670                     break;
5671                 default:
5672                     throw new IllegalArgumentException( "unknown data element: "
5673                             + getOptions().getExtDescNodeDataToReturn() );
5674             }
5675         } // for loop
5676         final StringBuilder sb = new StringBuilder();
5677         final int size = TreePanelUtil.makeSB( data, getOptions(), sb );
5678         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
5679                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
5680             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5681                 System.out.println( sb );
5682             }
5683             if ( sb.length() < 1 ) {
5684                 clearCurrentExternalNodesDataBuffer();
5685             }
5686             else {
5687                 setCurrentExternalNodesDataBuffer( sb );
5688             }
5689         }
5690         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5691             if ( sb.length() < 1 ) {
5692                 TreePanelUtil.showInformationMessage( this, "No Appropriate Data (" + obtainTitleForExtDescNodeData()
5693                                                       + ")", "Descendants of selected node do not contain selected data" );
5694                 clearCurrentExternalNodesDataBuffer();
5695             }
5696             else {
5697                 setCurrentExternalNodesDataBuffer( sb );
5698                 String title;
5699                 if ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() ) {
5700                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5701                             : obtainTitleForExtDescNodeData() )
5702                             + " for "
5703                             + data.size()
5704                             + " nodes, unique entries: "
5705                             + size;
5706                 }
5707                 else {
5708                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5709                             : obtainTitleForExtDescNodeData() )
5710                             + " for "
5711                             + data.size()
5712                             + "/"
5713                             + node.getNumberOfExternalNodes()
5714                             + " external descendats of node "
5715                             + node
5716                             + ", unique entries: " + size;
5717                 }
5718                 final String s = sb.toString().trim();
5719                 if ( getMainPanel().getMainFrame() == null ) {
5720                     // Must be "E" applet version.
5721                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5722                     ae.showTextFrame( s, title );
5723                 }
5724                 else {
5725                     getMainPanel().getMainFrame().showTextFrame( s, title );
5726                 }
5727             }
5728         }
5729     }
5730
5731     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5732         try {
5733             if ( ( node.getName().length() > 0 )
5734                     || ( node.getNodeData().isHasTaxonomy() && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData()
5735                                                                                                .getTaxonomy() ) )
5736                                                                                                || ( node.getNodeData().isHasSequence() && !TreePanelUtil.isSequenceEmpty( node.getNodeData()
5737                                                                                                                                                                           .getSequence() ) ) || ( node.getNodeData().isHasDate() )
5738                                                                                                                                                                           || ( node.getNodeData().isHasDistribution() ) || node.getBranchData().isHasConfidences() ) {
5739                 _popup_buffer.setLength( 0 );
5740                 short lines = 0;
5741                 if ( node.getName().length() > 0 ) {
5742                     lines++;
5743                     _popup_buffer.append( node.getName() );
5744                 }
5745                 if ( node.getNodeData().isHasTaxonomy()
5746                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5747                     lines++;
5748                     boolean enc_data = false;
5749                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5750                     if ( _popup_buffer.length() > 0 ) {
5751                         _popup_buffer.append( "\n" );
5752                     }
5753                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5754                         _popup_buffer.append( "[" );
5755                         _popup_buffer.append( tax.getTaxonomyCode() );
5756                         _popup_buffer.append( "]" );
5757                         enc_data = true;
5758                     }
5759                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5760                         if ( enc_data ) {
5761                             _popup_buffer.append( " " );
5762                         }
5763                         _popup_buffer.append( tax.getScientificName() );
5764                         enc_data = true;
5765                     }
5766                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5767                         if ( enc_data ) {
5768                             _popup_buffer.append( " (" );
5769                         }
5770                         else {
5771                             _popup_buffer.append( "(" );
5772                         }
5773                         _popup_buffer.append( tax.getCommonName() );
5774                         _popup_buffer.append( ")" );
5775                         enc_data = true;
5776                     }
5777                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5778                         if ( enc_data ) {
5779                             _popup_buffer.append( " (" );
5780                         }
5781                         else {
5782                             _popup_buffer.append( "(" );
5783                         }
5784                         _popup_buffer.append( tax.getAuthority() );
5785                         _popup_buffer.append( ")" );
5786                         enc_data = true;
5787                     }
5788                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5789                         if ( enc_data ) {
5790                             _popup_buffer.append( " [" );
5791                         }
5792                         else {
5793                             _popup_buffer.append( "[" );
5794                         }
5795                         _popup_buffer.append( tax.getRank() );
5796                         _popup_buffer.append( "]" );
5797                         enc_data = true;
5798                     }
5799                     if ( tax.getSynonyms().size() > 0 ) {
5800                         if ( enc_data ) {
5801                             _popup_buffer.append( " " );
5802                         }
5803                         _popup_buffer.append( "[" );
5804                         int counter = 1;
5805                         for( final String syn : tax.getSynonyms() ) {
5806                             if ( !ForesterUtil.isEmpty( syn ) ) {
5807                                 enc_data = true;
5808                                 _popup_buffer.append( syn );
5809                                 if ( counter < tax.getSynonyms().size() ) {
5810                                     _popup_buffer.append( ", " );
5811                                 }
5812                             }
5813                             counter++;
5814                         }
5815                         _popup_buffer.append( "]" );
5816                     }
5817                     if ( !enc_data ) {
5818                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5819                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5820                                 _popup_buffer.append( "[" );
5821                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5822                                 _popup_buffer.append( "] " );
5823                             }
5824                             _popup_buffer.append( tax.getIdentifier().getValue() );
5825                         }
5826                     }
5827                 }
5828                 if ( node.getNodeData().isHasSequence()
5829                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5830                     lines++;
5831                     boolean enc_data = false;
5832                     if ( _popup_buffer.length() > 0 ) {
5833                         _popup_buffer.append( "\n" );
5834                     }
5835                     final Sequence seq = node.getNodeData().getSequence();
5836                     if ( seq.getAccession() != null ) {
5837                         _popup_buffer.append( "[" );
5838                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5839                             _popup_buffer.append( seq.getAccession().getSource() );
5840                             _popup_buffer.append( ":" );
5841                         }
5842                         _popup_buffer.append( seq.getAccession().getValue() );
5843                         _popup_buffer.append( "]" );
5844                         enc_data = true;
5845                     }
5846                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5847                         if ( enc_data ) {
5848                             _popup_buffer.append( " [" );
5849                         }
5850                         else {
5851                             _popup_buffer.append( "[" );
5852                         }
5853                         _popup_buffer.append( seq.getSymbol() );
5854                         _popup_buffer.append( "]" );
5855                         enc_data = true;
5856                     }
5857                     if ( !ForesterUtil.isEmpty( seq.getGeneName() ) ) {
5858                         if ( enc_data ) {
5859                             _popup_buffer.append( " [" );
5860                         }
5861                         else {
5862                             _popup_buffer.append( "[" );
5863                         }
5864                         _popup_buffer.append( seq.getGeneName() );
5865                         _popup_buffer.append( "]" );
5866                         enc_data = true;
5867                     }
5868                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5869                         if ( enc_data ) {
5870                             _popup_buffer.append( " " );
5871                         }
5872                         _popup_buffer.append( seq.getName() );
5873                     }
5874                 }
5875                 if ( node.getNodeData().isHasDate() ) {
5876                     lines++;
5877                     if ( _popup_buffer.length() > 0 ) {
5878                         _popup_buffer.append( "\n" );
5879                     }
5880                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5881                 }
5882                 if ( node.getNodeData().isHasDistribution() ) {
5883                     lines++;
5884                     if ( _popup_buffer.length() > 0 ) {
5885                         _popup_buffer.append( "\n" );
5886                     }
5887                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5888                 }
5889                 if ( node.getBranchData().isHasConfidences() ) {
5890                     final List<Confidence> confs = node.getBranchData().getConfidences();
5891                     for( final Confidence confidence : confs ) {
5892                         lines++;
5893                         if ( _popup_buffer.length() > 0 ) {
5894                             _popup_buffer.append( "\n" );
5895                         }
5896                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5897                             _popup_buffer.append( "[" );
5898                             _popup_buffer.append( confidence.getType() );
5899                             _popup_buffer.append( "] " );
5900                         }
5901                         _popup_buffer
5902                         .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5903                                                                                   getOptions()
5904                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5905                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5906                             _popup_buffer.append( " (sd=" );
5907                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5908                                                                                                    .getStandardDeviation(), getOptions()
5909                                                                                                    .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5910                             _popup_buffer.append( ")" );
5911                         }
5912                     }
5913                 }
5914                 if ( node.getNodeData().isHasProperties() ) {
5915                     final PropertiesMap properties = node.getNodeData().getProperties();
5916                     for( final String ref : properties.getPropertyRefs() ) {
5917                         _popup_buffer.append( "\n" );
5918                         final Property p = properties.getProperty( ref );
5919                         _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5920                         _popup_buffer.append( "=" );
5921                         _popup_buffer.append( p.getValue() );
5922                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5923                             _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5924                         }
5925                     }
5926                 }
5927                 if ( _popup_buffer.length() > 0 ) {
5928                     if ( !getConfiguration().isUseNativeUI() ) {
5929                         _rollover_popup
5930                         .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5931                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5932                         if ( isInFoundNodes0( node ) && !isInFoundNodes1( node ) ) {
5933                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0() );
5934                         }
5935                         else if ( !isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5936                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor1() );
5937                         }
5938                         else if ( isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5939                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0and1() );
5940                         }
5941                         else {
5942                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5943                         }
5944                     }
5945                     else {
5946                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5947                     }
5948                     _rollover_popup.setText( _popup_buffer.toString() );
5949                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5950                                                                                   _rollover_popup,
5951                                                                                   e.getLocationOnScreen().x + 10,
5952                                                                                   e.getLocationOnScreen().y
5953                                                                                   - ( lines * 20 ) );
5954                     _node_desc_popup.show();
5955                 }
5956             }
5957         }
5958         catch ( final Exception ex ) {
5959             // Do nothing.
5960         }
5961     }
5962
5963     final private void showNodeEditFrame( final PhylogenyNode n ) {
5964         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5965             // pop up edit box for single node
5966             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5967             _node_frame_index++;
5968         }
5969         else {
5970             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5971         }
5972     }
5973
5974     final private void showNodeFrame( final PhylogenyNode n ) {
5975         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5976             // pop up edit box for single node
5977             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5978             _node_frame_index++;
5979         }
5980         else {
5981             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5982         }
5983     }
5984
5985     final private void switchDisplaygetPhylogenyGraphicsType() {
5986         switch ( getPhylogenyGraphicsType() ) {
5987             case RECTANGULAR:
5988                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5989                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5990                 break;
5991             case EURO_STYLE:
5992                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5993                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5994                 break;
5995             case ROUNDED:
5996                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5997                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5998                 break;
5999             case CURVED:
6000                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
6001                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
6002                 break;
6003             case TRIANGULAR:
6004                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
6005                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
6006                 break;
6007             case CONVEX:
6008                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
6009                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
6010                 break;
6011             case UNROOTED:
6012                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
6013                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
6014                 break;
6015             case CIRCULAR:
6016                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
6017                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
6018                 break;
6019             default:
6020                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
6021         }
6022         if ( getControlPanel().getDynamicallyHideData() != null ) {
6023             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
6024                 getControlPanel().getDynamicallyHideData().setEnabled( false );
6025             }
6026             else {
6027                 getControlPanel().getDynamicallyHideData().setEnabled( true );
6028             }
6029         }
6030         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
6031             getControlPanel().setDrawPhylogramEnabled( true );
6032         }
6033         else {
6034             getControlPanel().setDrawPhylogramEnabled( false );
6035         }
6036         if ( getMainPanel().getMainFrame() == null ) {
6037             // Must be "E" applet version.
6038             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
6039             .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
6040         }
6041         else {
6042             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
6043         }
6044     }
6045
6046     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
6047         if ( node.getNodeData().getNodeVisualData() == null ) {
6048             node.getNodeData().setNodeVisualData( new NodeVisualData() );
6049         }
6050         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
6051     }
6052
6053     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
6054         g.drawString( str, x, y );
6055     }
6056
6057     final private static boolean plusPressed( final int key_code ) {
6058         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
6059                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
6060     }
6061
6062     final private class NodeColorizationActionListener implements ActionListener {
6063
6064         List<PhylogenyNode> _additional_nodes = null;
6065         JColorChooser       _chooser          = null;
6066         PhylogenyNode       _node             = null;
6067
6068         NodeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
6069             _chooser = chooser;
6070             _node = node;
6071         }
6072
6073         NodeColorizationActionListener( final JColorChooser chooser,
6074                                         final PhylogenyNode node,
6075                                         final List<PhylogenyNode> additional_nodes ) {
6076             _chooser = chooser;
6077             _node = node;
6078             _additional_nodes = additional_nodes;
6079         }
6080
6081         @Override
6082         public void actionPerformed( final ActionEvent e ) {
6083             final Color c = _chooser.getColor();
6084             if ( c != null ) {
6085                 colorizeNodes( c, _node, _additional_nodes );
6086             }
6087         }
6088     }
6089
6090     final private class SubtreeColorizationActionListener implements ActionListener {
6091
6092         List<PhylogenyNode> _additional_nodes = null;
6093         JColorChooser       _chooser          = null;
6094         PhylogenyNode       _node             = null;
6095
6096         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
6097             _chooser = chooser;
6098             _node = node;
6099         }
6100
6101         SubtreeColorizationActionListener( final JColorChooser chooser,
6102                                            final PhylogenyNode node,
6103                                            final List<PhylogenyNode> additional_nodes ) {
6104             _chooser = chooser;
6105             _node = node;
6106             _additional_nodes = additional_nodes;
6107         }
6108
6109         @Override
6110         public void actionPerformed( final ActionEvent e ) {
6111             final Color c = _chooser.getColor();
6112             if ( c != null ) {
6113                 colorizeSubtree( c, _node, _additional_nodes );
6114             }
6115         }
6116     }
6117 }