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