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