6afa7a7c51dd1dac30d6daa6b6710740d039b4bd
[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             final Property p = properties.getProperty( ref );
3172             sb.append( getPartAfterColon( p.getRef() ) );
3173             sb.append( "=" );
3174             sb.append( p.getValue() );
3175             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
3176                 sb.append( getPartAfterColon( p.getUnit() ) );
3177             }
3178         }
3179         return sb;
3180     }
3181
3182     final private static String getPartAfterColon( final String s ) {
3183         final int i = s.indexOf( ':' );
3184         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
3185             return s;
3186         }
3187         return s.substring( i + 1, s.length() );
3188     }
3189
3190     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
3191         final List<Uri> us = new ArrayList<Uri>();
3192         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
3193             for( final Uri uri : t.getUris() ) {
3194                 us.add( uri );
3195             }
3196         }
3197         double offset = 0;
3198         for( final Uri uri : us ) {
3199             if ( uri != null ) {
3200                 final String uri_str = uri.getValue().toString().toLowerCase();
3201                 if ( getImageMap().containsKey( uri_str ) ) {
3202                     final BufferedImage bi = getImageMap().get( uri_str );
3203                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
3204                         double scaling_factor = 1;
3205                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
3206                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
3207                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
3208                         }
3209                         // y = y - ( 0.9 * getYdistance() );
3210                         final double hs = bi.getHeight() * scaling_factor;
3211                         double ws = bi.getWidth() * scaling_factor + offset;
3212                         final double my_y = y - ( 0.5 * hs );
3213                         final int x_w = ( int ) ( x + ws + 0.5 );
3214                         final int y_h = ( int ) ( my_y + hs + 0.5 );
3215                         if ( ( x_w - x > 7 ) && ( y_h - my_y > 7 ) ) {
3216                             g.drawImage( bi,
3217                                          ( int ) ( x + 0.5 + offset ),
3218                                          ( int ) ( my_y + 0.5 ),
3219                                          x_w,
3220                                          y_h,
3221                                          0,
3222                                          0,
3223                                          bi.getWidth(),
3224                                          bi.getHeight(),
3225                                          null );
3226                             ws += 8;
3227                         }
3228                         else {
3229                             ws = 0.0;
3230                         }
3231                         offset = ws;
3232                     }
3233                 }
3234             }
3235         }
3236         return offset;
3237     }
3238
3239     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
3240                                                   final PhylogenyNode node,
3241                                                   final boolean to_pdf,
3242                                                   final boolean to_graphics_file,
3243                                                   final boolean radial_labels,
3244                                                   final double ur_angle,
3245                                                   final boolean is_in_found_nodes ) {
3246         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
3247             return;
3248         }
3249         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3250             g.setColor( Color.BLACK );
3251         }
3252         else if ( is_in_found_nodes ) {
3253             g.setColor( getTreeColorSet().getFoundColor() );
3254         }
3255         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3256             g.setColor( getTaxonomyBasedColor( node ) );
3257         }
3258         else {
3259             g.setColor( getTreeColorSet().getSequenceColor() );
3260         }
3261         _sb.setLength( 0 );
3262         _sb.append( " " );
3263         if ( node.getNodeData().isHasTaxonomy()
3264                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3265                         .isShowTaxonomyCommonNames() ) ) {
3266             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3267             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3268                 _sb.append( taxonomy.getTaxonomyCode() );
3269                 _sb.append( " " );
3270             }
3271             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3272                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3273                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3274                     _sb.append( taxonomy.getScientificName() );
3275                     _sb.append( " (" );
3276                     _sb.append( taxonomy.getCommonName() );
3277                     _sb.append( ") " );
3278                 }
3279                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3280                     _sb.append( taxonomy.getScientificName() );
3281                     _sb.append( " " );
3282                 }
3283                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3284                     _sb.append( taxonomy.getCommonName() );
3285                     _sb.append( " " );
3286                 }
3287             }
3288             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3289                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3290                     _sb.append( taxonomy.getScientificName() );
3291                     _sb.append( " " );
3292                 }
3293             }
3294             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3295                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3296                     _sb.append( taxonomy.getCommonName() );
3297                     _sb.append( " " );
3298                 }
3299             }
3300         }
3301         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3302             _sb.append( " [" );
3303             _sb.append( node.getAllExternalDescendants().size() );
3304             _sb.append( "]" );
3305         }
3306         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3307             if ( _sb.length() > 0 ) {
3308                 _sb.append( " " );
3309             }
3310             _sb.append( node.getName() );
3311         }
3312         if ( node.getNodeData().isHasSequence() ) {
3313             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3314                 if ( _sb.length() > 0 ) {
3315                     _sb.append( " " );
3316                 }
3317                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3318                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3319                     _sb.append( ":" );
3320                 }
3321                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3322             }
3323             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3324                 if ( _sb.length() > 0 ) {
3325                     _sb.append( " " );
3326                 }
3327                 _sb.append( node.getNodeData().getSequence().getName() );
3328             }
3329         }
3330         g.setFont( getTreeFontSet().getLargeFont() );
3331         if ( is_in_found_nodes ) {
3332             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3333         }
3334         if ( _sb.length() > 1 ) {
3335             final String sb_str = _sb.toString();
3336             double m = 0;
3337             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3338                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
3339             }
3340             else {
3341                 m = ( float ) ( ur_angle % TWO_PI );
3342             }
3343             _at = g.getTransform();
3344             boolean need_to_reset = false;
3345             final float x_coord = node.getXcoord();
3346             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
3347             if ( radial_labels ) {
3348                 need_to_reset = true;
3349                 boolean left = false;
3350                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3351                     m -= PI;
3352                     left = true;
3353                 }
3354                 g.rotate( m, x_coord, node.getYcoord() );
3355                 if ( left ) {
3356                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
3357                 }
3358             }
3359             else {
3360                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3361                     need_to_reset = true;
3362                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
3363                 }
3364             }
3365             TreePanel.drawString( sb_str, x_coord, y_coord, g );
3366             if ( need_to_reset ) {
3367                 g.setTransform( _at );
3368             }
3369         }
3370     }
3371
3372     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
3373         if ( node.isCollapse() ) {
3374             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3375                 paintCollapsedNode( g, node, false, false, false );
3376             }
3377             return;
3378         }
3379         if ( isInFoundNodes( node ) ) {
3380             g.setColor( getTreeColorSet().getFoundColor() );
3381             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
3382         }
3383         float new_x = 0;
3384         if ( !node.isExternal() && !node.isCollapse() ) {
3385             boolean first_child = true;
3386             float y2 = 0.0f;
3387             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3388             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3389                 final PhylogenyNode child_node = node.getChildNode( i );
3390                 int factor_x;
3391                 if ( !isUniformBranchLengthsForCladogram() ) {
3392                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3393                 }
3394                 else {
3395                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3396                 }
3397                 if ( first_child ) {
3398                     first_child = false;
3399                     y2 = node.getYSecondary()
3400                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
3401                                     .getNumberOfExternalNodes() ) );
3402                 }
3403                 else {
3404                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3405                 }
3406                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
3407                 new_x = x2 + node.getXSecondary();
3408                 final float diff_y = node.getYSecondary() - y2;
3409                 final float diff_x = node.getXSecondary() - new_x;
3410                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
3411                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
3412                 }
3413                 child_node.setXSecondary( new_x );
3414                 child_node.setYSecondary( y2 );
3415                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3416             }
3417         }
3418     }
3419
3420     final private void paintNodeRectangular( final Graphics2D g,
3421                                              final PhylogenyNode node,
3422                                              final boolean to_pdf,
3423                                              final boolean dynamically_hide,
3424                                              final int dynamic_hiding_factor,
3425                                              final boolean to_graphics_file ) {
3426         final boolean is_in_found_nodes = isInFoundNodes( node );
3427         if ( node.isCollapse() ) {
3428             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3429                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3430             }
3431             return;
3432         }
3433         if ( node.isExternal() ) {
3434             ++_external_node_index;
3435         }
3436         // Confidence values
3437         if ( getControlPanel().isShowBootstrapValues()
3438                 && !node.isExternal()
3439                 && !node.isRoot()
3440                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
3441                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3442                 && node.getBranchData().isHasConfidences() ) {
3443             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
3444         }
3445         // Draw a line to root:
3446         if ( node.isRoot() && _phylogeny.isRooted() ) {
3447             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
3448         }
3449         float new_x = 0;
3450         float new_x_min = Float.MAX_VALUE;
3451         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
3452         float min_dist = 1.5f;
3453         if ( !disallow_shortcutting ) {
3454             //   System.out.println( dynamic_hiding_factor );
3455             if ( dynamic_hiding_factor > 4000 ) {
3456                 min_dist = 4;
3457             }
3458             else if ( dynamic_hiding_factor > 1000 ) {
3459                 min_dist = 3;
3460             }
3461             else if ( dynamic_hiding_factor > 100 ) {
3462                 min_dist = 2;
3463             }
3464         }
3465         if ( !node.isExternal() && !node.isCollapse() ) {
3466             boolean first_child = true;
3467             float y2 = 0.0f;
3468             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3469             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3470                 final PhylogenyNode child_node = node.getChildNode( i );
3471                 int factor_x;
3472                 if ( !isUniformBranchLengthsForCladogram() ) {
3473                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3474                 }
3475                 else {
3476                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3477                 }
3478                 if ( first_child ) {
3479                     first_child = false;
3480                     y2 = node.getYcoord()
3481                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3482                 }
3483                 else {
3484                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
3485                 }
3486                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
3487                 new_x = x2 + node.getXcoord();
3488                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
3489                     new_x_min = x2;
3490                 }
3491                 final float diff_y = node.getYcoord() - y2;
3492                 final float diff_x = node.getXcoord() - new_x;
3493                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
3494                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
3495                     paintBranchRectangular( g,
3496                                             node.getXcoord(),
3497                                             new_x,
3498                                             node.getYcoord(),
3499                                             y2,
3500                                             child_node,
3501                                             to_pdf,
3502                                             to_graphics_file );
3503                 }
3504                 child_node.setXcoord( new_x );
3505                 child_node.setYcoord( y2 );
3506                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
3507             }
3508             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
3509         }
3510         if ( dynamically_hide
3511                 && !is_in_found_nodes
3512                 && ( ( node.isExternal() && ( _external_node_index % dynamic_hiding_factor != 1 ) ) || ( !node
3513                         .isExternal() && ( ( new_x_min < 20 ) || ( _y_distance * node.getNumberOfExternalNodes() < getTreeFontSet()._fm_large
3514                         .getHeight() ) ) ) ) ) {
3515             return;
3516         }
3517         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3518         paintNodeWithRenderableData( g, node, to_graphics_file, to_pdf );
3519     }
3520
3521     final private void paintNodeWithRenderableData( final Graphics2D g,
3522                                                     final PhylogenyNode node,
3523                                                     final boolean to_graphics_file,
3524                                                     final boolean to_pdf ) {
3525         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
3526             return;
3527         }
3528         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
3529             return;
3530         }
3531         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3532                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
3533             RenderableDomainArchitecture rds = null;
3534             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
3535                 try {
3536                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
3537                 }
3538                 catch ( final ClassCastException cce ) {
3539                     cce.printStackTrace();
3540                     return;
3541                 }
3542                 rds.setRenderingHeight( 6 );
3543                 int x = 0;
3544                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( node ).length() > 0 ) ) {
3545                     x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( node ) + " " );
3546                 }
3547                 if ( getControlPanel().isShowGeneNames() ) {
3548                     x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName() + " " );
3549                 }
3550                 if ( getControlPanel().isShowGeneSymbols() ) {
3551                     x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getSymbol() + " " );
3552                 }
3553                 if ( getControlPanel().isShowSequenceAcc() ) {
3554                     x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getAccession()
3555                             .toString()
3556                             + " " );
3557                 }
3558                 if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3559                     x += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
3560                 }
3561                 rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
3562             }
3563         }
3564         //////////////
3565         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
3566                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
3567             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
3568                                                                          getStatisticsForExpressionValues(),
3569                                                                          getConfiguration() );
3570             int x = 0;
3571             PhylogenyNode my_node = node;
3572             if ( !getControlPanel().isDrawPhylogram() ) {
3573                 my_node = getPhylogeny().getFirstExternalNode();
3574             }
3575             if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
3576                 x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
3577             }
3578             if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
3579                 x += getTreeFontSet()._fm_large.stringWidth( my_node.getName() + " " );
3580             }
3581             rv.render( my_node.getXcoord() + x, node.getYcoord() - 5, g, this, to_pdf );
3582         }
3583         //////////////
3584     }
3585
3586     final private void paintOvRectangle( final Graphics2D g ) {
3587         final float w_ratio = ( float ) getWidth() / getVisibleRect().width;
3588         final float h_ratio = ( float ) getHeight() / getVisibleRect().height;
3589         final float x_ratio = ( float ) getWidth() / getVisibleRect().x;
3590         final float y_ratio = ( float ) getHeight() / getVisibleRect().y;
3591         final float width = getOvMaxWidth() / w_ratio;
3592         final float height = getOvMaxHeight() / h_ratio;
3593         final float x = getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / x_ratio;
3594         final float y = getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / y_ratio;
3595         g.setColor( getTreeColorSet().getFoundColor() );
3596         getOvRectangle().setRect( x, y, width, height );
3597         if ( ( width < 6 ) && ( height < 6 ) ) {
3598             drawRectFilled( x, y, 6, 6, g );
3599             getOvVirtualRectangle().setRect( x, y, 6, 6 );
3600         }
3601         else if ( width < 6 ) {
3602             drawRectFilled( x, y, 6, height, g );
3603             getOvVirtualRectangle().setRect( x, y, 6, height );
3604         }
3605         else if ( height < 6 ) {
3606             drawRectFilled( x, y, width, 6, g );
3607             getOvVirtualRectangle().setRect( x, y, width, 6 );
3608         }
3609         else {
3610             drawRect( x, y, width, height, g );
3611             if ( isInOvRect() ) {
3612                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
3613             }
3614             getOvVirtualRectangle().setRect( x, y, width, height );
3615         }
3616     }
3617
3618     final void paintPhylogeny( final Graphics2D g,
3619                                final boolean to_pdf,
3620                                final boolean to_graphics_file,
3621                                final int graphics_file_width,
3622                                final int graphics_file_height,
3623                                final int graphics_file_x,
3624                                final int graphics_file_y ) {
3625         if ( _control_panel.isShowSequenceRelations() ) {
3626             _query_sequence = _control_panel.getSelectedQuerySequence();
3627         }
3628         // Color the background
3629         if ( !to_pdf ) {
3630             final Rectangle r = getVisibleRect();
3631             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
3632                 g.setColor( getTreeColorSet().getBackgroundColor() );
3633                 if ( !to_graphics_file ) {
3634                     g.fill( r );
3635                 }
3636                 else {
3637                     if ( getOptions().isPrintBlackAndWhite() ) {
3638                         g.setColor( Color.WHITE );
3639                     }
3640                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3641                 }
3642             }
3643             else {
3644                 if ( !to_graphics_file ) {
3645                     g.setPaint( new GradientPaint( r.x, r.y, getTreeColorSet().getBackgroundColor(), r.x, r.y
3646                             + r.height, getTreeColorSet().getBackgroundColorGradientBottom() ) );
3647                     g.fill( r );
3648                 }
3649                 else {
3650                     g.setPaint( new GradientPaint( graphics_file_x,
3651                                                    graphics_file_y,
3652                                                    getTreeColorSet().getBackgroundColor(),
3653                                                    graphics_file_x,
3654                                                    graphics_file_y + graphics_file_height,
3655                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
3656                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3657                 }
3658             }
3659             g.setStroke( new BasicStroke( 1 ) );
3660         }
3661         else {
3662             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
3663         }
3664         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3665                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3666             _external_node_index = 0;
3667             // Position starting X of tree
3668             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/) {
3669                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
3670             }
3671             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
3672                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE + ( _phylogeny.getRoot()
3673                         .getDistanceToParent() * getXcorrectionFactor() ) ) );
3674             }
3675             else {
3676                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
3677             }
3678             // Position starting Y of tree
3679             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
3680                     + ( TreePanel.MOVE / 2.0f ) );
3681             final int dynamic_hiding_factor = ( int ) ( getTreeFontSet()._fm_large.getHeight() / ( 1.5 * getYdistance() ) );
3682             if ( getControlPanel().isDynamicallyHideData() ) {
3683                 if ( dynamic_hiding_factor > 1 ) {
3684                     getControlPanel().setDynamicHidingIsOn( true );
3685                 }
3686                 else {
3687                     getControlPanel().setDynamicHidingIsOn( false );
3688                 }
3689             }
3690             if ( _nodes_in_preorder == null ) {
3691                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
3692                 int i = 0;
3693                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3694                     _nodes_in_preorder[ i++ ] = it.next();
3695                 }
3696             }
3697             //final PhylogenyNodeIterator it;
3698             //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3699             //    paintNodeRectangular( g, it.next(), to_pdf, getControlPanel().isDynamicallyHideData()
3700             //            && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3701             //}
3702             for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3703                 paintNodeRectangular( g, _nodes_in_preorder[ i ], to_pdf, getControlPanel().isDynamicallyHideData()
3704                         && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3705             }
3706             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
3707                 if ( !( to_graphics_file || to_pdf ) ) {
3708                     paintScale( g,
3709                                 getVisibleRect().x,
3710                                 getVisibleRect().y + getVisibleRect().height,
3711                                 to_pdf,
3712                                 to_graphics_file );
3713                 }
3714                 else {
3715                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3716                 }
3717             }
3718             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3719                 paintPhylogenyLite( g );
3720             }
3721         }
3722         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
3723             if ( getControlPanel().getDynamicallyHideData() != null ) {
3724                 getControlPanel().setDynamicHidingIsOn( false );
3725             }
3726             final double angle = getStartingAngle();
3727             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
3728             _dynamic_hiding_factor = 0;
3729             if ( getControlPanel().isDynamicallyHideData() ) {
3730                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3731                         .getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
3732             }
3733             if ( getControlPanel().getDynamicallyHideData() != null ) {
3734                 if ( _dynamic_hiding_factor > 1 ) {
3735                     getControlPanel().setDynamicHidingIsOn( true );
3736                 }
3737                 else {
3738                     getControlPanel().setDynamicHidingIsOn( false );
3739                 }
3740             }
3741             paintUnrooted( _phylogeny.getRoot(),
3742                            angle,
3743                            ( float ) ( angle + 2 * Math.PI ),
3744                            radial_labels,
3745                            g,
3746                            to_pdf,
3747                            to_graphics_file );
3748             if ( getOptions().isShowScale() ) {
3749                 if ( !( to_graphics_file || to_pdf ) ) {
3750                     paintScale( g,
3751                                 getVisibleRect().x,
3752                                 getVisibleRect().y + getVisibleRect().height,
3753                                 to_pdf,
3754                                 to_graphics_file );
3755                 }
3756                 else {
3757                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3758                 }
3759             }
3760             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3761                 g.setColor( getTreeColorSet().getOvColor() );
3762                 paintUnrootedLite( _phylogeny.getRoot(),
3763                                    angle,
3764                                    angle + 2 * Math.PI,
3765                                    g,
3766                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
3767                 paintOvRectangle( g );
3768             }
3769         }
3770         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3771             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() ) / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
3772             final int d = radius + MOVE + getLongestExtNodeInfo();
3773             _dynamic_hiding_factor = 0;
3774             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
3775                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3776                         .getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
3777             }
3778             if ( getControlPanel().getDynamicallyHideData() != null ) {
3779                 if ( _dynamic_hiding_factor > 1 ) {
3780                     getControlPanel().setDynamicHidingIsOn( true );
3781                 }
3782                 else {
3783                     getControlPanel().setDynamicHidingIsOn( false );
3784                 }
3785             }
3786             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
3787             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3788                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
3789                         : getOvMaxWidth() / 2 );
3790                 double x_scale = 1.0;
3791                 double y_scale = 1.0;
3792                 int x_pos = getVisibleRect().x + getOvXPosition();
3793                 int y_pos = getVisibleRect().y + getOvYPosition();
3794                 if ( getWidth() > getHeight() ) {
3795                     x_scale = ( double ) getHeight() / getWidth();
3796                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
3797                 }
3798                 else {
3799                     y_scale = ( double ) getWidth() / getHeight();
3800                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
3801                 }
3802                 _at = g.getTransform();
3803                 g.scale( x_scale, y_scale );
3804                 paintCircularLite( _phylogeny,
3805                                    getStartingAngle(),
3806                                    x_pos + radius_ov,
3807                                    y_pos + radius_ov,
3808                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo() / ( getVisibleRect().width / getOvRectangle()
3809                                            .getWidth() ) ) ),
3810                                    g );
3811                 g.setTransform( _at );
3812                 paintOvRectangle( g );
3813             }
3814         }
3815     }
3816
3817     final private void paintPhylogenyLite( final Graphics2D g ) {
3818         //System.out.println( getVisibleRect().x + " " + getVisibleRect().y );
3819         _phylogeny
3820                 .getRoot()
3821                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
3822                         .getWidth() ) ) ) );
3823         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
3824         //final PhylogenyNodeIterator it;
3825         //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3826         //    paintNodeLite( g, it.next() );
3827         //}
3828         for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3829             paintNodeLite( g, _nodes_in_preorder[ i ] );
3830         }
3831         paintOvRectangle( g );
3832     }
3833
3834     /**
3835      * Paint the root branch. (Differs from others because it will always be a
3836      * single horizontal line).
3837      * @param to_graphics_file 
3838      * 
3839      * @return new x1 value
3840      */
3841     final private void paintRootBranch( final Graphics2D g,
3842                                         final float x1,
3843                                         final float y1,
3844                                         final PhylogenyNode root,
3845                                         final boolean to_pdf,
3846                                         final boolean to_graphics_file ) {
3847         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
3848         float d = getXdistance();
3849         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
3850             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
3851         }
3852         if ( d < MIN_ROOT_LENGTH ) {
3853             d = MIN_ROOT_LENGTH;
3854         }
3855         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
3856             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
3857         }
3858         else {
3859             final double w = PhylogenyMethods.getBranchWidthValue( root );
3860             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
3861         }
3862         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file, isInFoundNodes( root ) );
3863     }
3864
3865     final private void paintScale( final Graphics2D g,
3866                                    int x1,
3867                                    int y1,
3868                                    final boolean to_pdf,
3869                                    final boolean to_graphics_file ) {
3870         x1 += MOVE;
3871         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
3872         y1 -= 12;
3873         final int y2 = y1 - 8;
3874         final int y3 = y1 - 4;
3875         g.setFont( getTreeFontSet().getSmallFont() );
3876         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3877             g.setColor( Color.BLACK );
3878         }
3879         else {
3880             g.setColor( getTreeColorSet().getBranchLengthColor() );
3881         }
3882         drawLine( x1, y1, x1, y2, g );
3883         drawLine( x2, y1, x2, y2, g );
3884         drawLine( x1, y3, x2, y3, g );
3885         if ( getScaleLabel() != null ) {
3886             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
3887         }
3888     }
3889
3890     final private int paintTaxonomy( final Graphics2D g,
3891                                      final PhylogenyNode node,
3892                                      final boolean is_in_found_nodes,
3893                                      final boolean to_pdf,
3894                                      final boolean to_graphics_file,
3895                                      final double x_shift ) {
3896         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3897         g.setFont( getTreeFontSet().getLargeItalicFont() );
3898         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3899             g.setColor( Color.BLACK );
3900         }
3901         else if ( is_in_found_nodes ) {
3902             g.setFont( getTreeFontSet().getLargeItalicFont().deriveFont( TreeFontSet.BOLD_AND_ITALIC ) );
3903             g.setColor( getTreeColorSet().getFoundColor() );
3904         }
3905         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3906             g.setColor( getTaxonomyBasedColor( node ) );
3907         }
3908         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3909                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3910             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3911         }
3912         else if ( to_pdf ) {
3913             g.setColor( Color.BLACK );
3914         }
3915         else {
3916             g.setColor( getTreeColorSet().getTaxonomyColor() );
3917         }
3918         final double start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
3919         final double start_y = node.getYcoord()
3920                 + ( getTreeFontSet()._fm_large.getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0 ) );
3921         _sb.setLength( 0 );
3922         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3923             _sb.append( taxonomy.getTaxonomyCode() );
3924             _sb.append( " " );
3925         }
3926         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3927             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3928                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3929                 if ( getOptions().isAbbreviateScientificTaxonNames()
3930                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3931                     abbreviateScientificName( taxonomy.getScientificName() );
3932                 }
3933                 else {
3934                     _sb.append( taxonomy.getScientificName() );
3935                 }
3936                 _sb.append( " (" );
3937                 _sb.append( taxonomy.getCommonName() );
3938                 _sb.append( ") " );
3939             }
3940             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3941                 if ( getOptions().isAbbreviateScientificTaxonNames()
3942                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3943                     abbreviateScientificName( taxonomy.getScientificName() );
3944                 }
3945                 else {
3946                     _sb.append( taxonomy.getScientificName() );
3947                 }
3948                 _sb.append( " " );
3949             }
3950             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3951                 _sb.append( taxonomy.getCommonName() );
3952                 _sb.append( " " );
3953             }
3954         }
3955         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3956             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3957                 if ( getOptions().isAbbreviateScientificTaxonNames()
3958                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3959                     abbreviateScientificName( taxonomy.getScientificName() );
3960                 }
3961                 else {
3962                     _sb.append( taxonomy.getScientificName() );
3963                 }
3964                 _sb.append( " " );
3965             }
3966         }
3967         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3968             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3969                 _sb.append( taxonomy.getCommonName() );
3970                 _sb.append( " " );
3971             }
3972         }
3973         final String label = _sb.toString();
3974         /* GUILHEM_BEG */
3975         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
3976                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
3977             // invert font color and background color to show that this is the query sequence
3978             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
3979                                                                                                           false,
3980                                                                                                           false ) )
3981                     .getBounds();
3982             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
3983             g.setColor( getTreeColorSet().getBackgroundColor() );
3984         }
3985         /* GUILHEM_END */
3986         TreePanel.drawString( label, start_x, start_y, g );
3987         if ( is_in_found_nodes ) {
3988             return getTreeFontSet()._fm_large_italic_bold.stringWidth( label );
3989         }
3990         else {
3991             return getTreeFontSet()._fm_large_italic.stringWidth( label );
3992         }
3993     }
3994
3995     private void abbreviateScientificName( final String sn ) {
3996         final String[] a = sn.split( "\\s+" );
3997         _sb.append( a[ 0 ].substring( 0, 1 ) );
3998         _sb.append( a[ 1 ].substring( 0, 2 ) );
3999         if ( a.length > 2 ) {
4000             for( int i = 2; i < a.length; i++ ) {
4001                 _sb.append( " " );
4002                 _sb.append( a[ i ] );
4003             }
4004         }
4005     }
4006
4007     final private void paintUnrooted( final PhylogenyNode n,
4008                                       final double low_angle,
4009                                       final double high_angle,
4010                                       final boolean radial_labels,
4011                                       final Graphics2D g,
4012                                       final boolean to_pdf,
4013                                       final boolean to_graphics_file ) {
4014         if ( n.isRoot() ) {
4015             n.setXcoord( getWidth() / 2 );
4016             n.setYcoord( getHeight() / 2 );
4017         }
4018         if ( n.isExternal() ) {
4019             paintNodeDataUnrootedCirc( g,
4020                                        n,
4021                                        to_pdf,
4022                                        to_graphics_file,
4023                                        radial_labels,
4024                                        ( high_angle + low_angle ) / 2,
4025                                        isInFoundNodes( n ) );
4026             return;
4027         }
4028         final float num_enclosed = n.getNumberOfExternalNodes();
4029         final float x = n.getXcoord();
4030         final float y = n.getYcoord();
4031         double current_angle = low_angle;
4032         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4033         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4034         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4035         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4036         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4037             final PhylogenyNode desc = n.getChildNode( i );
4038             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4039             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4040             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4041             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4042             //     continue;
4043             // }
4044             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4045             //    continue;
4046             //}
4047             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4048             //    continue;
4049             // }
4050             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4051             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4052             float length;
4053             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4054                 if ( desc.getDistanceToParent() < 0 ) {
4055                     length = 0;
4056                 }
4057                 else {
4058                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4059                 }
4060             }
4061             else {
4062                 length = getUrtFactor();
4063             }
4064             final double mid_angle = current_angle + arc_size / 2;
4065             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4066             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4067             desc.setXcoord( new_x );
4068             desc.setYcoord( new_y );
4069             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
4070             current_angle += arc_size;
4071             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
4072             drawLine( x, y, new_x, new_y, g );
4073             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file, isInFoundNodes( desc ) );
4074         }
4075         if ( n.isRoot() ) {
4076             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file, isInFoundNodes( n ) );
4077         }
4078     }
4079
4080     final private void paintUnrootedLite( final PhylogenyNode n,
4081                                           final double low_angle,
4082                                           final double high_angle,
4083                                           final Graphics2D g,
4084                                           final float urt_ov_factor ) {
4085         if ( n.isRoot() ) {
4086             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / 2 );
4087             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / 2 );
4088             n.setXSecondary( x_pos );
4089             n.setYSecondary( y_pos );
4090         }
4091         if ( n.isExternal() ) {
4092             return;
4093         }
4094         final float num_enclosed = n.getNumberOfExternalNodes();
4095         final float x = n.getXSecondary();
4096         final float y = n.getYSecondary();
4097         double current_angle = low_angle;
4098         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4099             final PhylogenyNode desc = n.getChildNode( i );
4100             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4101             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4102             float length;
4103             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4104                 if ( desc.getDistanceToParent() < 0 ) {
4105                     length = 0;
4106                 }
4107                 else {
4108                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
4109                 }
4110             }
4111             else {
4112                 length = urt_ov_factor;
4113             }
4114             final double mid_angle = current_angle + arc_size / 2;
4115             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4116             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4117             desc.setXSecondary( new_x );
4118             desc.setYSecondary( new_y );
4119             if ( isInFoundNodes( desc ) ) {
4120                 g.setColor( getTreeColorSet().getFoundColor() );
4121                 drawRectFilled( desc.getXSecondary() - 1, desc.getYSecondary() - 1, 3, 3, g );
4122                 g.setColor( getTreeColorSet().getOvColor() );
4123             }
4124             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
4125             current_angle += arc_size;
4126             drawLine( x, y, new_x, new_y, g );
4127         }
4128     }
4129
4130     final private void pasteSubtree( final PhylogenyNode node ) {
4131         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4132             errorMessageNoCutCopyPasteInUnrootedDisplay();
4133             return;
4134         }
4135         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
4136             JOptionPane.showMessageDialog( this,
4137                                            "No tree in buffer (need to copy or cut a subtree first)",
4138                                            "Attempt to paste with empty buffer",
4139                                            JOptionPane.ERROR_MESSAGE );
4140             return;
4141         }
4142         final String label = getASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
4143         final Object[] options = { "As sibling", "As descendant", "Cancel" };
4144         final int r = JOptionPane.showOptionDialog( this,
4145                                                     "How to paste subtree" + label + "?",
4146                                                     "Paste Subtree",
4147                                                     JOptionPane.CLOSED_OPTION,
4148                                                     JOptionPane.QUESTION_MESSAGE,
4149                                                     null,
4150                                                     options,
4151                                                     options[ 2 ] );
4152         boolean paste_as_sibling = true;
4153         if ( r == 1 ) {
4154             paste_as_sibling = false;
4155         }
4156         else if ( r != 0 ) {
4157             return;
4158         }
4159         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
4160         buffer_phy.setAllNodesToNotCollapse();
4161         buffer_phy.preOrderReId();
4162         buffer_phy.setRooted( true );
4163         boolean need_to_show_whole = false;
4164         if ( paste_as_sibling ) {
4165             if ( node.isRoot() ) {
4166                 JOptionPane.showMessageDialog( this,
4167                                                "Cannot paste sibling to root",
4168                                                "Attempt to paste sibling to root",
4169                                                JOptionPane.ERROR_MESSAGE );
4170                 return;
4171             }
4172             buffer_phy.addAsSibling( node );
4173         }
4174         else {
4175             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
4176                 need_to_show_whole = true;
4177                 _phylogeny = buffer_phy;
4178             }
4179             else {
4180                 buffer_phy.addAsChild( node );
4181             }
4182         }
4183         if ( getCopiedAndPastedNodes() == null ) {
4184             setCopiedAndPastedNodes( new HashSet<Integer>() );
4185         }
4186         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
4187         final Set<Integer> node_ids = new HashSet<Integer>( nodes.size() );
4188         for( final PhylogenyNode n : nodes ) {
4189             node_ids.add( n.getId() );
4190         }
4191         node_ids.add( node.getId() );
4192         getCopiedAndPastedNodes().addAll( node_ids );
4193         _nodes_in_preorder = null;
4194         _phylogeny.externalNodesHaveChanged();
4195         _phylogeny.hashIDs();
4196         _phylogeny.recalculateNumberOfExternalDescendants( true );
4197         resetNodeIdToDistToLeafMap();
4198         setEdited( true );
4199         if ( need_to_show_whole ) {
4200             getControlPanel().showWhole();
4201         }
4202         repaint();
4203     }
4204
4205     final public int print( final Graphics g, final PageFormat page_format, final int page_index )
4206             throws PrinterException {
4207         if ( page_index > 0 ) {
4208             return ( NO_SUCH_PAGE );
4209         }
4210         else {
4211             final Graphics2D g2d = ( Graphics2D ) g;
4212             g2d.translate( page_format.getImageableX(), page_format.getImageableY() );
4213             // Turn off double buffering !?
4214             paintPhylogeny( g2d, true, false, 0, 0, 0, 0 );
4215             // Turn double buffering back on !?
4216             return ( PAGE_EXISTS );
4217         }
4218     }
4219
4220     final void recalculateMaxDistanceToRoot() {
4221         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
4222     }
4223
4224     /**
4225      * Remove all edit-node frames
4226      */
4227     final void removeAllEditNodeJFrames() {
4228         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
4229             if ( _node_frames[ i ] != null ) {
4230                 _node_frames[ i ].dispose();
4231                 _node_frames[ i ] = null;
4232             }
4233         }
4234         _node_frame_index = 0;
4235     }
4236
4237     /**
4238      * Remove a node-edit frame.
4239      */
4240     final void removeEditNodeFrame( final int i ) {
4241         _node_frame_index--;
4242         _node_frames[ i ] = null;
4243         if ( i < _node_frame_index ) {
4244             for( int j = 0; j < _node_frame_index - 1; j++ ) {
4245                 _node_frames[ j ] = _node_frames[ j + 1 ];
4246             }
4247             _node_frames[ _node_frame_index ] = null;
4248         }
4249     }
4250
4251     final void reRoot( final PhylogenyNode node ) {
4252         if ( !getPhylogeny().isRerootable() ) {
4253             JOptionPane.showMessageDialog( this,
4254                                            "This is not rerootable",
4255                                            "Not rerootable",
4256                                            JOptionPane.WARNING_MESSAGE );
4257             return;
4258         }
4259         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4260             JOptionPane.showMessageDialog( this,
4261                                            "Cannot reroot in unrooted display type",
4262                                            "Attempt to reroot tree in unrooted display",
4263                                            JOptionPane.WARNING_MESSAGE );
4264             return;
4265         }
4266         getPhylogeny().reRoot( node );
4267         getPhylogeny().recalculateNumberOfExternalDescendants( true );
4268         resetNodeIdToDistToLeafMap();
4269         _nodes_in_preorder = null;
4270         resetPreferredSize();
4271         getMainPanel().adjustJScrollPane();
4272         repaint();
4273         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4274             getControlPanel().showWhole();
4275         }
4276     }
4277
4278     final void resetNodeIdToDistToLeafMap() {
4279         _nodeid_dist_to_leaf = new HashMap<Integer, Short>();
4280     }
4281
4282     final void resetPreferredSize() {
4283         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
4284             return;
4285         }
4286         int x = 0;
4287         int y = 0;
4288         y = TreePanel.MOVE
4289                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
4290         if ( getControlPanel().isDrawPhylogram() ) {
4291             x = TreePanel.MOVE
4292                     + getLongestExtNodeInfo()
4293                     + ForesterUtil
4294                             .roundToInt( ( getXcorrectionFactor() * getPhylogeny().getHeight() ) + getXdistance() );
4295         }
4296         else {
4297             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4298                 x = TreePanel.MOVE
4299                         + getLongestExtNodeInfo()
4300                         + ForesterUtil.roundToInt( getXdistance()
4301                                 * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
4302             }
4303             else {
4304                 x = TreePanel.MOVE
4305                         + getLongestExtNodeInfo()
4306                         + ForesterUtil.roundToInt( getXdistance()
4307                                 * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
4308             }
4309         }
4310         setPreferredSize( new Dimension( x, y ) );
4311     }
4312
4313     public final void setArrowCursor() {
4314         setCursor( ARROW_CURSOR );
4315         repaint();
4316     }
4317
4318     final void setControlPanel( final ControlPanel atv_control ) {
4319         _control_panel = atv_control;
4320     }
4321
4322     final private void setCopiedAndPastedNodes( final Set<Integer> nodeIds ) {
4323         getMainPanel().setCopiedAndPastedNodes( nodeIds );
4324     }
4325
4326     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
4327         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
4328     }
4329
4330     public final void setEdited( final boolean edited ) {
4331         _edited = edited;
4332     }
4333
4334     final void setFoundNodes( final Set<Integer> found_nodes ) {
4335         _found_nodes = found_nodes;
4336     }
4337
4338     final private void setInOv( final boolean in_ov ) {
4339         _in_ov = in_ov;
4340     }
4341
4342     final void setInOvRect( final boolean in_ov_rect ) {
4343         _in_ov_rect = in_ov_rect;
4344     }
4345
4346     final void setLargeFonts() {
4347         getTreeFontSet().largeFonts();
4348     }
4349
4350     final void setLastMouseDragPointX( final float x ) {
4351         _last_drag_point_x = x;
4352     }
4353
4354     final void setLastMouseDragPointY( final float y ) {
4355         _last_drag_point_y = y;
4356     }
4357
4358     final void setLongestExtNodeInfo( final int i ) {
4359         _longest_ext_node_info = i;
4360     }
4361
4362     final void setMediumFonts() {
4363         getTreeFontSet().mediumFonts();
4364     }
4365
4366     final private void setOvMaxHeight( final float ov_max_height ) {
4367         _ov_max_height = ov_max_height;
4368     }
4369
4370     final private void setOvMaxWidth( final float ov_max_width ) {
4371         _ov_max_width = ov_max_width;
4372     }
4373
4374     final void setOvOn( final boolean ov_on ) {
4375         _ov_on = ov_on;
4376     }
4377
4378     final private void setOvXcorrectionFactor( final float f ) {
4379         _ov_x_correction_factor = f;
4380     }
4381
4382     final private void setOvXDistance( final float ov_x_distance ) {
4383         _ov_x_distance = ov_x_distance;
4384     }
4385
4386     final private void setOvXPosition( final int ov_x_position ) {
4387         _ov_x_position = ov_x_position;
4388     }
4389
4390     final private void setOvYDistance( final float ov_y_distance ) {
4391         _ov_y_distance = ov_y_distance;
4392     }
4393
4394     final private void setOvYPosition( final int ov_y_position ) {
4395         _ov_y_position = ov_y_position;
4396     }
4397
4398     final private void setOvYStart( final int ov_y_start ) {
4399         _ov_y_start = ov_y_start;
4400     }
4401
4402     /**
4403      * Set parameters for printing the displayed tree
4404      * 
4405      * @param x
4406      * @param y
4407      */
4408     public final void setParametersForPainting( final int x, final int y, final boolean recalc_longest_ext_node_info ) {
4409         // updateStyle(); not needed?
4410         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4411             initNodeData();
4412             if ( recalc_longest_ext_node_info ) {
4413                 calculateLongestExtNodeInfo();
4414             }
4415             int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4416             final int max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4417             if ( ext_nodes == 1 ) {
4418                 ext_nodes = max_depth;
4419                 if ( ext_nodes < 1 ) {
4420                     ext_nodes = 1;
4421                 }
4422             }
4423             updateOvSizes();
4424             float xdist = 0;
4425             float ov_xdist = 0;
4426             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4427                 xdist = ( float ) ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( ext_nodes + 3.0 ) );
4428                 ov_xdist = ( float ) ( getOvMaxWidth() / ( ext_nodes + 3.0 ) );
4429             }
4430             else {
4431                 xdist = ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( max_depth + 1 ) );
4432                 ov_xdist = ( getOvMaxWidth() / ( max_depth + 1 ) );
4433             }
4434             float ydist = ( float ) ( ( y - TreePanel.MOVE ) / ( ext_nodes * 2.0 ) );
4435             if ( xdist < 0.0 ) {
4436                 xdist = 0.0f;
4437             }
4438             if ( ov_xdist < 0.0 ) {
4439                 ov_xdist = 0.0f;
4440             }
4441             if ( ydist < 0.0 ) {
4442                 ydist = 0.0f;
4443             }
4444             setXdistance( xdist );
4445             setYdistance( ydist );
4446             setOvXDistance( ov_xdist );
4447             final double height = _phylogeny.getHeight();
4448             if ( height > 0 ) {
4449                 final float corr = ( float ) ( ( x - TreePanel.MOVE - getLongestExtNodeInfo() - getXdistance() ) / height );
4450                 setXcorrectionFactor( corr > 0 ? corr : 0 );
4451                 final float ov_corr = ( float ) ( ( getOvMaxWidth() - getOvXDistance() ) / height );
4452                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
4453             }
4454             else {
4455                 setXcorrectionFactor( 0 );
4456                 setOvXcorrectionFactor( 0 );
4457             }
4458             _circ_max_depth = max_depth;
4459             setUpUrtFactor();
4460         }
4461     }
4462
4463     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
4464         _graphics_type = graphics_type;
4465         setTextAntialias();
4466     }
4467
4468     final private void setScaleDistance( final double scale_distance ) {
4469         _scale_distance = scale_distance;
4470     }
4471
4472     final private void setScaleLabel( final String scale_label ) {
4473         _scale_label = scale_label;
4474     }
4475
4476     final void setSmallFonts() {
4477         getTreeFontSet().smallFonts();
4478     }
4479
4480     final void setStartingAngle( final double starting_angle ) {
4481         _urt_starting_angle = starting_angle;
4482     }
4483
4484     final void setSuperTinyFonts() {
4485         getTreeFontSet().superTinyFonts();
4486     }
4487
4488     final void setTextAntialias() {
4489         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4490             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
4491                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
4492             }
4493             else {
4494                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
4495             }
4496         }
4497         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
4498             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) {
4499                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4500             }
4501             else {
4502                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
4503             }
4504             try {
4505                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING,
4506                                       RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
4507             }
4508             catch ( final Throwable e ) {
4509                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
4510             }
4511         }
4512         else {
4513             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
4514             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4515         }
4516     }
4517
4518     final void setTinyFonts() {
4519         getTreeFontSet().tinyFonts();
4520     }
4521
4522     /**
4523      * Set a phylogeny tree.
4524      * 
4525      * @param t
4526      *            an instance of a Phylogeny
4527      */
4528     public final void setTree( final Phylogeny t ) {
4529         _nodes_in_preorder = null;
4530         _phylogeny = t;
4531     }
4532
4533     final void setTreeFile( final File treefile ) {
4534         _treefile = treefile;
4535     }
4536
4537     final private void setUpUrtFactor() {
4538         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
4539                 : getVisibleRect().height;
4540         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4541             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
4542         }
4543         else {
4544             final int max_depth = _circ_max_depth;
4545             if ( max_depth > 0 ) {
4546                 setUrtFactor( d / ( 2 * max_depth ) );
4547             }
4548             else {
4549                 setUrtFactor( d / 2 );
4550             }
4551         }
4552         setUrtFactorOv( getUrtFactor() );
4553     }
4554
4555     final private void setUrtFactor( final float urt_factor ) {
4556         _urt_factor = urt_factor;
4557     }
4558
4559     final private void setUrtFactorOv( final float urt_factor_ov ) {
4560         _urt_factor_ov = urt_factor_ov;
4561     }
4562
4563     public final void setWaitCursor() {
4564         setCursor( WAIT_CURSOR );
4565         repaint();
4566     }
4567
4568     final void setXcorrectionFactor( final float f ) {
4569         _x_correction_factor = f;
4570     }
4571
4572     final void setXdistance( final float x ) {
4573         _x_distance = x;
4574     }
4575
4576     final void setYdistance( final float y ) {
4577         _y_distance = y;
4578     }
4579
4580     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
4581         try {
4582             if ( ( node.getName().length() > 0 )
4583                     || ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
4584                     || ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) )
4585                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
4586                     || node.getBranchData().isHasConfidences() ) {
4587                 _popup_buffer.setLength( 0 );
4588                 short lines = 0;
4589                 if ( node.getName().length() > 0 ) {
4590                     lines++;
4591                     _popup_buffer.append( node.getName() );
4592                 }
4593                 if ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
4594                     lines++;
4595                     boolean enc_data = false;
4596                     final Taxonomy tax = node.getNodeData().getTaxonomy();
4597                     if ( _popup_buffer.length() > 0 ) {
4598                         _popup_buffer.append( "\n" );
4599                     }
4600                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4601                         _popup_buffer.append( "[" );
4602                         _popup_buffer.append( tax.getTaxonomyCode() );
4603                         _popup_buffer.append( "]" );
4604                         enc_data = true;
4605                     }
4606                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4607                         if ( enc_data ) {
4608                             _popup_buffer.append( " " );
4609                         }
4610                         _popup_buffer.append( tax.getScientificName() );
4611                         enc_data = true;
4612                     }
4613                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
4614                         if ( enc_data ) {
4615                             _popup_buffer.append( " (" );
4616                         }
4617                         else {
4618                             _popup_buffer.append( "(" );
4619                         }
4620                         _popup_buffer.append( tax.getCommonName() );
4621                         _popup_buffer.append( ")" );
4622                         enc_data = true;
4623                     }
4624                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
4625                         if ( enc_data ) {
4626                             _popup_buffer.append( " (" );
4627                         }
4628                         else {
4629                             _popup_buffer.append( "(" );
4630                         }
4631                         _popup_buffer.append( tax.getAuthority() );
4632                         _popup_buffer.append( ")" );
4633                         enc_data = true;
4634                     }
4635                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
4636                         if ( enc_data ) {
4637                             _popup_buffer.append( " [" );
4638                         }
4639                         else {
4640                             _popup_buffer.append( "[" );
4641                         }
4642                         _popup_buffer.append( tax.getRank() );
4643                         _popup_buffer.append( "]" );
4644                         enc_data = true;
4645                     }
4646                     if ( tax.getSynonyms().size() > 0 ) {
4647                         if ( enc_data ) {
4648                             _popup_buffer.append( " " );
4649                         }
4650                         _popup_buffer.append( "[" );
4651                         int counter = 1;
4652                         for( final String syn : tax.getSynonyms() ) {
4653                             if ( !ForesterUtil.isEmpty( syn ) ) {
4654                                 enc_data = true;
4655                                 _popup_buffer.append( syn );
4656                                 if ( counter < tax.getSynonyms().size() ) {
4657                                     _popup_buffer.append( ", " );
4658                                 }
4659                             }
4660                             counter++;
4661                         }
4662                         _popup_buffer.append( "]" );
4663                     }
4664                     if ( !enc_data ) {
4665                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
4666                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
4667                                 _popup_buffer.append( "[" );
4668                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
4669                                 _popup_buffer.append( "] " );
4670                             }
4671                             _popup_buffer.append( tax.getIdentifier().getValue() );
4672                         }
4673                     }
4674                 }
4675                 if ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) ) {
4676                     lines++;
4677                     boolean enc_data = false;
4678                     if ( _popup_buffer.length() > 0 ) {
4679                         _popup_buffer.append( "\n" );
4680                     }
4681                     final Sequence seq = node.getNodeData().getSequence();
4682                     if ( seq.getAccession() != null ) {
4683                         _popup_buffer.append( "[" );
4684                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
4685                             _popup_buffer.append( seq.getAccession().getSource() );
4686                             _popup_buffer.append( ":" );
4687                         }
4688                         _popup_buffer.append( seq.getAccession().getValue() );
4689                         _popup_buffer.append( "]" );
4690                         enc_data = true;
4691                     }
4692                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
4693                         if ( enc_data ) {
4694                             _popup_buffer.append( " [" );
4695                         }
4696                         else {
4697                             _popup_buffer.append( "[" );
4698                         }
4699                         _popup_buffer.append( seq.getSymbol() );
4700                         _popup_buffer.append( "]" );
4701                         enc_data = true;
4702                     }
4703                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
4704                         if ( enc_data ) {
4705                             _popup_buffer.append( " " );
4706                         }
4707                         _popup_buffer.append( seq.getName() );
4708                     }
4709                 }
4710                 if ( node.getNodeData().isHasDate() ) {
4711                     lines++;
4712                     if ( _popup_buffer.length() > 0 ) {
4713                         _popup_buffer.append( "\n" );
4714                     }
4715                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
4716                 }
4717                 if ( node.getNodeData().isHasDistribution() ) {
4718                     lines++;
4719                     if ( _popup_buffer.length() > 0 ) {
4720                         _popup_buffer.append( "\n" );
4721                     }
4722                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
4723                 }
4724                 if ( node.getBranchData().isHasConfidences() ) {
4725                     final List<Confidence> confs = node.getBranchData().getConfidences();
4726                     for( final Confidence confidence : confs ) {
4727                         lines++;
4728                         if ( _popup_buffer.length() > 0 ) {
4729                             _popup_buffer.append( "\n" );
4730                         }
4731                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
4732                             _popup_buffer.append( "[" );
4733                             _popup_buffer.append( confidence.getType() );
4734                             _popup_buffer.append( "] " );
4735                         }
4736                         _popup_buffer
4737                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
4738                                                                                           getOptions()
4739                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4740                     }
4741                 }
4742                 if ( node.getNodeData().isHasProperties() ) {
4743                     final PropertiesMap properties = node.getNodeData().getProperties();
4744                     for( final String ref : properties.getPropertyRefs() ) {
4745                         _popup_buffer.append( "\n" );
4746                         final Property p = properties.getProperty( ref );
4747                         _popup_buffer.append( getPartAfterColon( p.getRef() ) );
4748                         _popup_buffer.append( "=" );
4749                         _popup_buffer.append( p.getValue() );
4750                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
4751                             _popup_buffer.append( getPartAfterColon( p.getUnit() ) );
4752                         }
4753                     }
4754                 }
4755                 if ( _popup_buffer.length() > 0 ) {
4756                     if ( !getConfiguration().isUseNativeUI() ) {
4757                         _rollover_popup
4758                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
4759                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
4760                         if ( isInFoundNodes( node ) ) {
4761                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
4762                         }
4763                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4764                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
4765                         }
4766                         else {
4767                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
4768                         }
4769                     }
4770                     else {
4771                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
4772                     }
4773                     _rollover_popup.setText( _popup_buffer.toString() );
4774                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
4775                                                                                   _rollover_popup,
4776                                                                                   e.getLocationOnScreen().x + 10,
4777                                                                                   e.getLocationOnScreen().y
4778                                                                                           - ( lines * 20 ) );
4779                     _node_desc_popup.show();
4780                 }
4781             }
4782         }
4783         catch ( final Exception ex ) {
4784             // Do nothing.
4785         }
4786     }
4787
4788     final private void showNodeEditFrame( final PhylogenyNode n ) {
4789         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4790             // pop up edit box for single node
4791             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
4792             _node_frame_index++;
4793         }
4794         else {
4795             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4796         }
4797     }
4798
4799     final private void showNodeFrame( final PhylogenyNode n ) {
4800         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4801             // pop up edit box for single node
4802             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
4803             _node_frame_index++;
4804         }
4805         else {
4806             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4807         }
4808     }
4809
4810     final void subTree( final PhylogenyNode node ) {
4811         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4812             JOptionPane.showMessageDialog( this,
4813                                            "Cannot get a sub/super tree in unrooted display",
4814                                            "Attempt to get sub/super tree in unrooted display",
4815                                            JOptionPane.WARNING_MESSAGE );
4816             return;
4817         }
4818         if ( node.isExternal() ) {
4819             JOptionPane.showMessageDialog( this,
4820                                            "Cannot get a subtree of a external node",
4821                                            "Attempt to get subtree of external node",
4822                                            JOptionPane.WARNING_MESSAGE );
4823             return;
4824         }
4825         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
4826             JOptionPane.showMessageDialog( this,
4827                                            "Cannot get a subtree of the root node",
4828                                            "Attempt to get subtree of root node",
4829                                            JOptionPane.WARNING_MESSAGE );
4830             return;
4831         }
4832         _nodes_in_preorder = null;
4833         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
4834             _sub_phylogenies[ _subtree_index ] = _phylogeny;
4835             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
4836             ++_subtree_index;
4837             _phylogeny = subTree( node, _phylogeny );
4838             updateSubSuperTreeButton();
4839         }
4840         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
4841             superTree();
4842         }
4843         _main_panel.getControlPanel().showWhole();
4844         repaint();
4845     }
4846
4847     final boolean isCurrentTreeIsSubtree() {
4848         return ( _subtree_index > 0 );
4849     }
4850
4851     final private static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
4852         final Phylogeny new_phy = new Phylogeny();
4853         new_phy.setRooted( true );
4854         new_phy.setName( source_phy.getName() );
4855         new_phy.setDescription( source_phy.getDescription() );
4856         new_phy.setType( source_phy.getType() );
4857         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
4858         new_phy.setConfidence( source_phy.getConfidence() );
4859         new_phy.setIdentifier( source_phy.getIdentifier() );
4860         new_phy.setRoot( new_root.copyNodeDataShallow() );
4861         int i = 0;
4862         for( final PhylogenyNode n : new_root.getDescendants() ) {
4863             new_phy.getRoot().setChildNode( i++, n );
4864         }
4865         return new_phy;
4866     }
4867
4868     final void superTree() {
4869         _nodes_in_preorder = null;
4870         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
4871         for( final PhylogenyNode n : temp_root.getDescendants() ) {
4872             n.setParent( temp_root );
4873         }
4874         _sub_phylogenies[ _subtree_index ] = null;
4875         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
4876         _phylogeny = _sub_phylogenies[ --_subtree_index ];
4877         updateSubSuperTreeButton();
4878     }
4879
4880     final void swap( final PhylogenyNode node ) {
4881         if ( !node.isExternal() ) {
4882             _phylogeny.swapChildren( node );
4883             _nodes_in_preorder = null;
4884         }
4885         repaint();
4886     }
4887
4888     final private void switchDisplaygetPhylogenyGraphicsType() {
4889         switch ( getPhylogenyGraphicsType() ) {
4890             case RECTANGULAR:
4891                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4892                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4893                 break;
4894             case EURO_STYLE:
4895                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4896                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4897                 break;
4898             case ROUNDED:
4899                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4900                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4901                 break;
4902             case CURVED:
4903                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4904                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4905                 break;
4906             case TRIANGULAR:
4907                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4908                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4909                 break;
4910             case CONVEX:
4911                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4912                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4913                 break;
4914             case UNROOTED:
4915                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4916                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4917                 break;
4918             case CIRCULAR:
4919                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4920                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4921                 break;
4922             default:
4923                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
4924         }
4925         if ( getControlPanel().getDynamicallyHideData() != null ) {
4926             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4927                 getControlPanel().getDynamicallyHideData().setEnabled( false );
4928             }
4929             else {
4930                 getControlPanel().getDynamicallyHideData().setEnabled( true );
4931             }
4932         }
4933         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
4934             getControlPanel().setDrawPhylogramEnabled( true );
4935         }
4936         else {
4937             getControlPanel().setDrawPhylogramEnabled( false );
4938         }
4939         if ( getMainPanel().getMainFrame() == null ) {
4940             // Must be "E" applet version.
4941             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
4942                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
4943         }
4944         else {
4945             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
4946         }
4947     }
4948
4949     final void taxColor() {
4950         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4951             return;
4952         }
4953         setWaitCursor();
4954         AptxUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
4955         _control_panel.setColorBranches( true );
4956         if ( _control_panel.getColorBranchesCb() != null ) {
4957             _control_panel.getColorBranchesCb().setSelected( true );
4958         }
4959         setArrowCursor();
4960         repaint();
4961     }
4962
4963     final void updateOvSettings() {
4964         switch ( getOptions().getOvPlacement() ) {
4965             case LOWER_LEFT:
4966                 setOvXPosition( OV_BORDER );
4967                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
4968                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
4969                 break;
4970             case LOWER_RIGHT:
4971                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
4972                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
4973                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
4974                 break;
4975             case UPPER_RIGHT:
4976                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
4977                 setOvYPosition( OV_BORDER );
4978                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
4979                 break;
4980             default:
4981                 setOvXPosition( OV_BORDER );
4982                 setOvYPosition( OV_BORDER );
4983                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
4984                 break;
4985         }
4986     }
4987
4988     final void updateOvSizes() {
4989         if ( ( getWidth() > 1.05 * getVisibleRect().width ) || ( getHeight() > 1.05 * getVisibleRect().height ) ) {
4990             setOvOn( true );
4991             float l = getLongestExtNodeInfo();
4992             final float w_ratio = getOvMaxWidth() / getWidth();
4993             l *= w_ratio;
4994             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4995             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
4996             float ov_xdist = 0;
4997             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4998                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
4999             }
5000             else {
5001                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
5002             }
5003             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
5004             if ( ov_xdist < 0.0 ) {
5005                 ov_xdist = 0.0f;
5006             }
5007             if ( ydist < 0.0 ) {
5008                 ydist = 0.0f;
5009             }
5010             setOvXDistance( ov_xdist );
5011             final double height = _phylogeny.getHeight();
5012             if ( height > 0 ) {
5013                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
5014                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
5015             }
5016             else {
5017                 setOvXcorrectionFactor( 0 );
5018             }
5019         }
5020         else {
5021             setOvOn( false );
5022         }
5023     }
5024
5025     final void updateSubSuperTreeButton() {
5026         if ( _subtree_index < 1 ) {
5027             getControlPanel().deactivateButtonToReturnToSuperTree();
5028         }
5029         else {
5030             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
5031         }
5032     }
5033
5034     final void zoomInDomainStructure() {
5035         if ( _domain_structure_width < 2000 ) {
5036             _domain_structure_width *= 1.2;
5037         }
5038     }
5039
5040     final void zoomOutDomainStructure() {
5041         if ( _domain_structure_width > 20 ) {
5042             _domain_structure_width *= 0.8;
5043         }
5044     }
5045
5046     final private static void drawString( final int i, final double x, final double y, final Graphics2D g ) {
5047         g.drawString( String.valueOf( i ), ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5048     }
5049
5050     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5051         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5052     }
5053
5054     final private static boolean isSequenceEmpty( final Sequence seq ) {
5055         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
5056                 && ForesterUtil.isEmpty( seq.getSymbol() );
5057     }
5058
5059     final private static boolean isTaxonomyEmpty( final Taxonomy tax ) {
5060         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
5061                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
5062                 .getSynonyms().isEmpty() );
5063     }
5064
5065     final private static boolean plusPressed( final int key_code ) {
5066         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5067                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5068     }
5069
5070     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
5071         _statistics_for_vector_data = statistics_for_expression_values;
5072     }
5073
5074     DescriptiveStatistics getStatisticsForExpressionValues() {
5075         return _statistics_for_vector_data;
5076     }
5077
5078     final private class SubtreeColorizationActionListener implements ActionListener {
5079
5080         JColorChooser _chooser;
5081         PhylogenyNode _node;
5082
5083         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5084             _chooser = chooser;
5085             _node = node;
5086         }
5087
5088         @Override
5089         public void actionPerformed( final ActionEvent e ) {
5090             final Color c = _chooser.getColor();
5091             if ( c != null ) {
5092                 colorizeSubtree( c, _node );
5093             }
5094         }
5095     }
5096
5097     public synchronized void setImageMap( final Hashtable<String, BufferedImage> image_map ) {
5098         getMainPanel().setImageMap( image_map );
5099     }
5100
5101     public synchronized Hashtable<String, BufferedImage> getImageMap() {
5102         return getMainPanel().getImageMap();
5103     }
5104 }