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