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