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