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