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