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