special domain coloring
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanel.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.BasicStroke;
29 import java.awt.Color;
30 import java.awt.Cursor;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.FontMetrics;
34 import java.awt.GradientPaint;
35 import java.awt.Graphics;
36 import java.awt.Graphics2D;
37 import java.awt.Point;
38 import java.awt.Rectangle;
39 import java.awt.RenderingHints;
40 import java.awt.Stroke;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.awt.event.FocusAdapter;
44 import java.awt.event.FocusEvent;
45 import java.awt.event.InputEvent;
46 import java.awt.event.KeyAdapter;
47 import java.awt.event.KeyEvent;
48 import java.awt.event.MouseEvent;
49 import java.awt.event.MouseWheelEvent;
50 import java.awt.event.MouseWheelListener;
51 import java.awt.font.FontRenderContext;
52 import java.awt.font.TextLayout;
53 import java.awt.geom.AffineTransform;
54 import java.awt.geom.Arc2D;
55 import java.awt.geom.CubicCurve2D;
56 import java.awt.geom.Ellipse2D;
57 import java.awt.geom.Line2D;
58 import java.awt.geom.Path2D;
59 import java.awt.geom.QuadCurve2D;
60 import java.awt.geom.Rectangle2D;
61 import java.awt.image.BufferedImage;
62 import java.awt.print.PageFormat;
63 import java.awt.print.Printable;
64 import java.awt.print.PrinterException;
65 import java.io.File;
66 import java.io.IOException;
67 import java.io.UnsupportedEncodingException;
68 import java.net.URI;
69 import java.net.URISyntaxException;
70 import java.net.URLEncoder;
71 import java.text.DecimalFormat;
72 import java.text.DecimalFormatSymbols;
73 import java.text.NumberFormat;
74 import java.util.ArrayList;
75 import java.util.Collections;
76 import java.util.HashMap;
77 import java.util.HashSet;
78 import java.util.Hashtable;
79 import java.util.List;
80 import java.util.Set;
81 import java.util.SortedSet;
82
83 import javax.swing.BorderFactory;
84 import javax.swing.JApplet;
85 import javax.swing.JColorChooser;
86 import javax.swing.JDialog;
87 import javax.swing.JMenuItem;
88 import javax.swing.JOptionPane;
89 import javax.swing.JPanel;
90 import javax.swing.JPopupMenu;
91 import javax.swing.JTextArea;
92 import javax.swing.Popup;
93 import javax.swing.PopupFactory;
94
95 import org.forester.archaeopteryx.Configuration.EXT_NODE_DATA_RETURN_ON;
96 import org.forester.archaeopteryx.ControlPanel.NodeClickAction;
97 import org.forester.archaeopteryx.Options.CLADOGRAM_TYPE;
98 import org.forester.archaeopteryx.Options.NODE_LABEL_DIRECTION;
99 import org.forester.archaeopteryx.Options.PHYLOGENY_GRAPHICS_TYPE;
100 import org.forester.archaeopteryx.phylogeny.data.RenderableDomainArchitecture;
101 import org.forester.archaeopteryx.phylogeny.data.RenderableMsaSequence;
102 import org.forester.archaeopteryx.phylogeny.data.RenderableVector;
103 import org.forester.archaeopteryx.tools.Blast;
104 import org.forester.archaeopteryx.tools.ImageLoader;
105 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
106 import org.forester.io.writers.SequenceWriter;
107 import org.forester.phylogeny.Phylogeny;
108 import org.forester.phylogeny.PhylogenyMethods;
109 import org.forester.phylogeny.PhylogenyMethods.DESCENDANT_SORT_PRIORITY;
110 import org.forester.phylogeny.PhylogenyNode;
111 import org.forester.phylogeny.data.Accession;
112 import org.forester.phylogeny.data.Annotation;
113 import org.forester.phylogeny.data.BranchColor;
114 import org.forester.phylogeny.data.Confidence;
115 import org.forester.phylogeny.data.DomainArchitecture;
116 import org.forester.phylogeny.data.Event;
117 import org.forester.phylogeny.data.NodeData.NODE_DATA;
118 import org.forester.phylogeny.data.NodeVisualData;
119 import org.forester.phylogeny.data.NodeVisualData.NodeFill;
120 import org.forester.phylogeny.data.NodeVisualData.NodeShape;
121 import org.forester.phylogeny.data.PhylogenyDataUtil;
122 import org.forester.phylogeny.data.PropertiesMap;
123 import org.forester.phylogeny.data.Property;
124 import org.forester.phylogeny.data.Sequence;
125 import org.forester.phylogeny.data.SequenceRelation;
126 import org.forester.phylogeny.data.Taxonomy;
127 import org.forester.phylogeny.data.Uri;
128 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
129 import org.forester.phylogeny.iterators.PreorderTreeIterator;
130 import org.forester.util.BasicDescriptiveStatistics;
131 import org.forester.util.DescriptiveStatistics;
132 import org.forester.util.ForesterConstants;
133 import org.forester.util.ForesterUtil;
134 import org.forester.util.SequenceAccessionTools;
135 import org.forester.util.TaxonomyUtil;
136
137 public final class TreePanel extends JPanel implements ActionListener, MouseWheelListener, Printable {
138
139     final static Cursor                  ARROW_CURSOR                                       = Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR );
140     final static Cursor                  CUT_CURSOR                                         = Cursor.getPredefinedCursor( Cursor.CROSSHAIR_CURSOR );
141     final static Cursor                  HAND_CURSOR                                        = Cursor.getPredefinedCursor( Cursor.HAND_CURSOR );
142     final static Cursor                  MOVE_CURSOR                                        = Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR );
143     final static Cursor                  WAIT_CURSOR                                        = Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR );
144     final private static double          _180_OVER_PI                                       = 180.0 / Math.PI;
145     private static final float           ANGLE_ROTATION_UNIT                                = ( float ) ( Math.PI / 32 );
146     private final static int             CONFIDENCE_LEFT_MARGIN                             = 4;
147     private final static int             EURO_D                                             = 10;
148     private final static NumberFormat    FORMATTER_BRANCH_LENGTH;
149     private final static NumberFormat    FORMATTER_CONFIDENCE;
150     private static final float           HALF_PI                                            = ( float ) ( Math.PI / 2.0 );
151     private final static int             LIMIT_FOR_HQ_RENDERING                             = 2000;
152     private final static int             MAX_NODE_FRAMES                                    = 10;
153     private final static int             MAX_SUBTREES                                       = 100;
154     private final static int             MIN_ROOT_LENGTH                                    = 3;
155     private final static int             MOVE                                               = 20;
156     private final static String          NODE_POPMENU_NODE_CLIENT_PROPERTY                  = "node";
157     private static final float           ONEHALF_PI                                         = ( float ) ( 1.5 * Math.PI );
158     private static final short           OV_BORDER                                          = 10;
159     private final static double          OVERVIEW_FOUND_NODE_BOX_SIZE                       = 2;
160     private final static double          OVERVIEW_FOUND_NODE_BOX_SIZE_HALF                  = 1;
161     private static final float           PI                                                 = ( float ) ( Math.PI );
162     final private static Font            POPUP_FONT                                         = new Font( Configuration.getDefaultFontFamilyName(),
163                                                                                                         Font.PLAIN,
164                                                                                                         12 );
165     private static final float           ROUNDED_D                                          = 8;
166     private final static long            serialVersionUID                                   = -978349745916505029L;
167     private static final BasicStroke     STROKE_005                                         = new BasicStroke( 0.05f );
168     private static final BasicStroke     STROKE_01                                          = new BasicStroke( 0.1f );
169     private static final BasicStroke     STROKE_025                                         = new BasicStroke( 0.25f );
170     private static final BasicStroke     STROKE_05                                          = new BasicStroke( 0.5f );
171     private static final BasicStroke     STROKE_075                                         = new BasicStroke( 0.75f );
172     private static final BasicStroke     STROKE_1                                           = new BasicStroke( 1f );
173     private static final BasicStroke     STROKE_2                                           = new BasicStroke( 2f );
174     private static final double          TWO_PI                                             = 2 * Math.PI;
175     private final static int             WIGGLE                                             = 2;
176     private static final String          SHOW_ONLY_THIS_CONF_TYPE                           = null;                                                     //TODO remove me
177     HashMap<Long, Short>                 _nodeid_dist_to_leaf                               = new HashMap<Long, Short>();
178     final private Arc2D                  _arc                                               = new Arc2D.Double();
179     private AffineTransform              _at;
180     private int                          _circ_max_depth;
181     final private Set<Long>              _collapsed_external_nodeid_set                     = new HashSet<Long>();
182     private JColorChooser                _color_chooser                                     = null;
183     private Configuration                _configuration                                     = null;
184     private ControlPanel                 _control_panel                                     = null;
185     private final CubicCurve2D           _cubic_curve                                       = new CubicCurve2D.Float();
186     private Set<Long>                    _current_external_nodes                            = null;
187     private StringBuilder                _current_external_nodes_data_buffer                = new StringBuilder();
188     private int                          _current_external_nodes_data_buffer_change_counter = 0;
189     private int                          _domain_structure_e_value_thr_exp                  = Constants.DOMAIN_STRUCTURE_E_VALUE_THR_DEFAULT_EXP;
190     private double                       _domain_structure_width                            = Constants.DOMAIN_STRUCTURE_DEFAULT_WIDTH;
191     private int                          _dynamic_hiding_factor                             = 0;
192     private boolean                      _edited                                            = false;
193     private final Ellipse2D              _ellipse                                           = new Ellipse2D.Float();
194     private int                          _external_node_index                               = 0;
195     private Set<Long>                    _found_nodes_0                                     = null;
196     private Set<Long>                    _found_nodes_1                                     = null;
197     private final FontRenderContext      _frc                                               = new FontRenderContext( null,
198                                                                                                                      false,
199                                                                                                                      false );
200     private PHYLOGENY_GRAPHICS_TYPE      _graphics_type                                     = PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR;
201     private PhylogenyNode                _highlight_node                                    = null;
202     private boolean                      _in_ov                                             = false;
203     private boolean                      _in_ov_rect                                        = false;
204     private float                        _last_drag_point_x                                 = 0;
205     private float                        _last_drag_point_y                                 = 0;
206     private final Line2D                 _line                                              = new Line2D.Float();
207     private int                          _longest_ext_node_info                             = 0;
208     private PhylogenyNode                _ext_node_with_longest_txt_info                    = null;
209     private MainPanel                    _main_panel                                        = null;
210     private double                       _max_distance_to_root                              = -1;
211     private Popup                        _node_desc_popup;
212     private int                          _node_frame_index                                  = 0;
213     private final NodeFrame[]            _node_frames                                       = new NodeFrame[ TreePanel.MAX_NODE_FRAMES ];
214     private JPopupMenu                   _node_popup_menu                                   = null;
215     private JMenuItem                    _node_popup_menu_items[]                           = null;
216     private PhylogenyNode[]              _nodes_in_preorder                                 = null;
217     private Options                      _options                                           = null;
218     private float                        _ov_max_height                                     = 0;
219     private float                        _ov_max_width                                      = 0;
220     private boolean                      _ov_on                                             = false;
221     private final Rectangle2D            _ov_rectangle                                      = new Rectangle2D.Float();
222     private final Rectangle              _ov_virtual_rectangle                              = new Rectangle();
223     private float                        _ov_x_correction_factor                            = 0.0f;
224     private float                        _ov_x_distance                                     = 0;
225     private int                          _ov_x_position                                     = 0;
226     private float                        _ov_y_distance                                     = 0;
227     private int                          _ov_y_position                                     = 0;
228     private int                          _ov_y_start                                        = 0;
229     private final boolean                _phy_has_branch_lengths;
230     private Phylogeny                    _phylogeny                                         = null;
231     private final Path2D.Float           _polygon                                           = new Path2D.Float();
232     private final StringBuffer           _popup_buffer                                      = new StringBuffer();
233     private final QuadCurve2D            _quad_curve                                        = new QuadCurve2D.Float();
234     private Sequence                     _query_sequence                                    = null;
235     private final Rectangle2D            _rectangle                                         = new Rectangle2D.Float();
236     private final RenderingHints         _rendering_hints                                   = new RenderingHints( RenderingHints.KEY_RENDERING,
237                                                                                                                   RenderingHints.VALUE_RENDER_DEFAULT );
238     private JTextArea                    _rollover_popup;
239     private PhylogenyNode                _root;
240     private final StringBuilder          _sb                                                = new StringBuilder();
241     private double                       _scale_distance                                    = 0.0;
242     private String                       _scale_label                                       = null;
243     private DescriptiveStatistics        _statistics_for_vector_data;
244     private final Phylogeny[]            _sub_phylogenies                                   = new Phylogeny[ TreePanel.MAX_SUBTREES ];
245     private final PhylogenyNode[]        _sub_phylogenies_temp_roots                        = new PhylogenyNode[ TreePanel.MAX_SUBTREES ];
246     private int                          _subtree_index                                     = 0;
247     private File                         _treefile                                          = null;
248     private float                        _urt_factor                                        = 1;
249     private float                        _urt_factor_ov                                     = 1;
250     final private HashMap<Long, Double>  _urt_nodeid_angle_map                              = new HashMap<Long, Double>();
251     final private HashMap<Long, Integer> _urt_nodeid_index_map                              = new HashMap<Long, Integer>();
252     private double                       _urt_starting_angle                                = ( float ) ( Math.PI / 2 );
253     private float                        _x_correction_factor                               = 0.0f;
254     private float                        _x_distance                                        = 0.0f;
255     private float                        _y_distance                                        = 0.0f;
256     private int                          _length_of_longest_text;
257     private int                          _longest_domain;
258     public final static boolean          SPECIAL_DOMAIN_COLORING                            = true;
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                 || ( getControlPanel().isUseVisualStyles() && ( ( node.getNodeData().getNodeVisualData() != null ) && ( ( node
4170                         .getNodeData().getNodeVisualData().getNodeColor() != null )
4171                         || ( node.getNodeData().getNodeVisualData().getSize() != NodeVisualData.DEFAULT_SIZE )
4172                         || ( node.getNodeData().getNodeVisualData().getFillType() != NodeFill.DEFAULT ) || ( node
4173                         .getNodeData().getNodeVisualData().getShape() != NodeShape.DEFAULT ) ) ) )
4174                 || ( getControlPanel().isEvents() && node.isHasAssignedEvent() && ( node.getNodeData().getEvent()
4175                         .isDuplication()
4176                         || node.getNodeData().getEvent().isSpeciation() || node.getNodeData().getEvent()
4177                         .isSpeciationOrDuplication() ) ) ) {
4178             NodeVisualData vis = null;
4179             if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
4180                     && ( !node.getNodeData().getNodeVisualData().isEmpty() ) ) {
4181                 vis = node.getNodeData().getNodeVisualData();
4182             }
4183             float box_size = getOptions().getDefaultNodeShapeSize();
4184             if ( ( vis != null ) && ( vis.getSize() != NodeVisualData.DEFAULT_SIZE ) ) {
4185                 box_size = vis.getSize();
4186             }
4187             final float half_box_size = box_size / 2.0f;
4188             Color outline_color = null;
4189             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4190                 outline_color = Color.BLACK;
4191             }
4192             else if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4193                 outline_color = getColorForFoundNode( node );
4194             }
4195             else if ( vis != null ) {
4196                 if ( vis.getNodeColor() != null ) {
4197                     outline_color = vis.getNodeColor();
4198                 }
4199                 else if ( vis.getFontColor() != null ) {
4200                     outline_color = vis.getFontColor();
4201                 }
4202             }
4203             else if ( getControlPanel().isEvents() && TreePanelUtil.isHasAssignedEvent( node ) ) {
4204                 final Event event = node.getNodeData().getEvent();
4205                 if ( event.isDuplication() ) {
4206                     outline_color = getTreeColorSet().getDuplicationBoxColor();
4207                 }
4208                 else if ( event.isSpeciation() ) {
4209                     outline_color = getTreeColorSet().getSpecBoxColor();
4210                 }
4211                 else if ( event.isSpeciationOrDuplication() ) {
4212                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
4213                 }
4214             }
4215             if ( outline_color == null ) {
4216                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
4217                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
4218                     outline_color = getTreeColorSet().getBranchColorForPdf();
4219                 }
4220             }
4221             NodeShape shape = null;
4222             if ( vis != null ) {
4223                 if ( vis.getShape() == NodeShape.CIRCLE ) {
4224                     shape = NodeShape.CIRCLE;
4225                 }
4226                 else if ( vis.getShape() == NodeShape.RECTANGLE ) {
4227                     shape = NodeShape.RECTANGLE;
4228                 }
4229             }
4230             if ( shape == null ) {
4231                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
4232                     shape = NodeShape.CIRCLE;
4233                 }
4234                 else if ( getOptions().getDefaultNodeShape() == NodeShape.RECTANGLE ) {
4235                     shape = NodeShape.RECTANGLE;
4236                 }
4237             }
4238             NodeFill fill = null;
4239             if ( vis != null ) {
4240                 if ( vis.getFillType() == NodeFill.SOLID ) {
4241                     fill = NodeFill.SOLID;
4242                 }
4243                 else if ( vis.getFillType() == NodeFill.NONE ) {
4244                     fill = NodeFill.NONE;
4245                 }
4246                 else if ( vis.getFillType() == NodeFill.GRADIENT ) {
4247                     fill = NodeFill.GRADIENT;
4248                 }
4249             }
4250             if ( fill == null ) {
4251                 if ( getOptions().getDefaultNodeFill() == NodeFill.SOLID ) {
4252                     fill = NodeFill.SOLID;
4253                 }
4254                 else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
4255                     fill = NodeFill.NONE;
4256                 }
4257                 else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
4258                     fill = NodeFill.GRADIENT;
4259                 }
4260             }
4261             Color vis_fill_color = null;
4262             if ( ( vis != null ) && ( vis.getNodeColor() != null ) ) {
4263                 vis_fill_color = vis.getNodeColor();
4264             }
4265             if ( shape == NodeShape.CIRCLE ) {
4266                 if ( fill == NodeFill.GRADIENT ) {
4267                     drawOvalGradient( x - half_box_size, y - half_box_size, box_size, box_size, g, to_pdf ? Color.WHITE
4268                             : outline_color, to_pdf ? outline_color : getBackground(), outline_color );
4269                 }
4270                 else if ( fill == NodeFill.NONE ) {
4271                     Color background = getBackground();
4272                     if ( to_pdf ) {
4273                         background = Color.WHITE;
4274                     }
4275                     drawOvalGradient( x - half_box_size,
4276                                       y - half_box_size,
4277                                       box_size,
4278                                       box_size,
4279                                       g,
4280                                       background,
4281                                       background,
4282                                       outline_color );
4283                 }
4284                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
4285                     if ( vis_fill_color != null ) {
4286                         g.setColor( vis_fill_color );
4287                     }
4288                     else {
4289                         g.setColor( outline_color );
4290                     }
4291                     drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4292                 }
4293             }
4294             else if ( shape == NodeVisualData.NodeShape.RECTANGLE ) {
4295                 if ( fill == NodeVisualData.NodeFill.GRADIENT ) {
4296                     drawRectGradient( x - half_box_size, y - half_box_size, box_size, box_size, g, to_pdf ? Color.WHITE
4297                             : outline_color, to_pdf ? outline_color : getBackground(), outline_color );
4298                 }
4299                 else if ( fill == NodeVisualData.NodeFill.NONE ) {
4300                     Color background = getBackground();
4301                     if ( to_pdf ) {
4302                         background = Color.WHITE;
4303                     }
4304                     drawRectGradient( x - half_box_size,
4305                                       y - half_box_size,
4306                                       box_size,
4307                                       box_size,
4308                                       g,
4309                                       background,
4310                                       background,
4311                                       outline_color );
4312                 }
4313                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
4314                     if ( vis_fill_color != null ) {
4315                         g.setColor( vis_fill_color );
4316                     }
4317                     else {
4318                         g.setColor( outline_color );
4319                     }
4320                     drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4321                 }
4322             }
4323         }
4324     }
4325
4326     final private int paintNodeData( final Graphics2D g,
4327                                      final PhylogenyNode node,
4328                                      final boolean to_graphics_file,
4329                                      final boolean to_pdf,
4330                                      final boolean is_in_found_nodes ) {
4331         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
4332             return 0;
4333         }
4334         if ( getControlPanel().isWriteBranchLengthValues()
4335                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4336                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4337                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
4338             paintBranchLength( g, node, to_pdf, to_graphics_file );
4339         }
4340         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
4341             return 0;
4342         }
4343         _sb.setLength( 0 );
4344         int x = 0;
4345         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
4346         if ( getControlPanel().isShowTaxonomyImages()
4347                 && ( getImageMap() != null )
4348                 && !getImageMap().isEmpty()
4349                 && node.getNodeData().isHasTaxonomy()
4350                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
4351                         .getUris().isEmpty() ) ) {
4352             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
4353         }
4354         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4355                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
4356             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
4357         }
4358         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
4359         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4360             if ( _sb.length() > 0 ) {
4361                 _sb.setLength( 0 );
4362                 _sb.append( " (" );
4363                 _sb.append( node.getAllExternalDescendants().size() );
4364                 _sb.append( ")" );
4365             }
4366         }
4367         else {
4368             _sb.setLength( 0 );
4369         }
4370         nodeDataAsSB( node, _sb );
4371         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4372         float down_shift_factor = 3.0f;
4373         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
4374             down_shift_factor = 1;
4375         }
4376         final float pos_x = node.getXcoord() + x + 2 + half_box_size;
4377         float pos_y;
4378         if ( !using_visual_font ) {
4379             pos_y = ( node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ) );
4380         }
4381         else {
4382             pos_y = ( node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / down_shift_factor ) );
4383         }
4384         final String sb_str = _sb.toString();
4385         // GUILHEM_BEG ______________
4386         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
4387                 && ( _query_sequence != null ) ) {
4388             int nodeTextBoundsWidth = 0;
4389             if ( sb_str.length() > 0 ) {
4390                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
4391                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
4392             }
4393             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
4394                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
4395                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
4396                     g.setColor( getTreeColorSet().getBackgroundColor() );
4397                 }
4398             }
4399             else {
4400                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
4401                 for( final SequenceRelation seqRelation : seqRelations ) {
4402                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
4403                             .getRef1().isEqual( _query_sequence ) )
4404                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
4405                                     .getSelectedItem() );
4406                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
4407                         final double linePosX = node.getXcoord() + 2 + half_box_size;
4408                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
4409                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
4410                                 + ")";
4411                         if ( sConfidence != null ) {
4412                             float confidenceX = pos_x;
4413                             if ( sb_str.length() > 0 ) {
4414                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
4415                                         + CONFIDENCE_LEFT_MARGIN;
4416                             }
4417                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
4418                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
4419                                         .getBounds().getWidth();
4420                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
4421                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
4422                             }
4423                         }
4424                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
4425                         {
4426                             if ( nodeTextBoundsWidth == 0 ) {
4427                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
4428                             }
4429                             else {
4430                                 nodeTextBoundsWidth += 2;
4431                             }
4432                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
4433                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
4434                             break;
4435                         }
4436                     }
4437                 }
4438             }
4439         }
4440         if ( sb_str.length() > 0 ) {
4441             TreePanel.drawString( sb_str, pos_x, pos_y, g );
4442         }
4443         // GUILHEM_END _____________
4444         if ( _sb.length() > 0 ) {
4445             if ( !using_visual_font && !is_in_found_nodes ) {
4446                 x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
4447             }
4448             else {
4449                 x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
4450             }
4451         }
4452         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
4453                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4454                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4455             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
4456             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4457                 g.setColor( Color.BLACK );
4458             }
4459             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
4460                 g.setColor( calculateColorForAnnotation( ann ) );
4461             }
4462             final String ann_str = TreePanelUtil.createAnnotationString( ann, getOptions().isShowAnnotationRefSource() );
4463             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
4464                     + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ), g );
4465             _sb.setLength( 0 );
4466             _sb.append( ann_str );
4467             if ( _sb.length() > 0 ) {
4468                 if ( !using_visual_font && !is_in_found_nodes ) {
4469                     x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
4470                 }
4471                 else {
4472                     x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
4473                 }
4474             }
4475         }
4476         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4477                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
4478                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
4479             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
4480                     && node.getNodeData().isHasBinaryCharacters() ) {
4481                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4482                     g.setColor( Color.BLACK );
4483                 }
4484                 else {
4485                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
4486                 }
4487                 if ( getControlPanel().isShowBinaryCharacters() ) {
4488                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
4489                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
4490                             + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ), g );
4491                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
4492                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
4493                             .getLostCharactersAsStringBuffer().toString() );
4494                 }
4495                 else {
4496                     TreePanel
4497                             .drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
4498                                          node.getXcoord() + x + 4 + half_box_size,
4499                                          node.getYcoord()
4500                                                  + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ),
4501                                          g );
4502                     paintGainedAndLostCharacters( g, node, "+"
4503                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
4504                             + node.getNodeData().getBinaryCharacters().getLostCount() );
4505                 }
4506             }
4507         }
4508         return x;
4509     }
4510
4511     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
4512                                                   final PhylogenyNode node,
4513                                                   final boolean to_pdf,
4514                                                   final boolean to_graphics_file,
4515                                                   final boolean radial_labels,
4516                                                   final double ur_angle,
4517                                                   final boolean is_in_found_nodes ) {
4518         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
4519             return;
4520         }
4521         _sb.setLength( 0 );
4522         _sb.append( " " );
4523         if ( node.getNodeData().isHasTaxonomy()
4524                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4525                         .isShowTaxonomyCommonNames() ) ) {
4526             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4527             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4528                 _sb.append( taxonomy.getTaxonomyCode() );
4529                 _sb.append( " " );
4530             }
4531             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4532                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4533                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4534                     _sb.append( taxonomy.getScientificName() );
4535                     _sb.append( " (" );
4536                     _sb.append( taxonomy.getCommonName() );
4537                     _sb.append( ") " );
4538                 }
4539                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4540                     _sb.append( taxonomy.getScientificName() );
4541                     _sb.append( " " );
4542                 }
4543                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4544                     _sb.append( taxonomy.getCommonName() );
4545                     _sb.append( " " );
4546                 }
4547             }
4548             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4549                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4550                     _sb.append( taxonomy.getScientificName() );
4551                     _sb.append( " " );
4552                 }
4553             }
4554             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4555                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4556                     _sb.append( taxonomy.getCommonName() );
4557                     _sb.append( " " );
4558                 }
4559             }
4560         }
4561         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4562             _sb.append( " [" );
4563             _sb.append( node.getAllExternalDescendants().size() );
4564             _sb.append( "]" );
4565         }
4566         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
4567             if ( _sb.length() > 0 ) {
4568                 _sb.append( " " );
4569             }
4570             _sb.append( node.getName() );
4571         }
4572         if ( node.getNodeData().isHasSequence() ) {
4573             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4574                 if ( _sb.length() > 0 ) {
4575                     _sb.append( " " );
4576                 }
4577                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4578                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4579                     _sb.append( ":" );
4580                 }
4581                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4582             }
4583             if ( getControlPanel().isShowSeqNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
4584                 if ( _sb.length() > 0 ) {
4585                     _sb.append( " " );
4586                 }
4587                 _sb.append( node.getNodeData().getSequence().getName() );
4588             }
4589         }
4590         //g.setFont( getTreeFontSet().getLargeFont() );
4591         //if ( is_in_found_nodes ) {
4592         //    g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4593         // }
4594         if ( _sb.length() > 1 ) {
4595             setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
4596             final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4597             final String sb_str = _sb.toString();
4598             double m = 0;
4599             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4600                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
4601             }
4602             else {
4603                 m = ( float ) ( ur_angle % TWO_PI );
4604             }
4605             _at = g.getTransform();
4606             boolean need_to_reset = false;
4607             final float x_coord = node.getXcoord();
4608             float y_coord;
4609             if ( !using_visual_font ) {
4610                 y_coord = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / 3.0f );
4611             }
4612             else {
4613                 y_coord = node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / 3.0f );
4614             }
4615             if ( radial_labels ) {
4616                 need_to_reset = true;
4617                 boolean left = false;
4618                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4619                     m -= PI;
4620                     left = true;
4621                 }
4622                 g.rotate( m, x_coord, node.getYcoord() );
4623                 if ( left ) {
4624                     if ( !using_visual_font ) {
4625                         g.translate( -( getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth() ),
4626                                      0 );
4627                     }
4628                     else {
4629                         g.translate( -( getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth() ), 0 );
4630                     }
4631                 }
4632             }
4633             else {
4634                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4635                     need_to_reset = true;
4636                     if ( !using_visual_font ) {
4637                         g.translate( -getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth(), 0 );
4638                     }
4639                     else {
4640                         g.translate( -getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth(), 0 );
4641                     }
4642                 }
4643             }
4644             TreePanel.drawString( sb_str, x_coord, y_coord, g );
4645             if ( need_to_reset ) {
4646                 g.setTransform( _at );
4647             }
4648         }
4649     }
4650
4651     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
4652         if ( node.isCollapse() ) {
4653             if ( !node.isRoot() && !node.getParent().isCollapse() ) {
4654                 paintCollapsedNode( g, node, false, false, false );
4655             }
4656             return;
4657         }
4658         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4659             g.setColor( getColorForFoundNode( node ) );
4660             drawRectFilled( node.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, node.getYSecondary()
4661                     - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, OVERVIEW_FOUND_NODE_BOX_SIZE, OVERVIEW_FOUND_NODE_BOX_SIZE, g );
4662         }
4663         float new_x = 0;
4664         if ( !node.isExternal() && !node.isCollapse() ) {
4665             boolean first_child = true;
4666             float y2 = 0.0f;
4667             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4668             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4669                 final PhylogenyNode child_node = node.getChildNode( i );
4670                 int factor_x;
4671                 if ( !isUniformBranchLengthsForCladogram() ) {
4672                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4673                 }
4674                 else {
4675                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4676                 }
4677                 if ( first_child ) {
4678                     first_child = false;
4679                     y2 = node.getYSecondary()
4680                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
4681                                     .getNumberOfExternalNodes() ) );
4682                 }
4683                 else {
4684                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4685                 }
4686                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
4687                 new_x = x2 + node.getXSecondary();
4688                 final float diff_y = node.getYSecondary() - y2;
4689                 final float diff_x = node.getXSecondary() - new_x;
4690                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
4691                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
4692                 }
4693                 child_node.setXSecondary( new_x );
4694                 child_node.setYSecondary( y2 );
4695                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4696             }
4697         }
4698     }
4699
4700     final private void paintNodeRectangular( final Graphics2D g,
4701                                              final PhylogenyNode node,
4702                                              final boolean to_pdf,
4703                                              final boolean dynamically_hide,
4704                                              final int dynamic_hiding_factor,
4705                                              final boolean to_graphics_file ) {
4706         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
4707         if ( node.isCollapse() ) {
4708             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) ) {
4709                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4710             }
4711             return;
4712         }
4713         if ( node.isExternal() ) {
4714             ++_external_node_index;
4715         }
4716         // Confidence values
4717         if ( getControlPanel().isShowConfidenceValues()
4718                 && !node.isExternal()
4719                 && !node.isRoot()
4720                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
4721                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4722                 && node.getBranchData().isHasConfidences() ) {
4723             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
4724         }
4725         // Draw a line to root:
4726         if ( node.isRoot() && _phylogeny.isRooted() ) {
4727             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
4728         }
4729         float new_x = 0;
4730         float new_x_min = Float.MAX_VALUE;
4731         final boolean disallow_shortcutting = ( dynamic_hiding_factor < 40 );
4732         float min_dist = 1.5f;
4733         if ( !disallow_shortcutting ) {
4734             if ( dynamic_hiding_factor > 4000 ) {
4735                 min_dist = 4;
4736             }
4737             else if ( dynamic_hiding_factor > 1000 ) {
4738                 min_dist = 3;
4739             }
4740             else if ( dynamic_hiding_factor > 100 ) {
4741                 min_dist = 2;
4742             }
4743         }
4744         if ( !node.isExternal() && !node.isCollapse() ) {
4745             boolean first_child = true;
4746             float y2 = 0.0f;
4747             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4748             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4749                 final PhylogenyNode child_node = node.getChildNode( i );
4750                 int factor_x;
4751                 if ( !isUniformBranchLengthsForCladogram() ) {
4752                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4753                 }
4754                 else {
4755                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4756                 }
4757                 if ( first_child ) {
4758                     first_child = false;
4759                     y2 = node.getYcoord()
4760                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
4761                 }
4762                 else {
4763                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
4764                 }
4765                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
4766                 new_x = x2 + node.getXcoord();
4767                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
4768                     new_x_min = x2;
4769                 }
4770                 final float diff_y = node.getYcoord() - y2;
4771                 final float diff_x = node.getXcoord() - new_x;
4772                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
4773                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
4774                     paintBranchRectangular( g,
4775                                             node.getXcoord(),
4776                                             new_x,
4777                                             node.getYcoord(),
4778                                             y2,
4779                                             child_node,
4780                                             to_pdf,
4781                                             to_graphics_file );
4782                 }
4783                 child_node.setXcoord( new_x );
4784                 child_node.setYcoord( y2 );
4785                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
4786             }
4787             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file );
4788         }
4789         if ( dynamically_hide
4790                 && !is_in_found_nodes
4791                 && ( ( node.isExternal() && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) ) || ( !node
4792                         .isExternal() && ( ( new_x_min < 20 ) || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getFontMetricsForLargeDefaultFont()
4793                         .getHeight() ) ) ) ) ) {
4794             return;
4795         }
4796         final int x = paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4797         paintNodeWithRenderableData( x, g, node, to_graphics_file, to_pdf );
4798     }
4799
4800     final private void paintNodeWithRenderableData( final int x,
4801                                                     final Graphics2D g,
4802                                                     final PhylogenyNode node,
4803                                                     final boolean to_graphics_file,
4804                                                     final boolean to_pdf ) {
4805         if ( isNodeDataInvisible( node ) && !( to_graphics_file || to_pdf ) ) {
4806             return;
4807         }
4808         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
4809             return;
4810         }
4811         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4812                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null )
4813                 && ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
4814             RenderableDomainArchitecture rds = null;
4815             try {
4816                 rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
4817             }
4818             catch ( final ClassCastException cce ) {
4819                 cce.printStackTrace();
4820             }
4821             if ( rds != null ) {
4822                 final int default_height = 7;
4823                 float y = getYdistance();
4824                 if ( getControlPanel().isDynamicallyHideData() ) {
4825                     y = getTreeFontSet().getFontMetricsLarge().getHeight();
4826                 }
4827                 final int h = y < default_height ? ForesterUtil.roundToInt( y ) : default_height;
4828                 rds.setRenderingHeight( h > 1 ? h : 2 );
4829                 if ( getControlPanel().isDrawPhylogram() ) {
4830                     if ( getOptions().isLineUpRendarableNodeData() ) {
4831                         if ( getOptions().isRightLineUpDomains() ) {
4832                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
4833                                     + _length_of_longest_text + ( ( _longest_domain - rds.getTotalLength() ) * rds
4834                                     .getRenderingFactorWidth() ) ), node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
4835                         }
4836                         else {
4837                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
4838                                         node.getYcoord() - ( h / 2.0f ),
4839                                         g,
4840                                         this,
4841                                         to_pdf );
4842                         }
4843                     }
4844                     else {
4845                         rds.render( node.getXcoord() + x, node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
4846                     }
4847                 }
4848                 else {
4849                     if ( getOptions().isRightLineUpDomains() ) {
4850                         rds.render( ( ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text ) - 20 )
4851                                             + ( ( _longest_domain - rds.getTotalLength() ) * rds
4852                                                     .getRenderingFactorWidth() ),
4853                                     node.getYcoord() - ( h / 2.0f ),
4854                                     g,
4855                                     this,
4856                                     to_pdf );
4857                     }
4858                     else {
4859                         rds.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
4860                                     node.getYcoord() - ( h / 2.0f ),
4861                                     g,
4862                                     this,
4863                                     to_pdf );
4864                     }
4865                 }
4866             }
4867         }
4868         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4869                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
4870             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
4871                                                                          getStatisticsForExpressionValues(),
4872                                                                          getConfiguration() );
4873             if ( rv != null ) {
4874                 double domain_add = 0;
4875                 if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4876                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4877                     domain_add = _domain_structure_width + 10;
4878                 }
4879                 if ( getControlPanel().isDrawPhylogram() ) {
4880                     rv.render( ( float ) ( node.getXcoord() + x + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
4881                 }
4882                 else {
4883                     rv.render( ( float ) ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text + domain_add ),
4884                                node.getYcoord() - 3,
4885                                g,
4886                                this,
4887                                to_pdf );
4888                 }
4889             }
4890         }
4891         if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
4892                 && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
4893                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
4894             final RenderableMsaSequence rs = RenderableMsaSequence.createInstance( node.getNodeData().getSequence()
4895                     .getMolecularSequence(), node.getNodeData().getSequence().getType(), getConfiguration() );
4896             if ( rs != null ) {
4897                 final int default_height = 7;
4898                 float y = getYdistance();
4899                 if ( getControlPanel().isDynamicallyHideData() ) {
4900                     y = getTreeFontSet().getFontMetricsLarge().getHeight();
4901                 }
4902                 final int h = y < default_height ? ForesterUtil.roundToInt( y ) : default_height;
4903                 rs.setRenderingHeight( h > 1 ? h : 2 );
4904                 if ( getControlPanel().isDrawPhylogram() ) {
4905                     rs.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
4906                                node.getYcoord() - ( h / 2.0f ),
4907                                g,
4908                                this,
4909                                to_pdf );
4910                 }
4911                 else {
4912                     rs.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
4913                                node.getYcoord() - ( h / 2.0f ),
4914                                g,
4915                                this,
4916                                to_pdf );
4917                 }
4918             }
4919         }
4920     }
4921
4922     final private int calcLengthOfLongestText() {
4923         final StringBuilder sb = new StringBuilder();
4924         if ( _ext_node_with_longest_txt_info != null ) {
4925             nodeDataAsSB( _ext_node_with_longest_txt_info, sb );
4926             if ( _ext_node_with_longest_txt_info.getNodeData().isHasTaxonomy() ) {
4927                 nodeTaxonomyDataAsSB( _ext_node_with_longest_txt_info.getNodeData().getTaxonomy(), sb );
4928             }
4929         }
4930         return getFontMetricsForLargeDefaultFont().stringWidth( sb.toString() );
4931     }
4932
4933     final private void paintOvRectangle( final Graphics2D g ) {
4934         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
4935         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
4936         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
4937         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
4938         final float width = getOvMaxWidth() / w_ratio;
4939         final float height = getOvMaxHeight() / h_ratio;
4940         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
4941         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
4942         g.setColor( getTreeColorSet().getFoundColor0() );
4943         getOvRectangle().setRect( x, y, width, height );
4944         final Stroke s = g.getStroke();
4945         g.setStroke( STROKE_1 );
4946         if ( ( width < 6 ) && ( height < 6 ) ) {
4947             drawRectFilled( x, y, 6, 6, g );
4948             getOvVirtualRectangle().setRect( x, y, 6, 6 );
4949         }
4950         else if ( width < 6 ) {
4951             drawRectFilled( x, y, 6, height, g );
4952             getOvVirtualRectangle().setRect( x, y, 6, height );
4953         }
4954         else if ( height < 6 ) {
4955             drawRectFilled( x, y, width, 6, g );
4956             getOvVirtualRectangle().setRect( x, y, width, 6 );
4957         }
4958         else {
4959             drawRect( x, y, width, height, g );
4960             if ( isInOvRect() ) {
4961                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
4962             }
4963             getOvVirtualRectangle().setRect( x, y, width, height );
4964         }
4965         g.setStroke( s );
4966     }
4967
4968     final private void paintPhylogenyLite( final Graphics2D g ) {
4969         _phylogeny
4970                 .getRoot()
4971                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
4972                         .getWidth() ) ) ) );
4973         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
4974         final Stroke s = g.getStroke();
4975         g.setStroke( STROKE_05 );
4976         for( final PhylogenyNode element : _nodes_in_preorder ) {
4977             paintNodeLite( g, element );
4978         }
4979         g.setStroke( s );
4980         paintOvRectangle( g );
4981     }
4982
4983     /**
4984      * Paint the root branch. (Differs from others because it will always be a
4985      * single horizontal line).
4986      * @param to_graphics_file 
4987      * 
4988      * @return new x1 value
4989      */
4990     final private void paintRootBranch( final Graphics2D g,
4991                                         final float x1,
4992                                         final float y1,
4993                                         final PhylogenyNode root,
4994                                         final boolean to_pdf,
4995                                         final boolean to_graphics_file ) {
4996         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
4997         float d = getXdistance();
4998         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
4999             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
5000         }
5001         if ( d < MIN_ROOT_LENGTH ) {
5002             d = MIN_ROOT_LENGTH;
5003         }
5004         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
5005             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
5006         }
5007         else {
5008             final double w = PhylogenyMethods.getBranchWidthValue( root );
5009             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
5010         }
5011         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file );
5012     }
5013
5014     final private void paintScale( final Graphics2D g,
5015                                    int x1,
5016                                    int y1,
5017                                    final boolean to_pdf,
5018                                    final boolean to_graphics_file ) {
5019         x1 += MOVE;
5020         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
5021         y1 -= 12;
5022         final int y2 = y1 - 8;
5023         final int y3 = y1 - 4;
5024         g.setFont( getTreeFontSet().getSmallFont() );
5025         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5026             g.setColor( Color.BLACK );
5027         }
5028         else {
5029             g.setColor( getTreeColorSet().getBranchLengthColor() );
5030         }
5031         final Stroke s = g.getStroke();
5032         g.setStroke( STROKE_1 );
5033         drawLine( x1, y1, x1, y2, g );
5034         drawLine( x2, y1, x2, y2, g );
5035         drawLine( x1, y3, x2, y3, g );
5036         if ( getScaleLabel() != null ) {
5037             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
5038         }
5039         g.setStroke( s );
5040     }
5041
5042     final private int paintTaxonomy( final Graphics2D g,
5043                                      final PhylogenyNode node,
5044                                      final boolean is_in_found_nodes,
5045                                      final boolean to_pdf,
5046                                      final boolean to_graphics_file,
5047                                      final float x_shift ) {
5048         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
5049         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
5050         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getTaxonomyColor() );
5051         final float start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
5052         float start_y;
5053         if ( !using_visual_font ) {
5054             start_y = node.getYcoord()
5055                     + ( getFontMetricsForLargeDefaultFont().getAscent() / ( node.getNumberOfDescendants() == 1 ? 1
5056                             : 3.0f ) );
5057         }
5058         else {
5059             start_y = node.getYcoord()
5060                     + ( getFontMetrics( g.getFont() ).getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
5061         }
5062         _sb.setLength( 0 );
5063         nodeTaxonomyDataAsSB( taxonomy, _sb );
5064         final String label = _sb.toString();
5065         /* GUILHEM_BEG */
5066         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
5067                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
5068             // invert font color and background color to show that this is the query sequence
5069             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
5070                                                                                                           false,
5071                                                                                                           false ) )
5072                     .getBounds();
5073             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
5074             g.setColor( getTreeColorSet().getBackgroundColor() );
5075         }
5076         /* GUILHEM_END */
5077         TreePanel.drawString( label, start_x, start_y, g );
5078         if ( !using_visual_font && !is_in_found_nodes ) {
5079             return getFontMetricsForLargeDefaultFont().stringWidth( label );
5080         }
5081         return getFontMetrics( g.getFont() ).stringWidth( label );
5082     }
5083
5084     final private void paintUnrooted( final PhylogenyNode n,
5085                                       final double low_angle,
5086                                       final double high_angle,
5087                                       final boolean radial_labels,
5088                                       final Graphics2D g,
5089                                       final boolean to_pdf,
5090                                       final boolean to_graphics_file ) {
5091         if ( n.isRoot() ) {
5092             n.setXcoord( getWidth() / 2 );
5093             n.setYcoord( getHeight() / 2 );
5094         }
5095         if ( n.isExternal() ) {
5096             paintNodeDataUnrootedCirc( g,
5097                                        n,
5098                                        to_pdf,
5099                                        to_graphics_file,
5100                                        radial_labels,
5101                                        ( high_angle + low_angle ) / 2,
5102                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
5103             return;
5104         }
5105         final float num_enclosed = n.getNumberOfExternalNodes();
5106         final float x = n.getXcoord();
5107         final float y = n.getYcoord();
5108         double current_angle = low_angle;
5109         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
5110         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
5111         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
5112         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
5113         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5114             final PhylogenyNode desc = n.getChildNode( i );
5115             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
5116             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
5117             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
5118             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
5119             //     continue;
5120             // }
5121             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
5122             //    continue;
5123             //}
5124             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
5125             //    continue;
5126             // }
5127             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5128             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5129             float length;
5130             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5131                 if ( desc.getDistanceToParent() < 0 ) {
5132                     length = 0;
5133                 }
5134                 else {
5135                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
5136                 }
5137             }
5138             else {
5139                 length = getUrtFactor();
5140             }
5141             final double mid_angle = current_angle + ( arc_size / 2 );
5142             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5143             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5144             desc.setXcoord( new_x );
5145             desc.setYcoord( new_y );
5146             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
5147             current_angle += arc_size;
5148             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
5149             drawLine( x, y, new_x, new_y, g );
5150             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file );
5151         }
5152         if ( n.isRoot() ) {
5153             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file );
5154         }
5155     }
5156
5157     final private void paintUnrootedLite( final PhylogenyNode n,
5158                                           final double low_angle,
5159                                           final double high_angle,
5160                                           final Graphics2D g,
5161                                           final float urt_ov_factor ) {
5162         if ( n.isRoot() ) {
5163             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
5164             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
5165             n.setXSecondary( x_pos );
5166             n.setYSecondary( y_pos );
5167         }
5168         if ( n.isExternal() ) {
5169             return;
5170         }
5171         final float num_enclosed = n.getNumberOfExternalNodes();
5172         final float x = n.getXSecondary();
5173         final float y = n.getYSecondary();
5174         double current_angle = low_angle;
5175         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5176             final PhylogenyNode desc = n.getChildNode( i );
5177             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5178             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5179             float length;
5180             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5181                 if ( desc.getDistanceToParent() < 0 ) {
5182                     length = 0;
5183                 }
5184                 else {
5185                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
5186                 }
5187             }
5188             else {
5189                 length = urt_ov_factor;
5190             }
5191             final double mid_angle = current_angle + ( arc_size / 2 );
5192             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5193             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5194             desc.setXSecondary( new_x );
5195             desc.setYSecondary( new_y );
5196             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
5197                 g.setColor( getColorForFoundNode( desc ) );
5198                 drawRectFilled( desc.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5199                                 desc.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5200                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5201                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5202                                 g );
5203                 g.setColor( getTreeColorSet().getOvColor() );
5204             }
5205             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
5206             current_angle += arc_size;
5207             drawLine( x, y, new_x, new_y, g );
5208         }
5209     }
5210
5211     final private void pasteSubtree( final PhylogenyNode node ) {
5212         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5213             errorMessageNoCutCopyPasteInUnrootedDisplay();
5214             return;
5215         }
5216         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
5217             JOptionPane.showMessageDialog( this,
5218                                            "No tree in buffer (need to copy or cut a subtree first)",
5219                                            "Attempt to paste with empty buffer",
5220                                            JOptionPane.ERROR_MESSAGE );
5221             return;
5222         }
5223         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
5224         final Object[] options = { "As sibling", "As descendant", "Cancel" };
5225         final int r = JOptionPane.showOptionDialog( this,
5226                                                     "How to paste subtree" + label + "?",
5227                                                     "Paste Subtree",
5228                                                     JOptionPane.CLOSED_OPTION,
5229                                                     JOptionPane.QUESTION_MESSAGE,
5230                                                     null,
5231                                                     options,
5232                                                     options[ 2 ] );
5233         boolean paste_as_sibling = true;
5234         if ( r == 1 ) {
5235             paste_as_sibling = false;
5236         }
5237         else if ( r != 0 ) {
5238             return;
5239         }
5240         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
5241         buffer_phy.setAllNodesToNotCollapse();
5242         PhylogenyMethods.preOrderReId( buffer_phy );
5243         buffer_phy.setRooted( true );
5244         boolean need_to_show_whole = false;
5245         if ( paste_as_sibling ) {
5246             if ( node.isRoot() ) {
5247                 JOptionPane.showMessageDialog( this,
5248                                                "Cannot paste sibling to root",
5249                                                "Attempt to paste sibling to root",
5250                                                JOptionPane.ERROR_MESSAGE );
5251                 return;
5252             }
5253             buffer_phy.addAsSibling( node );
5254         }
5255         else {
5256             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
5257                 need_to_show_whole = true;
5258                 _phylogeny = buffer_phy;
5259             }
5260             else {
5261                 buffer_phy.addAsChild( node );
5262             }
5263         }
5264         if ( getCopiedAndPastedNodes() == null ) {
5265             setCopiedAndPastedNodes( new HashSet<Long>() );
5266         }
5267         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
5268         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
5269         for( final PhylogenyNode n : nodes ) {
5270             node_ids.add( n.getId() );
5271         }
5272         node_ids.add( node.getId() );
5273         getCopiedAndPastedNodes().addAll( node_ids );
5274         setNodeInPreorderToNull();
5275         _phylogeny.externalNodesHaveChanged();
5276         _phylogeny.clearHashIdToNodeMap();
5277         _phylogeny.recalculateNumberOfExternalDescendants( true );
5278         resetNodeIdToDistToLeafMap();
5279         setEdited( true );
5280         if ( need_to_show_whole ) {
5281             getControlPanel().showWhole();
5282         }
5283         repaint();
5284     }
5285
5286     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
5287         final PropertiesMap properties = node.getNodeData().getProperties();
5288         final StringBuffer sb = new StringBuffer();
5289         boolean first = true;
5290         for( final String ref : properties.getPropertyRefs() ) {
5291             if ( first ) {
5292                 first = false;
5293             }
5294             else {
5295                 sb.append( " " );
5296             }
5297             final Property p = properties.getProperty( ref );
5298             sb.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5299             sb.append( "=" );
5300             sb.append( p.getValue() );
5301             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5302                 sb.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5303             }
5304         }
5305         return sb;
5306     }
5307
5308     private void setColor( final Graphics2D g,
5309                            final PhylogenyNode node,
5310                            final boolean to_graphics_file,
5311                            final boolean to_pdf,
5312                            final boolean is_in_found_nodes,
5313                            final Color default_color ) {
5314         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5315             g.setColor( Color.BLACK );
5316         }
5317         else if ( is_in_found_nodes ) {
5318             g.setColor( getColorForFoundNode( node ) );
5319         }
5320         else if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
5321                 && ( node.getNodeData().getNodeVisualData().getFontColor() != null ) ) {
5322             g.setColor( node.getNodeData().getNodeVisualData().getFontColor() );
5323         }
5324         else if ( getControlPanel().isColorAccordingToSequence() ) {
5325             g.setColor( getSequenceBasedColor( node ) );
5326         }
5327         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5328             g.setColor( getTaxonomyBasedColor( node ) );
5329         }
5330         else if ( getControlPanel().isColorAccordingToAnnotation()
5331                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
5332                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
5333             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
5334         }
5335         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
5336                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
5337             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
5338         }
5339         else if ( to_pdf ) {
5340             g.setColor( Color.BLACK );
5341         }
5342         else {
5343             g.setColor( default_color );
5344         }
5345     }
5346
5347     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
5348         getMainPanel().setCopiedAndPastedNodes( nodeIds );
5349     }
5350
5351     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
5352         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
5353     }
5354
5355     private boolean setFont( final Graphics2D g, final PhylogenyNode node, final boolean is_in_found_nodes ) {
5356         Font visual_font = null;
5357         if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null ) ) {
5358             visual_font = node.getNodeData().getNodeVisualData().getFont();
5359             g.setFont( visual_font != null ? visual_font : getTreeFontSet().getLargeFont() );
5360         }
5361         else {
5362             g.setFont( getTreeFontSet().getLargeFont() );
5363         }
5364         if ( is_in_found_nodes ) {
5365             g.setFont( g.getFont().deriveFont( Font.BOLD ) );
5366         }
5367         return visual_font != null;
5368     }
5369
5370     final private void setInOv( final boolean in_ov ) {
5371         _in_ov = in_ov;
5372     }
5373
5374     final private void setOvMaxHeight( final float ov_max_height ) {
5375         _ov_max_height = ov_max_height;
5376     }
5377
5378     final private void setOvMaxWidth( final float ov_max_width ) {
5379         _ov_max_width = ov_max_width;
5380     }
5381
5382     final private void setOvXcorrectionFactor( final float f ) {
5383         _ov_x_correction_factor = f;
5384     }
5385
5386     final private void setOvXDistance( final float ov_x_distance ) {
5387         _ov_x_distance = ov_x_distance;
5388     }
5389
5390     final private void setOvXPosition( final int ov_x_position ) {
5391         _ov_x_position = ov_x_position;
5392     }
5393
5394     final private void setOvYDistance( final float ov_y_distance ) {
5395         _ov_y_distance = ov_y_distance;
5396     }
5397
5398     final private void setOvYPosition( final int ov_y_position ) {
5399         _ov_y_position = ov_y_position;
5400     }
5401
5402     final private void setOvYStart( final int ov_y_start ) {
5403         _ov_y_start = ov_y_start;
5404     }
5405
5406     final private void setScaleDistance( final double scale_distance ) {
5407         _scale_distance = scale_distance;
5408     }
5409
5410     final private void setScaleLabel( final String scale_label ) {
5411         _scale_label = scale_label;
5412     }
5413
5414     private final void setupStroke( final Graphics2D g ) {
5415         if ( getYdistance() < 0.001 ) {
5416             g.setStroke( STROKE_005 );
5417         }
5418         else if ( getYdistance() < 0.01 ) {
5419             g.setStroke( STROKE_01 );
5420         }
5421         else if ( getYdistance() < 0.5 ) {
5422             g.setStroke( STROKE_025 );
5423         }
5424         else if ( getYdistance() < 1 ) {
5425             g.setStroke( STROKE_05 );
5426         }
5427         else if ( getYdistance() < 2 ) {
5428             g.setStroke( STROKE_075 );
5429         }
5430         else if ( getYdistance() < 20 ) {
5431             g.setStroke( STROKE_1 );
5432         }
5433         else {
5434             g.setStroke( STROKE_2 );
5435         }
5436     }
5437
5438     final private void setUpUrtFactor() {
5439         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
5440                 : getVisibleRect().height;
5441         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5442             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
5443         }
5444         else {
5445             final int max_depth = _circ_max_depth;
5446             if ( max_depth > 0 ) {
5447                 setUrtFactor( d / ( 2 * max_depth ) );
5448             }
5449             else {
5450                 setUrtFactor( d / 2 );
5451             }
5452         }
5453         setUrtFactorOv( getUrtFactor() );
5454     }
5455
5456     final private void setUrtFactor( final float urt_factor ) {
5457         _urt_factor = urt_factor;
5458     }
5459
5460     final private void setUrtFactorOv( final float urt_factor_ov ) {
5461         _urt_factor_ov = urt_factor_ov;
5462     }
5463
5464     private void showExtDescNodeData( final PhylogenyNode node ) {
5465         final List<String> data = new ArrayList<String>();
5466         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
5467         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
5468             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
5469                 if ( !nodes.contains( n ) ) {
5470                     nodes.add( n );
5471                 }
5472             }
5473         }
5474         for( final PhylogenyNode n : nodes ) {
5475             switch ( getOptions().getExtDescNodeDataToReturn() ) {
5476                 case NODE_NAME:
5477                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5478                         data.add( n.getName() );
5479                     }
5480                     break;
5481                 case SEQUENCE_NAME:
5482                     if ( n.getNodeData().isHasSequence()
5483                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5484                         data.add( n.getNodeData().getSequence().getName() );
5485                     }
5486                     break;
5487                 case GENE_NAME:
5488                     if ( n.getNodeData().isHasSequence()
5489                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5490                         data.add( n.getNodeData().getSequence().getGeneName() );
5491                     }
5492                     break;
5493                 case SEQUENCE_SYMBOL:
5494                     if ( n.getNodeData().isHasSequence()
5495                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5496                         data.add( n.getNodeData().getSequence().getSymbol() );
5497                     }
5498                     break;
5499                 case SEQUENCE_MOL_SEQ:
5500                     if ( n.getNodeData().isHasSequence()
5501                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5502                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
5503                     }
5504                     break;
5505                 case SEQUENCE_MOL_SEQ_FASTA:
5506                     final StringBuilder sb = new StringBuilder();
5507                     if ( n.getNodeData().isHasSequence()
5508                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5509                         final StringBuilder ann = new StringBuilder();
5510                         if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5511                             ann.append( n.getName() );
5512                             ann.append( "|" );
5513                         }
5514                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5515                             ann.append( "SYM=" );
5516                             ann.append( n.getNodeData().getSequence().getSymbol() );
5517                             ann.append( "|" );
5518                         }
5519                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5520                             ann.append( "NAME=" );
5521                             ann.append( n.getNodeData().getSequence().getName() );
5522                             ann.append( "|" );
5523                         }
5524                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5525                             ann.append( "GN=" );
5526                             ann.append( n.getNodeData().getSequence().getGeneName() );
5527                             ann.append( "|" );
5528                         }
5529                         if ( n.getNodeData().getSequence().getAccession() != null ) {
5530                             ann.append( "ACC=" );
5531                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
5532                             ann.append( "|" );
5533                         }
5534                         if ( n.getNodeData().isHasTaxonomy() ) {
5535                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5536                                 ann.append( "TAXID=" );
5537                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5538                                 ann.append( "|" );
5539                             }
5540                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5541                                 ann.append( "SN=" );
5542                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
5543                                 ann.append( "|" );
5544                             }
5545                         }
5546                         String ann_str;
5547                         if ( ann.charAt( ann.length() - 1 ) == '|' ) {
5548                             ann_str = ann.substring( 0, ann.length() - 1 );
5549                         }
5550                         else {
5551                             ann_str = ann.toString();
5552                         }
5553                         sb.append( SequenceWriter.toFasta( ann_str, n.getNodeData().getSequence()
5554                                 .getMolecularSequence(), 60 ) );
5555                         data.add( sb.toString() );
5556                     }
5557                     break;
5558                 case SEQUENCE_ACC:
5559                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
5560                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
5561                         data.add( n.getNodeData().getSequence().getAccession().toString() );
5562                     }
5563                     break;
5564                 case TAXONOMY_SCIENTIFIC_NAME:
5565                     if ( n.getNodeData().isHasTaxonomy()
5566                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5567                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
5568                     }
5569                     break;
5570                 case TAXONOMY_COMM0N_NAME:
5571                     if ( n.getNodeData().isHasTaxonomy()
5572                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) ) {
5573                         data.add( n.getNodeData().getTaxonomy().getCommonName() );
5574                     }
5575                     break;
5576                 case TAXONOMY_CODE:
5577                     if ( n.getNodeData().isHasTaxonomy()
5578                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5579                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5580                     }
5581                     break;
5582                 case UNKNOWN:
5583                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
5584                     break;
5585                 default:
5586                     throw new IllegalArgumentException( "unknown data element: "
5587                             + getOptions().getExtDescNodeDataToReturn() );
5588             }
5589         } // for loop
5590         final StringBuilder sb = new StringBuilder();
5591         final int size = TreePanelUtil.makeSB( data, getOptions(), sb );
5592         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
5593                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
5594             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5595                 System.out.println( sb );
5596             }
5597             if ( sb.length() < 1 ) {
5598                 clearCurrentExternalNodesDataBuffer();
5599             }
5600             else {
5601                 setCurrentExternalNodesDataBuffer( sb );
5602             }
5603         }
5604         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5605             if ( sb.length() < 1 ) {
5606                 TreePanelUtil.showInformationMessage( this, "No Appropriate Data (" + obtainTitleForExtDescNodeData()
5607                         + ")", "Descendants of selected node do not contain selected data" );
5608                 clearCurrentExternalNodesDataBuffer();
5609             }
5610             else {
5611                 setCurrentExternalNodesDataBuffer( sb );
5612                 String title;
5613                 if ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() ) {
5614                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5615                             : obtainTitleForExtDescNodeData() )
5616                             + " for "
5617                             + data.size()
5618                             + " nodes, unique entries: "
5619                             + size;
5620                 }
5621                 else {
5622                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5623                             : obtainTitleForExtDescNodeData() )
5624                             + " for "
5625                             + data.size()
5626                             + "/"
5627                             + node.getNumberOfExternalNodes()
5628                             + " external descendats of node "
5629                             + node
5630                             + ", unique entries: " + size;
5631                 }
5632                 final String s = sb.toString().trim();
5633                 if ( getMainPanel().getMainFrame() == null ) {
5634                     // Must be "E" applet version.
5635                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5636                     ae.showTextFrame( s, title );
5637                 }
5638                 else {
5639                     getMainPanel().getMainFrame().showTextFrame( s, title );
5640                 }
5641             }
5642         }
5643     }
5644
5645     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5646         try {
5647             if ( ( node.getName().length() > 0 )
5648                     || ( node.getNodeData().isHasTaxonomy() && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData()
5649                             .getTaxonomy() ) )
5650                     || ( node.getNodeData().isHasSequence() && !TreePanelUtil.isSequenceEmpty( node.getNodeData()
5651                             .getSequence() ) ) || ( node.getNodeData().isHasDate() )
5652                     || ( node.getNodeData().isHasDistribution() ) || node.getBranchData().isHasConfidences() ) {
5653                 _popup_buffer.setLength( 0 );
5654                 short lines = 0;
5655                 if ( node.getName().length() > 0 ) {
5656                     lines++;
5657                     _popup_buffer.append( node.getName() );
5658                 }
5659                 if ( node.getNodeData().isHasTaxonomy()
5660                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5661                     lines++;
5662                     boolean enc_data = false;
5663                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5664                     if ( _popup_buffer.length() > 0 ) {
5665                         _popup_buffer.append( "\n" );
5666                     }
5667                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5668                         _popup_buffer.append( "[" );
5669                         _popup_buffer.append( tax.getTaxonomyCode() );
5670                         _popup_buffer.append( "]" );
5671                         enc_data = true;
5672                     }
5673                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5674                         if ( enc_data ) {
5675                             _popup_buffer.append( " " );
5676                         }
5677                         _popup_buffer.append( tax.getScientificName() );
5678                         enc_data = true;
5679                     }
5680                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5681                         if ( enc_data ) {
5682                             _popup_buffer.append( " (" );
5683                         }
5684                         else {
5685                             _popup_buffer.append( "(" );
5686                         }
5687                         _popup_buffer.append( tax.getCommonName() );
5688                         _popup_buffer.append( ")" );
5689                         enc_data = true;
5690                     }
5691                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5692                         if ( enc_data ) {
5693                             _popup_buffer.append( " (" );
5694                         }
5695                         else {
5696                             _popup_buffer.append( "(" );
5697                         }
5698                         _popup_buffer.append( tax.getAuthority() );
5699                         _popup_buffer.append( ")" );
5700                         enc_data = true;
5701                     }
5702                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5703                         if ( enc_data ) {
5704                             _popup_buffer.append( " [" );
5705                         }
5706                         else {
5707                             _popup_buffer.append( "[" );
5708                         }
5709                         _popup_buffer.append( tax.getRank() );
5710                         _popup_buffer.append( "]" );
5711                         enc_data = true;
5712                     }
5713                     if ( tax.getSynonyms().size() > 0 ) {
5714                         if ( enc_data ) {
5715                             _popup_buffer.append( " " );
5716                         }
5717                         _popup_buffer.append( "[" );
5718                         int counter = 1;
5719                         for( final String syn : tax.getSynonyms() ) {
5720                             if ( !ForesterUtil.isEmpty( syn ) ) {
5721                                 enc_data = true;
5722                                 _popup_buffer.append( syn );
5723                                 if ( counter < tax.getSynonyms().size() ) {
5724                                     _popup_buffer.append( ", " );
5725                                 }
5726                             }
5727                             counter++;
5728                         }
5729                         _popup_buffer.append( "]" );
5730                     }
5731                     if ( !enc_data ) {
5732                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5733                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5734                                 _popup_buffer.append( "[" );
5735                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5736                                 _popup_buffer.append( "] " );
5737                             }
5738                             _popup_buffer.append( tax.getIdentifier().getValue() );
5739                         }
5740                     }
5741                 }
5742                 if ( node.getNodeData().isHasSequence()
5743                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5744                     lines++;
5745                     boolean enc_data = false;
5746                     if ( _popup_buffer.length() > 0 ) {
5747                         _popup_buffer.append( "\n" );
5748                     }
5749                     final Sequence seq = node.getNodeData().getSequence();
5750                     if ( seq.getAccession() != null ) {
5751                         _popup_buffer.append( "[" );
5752                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5753                             _popup_buffer.append( seq.getAccession().getSource() );
5754                             _popup_buffer.append( ":" );
5755                         }
5756                         _popup_buffer.append( seq.getAccession().getValue() );
5757                         _popup_buffer.append( "]" );
5758                         enc_data = true;
5759                     }
5760                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5761                         if ( enc_data ) {
5762                             _popup_buffer.append( " [" );
5763                         }
5764                         else {
5765                             _popup_buffer.append( "[" );
5766                         }
5767                         _popup_buffer.append( seq.getSymbol() );
5768                         _popup_buffer.append( "]" );
5769                         enc_data = true;
5770                     }
5771                     if ( !ForesterUtil.isEmpty( seq.getGeneName() ) ) {
5772                         if ( enc_data ) {
5773                             _popup_buffer.append( " [" );
5774                         }
5775                         else {
5776                             _popup_buffer.append( "[" );
5777                         }
5778                         _popup_buffer.append( seq.getGeneName() );
5779                         _popup_buffer.append( "]" );
5780                         enc_data = true;
5781                     }
5782                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5783                         if ( enc_data ) {
5784                             _popup_buffer.append( " " );
5785                         }
5786                         _popup_buffer.append( seq.getName() );
5787                     }
5788                 }
5789                 if ( node.getNodeData().isHasDate() ) {
5790                     lines++;
5791                     if ( _popup_buffer.length() > 0 ) {
5792                         _popup_buffer.append( "\n" );
5793                     }
5794                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5795                 }
5796                 if ( node.getNodeData().isHasDistribution() ) {
5797                     lines++;
5798                     if ( _popup_buffer.length() > 0 ) {
5799                         _popup_buffer.append( "\n" );
5800                     }
5801                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5802                 }
5803                 if ( node.getBranchData().isHasConfidences() ) {
5804                     final List<Confidence> confs = node.getBranchData().getConfidences();
5805                     for( final Confidence confidence : confs ) {
5806                         lines++;
5807                         if ( _popup_buffer.length() > 0 ) {
5808                             _popup_buffer.append( "\n" );
5809                         }
5810                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5811                             _popup_buffer.append( "[" );
5812                             _popup_buffer.append( confidence.getType() );
5813                             _popup_buffer.append( "] " );
5814                         }
5815                         _popup_buffer
5816                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5817                                                                                           getOptions()
5818                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5819                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5820                             _popup_buffer.append( " (sd=" );
5821                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5822                                     .getStandardDeviation(), getOptions()
5823                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5824                             _popup_buffer.append( ")" );
5825                         }
5826                     }
5827                 }
5828                 if ( node.getNodeData().isHasProperties() ) {
5829                     final PropertiesMap properties = node.getNodeData().getProperties();
5830                     for( final String ref : properties.getPropertyRefs() ) {
5831                         _popup_buffer.append( "\n" );
5832                         final Property p = properties.getProperty( ref );
5833                         _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5834                         _popup_buffer.append( "=" );
5835                         _popup_buffer.append( p.getValue() );
5836                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5837                             _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5838                         }
5839                     }
5840                 }
5841                 if ( _popup_buffer.length() > 0 ) {
5842                     if ( !getConfiguration().isUseNativeUI() ) {
5843                         _rollover_popup
5844                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5845                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5846                         if ( isInFoundNodes0( node ) && !isInFoundNodes1( node ) ) {
5847                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0() );
5848                         }
5849                         else if ( !isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5850                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor1() );
5851                         }
5852                         else if ( isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5853                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0and1() );
5854                         }
5855                         else {
5856                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5857                         }
5858                     }
5859                     else {
5860                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5861                     }
5862                     _rollover_popup.setText( _popup_buffer.toString() );
5863                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5864                                                                                   _rollover_popup,
5865                                                                                   e.getLocationOnScreen().x + 10,
5866                                                                                   e.getLocationOnScreen().y
5867                                                                                           - ( lines * 20 ) );
5868                     _node_desc_popup.show();
5869                 }
5870             }
5871         }
5872         catch ( final Exception ex ) {
5873             // Do nothing.
5874         }
5875     }
5876
5877     final private void showNodeEditFrame( final PhylogenyNode n ) {
5878         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5879             // pop up edit box for single node
5880             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5881             _node_frame_index++;
5882         }
5883         else {
5884             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5885         }
5886     }
5887
5888     final private void showNodeFrame( final PhylogenyNode n ) {
5889         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5890             // pop up edit box for single node
5891             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5892             _node_frame_index++;
5893         }
5894         else {
5895             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5896         }
5897     }
5898
5899     final private void switchDisplaygetPhylogenyGraphicsType() {
5900         switch ( getPhylogenyGraphicsType() ) {
5901             case RECTANGULAR:
5902                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5903                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5904                 break;
5905             case EURO_STYLE:
5906                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5907                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5908                 break;
5909             case ROUNDED:
5910                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5911                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5912                 break;
5913             case CURVED:
5914                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5915                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5916                 break;
5917             case TRIANGULAR:
5918                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5919                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5920                 break;
5921             case CONVEX:
5922                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5923                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5924                 break;
5925             case UNROOTED:
5926                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5927                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5928                 break;
5929             case CIRCULAR:
5930                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5931                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5932                 break;
5933             default:
5934                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5935         }
5936         if ( getControlPanel().getDynamicallyHideData() != null ) {
5937             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5938                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5939             }
5940             else {
5941                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5942             }
5943         }
5944         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5945             getControlPanel().setDrawPhylogramEnabled( true );
5946         }
5947         else {
5948             getControlPanel().setDrawPhylogramEnabled( false );
5949         }
5950         if ( getMainPanel().getMainFrame() == null ) {
5951             // Must be "E" applet version.
5952             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5953                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5954         }
5955         else {
5956             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5957         }
5958     }
5959
5960     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
5961         if ( node.getNodeData().getNodeVisualData() == null ) {
5962             node.getNodeData().setNodeVisualData( new NodeVisualData() );
5963         }
5964         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
5965     }
5966
5967     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
5968         g.drawString( str, x, y );
5969     }
5970
5971     final private static boolean plusPressed( final int key_code ) {
5972         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5973                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5974     }
5975
5976     final private class NodeColorizationActionListener implements ActionListener {
5977
5978         List<PhylogenyNode> _additional_nodes = null;
5979         JColorChooser       _chooser          = null;
5980         PhylogenyNode       _node             = null;
5981
5982         NodeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5983             _chooser = chooser;
5984             _node = node;
5985         }
5986
5987         NodeColorizationActionListener( final JColorChooser chooser,
5988                                         final PhylogenyNode node,
5989                                         final List<PhylogenyNode> additional_nodes ) {
5990             _chooser = chooser;
5991             _node = node;
5992             _additional_nodes = additional_nodes;
5993         }
5994
5995         @Override
5996         public void actionPerformed( final ActionEvent e ) {
5997             final Color c = _chooser.getColor();
5998             if ( c != null ) {
5999                 colorizeNodes( c, _node, _additional_nodes );
6000             }
6001         }
6002     }
6003
6004     final private class SubtreeColorizationActionListener implements ActionListener {
6005
6006         List<PhylogenyNode> _additional_nodes = null;
6007         JColorChooser       _chooser          = null;
6008         PhylogenyNode       _node             = null;
6009
6010         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
6011             _chooser = chooser;
6012             _node = node;
6013         }
6014
6015         SubtreeColorizationActionListener( final JColorChooser chooser,
6016                                            final PhylogenyNode node,
6017                                            final List<PhylogenyNode> additional_nodes ) {
6018             _chooser = chooser;
6019             _node = node;
6020             _additional_nodes = additional_nodes;
6021         }
6022
6023         @Override
6024         public void actionPerformed( final ActionEvent e ) {
6025             final Color c = _chooser.getColor();
6026             if ( c != null ) {
6027                 colorizeSubtree( c, _node, _additional_nodes );
6028             }
6029         }
6030     }
6031 }