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