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