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