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