in progress (special coloring is still true)
[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 PhylogenyNodeIterator it;
1641             //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
1642             //    paintNodeRectangular( g, it.next(), to_pdf, getControlPanel().isDynamicallyHideData()
1643             //            && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
1644             //}
1645             for( final PhylogenyNode element : _nodes_in_preorder ) {
1646                 paintNodeRectangular( g, element, to_pdf, getControlPanel().isDynamicallyHideData()
1647                         && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
1648             }
1649             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
1650                 if ( !( to_graphics_file || to_pdf ) ) {
1651                     paintScale( g,
1652                                 getVisibleRect().x,
1653                                 getVisibleRect().y + getVisibleRect().height,
1654                                 to_pdf,
1655                                 to_graphics_file );
1656                 }
1657                 else {
1658                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
1659                 }
1660             }
1661             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
1662                 paintPhylogenyLite( g );
1663             }
1664         }
1665         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
1666             if ( getControlPanel().getDynamicallyHideData() != null ) {
1667                 getControlPanel().setDynamicHidingIsOn( false );
1668             }
1669             final double angle = getStartingAngle();
1670             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
1671             _dynamic_hiding_factor = 0;
1672             if ( getControlPanel().isDynamicallyHideData() ) {
1673                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5 * getPhylogeny()
1674                         .getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
1675             }
1676             if ( getControlPanel().getDynamicallyHideData() != null ) {
1677                 if ( _dynamic_hiding_factor > 1 ) {
1678                     getControlPanel().setDynamicHidingIsOn( true );
1679                 }
1680                 else {
1681                     getControlPanel().setDynamicHidingIsOn( false );
1682                 }
1683             }
1684             paintUnrooted( _phylogeny.getRoot(),
1685                            angle,
1686                            ( float ) ( angle + ( 2 * Math.PI ) ),
1687                            radial_labels,
1688                            g,
1689                            to_pdf,
1690                            to_graphics_file );
1691             if ( getOptions().isShowScale() ) {
1692                 if ( !( to_graphics_file || to_pdf ) ) {
1693                     paintScale( g,
1694                                 getVisibleRect().x,
1695                                 getVisibleRect().y + getVisibleRect().height,
1696                                 to_pdf,
1697                                 to_graphics_file );
1698                 }
1699                 else {
1700                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
1701                 }
1702             }
1703             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
1704                 g.setColor( getTreeColorSet().getOvColor() );
1705                 paintUnrootedLite( _phylogeny.getRoot(),
1706                                    angle,
1707                                    angle + ( 2 * Math.PI ),
1708                                    g,
1709                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
1710                 paintOvRectangle( g );
1711             }
1712         }
1713         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
1714             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() ) / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
1715             final int d = radius + MOVE + getLongestExtNodeInfo();
1716             _dynamic_hiding_factor = 0;
1717             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
1718                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5 * getPhylogeny()
1719                         .getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
1720             }
1721             if ( getControlPanel().getDynamicallyHideData() != null ) {
1722                 if ( _dynamic_hiding_factor > 1 ) {
1723                     getControlPanel().setDynamicHidingIsOn( true );
1724                 }
1725                 else {
1726                     getControlPanel().setDynamicHidingIsOn( false );
1727                 }
1728             }
1729             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
1730             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
1731                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
1732                         : getOvMaxWidth() / 2 );
1733                 double x_scale = 1.0;
1734                 double y_scale = 1.0;
1735                 int x_pos = getVisibleRect().x + getOvXPosition();
1736                 int y_pos = getVisibleRect().y + getOvYPosition();
1737                 if ( getWidth() > getHeight() ) {
1738                     x_scale = ( double ) getHeight() / getWidth();
1739                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
1740                 }
1741                 else {
1742                     y_scale = ( double ) getWidth() / getHeight();
1743                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
1744                 }
1745                 _at = g.getTransform();
1746                 g.scale( x_scale, y_scale );
1747                 paintCircularLite( _phylogeny,
1748                                    getStartingAngle(),
1749                                    x_pos + radius_ov,
1750                                    y_pos + radius_ov,
1751                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo() / ( getVisibleRect().width / getOvRectangle()
1752                                            .getWidth() ) ) ),
1753                                    g );
1754                 g.setTransform( _at );
1755                 paintOvRectangle( g );
1756             }
1757         }
1758     }
1759
1760     final void recalculateMaxDistanceToRoot() {
1761         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
1762     }
1763
1764     /**
1765      * Remove all edit-node frames
1766      */
1767     final void removeAllEditNodeJFrames() {
1768         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
1769             if ( _node_frames[ i ] != null ) {
1770                 _node_frames[ i ].dispose();
1771                 _node_frames[ i ] = null;
1772             }
1773         }
1774         _node_frame_index = 0;
1775     }
1776
1777     /**
1778      * Remove a node-edit frame.
1779      */
1780     final void removeEditNodeFrame( final int i ) {
1781         _node_frame_index--;
1782         _node_frames[ i ] = null;
1783         if ( i < _node_frame_index ) {
1784             for( int j = 0; j < ( _node_frame_index - 1 ); j++ ) {
1785                 _node_frames[ j ] = _node_frames[ j + 1 ];
1786             }
1787             _node_frames[ _node_frame_index ] = null;
1788         }
1789     }
1790
1791     final void reRoot( final PhylogenyNode node ) {
1792         if ( !getPhylogeny().isRerootable() ) {
1793             JOptionPane.showMessageDialog( this,
1794                                            "This is not rerootable",
1795                                            "Not rerootable",
1796                                            JOptionPane.WARNING_MESSAGE );
1797             return;
1798         }
1799         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
1800             JOptionPane.showMessageDialog( this,
1801                                            "Cannot reroot in unrooted display type",
1802                                            "Attempt to reroot tree in unrooted display",
1803                                            JOptionPane.WARNING_MESSAGE );
1804             return;
1805         }
1806         getPhylogeny().reRoot( node );
1807         getPhylogeny().recalculateNumberOfExternalDescendants( true );
1808         resetNodeIdToDistToLeafMap();
1809         setNodeInPreorderToNull();
1810         resetPreferredSize();
1811         getMainPanel().adjustJScrollPane();
1812         setEdited( true );
1813         repaint();
1814         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
1815             getControlPanel().showWhole();
1816         }
1817     }
1818
1819     final void resetNodeIdToDistToLeafMap() {
1820         _nodeid_dist_to_leaf = new HashMap<Long, Short>();
1821     }
1822
1823     final void resetPreferredSize() {
1824         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
1825             return;
1826         }
1827         int x = 0;
1828         int y = 0;
1829         y = TreePanel.MOVE
1830                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
1831         if ( getControlPanel().isDrawPhylogram() ) {
1832             x = TreePanel.MOVE
1833                     + getLongestExtNodeInfo()
1834                     + ForesterUtil
1835                             .roundToInt( ( getXcorrectionFactor() * getPhylogeny().getHeight() ) + getXdistance() );
1836         }
1837         else {
1838             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
1839                 x = TreePanel.MOVE
1840                         + getLongestExtNodeInfo()
1841                         + ForesterUtil.roundToInt( getXdistance()
1842                                 * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
1843             }
1844             else {
1845                 x = TreePanel.MOVE
1846                         + getLongestExtNodeInfo()
1847                         + ForesterUtil.roundToInt( getXdistance()
1848                                 * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
1849             }
1850         }
1851         setPreferredSize( new Dimension( x, y ) );
1852     }
1853
1854     final void selectNode( final PhylogenyNode node ) {
1855         if ( ( getFoundNodes0() != null ) && getFoundNodes0().contains( node.getId() ) ) {
1856             getFoundNodes0().remove( node.getId() );
1857             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
1858             if ( getFoundNodes0().size() < 1 ) {
1859                 getControlPanel().searchReset0();
1860             }
1861         }
1862         else {
1863             getControlPanel().getSearchFoundCountsLabel0().setVisible( true );
1864             getControlPanel().getSearchResetButton0().setEnabled( true );
1865             getControlPanel().getSearchResetButton0().setVisible( true );
1866             if ( getFoundNodes0() == null ) {
1867                 setFoundNodes0( new HashSet<Long>() );
1868             }
1869             getFoundNodes0().add( node.getId() );
1870             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
1871         }
1872     }
1873
1874     final void setArrowCursor() {
1875         setCursor( ARROW_CURSOR );
1876         repaint();
1877     }
1878
1879     final void setControlPanel( final ControlPanel atv_control ) {
1880         _control_panel = atv_control;
1881     }
1882
1883     void setCurrentExternalNodesDataBuffer( final StringBuilder sb ) {
1884         increaseCurrentExternalNodesDataBufferChangeCounter();
1885         _current_external_nodes_data_buffer = sb;
1886     }
1887
1888     final void setFoundNodes0( final Set<Long> found_nodes ) {
1889         _found_nodes_0 = found_nodes;
1890     }
1891
1892     final void setFoundNodes1( final Set<Long> found_nodes ) {
1893         _found_nodes_1 = found_nodes;
1894     }
1895
1896     final void setInOvRect( final boolean in_ov_rect ) {
1897         _in_ov_rect = in_ov_rect;
1898     }
1899
1900     final void setLargeFonts() {
1901         getTreeFontSet().largeFonts();
1902     }
1903
1904     final void setLastMouseDragPointX( final float x ) {
1905         _last_drag_point_x = x;
1906     }
1907
1908     final void setLastMouseDragPointY( final float y ) {
1909         _last_drag_point_y = y;
1910     }
1911
1912     final void setMediumFonts() {
1913         getTreeFontSet().mediumFonts();
1914     }
1915
1916     final void setNodeInPreorderToNull() {
1917         _nodes_in_preorder = null;
1918     }
1919
1920     final void setOvOn( final boolean ov_on ) {
1921         _ov_on = ov_on;
1922     }
1923
1924     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
1925         _graphics_type = graphics_type;
1926         setTextAntialias();
1927     }
1928
1929     final void setSmallFonts() {
1930         getTreeFontSet().smallFonts();
1931     }
1932
1933     final void setStartingAngle( final double starting_angle ) {
1934         _urt_starting_angle = starting_angle;
1935     }
1936
1937     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
1938         _statistics_for_vector_data = statistics_for_expression_values;
1939     }
1940
1941     final void setSuperTinyFonts() {
1942         getTreeFontSet().superTinyFonts();
1943     }
1944
1945     final void setTextAntialias() {
1946         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
1947             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
1948                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
1949             }
1950             else {
1951                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
1952             }
1953         }
1954         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
1955             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
1956             // try {
1957             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
1958             // }
1959             // catch ( final Throwable e ) {
1960             //    _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
1961             //}
1962         }
1963         else {
1964             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
1965             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
1966         }
1967     }
1968
1969     final void setTinyFonts() {
1970         getTreeFontSet().tinyFonts();
1971     }
1972
1973     final void setTreeFile( final File treefile ) {
1974         _treefile = treefile;
1975     }
1976
1977     final void setXcorrectionFactor( final float f ) {
1978         _x_correction_factor = f;
1979     }
1980
1981     final void setXdistance( final float x ) {
1982         _x_distance = x;
1983     }
1984
1985     final void setYdistance( final float y ) {
1986         _y_distance = y;
1987     }
1988
1989     final void sortDescendants( final PhylogenyNode node ) {
1990         if ( !node.isExternal() ) {
1991             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
1992             if ( ( !getControlPanel().isShowTaxonomyScientificNames() && !getControlPanel().isShowTaxonomyCode() && !getControlPanel()
1993                     .isShowTaxonomyCommonNames() ) ) {
1994                 if ( ( getControlPanel().isShowSequenceAcc() || getControlPanel().isShowSeqNames() || getControlPanel()
1995                         .isShowSeqSymbols() ) ) {
1996                     pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
1997                 }
1998                 else if ( getControlPanel().isShowNodeNames() ) {
1999                     pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
2000                 }
2001             }
2002             PhylogenyMethods.sortNodeDescendents( node, pri );
2003             setNodeInPreorderToNull();
2004             _phylogeny.externalNodesHaveChanged();
2005             _phylogeny.clearHashIdToNodeMap();
2006             _phylogeny.recalculateNumberOfExternalDescendants( true );
2007             resetNodeIdToDistToLeafMap();
2008             setEdited( true );
2009         }
2010         repaint();
2011     }
2012
2013     final void subTree( final PhylogenyNode node ) {
2014         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2015             JOptionPane.showMessageDialog( this,
2016                                            "Cannot get a sub/super tree in unrooted display",
2017                                            "Attempt to get sub/super tree in unrooted display",
2018                                            JOptionPane.WARNING_MESSAGE );
2019             return;
2020         }
2021         if ( node.isExternal() ) {
2022             JOptionPane.showMessageDialog( this,
2023                                            "Cannot get a subtree of a external node",
2024                                            "Attempt to get subtree of external node",
2025                                            JOptionPane.WARNING_MESSAGE );
2026             return;
2027         }
2028         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
2029             JOptionPane.showMessageDialog( this,
2030                                            "Cannot get a subtree of the root node",
2031                                            "Attempt to get subtree of root node",
2032                                            JOptionPane.WARNING_MESSAGE );
2033             return;
2034         }
2035         setNodeInPreorderToNull();
2036         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
2037             _sub_phylogenies[ _subtree_index ] = _phylogeny;
2038             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
2039             ++_subtree_index;
2040             _phylogeny = TreePanelUtil.subTree( node, _phylogeny );
2041             updateSubSuperTreeButton();
2042         }
2043         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
2044             superTree();
2045         }
2046         _main_panel.getControlPanel().showWhole();
2047         repaint();
2048     }
2049
2050     final void superTree() {
2051         setNodeInPreorderToNull();
2052         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
2053         for( final PhylogenyNode n : temp_root.getDescendants() ) {
2054             n.setParent( temp_root );
2055         }
2056         _sub_phylogenies[ _subtree_index ] = null;
2057         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
2058         _phylogeny = _sub_phylogenies[ --_subtree_index ];
2059         updateSubSuperTreeButton();
2060     }
2061
2062     final void swap( final PhylogenyNode node ) {
2063         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
2064             return;
2065         }
2066         if ( node.getNumberOfDescendants() > 2 ) {
2067             JOptionPane.showMessageDialog( this,
2068                                            "Cannot swap descendants of nodes with more than 2 descendants",
2069                                            "Cannot swap descendants",
2070                                            JOptionPane.ERROR_MESSAGE );
2071             return;
2072         }
2073         if ( !node.isExternal() ) {
2074             node.swapChildren();
2075             setNodeInPreorderToNull();
2076             _phylogeny.externalNodesHaveChanged();
2077             _phylogeny.clearHashIdToNodeMap();
2078             _phylogeny.recalculateNumberOfExternalDescendants( true );
2079             resetNodeIdToDistToLeafMap();
2080             setEdited( true );
2081         }
2082         repaint();
2083     }
2084
2085     final void taxColor() {
2086         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
2087             return;
2088         }
2089         setWaitCursor();
2090         TreePanelUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
2091         _control_panel.setColorBranches( true );
2092         if ( _control_panel.getUseVisualStylesCb() != null ) {
2093             _control_panel.getUseVisualStylesCb().setSelected( true );
2094         }
2095         setEdited( true );
2096         setArrowCursor();
2097         repaint();
2098     }
2099
2100     final void updateOvSettings() {
2101         switch ( getOptions().getOvPlacement() ) {
2102             case LOWER_LEFT:
2103                 setOvXPosition( OV_BORDER );
2104                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
2105                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
2106                 break;
2107             case LOWER_RIGHT:
2108                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
2109                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
2110                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
2111                 break;
2112             case UPPER_RIGHT:
2113                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
2114                 setOvYPosition( OV_BORDER );
2115                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
2116                 break;
2117             default:
2118                 setOvXPosition( OV_BORDER );
2119                 setOvYPosition( OV_BORDER );
2120                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
2121                 break;
2122         }
2123     }
2124
2125     final void updateOvSizes() {
2126         if ( ( getWidth() > ( 1.05 * getVisibleRect().width ) ) || ( getHeight() > ( 1.05 * getVisibleRect().height ) ) ) {
2127             setOvOn( true );
2128             float l = getLongestExtNodeInfo();
2129             final float w_ratio = getOvMaxWidth() / getWidth();
2130             l *= w_ratio;
2131             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
2132             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
2133             float ov_xdist = 0;
2134             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
2135                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
2136             }
2137             else {
2138                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
2139             }
2140             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
2141             if ( ov_xdist < 0.0 ) {
2142                 ov_xdist = 0.0f;
2143             }
2144             if ( ydist < 0.0 ) {
2145                 ydist = 0.0f;
2146             }
2147             setOvXDistance( ov_xdist );
2148             final double height = _phylogeny.getHeight();
2149             if ( height > 0 ) {
2150                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
2151                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
2152             }
2153             else {
2154                 setOvXcorrectionFactor( 0 );
2155             }
2156         }
2157         else {
2158             setOvOn( false );
2159         }
2160     }
2161
2162     void updateSetOfCollapsedExternalNodes() {
2163         final Phylogeny phy = getPhylogeny();
2164         _collapsed_external_nodeid_set.clear();
2165         if ( phy != null ) {
2166             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2167                 final PhylogenyNode ext_node = it.next();
2168                 PhylogenyNode n = ext_node;
2169                 while ( !n.isRoot() ) {
2170                     if ( n.isCollapse() ) {
2171                         _collapsed_external_nodeid_set.add( ext_node.getId() );
2172                         ext_node.setCollapse( true );
2173                         continue E;
2174                     }
2175                     n = n.getParent();
2176                 }
2177             }
2178         }
2179     }
2180
2181     final void updateSubSuperTreeButton() {
2182         if ( _subtree_index < 1 ) {
2183             getControlPanel().deactivateButtonToReturnToSuperTree();
2184         }
2185         else {
2186             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
2187         }
2188     }
2189
2190     final void zoomInDomainStructure() {
2191         if ( _domain_structure_width < 2000 ) {
2192             _domain_structure_width *= 1.2;
2193         }
2194     }
2195
2196     final void zoomOutDomainStructure() {
2197         if ( _domain_structure_width > 20 ) {
2198             _domain_structure_width *= 0.8;
2199         }
2200     }
2201
2202     private void abbreviateScientificName( final String sn, final StringBuilder sb ) {
2203         final String[] a = sn.split( "\\s+" );
2204         sb.append( a[ 0 ].substring( 0, 1 ) );
2205         sb.append( a[ 1 ].substring( 0, 2 ) );
2206         if ( a.length > 2 ) {
2207             for( int i = 2; i < a.length; i++ ) {
2208                 sb.append( " " );
2209                 sb.append( a[ i ] );
2210             }
2211         }
2212     }
2213
2214     final private void addEmptyNode( final PhylogenyNode node ) {
2215         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2216             errorMessageNoCutCopyPasteInUnrootedDisplay();
2217             return;
2218         }
2219         final String label = createASimpleTextRepresentationOfANode( node );
2220         String msg = "";
2221         if ( ForesterUtil.isEmpty( label ) ) {
2222             msg = "How to add the new, empty node?";
2223         }
2224         else {
2225             msg = "How to add the new, empty node to node" + label + "?";
2226         }
2227         final Object[] options = { "As sibling", "As descendant", "Cancel" };
2228         final int r = JOptionPane.showOptionDialog( this,
2229                                                     msg,
2230                                                     "Addition of Empty New Node",
2231                                                     JOptionPane.CLOSED_OPTION,
2232                                                     JOptionPane.QUESTION_MESSAGE,
2233                                                     null,
2234                                                     options,
2235                                                     options[ 2 ] );
2236         boolean add_as_sibling = true;
2237         if ( r == 1 ) {
2238             add_as_sibling = false;
2239         }
2240         else if ( r != 0 ) {
2241             return;
2242         }
2243         final Phylogeny phy = new Phylogeny();
2244         phy.setRoot( new PhylogenyNode() );
2245         phy.setRooted( true );
2246         if ( add_as_sibling ) {
2247             if ( node.isRoot() ) {
2248                 JOptionPane.showMessageDialog( this,
2249                                                "Cannot add sibling to root",
2250                                                "Attempt to add sibling to root",
2251                                                JOptionPane.ERROR_MESSAGE );
2252                 return;
2253             }
2254             phy.addAsSibling( node );
2255         }
2256         else {
2257             phy.addAsChild( node );
2258         }
2259         setNodeInPreorderToNull();
2260         _phylogeny.externalNodesHaveChanged();
2261         _phylogeny.clearHashIdToNodeMap();
2262         _phylogeny.recalculateNumberOfExternalDescendants( true );
2263         resetNodeIdToDistToLeafMap();
2264         setEdited( true );
2265         repaint();
2266     }
2267
2268     final private void addToCurrentExternalNodes( final long i ) {
2269         if ( _current_external_nodes == null ) {
2270             _current_external_nodes = new HashSet<Long>();
2271         }
2272         _current_external_nodes.add( i );
2273     }
2274
2275     final private void assignGraphicsForBranchWithColorForParentBranch( final PhylogenyNode node,
2276                                                                         final boolean is_vertical,
2277                                                                         final Graphics g,
2278                                                                         final boolean to_pdf,
2279                                                                         final boolean to_graphics_file ) {
2280         final NodeClickAction action = _control_panel.getActionWhenNodeClicked();
2281         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2282             g.setColor( Color.BLACK );
2283         }
2284         else if ( ( ( action == NodeClickAction.COPY_SUBTREE ) || ( action == NodeClickAction.CUT_SUBTREE )
2285                 || ( action == NodeClickAction.DELETE_NODE_OR_SUBTREE ) || ( action == NodeClickAction.PASTE_SUBTREE ) || ( action == NodeClickAction.ADD_NEW_NODE ) )
2286                 && ( getCutOrCopiedTree() != null )
2287                 && ( getCopiedAndPastedNodes() != null )
2288                 && !to_pdf
2289                 && !to_graphics_file && getCopiedAndPastedNodes().contains( node.getId() ) ) {
2290             g.setColor( getTreeColorSet().getFoundColor0() );
2291         }
2292         else if ( getControlPanel().isUseVisualStyles() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2293             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
2294         }
2295         else if ( to_pdf ) {
2296             g.setColor( getTreeColorSet().getBranchColorForPdf() );
2297         }
2298         else {
2299             g.setColor( getTreeColorSet().getBranchColor() );
2300         }
2301     }
2302
2303     final private void blast( final PhylogenyNode node ) {
2304         if ( !isCanBlast( node ) ) {
2305             JOptionPane.showMessageDialog( this,
2306                                            "Insufficient information present",
2307                                            "Cannot Blast",
2308                                            JOptionPane.INFORMATION_MESSAGE );
2309             return;
2310         }
2311         else {
2312             final String query = Blast.obtainQueryForBlast( node );
2313             System.out.println( "query for BLAST is: " + query );
2314             char type = '?';
2315             if ( !ForesterUtil.isEmpty( query ) ) {
2316                 if ( node.getNodeData().isHasSequence() ) {
2317                     if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getType() ) ) {
2318                         if ( node.getNodeData().getSequence().getType().toLowerCase()
2319                                 .equals( PhyloXmlUtil.SEQ_TYPE_PROTEIN ) ) {
2320                             type = 'p';
2321                         }
2322                         else {
2323                             type = 'n';
2324                         }
2325                     }
2326                     else if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) {
2327                         if ( ForesterUtil.seqIsLikelyToBeAa( node.getNodeData().getSequence().getMolecularSequence() ) ) {
2328                             type = 'p';
2329                         }
2330                         else {
2331                             type = 'n';
2332                         }
2333                     }
2334                 }
2335                 if ( type == '?' ) {
2336                     if ( SequenceAccessionTools.isProteinDbQuery( query ) ) {
2337                         type = 'p';
2338                     }
2339                     else {
2340                         type = 'n';
2341                     }
2342                 }
2343                 JApplet applet = null;
2344                 if ( isApplet() ) {
2345                     applet = obtainApplet();
2346                 }
2347                 try {
2348                     Blast.openNcbiBlastWeb( query, type == 'n', applet, this );
2349                 }
2350                 catch ( final Exception e ) {
2351                     e.printStackTrace();
2352                 }
2353                 if ( Constants.ALLOW_DDBJ_BLAST ) {
2354                     try {
2355                         System.out.println( "trying: " + query );
2356                         final Blast s = new Blast();
2357                         s.ddbjBlast( query );
2358                     }
2359                     catch ( final Exception e ) {
2360                         e.printStackTrace();
2361                     }
2362                 }
2363             }
2364         }
2365     }
2366
2367     private final int calcDynamicHidingFactor() {
2368         return ( int ) ( 0.5 + ( getFontMetricsForLargeDefaultFont().getHeight() / ( 1.5 * getYdistance() ) ) );
2369     }
2370
2371     /**
2372      * Calculate the length of the distance between the given node and its
2373      * parent.
2374      * 
2375      * @param node
2376      * @param ext_node_x
2377      * @factor
2378      * @return the distance value
2379      */
2380     final private float calculateBranchLengthToParent( final PhylogenyNode node, final float factor ) {
2381         if ( getControlPanel().isDrawPhylogram() ) {
2382             if ( node.getDistanceToParent() < 0.0 ) {
2383                 return 0.0f;
2384             }
2385             return ( float ) ( getXcorrectionFactor() * node.getDistanceToParent() );
2386         }
2387         else {
2388             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
2389                 return getXdistance();
2390             }
2391             return getXdistance() * factor;
2392         }
2393     }
2394
2395     final private Color calculateColorForAnnotation( final SortedSet<Annotation> ann ) {
2396         Color c = getTreeColorSet().getAnnotationColor();
2397         if ( getControlPanel().isColorAccordingToAnnotation() && ( getControlPanel().getAnnotationColors() != null ) ) {
2398             final StringBuilder sb = new StringBuilder();
2399             for( final Annotation a : ann ) {
2400                 sb.append( !ForesterUtil.isEmpty( a.getRefValue() ) ? a.getRefValue() : a.getDesc() );
2401             }
2402             final String ann_str = sb.toString();
2403             if ( !ForesterUtil.isEmpty( ann_str ) ) {
2404                 c = getControlPanel().getAnnotationColors().get( ann_str );
2405                 if ( c == null ) {
2406                     c = AptxUtil.calculateColorFromString( ann_str, false );
2407                     getControlPanel().getAnnotationColors().put( ann_str, c );
2408                 }
2409                 if ( c == null ) {
2410                     c = getTreeColorSet().getAnnotationColor();
2411                 }
2412             }
2413         }
2414         return c;
2415     }
2416
2417     final private float calculateOvBranchLengthToParent( final PhylogenyNode node, final int factor ) {
2418         if ( getControlPanel().isDrawPhylogram() ) {
2419             if ( node.getDistanceToParent() < 0.0 ) {
2420                 return 0.0f;
2421             }
2422             return ( float ) ( getOvXcorrectionFactor() * node.getDistanceToParent() );
2423         }
2424         else {
2425             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
2426                 return getOvXDistance();
2427             }
2428             return getOvXDistance() * factor;
2429         }
2430     }
2431
2432     final private void cannotOpenBrowserWarningMessage( final String type_type ) {
2433         JOptionPane.showMessageDialog( this,
2434                                        "Cannot launch web browser for " + type_type + " data of this node",
2435                                        "Cannot launch web browser",
2436                                        JOptionPane.WARNING_MESSAGE );
2437     }
2438
2439     private void changeNodeFont( final PhylogenyNode node ) {
2440         final FontChooser fc = new FontChooser();
2441         Font f = null;
2442         if ( ( node.getNodeData().getNodeVisualData() != null ) && !node.getNodeData().getNodeVisualData().isEmpty() ) {
2443             f = node.getNodeData().getNodeVisualData().getFont();
2444         }
2445         if ( f != null ) {
2446             fc.setFont( f );
2447         }
2448         else {
2449             fc.setFont( getMainPanel().getTreeFontSet().getLargeFont() );
2450         }
2451         fc.showDialog( this, "Select Font" );
2452         if ( ( fc.getFont() != null ) && !ForesterUtil.isEmpty( fc.getFont().getFamily().trim() ) ) {
2453             List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
2454             if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
2455                 nodes = getFoundNodesAsListOfPhylogenyNodes();
2456             }
2457             nodes.add( node );
2458             for( final PhylogenyNode n : nodes ) {
2459                 if ( n.getNodeData().getNodeVisualData() == null ) {
2460                     n.getNodeData().setNodeVisualData( new NodeVisualData() );
2461                 }
2462                 final NodeVisualData vd = n.getNodeData().getNodeVisualData();
2463                 final Font ff = fc.getFont();
2464                 vd.setFontName( ff.getFamily().trim() );
2465                 int s = ff.getSize();
2466                 if ( s < 0 ) {
2467                     s = 0;
2468                 }
2469                 if ( s > Byte.MAX_VALUE ) {
2470                     s = Byte.MAX_VALUE;
2471                 }
2472                 vd.setFontSize( s );
2473                 vd.setFontStyle( ff.getStyle() );
2474             }
2475             if ( _control_panel.getUseVisualStylesCb() != null ) {
2476                 getControlPanel().getUseVisualStylesCb().setSelected( true );
2477             }
2478         }
2479         repaint();
2480     }
2481
2482     final private void colorizeNodes( final Color c,
2483                                       final PhylogenyNode node,
2484                                       final List<PhylogenyNode> additional_nodes ) {
2485         _control_panel.setColorBranches( true );
2486         if ( _control_panel.getUseVisualStylesCb() != null ) {
2487             _control_panel.getUseVisualStylesCb().setSelected( true );
2488         }
2489         if ( node != null ) {
2490             colorizeNodesHelper( c, node );
2491         }
2492         if ( additional_nodes != null ) {
2493             for( final PhylogenyNode n : additional_nodes ) {
2494                 colorizeNodesHelper( c, n );
2495             }
2496         }
2497         repaint();
2498     }
2499
2500     final private void colorizeSubtree( final Color c,
2501                                         final PhylogenyNode node,
2502                                         final List<PhylogenyNode> additional_nodes ) {
2503         _control_panel.setColorBranches( true );
2504         if ( _control_panel.getUseVisualStylesCb() != null ) {
2505             _control_panel.getUseVisualStylesCb().setSelected( true );
2506         }
2507         if ( node != null ) {
2508             for( final PreorderTreeIterator it = new PreorderTreeIterator( node ); it.hasNext(); ) {
2509                 it.next().getBranchData().setBranchColor( new BranchColor( c ) );
2510             }
2511         }
2512         if ( additional_nodes != null ) {
2513             for( final PhylogenyNode an : additional_nodes ) {
2514                 for( final PreorderTreeIterator it = new PreorderTreeIterator( an ); it.hasNext(); ) {
2515                     it.next().getBranchData().setBranchColor( new BranchColor( c ) );
2516                 }
2517             }
2518         }
2519         repaint();
2520     }
2521
2522     private void colorNodeFont( final PhylogenyNode node ) {
2523         _color_chooser.setPreviewPanel( new JPanel() );
2524         NodeColorizationActionListener al;
2525         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
2526             final List<PhylogenyNode> additional_nodes = getFoundNodesAsListOfPhylogenyNodes();
2527             al = new NodeColorizationActionListener( _color_chooser, node, additional_nodes );
2528         }
2529         else {
2530             al = new NodeColorizationActionListener( _color_chooser, node );
2531         }
2532         final JDialog dialog = JColorChooser.createDialog( this, "Node colorization", true, _color_chooser, al, null );
2533         dialog.setVisible( true );
2534     }
2535
2536     final private void colorSubtree( final PhylogenyNode node ) {
2537         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2538             JOptionPane.showMessageDialog( this,
2539                                            "Cannot colorize subtree in unrooted display type",
2540                                            "Attempt to colorize subtree in unrooted display",
2541                                            JOptionPane.WARNING_MESSAGE );
2542             return;
2543         }
2544         _color_chooser.setPreviewPanel( new JPanel() );
2545         SubtreeColorizationActionListener al;
2546         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
2547             final List<PhylogenyNode> additional_nodes = getFoundNodesAsListOfPhylogenyNodes();
2548             al = new SubtreeColorizationActionListener( _color_chooser, node, additional_nodes );
2549         }
2550         else {
2551             al = new SubtreeColorizationActionListener( _color_chooser, node );
2552         }
2553         final JDialog dialog = JColorChooser
2554                 .createDialog( this, "Subtree colorization", true, _color_chooser, al, null );
2555         dialog.setVisible( true );
2556     }
2557
2558     final private void copySubtree( final PhylogenyNode node ) {
2559         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2560             errorMessageNoCutCopyPasteInUnrootedDisplay();
2561             return;
2562         }
2563         setNodeInPreorderToNull();
2564         setCutOrCopiedTree( _phylogeny.copy( node ) );
2565         final List<PhylogenyNode> nodes = PhylogenyMethods.getAllDescendants( node );
2566         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
2567         for( final PhylogenyNode n : nodes ) {
2568             node_ids.add( n.getId() );
2569         }
2570         node_ids.add( node.getId() );
2571         setCopiedAndPastedNodes( node_ids );
2572         repaint();
2573     }
2574
2575     final private String createASimpleTextRepresentationOfANode( final PhylogenyNode node ) {
2576         final String tax = PhylogenyMethods.getSpecies( node );
2577         String label = node.getName();
2578         if ( !ForesterUtil.isEmpty( label ) && !ForesterUtil.isEmpty( tax ) ) {
2579             label = label + " " + tax;
2580         }
2581         else if ( !ForesterUtil.isEmpty( tax ) ) {
2582             label = tax;
2583         }
2584         else {
2585             label = "";
2586         }
2587         if ( !ForesterUtil.isEmpty( label ) ) {
2588             label = " [" + label + "]";
2589         }
2590         return label;
2591     }
2592
2593     final private void cutSubtree( final PhylogenyNode node ) {
2594         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2595             errorMessageNoCutCopyPasteInUnrootedDisplay();
2596             return;
2597         }
2598         if ( node.isRoot() ) {
2599             JOptionPane.showMessageDialog( this,
2600                                            "Cannot cut entire tree as subtree",
2601                                            "Attempt to cut entire tree",
2602                                            JOptionPane.ERROR_MESSAGE );
2603             return;
2604         }
2605         final String label = createASimpleTextRepresentationOfANode( node );
2606         final int r = JOptionPane.showConfirmDialog( null,
2607                                                      "Cut subtree" + label + "?",
2608                                                      "Confirm Cutting of Subtree",
2609                                                      JOptionPane.YES_NO_OPTION );
2610         if ( r != JOptionPane.OK_OPTION ) {
2611             return;
2612         }
2613         setNodeInPreorderToNull();
2614         setCopiedAndPastedNodes( null );
2615         setCutOrCopiedTree( _phylogeny.copy( node ) );
2616         _phylogeny.deleteSubtree( node, true );
2617         _phylogeny.clearHashIdToNodeMap();
2618         _phylogeny.recalculateNumberOfExternalDescendants( true );
2619         resetNodeIdToDistToLeafMap();
2620         setEdited( true );
2621         repaint();
2622     }
2623
2624     final private void cycleColors() {
2625         getMainPanel().getTreeColorSet().cycleColorScheme();
2626         for( final TreePanel tree_panel : getMainPanel().getTreePanels() ) {
2627             tree_panel.setBackground( getMainPanel().getTreeColorSet().getBackgroundColor() );
2628         }
2629     }
2630
2631     final private void decreaseOvSize() {
2632         if ( ( getOvMaxWidth() > 20 ) && ( getOvMaxHeight() > 20 ) ) {
2633             setOvMaxWidth( getOvMaxWidth() - 5 );
2634             setOvMaxHeight( getOvMaxHeight() - 5 );
2635             updateOvSettings();
2636             getControlPanel().displayedPhylogenyMightHaveChanged( false );
2637         }
2638     }
2639
2640     final private void deleteNodeOrSubtree( final PhylogenyNode node ) {
2641         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2642             errorMessageNoCutCopyPasteInUnrootedDisplay();
2643             return;
2644         }
2645         if ( node.isRoot() && ( node.getNumberOfDescendants() != 1 ) ) {
2646             JOptionPane.showMessageDialog( this,
2647                                            "Cannot delete entire tree",
2648                                            "Attempt to delete entire tree",
2649                                            JOptionPane.ERROR_MESSAGE );
2650             return;
2651         }
2652         final String label = createASimpleTextRepresentationOfANode( node );
2653         final Object[] options = { "Node only", "Entire subtree", "Cancel" };
2654         final int r = JOptionPane.showOptionDialog( this,
2655                                                     "Delete" + label + "?",
2656                                                     "Delete Node/Subtree",
2657                                                     JOptionPane.CLOSED_OPTION,
2658                                                     JOptionPane.QUESTION_MESSAGE,
2659                                                     null,
2660                                                     options,
2661                                                     options[ 2 ] );
2662         setNodeInPreorderToNull();
2663         boolean node_only = true;
2664         if ( r == 1 ) {
2665             node_only = false;
2666         }
2667         else if ( r != 0 ) {
2668             return;
2669         }
2670         if ( node_only ) {
2671             PhylogenyMethods.removeNode( node, _phylogeny );
2672         }
2673         else {
2674             _phylogeny.deleteSubtree( node, true );
2675         }
2676         _phylogeny.externalNodesHaveChanged();
2677         _phylogeny.clearHashIdToNodeMap();
2678         _phylogeny.recalculateNumberOfExternalDescendants( true );
2679         resetNodeIdToDistToLeafMap();
2680         setEdited( true );
2681         repaint();
2682     }
2683
2684     final private void displayNodePopupMenu( final PhylogenyNode node, final int x, final int y ) {
2685         makePopupMenus( node );
2686         _node_popup_menu.putClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY, node );
2687         _node_popup_menu.show( this, x, y );
2688     }
2689
2690     final private void drawArc( final double x,
2691                                 final double y,
2692                                 final double width,
2693                                 final double heigth,
2694                                 final double start_angle,
2695                                 final double arc_angle,
2696                                 final Graphics2D g ) {
2697         _arc.setArc( x, y, width, heigth, _180_OVER_PI * start_angle, _180_OVER_PI * arc_angle, Arc2D.OPEN );
2698         g.draw( _arc );
2699     }
2700
2701     final private void drawLine( final double x1, final double y1, final double x2, final double y2, final Graphics2D g ) {
2702         if ( ( x1 == x2 ) && ( y1 == y2 ) ) {
2703             return;
2704         }
2705         _line.setLine( x1, y1, x2, y2 );
2706         g.draw( _line );
2707     }
2708
2709     final private void drawOval( final double x,
2710                                  final double y,
2711                                  final double width,
2712                                  final double heigth,
2713                                  final Graphics2D g ) {
2714         _ellipse.setFrame( x, y, width, heigth );
2715         g.draw( _ellipse );
2716     }
2717
2718     final private void drawOvalFilled( final double x,
2719                                        final double y,
2720                                        final double width,
2721                                        final double heigth,
2722                                        final Graphics2D g ) {
2723         _ellipse.setFrame( x, y, width, heigth );
2724         g.fill( _ellipse );
2725     }
2726
2727     final private void drawOvalGradient( final float x,
2728                                          final float y,
2729                                          final float width,
2730                                          final float heigth,
2731                                          final Graphics2D g,
2732                                          final Color color_1,
2733                                          final Color color_2,
2734                                          final Color color_border ) {
2735         _ellipse.setFrame( x, y, width, heigth );
2736         g.setPaint( new GradientPaint( x, y, color_1, ( x + width ), ( y + heigth ), color_2, false ) );
2737         g.fill( _ellipse );
2738         if ( color_border != null ) {
2739             g.setPaint( color_border );
2740             g.draw( _ellipse );
2741         }
2742     }
2743
2744     final private void drawRect( final float x, final float y, final float width, final float heigth, final Graphics2D g ) {
2745         _rectangle.setFrame( x, y, width, heigth );
2746         g.draw( _rectangle );
2747     }
2748
2749     final private void drawRectFilled( final double x,
2750                                        final double y,
2751                                        final double width,
2752                                        final double heigth,
2753                                        final Graphics2D g ) {
2754         _rectangle.setFrame( x, y, width, heigth );
2755         g.fill( _rectangle );
2756     }
2757
2758     final private void drawRectGradient( final float x,
2759                                          final float y,
2760                                          final float width,
2761                                          final float heigth,
2762                                          final Graphics2D g,
2763                                          final Color color_1,
2764                                          final Color color_2,
2765                                          final Color color_border ) {
2766         _rectangle.setFrame( x, y, width, heigth );
2767         g.setPaint( new GradientPaint( x, y, color_1, ( x + width ), ( y + heigth ), color_2, false ) );
2768         g.fill( _rectangle );
2769         if ( color_border != null ) {
2770             g.setPaint( color_border );
2771             g.draw( _rectangle );
2772         }
2773     }
2774
2775     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
2776         final List<Uri> us = new ArrayList<Uri>();
2777         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
2778             for( final Uri uri : t.getUris() ) {
2779                 us.add( uri );
2780             }
2781         }
2782         double offset = 0;
2783         for( final Uri uri : us ) {
2784             if ( uri != null ) {
2785                 final String uri_str = uri.getValue().toString().toLowerCase();
2786                 if ( getImageMap().containsKey( uri_str ) ) {
2787                     final BufferedImage bi = getImageMap().get( uri_str );
2788                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
2789                         double scaling_factor = 1;
2790                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
2791                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
2792                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
2793                         }
2794                         // y = y - ( 0.9 * getYdistance() );
2795                         final double hs = bi.getHeight() * scaling_factor;
2796                         double ws = ( bi.getWidth() * scaling_factor ) + offset;
2797                         final double my_y = y - ( 0.5 * hs );
2798                         final int x_w = ( int ) ( x + ws + 0.5 );
2799                         final int y_h = ( int ) ( my_y + hs + 0.5 );
2800                         if ( ( ( x_w - x ) > 7 ) && ( ( y_h - my_y ) > 7 ) ) {
2801                             g.drawImage( bi,
2802                                          ( int ) ( x + 0.5 + offset ),
2803                                          ( int ) ( my_y + 0.5 ),
2804                                          x_w,
2805                                          y_h,
2806                                          0,
2807                                          0,
2808                                          bi.getWidth(),
2809                                          bi.getHeight(),
2810                                          null );
2811                             ws += 8;
2812                         }
2813                         else {
2814                             ws = 0.0;
2815                         }
2816                         offset = ws;
2817                     }
2818                 }
2819             }
2820         }
2821         return offset;
2822     }
2823
2824     final private void errorMessageNoCutCopyPasteInUnrootedDisplay() {
2825         JOptionPane.showMessageDialog( this,
2826                                        "Cannot cut, copy, paste, add, or delete subtrees/nodes in unrooted display",
2827                                        "Attempt to cut/copy/paste/add/delete in unrooted display",
2828                                        JOptionPane.ERROR_MESSAGE );
2829     }
2830
2831     private final Color getColorForFoundNode( final PhylogenyNode n ) {
2832         if ( isInCurrentExternalNodes( n ) ) {
2833             return getTreeColorSet().getFoundColor0();
2834         }
2835         else if ( isInFoundNodes0( n ) && !isInFoundNodes1( n ) ) {
2836             return getTreeColorSet().getFoundColor0();
2837         }
2838         else if ( !isInFoundNodes0( n ) && isInFoundNodes1( n ) ) {
2839             return getTreeColorSet().getFoundColor1();
2840         }
2841         else {
2842             return getTreeColorSet().getFoundColor0and1();
2843         }
2844     }
2845
2846     final private Set<Long> getCopiedAndPastedNodes() {
2847         return getMainPanel().getCopiedAndPastedNodes();
2848     }
2849
2850     final private Set<Long> getCurrentExternalNodes() {
2851         return _current_external_nodes;
2852     }
2853
2854     final private Phylogeny getCutOrCopiedTree() {
2855         return getMainPanel().getCutOrCopiedTree();
2856     }
2857
2858     private FontMetrics getFontMetricsForLargeDefaultFont() {
2859         return getTreeFontSet().getFontMetricsLarge();
2860     }
2861
2862     List<PhylogenyNode> getFoundNodesAsListOfPhylogenyNodes() {
2863         final List<PhylogenyNode> additional_nodes = new ArrayList<PhylogenyNode>();
2864         if ( getFoundNodes0() != null ) {
2865             for( final Long id : getFoundNodes0() ) {
2866                 final PhylogenyNode n = _phylogeny.getNode( id );
2867                 if ( n != null ) {
2868                     additional_nodes.add( n );
2869                 }
2870             }
2871         }
2872         if ( getFoundNodes1() != null ) {
2873             for( final Long id : getFoundNodes1() ) {
2874                 if ( ( getFoundNodes0() == null ) || !getFoundNodes0().contains( id ) ) {
2875                     final PhylogenyNode n = _phylogeny.getNode( id );
2876                     if ( n != null ) {
2877                         additional_nodes.add( n );
2878                     }
2879                 }
2880             }
2881         }
2882         return additional_nodes;
2883     }
2884
2885     final private float getLastDragPointX() {
2886         return _last_drag_point_x;
2887     }
2888
2889     final private float getLastDragPointY() {
2890         return _last_drag_point_y;
2891     }
2892
2893     final private short getMaxBranchesToLeaf( final PhylogenyNode node ) {
2894         if ( !_nodeid_dist_to_leaf.containsKey( node.getId() ) ) {
2895             final short m = PhylogenyMethods.calculateMaxBranchesToLeaf( node );
2896             _nodeid_dist_to_leaf.put( node.getId(), m );
2897             return m;
2898         }
2899         else {
2900             return _nodeid_dist_to_leaf.get( node.getId() );
2901         }
2902     }
2903
2904     final private double getMaxDistanceToRoot() {
2905         if ( _max_distance_to_root < 0 ) {
2906             recalculateMaxDistanceToRoot();
2907         }
2908         return _max_distance_to_root;
2909     }
2910
2911     final private float getOvMaxHeight() {
2912         return _ov_max_height;
2913     }
2914
2915     final private float getOvMaxWidth() {
2916         return _ov_max_width;
2917     }
2918
2919     final private float getOvXcorrectionFactor() {
2920         return _ov_x_correction_factor;
2921     }
2922
2923     final private float getOvXDistance() {
2924         return _ov_x_distance;
2925     }
2926
2927     final private int getOvXPosition() {
2928         return _ov_x_position;
2929     }
2930
2931     final private float getOvYDistance() {
2932         return _ov_y_distance;
2933     }
2934
2935     final private int getOvYPosition() {
2936         return _ov_y_position;
2937     }
2938
2939     final private int getOvYStart() {
2940         return _ov_y_start;
2941     }
2942
2943     final private List<Accession> getPdbAccs( final PhylogenyNode node ) {
2944         final List<Accession> pdb_ids = new ArrayList<Accession>();
2945         if ( node.getNodeData().isHasSequence() ) {
2946             final Sequence seq = node.getNodeData().getSequence();
2947             if ( !ForesterUtil.isEmpty( seq.getCrossReferences() ) ) {
2948                 final SortedSet<Accession> cross_refs = seq.getCrossReferences();
2949                 for( final Accession acc : cross_refs ) {
2950                     if ( acc.getSource().equalsIgnoreCase( "pdb" ) ) {
2951                         pdb_ids.add( acc );
2952                     }
2953                 }
2954             }
2955         }
2956         return pdb_ids;
2957     }
2958
2959     final private double getScaleDistance() {
2960         return _scale_distance;
2961     }
2962
2963     final private String getScaleLabel() {
2964         return _scale_label;
2965     }
2966
2967     final private TreeFontSet getTreeFontSet() {
2968         return getMainPanel().getTreeFontSet();
2969     }
2970
2971     final private float getUrtFactor() {
2972         return _urt_factor;
2973     }
2974
2975     final private float getUrtFactorOv() {
2976         return _urt_factor_ov;
2977     }
2978
2979     final private void handleClickToAction( final NodeClickAction action, final PhylogenyNode node ) {
2980         switch ( action ) {
2981             case SHOW_DATA:
2982                 showNodeFrame( node );
2983                 break;
2984             case COLLAPSE:
2985                 collapse( node );
2986                 break;
2987             case REROOT:
2988                 reRoot( node );
2989                 break;
2990             case SUBTREE:
2991                 subTree( node );
2992                 break;
2993             case SWAP:
2994                 swap( node );
2995                 break;
2996             case COLOR_SUBTREE:
2997                 colorSubtree( node );
2998                 break;
2999             case COLOR_NODE_FONT:
3000                 colorNodeFont( node );
3001                 break;
3002             case CHANGE_NODE_FONT:
3003                 changeNodeFont( node );
3004                 break;
3005             case OPEN_SEQ_WEB:
3006                 openSeqWeb( node );
3007                 break;
3008             case BLAST:
3009                 blast( node );
3010                 break;
3011             case OPEN_TAX_WEB:
3012                 openTaxWeb( node );
3013                 break;
3014             case OPEN_PDB_WEB:
3015                 openPdbWeb( node );
3016                 break;
3017             case CUT_SUBTREE:
3018                 cutSubtree( node );
3019                 break;
3020             case COPY_SUBTREE:
3021                 copySubtree( node );
3022                 break;
3023             case PASTE_SUBTREE:
3024                 pasteSubtree( node );
3025                 break;
3026             case DELETE_NODE_OR_SUBTREE:
3027                 deleteNodeOrSubtree( node );
3028                 break;
3029             case ADD_NEW_NODE:
3030                 addEmptyNode( node );
3031                 break;
3032             case EDIT_NODE_DATA:
3033                 showNodeEditFrame( node );
3034                 break;
3035             case SELECT_NODES:
3036                 selectNode( node );
3037                 break;
3038             case SORT_DESCENDENTS:
3039                 sortDescendants( node );
3040                 break;
3041             case GET_EXT_DESC_DATA:
3042                 showExtDescNodeData( node );
3043                 break;
3044             default:
3045                 throw new IllegalArgumentException( "unknown action: " + action );
3046         }
3047     }
3048
3049     final private void increaseCurrentExternalNodesDataBufferChangeCounter() {
3050         _current_external_nodes_data_buffer_change_counter++;
3051     }
3052
3053     final private void increaseOvSize() {
3054         if ( ( getOvMaxWidth() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth() / 2 ) )
3055                 && ( getOvMaxHeight() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
3056                         .getHeight() / 2 ) ) ) {
3057             setOvMaxWidth( getOvMaxWidth() + 5 );
3058             setOvMaxHeight( getOvMaxHeight() + 5 );
3059             updateOvSettings();
3060             getControlPanel().displayedPhylogenyMightHaveChanged( false );
3061         }
3062     }
3063
3064     final private void init() {
3065         _color_chooser = new JColorChooser();
3066         _rollover_popup = new JTextArea();
3067         _rollover_popup.setFont( POPUP_FONT );
3068         resetNodeIdToDistToLeafMap();
3069         setTextAntialias();
3070         setTreeFile( null );
3071         setEdited( false );
3072         initializeOvSettings();
3073         setStartingAngle( ( TWO_PI * 3 ) / 4 );
3074         final ImageLoader il = new ImageLoader( this );
3075         new Thread( il ).start();
3076     }
3077
3078     final private void initializeOvSettings() {
3079         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
3080         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
3081     }
3082
3083     final private boolean inOvVirtualRectangle( final int x, final int y ) {
3084         return ( ( x >= ( getOvVirtualRectangle().x - 1 ) )
3085                 && ( x <= ( getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 ) )
3086                 && ( y >= ( getOvVirtualRectangle().y - 1 ) ) && ( y <= ( getOvVirtualRectangle().y
3087                 + getOvVirtualRectangle().height + 1 ) ) );
3088     }
3089
3090     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
3091         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
3092     }
3093
3094     final private boolean isCanBlast( final PhylogenyNode node ) {
3095         if ( !node.getNodeData().isHasSequence() && ForesterUtil.isEmpty( node.getName() ) ) {
3096             return false;
3097         }
3098         return Blast.isContainsQueryForBlast( node );
3099     }
3100
3101     final private String isCanOpenSeqWeb( final PhylogenyNode node ) {
3102         final Accession a = SequenceAccessionTools.obtainAccessorFromDataFields( node );
3103         if ( a != null ) {
3104             return a.getValue();
3105         }
3106         return null;
3107     }
3108
3109     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
3110         if ( node.getNodeData().isHasTaxonomy()
3111                 && ( ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
3112                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
3113                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) || ( ( node
3114                         .getNodeData().getTaxonomy().getIdentifier() != null ) && !ForesterUtil.isEmpty( node
3115                         .getNodeData().getTaxonomy().getIdentifier().getValue() ) ) ) ) {
3116             return true;
3117         }
3118         else {
3119             return false;
3120         }
3121     }
3122
3123     final private boolean isInCurrentExternalNodes( final PhylogenyNode node ) {
3124         return ( ( getCurrentExternalNodes() != null ) && getCurrentExternalNodes().contains( node.getId() ) );
3125     }
3126
3127     private boolean isInFoundNodes( final PhylogenyNode n ) {
3128         return isInFoundNodes0( n ) || isInFoundNodes1( n );
3129     }
3130
3131     final private boolean isInFoundNodes0( final PhylogenyNode node ) {
3132         return ( ( getFoundNodes0() != null ) && getFoundNodes0().contains( node.getId() ) );
3133     }
3134
3135     final private boolean isInFoundNodes1( final PhylogenyNode node ) {
3136         return ( ( getFoundNodes1() != null ) && getFoundNodes1().contains( node.getId() ) );
3137     }
3138
3139     final private boolean isInOv() {
3140         return _in_ov;
3141     }
3142
3143     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
3144         int y_dist = 40;
3145         if ( getControlPanel().isShowTaxonomyImages() ) {
3146             y_dist = 40 + ( int ) getYdistance();
3147         }
3148         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - y_dist ) )
3149                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + y_dist ) ) || ( ( node.getParent() != null ) && ( node
3150                 .getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
3151     }
3152
3153     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
3154         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - 20 ) )
3155                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + 20 ) )
3156                 || ( node.getXcoord() < ( getVisibleRect().getMinX() - 20 ) ) || ( node.getXcoord() > ( getVisibleRect()
3157                 .getMaxX() + 20 ) ) );
3158     }
3159
3160     final private boolean isNonLinedUpCladogram() {
3161         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
3162     }
3163
3164     final private boolean isUniformBranchLengthsForCladogram() {
3165         return getOptions().getCladogramType() == CLADOGRAM_TYPE.TOTAL_NODE_SUM_DEP;
3166     }
3167
3168     final private void keyPressedCalls( final KeyEvent e ) {
3169         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
3170             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
3171                 if ( !isInOvRect() ) {
3172                     setInOvRect( true );
3173                 }
3174             }
3175             else if ( isInOvRect() ) {
3176                 setInOvRect( false );
3177             }
3178         }
3179         if ( e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK ) {
3180             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
3181                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
3182                 getMainPanel().getTreeFontSet().mediumFonts();
3183                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
3184             }
3185             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
3186                 getMainPanel().getTreeFontSet().decreaseFontSize( 1, false );
3187                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
3188             }
3189             else if ( plusPressed( e.getKeyCode() ) ) {
3190                 getMainPanel().getTreeFontSet().increaseFontSize();
3191                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
3192             }
3193         }
3194         else {
3195             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
3196                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
3197                 getControlPanel().showWhole();
3198             }
3199             else if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
3200                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
3201                 if ( e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK ) {
3202                     if ( e.getKeyCode() == KeyEvent.VK_UP ) {
3203                         getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
3204                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3205                     }
3206                     else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
3207                         getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
3208                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3209                     }
3210                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
3211                         getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
3212                                                                    Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
3213                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3214                     }
3215                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
3216                         getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
3217                                                                   Constants.WHEEL_ZOOM_IN_FACTOR );
3218                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3219                     }
3220                 }
3221                 else {
3222                     final int d = 80;
3223                     int dx = 0;
3224                     int dy = -d;
3225                     if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
3226                         dy = d;
3227                     }
3228                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
3229                         dx = -d;
3230                         dy = 0;
3231                     }
3232                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
3233                         dx = d;
3234                         dy = 0;
3235                     }
3236                     final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
3237                     scroll_position.x = scroll_position.x + dx;
3238                     scroll_position.y = scroll_position.y + dy;
3239                     if ( scroll_position.x <= 0 ) {
3240                         scroll_position.x = 0;
3241                     }
3242                     else {
3243                         final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
3244                                 - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
3245                         if ( scroll_position.x >= max_x ) {
3246                             scroll_position.x = max_x;
3247                         }
3248                     }
3249                     if ( scroll_position.y <= 0 ) {
3250                         scroll_position.y = 0;
3251                     }
3252                     else {
3253                         final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
3254                                 - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
3255                         if ( scroll_position.y >= max_y ) {
3256                             scroll_position.y = max_y;
3257                         }
3258                     }
3259                     repaint();
3260                     getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
3261                 }
3262             }
3263             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
3264                 getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
3265                 getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
3266                                                            Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
3267                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3268             }
3269             else if ( plusPressed( e.getKeyCode() ) ) {
3270                 getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
3271                                                           Constants.WHEEL_ZOOM_IN_FACTOR );
3272                 getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
3273                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3274             }
3275             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
3276                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3277                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3278                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
3279                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3280                 }
3281             }
3282             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
3283                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3284                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3285                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
3286                     if ( getStartingAngle() < 0 ) {
3287                         setStartingAngle( TWO_PI + getStartingAngle() );
3288                     }
3289                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3290                 }
3291             }
3292             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
3293                 boolean selected = false;
3294                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
3295                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
3296                     selected = true;
3297                 }
3298                 else {
3299                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
3300                 }
3301                 if ( getMainPanel().getMainFrame() == null ) {
3302                     // Must be "E" applet version.
3303                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
3304                     if ( ae.getlabelDirectionCbmi() != null ) {
3305                         ae.getlabelDirectionCbmi().setSelected( selected );
3306                     }
3307                 }
3308                 else {
3309                     getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
3310                 }
3311                 repaint();
3312             }
3313             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
3314                 switchDisplaygetPhylogenyGraphicsType();
3315                 repaint();
3316             }
3317             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
3318                 cycleColors();
3319                 repaint();
3320             }
3321             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
3322                 MainFrame.cycleOverview( getOptions(), this );
3323                 repaint();
3324             }
3325             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
3326                 increaseOvSize();
3327             }
3328             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
3329                 decreaseOvSize();
3330             }
3331             e.consume();
3332         }
3333     }
3334
3335     final private void makePopupMenus( final PhylogenyNode node ) {
3336         _node_popup_menu = new JPopupMenu();
3337         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
3338         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
3339         for( int i = 0; i < clickto_names.size(); i++ ) {
3340             final String title = clickto_names.get( i );
3341             _node_popup_menu_items[ i ] = new JMenuItem( title );
3342             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
3343                 final String id = isCanOpenSeqWeb( node );
3344                 if ( !ForesterUtil.isEmpty( id ) ) {
3345                     _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " [" + id + "]" );
3346                     _node_popup_menu_items[ i ].setEnabled( true );
3347                 }
3348                 else {
3349                     _node_popup_menu_items[ i ].setEnabled( false );
3350                 }
3351             }
3352             else if ( title.equals( Configuration.clickto_options[ Configuration.open_pdb_web ][ 0 ] ) ) {
3353                 final List<Accession> accs = getPdbAccs( node );
3354                 _node_popup_menu_items[ i ] = new JMenuItem( title );
3355                 if ( !ForesterUtil.isEmpty( accs ) ) {
3356                     if ( accs.size() == 1 ) {
3357                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
3358                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + "]" );
3359                         _node_popup_menu_items[ i ].setEnabled( true );
3360                     }
3361                     else if ( accs.size() == 2 ) {
3362                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
3363                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
3364                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + "]" );
3365                         _node_popup_menu_items[ i ].setEnabled( true );
3366                     }
3367                     else if ( accs.size() == 3 ) {
3368                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
3369                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
3370                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + ", "
3371                                 + TreePanelUtil.pdbAccToString( accs, 2 ) + "]" );
3372                         _node_popup_menu_items[ i ].setEnabled( true );
3373                     }
3374                     else {
3375                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
3376                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
3377                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + ", "
3378                                 + TreePanelUtil.pdbAccToString( accs, 2 ) + ", + " + ( accs.size() - 3 ) + " more]" );
3379                         _node_popup_menu_items[ i ].setEnabled( true );
3380                     }
3381                 }
3382                 else {
3383                     _node_popup_menu_items[ i ].setEnabled( false );
3384                 }
3385                 //
3386             }
3387             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
3388                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
3389             }
3390             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
3391                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
3392             }
3393             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
3394                 if ( !getOptions().isEditable() ) {
3395                     continue;
3396                 }
3397                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
3398             }
3399             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
3400                 if ( !getOptions().isEditable() ) {
3401                     continue;
3402                 }
3403                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
3404             }
3405             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
3406                 if ( !getOptions().isEditable() ) {
3407                     continue;
3408                 }
3409                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
3410             }
3411             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
3412                 if ( !getOptions().isEditable() ) {
3413                     continue;
3414                 }
3415                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
3416             }
3417             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
3418                 if ( !getOptions().isEditable() ) {
3419                     continue;
3420                 }
3421             }
3422             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
3423                 if ( !getOptions().isEditable() ) {
3424                     continue;
3425                 }
3426             }
3427             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
3428                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
3429             }
3430             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
3431                 _node_popup_menu_items[ i ].setEnabled( ( isCanCollapse() && !node.isExternal() ) );
3432             }
3433             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
3434                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
3435             }
3436             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
3437                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
3438             }
3439             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
3440                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
3441             }
3442             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
3443                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
3444             }
3445             _node_popup_menu_items[ i ].addActionListener( this );
3446             _node_popup_menu.add( _node_popup_menu_items[ i ] );
3447         }
3448     }
3449
3450     private final void nodeDataAsSB( final PhylogenyNode node, final StringBuilder sb ) {
3451         if ( node != null ) {
3452             if ( getControlPanel().isShowNodeNames() && ( !ForesterUtil.isEmpty( node.getName() ) ) ) {
3453                 if ( sb.length() > 0 ) {
3454                     sb.append( " " );
3455                 }
3456                 sb.append( node.getName() );
3457             }
3458             if ( node.getNodeData().isHasSequence() ) {
3459                 if ( getControlPanel().isShowSeqSymbols()
3460                         && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3461                     if ( sb.length() > 0 ) {
3462                         sb.append( " " );
3463                     }
3464                     sb.append( node.getNodeData().getSequence().getSymbol() );
3465                 }
3466                 if ( getControlPanel().isShowGeneNames()
3467                         && ( node.getNodeData().getSequence().getGeneName().length() > 0 ) ) {
3468                     if ( sb.length() > 0 ) {
3469                         sb.append( " " );
3470                     }
3471                     sb.append( node.getNodeData().getSequence().getGeneName() );
3472                 }
3473                 if ( getControlPanel().isShowSeqNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3474                     if ( sb.length() > 0 ) {
3475                         sb.append( " " );
3476                     }
3477                     sb.append( node.getNodeData().getSequence().getName() );
3478                 }
3479                 if ( getControlPanel().isShowSequenceAcc()
3480                         && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3481                     if ( sb.length() > 0 ) {
3482                         sb.append( " " );
3483                     }
3484                     if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3485                         sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3486                         sb.append( ":" );
3487                     }
3488                     sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3489                 }
3490             }
3491             if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
3492                 if ( sb.length() > 0 ) {
3493                     sb.append( " " );
3494                 }
3495                 sb.append( propertiesToString( node ) );
3496             }
3497         }
3498     }
3499
3500     private final void nodeTaxonomyDataAsSB( final Taxonomy taxonomy, final StringBuilder sb ) {
3501         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3502             sb.append( taxonomy.getTaxonomyCode() );
3503             sb.append( " " );
3504         }
3505         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3506             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3507                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3508                 if ( getOptions().isAbbreviateScientificTaxonNames()
3509                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3510                     abbreviateScientificName( taxonomy.getScientificName(), sb );
3511                 }
3512                 else {
3513                     sb.append( taxonomy.getScientificName() );
3514                 }
3515                 sb.append( " (" );
3516                 sb.append( taxonomy.getCommonName() );
3517                 sb.append( ") " );
3518             }
3519             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3520                 if ( getOptions().isAbbreviateScientificTaxonNames()
3521                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3522                     abbreviateScientificName( taxonomy.getScientificName(), sb );
3523                 }
3524                 else {
3525                     sb.append( taxonomy.getScientificName() );
3526                 }
3527                 sb.append( " " );
3528             }
3529             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3530                 sb.append( taxonomy.getCommonName() );
3531                 sb.append( " " );
3532             }
3533         }
3534         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3535             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3536                 if ( getOptions().isAbbreviateScientificTaxonNames()
3537                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3538                     abbreviateScientificName( taxonomy.getScientificName(), sb );
3539                 }
3540                 else {
3541                     sb.append( taxonomy.getScientificName() );
3542                 }
3543                 sb.append( " " );
3544             }
3545         }
3546         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3547             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3548                 sb.append( taxonomy.getCommonName() );
3549                 sb.append( " " );
3550             }
3551         }
3552     }
3553
3554     private final String obtainTitleForExtDescNodeData() {
3555         switch ( getOptions().getExtDescNodeDataToReturn() ) {
3556             case NODE_NAME:
3557                 return "Node Names";
3558             case GENE_NAME:
3559                 return "Gene Names";
3560             case SEQUENCE_NAME:
3561                 return "Sequence Names";
3562             case SEQUENCE_SYMBOL:
3563                 return "Sequence Symbols";
3564             case SEQUENCE_MOL_SEQ:
3565                 return "Molecular Sequences";
3566             case SEQUENCE_MOL_SEQ_FASTA:
3567                 return "Molecular Sequences (Fasta)";
3568             case SEQUENCE_ACC:
3569                 return "Sequence Accessors";
3570             case TAXONOMY_SCIENTIFIC_NAME:
3571                 return "Scientific Names";
3572             case TAXONOMY_CODE:
3573                 return "Taxonomy Codes";
3574             case TAXONOMY_COMM0N_NAME:
3575                 return "Taxonomy Common Names";
3576             case UNKNOWN:
3577                 return "User Selected Data";
3578             default:
3579                 throw new IllegalArgumentException( "unknown data element: "
3580                         + getOptions().getExtDescNodeDataToReturn() );
3581         }
3582     }
3583
3584     final private void openPdbWeb( final PhylogenyNode node ) {
3585         final List<Accession> pdb_ids = getPdbAccs( node );
3586         if ( ForesterUtil.isEmpty( pdb_ids ) ) {
3587             cannotOpenBrowserWarningMessage( "PDB" );
3588             return;
3589         }
3590         final List<String> uri_strs = TreePanelUtil.createUrisForPdbWeb( node, pdb_ids, getConfiguration(), this );
3591         if ( !ForesterUtil.isEmpty( uri_strs ) ) {
3592             for( final String uri_str : uri_strs ) {
3593                 try {
3594                     AptxUtil.launchWebBrowser( new URI( uri_str ),
3595                                                isApplet(),
3596                                                isApplet() ? obtainApplet() : null,
3597                                                "_aptx_seq" );
3598                 }
3599                 catch ( final IOException e ) {
3600                     AptxUtil.showErrorMessage( this, e.toString() );
3601                     e.printStackTrace();
3602                 }
3603                 catch ( final URISyntaxException e ) {
3604                     AptxUtil.showErrorMessage( this, e.toString() );
3605                     e.printStackTrace();
3606                 }
3607             }
3608         }
3609         else {
3610             cannotOpenBrowserWarningMessage( "PDB" );
3611         }
3612     }
3613
3614     final private void openSeqWeb( final PhylogenyNode node ) {
3615         if ( ForesterUtil.isEmpty( isCanOpenSeqWeb( node ) ) ) {
3616             cannotOpenBrowserWarningMessage( "sequence" );
3617             return;
3618         }
3619         final String uri_str = TreePanelUtil.createUriForSeqWeb( node, getConfiguration(), this );
3620         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3621             try {
3622                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3623                                            isApplet(),
3624                                            isApplet() ? obtainApplet() : null,
3625                                            "_aptx_seq" );
3626             }
3627             catch ( final IOException e ) {
3628                 AptxUtil.showErrorMessage( this, e.toString() );
3629                 e.printStackTrace();
3630             }
3631             catch ( final URISyntaxException e ) {
3632                 AptxUtil.showErrorMessage( this, e.toString() );
3633                 e.printStackTrace();
3634             }
3635         }
3636         else {
3637             cannotOpenBrowserWarningMessage( "sequence" );
3638         }
3639     }
3640
3641     final private void openTaxWeb( final PhylogenyNode node ) {
3642         if ( !isCanOpenTaxWeb( node ) ) {
3643             cannotOpenBrowserWarningMessage( "taxonomic" );
3644             return;
3645         }
3646         String uri_str = null;
3647         final Taxonomy tax = node.getNodeData().getTaxonomy();
3648         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3649                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
3650             try {
3651                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
3652             }
3653             catch ( final URISyntaxException e ) {
3654                 AptxUtil.showErrorMessage( this, e.toString() );
3655                 uri_str = null;
3656                 e.printStackTrace();
3657             }
3658         }
3659         else if ( ( tax.getIdentifier() != null )
3660                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3661                 && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
3662                 && ( tax.getIdentifier().getProvider().equalsIgnoreCase( "ncbi" ) || tax.getIdentifier().getProvider()
3663                         .equalsIgnoreCase( "uniprot" ) ) ) {
3664             try {
3665                 uri_str = "http://www.uniprot.org/taxonomy/"
3666                         + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
3667             }
3668             catch ( final UnsupportedEncodingException e ) {
3669                 AptxUtil.showErrorMessage( this, e.toString() );
3670                 e.printStackTrace();
3671             }
3672         }
3673         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
3674             try {
3675                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3676                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
3677             }
3678             catch ( final UnsupportedEncodingException e ) {
3679                 AptxUtil.showErrorMessage( this, e.toString() );
3680                 e.printStackTrace();
3681             }
3682         }
3683         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
3684             try {
3685                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3686                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
3687             }
3688             catch ( final UnsupportedEncodingException e ) {
3689                 AptxUtil.showErrorMessage( this, e.toString() );
3690                 e.printStackTrace();
3691             }
3692         }
3693         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
3694             try {
3695                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3696                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
3697             }
3698             catch ( final UnsupportedEncodingException e ) {
3699                 AptxUtil.showErrorMessage( this, e.toString() );
3700                 e.printStackTrace();
3701             }
3702         }
3703         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3704             try {
3705                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3706                                            isApplet(),
3707                                            isApplet() ? obtainApplet() : null,
3708                                            "_aptx_tax" );
3709             }
3710             catch ( final IOException e ) {
3711                 AptxUtil.showErrorMessage( this, e.toString() );
3712                 e.printStackTrace();
3713             }
3714             catch ( final URISyntaxException e ) {
3715                 AptxUtil.showErrorMessage( this, e.toString() );
3716                 e.printStackTrace();
3717             }
3718         }
3719         else {
3720             cannotOpenBrowserWarningMessage( "taxonomic" );
3721         }
3722     }
3723
3724     final private void paintBranchLength( final Graphics2D g,
3725                                           final PhylogenyNode node,
3726                                           final boolean to_pdf,
3727                                           final boolean to_graphics_file ) {
3728         g.setFont( getTreeFontSet().getSmallFont() );
3729         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3730             g.setColor( Color.BLACK );
3731         }
3732         else {
3733             g.setColor( getTreeColorSet().getBranchLengthColor() );
3734         }
3735         if ( !node.isRoot() ) {
3736             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3737                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3738                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3739             }
3740             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3741                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3742                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3743             }
3744             else {
3745                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3746                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3747             }
3748         }
3749         else {
3750             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
3751                     - getTreeFontSet().getSmallMaxDescent(), g );
3752         }
3753     }
3754
3755     final private void paintBranchLite( final Graphics2D g,
3756                                         final float x1,
3757                                         final float x2,
3758                                         final float y1,
3759                                         final float y2,
3760                                         final PhylogenyNode node ) {
3761         g.setColor( getTreeColorSet().getOvColor() );
3762         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3763             drawLine( x1, y1, x2, y2, g );
3764         }
3765         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3766             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3767             ( g ).draw( _quad_curve );
3768         }
3769         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3770             final float dx = x2 - x1;
3771             final float dy = y2 - y1;
3772             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3773                     + ( dy * 0.8f ), x2, y2 );
3774             ( g ).draw( _cubic_curve );
3775         }
3776         else {
3777             final float x2a = x2;
3778             final float x1a = x1;
3779             // draw the vertical line
3780             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
3781                 drawLine( x1, y1, x1, y2, g );
3782             }
3783             // draw the horizontal line
3784             drawLine( x1a, y2, x2a, y2, g );
3785         }
3786     }
3787
3788     /**
3789      * Paint a branch which consists of a vertical and a horizontal bar
3790      * @param is_ind_found_nodes 
3791      */
3792     final private void paintBranchRectangular( final Graphics2D g,
3793                                                final float x1,
3794                                                final float x2,
3795                                                final float y1,
3796                                                final float y2,
3797                                                final PhylogenyNode node,
3798                                                final boolean to_pdf,
3799                                                final boolean to_graphics_file ) {
3800         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
3801         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3802             drawLine( x1, y1, x2, y2, g );
3803         }
3804         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3805             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3806             g.draw( _quad_curve );
3807         }
3808         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3809             final float dx = x2 - x1;
3810             final float dy = y2 - y1;
3811             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3812                     + ( dy * 0.8f ), x2, y2 );
3813             g.draw( _cubic_curve );
3814         }
3815         else {
3816             final float x2a = x2;
3817             final float x1a = x1;
3818             float y2_r = 0;
3819             if ( node.isFirstChildNode() || node.isLastChildNode()
3820                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3821                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3822                 if ( !to_graphics_file
3823                         && !to_pdf
3824                         && ( ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) && ( y1 < ( getVisibleRect().getMinY() - 20 ) ) ) || ( ( y2 > ( getVisibleRect()
3825                                 .getMaxY() + 20 ) ) && ( y1 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) ) {
3826                     // Do nothing.
3827                 }
3828                 else {
3829                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3830                         float x2c = x1 + EURO_D;
3831                         if ( x2c > x2a ) {
3832                             x2c = x2a;
3833                         }
3834                         drawLine( x1, y1, x2c, y2, g );
3835                     }
3836                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3837                         if ( y2 > y1 ) {
3838                             y2_r = y2 - ROUNDED_D;
3839                             if ( y2_r < y1 ) {
3840                                 y2_r = y1;
3841                             }
3842                             drawLine( x1, y1, x1, y2_r, g );
3843                         }
3844                         else {
3845                             y2_r = y2 + ROUNDED_D;
3846                             if ( y2_r > y1 ) {
3847                                 y2_r = y1;
3848                             }
3849                             drawLine( x1, y1, x1, y2_r, g );
3850                         }
3851                     }
3852                     else {
3853                         drawLine( x1, y1, x1, y2, g );
3854                     }
3855                 }
3856             }
3857             // draw the horizontal line
3858             if ( !to_graphics_file && !to_pdf
3859                     && ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) || ( y2 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) {
3860                 return;
3861             }
3862             float x1_r = 0;
3863             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
3864                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3865                     x1_r = x1a + ROUNDED_D;
3866                     if ( x1_r < x2a ) {
3867                         drawLine( x1_r, y2, x2a, y2, g );
3868                     }
3869                 }
3870                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3871                     final float x1c = x1a + EURO_D;
3872                     if ( x1c < x2a ) {
3873                         drawLine( x1c, y2, x2a, y2, g );
3874                     }
3875                 }
3876                 else {
3877                     drawLine( x1a, y2, x2a, y2, g );
3878                 }
3879             }
3880             else {
3881                 final double w = PhylogenyMethods.getBranchWidthValue( node );
3882                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3883                     x1_r = x1a + ROUNDED_D;
3884                     if ( x1_r < x2a ) {
3885                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
3886                     }
3887                 }
3888                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3889                     final float x1c = x1a + EURO_D;
3890                     if ( x1c < x2a ) {
3891                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
3892                     }
3893                 }
3894                 else {
3895                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
3896                 }
3897             }
3898             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3899                 if ( x1_r > x2a ) {
3900                     x1_r = x2a;
3901                 }
3902                 if ( y2 > y2_r ) {
3903                     final double diff = y2 - y2_r;
3904                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
3905                 }
3906                 else {
3907                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
3908                 }
3909                 g.draw( _arc );
3910             }
3911         }
3912         if ( node.isExternal() ) {
3913             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file );
3914         }
3915     }
3916
3917     final private double paintCirculars( final PhylogenyNode n,
3918                                          final Phylogeny phy,
3919                                          final float center_x,
3920                                          final float center_y,
3921                                          final double radius,
3922                                          final boolean radial_labels,
3923                                          final Graphics2D g,
3924                                          final boolean to_pdf,
3925                                          final boolean to_graphics_file ) {
3926         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
3927             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
3928                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
3929             }
3930             return _urt_nodeid_angle_map.get( n.getId() );
3931         }
3932         else {
3933             final List<PhylogenyNode> descs = n.getDescendants();
3934             double sum = 0;
3935             for( final PhylogenyNode desc : descs ) {
3936                 sum += paintCirculars( desc,
3937                                        phy,
3938                                        center_x,
3939                                        center_y,
3940                                        radius,
3941                                        radial_labels,
3942                                        g,
3943                                        to_pdf,
3944                                        to_graphics_file );
3945             }
3946             double r = 0;
3947             if ( !n.isRoot() ) {
3948                 r = 1 - ( ( ( double ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3949             }
3950             final double theta = sum / descs.size();
3951             n.setXcoord( ( float ) ( center_x + ( r * radius * Math.cos( theta ) ) ) );
3952             n.setYcoord( ( float ) ( center_y + ( r * radius * Math.sin( theta ) ) ) );
3953             _urt_nodeid_angle_map.put( n.getId(), theta );
3954             for( final PhylogenyNode desc : descs ) {
3955                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
3956             }
3957             return theta;
3958         }
3959     }
3960
3961     final private void paintCircularsLite( final PhylogenyNode n,
3962                                            final Phylogeny phy,
3963                                            final int center_x,
3964                                            final int center_y,
3965                                            final int radius,
3966                                            final Graphics2D g ) {
3967         if ( n.isExternal() ) {
3968             return;
3969         }
3970         else {
3971             final List<PhylogenyNode> descs = n.getDescendants();
3972             for( final PhylogenyNode desc : descs ) {
3973                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
3974             }
3975             float r = 0;
3976             if ( !n.isRoot() ) {
3977                 r = 1 - ( ( ( float ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3978             }
3979             final double theta = _urt_nodeid_angle_map.get( n.getId() );
3980             n.setXSecondary( ( float ) ( center_x + ( radius * r * Math.cos( theta ) ) ) );
3981             n.setYSecondary( ( float ) ( center_y + ( radius * r * Math.sin( theta ) ) ) );
3982             for( final PhylogenyNode desc : descs ) {
3983                 paintBranchCircularLite( n, desc, g );
3984             }
3985         }
3986     }
3987
3988     final private void paintCollapsedNode( final Graphics2D g,
3989                                            final PhylogenyNode node,
3990                                            final boolean to_graphics_file,
3991                                            final boolean to_pdf,
3992                                            final boolean is_in_found_nodes ) {
3993         Color c = null;
3994         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3995             c = Color.BLACK;
3996         }
3997         else if ( is_in_found_nodes ) {
3998             c = getColorForFoundNode( node );
3999         }
4000         else if ( getControlPanel().isColorAccordingToSequence() ) {
4001             c = getSequenceBasedColor( node );
4002         }
4003         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4004             c = getTaxonomyBasedColor( node );
4005         }
4006         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
4007                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
4008             c = PhylogenyMethods.getBranchColorValue( node );
4009         }
4010         else {
4011             c = getTreeColorSet().getCollapseFillColor();
4012         }
4013         double d = node.getAllExternalDescendants().size();
4014         if ( d > 1000 ) {
4015             d = ( 3 * _y_distance ) / 3;
4016         }
4017         else {
4018             d = ( Math.log10( d ) * _y_distance ) / 2.5;
4019         }
4020         final int box_size = getOptions().getDefaultNodeShapeSize() + 1;
4021         if ( d < box_size ) {
4022             d = box_size;
4023         }
4024         final float xx = node.getXcoord() - ( 2 * box_size );
4025         final float xxx = xx > ( node.getParent().getXcoord() + 1 ) ? xx : node.getParent().getXcoord() + 1;
4026         _polygon.reset();
4027         _polygon.moveTo( xxx, node.getYcoord() );
4028         _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() - d );
4029         _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() + d );
4030         _polygon.closePath();
4031         if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.SOLID ) {
4032             g.setColor( c );
4033             g.fill( _polygon );
4034         }
4035         else if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.NONE ) {
4036             g.setColor( getBackground() );
4037             g.fill( _polygon );
4038             g.setColor( c );
4039             g.draw( _polygon );
4040         }
4041         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
4042             g.setPaint( new GradientPaint( xxx, node.getYcoord(), getBackground(), node.getXcoord(), ( float ) ( node
4043                     .getYcoord() - d ), c, false ) );
4044             g.fill( _polygon );
4045             g.setPaint( c );
4046             g.draw( _polygon );
4047         }
4048         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4049     }
4050
4051     final private void paintConfidenceValues( final Graphics2D g,
4052                                               final PhylogenyNode node,
4053                                               final boolean to_pdf,
4054                                               final boolean to_graphics_file ) {
4055         final List<Confidence> confidences = node.getBranchData().getConfidences();
4056         boolean not_first = false;
4057         Collections.sort( confidences );
4058         final StringBuilder sb = new StringBuilder();
4059         for( final Confidence confidence : confidences ) {
4060             if ( ForesterUtil.isEmpty( SHOW_ONLY_THIS_CONF_TYPE )
4061                     || ( !ForesterUtil.isEmpty( confidence.getType() ) && confidence.getType()
4062                             .equalsIgnoreCase( SHOW_ONLY_THIS_CONF_TYPE ) ) ) {
4063                 final double value = confidence.getValue();
4064                 if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4065                     if ( value < getOptions().getMinConfidenceValue() ) {
4066                         return;
4067                     }
4068                     if ( not_first ) {
4069                         sb.append( "/" );
4070                     }
4071                     else {
4072                         not_first = true;
4073                     }
4074                     sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
4075                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4076                     if ( getOptions().isShowConfidenceStddev() ) {
4077                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4078                             sb.append( "(" );
4079                             sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
4080                                     .getStandardDeviation(), getOptions()
4081                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4082                             sb.append( ")" );
4083                         }
4084                     }
4085                 }
4086             }
4087         }
4088         if ( sb.length() > 0 ) {
4089             final float parent_x = node.getParent().getXcoord();
4090             float x = node.getXcoord();
4091             g.setFont( getTreeFontSet().getSmallFont() );
4092             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
4093                 x += EURO_D;
4094             }
4095             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
4096                 x += ROUNDED_D;
4097             }
4098             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4099                 g.setColor( Color.BLACK );
4100             }
4101             else {
4102                 g.setColor( getTreeColorSet().getConfidenceColor() );
4103             }
4104             final String conf_str = sb.toString();
4105             TreePanel.drawString( conf_str,
4106                                   parent_x
4107                                           + ( ( x - parent_x - getTreeFontSet().getFontMetricsSmall()
4108                                                   .stringWidth( conf_str ) ) / 2 ),
4109                                   ( node.getYcoord() + getTreeFontSet().getSmallMaxAscent() ) - 1,
4110                                   g );
4111         }
4112     }
4113
4114     final private void paintGainedAndLostCharacters( final Graphics2D g,
4115                                                      final PhylogenyNode node,
4116                                                      final String gained,
4117                                                      final String lost ) {
4118         if ( node.getParent() != null ) {
4119             final float parent_x = node.getParent().getXcoord();
4120             final float x = node.getXcoord();
4121             g.setFont( getTreeFontSet().getLargeFont() );
4122             g.setColor( getTreeColorSet().getGainedCharactersColor() );
4123             if ( Constants.SPECIAL_CUSTOM ) {
4124                 g.setColor( Color.BLUE );
4125             }
4126             TreePanel
4127                     .drawString( gained,
4128                                  parent_x
4129                                          + ( ( x - parent_x - getFontMetricsForLargeDefaultFont().stringWidth( gained ) ) / 2 ),
4130                                  ( node.getYcoord() - getFontMetricsForLargeDefaultFont().getMaxDescent() ),
4131                                  g );
4132             g.setColor( getTreeColorSet().getLostCharactersColor() );
4133             TreePanel
4134                     .drawString( lost,
4135                                  parent_x
4136                                          + ( ( x - parent_x - getFontMetricsForLargeDefaultFont().stringWidth( lost ) ) / 2 ),
4137                                  ( node.getYcoord() + getFontMetricsForLargeDefaultFont().getMaxAscent() ),
4138                                  g );
4139         }
4140     }
4141
4142     /**
4143      * Draw a box at the indicated node.
4144      * 
4145      * @param x
4146      * @param y
4147      * @param node
4148      * @param g
4149      */
4150     final private void paintNodeBox( final float x,
4151                                      final float y,
4152                                      final PhylogenyNode node,
4153                                      final Graphics2D g,
4154                                      final boolean to_pdf,
4155                                      final boolean to_graphics_file ) {
4156         if ( node.isCollapse() ) {
4157             return;
4158         }
4159         // if this node should be highlighted, do so
4160         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
4161             g.setColor( getTreeColorSet().getFoundColor0() );
4162             drawOval( x - 8, y - 8, 16, 16, g );
4163             drawOval( x - 9, y - 8, 17, 17, g );
4164             drawOval( x - 9, y - 9, 18, 18, g );
4165         }
4166         if ( ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) )
4167                 || ( getOptions().isShowDefaultNodeShapesExternal() && node.isExternal() )
4168                 || ( getOptions().isShowDefaultNodeShapesInternal() && node.isInternal() )
4169                 || ( getOptions().isShowDefaultNodeShapesForMarkedNodes()
4170                         && ( node.getNodeData().getNodeVisualData() != null ) && ( !node.getNodeData()
4171                         .getNodeVisualData().isEmpty() ) )
4172                 || ( getControlPanel().isUseVisualStyles() && ( ( node.getNodeData().getNodeVisualData() != null ) && ( ( node
4173                         .getNodeData().getNodeVisualData().getNodeColor() != null )
4174                         || ( node.getNodeData().getNodeVisualData().getSize() != NodeVisualData.DEFAULT_SIZE )
4175                         || ( node.getNodeData().getNodeVisualData().getFillType() != NodeFill.DEFAULT ) || ( node
4176                         .getNodeData().getNodeVisualData().getShape() != NodeShape.DEFAULT ) ) ) )
4177                 || ( getControlPanel().isEvents() && node.isHasAssignedEvent() && ( node.getNodeData().getEvent()
4178                         .isDuplication()
4179                         || node.getNodeData().getEvent().isSpeciation() || node.getNodeData().getEvent()
4180                         .isSpeciationOrDuplication() ) ) ) {
4181             NodeVisualData vis = null;
4182             if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
4183                     && ( !node.getNodeData().getNodeVisualData().isEmpty() ) ) {
4184                 vis = node.getNodeData().getNodeVisualData();
4185             }
4186             float box_size = getOptions().getDefaultNodeShapeSize();
4187             if ( ( vis != null ) && ( vis.getSize() != NodeVisualData.DEFAULT_SIZE ) ) {
4188                 box_size = vis.getSize();
4189             }
4190             final float half_box_size = box_size / 2.0f;
4191             Color outline_color = null;
4192             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4193                 outline_color = Color.BLACK;
4194             }
4195             else if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4196                 outline_color = getColorForFoundNode( node );
4197             }
4198             else if ( vis != null ) {
4199                 if ( vis.getNodeColor() != null ) {
4200                     outline_color = vis.getNodeColor();
4201                 }
4202                 else if ( vis.getFontColor() != null ) {
4203                     outline_color = vis.getFontColor();
4204                 }
4205             }
4206             else if ( getControlPanel().isEvents() && TreePanelUtil.isHasAssignedEvent( node ) ) {
4207                 final Event event = node.getNodeData().getEvent();
4208                 if ( event.isDuplication() ) {
4209                     outline_color = getTreeColorSet().getDuplicationBoxColor();
4210                 }
4211                 else if ( event.isSpeciation() ) {
4212                     outline_color = getTreeColorSet().getSpecBoxColor();
4213                 }
4214                 else if ( event.isSpeciationOrDuplication() ) {
4215                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
4216                 }
4217             }
4218             if ( outline_color == null ) {
4219                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
4220                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
4221                     outline_color = getTreeColorSet().getBranchColorForPdf();
4222                 }
4223             }
4224             NodeShape shape = null;
4225             if ( vis != null ) {
4226                 if ( vis.getShape() == NodeShape.CIRCLE ) {
4227                     shape = NodeShape.CIRCLE;
4228                 }
4229                 else if ( vis.getShape() == NodeShape.RECTANGLE ) {
4230                     shape = NodeShape.RECTANGLE;
4231                 }
4232             }
4233             if ( shape == null ) {
4234                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
4235                     shape = NodeShape.CIRCLE;
4236                 }
4237                 else if ( getOptions().getDefaultNodeShape() == NodeShape.RECTANGLE ) {
4238                     shape = NodeShape.RECTANGLE;
4239                 }
4240             }
4241             NodeFill fill = null;
4242             if ( vis != null ) {
4243                 if ( vis.getFillType() == NodeFill.SOLID ) {
4244                     fill = NodeFill.SOLID;
4245                 }
4246                 else if ( vis.getFillType() == NodeFill.NONE ) {
4247                     fill = NodeFill.NONE;
4248                 }
4249                 else if ( vis.getFillType() == NodeFill.GRADIENT ) {
4250                     fill = NodeFill.GRADIENT;
4251                 }
4252             }
4253             if ( fill == null ) {
4254                 if ( getOptions().getDefaultNodeFill() == NodeFill.SOLID ) {
4255                     fill = NodeFill.SOLID;
4256                 }
4257                 else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
4258                     fill = NodeFill.NONE;
4259                 }
4260                 else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
4261                     fill = NodeFill.GRADIENT;
4262                 }
4263             }
4264             Color vis_fill_color = null;
4265             if ( ( vis != null ) && ( vis.getNodeColor() != null ) ) {
4266                 vis_fill_color = vis.getNodeColor();
4267             }
4268             if ( shape == NodeShape.CIRCLE ) {
4269                 if ( fill == NodeFill.GRADIENT ) {
4270                     drawOvalGradient( x - half_box_size, y - half_box_size, box_size, box_size, g, to_pdf ? Color.WHITE
4271                             : outline_color, to_pdf ? outline_color : getBackground(), outline_color );
4272                 }
4273                 else if ( fill == NodeFill.NONE ) {
4274                     Color background = getBackground();
4275                     if ( to_pdf ) {
4276                         background = Color.WHITE;
4277                     }
4278                     drawOvalGradient( x - half_box_size,
4279                                       y - half_box_size,
4280                                       box_size,
4281                                       box_size,
4282                                       g,
4283                                       background,
4284                                       background,
4285                                       outline_color );
4286                 }
4287                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
4288                     if ( vis_fill_color != null ) {
4289                         g.setColor( vis_fill_color );
4290                     }
4291                     else {
4292                         g.setColor( outline_color );
4293                     }
4294                     drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4295                 }
4296             }
4297             else if ( shape == NodeVisualData.NodeShape.RECTANGLE ) {
4298                 if ( fill == NodeVisualData.NodeFill.GRADIENT ) {
4299                     drawRectGradient( x - half_box_size, y - half_box_size, box_size, box_size, g, to_pdf ? Color.WHITE
4300                             : outline_color, to_pdf ? outline_color : getBackground(), outline_color );
4301                 }
4302                 else if ( fill == NodeVisualData.NodeFill.NONE ) {
4303                     Color background = getBackground();
4304                     if ( to_pdf ) {
4305                         background = Color.WHITE;
4306                     }
4307                     drawRectGradient( x - half_box_size,
4308                                       y - half_box_size,
4309                                       box_size,
4310                                       box_size,
4311                                       g,
4312                                       background,
4313                                       background,
4314                                       outline_color );
4315                 }
4316                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
4317                     if ( vis_fill_color != null ) {
4318                         g.setColor( vis_fill_color );
4319                     }
4320                     else {
4321                         g.setColor( outline_color );
4322                     }
4323                     drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4324                 }
4325             }
4326         }
4327     }
4328
4329     final private int paintNodeData( final Graphics2D g,
4330                                      final PhylogenyNode node,
4331                                      final boolean to_graphics_file,
4332                                      final boolean to_pdf,
4333                                      final boolean is_in_found_nodes ) {
4334         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
4335             return 0;
4336         }
4337         if ( getControlPanel().isWriteBranchLengthValues()
4338                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4339                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4340                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
4341             paintBranchLength( g, node, to_pdf, to_graphics_file );
4342         }
4343         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
4344             return 0;
4345         }
4346         _sb.setLength( 0 );
4347         int x = 0;
4348         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
4349         if ( getControlPanel().isShowTaxonomyImages()
4350                 && ( getImageMap() != null )
4351                 && !getImageMap().isEmpty()
4352                 && node.getNodeData().isHasTaxonomy()
4353                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
4354                         .getUris().isEmpty() ) ) {
4355             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
4356         }
4357         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4358                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
4359             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
4360         }
4361         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
4362         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4363             if ( _sb.length() > 0 ) {
4364                 _sb.setLength( 0 );
4365                 _sb.append( " (" );
4366                 _sb.append( node.getAllExternalDescendants().size() );
4367                 _sb.append( ")" );
4368             }
4369         }
4370         else {
4371             _sb.setLength( 0 );
4372         }
4373         nodeDataAsSB( node, _sb );
4374         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4375         float down_shift_factor = 3.0f;
4376         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
4377             down_shift_factor = 1;
4378         }
4379         final float pos_x = node.getXcoord() + x + 2 + half_box_size;
4380         float pos_y;
4381         if ( !using_visual_font ) {
4382             pos_y = ( node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ) );
4383         }
4384         else {
4385             pos_y = ( node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / down_shift_factor ) );
4386         }
4387         final String sb_str = _sb.toString();
4388         // GUILHEM_BEG ______________
4389         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
4390                 && ( _query_sequence != null ) ) {
4391             int nodeTextBoundsWidth = 0;
4392             if ( sb_str.length() > 0 ) {
4393                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
4394                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
4395             }
4396             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
4397                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
4398                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
4399                     g.setColor( getTreeColorSet().getBackgroundColor() );
4400                 }
4401             }
4402             else {
4403                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
4404                 for( final SequenceRelation seqRelation : seqRelations ) {
4405                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
4406                             .getRef1().isEqual( _query_sequence ) )
4407                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
4408                                     .getSelectedItem() );
4409                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
4410                         final double linePosX = node.getXcoord() + 2 + half_box_size;
4411                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
4412                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
4413                                 + ")";
4414                         if ( sConfidence != null ) {
4415                             float confidenceX = pos_x;
4416                             if ( sb_str.length() > 0 ) {
4417                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
4418                                         + CONFIDENCE_LEFT_MARGIN;
4419                             }
4420                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
4421                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
4422                                         .getBounds().getWidth();
4423                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
4424                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
4425                             }
4426                         }
4427                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
4428                         {
4429                             if ( nodeTextBoundsWidth == 0 ) {
4430                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
4431                             }
4432                             else {
4433                                 nodeTextBoundsWidth += 2;
4434                             }
4435                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
4436                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
4437                             break;
4438                         }
4439                     }
4440                 }
4441             }
4442         }
4443         if ( sb_str.length() > 0 ) {
4444             TreePanel.drawString( sb_str, pos_x, pos_y, g );
4445         }
4446         // GUILHEM_END _____________
4447         if ( _sb.length() > 0 ) {
4448             if ( !using_visual_font && !is_in_found_nodes ) {
4449                 x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
4450             }
4451             else {
4452                 x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
4453             }
4454         }
4455         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
4456                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4457                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4458             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
4459             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4460                 g.setColor( Color.BLACK );
4461             }
4462             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
4463                 g.setColor( calculateColorForAnnotation( ann ) );
4464             }
4465             final String ann_str = TreePanelUtil.createAnnotationString( ann, getOptions().isShowAnnotationRefSource() );
4466             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
4467                     + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ), g );
4468             _sb.setLength( 0 );
4469             _sb.append( ann_str );
4470             if ( _sb.length() > 0 ) {
4471                 if ( !using_visual_font && !is_in_found_nodes ) {
4472                     x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
4473                 }
4474                 else {
4475                     x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
4476                 }
4477             }
4478         }
4479         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4480                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
4481                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
4482             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
4483                     && node.getNodeData().isHasBinaryCharacters() ) {
4484                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4485                     g.setColor( Color.BLACK );
4486                 }
4487                 else {
4488                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
4489                 }
4490                 if ( getControlPanel().isShowBinaryCharacters() ) {
4491                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
4492                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
4493                             + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ), g );
4494                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
4495                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
4496                             .getLostCharactersAsStringBuffer().toString() );
4497                 }
4498                 else {
4499                     TreePanel
4500                             .drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
4501                                          node.getXcoord() + x + 4 + half_box_size,
4502                                          node.getYcoord()
4503                                                  + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ),
4504                                          g );
4505                     paintGainedAndLostCharacters( g, node, "+"
4506                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
4507                             + node.getNodeData().getBinaryCharacters().getLostCount() );
4508                 }
4509             }
4510         }
4511         return x;
4512     }
4513
4514     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
4515                                                   final PhylogenyNode node,
4516                                                   final boolean to_pdf,
4517                                                   final boolean to_graphics_file,
4518                                                   final boolean radial_labels,
4519                                                   final double ur_angle,
4520                                                   final boolean is_in_found_nodes ) {
4521         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
4522             return;
4523         }
4524         _sb.setLength( 0 );
4525         _sb.append( " " );
4526         if ( node.getNodeData().isHasTaxonomy()
4527                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4528                         .isShowTaxonomyCommonNames() ) ) {
4529             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4530             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4531                 _sb.append( taxonomy.getTaxonomyCode() );
4532                 _sb.append( " " );
4533             }
4534             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4535                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4536                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4537                     _sb.append( taxonomy.getScientificName() );
4538                     _sb.append( " (" );
4539                     _sb.append( taxonomy.getCommonName() );
4540                     _sb.append( ") " );
4541                 }
4542                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4543                     _sb.append( taxonomy.getScientificName() );
4544                     _sb.append( " " );
4545                 }
4546                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4547                     _sb.append( taxonomy.getCommonName() );
4548                     _sb.append( " " );
4549                 }
4550             }
4551             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4552                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4553                     _sb.append( taxonomy.getScientificName() );
4554                     _sb.append( " " );
4555                 }
4556             }
4557             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4558                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4559                     _sb.append( taxonomy.getCommonName() );
4560                     _sb.append( " " );
4561                 }
4562             }
4563         }
4564         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4565             _sb.append( " [" );
4566             _sb.append( node.getAllExternalDescendants().size() );
4567             _sb.append( "]" );
4568         }
4569         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
4570             if ( _sb.length() > 0 ) {
4571                 _sb.append( " " );
4572             }
4573             _sb.append( node.getName() );
4574         }
4575         if ( node.getNodeData().isHasSequence() ) {
4576             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4577                 if ( _sb.length() > 0 ) {
4578                     _sb.append( " " );
4579                 }
4580                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4581                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4582                     _sb.append( ":" );
4583                 }
4584                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4585             }
4586             if ( getControlPanel().isShowSeqNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
4587                 if ( _sb.length() > 0 ) {
4588                     _sb.append( " " );
4589                 }
4590                 _sb.append( node.getNodeData().getSequence().getName() );
4591             }
4592         }
4593         //g.setFont( getTreeFontSet().getLargeFont() );
4594         //if ( is_in_found_nodes ) {
4595         //    g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4596         // }
4597         if ( _sb.length() > 1 ) {
4598             setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
4599             final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4600             final String sb_str = _sb.toString();
4601             double m = 0;
4602             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4603                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
4604             }
4605             else {
4606                 m = ( float ) ( ur_angle % TWO_PI );
4607             }
4608             _at = g.getTransform();
4609             boolean need_to_reset = false;
4610             final float x_coord = node.getXcoord();
4611             float y_coord;
4612             if ( !using_visual_font ) {
4613                 y_coord = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / 3.0f );
4614             }
4615             else {
4616                 y_coord = node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / 3.0f );
4617             }
4618             if ( radial_labels ) {
4619                 need_to_reset = true;
4620                 boolean left = false;
4621                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4622                     m -= PI;
4623                     left = true;
4624                 }
4625                 g.rotate( m, x_coord, node.getYcoord() );
4626                 if ( left ) {
4627                     if ( !using_visual_font ) {
4628                         g.translate( -( getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth() ),
4629                                      0 );
4630                     }
4631                     else {
4632                         g.translate( -( getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth() ), 0 );
4633                     }
4634                 }
4635             }
4636             else {
4637                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4638                     need_to_reset = true;
4639                     if ( !using_visual_font ) {
4640                         g.translate( -getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth(), 0 );
4641                     }
4642                     else {
4643                         g.translate( -getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth(), 0 );
4644                     }
4645                 }
4646             }
4647             TreePanel.drawString( sb_str, x_coord, y_coord, g );
4648             if ( need_to_reset ) {
4649                 g.setTransform( _at );
4650             }
4651         }
4652     }
4653
4654     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
4655         if ( node.isCollapse() ) {
4656             if ( !node.isRoot() && !node.getParent().isCollapse() ) {
4657                 paintCollapsedNode( g, node, false, false, false );
4658             }
4659             return;
4660         }
4661         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4662             g.setColor( getColorForFoundNode( node ) );
4663             drawRectFilled( node.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, node.getYSecondary()
4664                     - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, OVERVIEW_FOUND_NODE_BOX_SIZE, OVERVIEW_FOUND_NODE_BOX_SIZE, g );
4665         }
4666         float new_x = 0;
4667         if ( !node.isExternal() && !node.isCollapse() ) {
4668             boolean first_child = true;
4669             float y2 = 0.0f;
4670             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4671             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4672                 final PhylogenyNode child_node = node.getChildNode( i );
4673                 int factor_x;
4674                 if ( !isUniformBranchLengthsForCladogram() ) {
4675                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4676                 }
4677                 else {
4678                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4679                 }
4680                 if ( first_child ) {
4681                     first_child = false;
4682                     y2 = node.getYSecondary()
4683                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
4684                                     .getNumberOfExternalNodes() ) );
4685                 }
4686                 else {
4687                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4688                 }
4689                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
4690                 new_x = x2 + node.getXSecondary();
4691                 final float diff_y = node.getYSecondary() - y2;
4692                 final float diff_x = node.getXSecondary() - new_x;
4693                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
4694                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
4695                 }
4696                 child_node.setXSecondary( new_x );
4697                 child_node.setYSecondary( y2 );
4698                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4699             }
4700         }
4701     }
4702
4703     final private void paintNodeRectangular( final Graphics2D g,
4704                                              final PhylogenyNode node,
4705                                              final boolean to_pdf,
4706                                              final boolean dynamically_hide,
4707                                              final int dynamic_hiding_factor,
4708                                              final boolean to_graphics_file ) {
4709         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
4710         if ( node.isCollapse() ) {
4711             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) ) {
4712                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4713             }
4714             return;
4715         }
4716         if ( node.isExternal() ) {
4717             ++_external_node_index;
4718         }
4719         // Confidence values
4720         if ( getControlPanel().isShowConfidenceValues()
4721                 && !node.isExternal()
4722                 && !node.isRoot()
4723                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
4724                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4725                 && node.getBranchData().isHasConfidences() ) {
4726             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
4727         }
4728         // Draw a line to root:
4729         if ( node.isRoot() && _phylogeny.isRooted() ) {
4730             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
4731         }
4732         float new_x = 0;
4733         float new_x_min = Float.MAX_VALUE;
4734         final boolean disallow_shortcutting = ( dynamic_hiding_factor < 40 );
4735         float min_dist = 1.5f;
4736         if ( !disallow_shortcutting ) {
4737             if ( dynamic_hiding_factor > 4000 ) {
4738                 min_dist = 4;
4739             }
4740             else if ( dynamic_hiding_factor > 1000 ) {
4741                 min_dist = 3;
4742             }
4743             else if ( dynamic_hiding_factor > 100 ) {
4744                 min_dist = 2;
4745             }
4746         }
4747         if ( !node.isExternal() && !node.isCollapse() ) {
4748             boolean first_child = true;
4749             float y2 = 0.0f;
4750             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4751             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4752                 final PhylogenyNode child_node = node.getChildNode( i );
4753                 int factor_x;
4754                 if ( !isUniformBranchLengthsForCladogram() ) {
4755                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4756                 }
4757                 else {
4758                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4759                 }
4760                 if ( first_child ) {
4761                     first_child = false;
4762                     y2 = node.getYcoord()
4763                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
4764                 }
4765                 else {
4766                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
4767                 }
4768                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
4769                 new_x = x2 + node.getXcoord();
4770                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
4771                     new_x_min = x2;
4772                 }
4773                 final float diff_y = node.getYcoord() - y2;
4774                 final float diff_x = node.getXcoord() - new_x;
4775                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
4776                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
4777                     paintBranchRectangular( g,
4778                                             node.getXcoord(),
4779                                             new_x,
4780                                             node.getYcoord(),
4781                                             y2,
4782                                             child_node,
4783                                             to_pdf,
4784                                             to_graphics_file );
4785                 }
4786                 child_node.setXcoord( new_x );
4787                 child_node.setYcoord( y2 );
4788                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
4789             }
4790             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file );
4791         }
4792         if ( dynamically_hide
4793                 && !is_in_found_nodes
4794                 && ( ( node.isExternal() && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) ) || ( !node
4795                         .isExternal() && ( ( new_x_min < 20 ) || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getFontMetricsForLargeDefaultFont()
4796                         .getHeight() ) ) ) ) ) {
4797             return;
4798         }
4799         final int x = paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4800         paintNodeWithRenderableData( x, g, node, to_graphics_file, to_pdf );
4801     }
4802
4803     final private void paintNodeWithRenderableData( final int x,
4804                                                     final Graphics2D g,
4805                                                     final PhylogenyNode node,
4806                                                     final boolean to_graphics_file,
4807                                                     final boolean to_pdf ) {
4808         if ( isNodeDataInvisible( node ) && !( to_graphics_file || to_pdf ) ) {
4809             return;
4810         }
4811         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
4812             return;
4813         }
4814         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4815                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null )
4816                 && ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
4817             RenderableDomainArchitecture rds = null;
4818             try {
4819                 rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
4820             }
4821             catch ( final ClassCastException cce ) {
4822                 cce.printStackTrace();
4823             }
4824             if ( rds != null ) {
4825                 final int default_height = 7;
4826                 float y = getYdistance();
4827                 if ( getControlPanel().isDynamicallyHideData() ) {
4828                     y = getTreeFontSet().getFontMetricsLarge().getHeight();
4829                 }
4830                 final int h = y < default_height ? ForesterUtil.roundToInt( y ) : default_height;
4831                 rds.setRenderingHeight( h > 1 ? h : 2 );
4832                 if ( getControlPanel().isDrawPhylogram() ) {
4833                     if ( getOptions().isLineUpRendarableNodeData() ) {
4834                         if ( getOptions().isRightLineUpDomains() ) {
4835                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
4836                                     + _length_of_longest_text + ( ( _longest_domain - rds.getTotalLength() ) * rds
4837                                     .getRenderingFactorWidth() ) ), node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
4838                         }
4839                         else {
4840                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
4841                                         node.getYcoord() - ( h / 2.0f ),
4842                                         g,
4843                                         this,
4844                                         to_pdf );
4845                         }
4846                     }
4847                     else {
4848                         rds.render( node.getXcoord() + x, node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
4849                     }
4850                 }
4851                 else {
4852                     if ( getOptions().isRightLineUpDomains() ) {
4853                         rds.render( ( ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text ) - 20 )
4854                                             + ( ( _longest_domain - rds.getTotalLength() ) * rds
4855                                                     .getRenderingFactorWidth() ),
4856                                     node.getYcoord() - ( h / 2.0f ),
4857                                     g,
4858                                     this,
4859                                     to_pdf );
4860                     }
4861                     else {
4862                         rds.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
4863                                     node.getYcoord() - ( h / 2.0f ),
4864                                     g,
4865                                     this,
4866                                     to_pdf );
4867                     }
4868                 }
4869             }
4870         }
4871         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4872                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
4873             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
4874                                                                          getStatisticsForExpressionValues(),
4875                                                                          getConfiguration() );
4876             if ( rv != null ) {
4877                 double domain_add = 0;
4878                 if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4879                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4880                     domain_add = _domain_structure_width + 10;
4881                 }
4882                 if ( getControlPanel().isDrawPhylogram() ) {
4883                     rv.render( ( float ) ( node.getXcoord() + x + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
4884                 }
4885                 else {
4886                     rv.render( ( float ) ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text + domain_add ),
4887                                node.getYcoord() - 3,
4888                                g,
4889                                this,
4890                                to_pdf );
4891                 }
4892             }
4893         }
4894         if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
4895                 && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
4896                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
4897             final RenderableMsaSequence rs = RenderableMsaSequence.createInstance( node.getNodeData().getSequence()
4898                     .getMolecularSequence(), node.getNodeData().getSequence().getType(), getConfiguration() );
4899             if ( rs != null ) {
4900                 final int default_height = 7;
4901                 float y = getYdistance();
4902                 if ( getControlPanel().isDynamicallyHideData() ) {
4903                     y = getTreeFontSet().getFontMetricsLarge().getHeight();
4904                 }
4905                 final int h = y < default_height ? ForesterUtil.roundToInt( y ) : default_height;
4906                 rs.setRenderingHeight( h > 1 ? h : 2 );
4907                 if ( getControlPanel().isDrawPhylogram() ) {
4908                     rs.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
4909                                node.getYcoord() - ( h / 2.0f ),
4910                                g,
4911                                this,
4912                                to_pdf );
4913                 }
4914                 else {
4915                     rs.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
4916                                node.getYcoord() - ( h / 2.0f ),
4917                                g,
4918                                this,
4919                                to_pdf );
4920                 }
4921             }
4922         }
4923     }
4924
4925     final private int calcLengthOfLongestText() {
4926         final StringBuilder sb = new StringBuilder();
4927         if ( _ext_node_with_longest_txt_info != null ) {
4928             nodeDataAsSB( _ext_node_with_longest_txt_info, sb );
4929             if ( _ext_node_with_longest_txt_info.getNodeData().isHasTaxonomy() ) {
4930                 nodeTaxonomyDataAsSB( _ext_node_with_longest_txt_info.getNodeData().getTaxonomy(), sb );
4931             }
4932         }
4933         return getFontMetricsForLargeDefaultFont().stringWidth( sb.toString() );
4934     }
4935
4936     final private void paintOvRectangle( final Graphics2D g ) {
4937         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
4938         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
4939         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
4940         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
4941         final float width = getOvMaxWidth() / w_ratio;
4942         final float height = getOvMaxHeight() / h_ratio;
4943         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
4944         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
4945         g.setColor( getTreeColorSet().getFoundColor0() );
4946         getOvRectangle().setRect( x, y, width, height );
4947         final Stroke s = g.getStroke();
4948         g.setStroke( STROKE_1 );
4949         if ( ( width < 6 ) && ( height < 6 ) ) {
4950             drawRectFilled( x, y, 6, 6, g );
4951             getOvVirtualRectangle().setRect( x, y, 6, 6 );
4952         }
4953         else if ( width < 6 ) {
4954             drawRectFilled( x, y, 6, height, g );
4955             getOvVirtualRectangle().setRect( x, y, 6, height );
4956         }
4957         else if ( height < 6 ) {
4958             drawRectFilled( x, y, width, 6, g );
4959             getOvVirtualRectangle().setRect( x, y, width, 6 );
4960         }
4961         else {
4962             drawRect( x, y, width, height, g );
4963             if ( isInOvRect() ) {
4964                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
4965             }
4966             getOvVirtualRectangle().setRect( x, y, width, height );
4967         }
4968         g.setStroke( s );
4969     }
4970
4971     final private void paintPhylogenyLite( final Graphics2D g ) {
4972         _phylogeny
4973                 .getRoot()
4974                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
4975                         .getWidth() ) ) ) );
4976         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
4977         final Stroke s = g.getStroke();
4978         g.setStroke( STROKE_05 );
4979         for( final PhylogenyNode element : _nodes_in_preorder ) {
4980             paintNodeLite( g, element );
4981         }
4982         g.setStroke( s );
4983         paintOvRectangle( g );
4984     }
4985
4986     /**
4987      * Paint the root branch. (Differs from others because it will always be a
4988      * single horizontal line).
4989      * @param to_graphics_file 
4990      * 
4991      * @return new x1 value
4992      */
4993     final private void paintRootBranch( final Graphics2D g,
4994                                         final float x1,
4995                                         final float y1,
4996                                         final PhylogenyNode root,
4997                                         final boolean to_pdf,
4998                                         final boolean to_graphics_file ) {
4999         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
5000         float d = getXdistance();
5001         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
5002             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
5003         }
5004         if ( d < MIN_ROOT_LENGTH ) {
5005             d = MIN_ROOT_LENGTH;
5006         }
5007         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
5008             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
5009         }
5010         else {
5011             final double w = PhylogenyMethods.getBranchWidthValue( root );
5012             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
5013         }
5014         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file );
5015     }
5016
5017     final private void paintScale( final Graphics2D g,
5018                                    int x1,
5019                                    int y1,
5020                                    final boolean to_pdf,
5021                                    final boolean to_graphics_file ) {
5022         x1 += MOVE;
5023         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
5024         y1 -= 12;
5025         final int y2 = y1 - 8;
5026         final int y3 = y1 - 4;
5027         g.setFont( getTreeFontSet().getSmallFont() );
5028         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5029             g.setColor( Color.BLACK );
5030         }
5031         else {
5032             g.setColor( getTreeColorSet().getBranchLengthColor() );
5033         }
5034         final Stroke s = g.getStroke();
5035         g.setStroke( STROKE_1 );
5036         drawLine( x1, y1, x1, y2, g );
5037         drawLine( x2, y1, x2, y2, g );
5038         drawLine( x1, y3, x2, y3, g );
5039         if ( getScaleLabel() != null ) {
5040             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
5041         }
5042         g.setStroke( s );
5043     }
5044
5045     final private int paintTaxonomy( final Graphics2D g,
5046                                      final PhylogenyNode node,
5047                                      final boolean is_in_found_nodes,
5048                                      final boolean to_pdf,
5049                                      final boolean to_graphics_file,
5050                                      final float x_shift ) {
5051         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
5052         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
5053         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getTaxonomyColor() );
5054         final float start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
5055         float start_y;
5056         if ( !using_visual_font ) {
5057             start_y = node.getYcoord()
5058                     + ( getFontMetricsForLargeDefaultFont().getAscent() / ( node.getNumberOfDescendants() == 1 ? 1
5059                             : 3.0f ) );
5060         }
5061         else {
5062             start_y = node.getYcoord()
5063                     + ( getFontMetrics( g.getFont() ).getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
5064         }
5065         _sb.setLength( 0 );
5066         nodeTaxonomyDataAsSB( taxonomy, _sb );
5067         final String label = _sb.toString();
5068         /* GUILHEM_BEG */
5069         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
5070                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
5071             // invert font color and background color to show that this is the query sequence
5072             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
5073                                                                                                           false,
5074                                                                                                           false ) )
5075                     .getBounds();
5076             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
5077             g.setColor( getTreeColorSet().getBackgroundColor() );
5078         }
5079         /* GUILHEM_END */
5080         TreePanel.drawString( label, start_x, start_y, g );
5081         if ( !using_visual_font && !is_in_found_nodes ) {
5082             return getFontMetricsForLargeDefaultFont().stringWidth( label );
5083         }
5084         return getFontMetrics( g.getFont() ).stringWidth( label );
5085     }
5086
5087     final private void paintUnrooted( final PhylogenyNode n,
5088                                       final double low_angle,
5089                                       final double high_angle,
5090                                       final boolean radial_labels,
5091                                       final Graphics2D g,
5092                                       final boolean to_pdf,
5093                                       final boolean to_graphics_file ) {
5094         if ( n.isRoot() ) {
5095             n.setXcoord( getWidth() / 2 );
5096             n.setYcoord( getHeight() / 2 );
5097         }
5098         if ( n.isExternal() ) {
5099             paintNodeDataUnrootedCirc( g,
5100                                        n,
5101                                        to_pdf,
5102                                        to_graphics_file,
5103                                        radial_labels,
5104                                        ( high_angle + low_angle ) / 2,
5105                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
5106             return;
5107         }
5108         final float num_enclosed = n.getNumberOfExternalNodes();
5109         final float x = n.getXcoord();
5110         final float y = n.getYcoord();
5111         double current_angle = low_angle;
5112         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
5113         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
5114         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
5115         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
5116         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5117             final PhylogenyNode desc = n.getChildNode( i );
5118             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
5119             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
5120             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
5121             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
5122             //     continue;
5123             // }
5124             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
5125             //    continue;
5126             //}
5127             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
5128             //    continue;
5129             // }
5130             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5131             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5132             float length;
5133             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5134                 if ( desc.getDistanceToParent() < 0 ) {
5135                     length = 0;
5136                 }
5137                 else {
5138                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
5139                 }
5140             }
5141             else {
5142                 length = getUrtFactor();
5143             }
5144             final double mid_angle = current_angle + ( arc_size / 2 );
5145             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5146             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5147             desc.setXcoord( new_x );
5148             desc.setYcoord( new_y );
5149             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
5150             current_angle += arc_size;
5151             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
5152             drawLine( x, y, new_x, new_y, g );
5153             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file );
5154         }
5155         if ( n.isRoot() ) {
5156             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file );
5157         }
5158     }
5159
5160     final private void paintUnrootedLite( final PhylogenyNode n,
5161                                           final double low_angle,
5162                                           final double high_angle,
5163                                           final Graphics2D g,
5164                                           final float urt_ov_factor ) {
5165         if ( n.isRoot() ) {
5166             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
5167             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
5168             n.setXSecondary( x_pos );
5169             n.setYSecondary( y_pos );
5170         }
5171         if ( n.isExternal() ) {
5172             return;
5173         }
5174         final float num_enclosed = n.getNumberOfExternalNodes();
5175         final float x = n.getXSecondary();
5176         final float y = n.getYSecondary();
5177         double current_angle = low_angle;
5178         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5179             final PhylogenyNode desc = n.getChildNode( i );
5180             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5181             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5182             float length;
5183             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5184                 if ( desc.getDistanceToParent() < 0 ) {
5185                     length = 0;
5186                 }
5187                 else {
5188                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
5189                 }
5190             }
5191             else {
5192                 length = urt_ov_factor;
5193             }
5194             final double mid_angle = current_angle + ( arc_size / 2 );
5195             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5196             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5197             desc.setXSecondary( new_x );
5198             desc.setYSecondary( new_y );
5199             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
5200                 g.setColor( getColorForFoundNode( desc ) );
5201                 drawRectFilled( desc.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5202                                 desc.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5203                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5204                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5205                                 g );
5206                 g.setColor( getTreeColorSet().getOvColor() );
5207             }
5208             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
5209             current_angle += arc_size;
5210             drawLine( x, y, new_x, new_y, g );
5211         }
5212     }
5213
5214     final private void pasteSubtree( final PhylogenyNode node ) {
5215         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5216             errorMessageNoCutCopyPasteInUnrootedDisplay();
5217             return;
5218         }
5219         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
5220             JOptionPane.showMessageDialog( this,
5221                                            "No tree in buffer (need to copy or cut a subtree first)",
5222                                            "Attempt to paste with empty buffer",
5223                                            JOptionPane.ERROR_MESSAGE );
5224             return;
5225         }
5226         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
5227         final Object[] options = { "As sibling", "As descendant", "Cancel" };
5228         final int r = JOptionPane.showOptionDialog( this,
5229                                                     "How to paste subtree" + label + "?",
5230                                                     "Paste Subtree",
5231                                                     JOptionPane.CLOSED_OPTION,
5232                                                     JOptionPane.QUESTION_MESSAGE,
5233                                                     null,
5234                                                     options,
5235                                                     options[ 2 ] );
5236         boolean paste_as_sibling = true;
5237         if ( r == 1 ) {
5238             paste_as_sibling = false;
5239         }
5240         else if ( r != 0 ) {
5241             return;
5242         }
5243         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
5244         buffer_phy.setAllNodesToNotCollapse();
5245         PhylogenyMethods.preOrderReId( buffer_phy );
5246         buffer_phy.setRooted( true );
5247         boolean need_to_show_whole = false;
5248         if ( paste_as_sibling ) {
5249             if ( node.isRoot() ) {
5250                 JOptionPane.showMessageDialog( this,
5251                                                "Cannot paste sibling to root",
5252                                                "Attempt to paste sibling to root",
5253                                                JOptionPane.ERROR_MESSAGE );
5254                 return;
5255             }
5256             buffer_phy.addAsSibling( node );
5257         }
5258         else {
5259             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
5260                 need_to_show_whole = true;
5261                 _phylogeny = buffer_phy;
5262             }
5263             else {
5264                 buffer_phy.addAsChild( node );
5265             }
5266         }
5267         if ( getCopiedAndPastedNodes() == null ) {
5268             setCopiedAndPastedNodes( new HashSet<Long>() );
5269         }
5270         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
5271         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
5272         for( final PhylogenyNode n : nodes ) {
5273             node_ids.add( n.getId() );
5274         }
5275         node_ids.add( node.getId() );
5276         getCopiedAndPastedNodes().addAll( node_ids );
5277         setNodeInPreorderToNull();
5278         _phylogeny.externalNodesHaveChanged();
5279         _phylogeny.clearHashIdToNodeMap();
5280         _phylogeny.recalculateNumberOfExternalDescendants( true );
5281         resetNodeIdToDistToLeafMap();
5282         setEdited( true );
5283         if ( need_to_show_whole ) {
5284             getControlPanel().showWhole();
5285         }
5286         repaint();
5287     }
5288
5289     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
5290         final PropertiesMap properties = node.getNodeData().getProperties();
5291         final StringBuffer sb = new StringBuffer();
5292         boolean first = true;
5293         for( final String ref : properties.getPropertyRefs() ) {
5294             if ( first ) {
5295                 first = false;
5296             }
5297             else {
5298                 sb.append( " " );
5299             }
5300             final Property p = properties.getProperty( ref );
5301             sb.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5302             sb.append( "=" );
5303             sb.append( p.getValue() );
5304             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5305                 sb.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5306             }
5307         }
5308         return sb;
5309     }
5310
5311     private void setColor( final Graphics2D g,
5312                            final PhylogenyNode node,
5313                            final boolean to_graphics_file,
5314                            final boolean to_pdf,
5315                            final boolean is_in_found_nodes,
5316                            final Color default_color ) {
5317         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5318             g.setColor( Color.BLACK );
5319         }
5320         else if ( is_in_found_nodes ) {
5321             g.setColor( getColorForFoundNode( node ) );
5322         }
5323         else if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
5324                 && ( node.getNodeData().getNodeVisualData().getFontColor() != null ) ) {
5325             g.setColor( node.getNodeData().getNodeVisualData().getFontColor() );
5326         }
5327         else if ( getControlPanel().isColorAccordingToSequence() ) {
5328             g.setColor( getSequenceBasedColor( node ) );
5329         }
5330         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5331             g.setColor( getTaxonomyBasedColor( node ) );
5332         }
5333         else if ( getControlPanel().isColorAccordingToAnnotation()
5334                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
5335                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
5336             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
5337         }
5338         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
5339                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
5340             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
5341         }
5342         else if ( to_pdf ) {
5343             g.setColor( Color.BLACK );
5344         }
5345         else {
5346             g.setColor( default_color );
5347         }
5348     }
5349
5350     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
5351         getMainPanel().setCopiedAndPastedNodes( nodeIds );
5352     }
5353
5354     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
5355         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
5356     }
5357
5358     private boolean setFont( final Graphics2D g, final PhylogenyNode node, final boolean is_in_found_nodes ) {
5359         Font visual_font = null;
5360         if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null ) ) {
5361             visual_font = node.getNodeData().getNodeVisualData().getFont();
5362             g.setFont( visual_font != null ? visual_font : getTreeFontSet().getLargeFont() );
5363         }
5364         else {
5365             g.setFont( getTreeFontSet().getLargeFont() );
5366         }
5367         if ( is_in_found_nodes ) {
5368             g.setFont( g.getFont().deriveFont( Font.BOLD ) );
5369         }
5370         return visual_font != null;
5371     }
5372
5373     final private void setInOv( final boolean in_ov ) {
5374         _in_ov = in_ov;
5375     }
5376
5377     final private void setOvMaxHeight( final float ov_max_height ) {
5378         _ov_max_height = ov_max_height;
5379     }
5380
5381     final private void setOvMaxWidth( final float ov_max_width ) {
5382         _ov_max_width = ov_max_width;
5383     }
5384
5385     final private void setOvXcorrectionFactor( final float f ) {
5386         _ov_x_correction_factor = f;
5387     }
5388
5389     final private void setOvXDistance( final float ov_x_distance ) {
5390         _ov_x_distance = ov_x_distance;
5391     }
5392
5393     final private void setOvXPosition( final int ov_x_position ) {
5394         _ov_x_position = ov_x_position;
5395     }
5396
5397     final private void setOvYDistance( final float ov_y_distance ) {
5398         _ov_y_distance = ov_y_distance;
5399     }
5400
5401     final private void setOvYPosition( final int ov_y_position ) {
5402         _ov_y_position = ov_y_position;
5403     }
5404
5405     final private void setOvYStart( final int ov_y_start ) {
5406         _ov_y_start = ov_y_start;
5407     }
5408
5409     final private void setScaleDistance( final double scale_distance ) {
5410         _scale_distance = scale_distance;
5411     }
5412
5413     final private void setScaleLabel( final String scale_label ) {
5414         _scale_label = scale_label;
5415     }
5416
5417     private final void setupStroke( final Graphics2D g ) {
5418         if ( getYdistance() < 0.001 ) {
5419             g.setStroke( STROKE_005 );
5420         }
5421         else if ( getYdistance() < 0.01 ) {
5422             g.setStroke( STROKE_01 );
5423         }
5424         else if ( getYdistance() < 0.5 ) {
5425             g.setStroke( STROKE_025 );
5426         }
5427         else if ( getYdistance() < 1 ) {
5428             g.setStroke( STROKE_05 );
5429         }
5430         else if ( getYdistance() < 2 ) {
5431             g.setStroke( STROKE_075 );
5432         }
5433         else if ( getYdistance() < 20 ) {
5434             g.setStroke( STROKE_1 );
5435         }
5436         else {
5437             g.setStroke( STROKE_2 );
5438         }
5439     }
5440
5441     final private void setUpUrtFactor() {
5442         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
5443                 : getVisibleRect().height;
5444         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5445             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
5446         }
5447         else {
5448             final int max_depth = _circ_max_depth;
5449             if ( max_depth > 0 ) {
5450                 setUrtFactor( d / ( 2 * max_depth ) );
5451             }
5452             else {
5453                 setUrtFactor( d / 2 );
5454             }
5455         }
5456         setUrtFactorOv( getUrtFactor() );
5457     }
5458
5459     final private void setUrtFactor( final float urt_factor ) {
5460         _urt_factor = urt_factor;
5461     }
5462
5463     final private void setUrtFactorOv( final float urt_factor_ov ) {
5464         _urt_factor_ov = urt_factor_ov;
5465     }
5466
5467     private void showExtDescNodeData( final PhylogenyNode node ) {
5468         final List<String> data = new ArrayList<String>();
5469         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
5470         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
5471             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
5472                 if ( !nodes.contains( n ) ) {
5473                     nodes.add( n );
5474                 }
5475             }
5476         }
5477         for( final PhylogenyNode n : nodes ) {
5478             switch ( getOptions().getExtDescNodeDataToReturn() ) {
5479                 case NODE_NAME:
5480                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5481                         data.add( n.getName() );
5482                     }
5483                     break;
5484                 case SEQUENCE_NAME:
5485                     if ( n.getNodeData().isHasSequence()
5486                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5487                         data.add( n.getNodeData().getSequence().getName() );
5488                     }
5489                     break;
5490                 case GENE_NAME:
5491                     if ( n.getNodeData().isHasSequence()
5492                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5493                         data.add( n.getNodeData().getSequence().getGeneName() );
5494                     }
5495                     break;
5496                 case SEQUENCE_SYMBOL:
5497                     if ( n.getNodeData().isHasSequence()
5498                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5499                         data.add( n.getNodeData().getSequence().getSymbol() );
5500                     }
5501                     break;
5502                 case SEQUENCE_MOL_SEQ:
5503                     if ( n.getNodeData().isHasSequence()
5504                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5505                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
5506                     }
5507                     break;
5508                 case SEQUENCE_MOL_SEQ_FASTA:
5509                     final StringBuilder sb = new StringBuilder();
5510                     if ( n.getNodeData().isHasSequence()
5511                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5512                         final StringBuilder ann = new StringBuilder();
5513                         if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5514                             ann.append( n.getName() );
5515                             ann.append( "|" );
5516                         }
5517                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5518                             ann.append( "SYM=" );
5519                             ann.append( n.getNodeData().getSequence().getSymbol() );
5520                             ann.append( "|" );
5521                         }
5522                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5523                             ann.append( "NAME=" );
5524                             ann.append( n.getNodeData().getSequence().getName() );
5525                             ann.append( "|" );
5526                         }
5527                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5528                             ann.append( "GN=" );
5529                             ann.append( n.getNodeData().getSequence().getGeneName() );
5530                             ann.append( "|" );
5531                         }
5532                         if ( n.getNodeData().getSequence().getAccession() != null ) {
5533                             ann.append( "ACC=" );
5534                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
5535                             ann.append( "|" );
5536                         }
5537                         if ( n.getNodeData().isHasTaxonomy() ) {
5538                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5539                                 ann.append( "TAXID=" );
5540                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5541                                 ann.append( "|" );
5542                             }
5543                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5544                                 ann.append( "SN=" );
5545                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
5546                                 ann.append( "|" );
5547                             }
5548                         }
5549                         String ann_str;
5550                         if ( ann.charAt( ann.length() - 1 ) == '|' ) {
5551                             ann_str = ann.substring( 0, ann.length() - 1 );
5552                         }
5553                         else {
5554                             ann_str = ann.toString();
5555                         }
5556                         sb.append( SequenceWriter.toFasta( ann_str, n.getNodeData().getSequence()
5557                                 .getMolecularSequence(), 60 ) );
5558                         data.add( sb.toString() );
5559                     }
5560                     break;
5561                 case SEQUENCE_ACC:
5562                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
5563                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
5564                         data.add( n.getNodeData().getSequence().getAccession().toString() );
5565                     }
5566                     break;
5567                 case TAXONOMY_SCIENTIFIC_NAME:
5568                     if ( n.getNodeData().isHasTaxonomy()
5569                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5570                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
5571                     }
5572                     break;
5573                 case TAXONOMY_COMM0N_NAME:
5574                     if ( n.getNodeData().isHasTaxonomy()
5575                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) ) {
5576                         data.add( n.getNodeData().getTaxonomy().getCommonName() );
5577                     }
5578                     break;
5579                 case TAXONOMY_CODE:
5580                     if ( n.getNodeData().isHasTaxonomy()
5581                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5582                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5583                     }
5584                     break;
5585                 case UNKNOWN:
5586                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
5587                     break;
5588                 default:
5589                     throw new IllegalArgumentException( "unknown data element: "
5590                             + getOptions().getExtDescNodeDataToReturn() );
5591             }
5592         } // for loop
5593         final StringBuilder sb = new StringBuilder();
5594         final int size = TreePanelUtil.makeSB( data, getOptions(), sb );
5595         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
5596                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
5597             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5598                 System.out.println( sb );
5599             }
5600             if ( sb.length() < 1 ) {
5601                 clearCurrentExternalNodesDataBuffer();
5602             }
5603             else {
5604                 setCurrentExternalNodesDataBuffer( sb );
5605             }
5606         }
5607         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5608             if ( sb.length() < 1 ) {
5609                 TreePanelUtil.showInformationMessage( this, "No Appropriate Data (" + obtainTitleForExtDescNodeData()
5610                         + ")", "Descendants of selected node do not contain selected data" );
5611                 clearCurrentExternalNodesDataBuffer();
5612             }
5613             else {
5614                 setCurrentExternalNodesDataBuffer( sb );
5615                 String title;
5616                 if ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() ) {
5617                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5618                             : obtainTitleForExtDescNodeData() )
5619                             + " for "
5620                             + data.size()
5621                             + " nodes, unique entries: "
5622                             + size;
5623                 }
5624                 else {
5625                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5626                             : obtainTitleForExtDescNodeData() )
5627                             + " for "
5628                             + data.size()
5629                             + "/"
5630                             + node.getNumberOfExternalNodes()
5631                             + " external descendats of node "
5632                             + node
5633                             + ", unique entries: " + size;
5634                 }
5635                 final String s = sb.toString().trim();
5636                 if ( getMainPanel().getMainFrame() == null ) {
5637                     // Must be "E" applet version.
5638                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5639                     ae.showTextFrame( s, title );
5640                 }
5641                 else {
5642                     getMainPanel().getMainFrame().showTextFrame( s, title );
5643                 }
5644             }
5645         }
5646     }
5647
5648     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5649         try {
5650             if ( ( node.getName().length() > 0 )
5651                     || ( node.getNodeData().isHasTaxonomy() && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData()
5652                             .getTaxonomy() ) )
5653                     || ( node.getNodeData().isHasSequence() && !TreePanelUtil.isSequenceEmpty( node.getNodeData()
5654                             .getSequence() ) ) || ( node.getNodeData().isHasDate() )
5655                     || ( node.getNodeData().isHasDistribution() ) || node.getBranchData().isHasConfidences() ) {
5656                 _popup_buffer.setLength( 0 );
5657                 short lines = 0;
5658                 if ( node.getName().length() > 0 ) {
5659                     lines++;
5660                     _popup_buffer.append( node.getName() );
5661                 }
5662                 if ( node.getNodeData().isHasTaxonomy()
5663                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5664                     lines++;
5665                     boolean enc_data = false;
5666                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5667                     if ( _popup_buffer.length() > 0 ) {
5668                         _popup_buffer.append( "\n" );
5669                     }
5670                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5671                         _popup_buffer.append( "[" );
5672                         _popup_buffer.append( tax.getTaxonomyCode() );
5673                         _popup_buffer.append( "]" );
5674                         enc_data = true;
5675                     }
5676                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5677                         if ( enc_data ) {
5678                             _popup_buffer.append( " " );
5679                         }
5680                         _popup_buffer.append( tax.getScientificName() );
5681                         enc_data = true;
5682                     }
5683                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5684                         if ( enc_data ) {
5685                             _popup_buffer.append( " (" );
5686                         }
5687                         else {
5688                             _popup_buffer.append( "(" );
5689                         }
5690                         _popup_buffer.append( tax.getCommonName() );
5691                         _popup_buffer.append( ")" );
5692                         enc_data = true;
5693                     }
5694                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5695                         if ( enc_data ) {
5696                             _popup_buffer.append( " (" );
5697                         }
5698                         else {
5699                             _popup_buffer.append( "(" );
5700                         }
5701                         _popup_buffer.append( tax.getAuthority() );
5702                         _popup_buffer.append( ")" );
5703                         enc_data = true;
5704                     }
5705                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5706                         if ( enc_data ) {
5707                             _popup_buffer.append( " [" );
5708                         }
5709                         else {
5710                             _popup_buffer.append( "[" );
5711                         }
5712                         _popup_buffer.append( tax.getRank() );
5713                         _popup_buffer.append( "]" );
5714                         enc_data = true;
5715                     }
5716                     if ( tax.getSynonyms().size() > 0 ) {
5717                         if ( enc_data ) {
5718                             _popup_buffer.append( " " );
5719                         }
5720                         _popup_buffer.append( "[" );
5721                         int counter = 1;
5722                         for( final String syn : tax.getSynonyms() ) {
5723                             if ( !ForesterUtil.isEmpty( syn ) ) {
5724                                 enc_data = true;
5725                                 _popup_buffer.append( syn );
5726                                 if ( counter < tax.getSynonyms().size() ) {
5727                                     _popup_buffer.append( ", " );
5728                                 }
5729                             }
5730                             counter++;
5731                         }
5732                         _popup_buffer.append( "]" );
5733                     }
5734                     if ( !enc_data ) {
5735                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5736                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5737                                 _popup_buffer.append( "[" );
5738                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5739                                 _popup_buffer.append( "] " );
5740                             }
5741                             _popup_buffer.append( tax.getIdentifier().getValue() );
5742                         }
5743                     }
5744                 }
5745                 if ( node.getNodeData().isHasSequence()
5746                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5747                     lines++;
5748                     boolean enc_data = false;
5749                     if ( _popup_buffer.length() > 0 ) {
5750                         _popup_buffer.append( "\n" );
5751                     }
5752                     final Sequence seq = node.getNodeData().getSequence();
5753                     if ( seq.getAccession() != null ) {
5754                         _popup_buffer.append( "[" );
5755                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5756                             _popup_buffer.append( seq.getAccession().getSource() );
5757                             _popup_buffer.append( ":" );
5758                         }
5759                         _popup_buffer.append( seq.getAccession().getValue() );
5760                         _popup_buffer.append( "]" );
5761                         enc_data = true;
5762                     }
5763                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5764                         if ( enc_data ) {
5765                             _popup_buffer.append( " [" );
5766                         }
5767                         else {
5768                             _popup_buffer.append( "[" );
5769                         }
5770                         _popup_buffer.append( seq.getSymbol() );
5771                         _popup_buffer.append( "]" );
5772                         enc_data = true;
5773                     }
5774                     if ( !ForesterUtil.isEmpty( seq.getGeneName() ) ) {
5775                         if ( enc_data ) {
5776                             _popup_buffer.append( " [" );
5777                         }
5778                         else {
5779                             _popup_buffer.append( "[" );
5780                         }
5781                         _popup_buffer.append( seq.getGeneName() );
5782                         _popup_buffer.append( "]" );
5783                         enc_data = true;
5784                     }
5785                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5786                         if ( enc_data ) {
5787                             _popup_buffer.append( " " );
5788                         }
5789                         _popup_buffer.append( seq.getName() );
5790                     }
5791                 }
5792                 if ( node.getNodeData().isHasDate() ) {
5793                     lines++;
5794                     if ( _popup_buffer.length() > 0 ) {
5795                         _popup_buffer.append( "\n" );
5796                     }
5797                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5798                 }
5799                 if ( node.getNodeData().isHasDistribution() ) {
5800                     lines++;
5801                     if ( _popup_buffer.length() > 0 ) {
5802                         _popup_buffer.append( "\n" );
5803                     }
5804                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5805                 }
5806                 if ( node.getBranchData().isHasConfidences() ) {
5807                     final List<Confidence> confs = node.getBranchData().getConfidences();
5808                     for( final Confidence confidence : confs ) {
5809                         lines++;
5810                         if ( _popup_buffer.length() > 0 ) {
5811                             _popup_buffer.append( "\n" );
5812                         }
5813                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5814                             _popup_buffer.append( "[" );
5815                             _popup_buffer.append( confidence.getType() );
5816                             _popup_buffer.append( "] " );
5817                         }
5818                         _popup_buffer
5819                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5820                                                                                           getOptions()
5821                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5822                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5823                             _popup_buffer.append( " (sd=" );
5824                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5825                                     .getStandardDeviation(), getOptions()
5826                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5827                             _popup_buffer.append( ")" );
5828                         }
5829                     }
5830                 }
5831                 if ( node.getNodeData().isHasProperties() ) {
5832                     final PropertiesMap properties = node.getNodeData().getProperties();
5833                     for( final String ref : properties.getPropertyRefs() ) {
5834                         _popup_buffer.append( "\n" );
5835                         final Property p = properties.getProperty( ref );
5836                         _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5837                         _popup_buffer.append( "=" );
5838                         _popup_buffer.append( p.getValue() );
5839                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5840                             _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5841                         }
5842                     }
5843                 }
5844                 if ( _popup_buffer.length() > 0 ) {
5845                     if ( !getConfiguration().isUseNativeUI() ) {
5846                         _rollover_popup
5847                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5848                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5849                         if ( isInFoundNodes0( node ) && !isInFoundNodes1( node ) ) {
5850                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0() );
5851                         }
5852                         else if ( !isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5853                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor1() );
5854                         }
5855                         else if ( isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5856                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0and1() );
5857                         }
5858                         else {
5859                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5860                         }
5861                     }
5862                     else {
5863                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5864                     }
5865                     _rollover_popup.setText( _popup_buffer.toString() );
5866                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5867                                                                                   _rollover_popup,
5868                                                                                   e.getLocationOnScreen().x + 10,
5869                                                                                   e.getLocationOnScreen().y
5870                                                                                           - ( lines * 20 ) );
5871                     _node_desc_popup.show();
5872                 }
5873             }
5874         }
5875         catch ( final Exception ex ) {
5876             // Do nothing.
5877         }
5878     }
5879
5880     final private void showNodeEditFrame( final PhylogenyNode n ) {
5881         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5882             // pop up edit box for single node
5883             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5884             _node_frame_index++;
5885         }
5886         else {
5887             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5888         }
5889     }
5890
5891     final private void showNodeFrame( final PhylogenyNode n ) {
5892         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5893             // pop up edit box for single node
5894             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5895             _node_frame_index++;
5896         }
5897         else {
5898             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5899         }
5900     }
5901
5902     final private void switchDisplaygetPhylogenyGraphicsType() {
5903         switch ( getPhylogenyGraphicsType() ) {
5904             case RECTANGULAR:
5905                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5906                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5907                 break;
5908             case EURO_STYLE:
5909                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5910                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5911                 break;
5912             case ROUNDED:
5913                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5914                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5915                 break;
5916             case CURVED:
5917                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5918                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5919                 break;
5920             case TRIANGULAR:
5921                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5922                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5923                 break;
5924             case CONVEX:
5925                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5926                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5927                 break;
5928             case UNROOTED:
5929                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5930                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5931                 break;
5932             case CIRCULAR:
5933                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5934                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5935                 break;
5936             default:
5937                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5938         }
5939         if ( getControlPanel().getDynamicallyHideData() != null ) {
5940             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5941                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5942             }
5943             else {
5944                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5945             }
5946         }
5947         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5948             getControlPanel().setDrawPhylogramEnabled( true );
5949         }
5950         else {
5951             getControlPanel().setDrawPhylogramEnabled( false );
5952         }
5953         if ( getMainPanel().getMainFrame() == null ) {
5954             // Must be "E" applet version.
5955             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5956                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5957         }
5958         else {
5959             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5960         }
5961     }
5962
5963     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
5964         if ( node.getNodeData().getNodeVisualData() == null ) {
5965             node.getNodeData().setNodeVisualData( new NodeVisualData() );
5966         }
5967         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
5968     }
5969
5970     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
5971         g.drawString( str, x, y );
5972     }
5973
5974     final private static boolean plusPressed( final int key_code ) {
5975         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5976                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5977     }
5978
5979     final private class NodeColorizationActionListener implements ActionListener {
5980
5981         List<PhylogenyNode> _additional_nodes = null;
5982         JColorChooser       _chooser          = null;
5983         PhylogenyNode       _node             = null;
5984
5985         NodeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5986             _chooser = chooser;
5987             _node = node;
5988         }
5989
5990         NodeColorizationActionListener( final JColorChooser chooser,
5991                                         final PhylogenyNode node,
5992                                         final List<PhylogenyNode> additional_nodes ) {
5993             _chooser = chooser;
5994             _node = node;
5995             _additional_nodes = additional_nodes;
5996         }
5997
5998         @Override
5999         public void actionPerformed( final ActionEvent e ) {
6000             final Color c = _chooser.getColor();
6001             if ( c != null ) {
6002                 colorizeNodes( c, _node, _additional_nodes );
6003             }
6004         }
6005     }
6006
6007     final private class SubtreeColorizationActionListener implements ActionListener {
6008
6009         List<PhylogenyNode> _additional_nodes = null;
6010         JColorChooser       _chooser          = null;
6011         PhylogenyNode       _node             = null;
6012
6013         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
6014             _chooser = chooser;
6015             _node = node;
6016         }
6017
6018         SubtreeColorizationActionListener( final JColorChooser chooser,
6019                                            final PhylogenyNode node,
6020                                            final List<PhylogenyNode> additional_nodes ) {
6021             _chooser = chooser;
6022             _node = node;
6023             _additional_nodes = additional_nodes;
6024         }
6025
6026         @Override
6027         public void actionPerformed( final ActionEvent e ) {
6028             final Color c = _chooser.getColor();
6029             if ( c != null ) {
6030                 colorizeSubtree( c, _node, _additional_nodes );
6031             }
6032         }
6033     }
6034 }