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