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