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