af982e4212d9b253ee9a998f95b068a666db046c
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanel.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: www.phylosoft.org/forester
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.BasicStroke;
29 import java.awt.Color;
30 import java.awt.Cursor;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.GradientPaint;
34 import java.awt.Graphics;
35 import java.awt.Graphics2D;
36 import java.awt.Point;
37 import java.awt.Polygon;
38 import java.awt.Rectangle;
39 import java.awt.RenderingHints;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.awt.event.FocusAdapter;
43 import java.awt.event.FocusEvent;
44 import java.awt.event.InputEvent;
45 import java.awt.event.KeyAdapter;
46 import java.awt.event.KeyEvent;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.MouseWheelEvent;
49 import java.awt.event.MouseWheelListener;
50 import java.awt.font.FontRenderContext;
51 import java.awt.font.TextLayout;
52 import java.awt.geom.AffineTransform;
53 import java.awt.geom.Arc2D;
54 import java.awt.geom.CubicCurve2D;
55 import java.awt.geom.Ellipse2D;
56 import java.awt.geom.Line2D;
57 import java.awt.geom.QuadCurve2D;
58 import java.awt.geom.Rectangle2D;
59 import java.awt.image.BufferedImage;
60 import java.awt.print.PageFormat;
61 import java.awt.print.Printable;
62 import java.awt.print.PrinterException;
63 import java.io.File;
64 import java.io.IOException;
65 import java.io.UnsupportedEncodingException;
66 import java.net.URI;
67 import java.net.URISyntaxException;
68 import java.net.URLEncoder;
69 import java.text.DecimalFormat;
70 import java.text.DecimalFormatSymbols;
71 import java.text.NumberFormat;
72 import java.util.ArrayList;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.HashSet;
76 import java.util.Hashtable;
77 import java.util.List;
78 import java.util.Set;
79
80 import javax.swing.BorderFactory;
81 import javax.swing.JApplet;
82 import javax.swing.JColorChooser;
83 import javax.swing.JDialog;
84 import javax.swing.JMenuItem;
85 import javax.swing.JOptionPane;
86 import javax.swing.JPanel;
87 import javax.swing.JPopupMenu;
88 import javax.swing.JTextArea;
89 import javax.swing.Popup;
90 import javax.swing.PopupFactory;
91
92 import org.forester.archaeopteryx.Configuration.EXT_NODE_DATA_RETURN_ON;
93 import org.forester.archaeopteryx.ControlPanel.NodeClickAction;
94 import org.forester.archaeopteryx.Options.CLADOGRAM_TYPE;
95 import org.forester.archaeopteryx.Options.NODE_LABEL_DIRECTION;
96 import org.forester.archaeopteryx.Options.PHYLOGENY_GRAPHICS_TYPE;
97 import org.forester.archaeopteryx.phylogeny.data.RenderableDomainArchitecture;
98 import org.forester.archaeopteryx.phylogeny.data.RenderableVector;
99 import org.forester.archaeopteryx.tools.Blast;
100 import org.forester.archaeopteryx.tools.ImageLoader;
101 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
102 import org.forester.phylogeny.Phylogeny;
103 import org.forester.phylogeny.PhylogenyMethods;
104 import org.forester.phylogeny.PhylogenyMethods.DESCENDANT_SORT_PRIORITY;
105 import org.forester.phylogeny.PhylogenyNode;
106 import org.forester.phylogeny.data.Annotation;
107 import org.forester.phylogeny.data.BranchColor;
108 import org.forester.phylogeny.data.Confidence;
109 import org.forester.phylogeny.data.Event;
110 import org.forester.phylogeny.data.NodeVisualization;
111 import org.forester.phylogeny.data.NodeVisualization.NodeFill;
112 import org.forester.phylogeny.data.NodeVisualization.NodeShape;
113 import org.forester.phylogeny.data.PhylogenyData;
114 import org.forester.phylogeny.data.PhylogenyDataUtil;
115 import org.forester.phylogeny.data.PropertiesMap;
116 import org.forester.phylogeny.data.Property;
117 import org.forester.phylogeny.data.Sequence;
118 import org.forester.phylogeny.data.SequenceRelation;
119 import org.forester.phylogeny.data.Taxonomy;
120 import org.forester.phylogeny.data.Uri;
121 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
122 import org.forester.phylogeny.iterators.PreorderTreeIterator;
123 import org.forester.util.BasicDescriptiveStatistics;
124 import org.forester.util.DescriptiveStatistics;
125 import org.forester.util.ForesterConstants;
126 import org.forester.util.ForesterUtil;
127
128 public final class TreePanel extends JPanel implements ActionListener, MouseWheelListener, Printable {
129
130     private static final float              PI                                = ( float ) ( Math.PI );
131     private static final double             TWO_PI                            = 2 * Math.PI;
132     private static final float              ONEHALF_PI                        = ( float ) ( 1.5 * Math.PI );
133     private static final float              HALF_PI                           = ( float ) ( Math.PI / 2.0 );
134     private static final float              ANGLE_ROTATION_UNIT               = ( float ) ( Math.PI / 32 );
135     private static final short              OV_BORDER                         = 10;
136     final static Cursor                     CUT_CURSOR                        = Cursor.getPredefinedCursor( Cursor.CROSSHAIR_CURSOR );
137     final static Cursor                     MOVE_CURSOR                       = Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR );
138     final static Cursor                     ARROW_CURSOR                      = Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR );
139     final static Cursor                     HAND_CURSOR                       = Cursor.getPredefinedCursor( Cursor.HAND_CURSOR );
140     final static Cursor                     WAIT_CURSOR                       = Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR );
141     private final static long               serialVersionUID                  = -978349745916505029L;
142     private final static int                EURO_D                            = 10;
143     private final static String             NODE_POPMENU_NODE_CLIENT_PROPERTY = "node";
144     private final static int                MIN_ROOT_LENGTH                   = 3;
145     private final static int                MAX_SUBTREES                      = 100;
146     private final static int                MAX_NODE_FRAMES                   = 10;
147     private final static int                MOVE                              = 20;
148     private final static NumberFormat       FORMATTER_CONFIDENCE;
149     private final static NumberFormat       FORMATTER_BRANCH_LENGTH;
150     private final static int                WIGGLE                            = 2;
151     private final static int                LIMIT_FOR_HQ_RENDERING            = 1000;
152     private final static int                CONFIDENCE_LEFT_MARGIN            = 4;
153     // TODO "rendering_hints" was static before. Need to make sure everything is OK with it not
154     // being static anymore (02/20/2009).
155     private final RenderingHints            _rendering_hints                  = new RenderingHints( RenderingHints.KEY_RENDERING,
156                                                                                                     RenderingHints.VALUE_RENDER_DEFAULT );
157     private File                            _treefile                         = null;
158     private Configuration                   _configuration                    = null;
159     private final NodeFrame[]               _node_frames                      = new NodeFrame[ TreePanel.MAX_NODE_FRAMES ];
160     private int                             _node_frame_index                 = 0;
161     private Phylogeny                       _phylogeny                        = null;
162     private final Phylogeny[]               _sub_phylogenies                  = new Phylogeny[ TreePanel.MAX_SUBTREES ];
163     private final PhylogenyNode[]           _sub_phylogenies_temp_roots       = new PhylogenyNode[ TreePanel.MAX_SUBTREES ];
164     private int                             _subtree_index                    = 0;
165     private MainPanel                       _main_panel                       = null;
166     private Set<Integer>                    _found_nodes                      = null;
167     private PhylogenyNode                   _highlight_node                   = null;
168     private JPopupMenu                      _node_popup_menu                  = null;
169     private JMenuItem                       _node_popup_menu_items[]          = null;
170     private int                             _longest_ext_node_info            = 0;
171     private float                           _x_correction_factor              = 0.0f;
172     private float                           _ov_x_correction_factor           = 0.0f;
173     private float                           _x_distance                       = 0.0f;
174     private float                           _y_distance                       = 0.0f;
175     private PHYLOGENY_GRAPHICS_TYPE         _graphics_type                    = PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR;
176     private double                          _domain_structure_width           = Constants.DOMAIN_STRUCTURE_DEFAULT_WIDTH;
177     private int                             _domain_structure_e_value_thr_exp = Constants.DOMAIN_STRUCTURE_E_VALUE_THR_DEFAULT_EXP;
178     private float                           _last_drag_point_x                = 0;
179     private float                           _last_drag_point_y                = 0;
180     private ControlPanel                    _control_panel                    = null;
181     private int                             _external_node_index              = 0;
182     private final Polygon                   _polygon                          = new Polygon();
183     private final StringBuilder             _sb                               = new StringBuilder();
184     private JColorChooser                   _color_chooser                    = null;
185     private double                          _scale_distance                   = 0.0;
186     private String                          _scale_label                      = null;
187     private final CubicCurve2D              _cubic_curve                      = new CubicCurve2D.Float();
188     private final QuadCurve2D               _quad_curve                       = new QuadCurve2D.Float();
189     private final Line2D                    _line                             = new Line2D.Float();
190     private final Ellipse2D                 _ellipse                          = new Ellipse2D.Float();
191     private final Rectangle2D               _rectangle                        = new Rectangle2D.Float();
192     private Options                         _options                          = null;
193     private float                           _ov_max_width                     = 0;
194     private float                           _ov_max_height                    = 0;
195     private int                             _ov_x_position                    = 0;
196     private int                             _ov_y_position                    = 0;
197     private int                             _ov_y_start                       = 0;
198     private float                           _ov_y_distance                    = 0;
199     private float                           _ov_x_distance                    = 0;
200     private boolean                         _ov_on                            = false;
201     private double                          _urt_starting_angle               = ( float ) ( Math.PI / 2 );
202     private float                           _urt_factor                       = 1;
203     private float                           _urt_factor_ov                    = 1;
204     private final boolean                   _phy_has_branch_lengths;
205     private final Rectangle2D               _ov_rectangle                     = new Rectangle2D.Float();
206     private boolean                         _in_ov_rect                       = false;
207     private boolean                         _in_ov                            = false;
208     private final Rectangle                 _ov_virtual_rectangle             = new Rectangle();
209     final private static double             _180_OVER_PI                      = 180.0 / Math.PI;
210     private static final float              ROUNDED_D                         = 8;
211     private int                             _circ_max_depth;
212     private PhylogenyNode                   _root;
213     final private Arc2D                     _arc                              = new Arc2D.Double();
214     final private HashMap<Integer, Double>  _urt_nodeid_angle_map             = new HashMap<Integer, Double>();
215     final private HashMap<Integer, Integer> _urt_nodeid_index_map             = new HashMap<Integer, Integer>();
216     final private Set<Integer>              _collapsed_external_nodeid_set    = new HashSet<Integer>();
217     HashMap<Integer, Short>                 _nodeid_dist_to_leaf              = new HashMap<Integer, Short>();
218     private AffineTransform                 _at;
219     private double                          _max_distance_to_root             = -1;
220     private int                             _dynamic_hiding_factor            = 0;
221     private boolean                         _edited                           = false;
222     private Popup                           _node_desc_popup;
223     private JTextArea                       _rollover_popup;
224     // private final int                       _box_size;
225     // private final int                       _half_box_size;
226     //private final short                     _skip_counter                     = 0;
227     private final StringBuffer              _popup_buffer                     = new StringBuffer();
228     final private static Font               POPUP_FONT                        = new Font( Configuration.getDefaultFontFamilyName(),
229                                                                                           Font.PLAIN,
230                                                                                           12 );
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         final List<String> data = new ArrayList<String>();
1350         for( final PhylogenyNode n : node.getAllExternalDescendants() ) {
1351             switch ( getOptions().getExtDescNodeDataToReturn() ) {
1352                 case NODE_NAME:
1353                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
1354                         data.add( n.getName() );
1355                     }
1356                     break;
1357                 case SEQUENCE_NAME:
1358                     if ( n.getNodeData().isHasSequence()
1359                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
1360                         data.add( n.getNodeData().getSequence().getName() );
1361                     }
1362                     break;
1363                 case SEQUENCE_SYMBOL:
1364                     if ( n.getNodeData().isHasSequence()
1365                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
1366                         data.add( n.getNodeData().getSequence().getSymbol() );
1367                     }
1368                     break;
1369                 case SEQUENCE_MOL_SEQ:
1370                     if ( n.getNodeData().isHasSequence()
1371                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
1372                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
1373                     }
1374                     break;
1375                 case SEQUENCE_ACC:
1376                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null ) ) {
1377                         data.add( n.getNodeData().getSequence().getAccession().toString() );
1378                     }
1379                     break;
1380                 case TAXONOMY_SCIENTIFIC_NAME:
1381                     if ( n.getNodeData().isHasTaxonomy()
1382                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
1383                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
1384                     }
1385                     break;
1386                 case TAXONOMY_CODE:
1387                     if ( n.getNodeData().isHasTaxonomy()
1388                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
1389                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
1390                     }
1391                     break;
1392                 default:
1393                     throw new IllegalArgumentException( "unknown data element: "
1394                             + getOptions().getExtDescNodeDataToReturn() );
1395             }
1396             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
1397                 for( final String d : data ) {
1398                     System.out.println( d );
1399                 }
1400             }
1401             else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
1402                 final StringBuilder sb = new StringBuilder();
1403                 for( final String d : data ) {
1404                     sb.append( d );
1405                     sb.append( "\n" );
1406                 }
1407                 getMainPanel().getMainFrame().showTextFrame( sb.toString() );
1408             }
1409         }
1410     }
1411
1412     final void increaseDomainStructureEvalueThreshold() {
1413         if ( _domain_structure_e_value_thr_exp < 3 ) {
1414             _domain_structure_e_value_thr_exp += 1;
1415         }
1416     }
1417
1418     final private void increaseOvSize() {
1419         if ( ( getOvMaxWidth() < getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth() / 2 )
1420                 && ( getOvMaxHeight() < getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
1421                         .getHeight() / 2 ) ) {
1422             setOvMaxWidth( getOvMaxWidth() + 5 );
1423             setOvMaxHeight( getOvMaxHeight() + 5 );
1424             updateOvSettings();
1425             getControlPanel().displayedPhylogenyMightHaveChanged( false );
1426         }
1427     }
1428
1429     final void inferCommonPartOfScientificNames() {
1430         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
1431             return;
1432         }
1433         setWaitCursor();
1434         AptxUtil.inferCommonPartOfScientificNames( _phylogeny );
1435         setArrowCursor();
1436         repaint();
1437     }
1438
1439     final private void init() {
1440         _color_chooser = new JColorChooser();
1441         _rollover_popup = new JTextArea();
1442         _rollover_popup.setFont( POPUP_FONT );
1443         resetNodeIdToDistToLeafMap();
1444         setTextAntialias();
1445         setTreeFile( null );
1446         setEdited( false );
1447         initializeOvSettings();
1448         setStartingAngle( TWO_PI * 3 / 4 );
1449         final ImageLoader il = new ImageLoader( this );
1450         new Thread( il ).start();
1451     }
1452
1453     final private void initializeOvSettings() {
1454         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
1455         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
1456     }
1457
1458     final void initNodeData() {
1459         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
1460             return;
1461         }
1462         double max_original_domain_structure_width = 0.0;
1463         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
1464             if ( node.getNodeData().isHasSequence()
1465                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1466                 RenderableDomainArchitecture rds = null;
1467                 if ( !( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
1468                     rds = new RenderableDomainArchitecture( node.getNodeData().getSequence().getDomainArchitecture(),
1469                                                             getConfiguration() );
1470                     node.getNodeData().getSequence().setDomainArchitecture( rds );
1471                 }
1472                 else {
1473                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
1474                 }
1475                 if ( getControlPanel().isShowDomainArchitectures() ) {
1476                     final double dsw = rds.getOriginalSize().getWidth();
1477                     if ( dsw > max_original_domain_structure_width ) {
1478                         max_original_domain_structure_width = dsw;
1479                     }
1480                 }
1481             }
1482         }
1483         if ( getControlPanel().isShowDomainArchitectures() ) {
1484             final double ds_factor_width = _domain_structure_width / max_original_domain_structure_width;
1485             for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
1486                 if ( node.getNodeData().isHasSequence()
1487                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1488                     final RenderableDomainArchitecture rds = ( RenderableDomainArchitecture ) node.getNodeData()
1489                             .getSequence().getDomainArchitecture();
1490                     rds.setRenderingFactorWidth( ds_factor_width );
1491                     rds.setParameter( _domain_structure_e_value_thr_exp );
1492                 }
1493             }
1494         }
1495     }
1496
1497     final boolean inOv( final MouseEvent e ) {
1498         return ( ( e.getX() > getVisibleRect().x + getOvXPosition() + 1 )
1499                 && ( e.getX() < getVisibleRect().x + getOvXPosition() + getOvMaxWidth() - 1 )
1500                 && ( e.getY() > getVisibleRect().y + getOvYPosition() + 1 ) && ( e.getY() < getVisibleRect().y
1501                 + getOvYPosition() + getOvMaxHeight() - 1 ) );
1502     }
1503
1504     final boolean inOvRectangle( final MouseEvent e ) {
1505         return ( ( e.getX() >= getOvRectangle().getX() - 1 )
1506                 && ( e.getX() <= getOvRectangle().getX() + getOvRectangle().getWidth() + 1 )
1507                 && ( e.getY() >= getOvRectangle().getY() - 1 ) && ( e.getY() <= getOvRectangle().getY()
1508                 + getOvRectangle().getHeight() + 1 ) );
1509     }
1510
1511     final private boolean inOvVirtualRectangle( final int x, final int y ) {
1512         return ( ( x >= getOvVirtualRectangle().x - 1 )
1513                 && ( x <= getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 )
1514                 && ( y >= getOvVirtualRectangle().y - 1 ) && ( y <= getOvVirtualRectangle().y
1515                 + getOvVirtualRectangle().height + 1 ) );
1516     }
1517
1518     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
1519         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
1520     }
1521
1522     final boolean isApplet() {
1523         return getMainPanel() instanceof MainPanelApplets;
1524     }
1525
1526     final private boolean isCanBlast( final PhylogenyNode node ) {
1527         return ( node.getNodeData().isHasSequence()
1528                 && ( ( ( node.getNodeData().getSequence().getAccession() != null ) && !ForesterUtil.isEmpty( node
1529                         .getNodeData().getSequence().getAccession().getValue() ) )
1530                         || !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) || !ForesterUtil
1531                         .isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) && Blast
1532                 .isContainsQueryForBlast( node ) );
1533     }
1534
1535     final boolean isCanCollapse() {
1536         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
1537     }
1538
1539     final boolean isCanColorSubtree() {
1540         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
1541     }
1542
1543     final boolean isCanCopy() {
1544         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
1545     }
1546
1547     final boolean isCanCut( final PhylogenyNode node ) {
1548         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() && !node
1549                 .isRoot() );
1550     }
1551
1552     final boolean isCanDelete() {
1553         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
1554     }
1555
1556     final private boolean isCanOpenSeqWeb( final PhylogenyNode node ) {
1557         if ( node.getNodeData().isHasSequence()
1558                 && ( node.getNodeData().getSequence().getAccession() != null )
1559                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() )
1560                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getValue() )
1561                 && getConfiguration().isHasWebLink( node.getNodeData().getSequence().getAccession().getSource()
1562                         .toLowerCase() ) ) {
1563             return true;
1564         }
1565         return false;
1566     }
1567
1568     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
1569         if ( node.getNodeData().isHasTaxonomy()
1570                 && ( ( ( node.getNodeData().getTaxonomy().getIdentifier() != null )
1571                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getProvider() )
1572                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && getConfiguration()
1573                         .isHasWebLink( node.getNodeData().getTaxonomy().getIdentifier().getProvider().toLowerCase() ) )
1574                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
1575                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
1576                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) || ( ( node
1577                         .getNodeData().getTaxonomy().getIdentifier() != null )
1578                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && node
1579                         .getNodeData().getTaxonomy().getIdentifier().getValue().startsWith( "http://" ) ) ) ) {
1580             return true;
1581         }
1582         else {
1583             return false;
1584         }
1585     }
1586
1587     final boolean isCanPaste() {
1588         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
1589                 && ( getCutOrCopiedTree() != null ) && !getCutOrCopiedTree().isEmpty() );
1590     }
1591
1592     final boolean isCanReroot() {
1593         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && ( _subtree_index < 1 ) );
1594     }
1595
1596     final boolean isCanSubtree( final PhylogenyNode node ) {
1597         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && !node.isExternal() && ( !node
1598                 .isRoot() || ( _subtree_index > 0 ) ) );
1599     }
1600
1601     final boolean isEdited() {
1602         return _edited;
1603     }
1604
1605     final private boolean isInFoundNodes( final PhylogenyNode node ) {
1606         return ( ( getFoundNodes() != null ) && getFoundNodes().contains( node.getId() ) );
1607     }
1608
1609     final private boolean isInOv() {
1610         return _in_ov;
1611     }
1612
1613     final boolean isInOvRect() {
1614         return _in_ov_rect;
1615     }
1616
1617     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
1618         int y_dist = 40;
1619         if ( getControlPanel().isShowTaxonomyImages() ) {
1620             y_dist = 40 + ( int ) getYdistance();
1621         }
1622         return ( ( node.getYcoord() < getVisibleRect().getMinY() - y_dist )
1623                 || ( node.getYcoord() > getVisibleRect().getMaxY() + y_dist ) || ( ( node.getParent() != null ) && ( node
1624                 .getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
1625     }
1626
1627     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
1628         return ( ( node.getYcoord() < getVisibleRect().getMinY() - 20 )
1629                 || ( node.getYcoord() > getVisibleRect().getMaxY() + 20 )
1630                 || ( node.getXcoord() < getVisibleRect().getMinX() - 20 ) || ( node.getXcoord() > getVisibleRect()
1631                 .getMaxX() + 20 ) );
1632     }
1633
1634     final private boolean isNonLinedUpCladogram() {
1635         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
1636     }
1637
1638     final boolean isOvOn() {
1639         return _ov_on;
1640     }
1641
1642     final boolean isPhyHasBranchLengths() {
1643         return _phy_has_branch_lengths;
1644     }
1645
1646     final private boolean isUniformBranchLengthsForCladogram() {
1647         return getOptions().getCladogramType() == CLADOGRAM_TYPE.TOTAL_NODE_SUM_DEP;
1648     }
1649
1650     final private void keyPressedCalls( final KeyEvent e ) {
1651         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
1652             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
1653                 if ( !isInOvRect() ) {
1654                     setInOvRect( true );
1655                 }
1656             }
1657             else if ( isInOvRect() ) {
1658                 setInOvRect( false );
1659             }
1660         }
1661         if ( e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK ) {
1662             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
1663                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
1664                 getMainPanel().getTreeFontSet().mediumFonts();
1665                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1666             }
1667             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
1668                 getMainPanel().getTreeFontSet().decreaseFontSize();
1669                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1670             }
1671             else if ( plusPressed( e.getKeyCode() ) ) {
1672                 getMainPanel().getTreeFontSet().increaseFontSize();
1673                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1674             }
1675         }
1676         else {
1677             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
1678                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
1679                 getControlPanel().showWhole();
1680             }
1681             else if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
1682                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
1683                 if ( e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK ) {
1684                     if ( e.getKeyCode() == KeyEvent.VK_UP ) {
1685                         getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
1686                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1687                     }
1688                     else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
1689                         getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
1690                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1691                     }
1692                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
1693                         getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
1694                                                                    Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
1695                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1696                     }
1697                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
1698                         getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
1699                                                                   Constants.WHEEL_ZOOM_IN_FACTOR );
1700                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1701                     }
1702                 }
1703                 else {
1704                     final int d = 80;
1705                     int dx = 0;
1706                     int dy = -d;
1707                     if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
1708                         dy = d;
1709                     }
1710                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
1711                         dx = -d;
1712                         dy = 0;
1713                     }
1714                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
1715                         dx = d;
1716                         dy = 0;
1717                     }
1718                     final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1719                     scroll_position.x = scroll_position.x + dx;
1720                     scroll_position.y = scroll_position.y + dy;
1721                     if ( scroll_position.x <= 0 ) {
1722                         scroll_position.x = 0;
1723                     }
1724                     else {
1725                         final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1726                                 - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1727                         if ( scroll_position.x >= max_x ) {
1728                             scroll_position.x = max_x;
1729                         }
1730                     }
1731                     if ( scroll_position.y <= 0 ) {
1732                         scroll_position.y = 0;
1733                     }
1734                     else {
1735                         final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1736                                 - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1737                         if ( scroll_position.y >= max_y ) {
1738                             scroll_position.y = max_y;
1739                         }
1740                     }
1741                     repaint();
1742                     getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1743                 }
1744             }
1745             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
1746                 getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
1747                 getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
1748                                                            Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
1749                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1750             }
1751             else if ( plusPressed( e.getKeyCode() ) ) {
1752                 getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
1753                                                           Constants.WHEEL_ZOOM_IN_FACTOR );
1754                 getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
1755                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1756             }
1757             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
1758                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1759                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1760                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
1761                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
1762                 }
1763             }
1764             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
1765                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1766                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1767                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
1768                     if ( getStartingAngle() < 0 ) {
1769                         setStartingAngle( TWO_PI + getStartingAngle() );
1770                     }
1771                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
1772                 }
1773             }
1774             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
1775                 boolean selected = false;
1776                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
1777                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
1778                     selected = true;
1779                 }
1780                 else {
1781                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
1782                 }
1783                 if ( getMainPanel().getMainFrame() == null ) {
1784                     // Must be "E" applet version.
1785                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
1786                     if ( ae.getlabelDirectionCbmi() != null ) {
1787                         ae.getlabelDirectionCbmi().setSelected( selected );
1788                     }
1789                 }
1790                 else {
1791                     getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
1792                 }
1793                 repaint();
1794             }
1795             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
1796                 switchDisplaygetPhylogenyGraphicsType();
1797                 repaint();
1798             }
1799             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
1800                 cycleColors();
1801                 repaint();
1802             }
1803             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
1804                 MainFrame.cycleOverview( getOptions(), this );
1805                 repaint();
1806             }
1807             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
1808                 increaseOvSize();
1809             }
1810             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
1811                 decreaseOvSize();
1812             }
1813             e.consume();
1814         }
1815     }
1816
1817     final private void makePopupMenus( final PhylogenyNode node ) {
1818         _node_popup_menu = new JPopupMenu();
1819         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
1820         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
1821         for( int i = 0; i < clickto_names.size(); i++ ) {
1822             final String title = clickto_names.get( i );
1823             _node_popup_menu_items[ i ] = new JMenuItem( title );
1824             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
1825                 _node_popup_menu_items[ i ].setEnabled( isCanOpenSeqWeb( node ) );
1826             }
1827             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
1828                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
1829             }
1830             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
1831                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
1832             }
1833             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
1834                 if ( !getOptions().isEditable() ) {
1835                     continue;
1836                 }
1837                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
1838             }
1839             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
1840                 if ( !getOptions().isEditable() ) {
1841                     continue;
1842                 }
1843                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
1844             }
1845             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
1846                 if ( !getOptions().isEditable() ) {
1847                     continue;
1848                 }
1849                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
1850             }
1851             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
1852                 if ( !getOptions().isEditable() ) {
1853                     continue;
1854                 }
1855                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
1856             }
1857             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
1858                 if ( !getOptions().isEditable() ) {
1859                     continue;
1860                 }
1861             }
1862             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
1863                 if ( !getOptions().isEditable() ) {
1864                     continue;
1865                 }
1866             }
1867             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
1868                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
1869             }
1870             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
1871                 _node_popup_menu_items[ i ].setEnabled( ( isCanCollapse() && !node.isExternal() ) );
1872             }
1873             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
1874                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
1875             }
1876             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
1877                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
1878             }
1879             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
1880                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
1881             }
1882             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
1883                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
1884             }
1885             _node_popup_menu_items[ i ].addActionListener( this );
1886             _node_popup_menu.add( _node_popup_menu_items[ i ] );
1887         }
1888     }
1889
1890     final void midpointRoot() {
1891         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
1892             return;
1893         }
1894         if ( !_phylogeny.isRerootable() ) {
1895             JOptionPane.showMessageDialog( this,
1896                                            "This is not rerootable",
1897                                            "Not rerootable",
1898                                            JOptionPane.WARNING_MESSAGE );
1899             return;
1900         }
1901         setNodeInPreorderToNull();
1902         setWaitCursor();
1903         PhylogenyMethods.midpointRoot( _phylogeny );
1904         resetNodeIdToDistToLeafMap();
1905         setArrowCursor();
1906         repaint();
1907     }
1908
1909     final void mouseClicked( final MouseEvent e ) {
1910         if ( getOptions().isShowOverview() && isOvOn() && isInOv() ) {
1911             final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
1912             final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
1913             double x = ( e.getX() - getVisibleRect().x - getOvXPosition() - getOvRectangle().getWidth() / 2.0 )
1914                     * w_ratio;
1915             double y = ( e.getY() - getVisibleRect().y - getOvYPosition() - getOvRectangle().getHeight() / 2.0 )
1916                     * h_ratio;
1917             if ( x < 0 ) {
1918                 x = 0;
1919             }
1920             if ( y < 0 ) {
1921                 y = 0;
1922             }
1923             final double max_x = getWidth() - getVisibleRect().width;
1924             final double max_y = getHeight() - getVisibleRect().height;
1925             if ( x > max_x ) {
1926                 x = max_x;
1927             }
1928             if ( y > max_y ) {
1929                 y = max_y;
1930             }
1931             getMainPanel().getCurrentScrollPane().getViewport()
1932                     .setViewPosition( new Point( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ) ) );
1933             setInOvRect( true );
1934             repaint();
1935         }
1936         else {
1937             final PhylogenyNode node = findNode( e.getX(), e.getY() );
1938             if ( node != null ) {
1939                 if ( !node.isRoot() && node.getParent().isCollapse() ) {
1940                     return;
1941                 }
1942                 _highlight_node = node;
1943                 // Check if shift key is down
1944                 if ( ( e.getModifiers() & InputEvent.SHIFT_MASK ) != 0 ) {
1945                     // Yes, so add to _found_nodes
1946                     if ( getFoundNodes() == null ) {
1947                         setFoundNodes( new HashSet<Integer>() );
1948                     }
1949                     getFoundNodes().add( node.getId() );
1950                     // Check if control key is down
1951                 }
1952                 else if ( ( e.getModifiers() & InputEvent.CTRL_MASK ) != 0 ) {
1953                     // Yes, so pop-up menu
1954                     displayNodePopupMenu( node, e.getX(), e.getY() );
1955                     // Handle unadorned click
1956                 }
1957                 else {
1958                     // Check for right mouse button
1959                     if ( e.getModifiers() == 4 ) {
1960                         displayNodePopupMenu( node, e.getX(), e.getY() );
1961                     }
1962                     else {
1963                         // if not in _found_nodes, clear _found_nodes
1964                         handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
1965                     }
1966                 }
1967             }
1968             else {
1969                 // no node was clicked
1970                 _highlight_node = null;
1971             }
1972         }
1973         repaint();
1974     }
1975
1976     final void mouseDragInBrowserPanel( final MouseEvent e ) {
1977         setCursor( MOVE_CURSOR );
1978         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1979         scroll_position.x -= ( e.getX() - getLastDragPointX() );
1980         scroll_position.y -= ( e.getY() - getLastDragPointY() );
1981         if ( scroll_position.x < 0 ) {
1982             scroll_position.x = 0;
1983         }
1984         else {
1985             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1986                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1987             if ( scroll_position.x > max_x ) {
1988                 scroll_position.x = max_x;
1989             }
1990         }
1991         if ( scroll_position.y < 0 ) {
1992             scroll_position.y = 0;
1993         }
1994         else {
1995             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1996                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1997             if ( scroll_position.y > max_y ) {
1998                 scroll_position.y = max_y;
1999             }
2000         }
2001         if ( isOvOn() || getOptions().isShowScale() ) {
2002             repaint();
2003         }
2004         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
2005     }
2006
2007     final void mouseDragInOvRectangle( final MouseEvent e ) {
2008         setCursor( HAND_CURSOR );
2009         final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
2010         final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
2011         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
2012         double dx = ( w_ratio * e.getX() - w_ratio * getLastDragPointX() );
2013         double dy = ( h_ratio * e.getY() - h_ratio * getLastDragPointY() );
2014         scroll_position.x = ForesterUtil.roundToInt( scroll_position.x + dx );
2015         scroll_position.y = ForesterUtil.roundToInt( scroll_position.y + dy );
2016         if ( scroll_position.x <= 0 ) {
2017             scroll_position.x = 0;
2018             dx = 0;
2019         }
2020         else {
2021             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
2022                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
2023             if ( scroll_position.x >= max_x ) {
2024                 dx = 0;
2025                 scroll_position.x = max_x;
2026             }
2027         }
2028         if ( scroll_position.y <= 0 ) {
2029             dy = 0;
2030             scroll_position.y = 0;
2031         }
2032         else {
2033             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
2034                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
2035             if ( scroll_position.y >= max_y ) {
2036                 dy = 0;
2037                 scroll_position.y = max_y;
2038             }
2039         }
2040         repaint();
2041         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
2042         setLastMouseDragPointX( ( float ) ( e.getX() + dx ) );
2043         setLastMouseDragPointY( ( float ) ( e.getY() + dy ) );
2044     }
2045
2046     final void mouseMoved( final MouseEvent e ) {
2047         requestFocusInWindow();
2048         if ( getControlPanel().isNodeDescPopup() ) {
2049             if ( _node_desc_popup != null ) {
2050                 _node_desc_popup.hide();
2051                 _node_desc_popup = null;
2052             }
2053         }
2054         if ( getOptions().isShowOverview() && isOvOn() ) {
2055             if ( inOvVirtualRectangle( e ) ) {
2056                 if ( !isInOvRect() ) {
2057                     setInOvRect( true );
2058                     repaint();
2059                 }
2060             }
2061             else {
2062                 if ( isInOvRect() ) {
2063                     setInOvRect( false );
2064                     repaint();
2065                 }
2066             }
2067         }
2068         if ( inOv( e ) && getOptions().isShowOverview() && isOvOn() ) {
2069             if ( !isInOv() ) {
2070                 setInOv( true );
2071             }
2072         }
2073         else {
2074             if ( isInOv() ) {
2075                 setInOv( false );
2076             }
2077             final PhylogenyNode node = findNode( e.getX(), e.getY() );
2078             if ( ( node != null ) && ( node.isRoot() || !node.getParent().isCollapse() ) ) {
2079                 // cursor is over a tree node
2080                 if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.CUT_SUBTREE )
2081                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.COPY_SUBTREE )
2082                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.PASTE_SUBTREE )
2083                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.DELETE_NODE_OR_SUBTREE )
2084                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.REROOT )
2085                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.ADD_NEW_NODE ) ) {
2086                     setCursor( CUT_CURSOR );
2087                 }
2088                 else {
2089                     setCursor( HAND_CURSOR );
2090                     if ( getControlPanel().isNodeDescPopup() ) {
2091                         showNodeDataPopup( e, node );
2092                     }
2093                 }
2094             }
2095             else {
2096                 setCursor( ARROW_CURSOR );
2097             }
2098         }
2099     }
2100
2101     final void mouseReleasedInBrowserPanel( final MouseEvent e ) {
2102         setCursor( ARROW_CURSOR );
2103     }
2104
2105     final public void mouseWheelMoved( final MouseWheelEvent e ) {
2106         final int notches = e.getWheelRotation();
2107         if ( inOvVirtualRectangle( e ) ) {
2108             if ( !isInOvRect() ) {
2109                 setInOvRect( true );
2110                 repaint();
2111             }
2112         }
2113         else {
2114             if ( isInOvRect() ) {
2115                 setInOvRect( false );
2116                 repaint();
2117             }
2118         }
2119         if ( e.isControlDown() ) {
2120             if ( notches < 0 ) {
2121                 getTreeFontSet().increaseFontSize();
2122                 getControlPanel().displayedPhylogenyMightHaveChanged( true );
2123             }
2124             else {
2125                 getTreeFontSet().decreaseFontSize();
2126                 getControlPanel().displayedPhylogenyMightHaveChanged( true );
2127             }
2128         }
2129         else if ( e.isShiftDown() ) {
2130             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
2131                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
2132                 if ( notches < 0 ) {
2133                     for( int i = 0; i < ( -notches ); ++i ) {
2134                         setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
2135                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2136                     }
2137                 }
2138                 else {
2139                     for( int i = 0; i < notches; ++i ) {
2140                         setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
2141                         if ( getStartingAngle() < 0 ) {
2142                             setStartingAngle( TWO_PI + getStartingAngle() );
2143                         }
2144                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2145                     }
2146                 }
2147             }
2148             else {
2149                 if ( notches < 0 ) {
2150                     for( int i = 0; i < ( -notches ); ++i ) {
2151                         getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2152                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2153                     }
2154                 }
2155                 else {
2156                     for( int i = 0; i < notches; ++i ) {
2157                         getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2158                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2159                     }
2160                 }
2161             }
2162         }
2163         else {
2164             if ( notches < 0 ) {
2165                 for( int i = 0; i < ( -notches ); ++i ) {
2166                     getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
2167                                                Constants.WHEEL_ZOOM_IN_X_CORRECTION_FACTOR );
2168                     getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2169                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2170                 }
2171             }
2172             else {
2173                 for( int i = 0; i < notches; ++i ) {
2174                     getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2175                     getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
2176                                                 Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
2177                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2178                 }
2179             }
2180         }
2181         requestFocus();
2182         requestFocusInWindow();
2183         requestFocus();
2184     }
2185
2186     final void multiplyUrtFactor( final float f ) {
2187         _urt_factor *= f;
2188     }
2189
2190     final JApplet obtainApplet() {
2191         return ( ( MainPanelApplets ) getMainPanel() ).getApplet();
2192     }
2193
2194     final private void openSeqWeb( final PhylogenyNode node ) {
2195         if ( !isCanOpenSeqWeb( node ) ) {
2196             cannotOpenBrowserWarningMessage( "sequence" );
2197             return;
2198         }
2199         String uri_str = null;
2200         final Sequence seq = node.getNodeData().getSequence();
2201         final String source = seq.getAccession().getSource().toLowerCase();
2202         String url;
2203         if ( source.toLowerCase().equals( "ncbi" ) ) {
2204             url = Constants.NCBI_ALL_DATABASE_SEARCH;
2205         }
2206         else {
2207             final WebLink weblink = getConfiguration().getWebLink( source );
2208             url = weblink.getUrl().toString();
2209         }
2210         try {
2211             uri_str = url + URLEncoder.encode( seq.getAccession().getValue(), ForesterConstants.UTF8 );
2212         }
2213         catch ( final UnsupportedEncodingException e ) {
2214             AptxUtil.showErrorMessage( this, e.toString() );
2215             e.printStackTrace();
2216         }
2217         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2218             try {
2219                 JApplet applet = null;
2220                 if ( isApplet() ) {
2221                     applet = obtainApplet();
2222                 }
2223                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_seq" );
2224             }
2225             catch ( final IOException e ) {
2226                 AptxUtil.showErrorMessage( this, e.toString() );
2227                 e.printStackTrace();
2228             }
2229             catch ( final URISyntaxException e ) {
2230                 AptxUtil.showErrorMessage( this, e.toString() );
2231                 e.printStackTrace();
2232             }
2233         }
2234         else {
2235             cannotOpenBrowserWarningMessage( "sequence" );
2236         }
2237     }
2238
2239     final private void openTaxWeb( final PhylogenyNode node ) {
2240         if ( !isCanOpenTaxWeb( node ) ) {
2241             cannotOpenBrowserWarningMessage( "taxonomic" );
2242             return;
2243         }
2244         String uri_str = null;
2245         final Taxonomy tax = node.getNodeData().getTaxonomy();
2246         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
2247                 && getConfiguration().isHasWebLink( tax.getIdentifier().getProvider().toLowerCase() ) ) {
2248             final String type = tax.getIdentifier().getProvider().toLowerCase();
2249             final WebLink weblink = getConfiguration().getWebLink( type );
2250             try {
2251                 uri_str = weblink.getUrl() + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
2252             }
2253             catch ( final UnsupportedEncodingException e ) {
2254                 AptxUtil.showErrorMessage( this, e.toString() );
2255                 e.printStackTrace();
2256             }
2257         }
2258         else if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
2259                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
2260             try {
2261                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
2262             }
2263             catch ( final URISyntaxException e ) {
2264                 AptxUtil.showErrorMessage( this, e.toString() );
2265                 uri_str = null;
2266                 e.printStackTrace();
2267             }
2268         }
2269         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
2270             try {
2271                 uri_str = "http://www.eol.org/search?q="
2272                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
2273             }
2274             catch ( final UnsupportedEncodingException e ) {
2275                 AptxUtil.showErrorMessage( this, e.toString() );
2276                 e.printStackTrace();
2277             }
2278         }
2279         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
2280             try {
2281                 uri_str = "http://www.uniprot.org/taxonomy/?query="
2282                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
2283             }
2284             catch ( final UnsupportedEncodingException e ) {
2285                 AptxUtil.showErrorMessage( this, e.toString() );
2286                 e.printStackTrace();
2287             }
2288         }
2289         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
2290             try {
2291                 uri_str = "http://www.eol.org/search?q="
2292                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
2293             }
2294             catch ( final UnsupportedEncodingException e ) {
2295                 AptxUtil.showErrorMessage( this, e.toString() );
2296                 e.printStackTrace();
2297             }
2298         }
2299         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2300             try {
2301                 JApplet applet = null;
2302                 if ( isApplet() ) {
2303                     applet = obtainApplet();
2304                 }
2305                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_tax" );
2306             }
2307             catch ( final IOException e ) {
2308                 AptxUtil.showErrorMessage( this, e.toString() );
2309                 e.printStackTrace();
2310             }
2311             catch ( final URISyntaxException e ) {
2312                 AptxUtil.showErrorMessage( this, e.toString() );
2313                 e.printStackTrace();
2314             }
2315         }
2316         else {
2317             cannotOpenBrowserWarningMessage( "taxonomic" );
2318         }
2319     }
2320
2321     final void paintBranchCircular( final PhylogenyNode p,
2322                                     final PhylogenyNode c,
2323                                     final Graphics2D g,
2324                                     final boolean radial_labels,
2325                                     final boolean to_pdf,
2326                                     final boolean to_graphics_file ) {
2327         final double angle = _urt_nodeid_angle_map.get( c.getId() );
2328         final double root_x = _root.getXcoord();
2329         final double root_y = _root.getYcoord();
2330         final double dx = root_x - p.getXcoord();
2331         final double dy = root_y - p.getYcoord();
2332         final double parent_radius = Math.sqrt( dx * dx + dy * dy );
2333         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
2334         assignGraphicsForBranchWithColorForParentBranch( c, false, g, to_pdf, to_graphics_file );
2335         if ( ( c.isFirstChildNode() || c.isLastChildNode() )
2336                 && ( ( Math.abs( parent_radius * arc ) > 1.5 ) || to_pdf || to_graphics_file ) ) {
2337             final double r2 = 2.0 * parent_radius;
2338             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
2339         }
2340         drawLine( c.getXcoord(),
2341                   c.getYcoord(),
2342                   root_x + ( Math.cos( angle ) * parent_radius ),
2343                   root_y + ( Math.sin( angle ) * parent_radius ),
2344                   g );
2345         paintNodeBox( c.getXcoord(), c.getYcoord(), c, g, to_pdf, to_graphics_file, isInFoundNodes( c ) );
2346         if ( c.isExternal() ) {
2347             final boolean is_in_found_nodes = isInFoundNodes( c );
2348             if ( ( _dynamic_hiding_factor > 1 ) && !is_in_found_nodes
2349                     && ( _urt_nodeid_index_map.get( c.getId() ) % _dynamic_hiding_factor != 1 ) ) {
2350                 return;
2351             }
2352             paintNodeDataUnrootedCirc( g, c, to_pdf, to_graphics_file, radial_labels, 0, is_in_found_nodes );
2353         }
2354     }
2355
2356     final void paintBranchCircularLite( final PhylogenyNode p, final PhylogenyNode c, final Graphics2D g ) {
2357         final double angle = _urt_nodeid_angle_map.get( c.getId() );
2358         final double root_x = _root.getXSecondary();
2359         final double root_y = _root.getYSecondary();
2360         final double dx = root_x - p.getXSecondary();
2361         final double dy = root_y - p.getYSecondary();
2362         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
2363         final double parent_radius = Math.sqrt( dx * dx + dy * dy );
2364         g.setColor( getTreeColorSet().getOvColor() );
2365         if ( ( c.isFirstChildNode() || c.isLastChildNode() ) && ( Math.abs( arc ) > 0.02 ) ) {
2366             final double r2 = 2.0 * parent_radius;
2367             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
2368         }
2369         drawLine( c.getXSecondary(),
2370                   c.getYSecondary(),
2371                   root_x + ( Math.cos( angle ) * parent_radius ),
2372                   root_y + ( Math.sin( angle ) * parent_radius ),
2373                   g );
2374         if ( isInFoundNodes( c ) ) {
2375             g.setColor( getTreeColorSet().getFoundColor() );
2376             drawRectFilled( c.getXSecondary() - 1, c.getYSecondary() - 1, 3, 3, g );
2377         }
2378     }
2379
2380     final private void paintBranchLength( final Graphics2D g,
2381                                           final PhylogenyNode node,
2382                                           final boolean to_pdf,
2383                                           final boolean to_graphics_file ) {
2384         g.setFont( getTreeFontSet().getSmallFont() );
2385         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2386             g.setColor( Color.BLACK );
2387         }
2388         else {
2389             g.setColor( getTreeColorSet().getBranchLengthColor() );
2390         }
2391         if ( !node.isRoot() ) {
2392             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2393                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2394                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2395             }
2396             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2397                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2398                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2399             }
2400             else {
2401                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2402                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2403             }
2404         }
2405         else {
2406             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
2407                     - getTreeFontSet()._small_max_descent, g );
2408         }
2409     }
2410
2411     final private void paintBranchLite( final Graphics2D g,
2412                                         final float x1,
2413                                         final float x2,
2414                                         final float y1,
2415                                         final float y2,
2416                                         final PhylogenyNode node ) {
2417         g.setColor( getTreeColorSet().getOvColor() );
2418         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2419             drawLine( x1, y1, x2, y2, g );
2420         }
2421         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2422             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2423             ( g ).draw( _quad_curve );
2424         }
2425         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2426             final float dx = x2 - x1;
2427             final float dy = y2 - y1;
2428             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
2429                     + ( dy * 0.8f ), x2, y2 );
2430             ( g ).draw( _cubic_curve );
2431         }
2432         else {
2433             final float x2a = x2;
2434             final float x1a = x1;
2435             // draw the vertical line
2436             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
2437                 drawLine( x1, y1, x1, y2, g );
2438             }
2439             // draw the horizontal line
2440             drawLine( x1a, y2, x2a, y2, g );
2441         }
2442     }
2443
2444     /**
2445      * Paint a branch which consists of a vertical and a horizontal bar
2446      * @param is_ind_found_nodes 
2447      */
2448     final private void paintBranchRectangular( final Graphics2D g,
2449                                                final float x1,
2450                                                final float x2,
2451                                                final float y1,
2452                                                final float y2,
2453                                                final PhylogenyNode node,
2454                                                final boolean to_pdf,
2455                                                final boolean to_graphics_file ) {
2456         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
2457         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2458             drawLine( x1, y1, x2, y2, g );
2459         }
2460         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2461             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2462             g.draw( _quad_curve );
2463         }
2464         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2465             final float dx = x2 - x1;
2466             final float dy = y2 - y1;
2467             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
2468                     + ( dy * 0.8f ), x2, y2 );
2469             g.draw( _cubic_curve );
2470         }
2471         else {
2472             final float x2a = x2;
2473             final float x1a = x1;
2474             float y2_r = 0;
2475             if ( node.isFirstChildNode() || node.isLastChildNode()
2476                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
2477                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2478                 if ( !to_graphics_file
2479                         && !to_pdf
2480                         && ( ( ( y2 < getVisibleRect().getMinY() - 20 ) && ( y1 < getVisibleRect().getMinY() - 20 ) ) || ( ( y2 > getVisibleRect()
2481                                 .getMaxY() + 20 ) && ( y1 > getVisibleRect().getMaxY() + 20 ) ) ) ) {
2482                     // Do nothing.
2483                 }
2484                 else {
2485                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2486                         float x2c = x1 + EURO_D;
2487                         if ( x2c > x2a ) {
2488                             x2c = x2a;
2489                         }
2490                         drawLine( x1, y1, x2c, y2, g );
2491                     }
2492                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2493                         if ( y2 > y1 ) {
2494                             y2_r = y2 - ROUNDED_D;
2495                             if ( y2_r < y1 ) {
2496                                 y2_r = y1;
2497                             }
2498                             drawLine( x1, y1, x1, y2_r, g );
2499                         }
2500                         else {
2501                             y2_r = y2 + ROUNDED_D;
2502                             if ( y2_r > y1 ) {
2503                                 y2_r = y1;
2504                             }
2505                             drawLine( x1, y1, x1, y2_r, g );
2506                         }
2507                     }
2508                     else {
2509                         drawLine( x1, y1, x1, y2, g );
2510                     }
2511                 }
2512             }
2513             // draw the horizontal line
2514             if ( !to_graphics_file && !to_pdf
2515                     && ( ( y2 < getVisibleRect().getMinY() - 20 ) || ( y2 > getVisibleRect().getMaxY() + 20 ) ) ) {
2516                 return;
2517             }
2518             float x1_r = 0;
2519             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
2520                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2521                     x1_r = x1a + ROUNDED_D;
2522                     if ( x1_r < x2a ) {
2523                         drawLine( x1_r, y2, x2a, y2, g );
2524                     }
2525                 }
2526                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2527                     final float x1c = x1a + EURO_D;
2528                     if ( x1c < x2a ) {
2529                         drawLine( x1c, y2, x2a, y2, g );
2530                     }
2531                 }
2532                 else {
2533                     drawLine( x1a, y2, x2a, y2, g );
2534                 }
2535             }
2536             else {
2537                 final double w = PhylogenyMethods.getBranchWidthValue( node );
2538                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2539                     x1_r = x1a + ROUNDED_D;
2540                     if ( x1_r < x2a ) {
2541                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
2542                     }
2543                 }
2544                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2545                     final float x1c = x1a + EURO_D;
2546                     if ( x1c < x2a ) {
2547                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
2548                     }
2549                 }
2550                 else {
2551                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
2552                 }
2553             }
2554             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2555                 if ( x1_r > x2a ) {
2556                     x1_r = x2a;
2557                 }
2558                 if ( y2 > y2_r ) {
2559                     final double diff = y2 - y2_r;
2560                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
2561                 }
2562                 else {
2563                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
2564                 }
2565                 g.draw( _arc );
2566             }
2567         }
2568         if ( node.isExternal() ) {
2569             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
2570         }
2571     }
2572
2573     final void paintCircular( final Phylogeny phy,
2574                               final double starting_angle,
2575                               final int center_x,
2576                               final int center_y,
2577                               final int radius,
2578                               final Graphics2D g,
2579                               final boolean to_pdf,
2580                               final boolean to_graphics_file ) {
2581         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes() - _collapsed_external_nodeid_set.size();
2582         System.out.println( "# collapsed external = " + _collapsed_external_nodeid_set.size() );
2583         _root = phy.getRoot();
2584         _root.setXcoord( center_x );
2585         _root.setYcoord( center_y );
2586         final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
2587         double current_angle = starting_angle;
2588         int i = 0;
2589         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2590             final PhylogenyNode n = it.next();
2591             if ( !n.isCollapse() ) {
2592                 n.setXcoord( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
2593                 n.setYcoord( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
2594                 _urt_nodeid_angle_map.put( n.getId(), current_angle );
2595                 _urt_nodeid_index_map.put( n.getId(), i++ );
2596                 current_angle += ( TWO_PI / circ_num_ext_nodes );
2597             }
2598             else {
2599                 //TODO remove me
2600                 System.out.println( "is collapse" + n.getName() );
2601             }
2602         }
2603         paintCirculars( phy.getRoot(), phy, center_x, center_y, radius, radial_labels, g, to_pdf, to_graphics_file );
2604         paintNodeBox( _root.getXcoord(), _root.getYcoord(), _root, g, to_pdf, to_graphics_file, isInFoundNodes( _root ) );
2605     }
2606
2607     void updateSetOfCollapsedExternalNodes() {
2608         final Phylogeny phy = getPhylogeny();
2609         _collapsed_external_nodeid_set.clear();
2610         if ( phy != null ) {
2611             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2612                 final PhylogenyNode ext_node = it.next();
2613                 PhylogenyNode n = ext_node;
2614                 while ( !n.isRoot() ) {
2615                     if ( n.isCollapse() ) {
2616                         _collapsed_external_nodeid_set.add( ext_node.getId() );
2617                         ext_node.setCollapse( true );
2618                         continue E;
2619                     }
2620                     n = n.getParent();
2621                 }
2622             }
2623         }
2624     }
2625
2626     final void paintCircularLite( final Phylogeny phy,
2627                                   final double starting_angle,
2628                                   final int center_x,
2629                                   final int center_y,
2630                                   final int radius,
2631                                   final Graphics2D g ) {
2632         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes();
2633         _root = phy.getRoot();
2634         _root.setXSecondary( center_x );
2635         _root.setYSecondary( center_y );
2636         double current_angle = starting_angle;
2637         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2638             final PhylogenyNode n = it.next();
2639             n.setXSecondary( ( float ) ( center_x + radius * Math.cos( current_angle ) ) );
2640             n.setYSecondary( ( float ) ( center_y + radius * Math.sin( current_angle ) ) );
2641             _urt_nodeid_angle_map.put( n.getId(), current_angle );
2642             current_angle += ( TWO_PI / circ_num_ext_nodes );
2643         }
2644         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
2645     }
2646
2647     final private double paintCirculars( final PhylogenyNode n,
2648                                          final Phylogeny phy,
2649                                          final float center_x,
2650                                          final float center_y,
2651                                          final double radius,
2652                                          final boolean radial_labels,
2653                                          final Graphics2D g,
2654                                          final boolean to_pdf,
2655                                          final boolean to_graphics_file ) {
2656         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
2657             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
2658                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
2659             }
2660             return _urt_nodeid_angle_map.get( n.getId() );
2661         }
2662         else {
2663             final List<PhylogenyNode> descs = n.getDescendants();
2664             double sum = 0;
2665             for( final PhylogenyNode desc : descs ) {
2666                 sum += paintCirculars( desc,
2667                                        phy,
2668                                        center_x,
2669                                        center_y,
2670                                        radius,
2671                                        radial_labels,
2672                                        g,
2673                                        to_pdf,
2674                                        to_graphics_file );
2675             }
2676             double r = 0;
2677             if ( !n.isRoot() ) {
2678                 r = 1 - ( ( ( double ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2679             }
2680             final double theta = sum / descs.size();
2681             n.setXcoord( ( float ) ( center_x + r * radius * Math.cos( theta ) ) );
2682             n.setYcoord( ( float ) ( center_y + r * radius * Math.sin( theta ) ) );
2683             _urt_nodeid_angle_map.put( n.getId(), theta );
2684             for( final PhylogenyNode desc : descs ) {
2685                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
2686             }
2687             return theta;
2688         }
2689     }
2690
2691     final private void paintCircularsLite( final PhylogenyNode n,
2692                                            final Phylogeny phy,
2693                                            final int center_x,
2694                                            final int center_y,
2695                                            final int radius,
2696                                            final Graphics2D g ) {
2697         if ( n.isExternal() ) {
2698             return;
2699         }
2700         else {
2701             final List<PhylogenyNode> descs = n.getDescendants();
2702             for( final PhylogenyNode desc : descs ) {
2703                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
2704             }
2705             float r = 0;
2706             if ( !n.isRoot() ) {
2707                 r = 1 - ( ( ( float ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2708             }
2709             final double theta = _urt_nodeid_angle_map.get( n.getId() );
2710             n.setXSecondary( ( float ) ( center_x + radius * r * Math.cos( theta ) ) );
2711             n.setYSecondary( ( float ) ( center_y + radius * r * Math.sin( theta ) ) );
2712             for( final PhylogenyNode desc : descs ) {
2713                 paintBranchCircularLite( n, desc, g );
2714             }
2715         }
2716     }
2717
2718     final private void paintCollapsedNode( final Graphics2D g,
2719                                            final PhylogenyNode node,
2720                                            final boolean to_graphics_file,
2721                                            final boolean to_pdf,
2722                                            final boolean is_in_found_nodes ) {
2723         Color c = null;
2724         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2725             c = Color.BLACK;
2726         }
2727         else if ( is_in_found_nodes ) {
2728             c = getTreeColorSet().getFoundColor();
2729         }
2730         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2731             c = getTaxonomyBasedColor( node );
2732         }
2733         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
2734                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2735             c = PhylogenyMethods.getBranchColorValue( node );
2736         }
2737         else {
2738             c = getTreeColorSet().getCollapseFillColor();
2739         }
2740         double d = node.getAllExternalDescendants().size();
2741         if ( d > 1000 ) {
2742             d = ( 3 * _y_distance ) / 3;
2743         }
2744         else {
2745             d = ( Math.log10( d ) * _y_distance ) / 2.5;
2746         }
2747         final int box_size = getOptions().getDefaultNodeShapeSize();
2748         if ( d < box_size ) {
2749             d = box_size;
2750         }
2751         _polygon.reset();
2752         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() - box_size ),
2753                            ForesterUtil.roundToInt( node.getYcoord() ) );
2754         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
2755                            ForesterUtil.roundToInt( node.getYcoord() - d ) );
2756         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
2757                            ForesterUtil.roundToInt( node.getYcoord() + d ) );
2758         if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2759             g.setColor( c );
2760             g.fillPolygon( _polygon );
2761         }
2762         else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
2763             g.setColor( getBackground() );
2764             g.fillPolygon( _polygon );
2765             g.setColor( c );
2766             g.drawPolygon( _polygon );
2767         }
2768         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2769             g.setPaint( new GradientPaint( node.getXcoord() - box_size, node.getYcoord(), getBackground(), ( node
2770                     .getXcoord() + box_size ), ( float ) ( node.getYcoord() - d ), c, false ) );
2771             g.fill( _polygon );
2772             g.setPaint( c );
2773             g.draw( _polygon );
2774         }
2775         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
2776     }
2777
2778     @Override
2779     final public void paintComponent( final Graphics g ) {
2780         // Dimension currentSize = getSize();
2781         //  if ( offscreenImage == null || !currentSize.equals( offscreenDimension ) ) {
2782         // call the 'java.awt.Component.createImage(...)' method to get an
2783         // image
2784         //   offscreenImage = createImage( currentSize.width, currentSize.height );
2785         //  offscreenGraphics = offscreenImage.getGraphics();
2786         //  offscreenDimension = currentSize;
2787         // }
2788         // super.paintComponent( g ); //why?
2789         //final Graphics2D g2d = ( Graphics2D ) offscreenGraphics;
2790         final Graphics2D g2d = ( Graphics2D ) g;
2791         g2d.setRenderingHints( _rendering_hints );
2792         paintPhylogeny( g2d, false, false, 0, 0, 0, 0 );
2793         //g.drawImage( offscreenImage, 0, 0, this );
2794     }
2795
2796     @Override
2797     public void update( final Graphics g ) {
2798         paint( g );
2799     }
2800
2801     final private void paintConfidenceValues( final Graphics2D g,
2802                                               final PhylogenyNode node,
2803                                               final boolean to_pdf,
2804                                               final boolean to_graphics_file ) {
2805         final List<Confidence> confidences = node.getBranchData().getConfidences();
2806         //        if ( confidences.size() == 1 ) {
2807         //            final double value = node.getBranchData().getConfidence( 0 ).getValue();
2808         //            if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
2809         //                return;
2810         //            }
2811         //            conf_str = FORMATTER_CONFIDENCE.format( value );
2812         //        }
2813         //        else if ( confidences.size() > 1 ) {
2814         boolean one_ok = false;
2815         boolean not_first = false;
2816         Collections.sort( confidences );
2817         final StringBuilder sb = new StringBuilder();
2818         String conf_str = "";
2819         for( final Confidence confidence : confidences ) {
2820             final double value = confidence.getValue();
2821             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2822                 if ( value >= getOptions().getMinConfidenceValue() ) {
2823                     one_ok = true;
2824                 }
2825                 if ( not_first ) {
2826                     sb.append( "/" );
2827                 }
2828                 else {
2829                     not_first = true;
2830                 }
2831                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
2832                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2833                 if ( getOptions().isShowConfidenceStddev() ) {
2834                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2835                         sb.append( "(" );
2836                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
2837                                                                                     getOptions()
2838                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2839                         sb.append( ")" );
2840                     }
2841                 }
2842             }
2843             //}
2844             if ( one_ok ) {
2845                 conf_str = sb.toString();
2846             }
2847         }
2848         if ( conf_str.length() > 0 ) {
2849             final double parent_x = node.getParent().getXcoord();
2850             double x = node.getXcoord();
2851             g.setFont( getTreeFontSet().getSmallFont() );
2852             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2853                 x += EURO_D;
2854             }
2855             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2856                 x += ROUNDED_D;
2857             }
2858             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2859                 g.setColor( Color.BLACK );
2860             }
2861             else {
2862                 g.setColor( getTreeColorSet().getConfidenceColor() );
2863             }
2864             TreePanel
2865                     .drawString( conf_str,
2866                                  parent_x
2867                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
2868                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
2869                                  g );
2870         }
2871     }
2872
2873     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
2874         final int box_size = getOptions().getDefaultNodeShapeSize();
2875         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2876         g.setColor( getTreeColorSet().getFoundColor() );
2877         g.fillRect( x - half_box_size, y - half_box_size, box_size, box_size );
2878     }
2879
2880     final private void paintGainedAndLostCharacters( final Graphics2D g,
2881                                                      final PhylogenyNode node,
2882                                                      final String gained,
2883                                                      final String lost ) {
2884         if ( node.getParent() != null ) {
2885             final double parent_x = node.getParent().getXcoord();
2886             final double x = node.getXcoord();
2887             g.setFont( getTreeFontSet().getLargeFont() );
2888             g.setColor( getTreeColorSet().getGainedCharactersColor() );
2889             if ( Constants.SPECIAL_CUSTOM ) {
2890                 g.setColor( Color.BLUE );
2891             }
2892             TreePanel
2893                     .drawString( gained,
2894                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
2895                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
2896                                  g );
2897             g.setColor( getTreeColorSet().getLostCharactersColor() );
2898             TreePanel.drawString( lost,
2899                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
2900                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
2901                                   g );
2902         }
2903     }
2904
2905     /**
2906      * Draw a box at the indicated node.
2907      * 
2908      * @param x
2909      * @param y
2910      * @param node
2911      * @param g
2912      */
2913     final private void paintNodeBox( final double x,
2914                                      final double y,
2915                                      final PhylogenyNode node,
2916                                      final Graphics2D g,
2917                                      final boolean to_pdf,
2918                                      final boolean to_graphics_file,
2919                                      final boolean is_in_found_nodes ) {
2920         if ( node.isCollapse() ) {
2921             return;
2922         }
2923         // if this node should be highlighted, do so
2924         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
2925             g.setColor( getTreeColorSet().getFoundColor() );
2926             drawOval( x - 8, y - 8, 16, 16, g );
2927             drawOval( x - 9, y - 8, 17, 17, g );
2928             drawOval( x - 9, y - 9, 18, 18, g );
2929         }
2930         if ( is_in_found_nodes ) {
2931             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
2932         }
2933         else {
2934             Color outline_color = null;
2935             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2936                 outline_color = Color.BLACK;
2937             }
2938             else if ( getControlPanel().isEvents() && AptxUtil.isHasAssignedEvent( node ) ) {
2939                 final Event event = node.getNodeData().getEvent();
2940                 if ( event.isDuplication() ) {
2941                     outline_color = getTreeColorSet().getDuplicationBoxColor();
2942                 }
2943                 else if ( event.isSpeciation() ) {
2944                     outline_color = getTreeColorSet().getSpecBoxColor();
2945                 }
2946                 else if ( event.isSpeciationOrDuplication() ) {
2947                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
2948                 }
2949             }
2950             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
2951                 outline_color = getTaxonomyBasedColor( node );
2952             }
2953             else {
2954                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
2955                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
2956                     outline_color = getTreeColorSet().getBranchColorForPdf();
2957                 }
2958             }
2959             final int box_size = getOptions().getDefaultNodeShapeSize();
2960             final int half_box_size = box_size / 2;
2961             if ( getOptions().isShowDefaultNodeShapes() || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
2962                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
2963                     if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2964                         drawOvalGradient( x - half_box_size,
2965                                           y - half_box_size,
2966                                           box_size,
2967                                           box_size,
2968                                           g,
2969                                           to_pdf ? Color.WHITE : outline_color,
2970                                           to_pdf ? outline_color : getBackground(),
2971                                           outline_color );
2972                     }
2973                     else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
2974                         Color background = getBackground();
2975                         if ( to_pdf ) {
2976                             background = Color.WHITE;
2977                         }
2978                         drawOvalGradient( x - half_box_size,
2979                                           y - half_box_size,
2980                                           box_size,
2981                                           box_size,
2982                                           g,
2983                                           background,
2984                                           background,
2985                                           outline_color );
2986                     }
2987                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2988                         g.setColor( outline_color );
2989                         drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2990                     }
2991                 }
2992                 else if ( getOptions().getDefaultNodeShape() == NodeVisualization.NodeShape.RECTANGLE ) {
2993                     if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.GRADIENT ) {
2994                         drawRectGradient( x - half_box_size,
2995                                           y - half_box_size,
2996                                           box_size,
2997                                           box_size,
2998                                           g,
2999                                           to_pdf ? Color.WHITE : outline_color,
3000                                           to_pdf ? outline_color : getBackground(),
3001                                           outline_color );
3002                     }
3003                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
3004                         Color background = getBackground();
3005                         if ( to_pdf ) {
3006                             background = Color.WHITE;
3007                         }
3008                         drawRectGradient( x - half_box_size,
3009                                           y - half_box_size,
3010                                           box_size,
3011                                           box_size,
3012                                           g,
3013                                           background,
3014                                           background,
3015                                           outline_color );
3016                     }
3017                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
3018                         g.setColor( outline_color );
3019                         drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
3020                     }
3021                 }
3022             }
3023         }
3024     }
3025
3026     final private void paintNodeData( final Graphics2D g,
3027                                       final PhylogenyNode node,
3028                                       final boolean to_graphics_file,
3029                                       final boolean to_pdf,
3030                                       final boolean is_in_found_nodes ) {
3031         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
3032             return;
3033         }
3034         if ( getOptions().isShowBranchLengthValues()
3035                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3036                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3037                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
3038             paintBranchLength( g, node, to_pdf, to_graphics_file );
3039         }
3040         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
3041             return;
3042         }
3043         int x = 0;
3044         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
3045         if ( getControlPanel().isShowTaxonomyImages()
3046                 && ( getImageMap() != null )
3047                 && !getImageMap().isEmpty()
3048                 && node.getNodeData().isHasTaxonomy()
3049                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
3050                         .getUris().isEmpty() ) ) {
3051             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
3052         }
3053         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3054                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
3055             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
3056         }
3057         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3058             g.setColor( Color.BLACK );
3059         }
3060         else if ( is_in_found_nodes ) {
3061             g.setColor( getTreeColorSet().getFoundColor() );
3062         }
3063         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3064             g.setColor( getTaxonomyBasedColor( node ) );
3065         }
3066         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3067                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3068             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3069         }
3070         else if ( to_pdf ) {
3071             g.setColor( Color.BLACK );
3072         }
3073         else {
3074             g.setColor( getTreeColorSet().getSequenceColor() );
3075         }
3076         _sb.setLength( 0 );
3077         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3078             _sb.append( " [" );
3079             _sb.append( node.getAllExternalDescendants().size() );
3080             _sb.append( "]" );
3081         }
3082         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3083             if ( _sb.length() > 0 ) {
3084                 _sb.append( " " );
3085             }
3086             _sb.append( node.getName() );
3087         }
3088         if ( node.getNodeData().isHasSequence() ) {
3089             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3090                 if ( _sb.length() > 0 ) {
3091                     _sb.append( " " );
3092                 }
3093                 _sb.append( node.getNodeData().getSequence().getSymbol() );
3094             }
3095             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3096                 if ( _sb.length() > 0 ) {
3097                     _sb.append( " " );
3098                 }
3099                 _sb.append( node.getNodeData().getSequence().getName() );
3100             }
3101             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3102                 if ( _sb.length() > 0 ) {
3103                     _sb.append( " " );
3104                 }
3105                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3106                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3107                     _sb.append( ":" );
3108                 }
3109                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3110             }
3111         }
3112         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
3113             if ( _sb.length() > 0 ) {
3114                 _sb.append( " " );
3115             }
3116             _sb.append( propertiesToString( node ) );
3117         }
3118         g.setFont( getTreeFontSet().getLargeFont() );
3119         if ( is_in_found_nodes ) {
3120             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3121         }
3122         double down_shift_factor = 3.0;
3123         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
3124             down_shift_factor = 1;
3125         }
3126         final double pos_x = node.getXcoord() + x + 2 + half_box_size;
3127         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
3128         final String sb_str = _sb.toString();
3129         // GUILHEM_BEG ______________
3130         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
3131                 && ( _query_sequence != null ) ) {
3132             int nodeTextBoundsWidth = 0;
3133             if ( sb_str.length() > 0 ) {
3134                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
3135                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
3136             }
3137             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
3138                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
3139                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
3140                     g.setColor( getTreeColorSet().getBackgroundColor() );
3141                 }
3142             }
3143             else {
3144                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
3145                 for( final SequenceRelation seqRelation : seqRelations ) {
3146                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
3147                             .getRef1().isEqual( _query_sequence ) )
3148                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
3149                                     .getSelectedItem() );
3150                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
3151                         final double linePosX = node.getXcoord() + 2 + half_box_size;
3152                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
3153                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
3154                                 + ")";
3155                         if ( sConfidence != null ) {
3156                             double confidenceX = pos_x;
3157                             if ( sb_str.length() > 0 ) {
3158                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
3159                                         + CONFIDENCE_LEFT_MARGIN;
3160                             }
3161                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
3162                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
3163                                         .getBounds().getWidth();
3164                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
3165                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
3166                             }
3167                         }
3168                         if ( x + nodeTextBoundsWidth > 0 ) /* we only underline if there is something displayed */
3169                         {
3170                             if ( nodeTextBoundsWidth == 0 ) {
3171                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
3172                             }
3173                             else {
3174                                 nodeTextBoundsWidth += 2;
3175                             }
3176                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
3177                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
3178                             break;
3179                         }
3180                     }
3181                 }
3182             }
3183         }
3184         if ( sb_str.length() > 0 ) {
3185             TreePanel.drawString( sb_str, pos_x, pos_y, g );
3186         }
3187         // GUILHEM_END _____________
3188         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
3189         // TODO FIXME need to check this one!
3190         //if ( _sb.length() > 0 ) {
3191         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
3192         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3193         //}
3194         // COMMENTED_OUT_BY_GUILHEM_END ________________
3195         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
3196                 && ( node.getNodeData().getSequence().getAnnotations() != null )
3197                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
3198             if ( _sb.length() > 0 ) {
3199                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3200             }
3201             final Annotation ann = node.getNodeData().getSequence().getAnnotation( 0 );
3202             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3203                 g.setColor( Color.BLACK );
3204             }
3205             else {
3206                 g.setColor( calculateColorForAnnotation( ann ) );
3207             }
3208             final String ann_str = ann.asSimpleText().toString();
3209             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
3210                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3211             _sb.setLength( 0 );
3212             _sb.append( ann_str );
3213         }
3214         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3215                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3216                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3217             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
3218                     && node.getNodeData().isHasBinaryCharacters() ) {
3219                 if ( _sb.length() > 0 ) {
3220                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3221                 }
3222                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3223                     g.setColor( Color.BLACK );
3224                 }
3225                 else {
3226                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
3227                 }
3228                 if ( getControlPanel().isShowBinaryCharacters() ) {
3229                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
3230                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
3231                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3232                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
3233                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
3234                             .getLostCharactersAsStringBuffer().toString() );
3235                 }
3236                 else {
3237                     TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
3238                                           node.getXcoord() + x + 4 + half_box_size,
3239                                           node.getYcoord()
3240                                                   + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
3241                                           g );
3242                     paintGainedAndLostCharacters( g, node, "+"
3243                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
3244                             + node.getNodeData().getBinaryCharacters().getLostCount() );
3245                 }
3246             }
3247         }
3248     }
3249
3250     private StringBuffer propertiesToString( final PhylogenyNode node ) {
3251         final PropertiesMap properties = node.getNodeData().getProperties();
3252         final StringBuffer sb = new StringBuffer();
3253         boolean first = true;
3254         for( final String ref : properties.getPropertyRefs() ) {
3255             if ( first ) {
3256                 first = false;
3257             }
3258             else {
3259                 sb.append( " " );
3260             }
3261             final Property p = properties.getProperty( ref );
3262             sb.append( getPartAfterColon( p.getRef() ) );
3263             sb.append( "=" );
3264             sb.append( p.getValue() );
3265             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
3266                 sb.append( getPartAfterColon( p.getUnit() ) );
3267             }
3268         }
3269         return sb;
3270     }
3271
3272     final private static String getPartAfterColon( final String s ) {
3273         final int i = s.indexOf( ':' );
3274         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
3275             return s;
3276         }
3277         return s.substring( i + 1, s.length() );
3278     }
3279
3280     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
3281         final List<Uri> us = new ArrayList<Uri>();
3282         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
3283             for( final Uri uri : t.getUris() ) {
3284                 us.add( uri );
3285             }
3286         }
3287         double offset = 0;
3288         for( final Uri uri : us ) {
3289             if ( uri != null ) {
3290                 final String uri_str = uri.getValue().toString().toLowerCase();
3291                 if ( getImageMap().containsKey( uri_str ) ) {
3292                     final BufferedImage bi = getImageMap().get( uri_str );
3293                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
3294                         double scaling_factor = 1;
3295                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
3296                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
3297                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
3298                         }
3299                         // y = y - ( 0.9 * getYdistance() );
3300                         final double hs = bi.getHeight() * scaling_factor;
3301                         double ws = bi.getWidth() * scaling_factor + offset;
3302                         final double my_y = y - ( 0.5 * hs );
3303                         final int x_w = ( int ) ( x + ws + 0.5 );
3304                         final int y_h = ( int ) ( my_y + hs + 0.5 );
3305                         if ( ( x_w - x > 7 ) && ( y_h - my_y > 7 ) ) {
3306                             g.drawImage( bi,
3307                                          ( int ) ( x + 0.5 + offset ),
3308                                          ( int ) ( my_y + 0.5 ),
3309                                          x_w,
3310                                          y_h,
3311                                          0,
3312                                          0,
3313                                          bi.getWidth(),
3314                                          bi.getHeight(),
3315                                          null );
3316                             ws += 8;
3317                         }
3318                         else {
3319                             ws = 0.0;
3320                         }
3321                         offset = ws;
3322                     }
3323                 }
3324             }
3325         }
3326         return offset;
3327     }
3328
3329     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
3330                                                   final PhylogenyNode node,
3331                                                   final boolean to_pdf,
3332                                                   final boolean to_graphics_file,
3333                                                   final boolean radial_labels,
3334                                                   final double ur_angle,
3335                                                   final boolean is_in_found_nodes ) {
3336         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
3337             return;
3338         }
3339         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3340             g.setColor( Color.BLACK );
3341         }
3342         else if ( is_in_found_nodes ) {
3343             g.setColor( getTreeColorSet().getFoundColor() );
3344         }
3345         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3346             g.setColor( getTaxonomyBasedColor( node ) );
3347         }
3348         else {
3349             g.setColor( getTreeColorSet().getSequenceColor() );
3350         }
3351         _sb.setLength( 0 );
3352         _sb.append( " " );
3353         if ( node.getNodeData().isHasTaxonomy()
3354                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3355                         .isShowTaxonomyCommonNames() ) ) {
3356             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3357             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3358                 _sb.append( taxonomy.getTaxonomyCode() );
3359                 _sb.append( " " );
3360             }
3361             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3362                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3363                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3364                     _sb.append( taxonomy.getScientificName() );
3365                     _sb.append( " (" );
3366                     _sb.append( taxonomy.getCommonName() );
3367                     _sb.append( ") " );
3368                 }
3369                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3370                     _sb.append( taxonomy.getScientificName() );
3371                     _sb.append( " " );
3372                 }
3373                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3374                     _sb.append( taxonomy.getCommonName() );
3375                     _sb.append( " " );
3376                 }
3377             }
3378             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3379                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3380                     _sb.append( taxonomy.getScientificName() );
3381                     _sb.append( " " );
3382                 }
3383             }
3384             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3385                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3386                     _sb.append( taxonomy.getCommonName() );
3387                     _sb.append( " " );
3388                 }
3389             }
3390         }
3391         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3392             _sb.append( " [" );
3393             _sb.append( node.getAllExternalDescendants().size() );
3394             _sb.append( "]" );
3395         }
3396         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3397             if ( _sb.length() > 0 ) {
3398                 _sb.append( " " );
3399             }
3400             _sb.append( node.getName() );
3401         }
3402         if ( node.getNodeData().isHasSequence() ) {
3403             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3404                 if ( _sb.length() > 0 ) {
3405                     _sb.append( " " );
3406                 }
3407                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3408                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3409                     _sb.append( ":" );
3410                 }
3411                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3412             }
3413             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3414                 if ( _sb.length() > 0 ) {
3415                     _sb.append( " " );
3416                 }
3417                 _sb.append( node.getNodeData().getSequence().getName() );
3418             }
3419         }
3420         g.setFont( getTreeFontSet().getLargeFont() );
3421         if ( is_in_found_nodes ) {
3422             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3423         }
3424         if ( _sb.length() > 1 ) {
3425             final String sb_str = _sb.toString();
3426             double m = 0;
3427             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3428                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
3429             }
3430             else {
3431                 m = ( float ) ( ur_angle % TWO_PI );
3432             }
3433             _at = g.getTransform();
3434             boolean need_to_reset = false;
3435             final float x_coord = node.getXcoord();
3436             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
3437             if ( radial_labels ) {
3438                 need_to_reset = true;
3439                 boolean left = false;
3440                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3441                     m -= PI;
3442                     left = true;
3443                 }
3444                 g.rotate( m, x_coord, node.getYcoord() );
3445                 if ( left ) {
3446                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
3447                 }
3448             }
3449             else {
3450                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3451                     need_to_reset = true;
3452                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
3453                 }
3454             }
3455             TreePanel.drawString( sb_str, x_coord, y_coord, g );
3456             if ( need_to_reset ) {
3457                 g.setTransform( _at );
3458             }
3459         }
3460     }
3461
3462     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
3463         if ( node.isCollapse() ) {
3464             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3465                 paintCollapsedNode( g, node, false, false, false );
3466             }
3467             return;
3468         }
3469         if ( isInFoundNodes( node ) ) {
3470             g.setColor( getTreeColorSet().getFoundColor() );
3471             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
3472         }
3473         float new_x = 0;
3474         if ( !node.isExternal() && !node.isCollapse() ) {
3475             boolean first_child = true;
3476             float y2 = 0.0f;
3477             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3478             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3479                 final PhylogenyNode child_node = node.getChildNode( i );
3480                 int factor_x;
3481                 if ( !isUniformBranchLengthsForCladogram() ) {
3482                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3483                 }
3484                 else {
3485                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3486                 }
3487                 if ( first_child ) {
3488                     first_child = false;
3489                     y2 = node.getYSecondary()
3490                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
3491                                     .getNumberOfExternalNodes() ) );
3492                 }
3493                 else {
3494                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3495                 }
3496                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
3497                 new_x = x2 + node.getXSecondary();
3498                 final float diff_y = node.getYSecondary() - y2;
3499                 final float diff_x = node.getXSecondary() - new_x;
3500                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
3501                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
3502                 }
3503                 child_node.setXSecondary( new_x );
3504                 child_node.setYSecondary( y2 );
3505                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3506             }
3507         }
3508     }
3509
3510     final private void paintNodeRectangular( final Graphics2D g,
3511                                              final PhylogenyNode node,
3512                                              final boolean to_pdf,
3513                                              final boolean dynamically_hide,
3514                                              final int dynamic_hiding_factor,
3515                                              final boolean to_graphics_file ) {
3516         final boolean is_in_found_nodes = isInFoundNodes( node );
3517         if ( node.isCollapse() ) {
3518             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3519                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3520             }
3521             return;
3522         }
3523         if ( node.isExternal() ) {
3524             ++_external_node_index;
3525         }
3526         // Confidence values
3527         if ( getControlPanel().isShowConfidenceValues()
3528                 && !node.isExternal()
3529                 && !node.isRoot()
3530                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
3531                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3532                 && node.getBranchData().isHasConfidences() ) {
3533             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
3534         }
3535         // Draw a line to root:
3536         if ( node.isRoot() && _phylogeny.isRooted() ) {
3537             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
3538         }
3539         float new_x = 0;
3540         float new_x_min = Float.MAX_VALUE;
3541         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
3542         float min_dist = 1.5f;
3543         if ( !disallow_shortcutting ) {
3544             //   System.out.println( dynamic_hiding_factor );
3545             if ( dynamic_hiding_factor > 4000 ) {
3546                 min_dist = 4;
3547             }
3548             else if ( dynamic_hiding_factor > 1000 ) {
3549                 min_dist = 3;
3550             }
3551             else if ( dynamic_hiding_factor > 100 ) {
3552                 min_dist = 2;
3553             }
3554         }
3555         if ( !node.isExternal() && !node.isCollapse() ) {
3556             boolean first_child = true;
3557             float y2 = 0.0f;
3558             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3559             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3560                 final PhylogenyNode child_node = node.getChildNode( i );
3561                 int factor_x;
3562                 if ( !isUniformBranchLengthsForCladogram() ) {
3563                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3564                 }
3565                 else {
3566                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3567                 }
3568                 if ( first_child ) {
3569                     first_child = false;
3570                     y2 = node.getYcoord()
3571                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3572                 }
3573                 else {
3574                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
3575                 }
3576                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
3577                 new_x = x2 + node.getXcoord();
3578                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
3579                     new_x_min = x2;
3580                 }
3581                 final float diff_y = node.getYcoord() - y2;
3582                 final float diff_x = node.getXcoord() - new_x;
3583                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
3584                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
3585                     paintBranchRectangular( g,
3586                                             node.getXcoord(),
3587                                             new_x,
3588                                             node.getYcoord(),
3589                                             y2,
3590                                             child_node,
3591                                             to_pdf,
3592                                             to_graphics_file );
3593                 }
3594                 child_node.setXcoord( new_x );
3595                 child_node.setYcoord( y2 );
3596                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
3597             }
3598             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
3599         }
3600         if ( dynamically_hide
3601                 && !is_in_found_nodes
3602                 && ( ( node.isExternal() && ( _external_node_index % dynamic_hiding_factor != 1 ) ) || ( !node
3603                         .isExternal() && ( ( new_x_min < 20 ) || ( _y_distance * node.getNumberOfExternalNodes() < getTreeFontSet()._fm_large
3604                         .getHeight() ) ) ) ) ) {
3605             return;
3606         }
3607         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3608         paintNodeWithRenderableData( g, node, to_graphics_file, to_pdf );
3609     }
3610
3611     final private void paintNodeWithRenderableData( final Graphics2D g,
3612                                                     final PhylogenyNode node,
3613                                                     final boolean to_graphics_file,
3614                                                     final boolean to_pdf ) {
3615         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
3616             return;
3617         }
3618         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
3619             return;
3620         }
3621         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3622                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
3623             RenderableDomainArchitecture rds = null;
3624             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
3625                 try {
3626                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
3627                 }
3628                 catch ( final ClassCastException cce ) {
3629                     cce.printStackTrace();
3630                 }
3631                 if ( rds != null ) {
3632                     rds.setRenderingHeight( 6 );
3633                     int x = 0;
3634                     if ( node.getNodeData().isHasTaxonomy() ) {
3635                         if ( getControlPanel().isShowTaxonomyCode()
3636                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
3637                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
3638                                     .getTaxonomyCode()
3639                                     + " " );
3640                         }
3641                         if ( getControlPanel().isShowTaxonomyScientificNames()
3642                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) ) {
3643                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
3644                                     .getScientificName()
3645                                     + " " );
3646                         }
3647                         if ( getControlPanel().isShowTaxonomyCommonNames()
3648                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) ) {
3649                             x += getTreeFontSet()._fm_large_italic.stringWidth( node.getNodeData().getTaxonomy()
3650                                     .getCommonName()
3651                                     + " " );
3652                         }
3653                     }
3654                     if ( node.getNodeData().isHasSequence() ) {
3655                         if ( getControlPanel().isShowGeneNames()
3656                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) ) {
3657                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName()
3658                                     + " " );
3659                         }
3660                         if ( getControlPanel().isShowGeneSymbols()
3661                                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) ) {
3662                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getSymbol()
3663                                     + " " );
3664                         }
3665                         if ( getControlPanel().isShowSequenceAcc()
3666                                 && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3667                             x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence()
3668                                     .getAccession().toString()
3669                                     + " " );
3670                         }
3671                     }
3672                     if ( getControlPanel().isShowNodeNames() && !ForesterUtil.isEmpty( node.getName() ) ) {
3673                         x += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
3674                     }
3675                     rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
3676                 }
3677             }
3678         }
3679         //////////////
3680         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
3681                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
3682             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
3683                                                                          getStatisticsForExpressionValues(),
3684                                                                          getConfiguration() );
3685             if ( rv != null ) {
3686                 int x = 0;
3687                 PhylogenyNode my_node = node;
3688                 if ( !getControlPanel().isDrawPhylogram() ) {
3689                     my_node = getPhylogeny().getFirstExternalNode();
3690                 }
3691                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
3692                     x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
3693                 }
3694                 if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
3695                     x += getTreeFontSet()._fm_large.stringWidth( my_node.getName() + " " );
3696                 }
3697                 rv.render( my_node.getXcoord() + x, node.getYcoord() - 5, g, this, to_pdf );
3698             }
3699         }
3700         //////////////
3701     }
3702
3703     final private void paintOvRectangle( final Graphics2D g ) {
3704         final float w_ratio = ( float ) getWidth() / getVisibleRect().width;
3705         final float h_ratio = ( float ) getHeight() / getVisibleRect().height;
3706         final float x_ratio = ( float ) getWidth() / getVisibleRect().x;
3707         final float y_ratio = ( float ) getHeight() / getVisibleRect().y;
3708         final float width = getOvMaxWidth() / w_ratio;
3709         final float height = getOvMaxHeight() / h_ratio;
3710         final float x = getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / x_ratio;
3711         final float y = getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / y_ratio;
3712         g.setColor( getTreeColorSet().getFoundColor() );
3713         getOvRectangle().setRect( x, y, width, height );
3714         if ( ( width < 6 ) && ( height < 6 ) ) {
3715             drawRectFilled( x, y, 6, 6, g );
3716             getOvVirtualRectangle().setRect( x, y, 6, 6 );
3717         }
3718         else if ( width < 6 ) {
3719             drawRectFilled( x, y, 6, height, g );
3720             getOvVirtualRectangle().setRect( x, y, 6, height );
3721         }
3722         else if ( height < 6 ) {
3723             drawRectFilled( x, y, width, 6, g );
3724             getOvVirtualRectangle().setRect( x, y, width, 6 );
3725         }
3726         else {
3727             drawRect( x, y, width, height, g );
3728             if ( isInOvRect() ) {
3729                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
3730             }
3731             getOvVirtualRectangle().setRect( x, y, width, height );
3732         }
3733     }
3734
3735     final void paintPhylogeny( final Graphics2D g,
3736                                final boolean to_pdf,
3737                                final boolean to_graphics_file,
3738                                final int graphics_file_width,
3739                                final int graphics_file_height,
3740                                final int graphics_file_x,
3741                                final int graphics_file_y ) {
3742         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
3743             return;
3744         }
3745         if ( _control_panel.isShowSequenceRelations() ) {
3746             _query_sequence = _control_panel.getSelectedQuerySequence();
3747         }
3748         // Color the background
3749         if ( !to_pdf ) {
3750             final Rectangle r = getVisibleRect();
3751             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
3752                 g.setColor( getTreeColorSet().getBackgroundColor() );
3753                 if ( !to_graphics_file ) {
3754                     g.fill( r );
3755                 }
3756                 else {
3757                     if ( getOptions().isPrintBlackAndWhite() ) {
3758                         g.setColor( Color.WHITE );
3759                     }
3760                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3761                 }
3762             }
3763             else {
3764                 if ( !to_graphics_file ) {
3765                     g.setPaint( new GradientPaint( r.x, r.y, getTreeColorSet().getBackgroundColor(), r.x, r.y
3766                             + r.height, getTreeColorSet().getBackgroundColorGradientBottom() ) );
3767                     g.fill( r );
3768                 }
3769                 else {
3770                     g.setPaint( new GradientPaint( graphics_file_x,
3771                                                    graphics_file_y,
3772                                                    getTreeColorSet().getBackgroundColor(),
3773                                                    graphics_file_x,
3774                                                    graphics_file_y + graphics_file_height,
3775                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
3776                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3777                 }
3778             }
3779             g.setStroke( new BasicStroke( 1 ) );
3780         }
3781         else {
3782             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
3783         }
3784         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3785                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3786             _external_node_index = 0;
3787             // Position starting X of tree
3788             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/) {
3789                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
3790             }
3791             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
3792                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE + ( _phylogeny.getRoot()
3793                         .getDistanceToParent() * getXcorrectionFactor() ) ) );
3794             }
3795             else {
3796                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
3797             }
3798             // Position starting Y of tree
3799             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
3800                     + ( TreePanel.MOVE / 2.0f ) );
3801             final int dynamic_hiding_factor = ( int ) ( getTreeFontSet()._fm_large.getHeight() / ( 1.5 * getYdistance() ) );
3802             if ( getControlPanel().isDynamicallyHideData() ) {
3803                 if ( dynamic_hiding_factor > 1 ) {
3804                     getControlPanel().setDynamicHidingIsOn( true );
3805                 }
3806                 else {
3807                     getControlPanel().setDynamicHidingIsOn( false );
3808                 }
3809             }
3810             if ( _nodes_in_preorder == null ) {
3811                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
3812                 int i = 0;
3813                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3814                     _nodes_in_preorder[ i++ ] = it.next();
3815                 }
3816             }
3817             //final PhylogenyNodeIterator it;
3818             //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3819             //    paintNodeRectangular( g, it.next(), to_pdf, getControlPanel().isDynamicallyHideData()
3820             //            && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3821             //}
3822             for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3823                 paintNodeRectangular( g, _nodes_in_preorder[ i ], to_pdf, getControlPanel().isDynamicallyHideData()
3824                         && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3825             }
3826             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
3827                 if ( !( to_graphics_file || to_pdf ) ) {
3828                     paintScale( g,
3829                                 getVisibleRect().x,
3830                                 getVisibleRect().y + getVisibleRect().height,
3831                                 to_pdf,
3832                                 to_graphics_file );
3833                 }
3834                 else {
3835                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3836                 }
3837             }
3838             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3839                 paintPhylogenyLite( g );
3840             }
3841         }
3842         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
3843             if ( getControlPanel().getDynamicallyHideData() != null ) {
3844                 getControlPanel().setDynamicHidingIsOn( false );
3845             }
3846             final double angle = getStartingAngle();
3847             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
3848             _dynamic_hiding_factor = 0;
3849             if ( getControlPanel().isDynamicallyHideData() ) {
3850                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3851                         .getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
3852             }
3853             if ( getControlPanel().getDynamicallyHideData() != null ) {
3854                 if ( _dynamic_hiding_factor > 1 ) {
3855                     getControlPanel().setDynamicHidingIsOn( true );
3856                 }
3857                 else {
3858                     getControlPanel().setDynamicHidingIsOn( false );
3859                 }
3860             }
3861             paintUnrooted( _phylogeny.getRoot(),
3862                            angle,
3863                            ( float ) ( angle + 2 * Math.PI ),
3864                            radial_labels,
3865                            g,
3866                            to_pdf,
3867                            to_graphics_file );
3868             if ( getOptions().isShowScale() ) {
3869                 if ( !( to_graphics_file || to_pdf ) ) {
3870                     paintScale( g,
3871                                 getVisibleRect().x,
3872                                 getVisibleRect().y + getVisibleRect().height,
3873                                 to_pdf,
3874                                 to_graphics_file );
3875                 }
3876                 else {
3877                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3878                 }
3879             }
3880             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3881                 g.setColor( getTreeColorSet().getOvColor() );
3882                 paintUnrootedLite( _phylogeny.getRoot(),
3883                                    angle,
3884                                    angle + 2 * Math.PI,
3885                                    g,
3886                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
3887                 paintOvRectangle( g );
3888             }
3889         }
3890         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3891             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() ) / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
3892             final int d = radius + MOVE + getLongestExtNodeInfo();
3893             _dynamic_hiding_factor = 0;
3894             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
3895                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3896                         .getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
3897             }
3898             if ( getControlPanel().getDynamicallyHideData() != null ) {
3899                 if ( _dynamic_hiding_factor > 1 ) {
3900                     getControlPanel().setDynamicHidingIsOn( true );
3901                 }
3902                 else {
3903                     getControlPanel().setDynamicHidingIsOn( false );
3904                 }
3905             }
3906             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
3907             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3908                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
3909                         : getOvMaxWidth() / 2 );
3910                 double x_scale = 1.0;
3911                 double y_scale = 1.0;
3912                 int x_pos = getVisibleRect().x + getOvXPosition();
3913                 int y_pos = getVisibleRect().y + getOvYPosition();
3914                 if ( getWidth() > getHeight() ) {
3915                     x_scale = ( double ) getHeight() / getWidth();
3916                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
3917                 }
3918                 else {
3919                     y_scale = ( double ) getWidth() / getHeight();
3920                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
3921                 }
3922                 _at = g.getTransform();
3923                 g.scale( x_scale, y_scale );
3924                 paintCircularLite( _phylogeny,
3925                                    getStartingAngle(),
3926                                    x_pos + radius_ov,
3927                                    y_pos + radius_ov,
3928                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo() / ( getVisibleRect().width / getOvRectangle()
3929                                            .getWidth() ) ) ),
3930                                    g );
3931                 g.setTransform( _at );
3932                 paintOvRectangle( g );
3933             }
3934         }
3935     }
3936
3937     final private void paintPhylogenyLite( final Graphics2D g ) {
3938         //System.out.println( getVisibleRect().x + " " + getVisibleRect().y );
3939         _phylogeny
3940                 .getRoot()
3941                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
3942                         .getWidth() ) ) ) );
3943         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
3944         //final PhylogenyNodeIterator it;
3945         //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3946         //    paintNodeLite( g, it.next() );
3947         //}
3948         for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3949             paintNodeLite( g, _nodes_in_preorder[ i ] );
3950         }
3951         paintOvRectangle( g );
3952     }
3953
3954     /**
3955      * Paint the root branch. (Differs from others because it will always be a
3956      * single horizontal line).
3957      * @param to_graphics_file 
3958      * 
3959      * @return new x1 value
3960      */
3961     final private void paintRootBranch( final Graphics2D g,
3962                                         final float x1,
3963                                         final float y1,
3964                                         final PhylogenyNode root,
3965                                         final boolean to_pdf,
3966                                         final boolean to_graphics_file ) {
3967         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
3968         float d = getXdistance();
3969         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
3970             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
3971         }
3972         if ( d < MIN_ROOT_LENGTH ) {
3973             d = MIN_ROOT_LENGTH;
3974         }
3975         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
3976             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
3977         }
3978         else {
3979             final double w = PhylogenyMethods.getBranchWidthValue( root );
3980             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
3981         }
3982         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file, isInFoundNodes( root ) );
3983     }
3984
3985     final private void paintScale( final Graphics2D g,
3986                                    int x1,
3987                                    int y1,
3988                                    final boolean to_pdf,
3989                                    final boolean to_graphics_file ) {
3990         x1 += MOVE;
3991         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
3992         y1 -= 12;
3993         final int y2 = y1 - 8;
3994         final int y3 = y1 - 4;
3995         g.setFont( getTreeFontSet().getSmallFont() );
3996         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3997             g.setColor( Color.BLACK );
3998         }
3999         else {
4000             g.setColor( getTreeColorSet().getBranchLengthColor() );
4001         }
4002         drawLine( x1, y1, x1, y2, g );
4003         drawLine( x2, y1, x2, y2, g );
4004         drawLine( x1, y3, x2, y3, g );
4005         if ( getScaleLabel() != null ) {
4006             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
4007         }
4008     }
4009
4010     final private int paintTaxonomy( final Graphics2D g,
4011                                      final PhylogenyNode node,
4012                                      final boolean is_in_found_nodes,
4013                                      final boolean to_pdf,
4014                                      final boolean to_graphics_file,
4015                                      final double x_shift ) {
4016         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4017         g.setFont( getTreeFontSet().getLargeItalicFont() );
4018         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4019             g.setColor( Color.BLACK );
4020         }
4021         else if ( is_in_found_nodes ) {
4022             g.setFont( getTreeFontSet().getLargeItalicFont().deriveFont( TreeFontSet.BOLD_AND_ITALIC ) );
4023             g.setColor( getTreeColorSet().getFoundColor() );
4024         }
4025         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4026             g.setColor( getTaxonomyBasedColor( node ) );
4027         }
4028         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
4029                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
4030             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
4031         }
4032         else if ( to_pdf ) {
4033             g.setColor( Color.BLACK );
4034         }
4035         else {
4036             g.setColor( getTreeColorSet().getTaxonomyColor() );
4037         }
4038         final double start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
4039         final double start_y = node.getYcoord()
4040                 + ( getTreeFontSet()._fm_large.getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0 ) );
4041         _sb.setLength( 0 );
4042         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4043             _sb.append( taxonomy.getTaxonomyCode() );
4044             _sb.append( " " );
4045         }
4046         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4047             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4048                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4049                 if ( getOptions().isAbbreviateScientificTaxonNames()
4050                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4051                     abbreviateScientificName( taxonomy.getScientificName() );
4052                 }
4053                 else {
4054                     _sb.append( taxonomy.getScientificName() );
4055                 }
4056                 _sb.append( " (" );
4057                 _sb.append( taxonomy.getCommonName() );
4058                 _sb.append( ") " );
4059             }
4060             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4061                 if ( getOptions().isAbbreviateScientificTaxonNames()
4062                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4063                     abbreviateScientificName( taxonomy.getScientificName() );
4064                 }
4065                 else {
4066                     _sb.append( taxonomy.getScientificName() );
4067                 }
4068                 _sb.append( " " );
4069             }
4070             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4071                 _sb.append( taxonomy.getCommonName() );
4072                 _sb.append( " " );
4073             }
4074         }
4075         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4076             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4077                 if ( getOptions().isAbbreviateScientificTaxonNames()
4078                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4079                     abbreviateScientificName( taxonomy.getScientificName() );
4080                 }
4081                 else {
4082                     _sb.append( taxonomy.getScientificName() );
4083                 }
4084                 _sb.append( " " );
4085             }
4086         }
4087         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4088             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4089                 _sb.append( taxonomy.getCommonName() );
4090                 _sb.append( " " );
4091             }
4092         }
4093         final String label = _sb.toString();
4094         /* GUILHEM_BEG */
4095         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
4096                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
4097             // invert font color and background color to show that this is the query sequence
4098             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
4099                                                                                                           false,
4100                                                                                                           false ) )
4101                     .getBounds();
4102             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
4103             g.setColor( getTreeColorSet().getBackgroundColor() );
4104         }
4105         /* GUILHEM_END */
4106         TreePanel.drawString( label, start_x, start_y, g );
4107         if ( is_in_found_nodes ) {
4108             return getTreeFontSet()._fm_large_italic_bold.stringWidth( label );
4109         }
4110         else {
4111             return getTreeFontSet()._fm_large_italic.stringWidth( label );
4112         }
4113     }
4114
4115     private void abbreviateScientificName( final String sn ) {
4116         final String[] a = sn.split( "\\s+" );
4117         _sb.append( a[ 0 ].substring( 0, 1 ) );
4118         _sb.append( a[ 1 ].substring( 0, 2 ) );
4119         if ( a.length > 2 ) {
4120             for( int i = 2; i < a.length; i++ ) {
4121                 _sb.append( " " );
4122                 _sb.append( a[ i ] );
4123             }
4124         }
4125     }
4126
4127     final private void paintUnrooted( final PhylogenyNode n,
4128                                       final double low_angle,
4129                                       final double high_angle,
4130                                       final boolean radial_labels,
4131                                       final Graphics2D g,
4132                                       final boolean to_pdf,
4133                                       final boolean to_graphics_file ) {
4134         if ( n.isRoot() ) {
4135             n.setXcoord( getWidth() / 2 );
4136             n.setYcoord( getHeight() / 2 );
4137         }
4138         if ( n.isExternal() ) {
4139             paintNodeDataUnrootedCirc( g,
4140                                        n,
4141                                        to_pdf,
4142                                        to_graphics_file,
4143                                        radial_labels,
4144                                        ( high_angle + low_angle ) / 2,
4145                                        isInFoundNodes( n ) );
4146             return;
4147         }
4148         final float num_enclosed = n.getNumberOfExternalNodes();
4149         final float x = n.getXcoord();
4150         final float y = n.getYcoord();
4151         double current_angle = low_angle;
4152         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4153         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4154         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4155         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4156         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4157             final PhylogenyNode desc = n.getChildNode( i );
4158             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4159             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4160             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4161             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4162             //     continue;
4163             // }
4164             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4165             //    continue;
4166             //}
4167             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4168             //    continue;
4169             // }
4170             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4171             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4172             float length;
4173             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4174                 if ( desc.getDistanceToParent() < 0 ) {
4175                     length = 0;
4176                 }
4177                 else {
4178                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4179                 }
4180             }
4181             else {
4182                 length = getUrtFactor();
4183             }
4184             final double mid_angle = current_angle + arc_size / 2;
4185             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4186             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4187             desc.setXcoord( new_x );
4188             desc.setYcoord( new_y );
4189             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
4190             current_angle += arc_size;
4191             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
4192             drawLine( x, y, new_x, new_y, g );
4193             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file, isInFoundNodes( desc ) );
4194         }
4195         if ( n.isRoot() ) {
4196             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file, isInFoundNodes( n ) );
4197         }
4198     }
4199
4200     final private void paintUnrootedLite( final PhylogenyNode n,
4201                                           final double low_angle,
4202                                           final double high_angle,
4203                                           final Graphics2D g,
4204                                           final float urt_ov_factor ) {
4205         if ( n.isRoot() ) {
4206             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / 2 );
4207             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / 2 );
4208             n.setXSecondary( x_pos );
4209             n.setYSecondary( y_pos );
4210         }
4211         if ( n.isExternal() ) {
4212             return;
4213         }
4214         final float num_enclosed = n.getNumberOfExternalNodes();
4215         final float x = n.getXSecondary();
4216         final float y = n.getYSecondary();
4217         double current_angle = low_angle;
4218         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4219             final PhylogenyNode desc = n.getChildNode( i );
4220             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4221             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4222             float length;
4223             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4224                 if ( desc.getDistanceToParent() < 0 ) {
4225                     length = 0;
4226                 }
4227                 else {
4228                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
4229                 }
4230             }
4231             else {
4232                 length = urt_ov_factor;
4233             }
4234             final double mid_angle = current_angle + arc_size / 2;
4235             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4236             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4237             desc.setXSecondary( new_x );
4238             desc.setYSecondary( new_y );
4239             if ( isInFoundNodes( desc ) ) {
4240                 g.setColor( getTreeColorSet().getFoundColor() );
4241                 drawRectFilled( desc.getXSecondary() - 1, desc.getYSecondary() - 1, 3, 3, g );
4242                 g.setColor( getTreeColorSet().getOvColor() );
4243             }
4244             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
4245             current_angle += arc_size;
4246             drawLine( x, y, new_x, new_y, g );
4247         }
4248     }
4249
4250     final private void pasteSubtree( final PhylogenyNode node ) {
4251         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4252             errorMessageNoCutCopyPasteInUnrootedDisplay();
4253             return;
4254         }
4255         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
4256             JOptionPane.showMessageDialog( this,
4257                                            "No tree in buffer (need to copy or cut a subtree first)",
4258                                            "Attempt to paste with empty buffer",
4259                                            JOptionPane.ERROR_MESSAGE );
4260             return;
4261         }
4262         final String label = getASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
4263         final Object[] options = { "As sibling", "As descendant", "Cancel" };
4264         final int r = JOptionPane.showOptionDialog( this,
4265                                                     "How to paste subtree" + label + "?",
4266                                                     "Paste Subtree",
4267                                                     JOptionPane.CLOSED_OPTION,
4268                                                     JOptionPane.QUESTION_MESSAGE,
4269                                                     null,
4270                                                     options,
4271                                                     options[ 2 ] );
4272         boolean paste_as_sibling = true;
4273         if ( r == 1 ) {
4274             paste_as_sibling = false;
4275         }
4276         else if ( r != 0 ) {
4277             return;
4278         }
4279         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
4280         buffer_phy.setAllNodesToNotCollapse();
4281         buffer_phy.preOrderReId();
4282         buffer_phy.setRooted( true );
4283         boolean need_to_show_whole = false;
4284         if ( paste_as_sibling ) {
4285             if ( node.isRoot() ) {
4286                 JOptionPane.showMessageDialog( this,
4287                                                "Cannot paste sibling to root",
4288                                                "Attempt to paste sibling to root",
4289                                                JOptionPane.ERROR_MESSAGE );
4290                 return;
4291             }
4292             buffer_phy.addAsSibling( node );
4293         }
4294         else {
4295             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
4296                 need_to_show_whole = true;
4297                 _phylogeny = buffer_phy;
4298             }
4299             else {
4300                 buffer_phy.addAsChild( node );
4301             }
4302         }
4303         if ( getCopiedAndPastedNodes() == null ) {
4304             setCopiedAndPastedNodes( new HashSet<Integer>() );
4305         }
4306         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
4307         final Set<Integer> node_ids = new HashSet<Integer>( nodes.size() );
4308         for( final PhylogenyNode n : nodes ) {
4309             node_ids.add( n.getId() );
4310         }
4311         node_ids.add( node.getId() );
4312         getCopiedAndPastedNodes().addAll( node_ids );
4313         setNodeInPreorderToNull();
4314         _phylogeny.externalNodesHaveChanged();
4315         _phylogeny.clearHashIdToNodeMap();
4316         _phylogeny.recalculateNumberOfExternalDescendants( true );
4317         resetNodeIdToDistToLeafMap();
4318         setEdited( true );
4319         if ( need_to_show_whole ) {
4320             getControlPanel().showWhole();
4321         }
4322         repaint();
4323     }
4324
4325     final public int print( final Graphics g, final PageFormat page_format, final int page_index )
4326             throws PrinterException {
4327         if ( page_index > 0 ) {
4328             return ( NO_SUCH_PAGE );
4329         }
4330         else {
4331             final Graphics2D g2d = ( Graphics2D ) g;
4332             g2d.translate( page_format.getImageableX(), page_format.getImageableY() );
4333             // Turn off double buffering !?
4334             paintPhylogeny( g2d, true, false, 0, 0, 0, 0 );
4335             // Turn double buffering back on !?
4336             return ( PAGE_EXISTS );
4337         }
4338     }
4339
4340     final void recalculateMaxDistanceToRoot() {
4341         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
4342     }
4343
4344     /**
4345      * Remove all edit-node frames
4346      */
4347     final void removeAllEditNodeJFrames() {
4348         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
4349             if ( _node_frames[ i ] != null ) {
4350                 _node_frames[ i ].dispose();
4351                 _node_frames[ i ] = null;
4352             }
4353         }
4354         _node_frame_index = 0;
4355     }
4356
4357     /**
4358      * Remove a node-edit frame.
4359      */
4360     final void removeEditNodeFrame( final int i ) {
4361         _node_frame_index--;
4362         _node_frames[ i ] = null;
4363         if ( i < _node_frame_index ) {
4364             for( int j = 0; j < _node_frame_index - 1; j++ ) {
4365                 _node_frames[ j ] = _node_frames[ j + 1 ];
4366             }
4367             _node_frames[ _node_frame_index ] = null;
4368         }
4369     }
4370
4371     final void reRoot( final PhylogenyNode node ) {
4372         if ( !getPhylogeny().isRerootable() ) {
4373             JOptionPane.showMessageDialog( this,
4374                                            "This is not rerootable",
4375                                            "Not rerootable",
4376                                            JOptionPane.WARNING_MESSAGE );
4377             return;
4378         }
4379         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4380             JOptionPane.showMessageDialog( this,
4381                                            "Cannot reroot in unrooted display type",
4382                                            "Attempt to reroot tree in unrooted display",
4383                                            JOptionPane.WARNING_MESSAGE );
4384             return;
4385         }
4386         getPhylogeny().reRoot( node );
4387         getPhylogeny().recalculateNumberOfExternalDescendants( true );
4388         resetNodeIdToDistToLeafMap();
4389         setNodeInPreorderToNull();
4390         resetPreferredSize();
4391         getMainPanel().adjustJScrollPane();
4392         repaint();
4393         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4394             getControlPanel().showWhole();
4395         }
4396     }
4397
4398     final void resetNodeIdToDistToLeafMap() {
4399         _nodeid_dist_to_leaf = new HashMap<Integer, Short>();
4400     }
4401
4402     final void resetPreferredSize() {
4403         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
4404             return;
4405         }
4406         int x = 0;
4407         int y = 0;
4408         y = TreePanel.MOVE
4409                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
4410         if ( getControlPanel().isDrawPhylogram() ) {
4411             x = TreePanel.MOVE
4412                     + getLongestExtNodeInfo()
4413                     + ForesterUtil
4414                             .roundToInt( ( getXcorrectionFactor() * getPhylogeny().getHeight() ) + getXdistance() );
4415         }
4416         else {
4417             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4418                 x = TreePanel.MOVE
4419                         + getLongestExtNodeInfo()
4420                         + ForesterUtil.roundToInt( getXdistance()
4421                                 * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
4422             }
4423             else {
4424                 x = TreePanel.MOVE
4425                         + getLongestExtNodeInfo()
4426                         + ForesterUtil.roundToInt( getXdistance()
4427                                 * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
4428             }
4429         }
4430         setPreferredSize( new Dimension( x, y ) );
4431     }
4432
4433     public final void setArrowCursor() {
4434         setCursor( ARROW_CURSOR );
4435         repaint();
4436     }
4437
4438     final void setControlPanel( final ControlPanel atv_control ) {
4439         _control_panel = atv_control;
4440     }
4441
4442     final private void setCopiedAndPastedNodes( final Set<Integer> nodeIds ) {
4443         getMainPanel().setCopiedAndPastedNodes( nodeIds );
4444     }
4445
4446     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
4447         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
4448     }
4449
4450     public final void setEdited( final boolean edited ) {
4451         _edited = edited;
4452     }
4453
4454     final void setFoundNodes( final Set<Integer> found_nodes ) {
4455         _found_nodes = found_nodes;
4456     }
4457
4458     final private void setInOv( final boolean in_ov ) {
4459         _in_ov = in_ov;
4460     }
4461
4462     final void setInOvRect( final boolean in_ov_rect ) {
4463         _in_ov_rect = in_ov_rect;
4464     }
4465
4466     final void setLargeFonts() {
4467         getTreeFontSet().largeFonts();
4468     }
4469
4470     final void setLastMouseDragPointX( final float x ) {
4471         _last_drag_point_x = x;
4472     }
4473
4474     final void setLastMouseDragPointY( final float y ) {
4475         _last_drag_point_y = y;
4476     }
4477
4478     final void setLongestExtNodeInfo( final int i ) {
4479         _longest_ext_node_info = i;
4480     }
4481
4482     final void setMediumFonts() {
4483         getTreeFontSet().mediumFonts();
4484     }
4485
4486     final private void setOvMaxHeight( final float ov_max_height ) {
4487         _ov_max_height = ov_max_height;
4488     }
4489
4490     final private void setOvMaxWidth( final float ov_max_width ) {
4491         _ov_max_width = ov_max_width;
4492     }
4493
4494     final void setOvOn( final boolean ov_on ) {
4495         _ov_on = ov_on;
4496     }
4497
4498     final private void setOvXcorrectionFactor( final float f ) {
4499         _ov_x_correction_factor = f;
4500     }
4501
4502     final private void setOvXDistance( final float ov_x_distance ) {
4503         _ov_x_distance = ov_x_distance;
4504     }
4505
4506     final private void setOvXPosition( final int ov_x_position ) {
4507         _ov_x_position = ov_x_position;
4508     }
4509
4510     final private void setOvYDistance( final float ov_y_distance ) {
4511         _ov_y_distance = ov_y_distance;
4512     }
4513
4514     final private void setOvYPosition( final int ov_y_position ) {
4515         _ov_y_position = ov_y_position;
4516     }
4517
4518     final private void setOvYStart( final int ov_y_start ) {
4519         _ov_y_start = ov_y_start;
4520     }
4521
4522     /**
4523      * Set parameters for printing the displayed tree
4524      * 
4525      * @param x
4526      * @param y
4527      */
4528     public final void setParametersForPainting( final int x, final int y, final boolean recalc_longest_ext_node_info ) {
4529         // updateStyle(); not needed?
4530         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4531             initNodeData();
4532             if ( recalc_longest_ext_node_info ) {
4533                 calculateLongestExtNodeInfo();
4534             }
4535             int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4536             final int max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4537             if ( ext_nodes == 1 ) {
4538                 ext_nodes = max_depth;
4539                 if ( ext_nodes < 1 ) {
4540                     ext_nodes = 1;
4541                 }
4542             }
4543             updateOvSizes();
4544             float xdist = 0;
4545             float ov_xdist = 0;
4546             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4547                 xdist = ( float ) ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( ext_nodes + 3.0 ) );
4548                 ov_xdist = ( float ) ( getOvMaxWidth() / ( ext_nodes + 3.0 ) );
4549             }
4550             else {
4551                 xdist = ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( max_depth + 1 ) );
4552                 ov_xdist = ( getOvMaxWidth() / ( max_depth + 1 ) );
4553             }
4554             float ydist = ( float ) ( ( y - TreePanel.MOVE ) / ( ext_nodes * 2.0 ) );
4555             if ( xdist < 0.0 ) {
4556                 xdist = 0.0f;
4557             }
4558             if ( ov_xdist < 0.0 ) {
4559                 ov_xdist = 0.0f;
4560             }
4561             if ( ydist < 0.0 ) {
4562                 ydist = 0.0f;
4563             }
4564             setXdistance( xdist );
4565             setYdistance( ydist );
4566             setOvXDistance( ov_xdist );
4567             final double height = _phylogeny.getHeight();
4568             if ( height > 0 ) {
4569                 final float corr = ( float ) ( ( x - TreePanel.MOVE - getLongestExtNodeInfo() - getXdistance() ) / height );
4570                 setXcorrectionFactor( corr > 0 ? corr : 0 );
4571                 final float ov_corr = ( float ) ( ( getOvMaxWidth() - getOvXDistance() ) / height );
4572                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
4573             }
4574             else {
4575                 setXcorrectionFactor( 0 );
4576                 setOvXcorrectionFactor( 0 );
4577             }
4578             _circ_max_depth = max_depth;
4579             setUpUrtFactor();
4580         }
4581     }
4582
4583     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
4584         _graphics_type = graphics_type;
4585         setTextAntialias();
4586     }
4587
4588     final private void setScaleDistance( final double scale_distance ) {
4589         _scale_distance = scale_distance;
4590     }
4591
4592     final private void setScaleLabel( final String scale_label ) {
4593         _scale_label = scale_label;
4594     }
4595
4596     final void setSmallFonts() {
4597         getTreeFontSet().smallFonts();
4598     }
4599
4600     final void setStartingAngle( final double starting_angle ) {
4601         _urt_starting_angle = starting_angle;
4602     }
4603
4604     final void setSuperTinyFonts() {
4605         getTreeFontSet().superTinyFonts();
4606     }
4607
4608     final void setTextAntialias() {
4609         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4610             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
4611                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
4612             }
4613             else {
4614                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
4615             }
4616         }
4617         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
4618             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4619                     && !getMainPanel().getOptions().isShowDefaultNodeShapes()
4620                     && ( ( getControlPanel() != null ) && !getControlPanel().isShowDomainArchitectures() ) ) {
4621                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4622             }
4623             else {
4624                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
4625             }
4626             try {
4627                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING,
4628                                       RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
4629             }
4630             catch ( final Throwable e ) {
4631                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
4632             }
4633         }
4634         else {
4635             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
4636             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4637         }
4638     }
4639
4640     final void setTinyFonts() {
4641         getTreeFontSet().tinyFonts();
4642     }
4643
4644     /**
4645      * Set a phylogeny tree.
4646      * 
4647      * @param t
4648      *            an instance of a Phylogeny
4649      */
4650     public final void setTree( final Phylogeny t ) {
4651         setNodeInPreorderToNull();
4652         _phylogeny = t;
4653     }
4654
4655     final void setNodeInPreorderToNull() {
4656         _nodes_in_preorder = null;
4657     }
4658
4659     final void setTreeFile( final File treefile ) {
4660         _treefile = treefile;
4661     }
4662
4663     final private void setUpUrtFactor() {
4664         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
4665                 : getVisibleRect().height;
4666         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4667             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
4668         }
4669         else {
4670             final int max_depth = _circ_max_depth;
4671             if ( max_depth > 0 ) {
4672                 setUrtFactor( d / ( 2 * max_depth ) );
4673             }
4674             else {
4675                 setUrtFactor( d / 2 );
4676             }
4677         }
4678         setUrtFactorOv( getUrtFactor() );
4679     }
4680
4681     final private void setUrtFactor( final float urt_factor ) {
4682         _urt_factor = urt_factor;
4683     }
4684
4685     final private void setUrtFactorOv( final float urt_factor_ov ) {
4686         _urt_factor_ov = urt_factor_ov;
4687     }
4688
4689     public final void setWaitCursor() {
4690         setCursor( WAIT_CURSOR );
4691         repaint();
4692     }
4693
4694     final void setXcorrectionFactor( final float f ) {
4695         _x_correction_factor = f;
4696     }
4697
4698     final void setXdistance( final float x ) {
4699         _x_distance = x;
4700     }
4701
4702     final void setYdistance( final float y ) {
4703         _y_distance = y;
4704     }
4705
4706     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
4707         try {
4708             if ( ( node.getName().length() > 0 )
4709                     || ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
4710                     || ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) )
4711                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
4712                     || node.getBranchData().isHasConfidences() ) {
4713                 _popup_buffer.setLength( 0 );
4714                 short lines = 0;
4715                 if ( node.getName().length() > 0 ) {
4716                     lines++;
4717                     _popup_buffer.append( node.getName() );
4718                 }
4719                 if ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
4720                     lines++;
4721                     boolean enc_data = false;
4722                     final Taxonomy tax = node.getNodeData().getTaxonomy();
4723                     if ( _popup_buffer.length() > 0 ) {
4724                         _popup_buffer.append( "\n" );
4725                     }
4726                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4727                         _popup_buffer.append( "[" );
4728                         _popup_buffer.append( tax.getTaxonomyCode() );
4729                         _popup_buffer.append( "]" );
4730                         enc_data = true;
4731                     }
4732                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4733                         if ( enc_data ) {
4734                             _popup_buffer.append( " " );
4735                         }
4736                         _popup_buffer.append( tax.getScientificName() );
4737                         enc_data = true;
4738                     }
4739                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
4740                         if ( enc_data ) {
4741                             _popup_buffer.append( " (" );
4742                         }
4743                         else {
4744                             _popup_buffer.append( "(" );
4745                         }
4746                         _popup_buffer.append( tax.getCommonName() );
4747                         _popup_buffer.append( ")" );
4748                         enc_data = true;
4749                     }
4750                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
4751                         if ( enc_data ) {
4752                             _popup_buffer.append( " (" );
4753                         }
4754                         else {
4755                             _popup_buffer.append( "(" );
4756                         }
4757                         _popup_buffer.append( tax.getAuthority() );
4758                         _popup_buffer.append( ")" );
4759                         enc_data = true;
4760                     }
4761                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
4762                         if ( enc_data ) {
4763                             _popup_buffer.append( " [" );
4764                         }
4765                         else {
4766                             _popup_buffer.append( "[" );
4767                         }
4768                         _popup_buffer.append( tax.getRank() );
4769                         _popup_buffer.append( "]" );
4770                         enc_data = true;
4771                     }
4772                     if ( tax.getSynonyms().size() > 0 ) {
4773                         if ( enc_data ) {
4774                             _popup_buffer.append( " " );
4775                         }
4776                         _popup_buffer.append( "[" );
4777                         int counter = 1;
4778                         for( final String syn : tax.getSynonyms() ) {
4779                             if ( !ForesterUtil.isEmpty( syn ) ) {
4780                                 enc_data = true;
4781                                 _popup_buffer.append( syn );
4782                                 if ( counter < tax.getSynonyms().size() ) {
4783                                     _popup_buffer.append( ", " );
4784                                 }
4785                             }
4786                             counter++;
4787                         }
4788                         _popup_buffer.append( "]" );
4789                     }
4790                     if ( !enc_data ) {
4791                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
4792                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
4793                                 _popup_buffer.append( "[" );
4794                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
4795                                 _popup_buffer.append( "] " );
4796                             }
4797                             _popup_buffer.append( tax.getIdentifier().getValue() );
4798                         }
4799                     }
4800                 }
4801                 if ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) ) {
4802                     lines++;
4803                     boolean enc_data = false;
4804                     if ( _popup_buffer.length() > 0 ) {
4805                         _popup_buffer.append( "\n" );
4806                     }
4807                     final Sequence seq = node.getNodeData().getSequence();
4808                     if ( seq.getAccession() != null ) {
4809                         _popup_buffer.append( "[" );
4810                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
4811                             _popup_buffer.append( seq.getAccession().getSource() );
4812                             _popup_buffer.append( ":" );
4813                         }
4814                         _popup_buffer.append( seq.getAccession().getValue() );
4815                         _popup_buffer.append( "]" );
4816                         enc_data = true;
4817                     }
4818                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
4819                         if ( enc_data ) {
4820                             _popup_buffer.append( " [" );
4821                         }
4822                         else {
4823                             _popup_buffer.append( "[" );
4824                         }
4825                         _popup_buffer.append( seq.getSymbol() );
4826                         _popup_buffer.append( "]" );
4827                         enc_data = true;
4828                     }
4829                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
4830                         if ( enc_data ) {
4831                             _popup_buffer.append( " " );
4832                         }
4833                         _popup_buffer.append( seq.getName() );
4834                     }
4835                 }
4836                 if ( node.getNodeData().isHasDate() ) {
4837                     lines++;
4838                     if ( _popup_buffer.length() > 0 ) {
4839                         _popup_buffer.append( "\n" );
4840                     }
4841                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
4842                 }
4843                 if ( node.getNodeData().isHasDistribution() ) {
4844                     lines++;
4845                     if ( _popup_buffer.length() > 0 ) {
4846                         _popup_buffer.append( "\n" );
4847                     }
4848                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
4849                 }
4850                 if ( node.getBranchData().isHasConfidences() ) {
4851                     final List<Confidence> confs = node.getBranchData().getConfidences();
4852                     for( final Confidence confidence : confs ) {
4853                         lines++;
4854                         if ( _popup_buffer.length() > 0 ) {
4855                             _popup_buffer.append( "\n" );
4856                         }
4857                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
4858                             _popup_buffer.append( "[" );
4859                             _popup_buffer.append( confidence.getType() );
4860                             _popup_buffer.append( "] " );
4861                         }
4862                         _popup_buffer
4863                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
4864                                                                                           getOptions()
4865                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4866                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4867                             _popup_buffer.append( " (sd=" );
4868                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
4869                                     .getStandardDeviation(), getOptions()
4870                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4871                             _popup_buffer.append( ")" );
4872                         }
4873                     }
4874                 }
4875                 if ( node.getNodeData().isHasProperties() ) {
4876                     final PropertiesMap properties = node.getNodeData().getProperties();
4877                     for( final String ref : properties.getPropertyRefs() ) {
4878                         _popup_buffer.append( "\n" );
4879                         final Property p = properties.getProperty( ref );
4880                         _popup_buffer.append( getPartAfterColon( p.getRef() ) );
4881                         _popup_buffer.append( "=" );
4882                         _popup_buffer.append( p.getValue() );
4883                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
4884                             _popup_buffer.append( getPartAfterColon( p.getUnit() ) );
4885                         }
4886                     }
4887                 }
4888                 if ( _popup_buffer.length() > 0 ) {
4889                     if ( !getConfiguration().isUseNativeUI() ) {
4890                         _rollover_popup
4891                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
4892                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
4893                         if ( isInFoundNodes( node ) ) {
4894                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
4895                         }
4896                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4897                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
4898                         }
4899                         else {
4900                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
4901                         }
4902                     }
4903                     else {
4904                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
4905                     }
4906                     _rollover_popup.setText( _popup_buffer.toString() );
4907                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
4908                                                                                   _rollover_popup,
4909                                                                                   e.getLocationOnScreen().x + 10,
4910                                                                                   e.getLocationOnScreen().y
4911                                                                                           - ( lines * 20 ) );
4912                     _node_desc_popup.show();
4913                 }
4914             }
4915         }
4916         catch ( final Exception ex ) {
4917             // Do nothing.
4918         }
4919     }
4920
4921     final private void showNodeEditFrame( final PhylogenyNode n ) {
4922         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4923             // pop up edit box for single node
4924             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
4925             _node_frame_index++;
4926         }
4927         else {
4928             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4929         }
4930     }
4931
4932     final private void showNodeFrame( final PhylogenyNode n ) {
4933         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4934             // pop up edit box for single node
4935             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
4936             _node_frame_index++;
4937         }
4938         else {
4939             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4940         }
4941     }
4942
4943     final void subTree( final PhylogenyNode node ) {
4944         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4945             JOptionPane.showMessageDialog( this,
4946                                            "Cannot get a sub/super tree in unrooted display",
4947                                            "Attempt to get sub/super tree in unrooted display",
4948                                            JOptionPane.WARNING_MESSAGE );
4949             return;
4950         }
4951         if ( node.isExternal() ) {
4952             JOptionPane.showMessageDialog( this,
4953                                            "Cannot get a subtree of a external node",
4954                                            "Attempt to get subtree of external node",
4955                                            JOptionPane.WARNING_MESSAGE );
4956             return;
4957         }
4958         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
4959             JOptionPane.showMessageDialog( this,
4960                                            "Cannot get a subtree of the root node",
4961                                            "Attempt to get subtree of root node",
4962                                            JOptionPane.WARNING_MESSAGE );
4963             return;
4964         }
4965         setNodeInPreorderToNull();
4966         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
4967             _sub_phylogenies[ _subtree_index ] = _phylogeny;
4968             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
4969             ++_subtree_index;
4970             _phylogeny = subTree( node, _phylogeny );
4971             updateSubSuperTreeButton();
4972         }
4973         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
4974             superTree();
4975         }
4976         _main_panel.getControlPanel().showWhole();
4977         repaint();
4978     }
4979
4980     final boolean isCurrentTreeIsSubtree() {
4981         return ( _subtree_index > 0 );
4982     }
4983
4984     final private static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
4985         final Phylogeny new_phy = new Phylogeny();
4986         new_phy.setRooted( true );
4987         new_phy.setName( source_phy.getName() );
4988         new_phy.setDescription( source_phy.getDescription() );
4989         new_phy.setType( source_phy.getType() );
4990         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
4991         new_phy.setConfidence( source_phy.getConfidence() );
4992         new_phy.setIdentifier( source_phy.getIdentifier() );
4993         new_phy.setRoot( new_root.copyNodeDataShallow() );
4994         int i = 0;
4995         for( final PhylogenyNode n : new_root.getDescendants() ) {
4996             new_phy.getRoot().setChildNode( i++, n );
4997         }
4998         return new_phy;
4999     }
5000
5001     final void superTree() {
5002         setNodeInPreorderToNull();
5003         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
5004         for( final PhylogenyNode n : temp_root.getDescendants() ) {
5005             n.setParent( temp_root );
5006         }
5007         _sub_phylogenies[ _subtree_index ] = null;
5008         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
5009         _phylogeny = _sub_phylogenies[ --_subtree_index ];
5010         updateSubSuperTreeButton();
5011     }
5012
5013     final void swap( final PhylogenyNode node ) {
5014         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
5015             return;
5016         }
5017         if ( node.getNumberOfDescendants() > 2 ) {
5018             JOptionPane.showMessageDialog( this,
5019                                            "Cannot swap descendants of nodes with more than 2 descendants",
5020                                            "Cannot swap descendants",
5021                                            JOptionPane.ERROR_MESSAGE );
5022             return;
5023         }
5024         if ( !node.isExternal() ) {
5025             node.swapChildren();
5026             setNodeInPreorderToNull();
5027             _phylogeny.externalNodesHaveChanged();
5028             _phylogeny.clearHashIdToNodeMap();
5029             _phylogeny.recalculateNumberOfExternalDescendants( true );
5030             resetNodeIdToDistToLeafMap();
5031             setEdited( true );
5032         }
5033         repaint();
5034     }
5035
5036     final void sortDescendants( final PhylogenyNode node ) {
5037         if ( !node.isExternal() ) {
5038             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
5039             if ( ( !getControlPanel().isShowTaxonomyScientificNames() && !getControlPanel().isShowTaxonomyCode() && !getControlPanel()
5040                     .isShowTaxonomyCommonNames() ) ) {
5041                 if ( ( getControlPanel().isShowSequenceAcc() || getControlPanel().isShowGeneNames() || getControlPanel()
5042                         .isShowGeneSymbols() ) ) {
5043                     pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
5044                 }
5045                 else if ( getControlPanel().isShowNodeNames() ) {
5046                     pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
5047                 }
5048             }
5049             PhylogenyMethods.sortNodeDescendents( node, pri );
5050             setNodeInPreorderToNull();
5051             _phylogeny.externalNodesHaveChanged();
5052             _phylogeny.clearHashIdToNodeMap();
5053             _phylogeny.recalculateNumberOfExternalDescendants( true );
5054             resetNodeIdToDistToLeafMap();
5055             setEdited( true );
5056         }
5057         repaint();
5058     }
5059
5060     final private void switchDisplaygetPhylogenyGraphicsType() {
5061         switch ( getPhylogenyGraphicsType() ) {
5062             case RECTANGULAR:
5063                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5064                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5065                 break;
5066             case EURO_STYLE:
5067                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5068                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5069                 break;
5070             case ROUNDED:
5071                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5072                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5073                 break;
5074             case CURVED:
5075                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5076                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5077                 break;
5078             case TRIANGULAR:
5079                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5080                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5081                 break;
5082             case CONVEX:
5083                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5084                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5085                 break;
5086             case UNROOTED:
5087                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5088                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5089                 break;
5090             case CIRCULAR:
5091                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5092                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5093                 break;
5094             default:
5095                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5096         }
5097         if ( getControlPanel().getDynamicallyHideData() != null ) {
5098             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5099                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5100             }
5101             else {
5102                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5103             }
5104         }
5105         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5106             getControlPanel().setDrawPhylogramEnabled( true );
5107         }
5108         else {
5109             getControlPanel().setDrawPhylogramEnabled( false );
5110         }
5111         if ( getMainPanel().getMainFrame() == null ) {
5112             // Must be "E" applet version.
5113             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5114                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5115         }
5116         else {
5117             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5118         }
5119     }
5120
5121     final void taxColor() {
5122         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5123             return;
5124         }
5125         setWaitCursor();
5126         AptxUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
5127         _control_panel.setColorBranches( true );
5128         if ( _control_panel.getColorBranchesCb() != null ) {
5129             _control_panel.getColorBranchesCb().setSelected( true );
5130         }
5131         setArrowCursor();
5132         repaint();
5133     }
5134
5135     final void updateOvSettings() {
5136         switch ( getOptions().getOvPlacement() ) {
5137             case LOWER_LEFT:
5138                 setOvXPosition( OV_BORDER );
5139                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5140                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5141                 break;
5142             case LOWER_RIGHT:
5143                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5144                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5145                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5146                 break;
5147             case UPPER_RIGHT:
5148                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5149                 setOvYPosition( OV_BORDER );
5150                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5151                 break;
5152             default:
5153                 setOvXPosition( OV_BORDER );
5154                 setOvYPosition( OV_BORDER );
5155                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5156                 break;
5157         }
5158     }
5159
5160     final void updateOvSizes() {
5161         if ( ( getWidth() > 1.05 * getVisibleRect().width ) || ( getHeight() > 1.05 * getVisibleRect().height ) ) {
5162             setOvOn( true );
5163             float l = getLongestExtNodeInfo();
5164             final float w_ratio = getOvMaxWidth() / getWidth();
5165             l *= w_ratio;
5166             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
5167             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
5168             float ov_xdist = 0;
5169             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
5170                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
5171             }
5172             else {
5173                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
5174             }
5175             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
5176             if ( ov_xdist < 0.0 ) {
5177                 ov_xdist = 0.0f;
5178             }
5179             if ( ydist < 0.0 ) {
5180                 ydist = 0.0f;
5181             }
5182             setOvXDistance( ov_xdist );
5183             final double height = _phylogeny.getHeight();
5184             if ( height > 0 ) {
5185                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
5186                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
5187             }
5188             else {
5189                 setOvXcorrectionFactor( 0 );
5190             }
5191         }
5192         else {
5193             setOvOn( false );
5194         }
5195     }
5196
5197     final void updateSubSuperTreeButton() {
5198         if ( _subtree_index < 1 ) {
5199             getControlPanel().deactivateButtonToReturnToSuperTree();
5200         }
5201         else {
5202             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
5203         }
5204     }
5205
5206     final void zoomInDomainStructure() {
5207         if ( _domain_structure_width < 2000 ) {
5208             _domain_structure_width *= 1.2;
5209         }
5210     }
5211
5212     final void zoomOutDomainStructure() {
5213         if ( _domain_structure_width > 20 ) {
5214             _domain_structure_width *= 0.8;
5215         }
5216     }
5217
5218     final private static void drawString( final int i, final double x, final double y, final Graphics2D g ) {
5219         g.drawString( String.valueOf( i ), ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5220     }
5221
5222     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5223         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5224     }
5225
5226     final private static boolean isSequenceEmpty( final Sequence seq ) {
5227         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
5228                 && ForesterUtil.isEmpty( seq.getSymbol() );
5229     }
5230
5231     final private static boolean isTaxonomyEmpty( final Taxonomy tax ) {
5232         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
5233                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
5234                 .getSynonyms().isEmpty() );
5235     }
5236
5237     final private static boolean plusPressed( final int key_code ) {
5238         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5239                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5240     }
5241
5242     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
5243         _statistics_for_vector_data = statistics_for_expression_values;
5244     }
5245
5246     DescriptiveStatistics getStatisticsForExpressionValues() {
5247         return _statistics_for_vector_data;
5248     }
5249
5250     final private class SubtreeColorizationActionListener implements ActionListener {
5251
5252         JColorChooser _chooser;
5253         PhylogenyNode _node;
5254
5255         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5256             _chooser = chooser;
5257             _node = node;
5258         }
5259
5260         @Override
5261         public void actionPerformed( final ActionEvent e ) {
5262             final Color c = _chooser.getColor();
5263             if ( c != null ) {
5264                 colorizeSubtree( c, _node );
5265             }
5266         }
5267     }
5268
5269     public synchronized void setImageMap( final Hashtable<String, BufferedImage> image_map ) {
5270         getMainPanel().setImageMap( image_map );
5271     }
5272
5273     public synchronized Hashtable<String, BufferedImage> getImageMap() {
5274         return getMainPanel().getImageMap();
5275     }
5276 }