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