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