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