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