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