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