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