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