a9c154b689c636bd4af1e2575bb44885f2f1dcab
[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() && !node.isExternal() ) );
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             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
1798                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
1799             }
1800             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
1801                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
1802             }
1803             _node_popup_menu_items[ i ].addActionListener( this );
1804             _node_popup_menu.add( _node_popup_menu_items[ i ] );
1805         }
1806     }
1807
1808     final void midpointRoot() {
1809         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
1810             return;
1811         }
1812         if ( !_phylogeny.isRerootable() ) {
1813             JOptionPane.showMessageDialog( this,
1814                                            "This is not rerootable",
1815                                            "Not rerootable",
1816                                            JOptionPane.WARNING_MESSAGE );
1817             return;
1818         }
1819         setNodeInPreorderToNull();
1820         setWaitCursor();
1821         PhylogenyMethods.midpointRoot( _phylogeny );
1822         resetNodeIdToDistToLeafMap();
1823         setArrowCursor();
1824         repaint();
1825     }
1826
1827     final void mouseClicked( final MouseEvent e ) {
1828         if ( getOptions().isShowOverview() && isOvOn() && isInOv() ) {
1829             final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
1830             final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
1831             double x = ( e.getX() - getVisibleRect().x - getOvXPosition() - getOvRectangle().getWidth() / 2.0 )
1832                     * w_ratio;
1833             double y = ( e.getY() - getVisibleRect().y - getOvYPosition() - getOvRectangle().getHeight() / 2.0 )
1834                     * h_ratio;
1835             if ( x < 0 ) {
1836                 x = 0;
1837             }
1838             if ( y < 0 ) {
1839                 y = 0;
1840             }
1841             final double max_x = getWidth() - getVisibleRect().width;
1842             final double max_y = getHeight() - getVisibleRect().height;
1843             if ( x > max_x ) {
1844                 x = max_x;
1845             }
1846             if ( y > max_y ) {
1847                 y = max_y;
1848             }
1849             getMainPanel().getCurrentScrollPane().getViewport()
1850                     .setViewPosition( new Point( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ) ) );
1851             setInOvRect( true );
1852             repaint();
1853         }
1854         else {
1855             final PhylogenyNode node = findNode( e.getX(), e.getY() );
1856             if ( node != null ) {
1857                 if ( !node.isRoot() && node.getParent().isCollapse() ) {
1858                     return;
1859                 }
1860                 _highlight_node = node;
1861                 // Check if shift key is down
1862                 if ( ( e.getModifiers() & InputEvent.SHIFT_MASK ) != 0 ) {
1863                     // Yes, so add to _found_nodes
1864                     if ( getFoundNodes() == null ) {
1865                         setFoundNodes( new HashSet<Integer>() );
1866                     }
1867                     getFoundNodes().add( node.getId() );
1868                     // Check if control key is down
1869                 }
1870                 else if ( ( e.getModifiers() & InputEvent.CTRL_MASK ) != 0 ) {
1871                     // Yes, so pop-up menu
1872                     displayNodePopupMenu( node, e.getX(), e.getY() );
1873                     // Handle unadorned click
1874                 }
1875                 else {
1876                     // Check for right mouse button
1877                     if ( e.getModifiers() == 4 ) {
1878                         displayNodePopupMenu( node, e.getX(), e.getY() );
1879                     }
1880                     else {
1881                         // if not in _found_nodes, clear _found_nodes
1882                         handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
1883                     }
1884                 }
1885             }
1886             else {
1887                 // no node was clicked
1888                 _highlight_node = null;
1889             }
1890         }
1891         repaint();
1892     }
1893
1894     final void mouseDragInBrowserPanel( final MouseEvent e ) {
1895         setCursor( MOVE_CURSOR );
1896         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1897         scroll_position.x -= ( e.getX() - getLastDragPointX() );
1898         scroll_position.y -= ( e.getY() - getLastDragPointY() );
1899         if ( scroll_position.x < 0 ) {
1900             scroll_position.x = 0;
1901         }
1902         else {
1903             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1904                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1905             if ( scroll_position.x > max_x ) {
1906                 scroll_position.x = max_x;
1907             }
1908         }
1909         if ( scroll_position.y < 0 ) {
1910             scroll_position.y = 0;
1911         }
1912         else {
1913             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1914                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1915             if ( scroll_position.y > max_y ) {
1916                 scroll_position.y = max_y;
1917             }
1918         }
1919         if ( isOvOn() || getOptions().isShowScale() ) {
1920             repaint();
1921         }
1922         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1923     }
1924
1925     final void mouseDragInOvRectangle( final MouseEvent e ) {
1926         setCursor( HAND_CURSOR );
1927         final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
1928         final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
1929         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1930         double dx = ( w_ratio * e.getX() - w_ratio * getLastDragPointX() );
1931         double dy = ( h_ratio * e.getY() - h_ratio * getLastDragPointY() );
1932         scroll_position.x = ForesterUtil.roundToInt( scroll_position.x + dx );
1933         scroll_position.y = ForesterUtil.roundToInt( scroll_position.y + dy );
1934         if ( scroll_position.x <= 0 ) {
1935             scroll_position.x = 0;
1936             dx = 0;
1937         }
1938         else {
1939             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1940                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1941             if ( scroll_position.x >= max_x ) {
1942                 dx = 0;
1943                 scroll_position.x = max_x;
1944             }
1945         }
1946         if ( scroll_position.y <= 0 ) {
1947             dy = 0;
1948             scroll_position.y = 0;
1949         }
1950         else {
1951             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1952                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1953             if ( scroll_position.y >= max_y ) {
1954                 dy = 0;
1955                 scroll_position.y = max_y;
1956             }
1957         }
1958         repaint();
1959         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1960         setLastMouseDragPointX( ( float ) ( e.getX() + dx ) );
1961         setLastMouseDragPointY( ( float ) ( e.getY() + dy ) );
1962     }
1963
1964     final void mouseMoved( final MouseEvent e ) {
1965         requestFocusInWindow();
1966         if ( getControlPanel().isNodeDescPopup() ) {
1967             if ( _node_desc_popup != null ) {
1968                 _node_desc_popup.hide();
1969                 _node_desc_popup = null;
1970             }
1971         }
1972         if ( getOptions().isShowOverview() && isOvOn() ) {
1973             if ( inOvVirtualRectangle( e ) ) {
1974                 if ( !isInOvRect() ) {
1975                     setInOvRect( true );
1976                     repaint();
1977                 }
1978             }
1979             else {
1980                 if ( isInOvRect() ) {
1981                     setInOvRect( false );
1982                     repaint();
1983                 }
1984             }
1985         }
1986         if ( inOv( e ) && getOptions().isShowOverview() && isOvOn() ) {
1987             if ( !isInOv() ) {
1988                 setInOv( true );
1989             }
1990         }
1991         else {
1992             if ( isInOv() ) {
1993                 setInOv( false );
1994             }
1995             final PhylogenyNode node = findNode( e.getX(), e.getY() );
1996             if ( ( node != null ) && ( node.isRoot() || !node.getParent().isCollapse() ) ) {
1997                 // cursor is over a tree node
1998                 if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.CUT_SUBTREE )
1999                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.COPY_SUBTREE )
2000                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.PASTE_SUBTREE )
2001                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.DELETE_NODE_OR_SUBTREE )
2002                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.REROOT )
2003                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.ADD_NEW_NODE ) ) {
2004                     setCursor( CUT_CURSOR );
2005                 }
2006                 else {
2007                     setCursor( HAND_CURSOR );
2008                     if ( getControlPanel().isNodeDescPopup() ) {
2009                         showNodeDataPopup( e, node );
2010                     }
2011                 }
2012             }
2013             else {
2014                 setCursor( ARROW_CURSOR );
2015             }
2016         }
2017     }
2018
2019     final void mouseReleasedInBrowserPanel( final MouseEvent e ) {
2020         setCursor( ARROW_CURSOR );
2021     }
2022
2023     final public void mouseWheelMoved( final MouseWheelEvent e ) {
2024         final int notches = e.getWheelRotation();
2025         if ( inOvVirtualRectangle( e ) ) {
2026             if ( !isInOvRect() ) {
2027                 setInOvRect( true );
2028                 repaint();
2029             }
2030         }
2031         else {
2032             if ( isInOvRect() ) {
2033                 setInOvRect( false );
2034                 repaint();
2035             }
2036         }
2037         if ( e.isControlDown() ) {
2038             if ( notches < 0 ) {
2039                 getTreeFontSet().increaseFontSize();
2040                 getControlPanel().displayedPhylogenyMightHaveChanged( true );
2041             }
2042             else {
2043                 getTreeFontSet().decreaseFontSize();
2044                 getControlPanel().displayedPhylogenyMightHaveChanged( true );
2045             }
2046         }
2047         else if ( e.isShiftDown() ) {
2048             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
2049                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
2050                 if ( notches < 0 ) {
2051                     for( int i = 0; i < ( -notches ); ++i ) {
2052                         setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
2053                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2054                     }
2055                 }
2056                 else {
2057                     for( int i = 0; i < notches; ++i ) {
2058                         setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
2059                         if ( getStartingAngle() < 0 ) {
2060                             setStartingAngle( TWO_PI + getStartingAngle() );
2061                         }
2062                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2063                     }
2064                 }
2065             }
2066             else {
2067                 if ( notches < 0 ) {
2068                     for( int i = 0; i < ( -notches ); ++i ) {
2069                         getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2070                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2071                     }
2072                 }
2073                 else {
2074                     for( int i = 0; i < notches; ++i ) {
2075                         getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2076                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2077                     }
2078                 }
2079             }
2080         }
2081         else {
2082             if ( notches < 0 ) {
2083                 for( int i = 0; i < ( -notches ); ++i ) {
2084                     getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
2085                                                Constants.WHEEL_ZOOM_IN_X_CORRECTION_FACTOR );
2086                     getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2087                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2088                 }
2089             }
2090             else {
2091                 for( int i = 0; i < notches; ++i ) {
2092                     getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2093                     getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
2094                                                 Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
2095                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2096                 }
2097             }
2098         }
2099         requestFocus();
2100         requestFocusInWindow();
2101         requestFocus();
2102     }
2103
2104     final void multiplyUrtFactor( final float f ) {
2105         _urt_factor *= f;
2106     }
2107
2108     final JApplet obtainApplet() {
2109         return ( ( MainPanelApplets ) getMainPanel() ).getApplet();
2110     }
2111
2112     final private void openSeqWeb( final PhylogenyNode node ) {
2113         if ( !isCanOpenSeqWeb( node ) ) {
2114             cannotOpenBrowserWarningMessage( "sequence" );
2115             return;
2116         }
2117         String uri_str = null;
2118         final Sequence seq = node.getNodeData().getSequence();
2119         final String source = seq.getAccession().getSource().toLowerCase();
2120         String url;
2121         if ( source.toLowerCase().equals( "ncbi" ) ) {
2122             url = Constants.NCBI_ALL_DATABASE_SEARCH;
2123         }
2124         else {
2125             final WebLink weblink = getConfiguration().getWebLink( source );
2126             url = weblink.getUrl().toString();
2127         }
2128         try {
2129             uri_str = url + URLEncoder.encode( seq.getAccession().getValue(), ForesterConstants.UTF8 );
2130         }
2131         catch ( final UnsupportedEncodingException e ) {
2132             AptxUtil.showErrorMessage( this, e.toString() );
2133             e.printStackTrace();
2134         }
2135         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2136             try {
2137                 JApplet applet = null;
2138                 if ( isApplet() ) {
2139                     applet = obtainApplet();
2140                 }
2141                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_seq" );
2142             }
2143             catch ( final IOException e ) {
2144                 AptxUtil.showErrorMessage( this, e.toString() );
2145                 e.printStackTrace();
2146             }
2147             catch ( final URISyntaxException e ) {
2148                 AptxUtil.showErrorMessage( this, e.toString() );
2149                 e.printStackTrace();
2150             }
2151         }
2152         else {
2153             cannotOpenBrowserWarningMessage( "sequence" );
2154         }
2155     }
2156
2157     final private void openTaxWeb( final PhylogenyNode node ) {
2158         if ( !isCanOpenTaxWeb( node ) ) {
2159             cannotOpenBrowserWarningMessage( "taxonomic" );
2160             return;
2161         }
2162         String uri_str = null;
2163         final Taxonomy tax = node.getNodeData().getTaxonomy();
2164         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
2165                 && getConfiguration().isHasWebLink( tax.getIdentifier().getProvider().toLowerCase() ) ) {
2166             final String type = tax.getIdentifier().getProvider().toLowerCase();
2167             final WebLink weblink = getConfiguration().getWebLink( type );
2168             try {
2169                 uri_str = weblink.getUrl() + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
2170             }
2171             catch ( final UnsupportedEncodingException e ) {
2172                 AptxUtil.showErrorMessage( this, e.toString() );
2173                 e.printStackTrace();
2174             }
2175         }
2176         else if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
2177                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
2178             try {
2179                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
2180             }
2181             catch ( final URISyntaxException e ) {
2182                 AptxUtil.showErrorMessage( this, e.toString() );
2183                 uri_str = null;
2184                 e.printStackTrace();
2185             }
2186         }
2187         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
2188             try {
2189                 uri_str = "http://www.eol.org/search?q="
2190                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
2191             }
2192             catch ( final UnsupportedEncodingException e ) {
2193                 AptxUtil.showErrorMessage( this, e.toString() );
2194                 e.printStackTrace();
2195             }
2196         }
2197         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
2198             try {
2199                 uri_str = "http://www.uniprot.org/taxonomy/?query="
2200                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
2201             }
2202             catch ( final UnsupportedEncodingException e ) {
2203                 AptxUtil.showErrorMessage( this, e.toString() );
2204                 e.printStackTrace();
2205             }
2206         }
2207         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
2208             try {
2209                 uri_str = "http://www.eol.org/search?q="
2210                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
2211             }
2212             catch ( final UnsupportedEncodingException e ) {
2213                 AptxUtil.showErrorMessage( this, e.toString() );
2214                 e.printStackTrace();
2215             }
2216         }
2217         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2218             try {
2219                 JApplet applet = null;
2220                 if ( isApplet() ) {
2221                     applet = obtainApplet();
2222                 }
2223                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_tax" );
2224             }
2225             catch ( final IOException e ) {
2226                 AptxUtil.showErrorMessage( this, e.toString() );
2227                 e.printStackTrace();
2228             }
2229             catch ( final URISyntaxException e ) {
2230                 AptxUtil.showErrorMessage( this, e.toString() );
2231                 e.printStackTrace();
2232             }
2233         }
2234         else {
2235             cannotOpenBrowserWarningMessage( "taxonomic" );
2236         }
2237     }
2238
2239     final void paintBranchCircular( final PhylogenyNode p,
2240                                     final PhylogenyNode c,
2241                                     final Graphics2D g,
2242                                     final boolean radial_labels,
2243                                     final boolean to_pdf,
2244                                     final boolean to_graphics_file ) {
2245         final double angle = _urt_nodeid_angle_map.get( c.getId() );
2246         final double root_x = _root.getXcoord();
2247         final double root_y = _root.getYcoord();
2248         final double dx = root_x - p.getXcoord();
2249         final double dy = root_y - p.getYcoord();
2250         final double parent_radius = Math.sqrt( dx * dx + dy * dy );
2251         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
2252         assignGraphicsForBranchWithColorForParentBranch( c, false, g, to_pdf, to_graphics_file );
2253         if ( ( c.isFirstChildNode() || c.isLastChildNode() )
2254                 && ( ( Math.abs( parent_radius * arc ) > 1.5 ) || to_pdf || to_graphics_file ) ) {
2255             final double r2 = 2.0 * parent_radius;
2256             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
2257         }
2258         drawLine( c.getXcoord(),
2259                   c.getYcoord(),
2260                   root_x + ( Math.cos( angle ) * parent_radius ),
2261                   root_y + ( Math.sin( angle ) * parent_radius ),
2262                   g );
2263         paintNodeBox( c.getXcoord(), c.getYcoord(), c, g, to_pdf, to_graphics_file, isInFoundNodes( c ) );
2264         if ( c.isExternal() ) {
2265             final boolean is_in_found_nodes = isInFoundNodes( c );
2266             if ( ( _dynamic_hiding_factor > 1 ) && !is_in_found_nodes
2267                     && ( _urt_nodeid_index_map.get( c.getId() ) % _dynamic_hiding_factor != 1 ) ) {
2268                 return;
2269             }
2270             paintNodeDataUnrootedCirc( g, c, to_pdf, to_graphics_file, radial_labels, 0, is_in_found_nodes );
2271         }
2272     }
2273
2274     final void paintBranchCircularLite( final PhylogenyNode p, final PhylogenyNode c, final Graphics2D g ) {
2275         final double angle = _urt_nodeid_angle_map.get( c.getId() );
2276         final double root_x = _root.getXSecondary();
2277         final double root_y = _root.getYSecondary();
2278         final double dx = root_x - p.getXSecondary();
2279         final double dy = root_y - p.getYSecondary();
2280         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
2281         final double parent_radius = Math.sqrt( dx * dx + dy * dy );
2282         g.setColor( getTreeColorSet().getOvColor() );
2283         if ( ( c.isFirstChildNode() || c.isLastChildNode() ) && ( Math.abs( arc ) > 0.02 ) ) {
2284             final double r2 = 2.0 * parent_radius;
2285             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
2286         }
2287         drawLine( c.getXSecondary(),
2288                   c.getYSecondary(),
2289                   root_x + ( Math.cos( angle ) * parent_radius ),
2290                   root_y + ( Math.sin( angle ) * parent_radius ),
2291                   g );
2292         if ( isInFoundNodes( c ) ) {
2293             g.setColor( getTreeColorSet().getFoundColor() );
2294             drawRectFilled( c.getXSecondary() - 1, c.getYSecondary() - 1, 3, 3, g );
2295         }
2296     }
2297
2298     final private void paintBranchLength( final Graphics2D g,
2299                                           final PhylogenyNode node,
2300                                           final boolean to_pdf,
2301                                           final boolean to_graphics_file ) {
2302         g.setFont( getTreeFontSet().getSmallFont() );
2303         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2304             g.setColor( Color.BLACK );
2305         }
2306         else {
2307             g.setColor( getTreeColorSet().getBranchLengthColor() );
2308         }
2309         if ( !node.isRoot() ) {
2310             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2311                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2312                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2313             }
2314             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2315                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2316                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2317             }
2318             else {
2319                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2320                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2321             }
2322         }
2323         else {
2324             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
2325                     - getTreeFontSet()._small_max_descent, g );
2326         }
2327     }
2328
2329     final private void paintBranchLite( final Graphics2D g,
2330                                         final float x1,
2331                                         final float x2,
2332                                         final float y1,
2333                                         final float y2,
2334                                         final PhylogenyNode node ) {
2335         g.setColor( getTreeColorSet().getOvColor() );
2336         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2337             drawLine( x1, y1, x2, y2, g );
2338         }
2339         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2340             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2341             ( g ).draw( _quad_curve );
2342         }
2343         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2344             final float dx = x2 - x1;
2345             final float dy = y2 - y1;
2346             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
2347                     + ( dy * 0.8f ), x2, y2 );
2348             ( g ).draw( _cubic_curve );
2349         }
2350         else {
2351             final float x2a = x2;
2352             final float x1a = x1;
2353             // draw the vertical line
2354             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
2355                 drawLine( x1, y1, x1, y2, g );
2356             }
2357             // draw the horizontal line
2358             drawLine( x1a, y2, x2a, y2, g );
2359         }
2360     }
2361
2362     /**
2363      * Paint a branch which consists of a vertical and a horizontal bar
2364      * @param is_ind_found_nodes 
2365      */
2366     final private void paintBranchRectangular( final Graphics2D g,
2367                                                final float x1,
2368                                                final float x2,
2369                                                final float y1,
2370                                                final float y2,
2371                                                final PhylogenyNode node,
2372                                                final boolean to_pdf,
2373                                                final boolean to_graphics_file ) {
2374         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
2375         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2376             drawLine( x1, y1, x2, y2, g );
2377         }
2378         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2379             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2380             g.draw( _quad_curve );
2381         }
2382         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2383             final float dx = x2 - x1;
2384             final float dy = y2 - y1;
2385             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
2386                     + ( dy * 0.8f ), x2, y2 );
2387             g.draw( _cubic_curve );
2388         }
2389         else {
2390             final float x2a = x2;
2391             final float x1a = x1;
2392             float y2_r = 0;
2393             if ( node.isFirstChildNode() || node.isLastChildNode()
2394                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
2395                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2396                 if ( !to_graphics_file
2397                         && !to_pdf
2398                         && ( ( ( y2 < getVisibleRect().getMinY() - 20 ) && ( y1 < getVisibleRect().getMinY() - 20 ) ) || ( ( y2 > getVisibleRect()
2399                                 .getMaxY() + 20 ) && ( y1 > getVisibleRect().getMaxY() + 20 ) ) ) ) {
2400                     // Do nothing.
2401                 }
2402                 else {
2403                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2404                         float x2c = x1 + EURO_D;
2405                         if ( x2c > x2a ) {
2406                             x2c = x2a;
2407                         }
2408                         drawLine( x1, y1, x2c, y2, g );
2409                     }
2410                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2411                         if ( y2 > y1 ) {
2412                             y2_r = y2 - ROUNDED_D;
2413                             if ( y2_r < y1 ) {
2414                                 y2_r = y1;
2415                             }
2416                             drawLine( x1, y1, x1, y2_r, g );
2417                         }
2418                         else {
2419                             y2_r = y2 + ROUNDED_D;
2420                             if ( y2_r > y1 ) {
2421                                 y2_r = y1;
2422                             }
2423                             drawLine( x1, y1, x1, y2_r, g );
2424                         }
2425                     }
2426                     else {
2427                         drawLine( x1, y1, x1, y2, g );
2428                     }
2429                 }
2430             }
2431             // draw the horizontal line
2432             if ( !to_graphics_file && !to_pdf
2433                     && ( ( y2 < getVisibleRect().getMinY() - 20 ) || ( y2 > getVisibleRect().getMaxY() + 20 ) ) ) {
2434                 return;
2435             }
2436             float x1_r = 0;
2437             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
2438                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2439                     x1_r = x1a + ROUNDED_D;
2440                     if ( x1_r < x2a ) {
2441                         drawLine( x1_r, y2, x2a, y2, g );
2442                     }
2443                 }
2444                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2445                     final float x1c = x1a + EURO_D;
2446                     if ( x1c < x2a ) {
2447                         drawLine( x1c, y2, x2a, y2, g );
2448                     }
2449                 }
2450                 else {
2451                     drawLine( x1a, y2, x2a, y2, g );
2452                 }
2453             }
2454             else {
2455                 final double w = PhylogenyMethods.getBranchWidthValue( node );
2456                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2457                     x1_r = x1a + ROUNDED_D;
2458                     if ( x1_r < x2a ) {
2459                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
2460                     }
2461                 }
2462                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2463                     final float x1c = x1a + EURO_D;
2464                     if ( x1c < x2a ) {
2465                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
2466                     }
2467                 }
2468                 else {
2469                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
2470                 }
2471             }
2472             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2473                 if ( x1_r > x2a ) {
2474                     x1_r = x2a;
2475                 }
2476                 if ( y2 > y2_r ) {
2477                     final double diff = y2 - y2_r;
2478                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
2479                 }
2480                 else {
2481                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
2482                 }
2483                 g.draw( _arc );
2484             }
2485         }
2486         if ( node.isExternal() ) {
2487             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
2488         }
2489     }
2490
2491     final void paintCircular( final Phylogeny phy,
2492                               final double starting_angle,
2493                               final int center_x,
2494                               final int center_y,
2495                               final int radius,
2496                               final Graphics2D g,
2497                               final boolean to_pdf,
2498                               final boolean to_graphics_file ) {
2499         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes() - _collapsed_external_nodeid_set.size();
2500         System.out.println( "# collapsed external = " + _collapsed_external_nodeid_set.size() );
2501         _root = phy.getRoot();
2502         _root.setXcoord( center_x );
2503         _root.setYcoord( center_y );
2504         final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
2505         double current_angle = starting_angle;
2506         int i = 0;
2507         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2508             final PhylogenyNode n = it.next();
2509             if ( !n.isCollapse() ) {
2510                 n.setXcoord( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
2511                 n.setYcoord( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
2512                 _urt_nodeid_angle_map.put( n.getId(), current_angle );
2513                 _urt_nodeid_index_map.put( n.getId(), i++ );
2514                 current_angle += ( TWO_PI / circ_num_ext_nodes );
2515             }
2516             else {
2517                 //TODO remove me
2518                 System.out.println( "is collapse" + n.getName() );
2519             }
2520         }
2521         paintCirculars( phy.getRoot(), phy, center_x, center_y, radius, radial_labels, g, to_pdf, to_graphics_file );
2522         paintNodeBox( _root.getXcoord(), _root.getYcoord(), _root, g, to_pdf, to_graphics_file, isInFoundNodes( _root ) );
2523     }
2524
2525     void updateSetOfCollapsedExternalNodes( final Phylogeny phy ) {
2526         _collapsed_external_nodeid_set.clear();
2527         E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2528             final PhylogenyNode ext_node = it.next();
2529             PhylogenyNode n = ext_node;
2530             while ( !n.isRoot() ) {
2531                 if ( n.isCollapse() ) {
2532                     _collapsed_external_nodeid_set.add( ext_node.getId() );
2533                     continue E;
2534                 }
2535                 n = n.getParent();
2536             }
2537         }
2538     }
2539
2540     final void paintCircularLite( final Phylogeny phy,
2541                                   final double starting_angle,
2542                                   final int center_x,
2543                                   final int center_y,
2544                                   final int radius,
2545                                   final Graphics2D g ) {
2546         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes();
2547         _root = phy.getRoot();
2548         _root.setXSecondary( center_x );
2549         _root.setYSecondary( center_y );
2550         double current_angle = starting_angle;
2551         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2552             final PhylogenyNode n = it.next();
2553             n.setXSecondary( ( float ) ( center_x + radius * Math.cos( current_angle ) ) );
2554             n.setYSecondary( ( float ) ( center_y + radius * Math.sin( current_angle ) ) );
2555             _urt_nodeid_angle_map.put( n.getId(), current_angle );
2556             current_angle += ( TWO_PI / circ_num_ext_nodes );
2557         }
2558         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
2559     }
2560
2561     final private double paintCirculars( final PhylogenyNode n,
2562                                          final Phylogeny phy,
2563                                          final float center_x,
2564                                          final float center_y,
2565                                          final double radius,
2566                                          final boolean radial_labels,
2567                                          final Graphics2D g,
2568                                          final boolean to_pdf,
2569                                          final boolean to_graphics_file ) {
2570         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
2571             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
2572                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
2573             }
2574             return _urt_nodeid_angle_map.get( n.getId() );
2575         }
2576         else {
2577             final List<PhylogenyNode> descs = n.getDescendants();
2578             double sum = 0;
2579             for( final PhylogenyNode desc : descs ) {
2580                 sum += paintCirculars( desc,
2581                                        phy,
2582                                        center_x,
2583                                        center_y,
2584                                        radius,
2585                                        radial_labels,
2586                                        g,
2587                                        to_pdf,
2588                                        to_graphics_file );
2589             }
2590             double r = 0;
2591             if ( !n.isRoot() ) {
2592                 r = 1 - ( ( ( double ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2593             }
2594             final double theta = sum / descs.size();
2595             n.setXcoord( ( float ) ( center_x + r * radius * Math.cos( theta ) ) );
2596             n.setYcoord( ( float ) ( center_y + r * radius * Math.sin( theta ) ) );
2597             _urt_nodeid_angle_map.put( n.getId(), theta );
2598             for( final PhylogenyNode desc : descs ) {
2599                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
2600             }
2601             return theta;
2602         }
2603     }
2604
2605     final private void paintCircularsLite( final PhylogenyNode n,
2606                                            final Phylogeny phy,
2607                                            final int center_x,
2608                                            final int center_y,
2609                                            final int radius,
2610                                            final Graphics2D g ) {
2611         if ( n.isExternal() ) {
2612             return;
2613         }
2614         else {
2615             final List<PhylogenyNode> descs = n.getDescendants();
2616             for( final PhylogenyNode desc : descs ) {
2617                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
2618             }
2619             float r = 0;
2620             if ( !n.isRoot() ) {
2621                 r = 1 - ( ( ( float ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2622             }
2623             final double theta = _urt_nodeid_angle_map.get( n.getId() );
2624             n.setXSecondary( ( float ) ( center_x + radius * r * Math.cos( theta ) ) );
2625             n.setYSecondary( ( float ) ( center_y + radius * r * Math.sin( theta ) ) );
2626             for( final PhylogenyNode desc : descs ) {
2627                 paintBranchCircularLite( n, desc, g );
2628             }
2629         }
2630     }
2631
2632     final private void paintCollapsedNode( final Graphics2D g,
2633                                            final PhylogenyNode node,
2634                                            final boolean to_graphics_file,
2635                                            final boolean to_pdf,
2636                                            final boolean is_in_found_nodes ) {
2637         Color c = null;
2638         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2639             c = Color.BLACK;
2640         }
2641         else if ( is_in_found_nodes ) {
2642             c = getTreeColorSet().getFoundColor();
2643         }
2644         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2645             c = getTaxonomyBasedColor( node );
2646         }
2647         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
2648                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2649             c = PhylogenyMethods.getBranchColorValue( node );
2650         }
2651         else {
2652             c = getTreeColorSet().getCollapseFillColor();
2653         }
2654         double d = node.getAllExternalDescendants().size();
2655         if ( d > 1000 ) {
2656             d = ( 3 * _y_distance ) / 3;
2657         }
2658         else {
2659             d = ( Math.log10( d ) * _y_distance ) / 2.5;
2660         }
2661         final int box_size = getOptions().getDefaultNodeShapeSize();
2662         if ( d < box_size ) {
2663             d = box_size;
2664         }
2665         _polygon.reset();
2666         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() - box_size ),
2667                            ForesterUtil.roundToInt( node.getYcoord() ) );
2668         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
2669                            ForesterUtil.roundToInt( node.getYcoord() - d ) );
2670         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
2671                            ForesterUtil.roundToInt( node.getYcoord() + d ) );
2672         if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2673             g.setColor( c );
2674             g.fillPolygon( _polygon );
2675         }
2676         else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
2677             g.setColor( getBackground() );
2678             g.fillPolygon( _polygon );
2679             g.setColor( c );
2680             g.drawPolygon( _polygon );
2681         }
2682         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2683             g.setPaint( new GradientPaint( node.getXcoord() - box_size, node.getYcoord(), getBackground(), ( node
2684                     .getXcoord() + box_size ), ( float ) ( node.getYcoord() - d ), c, false ) );
2685             g.fill( _polygon );
2686             g.setPaint( c );
2687             g.draw( _polygon );
2688         }
2689         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
2690     }
2691
2692     @Override
2693     final public void paintComponent( final Graphics g ) {
2694         // Dimension currentSize = getSize();
2695         //  if ( offscreenImage == null || !currentSize.equals( offscreenDimension ) ) {
2696         // call the 'java.awt.Component.createImage(...)' method to get an
2697         // image
2698         //   offscreenImage = createImage( currentSize.width, currentSize.height );
2699         //  offscreenGraphics = offscreenImage.getGraphics();
2700         //  offscreenDimension = currentSize;
2701         // }
2702         // super.paintComponent( g ); //why?
2703         //final Graphics2D g2d = ( Graphics2D ) offscreenGraphics;
2704         final Graphics2D g2d = ( Graphics2D ) g;
2705         g2d.setRenderingHints( _rendering_hints );
2706         paintPhylogeny( g2d, false, false, 0, 0, 0, 0 );
2707         //g.drawImage( offscreenImage, 0, 0, this );
2708     }
2709
2710     @Override
2711     public void update( final Graphics g ) {
2712         paint( g );
2713     }
2714
2715     final private void paintConfidenceValues( final Graphics2D g,
2716                                               final PhylogenyNode node,
2717                                               final boolean to_pdf,
2718                                               final boolean to_graphics_file ) {
2719         final List<Confidence> confidences = node.getBranchData().getConfidences();
2720         //        if ( confidences.size() == 1 ) {
2721         //            final double value = node.getBranchData().getConfidence( 0 ).getValue();
2722         //            if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
2723         //                return;
2724         //            }
2725         //            conf_str = FORMATTER_CONFIDENCE.format( value );
2726         //        }
2727         //        else if ( confidences.size() > 1 ) {
2728         boolean one_ok = false;
2729         boolean not_first = false;
2730         Collections.sort( confidences );
2731         final StringBuilder sb = new StringBuilder();
2732         String conf_str = "";
2733         for( final Confidence confidence : confidences ) {
2734             final double value = confidence.getValue();
2735             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2736                 if ( value >= getOptions().getMinConfidenceValue() ) {
2737                     one_ok = true;
2738                 }
2739                 if ( not_first ) {
2740                     sb.append( "/" );
2741                 }
2742                 else {
2743                     not_first = true;
2744                 }
2745                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
2746                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2747                 if ( getOptions().isShowConfidenceStddev() ) {
2748                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2749                         sb.append( "(" );
2750                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
2751                                                                                     getOptions()
2752                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2753                         sb.append( ")" );
2754                     }
2755                 }
2756             }
2757             //}
2758             if ( one_ok ) {
2759                 conf_str = sb.toString();
2760             }
2761         }
2762         if ( conf_str.length() > 0 ) {
2763             final double parent_x = node.getParent().getXcoord();
2764             double x = node.getXcoord();
2765             g.setFont( getTreeFontSet().getSmallFont() );
2766             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2767                 x += EURO_D;
2768             }
2769             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2770                 x += ROUNDED_D;
2771             }
2772             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2773                 g.setColor( Color.BLACK );
2774             }
2775             else {
2776                 g.setColor( getTreeColorSet().getConfidenceColor() );
2777             }
2778             TreePanel
2779                     .drawString( conf_str,
2780                                  parent_x
2781                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
2782                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
2783                                  g );
2784         }
2785     }
2786
2787     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
2788         final int box_size = getOptions().getDefaultNodeShapeSize();
2789         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2790         g.setColor( getTreeColorSet().getFoundColor() );
2791         g.fillRect( x - half_box_size, y - half_box_size, box_size, box_size );
2792     }
2793
2794     final private void paintGainedAndLostCharacters( final Graphics2D g,
2795                                                      final PhylogenyNode node,
2796                                                      final String gained,
2797                                                      final String lost ) {
2798         if ( node.getParent() != null ) {
2799             final double parent_x = node.getParent().getXcoord();
2800             final double x = node.getXcoord();
2801             g.setFont( getTreeFontSet().getLargeFont() );
2802             g.setColor( getTreeColorSet().getGainedCharactersColor() );
2803             if ( Constants.SPECIAL_CUSTOM ) {
2804                 g.setColor( Color.BLUE );
2805             }
2806             TreePanel
2807                     .drawString( gained,
2808                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
2809                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
2810                                  g );
2811             g.setColor( getTreeColorSet().getLostCharactersColor() );
2812             TreePanel.drawString( lost,
2813                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
2814                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
2815                                   g );
2816         }
2817     }
2818
2819     /**
2820      * Draw a box at the indicated node.
2821      * 
2822      * @param x
2823      * @param y
2824      * @param node
2825      * @param g
2826      */
2827     final private void paintNodeBox( final double x,
2828                                      final double y,
2829                                      final PhylogenyNode node,
2830                                      final Graphics2D g,
2831                                      final boolean to_pdf,
2832                                      final boolean to_graphics_file,
2833                                      final boolean is_in_found_nodes ) {
2834         if ( node.isCollapse() ) {
2835             return;
2836         }
2837         // if this node should be highlighted, do so
2838         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
2839             g.setColor( getTreeColorSet().getFoundColor() );
2840             drawOval( x - 8, y - 8, 16, 16, g );
2841             drawOval( x - 9, y - 8, 17, 17, g );
2842             drawOval( x - 9, y - 9, 18, 18, g );
2843         }
2844         if ( is_in_found_nodes ) {
2845             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
2846         }
2847         else {
2848             Color outline_color = null;
2849             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2850                 outline_color = Color.BLACK;
2851             }
2852             else if ( getControlPanel().isEvents() && AptxUtil.isHasAssignedEvent( node ) ) {
2853                 final Event event = node.getNodeData().getEvent();
2854                 if ( event.isDuplication() ) {
2855                     outline_color = getTreeColorSet().getDuplicationBoxColor();
2856                 }
2857                 else if ( event.isSpeciation() ) {
2858                     outline_color = getTreeColorSet().getSpecBoxColor();
2859                 }
2860                 else if ( event.isSpeciationOrDuplication() ) {
2861                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
2862                 }
2863             }
2864             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
2865                 outline_color = getTaxonomyBasedColor( node );
2866             }
2867             else {
2868                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
2869                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
2870                     outline_color = getTreeColorSet().getBranchColorForPdf();
2871                 }
2872             }
2873             final int box_size = getOptions().getDefaultNodeShapeSize();
2874             final int half_box_size = box_size / 2;
2875             if ( getOptions().isShowDefaultNodeShapes() || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
2876                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
2877                     if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2878                         drawOvalGradient( x - half_box_size,
2879                                           y - half_box_size,
2880                                           box_size,
2881                                           box_size,
2882                                           g,
2883                                           to_pdf ? Color.WHITE : outline_color,
2884                                           to_pdf ? outline_color : getBackground(),
2885                                           outline_color );
2886                     }
2887                     else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
2888                         Color background = getBackground();
2889                         if ( to_pdf ) {
2890                             background = Color.WHITE;
2891                         }
2892                         drawOvalGradient( x - half_box_size,
2893                                           y - half_box_size,
2894                                           box_size,
2895                                           box_size,
2896                                           g,
2897                                           background,
2898                                           background,
2899                                           outline_color );
2900                     }
2901                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2902                         g.setColor( outline_color );
2903                         drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2904                     }
2905                 }
2906                 else if ( getOptions().getDefaultNodeShape() == NodeVisualization.NodeShape.RECTANGLE ) {
2907                     if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.GRADIENT ) {
2908                         drawRectGradient( x - half_box_size,
2909                                           y - half_box_size,
2910                                           box_size,
2911                                           box_size,
2912                                           g,
2913                                           to_pdf ? Color.WHITE : outline_color,
2914                                           to_pdf ? outline_color : getBackground(),
2915                                           outline_color );
2916                     }
2917                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
2918                         Color background = getBackground();
2919                         if ( to_pdf ) {
2920                             background = Color.WHITE;
2921                         }
2922                         drawRectGradient( x - half_box_size,
2923                                           y - half_box_size,
2924                                           box_size,
2925                                           box_size,
2926                                           g,
2927                                           background,
2928                                           background,
2929                                           outline_color );
2930                     }
2931                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2932                         g.setColor( outline_color );
2933                         drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2934                     }
2935                 }
2936             }
2937         }
2938     }
2939
2940     final private void paintNodeData( final Graphics2D g,
2941                                       final PhylogenyNode node,
2942                                       final boolean to_graphics_file,
2943                                       final boolean to_pdf,
2944                                       final boolean is_in_found_nodes ) {
2945         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
2946             return;
2947         }
2948         if ( getOptions().isShowBranchLengthValues()
2949                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
2950                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
2951                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
2952             paintBranchLength( g, node, to_pdf, to_graphics_file );
2953         }
2954         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
2955             return;
2956         }
2957         int x = 0;
2958         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2959         if ( getControlPanel().isShowTaxonomyImages()
2960                 && ( getImageMap() != null )
2961                 && !getImageMap().isEmpty()
2962                 && node.getNodeData().isHasTaxonomy()
2963                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
2964                         .getUris().isEmpty() ) ) {
2965             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
2966         }
2967         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
2968                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
2969             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
2970         }
2971         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2972             g.setColor( Color.BLACK );
2973         }
2974         else if ( is_in_found_nodes ) {
2975             g.setColor( getTreeColorSet().getFoundColor() );
2976         }
2977         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2978             g.setColor( getTaxonomyBasedColor( node ) );
2979         }
2980         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
2981                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2982             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
2983         }
2984         else if ( to_pdf ) {
2985             g.setColor( Color.BLACK );
2986         }
2987         else {
2988             g.setColor( getTreeColorSet().getSequenceColor() );
2989         }
2990         _sb.setLength( 0 );
2991         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
2992             _sb.append( " [" );
2993             _sb.append( node.getAllExternalDescendants().size() );
2994             _sb.append( "]" );
2995         }
2996         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
2997             if ( _sb.length() > 0 ) {
2998                 _sb.append( " " );
2999             }
3000             _sb.append( node.getName() );
3001         }
3002         if ( node.getNodeData().isHasSequence() ) {
3003             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3004                 if ( _sb.length() > 0 ) {
3005                     _sb.append( " " );
3006                 }
3007                 _sb.append( node.getNodeData().getSequence().getSymbol() );
3008             }
3009             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3010                 if ( _sb.length() > 0 ) {
3011                     _sb.append( " " );
3012                 }
3013                 _sb.append( node.getNodeData().getSequence().getName() );
3014             }
3015             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3016                 if ( _sb.length() > 0 ) {
3017                     _sb.append( " " );
3018                 }
3019                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3020                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3021                     _sb.append( ":" );
3022                 }
3023                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3024             }
3025         }
3026         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
3027             if ( _sb.length() > 0 ) {
3028                 _sb.append( " " );
3029             }
3030             _sb.append( propertiesToString( node ) );
3031         }
3032         g.setFont( getTreeFontSet().getLargeFont() );
3033         if ( is_in_found_nodes ) {
3034             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3035         }
3036         double down_shift_factor = 3.0;
3037         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
3038             down_shift_factor = 1;
3039         }
3040         final double pos_x = node.getXcoord() + x + 2 + half_box_size;
3041         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
3042         final String sb_str = _sb.toString();
3043         // GUILHEM_BEG ______________
3044         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
3045                 && ( _query_sequence != null ) ) {
3046             int nodeTextBoundsWidth = 0;
3047             if ( sb_str.length() > 0 ) {
3048                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
3049                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
3050             }
3051             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
3052                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
3053                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
3054                     g.setColor( getTreeColorSet().getBackgroundColor() );
3055                 }
3056             }
3057             else {
3058                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
3059                 for( final SequenceRelation seqRelation : seqRelations ) {
3060                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
3061                             .getRef1().isEqual( _query_sequence ) )
3062                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
3063                                     .getSelectedItem() );
3064                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
3065                         final double linePosX = node.getXcoord() + 2 + half_box_size;
3066                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
3067                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
3068                                 + ")";
3069                         if ( sConfidence != null ) {
3070                             double confidenceX = pos_x;
3071                             if ( sb_str.length() > 0 ) {
3072                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
3073                                         + CONFIDENCE_LEFT_MARGIN;
3074                             }
3075                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
3076                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
3077                                         .getBounds().getWidth();
3078                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
3079                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
3080                             }
3081                         }
3082                         if ( x + nodeTextBoundsWidth > 0 ) /* we only underline if there is something displayed */
3083                         {
3084                             if ( nodeTextBoundsWidth == 0 ) {
3085                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
3086                             }
3087                             else {
3088                                 nodeTextBoundsWidth += 2;
3089                             }
3090                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
3091                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
3092                             break;
3093                         }
3094                     }
3095                 }
3096             }
3097         }
3098         if ( sb_str.length() > 0 ) {
3099             TreePanel.drawString( sb_str, pos_x, pos_y, g );
3100         }
3101         // GUILHEM_END _____________
3102         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
3103         // TODO FIXME need to check this one!
3104         //if ( _sb.length() > 0 ) {
3105         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
3106         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3107         //}
3108         // COMMENTED_OUT_BY_GUILHEM_END ________________
3109         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
3110                 && ( node.getNodeData().getSequence().getAnnotations() != null )
3111                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
3112             if ( _sb.length() > 0 ) {
3113                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3114             }
3115             final Annotation ann = node.getNodeData().getSequence().getAnnotation( 0 );
3116             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3117                 g.setColor( Color.BLACK );
3118             }
3119             else {
3120                 g.setColor( calculateColorForAnnotation( ann ) );
3121             }
3122             final String ann_str = ann.asSimpleText().toString();
3123             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
3124                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3125             _sb.setLength( 0 );
3126             _sb.append( ann_str );
3127         }
3128         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3129                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3130                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3131             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
3132                     && node.getNodeData().isHasBinaryCharacters() ) {
3133                 if ( _sb.length() > 0 ) {
3134                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3135                 }
3136                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3137                     g.setColor( Color.BLACK );
3138                 }
3139                 else {
3140                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
3141                 }
3142                 if ( getControlPanel().isShowBinaryCharacters() ) {
3143                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
3144                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
3145                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3146                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
3147                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
3148                             .getLostCharactersAsStringBuffer().toString() );
3149                 }
3150                 else {
3151                     if ( DRAW_MEAN_COUNTS && node.isInternal() ) {
3152                         final List<PhylogenyNode> ec = node.getAllExternalDescendants();
3153                         double sum = 0;
3154                         int count = 0;
3155                         for( final PhylogenyNode phylogenyNode : ec ) {
3156                             count++;
3157                             if ( phylogenyNode.getNodeData().getBinaryCharacters() != null ) {
3158                                 sum += phylogenyNode.getNodeData().getBinaryCharacters().getPresentCount();
3159                             }
3160                         }
3161                         final double mean = ForesterUtil.round( sum / count, 1 );
3162                         TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount() + " ["
3163                                 + mean + "]", node.getXcoord() + x + 4 + half_box_size, node.getYcoord()
3164                                 + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3165                     }
3166                     else {
3167                         TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
3168                                               node.getXcoord() + x + 4 + half_box_size,
3169                                               node.getYcoord()
3170                                                       + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
3171                                               g );
3172                     }
3173                     paintGainedAndLostCharacters( g, node, "+"
3174                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
3175                             + node.getNodeData().getBinaryCharacters().getLostCount() );
3176                 }
3177             }
3178         }
3179     }
3180
3181     private StringBuffer propertiesToString( final PhylogenyNode node ) {
3182         final PropertiesMap properties = node.getNodeData().getProperties();
3183         final StringBuffer sb = new StringBuffer();
3184         boolean first = true;
3185         for( final String ref : properties.getPropertyRefs() ) {
3186             if ( first ) {
3187                 first = false;
3188             }
3189             else {
3190                 sb.append( " " );
3191             }
3192             final Property p = properties.getProperty( ref );
3193             sb.append( getPartAfterColon( p.getRef() ) );
3194             sb.append( "=" );
3195             sb.append( p.getValue() );
3196             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
3197                 sb.append( getPartAfterColon( p.getUnit() ) );
3198             }
3199         }
3200         return sb;
3201     }
3202
3203     final private static String getPartAfterColon( final String s ) {
3204         final int i = s.indexOf( ':' );
3205         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
3206             return s;
3207         }
3208         return s.substring( i + 1, s.length() );
3209     }
3210
3211     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
3212         final List<Uri> us = new ArrayList<Uri>();
3213         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
3214             for( final Uri uri : t.getUris() ) {
3215                 us.add( uri );
3216             }
3217         }
3218         double offset = 0;
3219         for( final Uri uri : us ) {
3220             if ( uri != null ) {
3221                 final String uri_str = uri.getValue().toString().toLowerCase();
3222                 if ( getImageMap().containsKey( uri_str ) ) {
3223                     final BufferedImage bi = getImageMap().get( uri_str );
3224                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
3225                         double scaling_factor = 1;
3226                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
3227                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
3228                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
3229                         }
3230                         // y = y - ( 0.9 * getYdistance() );
3231                         final double hs = bi.getHeight() * scaling_factor;
3232                         double ws = bi.getWidth() * scaling_factor + offset;
3233                         final double my_y = y - ( 0.5 * hs );
3234                         final int x_w = ( int ) ( x + ws + 0.5 );
3235                         final int y_h = ( int ) ( my_y + hs + 0.5 );
3236                         if ( ( x_w - x > 7 ) && ( y_h - my_y > 7 ) ) {
3237                             g.drawImage( bi,
3238                                          ( int ) ( x + 0.5 + offset ),
3239                                          ( int ) ( my_y + 0.5 ),
3240                                          x_w,
3241                                          y_h,
3242                                          0,
3243                                          0,
3244                                          bi.getWidth(),
3245                                          bi.getHeight(),
3246                                          null );
3247                             ws += 8;
3248                         }
3249                         else {
3250                             ws = 0.0;
3251                         }
3252                         offset = ws;
3253                     }
3254                 }
3255             }
3256         }
3257         return offset;
3258     }
3259
3260     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
3261                                                   final PhylogenyNode node,
3262                                                   final boolean to_pdf,
3263                                                   final boolean to_graphics_file,
3264                                                   final boolean radial_labels,
3265                                                   final double ur_angle,
3266                                                   final boolean is_in_found_nodes ) {
3267         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
3268             return;
3269         }
3270         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3271             g.setColor( Color.BLACK );
3272         }
3273         else if ( is_in_found_nodes ) {
3274             g.setColor( getTreeColorSet().getFoundColor() );
3275         }
3276         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3277             g.setColor( getTaxonomyBasedColor( node ) );
3278         }
3279         else {
3280             g.setColor( getTreeColorSet().getSequenceColor() );
3281         }
3282         _sb.setLength( 0 );
3283         _sb.append( " " );
3284         if ( node.getNodeData().isHasTaxonomy()
3285                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3286                         .isShowTaxonomyCommonNames() ) ) {
3287             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3288             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3289                 _sb.append( taxonomy.getTaxonomyCode() );
3290                 _sb.append( " " );
3291             }
3292             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3293                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3294                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3295                     _sb.append( taxonomy.getScientificName() );
3296                     _sb.append( " (" );
3297                     _sb.append( taxonomy.getCommonName() );
3298                     _sb.append( ") " );
3299                 }
3300                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3301                     _sb.append( taxonomy.getScientificName() );
3302                     _sb.append( " " );
3303                 }
3304                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3305                     _sb.append( taxonomy.getCommonName() );
3306                     _sb.append( " " );
3307                 }
3308             }
3309             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3310                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3311                     _sb.append( taxonomy.getScientificName() );
3312                     _sb.append( " " );
3313                 }
3314             }
3315             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3316                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3317                     _sb.append( taxonomy.getCommonName() );
3318                     _sb.append( " " );
3319                 }
3320             }
3321         }
3322         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3323             _sb.append( " [" );
3324             _sb.append( node.getAllExternalDescendants().size() );
3325             _sb.append( "]" );
3326         }
3327         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3328             if ( _sb.length() > 0 ) {
3329                 _sb.append( " " );
3330             }
3331             _sb.append( node.getName() );
3332         }
3333         if ( node.getNodeData().isHasSequence() ) {
3334             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3335                 if ( _sb.length() > 0 ) {
3336                     _sb.append( " " );
3337                 }
3338                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3339                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3340                     _sb.append( ":" );
3341                 }
3342                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3343             }
3344             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3345                 if ( _sb.length() > 0 ) {
3346                     _sb.append( " " );
3347                 }
3348                 _sb.append( node.getNodeData().getSequence().getName() );
3349             }
3350         }
3351         g.setFont( getTreeFontSet().getLargeFont() );
3352         if ( is_in_found_nodes ) {
3353             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3354         }
3355         if ( _sb.length() > 1 ) {
3356             final String sb_str = _sb.toString();
3357             double m = 0;
3358             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3359                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
3360             }
3361             else {
3362                 m = ( float ) ( ur_angle % TWO_PI );
3363             }
3364             _at = g.getTransform();
3365             boolean need_to_reset = false;
3366             final float x_coord = node.getXcoord();
3367             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
3368             if ( radial_labels ) {
3369                 need_to_reset = true;
3370                 boolean left = false;
3371                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3372                     m -= PI;
3373                     left = true;
3374                 }
3375                 g.rotate( m, x_coord, node.getYcoord() );
3376                 if ( left ) {
3377                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
3378                 }
3379             }
3380             else {
3381                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3382                     need_to_reset = true;
3383                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
3384                 }
3385             }
3386             TreePanel.drawString( sb_str, x_coord, y_coord, g );
3387             if ( need_to_reset ) {
3388                 g.setTransform( _at );
3389             }
3390         }
3391     }
3392
3393     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
3394         if ( node.isCollapse() ) {
3395             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3396                 paintCollapsedNode( g, node, false, false, false );
3397             }
3398             return;
3399         }
3400         if ( isInFoundNodes( node ) ) {
3401             g.setColor( getTreeColorSet().getFoundColor() );
3402             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
3403         }
3404         float new_x = 0;
3405         if ( !node.isExternal() && !node.isCollapse() ) {
3406             boolean first_child = true;
3407             float y2 = 0.0f;
3408             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3409             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3410                 final PhylogenyNode child_node = node.getChildNode( i );
3411                 int factor_x;
3412                 if ( !isUniformBranchLengthsForCladogram() ) {
3413                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3414                 }
3415                 else {
3416                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3417                 }
3418                 if ( first_child ) {
3419                     first_child = false;
3420                     y2 = node.getYSecondary()
3421                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
3422                                     .getNumberOfExternalNodes() ) );
3423                 }
3424                 else {
3425                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3426                 }
3427                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
3428                 new_x = x2 + node.getXSecondary();
3429                 final float diff_y = node.getYSecondary() - y2;
3430                 final float diff_x = node.getXSecondary() - new_x;
3431                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
3432                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
3433                 }
3434                 child_node.setXSecondary( new_x );
3435                 child_node.setYSecondary( y2 );
3436                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3437             }
3438         }
3439     }
3440
3441     final private void paintNodeRectangular( final Graphics2D g,
3442                                              final PhylogenyNode node,
3443                                              final boolean to_pdf,
3444                                              final boolean dynamically_hide,
3445                                              final int dynamic_hiding_factor,
3446                                              final boolean to_graphics_file ) {
3447         final boolean is_in_found_nodes = isInFoundNodes( node );
3448         if ( node.isCollapse() ) {
3449             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3450                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3451             }
3452             return;
3453         }
3454         if ( node.isExternal() ) {
3455             ++_external_node_index;
3456         }
3457         // Confidence values
3458         if ( getControlPanel().isShowConfidenceValues()
3459                 && !node.isExternal()
3460                 && !node.isRoot()
3461                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
3462                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3463                 && node.getBranchData().isHasConfidences() ) {
3464             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
3465         }
3466         // Draw a line to root:
3467         if ( node.isRoot() && _phylogeny.isRooted() ) {
3468             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
3469         }
3470         float new_x = 0;
3471         float new_x_min = Float.MAX_VALUE;
3472         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
3473         float min_dist = 1.5f;
3474         if ( !disallow_shortcutting ) {
3475             //   System.out.println( dynamic_hiding_factor );
3476             if ( dynamic_hiding_factor > 4000 ) {
3477                 min_dist = 4;
3478             }
3479             else if ( dynamic_hiding_factor > 1000 ) {
3480                 min_dist = 3;
3481             }
3482             else if ( dynamic_hiding_factor > 100 ) {
3483                 min_dist = 2;
3484             }
3485         }
3486         if ( !node.isExternal() && !node.isCollapse() ) {
3487             boolean first_child = true;
3488             float y2 = 0.0f;
3489             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3490             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3491                 final PhylogenyNode child_node = node.getChildNode( i );
3492                 int factor_x;
3493                 if ( !isUniformBranchLengthsForCladogram() ) {
3494                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3495                 }
3496                 else {
3497                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3498                 }
3499                 if ( first_child ) {
3500                     first_child = false;
3501                     y2 = node.getYcoord()
3502                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3503                 }
3504                 else {
3505                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
3506                 }
3507                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
3508                 new_x = x2 + node.getXcoord();
3509                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
3510                     new_x_min = x2;
3511                 }
3512                 final float diff_y = node.getYcoord() - y2;
3513                 final float diff_x = node.getXcoord() - new_x;
3514                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
3515                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
3516                     paintBranchRectangular( g,
3517                                             node.getXcoord(),
3518                                             new_x,
3519                                             node.getYcoord(),
3520                                             y2,
3521                                             child_node,
3522                                             to_pdf,
3523                                             to_graphics_file );
3524                 }
3525                 child_node.setXcoord( new_x );
3526                 child_node.setYcoord( y2 );
3527                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
3528             }
3529             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
3530         }
3531         if ( dynamically_hide
3532                 && !is_in_found_nodes
3533                 && ( ( node.isExternal() && ( _external_node_index % dynamic_hiding_factor != 1 ) ) || ( !node
3534                         .isExternal() && ( ( new_x_min < 20 ) || ( _y_distance * node.getNumberOfExternalNodes() < getTreeFontSet()._fm_large
3535                         .getHeight() ) ) ) ) ) {
3536             return;
3537         }
3538         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3539         paintNodeWithRenderableData( g, node, to_graphics_file, to_pdf );
3540     }
3541
3542     final private void paintNodeWithRenderableData( final Graphics2D g,
3543                                                     final PhylogenyNode node,
3544                                                     final boolean to_graphics_file,
3545                                                     final boolean to_pdf ) {
3546         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
3547             return;
3548         }
3549         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
3550             return;
3551         }
3552         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3553                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
3554             RenderableDomainArchitecture rds = null;
3555             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
3556                 try {
3557                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
3558                 }
3559                 catch ( final ClassCastException cce ) {
3560                     cce.printStackTrace();
3561                 }
3562                 if ( rds != null ) {
3563                     rds.setRenderingHeight( 6 );
3564                     int x = 0;
3565                     if ( getControlPanel().isShowTaxonomyCode()
3566                             && ( !ForesterUtil.isEmpty( PhylogenyMethods.getSpecies( node ) ) ) ) {
3567                         x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( node ) + " " );
3568                     }
3569                     if ( getControlPanel().isShowGeneNames()
3570                             && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) ) {
3571                         x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName() + " " );
3572                     }
3573                     if ( getControlPanel().isShowGeneSymbols()
3574                             && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) ) {
3575                         x += getTreeFontSet()._fm_large
3576                                 .stringWidth( node.getNodeData().getSequence().getSymbol() + " " );
3577                     }
3578                     if ( getControlPanel().isShowSequenceAcc()
3579                             && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3580                         x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getAccession()
3581                                 .toString()
3582                                 + " " );
3583                     }
3584                     if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3585                         x += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
3586                     }
3587                     rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
3588                 }
3589             }
3590         }
3591         //////////////
3592         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
3593                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
3594             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
3595                                                                          getStatisticsForExpressionValues(),
3596                                                                          getConfiguration() );
3597             if ( rv != null ) {
3598                 int x = 0;
3599                 PhylogenyNode my_node = node;
3600                 if ( !getControlPanel().isDrawPhylogram() ) {
3601                     my_node = getPhylogeny().getFirstExternalNode();
3602                 }
3603                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
3604                     x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
3605                 }
3606                 if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
3607                     x += getTreeFontSet()._fm_large.stringWidth( my_node.getName() + " " );
3608                 }
3609                 rv.render( my_node.getXcoord() + x, node.getYcoord() - 5, g, this, to_pdf );
3610             }
3611         }
3612         //////////////
3613     }
3614
3615     final private void paintOvRectangle( final Graphics2D g ) {
3616         final float w_ratio = ( float ) getWidth() / getVisibleRect().width;
3617         final float h_ratio = ( float ) getHeight() / getVisibleRect().height;
3618         final float x_ratio = ( float ) getWidth() / getVisibleRect().x;
3619         final float y_ratio = ( float ) getHeight() / getVisibleRect().y;
3620         final float width = getOvMaxWidth() / w_ratio;
3621         final float height = getOvMaxHeight() / h_ratio;
3622         final float x = getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / x_ratio;
3623         final float y = getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / y_ratio;
3624         g.setColor( getTreeColorSet().getFoundColor() );
3625         getOvRectangle().setRect( x, y, width, height );
3626         if ( ( width < 6 ) && ( height < 6 ) ) {
3627             drawRectFilled( x, y, 6, 6, g );
3628             getOvVirtualRectangle().setRect( x, y, 6, 6 );
3629         }
3630         else if ( width < 6 ) {
3631             drawRectFilled( x, y, 6, height, g );
3632             getOvVirtualRectangle().setRect( x, y, 6, height );
3633         }
3634         else if ( height < 6 ) {
3635             drawRectFilled( x, y, width, 6, g );
3636             getOvVirtualRectangle().setRect( x, y, width, 6 );
3637         }
3638         else {
3639             drawRect( x, y, width, height, g );
3640             if ( isInOvRect() ) {
3641                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
3642             }
3643             getOvVirtualRectangle().setRect( x, y, width, height );
3644         }
3645     }
3646
3647     final void paintPhylogeny( final Graphics2D g,
3648                                final boolean to_pdf,
3649                                final boolean to_graphics_file,
3650                                final int graphics_file_width,
3651                                final int graphics_file_height,
3652                                final int graphics_file_x,
3653                                final int graphics_file_y ) {
3654         if ( _control_panel.isShowSequenceRelations() ) {
3655             _query_sequence = _control_panel.getSelectedQuerySequence();
3656         }
3657         // Color the background
3658         if ( !to_pdf ) {
3659             final Rectangle r = getVisibleRect();
3660             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
3661                 g.setColor( getTreeColorSet().getBackgroundColor() );
3662                 if ( !to_graphics_file ) {
3663                     g.fill( r );
3664                 }
3665                 else {
3666                     if ( getOptions().isPrintBlackAndWhite() ) {
3667                         g.setColor( Color.WHITE );
3668                     }
3669                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3670                 }
3671             }
3672             else {
3673                 if ( !to_graphics_file ) {
3674                     g.setPaint( new GradientPaint( r.x, r.y, getTreeColorSet().getBackgroundColor(), r.x, r.y
3675                             + r.height, getTreeColorSet().getBackgroundColorGradientBottom() ) );
3676                     g.fill( r );
3677                 }
3678                 else {
3679                     g.setPaint( new GradientPaint( graphics_file_x,
3680                                                    graphics_file_y,
3681                                                    getTreeColorSet().getBackgroundColor(),
3682                                                    graphics_file_x,
3683                                                    graphics_file_y + graphics_file_height,
3684                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
3685                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3686                 }
3687             }
3688             g.setStroke( new BasicStroke( 1 ) );
3689         }
3690         else {
3691             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
3692         }
3693         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3694                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3695             _external_node_index = 0;
3696             // Position starting X of tree
3697             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/) {
3698                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
3699             }
3700             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
3701                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE + ( _phylogeny.getRoot()
3702                         .getDistanceToParent() * getXcorrectionFactor() ) ) );
3703             }
3704             else {
3705                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
3706             }
3707             // Position starting Y of tree
3708             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
3709                     + ( TreePanel.MOVE / 2.0f ) );
3710             final int dynamic_hiding_factor = ( int ) ( getTreeFontSet()._fm_large.getHeight() / ( 1.5 * getYdistance() ) );
3711             if ( getControlPanel().isDynamicallyHideData() ) {
3712                 if ( dynamic_hiding_factor > 1 ) {
3713                     getControlPanel().setDynamicHidingIsOn( true );
3714                 }
3715                 else {
3716                     getControlPanel().setDynamicHidingIsOn( false );
3717                 }
3718             }
3719             if ( _nodes_in_preorder == null ) {
3720                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
3721                 int i = 0;
3722                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3723                     _nodes_in_preorder[ i++ ] = it.next();
3724                 }
3725             }
3726             //final PhylogenyNodeIterator it;
3727             //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3728             //    paintNodeRectangular( g, it.next(), to_pdf, getControlPanel().isDynamicallyHideData()
3729             //            && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3730             //}
3731             for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3732                 paintNodeRectangular( g, _nodes_in_preorder[ i ], to_pdf, getControlPanel().isDynamicallyHideData()
3733                         && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3734             }
3735             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
3736                 if ( !( to_graphics_file || to_pdf ) ) {
3737                     paintScale( g,
3738                                 getVisibleRect().x,
3739                                 getVisibleRect().y + getVisibleRect().height,
3740                                 to_pdf,
3741                                 to_graphics_file );
3742                 }
3743                 else {
3744                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3745                 }
3746             }
3747             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3748                 paintPhylogenyLite( g );
3749             }
3750         }
3751         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
3752             if ( getControlPanel().getDynamicallyHideData() != null ) {
3753                 getControlPanel().setDynamicHidingIsOn( false );
3754             }
3755             final double angle = getStartingAngle();
3756             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
3757             _dynamic_hiding_factor = 0;
3758             if ( getControlPanel().isDynamicallyHideData() ) {
3759                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3760                         .getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
3761             }
3762             if ( getControlPanel().getDynamicallyHideData() != null ) {
3763                 if ( _dynamic_hiding_factor > 1 ) {
3764                     getControlPanel().setDynamicHidingIsOn( true );
3765                 }
3766                 else {
3767                     getControlPanel().setDynamicHidingIsOn( false );
3768                 }
3769             }
3770             paintUnrooted( _phylogeny.getRoot(),
3771                            angle,
3772                            ( float ) ( angle + 2 * Math.PI ),
3773                            radial_labels,
3774                            g,
3775                            to_pdf,
3776                            to_graphics_file );
3777             if ( getOptions().isShowScale() ) {
3778                 if ( !( to_graphics_file || to_pdf ) ) {
3779                     paintScale( g,
3780                                 getVisibleRect().x,
3781                                 getVisibleRect().y + getVisibleRect().height,
3782                                 to_pdf,
3783                                 to_graphics_file );
3784                 }
3785                 else {
3786                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3787                 }
3788             }
3789             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3790                 g.setColor( getTreeColorSet().getOvColor() );
3791                 paintUnrootedLite( _phylogeny.getRoot(),
3792                                    angle,
3793                                    angle + 2 * Math.PI,
3794                                    g,
3795                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
3796                 paintOvRectangle( g );
3797             }
3798         }
3799         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3800             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() ) / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
3801             final int d = radius + MOVE + getLongestExtNodeInfo();
3802             _dynamic_hiding_factor = 0;
3803             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
3804                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3805                         .getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
3806             }
3807             if ( getControlPanel().getDynamicallyHideData() != null ) {
3808                 if ( _dynamic_hiding_factor > 1 ) {
3809                     getControlPanel().setDynamicHidingIsOn( true );
3810                 }
3811                 else {
3812                     getControlPanel().setDynamicHidingIsOn( false );
3813                 }
3814             }
3815             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
3816             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3817                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
3818                         : getOvMaxWidth() / 2 );
3819                 double x_scale = 1.0;
3820                 double y_scale = 1.0;
3821                 int x_pos = getVisibleRect().x + getOvXPosition();
3822                 int y_pos = getVisibleRect().y + getOvYPosition();
3823                 if ( getWidth() > getHeight() ) {
3824                     x_scale = ( double ) getHeight() / getWidth();
3825                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
3826                 }
3827                 else {
3828                     y_scale = ( double ) getWidth() / getHeight();
3829                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
3830                 }
3831                 _at = g.getTransform();
3832                 g.scale( x_scale, y_scale );
3833                 paintCircularLite( _phylogeny,
3834                                    getStartingAngle(),
3835                                    x_pos + radius_ov,
3836                                    y_pos + radius_ov,
3837                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo() / ( getVisibleRect().width / getOvRectangle()
3838                                            .getWidth() ) ) ),
3839                                    g );
3840                 g.setTransform( _at );
3841                 paintOvRectangle( g );
3842             }
3843         }
3844     }
3845
3846     final private void paintPhylogenyLite( final Graphics2D g ) {
3847         //System.out.println( getVisibleRect().x + " " + getVisibleRect().y );
3848         _phylogeny
3849                 .getRoot()
3850                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
3851                         .getWidth() ) ) ) );
3852         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
3853         //final PhylogenyNodeIterator it;
3854         //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3855         //    paintNodeLite( g, it.next() );
3856         //}
3857         for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3858             paintNodeLite( g, _nodes_in_preorder[ i ] );
3859         }
3860         paintOvRectangle( g );
3861     }
3862
3863     /**
3864      * Paint the root branch. (Differs from others because it will always be a
3865      * single horizontal line).
3866      * @param to_graphics_file 
3867      * 
3868      * @return new x1 value
3869      */
3870     final private void paintRootBranch( final Graphics2D g,
3871                                         final float x1,
3872                                         final float y1,
3873                                         final PhylogenyNode root,
3874                                         final boolean to_pdf,
3875                                         final boolean to_graphics_file ) {
3876         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
3877         float d = getXdistance();
3878         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
3879             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
3880         }
3881         if ( d < MIN_ROOT_LENGTH ) {
3882             d = MIN_ROOT_LENGTH;
3883         }
3884         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
3885             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
3886         }
3887         else {
3888             final double w = PhylogenyMethods.getBranchWidthValue( root );
3889             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
3890         }
3891         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file, isInFoundNodes( root ) );
3892     }
3893
3894     final private void paintScale( final Graphics2D g,
3895                                    int x1,
3896                                    int y1,
3897                                    final boolean to_pdf,
3898                                    final boolean to_graphics_file ) {
3899         x1 += MOVE;
3900         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
3901         y1 -= 12;
3902         final int y2 = y1 - 8;
3903         final int y3 = y1 - 4;
3904         g.setFont( getTreeFontSet().getSmallFont() );
3905         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3906             g.setColor( Color.BLACK );
3907         }
3908         else {
3909             g.setColor( getTreeColorSet().getBranchLengthColor() );
3910         }
3911         drawLine( x1, y1, x1, y2, g );
3912         drawLine( x2, y1, x2, y2, g );
3913         drawLine( x1, y3, x2, y3, g );
3914         if ( getScaleLabel() != null ) {
3915             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
3916         }
3917     }
3918
3919     final private int paintTaxonomy( final Graphics2D g,
3920                                      final PhylogenyNode node,
3921                                      final boolean is_in_found_nodes,
3922                                      final boolean to_pdf,
3923                                      final boolean to_graphics_file,
3924                                      final double x_shift ) {
3925         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3926         g.setFont( getTreeFontSet().getLargeItalicFont() );
3927         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3928             g.setColor( Color.BLACK );
3929         }
3930         else if ( is_in_found_nodes ) {
3931             g.setFont( getTreeFontSet().getLargeItalicFont().deriveFont( TreeFontSet.BOLD_AND_ITALIC ) );
3932             g.setColor( getTreeColorSet().getFoundColor() );
3933         }
3934         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3935             g.setColor( getTaxonomyBasedColor( node ) );
3936         }
3937         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3938                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3939             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3940         }
3941         else if ( to_pdf ) {
3942             g.setColor( Color.BLACK );
3943         }
3944         else {
3945             g.setColor( getTreeColorSet().getTaxonomyColor() );
3946         }
3947         final double start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
3948         final double start_y = node.getYcoord()
3949                 + ( getTreeFontSet()._fm_large.getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0 ) );
3950         _sb.setLength( 0 );
3951         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3952             _sb.append( taxonomy.getTaxonomyCode() );
3953             _sb.append( " " );
3954         }
3955         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3956             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3957                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3958                 if ( getOptions().isAbbreviateScientificTaxonNames()
3959                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3960                     abbreviateScientificName( taxonomy.getScientificName() );
3961                 }
3962                 else {
3963                     _sb.append( taxonomy.getScientificName() );
3964                 }
3965                 _sb.append( " (" );
3966                 _sb.append( taxonomy.getCommonName() );
3967                 _sb.append( ") " );
3968             }
3969             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3970                 if ( getOptions().isAbbreviateScientificTaxonNames()
3971                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3972                     abbreviateScientificName( taxonomy.getScientificName() );
3973                 }
3974                 else {
3975                     _sb.append( taxonomy.getScientificName() );
3976                 }
3977                 _sb.append( " " );
3978             }
3979             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3980                 _sb.append( taxonomy.getCommonName() );
3981                 _sb.append( " " );
3982             }
3983         }
3984         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3985             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3986                 if ( getOptions().isAbbreviateScientificTaxonNames()
3987                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3988                     abbreviateScientificName( taxonomy.getScientificName() );
3989                 }
3990                 else {
3991                     _sb.append( taxonomy.getScientificName() );
3992                 }
3993                 _sb.append( " " );
3994             }
3995         }
3996         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3997             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3998                 _sb.append( taxonomy.getCommonName() );
3999                 _sb.append( " " );
4000             }
4001         }
4002         final String label = _sb.toString();
4003         /* GUILHEM_BEG */
4004         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
4005                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
4006             // invert font color and background color to show that this is the query sequence
4007             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
4008                                                                                                           false,
4009                                                                                                           false ) )
4010                     .getBounds();
4011             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
4012             g.setColor( getTreeColorSet().getBackgroundColor() );
4013         }
4014         /* GUILHEM_END */
4015         TreePanel.drawString( label, start_x, start_y, g );
4016         if ( is_in_found_nodes ) {
4017             return getTreeFontSet()._fm_large_italic_bold.stringWidth( label );
4018         }
4019         else {
4020             return getTreeFontSet()._fm_large_italic.stringWidth( label );
4021         }
4022     }
4023
4024     private void abbreviateScientificName( final String sn ) {
4025         final String[] a = sn.split( "\\s+" );
4026         _sb.append( a[ 0 ].substring( 0, 1 ) );
4027         _sb.append( a[ 1 ].substring( 0, 2 ) );
4028         if ( a.length > 2 ) {
4029             for( int i = 2; i < a.length; i++ ) {
4030                 _sb.append( " " );
4031                 _sb.append( a[ i ] );
4032             }
4033         }
4034     }
4035
4036     final private void paintUnrooted( final PhylogenyNode n,
4037                                       final double low_angle,
4038                                       final double high_angle,
4039                                       final boolean radial_labels,
4040                                       final Graphics2D g,
4041                                       final boolean to_pdf,
4042                                       final boolean to_graphics_file ) {
4043         if ( n.isRoot() ) {
4044             n.setXcoord( getWidth() / 2 );
4045             n.setYcoord( getHeight() / 2 );
4046         }
4047         if ( n.isExternal() ) {
4048             paintNodeDataUnrootedCirc( g,
4049                                        n,
4050                                        to_pdf,
4051                                        to_graphics_file,
4052                                        radial_labels,
4053                                        ( high_angle + low_angle ) / 2,
4054                                        isInFoundNodes( n ) );
4055             return;
4056         }
4057         final float num_enclosed = n.getNumberOfExternalNodes();
4058         final float x = n.getXcoord();
4059         final float y = n.getYcoord();
4060         double current_angle = low_angle;
4061         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4062         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4063         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4064         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4065         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4066             final PhylogenyNode desc = n.getChildNode( i );
4067             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4068             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4069             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4070             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4071             //     continue;
4072             // }
4073             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4074             //    continue;
4075             //}
4076             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4077             //    continue;
4078             // }
4079             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4080             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4081             float length;
4082             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4083                 if ( desc.getDistanceToParent() < 0 ) {
4084                     length = 0;
4085                 }
4086                 else {
4087                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4088                 }
4089             }
4090             else {
4091                 length = getUrtFactor();
4092             }
4093             final double mid_angle = current_angle + arc_size / 2;
4094             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4095             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4096             desc.setXcoord( new_x );
4097             desc.setYcoord( new_y );
4098             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
4099             current_angle += arc_size;
4100             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
4101             drawLine( x, y, new_x, new_y, g );
4102             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file, isInFoundNodes( desc ) );
4103         }
4104         if ( n.isRoot() ) {
4105             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file, isInFoundNodes( n ) );
4106         }
4107     }
4108
4109     final private void paintUnrootedLite( final PhylogenyNode n,
4110                                           final double low_angle,
4111                                           final double high_angle,
4112                                           final Graphics2D g,
4113                                           final float urt_ov_factor ) {
4114         if ( n.isRoot() ) {
4115             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / 2 );
4116             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / 2 );
4117             n.setXSecondary( x_pos );
4118             n.setYSecondary( y_pos );
4119         }
4120         if ( n.isExternal() ) {
4121             return;
4122         }
4123         final float num_enclosed = n.getNumberOfExternalNodes();
4124         final float x = n.getXSecondary();
4125         final float y = n.getYSecondary();
4126         double current_angle = low_angle;
4127         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4128             final PhylogenyNode desc = n.getChildNode( i );
4129             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4130             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4131             float length;
4132             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4133                 if ( desc.getDistanceToParent() < 0 ) {
4134                     length = 0;
4135                 }
4136                 else {
4137                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
4138                 }
4139             }
4140             else {
4141                 length = urt_ov_factor;
4142             }
4143             final double mid_angle = current_angle + arc_size / 2;
4144             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4145             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4146             desc.setXSecondary( new_x );
4147             desc.setYSecondary( new_y );
4148             if ( isInFoundNodes( desc ) ) {
4149                 g.setColor( getTreeColorSet().getFoundColor() );
4150                 drawRectFilled( desc.getXSecondary() - 1, desc.getYSecondary() - 1, 3, 3, g );
4151                 g.setColor( getTreeColorSet().getOvColor() );
4152             }
4153             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
4154             current_angle += arc_size;
4155             drawLine( x, y, new_x, new_y, g );
4156         }
4157     }
4158
4159     final private void pasteSubtree( final PhylogenyNode node ) {
4160         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4161             errorMessageNoCutCopyPasteInUnrootedDisplay();
4162             return;
4163         }
4164         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
4165             JOptionPane.showMessageDialog( this,
4166                                            "No tree in buffer (need to copy or cut a subtree first)",
4167                                            "Attempt to paste with empty buffer",
4168                                            JOptionPane.ERROR_MESSAGE );
4169             return;
4170         }
4171         final String label = getASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
4172         final Object[] options = { "As sibling", "As descendant", "Cancel" };
4173         final int r = JOptionPane.showOptionDialog( this,
4174                                                     "How to paste subtree" + label + "?",
4175                                                     "Paste Subtree",
4176                                                     JOptionPane.CLOSED_OPTION,
4177                                                     JOptionPane.QUESTION_MESSAGE,
4178                                                     null,
4179                                                     options,
4180                                                     options[ 2 ] );
4181         boolean paste_as_sibling = true;
4182         if ( r == 1 ) {
4183             paste_as_sibling = false;
4184         }
4185         else if ( r != 0 ) {
4186             return;
4187         }
4188         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
4189         buffer_phy.setAllNodesToNotCollapse();
4190         buffer_phy.preOrderReId();
4191         buffer_phy.setRooted( true );
4192         boolean need_to_show_whole = false;
4193         if ( paste_as_sibling ) {
4194             if ( node.isRoot() ) {
4195                 JOptionPane.showMessageDialog( this,
4196                                                "Cannot paste sibling to root",
4197                                                "Attempt to paste sibling to root",
4198                                                JOptionPane.ERROR_MESSAGE );
4199                 return;
4200             }
4201             buffer_phy.addAsSibling( node );
4202         }
4203         else {
4204             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
4205                 need_to_show_whole = true;
4206                 _phylogeny = buffer_phy;
4207             }
4208             else {
4209                 buffer_phy.addAsChild( node );
4210             }
4211         }
4212         if ( getCopiedAndPastedNodes() == null ) {
4213             setCopiedAndPastedNodes( new HashSet<Integer>() );
4214         }
4215         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
4216         final Set<Integer> node_ids = new HashSet<Integer>( nodes.size() );
4217         for( final PhylogenyNode n : nodes ) {
4218             node_ids.add( n.getId() );
4219         }
4220         node_ids.add( node.getId() );
4221         getCopiedAndPastedNodes().addAll( node_ids );
4222         setNodeInPreorderToNull();
4223         _phylogeny.externalNodesHaveChanged();
4224         _phylogeny.hashIDs();
4225         _phylogeny.recalculateNumberOfExternalDescendants( true );
4226         resetNodeIdToDistToLeafMap();
4227         setEdited( true );
4228         if ( need_to_show_whole ) {
4229             getControlPanel().showWhole();
4230         }
4231         repaint();
4232     }
4233
4234     final public int print( final Graphics g, final PageFormat page_format, final int page_index )
4235             throws PrinterException {
4236         if ( page_index > 0 ) {
4237             return ( NO_SUCH_PAGE );
4238         }
4239         else {
4240             final Graphics2D g2d = ( Graphics2D ) g;
4241             g2d.translate( page_format.getImageableX(), page_format.getImageableY() );
4242             // Turn off double buffering !?
4243             paintPhylogeny( g2d, true, false, 0, 0, 0, 0 );
4244             // Turn double buffering back on !?
4245             return ( PAGE_EXISTS );
4246         }
4247     }
4248
4249     final void recalculateMaxDistanceToRoot() {
4250         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
4251     }
4252
4253     /**
4254      * Remove all edit-node frames
4255      */
4256     final void removeAllEditNodeJFrames() {
4257         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
4258             if ( _node_frames[ i ] != null ) {
4259                 _node_frames[ i ].dispose();
4260                 _node_frames[ i ] = null;
4261             }
4262         }
4263         _node_frame_index = 0;
4264     }
4265
4266     /**
4267      * Remove a node-edit frame.
4268      */
4269     final void removeEditNodeFrame( final int i ) {
4270         _node_frame_index--;
4271         _node_frames[ i ] = null;
4272         if ( i < _node_frame_index ) {
4273             for( int j = 0; j < _node_frame_index - 1; j++ ) {
4274                 _node_frames[ j ] = _node_frames[ j + 1 ];
4275             }
4276             _node_frames[ _node_frame_index ] = null;
4277         }
4278     }
4279
4280     final void reRoot( final PhylogenyNode node ) {
4281         if ( !getPhylogeny().isRerootable() ) {
4282             JOptionPane.showMessageDialog( this,
4283                                            "This is not rerootable",
4284                                            "Not rerootable",
4285                                            JOptionPane.WARNING_MESSAGE );
4286             return;
4287         }
4288         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4289             JOptionPane.showMessageDialog( this,
4290                                            "Cannot reroot in unrooted display type",
4291                                            "Attempt to reroot tree in unrooted display",
4292                                            JOptionPane.WARNING_MESSAGE );
4293             return;
4294         }
4295         getPhylogeny().reRoot( node );
4296         getPhylogeny().recalculateNumberOfExternalDescendants( true );
4297         resetNodeIdToDistToLeafMap();
4298         setNodeInPreorderToNull();
4299         resetPreferredSize();
4300         getMainPanel().adjustJScrollPane();
4301         repaint();
4302         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4303             getControlPanel().showWhole();
4304         }
4305     }
4306
4307     final void resetNodeIdToDistToLeafMap() {
4308         _nodeid_dist_to_leaf = new HashMap<Integer, Short>();
4309     }
4310
4311     final void resetPreferredSize() {
4312         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
4313             return;
4314         }
4315         int x = 0;
4316         int y = 0;
4317         y = TreePanel.MOVE
4318                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
4319         if ( getControlPanel().isDrawPhylogram() ) {
4320             x = TreePanel.MOVE
4321                     + getLongestExtNodeInfo()
4322                     + ForesterUtil
4323                             .roundToInt( ( getXcorrectionFactor() * getPhylogeny().getHeight() ) + getXdistance() );
4324         }
4325         else {
4326             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4327                 x = TreePanel.MOVE
4328                         + getLongestExtNodeInfo()
4329                         + ForesterUtil.roundToInt( getXdistance()
4330                                 * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
4331             }
4332             else {
4333                 x = TreePanel.MOVE
4334                         + getLongestExtNodeInfo()
4335                         + ForesterUtil.roundToInt( getXdistance()
4336                                 * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
4337             }
4338         }
4339         setPreferredSize( new Dimension( x, y ) );
4340     }
4341
4342     public final void setArrowCursor() {
4343         setCursor( ARROW_CURSOR );
4344         repaint();
4345     }
4346
4347     final void setControlPanel( final ControlPanel atv_control ) {
4348         _control_panel = atv_control;
4349     }
4350
4351     final private void setCopiedAndPastedNodes( final Set<Integer> nodeIds ) {
4352         getMainPanel().setCopiedAndPastedNodes( nodeIds );
4353     }
4354
4355     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
4356         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
4357     }
4358
4359     public final void setEdited( final boolean edited ) {
4360         _edited = edited;
4361     }
4362
4363     final void setFoundNodes( final Set<Integer> found_nodes ) {
4364         _found_nodes = found_nodes;
4365     }
4366
4367     final private void setInOv( final boolean in_ov ) {
4368         _in_ov = in_ov;
4369     }
4370
4371     final void setInOvRect( final boolean in_ov_rect ) {
4372         _in_ov_rect = in_ov_rect;
4373     }
4374
4375     final void setLargeFonts() {
4376         getTreeFontSet().largeFonts();
4377     }
4378
4379     final void setLastMouseDragPointX( final float x ) {
4380         _last_drag_point_x = x;
4381     }
4382
4383     final void setLastMouseDragPointY( final float y ) {
4384         _last_drag_point_y = y;
4385     }
4386
4387     final void setLongestExtNodeInfo( final int i ) {
4388         _longest_ext_node_info = i;
4389     }
4390
4391     final void setMediumFonts() {
4392         getTreeFontSet().mediumFonts();
4393     }
4394
4395     final private void setOvMaxHeight( final float ov_max_height ) {
4396         _ov_max_height = ov_max_height;
4397     }
4398
4399     final private void setOvMaxWidth( final float ov_max_width ) {
4400         _ov_max_width = ov_max_width;
4401     }
4402
4403     final void setOvOn( final boolean ov_on ) {
4404         _ov_on = ov_on;
4405     }
4406
4407     final private void setOvXcorrectionFactor( final float f ) {
4408         _ov_x_correction_factor = f;
4409     }
4410
4411     final private void setOvXDistance( final float ov_x_distance ) {
4412         _ov_x_distance = ov_x_distance;
4413     }
4414
4415     final private void setOvXPosition( final int ov_x_position ) {
4416         _ov_x_position = ov_x_position;
4417     }
4418
4419     final private void setOvYDistance( final float ov_y_distance ) {
4420         _ov_y_distance = ov_y_distance;
4421     }
4422
4423     final private void setOvYPosition( final int ov_y_position ) {
4424         _ov_y_position = ov_y_position;
4425     }
4426
4427     final private void setOvYStart( final int ov_y_start ) {
4428         _ov_y_start = ov_y_start;
4429     }
4430
4431     /**
4432      * Set parameters for printing the displayed tree
4433      * 
4434      * @param x
4435      * @param y
4436      */
4437     public final void setParametersForPainting( final int x, final int y, final boolean recalc_longest_ext_node_info ) {
4438         // updateStyle(); not needed?
4439         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4440             initNodeData();
4441             if ( recalc_longest_ext_node_info ) {
4442                 calculateLongestExtNodeInfo();
4443             }
4444             int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4445             final int max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4446             if ( ext_nodes == 1 ) {
4447                 ext_nodes = max_depth;
4448                 if ( ext_nodes < 1 ) {
4449                     ext_nodes = 1;
4450                 }
4451             }
4452             updateOvSizes();
4453             float xdist = 0;
4454             float ov_xdist = 0;
4455             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4456                 xdist = ( float ) ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( ext_nodes + 3.0 ) );
4457                 ov_xdist = ( float ) ( getOvMaxWidth() / ( ext_nodes + 3.0 ) );
4458             }
4459             else {
4460                 xdist = ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( max_depth + 1 ) );
4461                 ov_xdist = ( getOvMaxWidth() / ( max_depth + 1 ) );
4462             }
4463             float ydist = ( float ) ( ( y - TreePanel.MOVE ) / ( ext_nodes * 2.0 ) );
4464             if ( xdist < 0.0 ) {
4465                 xdist = 0.0f;
4466             }
4467             if ( ov_xdist < 0.0 ) {
4468                 ov_xdist = 0.0f;
4469             }
4470             if ( ydist < 0.0 ) {
4471                 ydist = 0.0f;
4472             }
4473             setXdistance( xdist );
4474             setYdistance( ydist );
4475             setOvXDistance( ov_xdist );
4476             final double height = _phylogeny.getHeight();
4477             if ( height > 0 ) {
4478                 final float corr = ( float ) ( ( x - TreePanel.MOVE - getLongestExtNodeInfo() - getXdistance() ) / height );
4479                 setXcorrectionFactor( corr > 0 ? corr : 0 );
4480                 final float ov_corr = ( float ) ( ( getOvMaxWidth() - getOvXDistance() ) / height );
4481                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
4482             }
4483             else {
4484                 setXcorrectionFactor( 0 );
4485                 setOvXcorrectionFactor( 0 );
4486             }
4487             _circ_max_depth = max_depth;
4488             setUpUrtFactor();
4489         }
4490     }
4491
4492     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
4493         _graphics_type = graphics_type;
4494         setTextAntialias();
4495     }
4496
4497     final private void setScaleDistance( final double scale_distance ) {
4498         _scale_distance = scale_distance;
4499     }
4500
4501     final private void setScaleLabel( final String scale_label ) {
4502         _scale_label = scale_label;
4503     }
4504
4505     final void setSmallFonts() {
4506         getTreeFontSet().smallFonts();
4507     }
4508
4509     final void setStartingAngle( final double starting_angle ) {
4510         _urt_starting_angle = starting_angle;
4511     }
4512
4513     final void setSuperTinyFonts() {
4514         getTreeFontSet().superTinyFonts();
4515     }
4516
4517     final void setTextAntialias() {
4518         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4519             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
4520                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
4521             }
4522             else {
4523                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
4524             }
4525         }
4526         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
4527             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) {
4528                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4529             }
4530             else {
4531                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
4532             }
4533             try {
4534                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING,
4535                                       RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
4536             }
4537             catch ( final Throwable e ) {
4538                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
4539             }
4540         }
4541         else {
4542             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
4543             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4544         }
4545     }
4546
4547     final void setTinyFonts() {
4548         getTreeFontSet().tinyFonts();
4549     }
4550
4551     /**
4552      * Set a phylogeny tree.
4553      * 
4554      * @param t
4555      *            an instance of a Phylogeny
4556      */
4557     public final void setTree( final Phylogeny t ) {
4558         setNodeInPreorderToNull();
4559         _phylogeny = t;
4560     }
4561
4562     final void setNodeInPreorderToNull() {
4563         _nodes_in_preorder = null;
4564     }
4565
4566     final void setTreeFile( final File treefile ) {
4567         _treefile = treefile;
4568     }
4569
4570     final private void setUpUrtFactor() {
4571         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
4572                 : getVisibleRect().height;
4573         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4574             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
4575         }
4576         else {
4577             final int max_depth = _circ_max_depth;
4578             if ( max_depth > 0 ) {
4579                 setUrtFactor( d / ( 2 * max_depth ) );
4580             }
4581             else {
4582                 setUrtFactor( d / 2 );
4583             }
4584         }
4585         setUrtFactorOv( getUrtFactor() );
4586     }
4587
4588     final private void setUrtFactor( final float urt_factor ) {
4589         _urt_factor = urt_factor;
4590     }
4591
4592     final private void setUrtFactorOv( final float urt_factor_ov ) {
4593         _urt_factor_ov = urt_factor_ov;
4594     }
4595
4596     public final void setWaitCursor() {
4597         setCursor( WAIT_CURSOR );
4598         repaint();
4599     }
4600
4601     final void setXcorrectionFactor( final float f ) {
4602         _x_correction_factor = f;
4603     }
4604
4605     final void setXdistance( final float x ) {
4606         _x_distance = x;
4607     }
4608
4609     final void setYdistance( final float y ) {
4610         _y_distance = y;
4611     }
4612
4613     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
4614         try {
4615             if ( ( node.getName().length() > 0 )
4616                     || ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
4617                     || ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) )
4618                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
4619                     || node.getBranchData().isHasConfidences() ) {
4620                 _popup_buffer.setLength( 0 );
4621                 short lines = 0;
4622                 if ( node.getName().length() > 0 ) {
4623                     lines++;
4624                     _popup_buffer.append( node.getName() );
4625                 }
4626                 if ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
4627                     lines++;
4628                     boolean enc_data = false;
4629                     final Taxonomy tax = node.getNodeData().getTaxonomy();
4630                     if ( _popup_buffer.length() > 0 ) {
4631                         _popup_buffer.append( "\n" );
4632                     }
4633                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4634                         _popup_buffer.append( "[" );
4635                         _popup_buffer.append( tax.getTaxonomyCode() );
4636                         _popup_buffer.append( "]" );
4637                         enc_data = true;
4638                     }
4639                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4640                         if ( enc_data ) {
4641                             _popup_buffer.append( " " );
4642                         }
4643                         _popup_buffer.append( tax.getScientificName() );
4644                         enc_data = true;
4645                     }
4646                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
4647                         if ( enc_data ) {
4648                             _popup_buffer.append( " (" );
4649                         }
4650                         else {
4651                             _popup_buffer.append( "(" );
4652                         }
4653                         _popup_buffer.append( tax.getCommonName() );
4654                         _popup_buffer.append( ")" );
4655                         enc_data = true;
4656                     }
4657                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
4658                         if ( enc_data ) {
4659                             _popup_buffer.append( " (" );
4660                         }
4661                         else {
4662                             _popup_buffer.append( "(" );
4663                         }
4664                         _popup_buffer.append( tax.getAuthority() );
4665                         _popup_buffer.append( ")" );
4666                         enc_data = true;
4667                     }
4668                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
4669                         if ( enc_data ) {
4670                             _popup_buffer.append( " [" );
4671                         }
4672                         else {
4673                             _popup_buffer.append( "[" );
4674                         }
4675                         _popup_buffer.append( tax.getRank() );
4676                         _popup_buffer.append( "]" );
4677                         enc_data = true;
4678                     }
4679                     if ( tax.getSynonyms().size() > 0 ) {
4680                         if ( enc_data ) {
4681                             _popup_buffer.append( " " );
4682                         }
4683                         _popup_buffer.append( "[" );
4684                         int counter = 1;
4685                         for( final String syn : tax.getSynonyms() ) {
4686                             if ( !ForesterUtil.isEmpty( syn ) ) {
4687                                 enc_data = true;
4688                                 _popup_buffer.append( syn );
4689                                 if ( counter < tax.getSynonyms().size() ) {
4690                                     _popup_buffer.append( ", " );
4691                                 }
4692                             }
4693                             counter++;
4694                         }
4695                         _popup_buffer.append( "]" );
4696                     }
4697                     if ( !enc_data ) {
4698                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
4699                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
4700                                 _popup_buffer.append( "[" );
4701                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
4702                                 _popup_buffer.append( "] " );
4703                             }
4704                             _popup_buffer.append( tax.getIdentifier().getValue() );
4705                         }
4706                     }
4707                 }
4708                 if ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) ) {
4709                     lines++;
4710                     boolean enc_data = false;
4711                     if ( _popup_buffer.length() > 0 ) {
4712                         _popup_buffer.append( "\n" );
4713                     }
4714                     final Sequence seq = node.getNodeData().getSequence();
4715                     if ( seq.getAccession() != null ) {
4716                         _popup_buffer.append( "[" );
4717                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
4718                             _popup_buffer.append( seq.getAccession().getSource() );
4719                             _popup_buffer.append( ":" );
4720                         }
4721                         _popup_buffer.append( seq.getAccession().getValue() );
4722                         _popup_buffer.append( "]" );
4723                         enc_data = true;
4724                     }
4725                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
4726                         if ( enc_data ) {
4727                             _popup_buffer.append( " [" );
4728                         }
4729                         else {
4730                             _popup_buffer.append( "[" );
4731                         }
4732                         _popup_buffer.append( seq.getSymbol() );
4733                         _popup_buffer.append( "]" );
4734                         enc_data = true;
4735                     }
4736                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
4737                         if ( enc_data ) {
4738                             _popup_buffer.append( " " );
4739                         }
4740                         _popup_buffer.append( seq.getName() );
4741                     }
4742                 }
4743                 if ( node.getNodeData().isHasDate() ) {
4744                     lines++;
4745                     if ( _popup_buffer.length() > 0 ) {
4746                         _popup_buffer.append( "\n" );
4747                     }
4748                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
4749                 }
4750                 if ( node.getNodeData().isHasDistribution() ) {
4751                     lines++;
4752                     if ( _popup_buffer.length() > 0 ) {
4753                         _popup_buffer.append( "\n" );
4754                     }
4755                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
4756                 }
4757                 if ( node.getBranchData().isHasConfidences() ) {
4758                     final List<Confidence> confs = node.getBranchData().getConfidences();
4759                     for( final Confidence confidence : confs ) {
4760                         lines++;
4761                         if ( _popup_buffer.length() > 0 ) {
4762                             _popup_buffer.append( "\n" );
4763                         }
4764                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
4765                             _popup_buffer.append( "[" );
4766                             _popup_buffer.append( confidence.getType() );
4767                             _popup_buffer.append( "] " );
4768                         }
4769                         _popup_buffer
4770                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
4771                                                                                           getOptions()
4772                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4773                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4774                             _popup_buffer.append( " (sd=" );
4775                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
4776                                     .getStandardDeviation(), getOptions()
4777                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4778                             _popup_buffer.append( ")" );
4779                         }
4780                     }
4781                 }
4782                 if ( node.getNodeData().isHasProperties() ) {
4783                     final PropertiesMap properties = node.getNodeData().getProperties();
4784                     for( final String ref : properties.getPropertyRefs() ) {
4785                         _popup_buffer.append( "\n" );
4786                         final Property p = properties.getProperty( ref );
4787                         _popup_buffer.append( getPartAfterColon( p.getRef() ) );
4788                         _popup_buffer.append( "=" );
4789                         _popup_buffer.append( p.getValue() );
4790                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
4791                             _popup_buffer.append( getPartAfterColon( p.getUnit() ) );
4792                         }
4793                     }
4794                 }
4795                 if ( _popup_buffer.length() > 0 ) {
4796                     if ( !getConfiguration().isUseNativeUI() ) {
4797                         _rollover_popup
4798                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
4799                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
4800                         if ( isInFoundNodes( node ) ) {
4801                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
4802                         }
4803                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4804                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
4805                         }
4806                         else {
4807                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
4808                         }
4809                     }
4810                     else {
4811                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
4812                     }
4813                     _rollover_popup.setText( _popup_buffer.toString() );
4814                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
4815                                                                                   _rollover_popup,
4816                                                                                   e.getLocationOnScreen().x + 10,
4817                                                                                   e.getLocationOnScreen().y
4818                                                                                           - ( lines * 20 ) );
4819                     _node_desc_popup.show();
4820                 }
4821             }
4822         }
4823         catch ( final Exception ex ) {
4824             // Do nothing.
4825         }
4826     }
4827
4828     final private void showNodeEditFrame( final PhylogenyNode n ) {
4829         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4830             // pop up edit box for single node
4831             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
4832             _node_frame_index++;
4833         }
4834         else {
4835             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4836         }
4837     }
4838
4839     final private void showNodeFrame( final PhylogenyNode n ) {
4840         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4841             // pop up edit box for single node
4842             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
4843             _node_frame_index++;
4844         }
4845         else {
4846             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4847         }
4848     }
4849
4850     final void subTree( final PhylogenyNode node ) {
4851         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4852             JOptionPane.showMessageDialog( this,
4853                                            "Cannot get a sub/super tree in unrooted display",
4854                                            "Attempt to get sub/super tree in unrooted display",
4855                                            JOptionPane.WARNING_MESSAGE );
4856             return;
4857         }
4858         if ( node.isExternal() ) {
4859             JOptionPane.showMessageDialog( this,
4860                                            "Cannot get a subtree of a external node",
4861                                            "Attempt to get subtree of external node",
4862                                            JOptionPane.WARNING_MESSAGE );
4863             return;
4864         }
4865         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
4866             JOptionPane.showMessageDialog( this,
4867                                            "Cannot get a subtree of the root node",
4868                                            "Attempt to get subtree of root node",
4869                                            JOptionPane.WARNING_MESSAGE );
4870             return;
4871         }
4872         setNodeInPreorderToNull();
4873         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
4874             _sub_phylogenies[ _subtree_index ] = _phylogeny;
4875             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
4876             ++_subtree_index;
4877             _phylogeny = subTree( node, _phylogeny );
4878             updateSubSuperTreeButton();
4879         }
4880         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
4881             superTree();
4882         }
4883         _main_panel.getControlPanel().showWhole();
4884         repaint();
4885     }
4886
4887     final boolean isCurrentTreeIsSubtree() {
4888         return ( _subtree_index > 0 );
4889     }
4890
4891     final private static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
4892         final Phylogeny new_phy = new Phylogeny();
4893         new_phy.setRooted( true );
4894         new_phy.setName( source_phy.getName() );
4895         new_phy.setDescription( source_phy.getDescription() );
4896         new_phy.setType( source_phy.getType() );
4897         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
4898         new_phy.setConfidence( source_phy.getConfidence() );
4899         new_phy.setIdentifier( source_phy.getIdentifier() );
4900         new_phy.setRoot( new_root.copyNodeDataShallow() );
4901         int i = 0;
4902         for( final PhylogenyNode n : new_root.getDescendants() ) {
4903             new_phy.getRoot().setChildNode( i++, n );
4904         }
4905         return new_phy;
4906     }
4907
4908     final void superTree() {
4909         setNodeInPreorderToNull();
4910         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
4911         for( final PhylogenyNode n : temp_root.getDescendants() ) {
4912             n.setParent( temp_root );
4913         }
4914         _sub_phylogenies[ _subtree_index ] = null;
4915         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
4916         _phylogeny = _sub_phylogenies[ --_subtree_index ];
4917         updateSubSuperTreeButton();
4918     }
4919
4920     final void swap( final PhylogenyNode node ) {
4921         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
4922             return;
4923         }
4924         if ( node.getNumberOfDescendants() > 2 ) {
4925             JOptionPane.showMessageDialog( this,
4926                                            "Cannot swap descendants of nodes with more than 2 descendants",
4927                                            "Cannot swap descendants",
4928                                            JOptionPane.ERROR_MESSAGE );
4929             return;
4930         }
4931         if ( !node.isExternal() ) {
4932             node.swapChildren();
4933             setNodeInPreorderToNull();
4934         }
4935         repaint();
4936     }
4937
4938     final void sortDescendants( final PhylogenyNode node ) {
4939         if ( !node.isExternal() ) {
4940             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
4941             if ( ( !getControlPanel().isShowTaxonomyScientificNames() && !getControlPanel().isShowTaxonomyCode() && !getControlPanel()
4942                     .isShowTaxonomyCommonNames() ) ) {
4943                 if ( ( getControlPanel().isShowSequenceAcc() || getControlPanel().isShowGeneNames() || getControlPanel()
4944                         .isShowGeneSymbols() ) ) {
4945                     pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
4946                 }
4947                 else if ( getControlPanel().isShowNodeNames() ) {
4948                     pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
4949                 }
4950             }
4951             PhylogenyMethods.sortNodeDescendents( node, pri );
4952             setNodeInPreorderToNull();
4953         }
4954         repaint();
4955     }
4956
4957     final private void switchDisplaygetPhylogenyGraphicsType() {
4958         switch ( getPhylogenyGraphicsType() ) {
4959             case RECTANGULAR:
4960                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4961                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4962                 break;
4963             case EURO_STYLE:
4964                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4965                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4966                 break;
4967             case ROUNDED:
4968                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4969                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4970                 break;
4971             case CURVED:
4972                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4973                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4974                 break;
4975             case TRIANGULAR:
4976                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4977                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4978                 break;
4979             case CONVEX:
4980                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4981                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4982                 break;
4983             case UNROOTED:
4984                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4985                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4986                 break;
4987             case CIRCULAR:
4988                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4989                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4990                 break;
4991             default:
4992                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
4993         }
4994         if ( getControlPanel().getDynamicallyHideData() != null ) {
4995             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4996                 getControlPanel().getDynamicallyHideData().setEnabled( false );
4997             }
4998             else {
4999                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5000             }
5001         }
5002         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5003             getControlPanel().setDrawPhylogramEnabled( true );
5004         }
5005         else {
5006             getControlPanel().setDrawPhylogramEnabled( false );
5007         }
5008         if ( getMainPanel().getMainFrame() == null ) {
5009             // Must be "E" applet version.
5010             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5011                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5012         }
5013         else {
5014             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5015         }
5016     }
5017
5018     final void taxColor() {
5019         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5020             return;
5021         }
5022         setWaitCursor();
5023         AptxUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
5024         _control_panel.setColorBranches( true );
5025         if ( _control_panel.getColorBranchesCb() != null ) {
5026             _control_panel.getColorBranchesCb().setSelected( true );
5027         }
5028         setArrowCursor();
5029         repaint();
5030     }
5031
5032     final void updateOvSettings() {
5033         switch ( getOptions().getOvPlacement() ) {
5034             case LOWER_LEFT:
5035                 setOvXPosition( OV_BORDER );
5036                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5037                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5038                 break;
5039             case LOWER_RIGHT:
5040                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5041                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5042                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5043                 break;
5044             case UPPER_RIGHT:
5045                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5046                 setOvYPosition( OV_BORDER );
5047                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5048                 break;
5049             default:
5050                 setOvXPosition( OV_BORDER );
5051                 setOvYPosition( OV_BORDER );
5052                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5053                 break;
5054         }
5055     }
5056
5057     final void updateOvSizes() {
5058         if ( ( getWidth() > 1.05 * getVisibleRect().width ) || ( getHeight() > 1.05 * getVisibleRect().height ) ) {
5059             setOvOn( true );
5060             float l = getLongestExtNodeInfo();
5061             final float w_ratio = getOvMaxWidth() / getWidth();
5062             l *= w_ratio;
5063             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
5064             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
5065             float ov_xdist = 0;
5066             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
5067                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
5068             }
5069             else {
5070                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
5071             }
5072             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
5073             if ( ov_xdist < 0.0 ) {
5074                 ov_xdist = 0.0f;
5075             }
5076             if ( ydist < 0.0 ) {
5077                 ydist = 0.0f;
5078             }
5079             setOvXDistance( ov_xdist );
5080             final double height = _phylogeny.getHeight();
5081             if ( height > 0 ) {
5082                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
5083                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
5084             }
5085             else {
5086                 setOvXcorrectionFactor( 0 );
5087             }
5088         }
5089         else {
5090             setOvOn( false );
5091         }
5092     }
5093
5094     final void updateSubSuperTreeButton() {
5095         if ( _subtree_index < 1 ) {
5096             getControlPanel().deactivateButtonToReturnToSuperTree();
5097         }
5098         else {
5099             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
5100         }
5101     }
5102
5103     final void zoomInDomainStructure() {
5104         if ( _domain_structure_width < 2000 ) {
5105             _domain_structure_width *= 1.2;
5106         }
5107     }
5108
5109     final void zoomOutDomainStructure() {
5110         if ( _domain_structure_width > 20 ) {
5111             _domain_structure_width *= 0.8;
5112         }
5113     }
5114
5115     final private static void drawString( final int i, final double x, final double y, final Graphics2D g ) {
5116         g.drawString( String.valueOf( i ), ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5117     }
5118
5119     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5120         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5121     }
5122
5123     final private static boolean isSequenceEmpty( final Sequence seq ) {
5124         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
5125                 && ForesterUtil.isEmpty( seq.getSymbol() );
5126     }
5127
5128     final private static boolean isTaxonomyEmpty( final Taxonomy tax ) {
5129         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
5130                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
5131                 .getSynonyms().isEmpty() );
5132     }
5133
5134     final private static boolean plusPressed( final int key_code ) {
5135         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5136                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5137     }
5138
5139     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
5140         _statistics_for_vector_data = statistics_for_expression_values;
5141     }
5142
5143     DescriptiveStatistics getStatisticsForExpressionValues() {
5144         return _statistics_for_vector_data;
5145     }
5146
5147     final private class SubtreeColorizationActionListener implements ActionListener {
5148
5149         JColorChooser _chooser;
5150         PhylogenyNode _node;
5151
5152         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5153             _chooser = chooser;
5154             _node = node;
5155         }
5156
5157         @Override
5158         public void actionPerformed( final ActionEvent e ) {
5159             final Color c = _chooser.getColor();
5160             if ( c != null ) {
5161                 colorizeSubtree( c, _node );
5162             }
5163         }
5164     }
5165
5166     public synchronized void setImageMap( final Hashtable<String, BufferedImage> image_map ) {
5167         getMainPanel().setImageMap( image_map );
5168     }
5169
5170     public synchronized Hashtable<String, BufferedImage> getImageMap() {
5171         return getMainPanel().getImageMap();
5172     }
5173 }