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