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