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