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