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