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