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