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