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