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