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