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