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