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.NodeFill;
96 import org.forester.archaeopteryx.Options.NodeShape;
97 import org.forester.archaeopteryx.Options.PHYLOGENY_GRAPHICS_TYPE;
98 import org.forester.archaeopteryx.phylogeny.data.RenderableDomainArchitecture;
99 import org.forester.archaeopteryx.phylogeny.data.RenderableVector;
100 import org.forester.archaeopteryx.tools.Blast;
101 import org.forester.archaeopteryx.tools.ImageLoader;
102 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
103 import org.forester.phylogeny.Phylogeny;
104 import org.forester.phylogeny.PhylogenyMethods;
105 import org.forester.phylogeny.PhylogenyNode;
106 import org.forester.phylogeny.data.Annotation;
107 import org.forester.phylogeny.data.BranchColor;
108 import org.forester.phylogeny.data.Confidence;
109 import org.forester.phylogeny.data.Event;
110 import org.forester.phylogeny.data.PhylogenyData;
111 import org.forester.phylogeny.data.PropertiesMap;
112 import org.forester.phylogeny.data.Property;
113 import org.forester.phylogeny.data.Sequence;
114 import org.forester.phylogeny.data.SequenceRelation;
115 import org.forester.phylogeny.data.Taxonomy;
116 import org.forester.phylogeny.data.Uri;
117 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
118 import org.forester.phylogeny.iterators.PreorderTreeIterator;
119 import org.forester.util.BasicDescriptiveStatistics;
120 import org.forester.util.DescriptiveStatistics;
121 import org.forester.util.ForesterConstants;
122 import org.forester.util.ForesterUtil;
123
124 public final class TreePanel extends JPanel implements ActionListener, MouseWheelListener, Printable {
125
126     private static final float              PI                                = ( float ) ( Math.PI );
127     private static final double             TWO_PI                            = 2 * Math.PI;
128     private static final float              ONEHALF_PI                        = ( float ) ( 1.5 * Math.PI );
129     private static final float              HALF_PI                           = ( float ) ( Math.PI / 2.0 );
130     private static final float              ANGLE_ROTATION_UNIT               = ( float ) ( Math.PI / 32 );
131     private static final short              OV_BORDER                         = 10;
132     final static Cursor                     CUT_CURSOR                        = Cursor.getPredefinedCursor( Cursor.CROSSHAIR_CURSOR );
133     final static Cursor                     MOVE_CURSOR                       = Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR );
134     final static Cursor                     ARROW_CURSOR                      = Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR );
135     final static Cursor                     HAND_CURSOR                       = Cursor.getPredefinedCursor( Cursor.HAND_CURSOR );
136     final static Cursor                     WAIT_CURSOR                       = Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR );
137     private final static long               serialVersionUID                  = -978349745916505029L;
138     private final static int                EURO_D                            = 10;
139     private final static String             NODE_POPMENU_NODE_CLIENT_PROPERTY = "node";
140     private final static int                MIN_ROOT_LENGTH                   = 3;
141     private final static int                MAX_SUBTREES                      = 100;
142     private final static int                MAX_NODE_FRAMES                   = 10;
143     private final static int                MOVE                              = 20;
144     private final static NumberFormat       FORMATTER_CONFIDENCE;
145     private final static NumberFormat       FORMATTER_BRANCH_LENGTH;
146     private final static int                WIGGLE                            = 2;
147     private final int                       _half_box_size_plus_wiggle;
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 int                             _circ_num_ext_nodes;
210     private PhylogenyNode                   _root;
211     final private Arc2D                     _arc                              = new Arc2D.Double();
212     final private HashMap<Integer, Double>  _urt_nodeid_angle_map             = new HashMap<Integer, Double>();
213     final private HashMap<Integer, Integer> _urt_nodeid_index_map             = new HashMap<Integer, 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         _box_size = tjp.getOptions().getDefaultNodeShapeSize();
272         _half_box_size = tjp.getOptions().getDefaultNodeShapeSize() / 2;
273         _half_box_size_plus_wiggle = _half_box_size + WIGGLE;
274         init();
275         // if ( !_phylogeny.isEmpty() ) {
276         _phylogeny.recalculateNumberOfExternalDescendants( true );
277         checkForVectorProperties( _phylogeny );
278         // }
279         setBackground( getTreeColorSet().getBackgroundColor() );
280         final MouseListener mouse_listener = new MouseListener( this );
281         addMouseListener( mouse_listener );
282         addMouseMotionListener( mouse_listener );
283         addMouseWheelListener( this );
284         calculateScaleDistance();
285         FORMATTER_CONFIDENCE.setMaximumFractionDigits( configuration.getNumberOfDigitsAfterCommaForConfidenceValues() );
286         FORMATTER_BRANCH_LENGTH.setMaximumFractionDigits( configuration
287                 .getNumberOfDigitsAfterCommaForBranchLengthValues() );
288     }
289
290     public void checkForVectorProperties( final Phylogeny phy ) {
291         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
292         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
293             final PhylogenyNode node = iter.next();
294             if ( node.getNodeData().getProperties() != null ) {
295                 final PropertiesMap pm = node.getNodeData().getProperties();
296                 final double[] vector = new double[ pm.getProperties().size() ];
297                 int counter = 0;
298                 for( final String ref : pm.getProperties().keySet() ) {
299                     if ( ref.startsWith( PhyloXmlUtil.VECTOR_PROPERTY_REF ) ) {
300                         final Property p = pm.getProperty( ref );
301                         final String value_str = p.getValue();
302                         final String index_str = ref
303                                 .substring( PhyloXmlUtil.VECTOR_PROPERTY_REF.length(), ref.length() );
304                         double d = -100;
305                         try {
306                             d = Double.parseDouble( value_str );
307                         }
308                         catch ( final NumberFormatException e ) {
309                             JOptionPane.showMessageDialog( this, "Could not parse \"" + value_str
310                                     + "\" into a decimal value", "Problem with Vector Data", JOptionPane.ERROR_MESSAGE );
311                             return;
312                         }
313                         int i = -1;
314                         try {
315                             i = Integer.parseInt( index_str );
316                         }
317                         catch ( final NumberFormatException e ) {
318                             JOptionPane.showMessageDialog( this,
319                                                            "Could not parse \"" + index_str
320                                                                    + "\" into index for vector data",
321                                                            "Problem with Vector Data",
322                                                            JOptionPane.ERROR_MESSAGE );
323                             return;
324                         }
325                         if ( i < 0 ) {
326                             JOptionPane.showMessageDialog( this,
327                                                            "Attempt to use negative index for vector data",
328                                                            "Problem with Vector Data",
329                                                            JOptionPane.ERROR_MESSAGE );
330                             return;
331                         }
332                         vector[ i ] = d;
333                         ++counter;
334                         stats.addValue( d );
335                     }
336                 }
337                 final List<Double> vector_l = new ArrayList<Double>( counter );
338                 for( int i = 0; i < counter; ++i ) {
339                     vector_l.add( vector[ i ] );
340                 }
341                 node.getNodeData().setVector( vector_l );
342             }
343         }
344         if ( stats.getN() > 0 ) {
345             _statistics_for_vector_data = stats;
346         }
347     }
348
349     final public void actionPerformed( final ActionEvent e ) {
350         int index;
351         boolean done = false;
352         final JMenuItem node_popup_menu_item = ( JMenuItem ) e.getSource();
353         for( index = 0; ( index < _node_popup_menu_items.length ) && !done; index++ ) {
354             // NOTE: index corresponds to the indices of click-to options
355             // in the control panel.
356             if ( node_popup_menu_item == _node_popup_menu_items[ index ] ) {
357                 // Set this as the new default click-to action
358                 _main_panel.getControlPanel().setClickToAction( index );
359                 final PhylogenyNode node = ( PhylogenyNode ) _node_popup_menu
360                         .getClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY );
361                 handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
362                 done = true;
363             }
364         }
365         repaint();
366         requestFocusInWindow();
367     }
368
369     final private void addEmptyNode( final PhylogenyNode node ) {
370         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
371             errorMessageNoCutCopyPasteInUnrootedDisplay();
372             return;
373         }
374         final String label = getASimpleTextRepresentationOfANode( node );
375         String msg = "";
376         if ( ForesterUtil.isEmpty( label ) ) {
377             msg = "How to add the new, empty node?";
378         }
379         else {
380             msg = "How to add the new, empty node to node" + label + "?";
381         }
382         final Object[] options = { "As sibling", "As descendant", "Cancel" };
383         final int r = JOptionPane.showOptionDialog( this,
384                                                     msg,
385                                                     "Addition of Empty New Node",
386                                                     JOptionPane.CLOSED_OPTION,
387                                                     JOptionPane.QUESTION_MESSAGE,
388                                                     null,
389                                                     options,
390                                                     options[ 2 ] );
391         boolean add_as_sibling = true;
392         if ( r == 1 ) {
393             add_as_sibling = false;
394         }
395         else if ( r != 0 ) {
396             return;
397         }
398         final Phylogeny phy = new Phylogeny();
399         phy.setRoot( new PhylogenyNode() );
400         phy.setRooted( true );
401         if ( add_as_sibling ) {
402             if ( node.isRoot() ) {
403                 JOptionPane.showMessageDialog( this,
404                                                "Cannot add sibling to root",
405                                                "Attempt to add sibling to root",
406                                                JOptionPane.ERROR_MESSAGE );
407                 return;
408             }
409             phy.addAsSibling( node );
410         }
411         else {
412             phy.addAsChild( node );
413         }
414         _nodes_in_preorder = null;
415         _phylogeny.externalNodesHaveChanged();
416         _phylogeny.hashIDs();
417         _phylogeny.recalculateNumberOfExternalDescendants( true );
418         resetNodeIdToDistToLeafMap();
419         setEdited( true );
420         repaint();
421     }
422
423     final private void assignGraphicsForBranchWithColorForParentBranch( final PhylogenyNode node,
424                                                                         final boolean is_vertical,
425                                                                         final Graphics g,
426                                                                         final boolean to_pdf,
427                                                                         final boolean to_graphics_file ) {
428         final NodeClickAction action = _control_panel.getActionWhenNodeClicked();
429         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
430             g.setColor( Color.BLACK );
431         }
432         else if ( ( ( action == NodeClickAction.COPY_SUBTREE ) || ( action == NodeClickAction.CUT_SUBTREE )
433                 || ( action == NodeClickAction.DELETE_NODE_OR_SUBTREE ) || ( action == NodeClickAction.PASTE_SUBTREE ) || ( action == NodeClickAction.ADD_NEW_NODE ) )
434                 && ( getCutOrCopiedTree() != null )
435                 && ( getCopiedAndPastedNodes() != null )
436                 && !to_pdf
437                 && !to_graphics_file && getCopiedAndPastedNodes().contains( node.getId() ) ) {
438             g.setColor( getTreeColorSet().getFoundColor() );
439         }
440         else if ( getControlPanel().isColorBranches() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
441             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
442         }
443         else if ( to_pdf ) {
444             g.setColor( getTreeColorSet().getBranchColorForPdf() );
445         }
446         else {
447             g.setColor( getTreeColorSet().getBranchColor() );
448         }
449     }
450
451     final Color getGraphicsForNodeBoxWithColorForParentBranch( final PhylogenyNode node ) {
452         if ( getControlPanel().isColorBranches() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
453             return ( PhylogenyMethods.getBranchColorValue( node ) );
454         }
455         else {
456             return ( getTreeColorSet().getBranchColor() );
457         }
458     }
459
460     final private void blast( final PhylogenyNode node ) {
461         if ( !isCanBlast( node ) ) {
462             JOptionPane.showMessageDialog( this,
463                                            "No sequence information present",
464                                            "Cannot Blast",
465                                            JOptionPane.WARNING_MESSAGE );
466             return;
467         }
468         if ( node.getNodeData().isHasSequence() ) {
469             String name = "";
470             if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) {
471                 name = node.getNodeData().getSequence().getName();
472             }
473             else if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) {
474                 name = node.getNodeData().getSequence().getSymbol();
475             }
476             else if ( node.getNodeData().getSequence().getAccession() != null ) {
477                 name = node.getNodeData().getSequence().getAccession().getValue();
478             }
479             if ( !ForesterUtil.isEmpty( name ) ) {
480                 try {
481                     System.out.println( "trying: " + name );
482                     final Blast s = new Blast();
483                     s.go( name );
484                 }
485                 catch ( final Exception e ) {
486                     e.printStackTrace();
487                 }
488             }
489         }
490     }
491
492     final void calcMaxDepth() {
493         if ( _phylogeny != null ) {
494             _circ_max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
495         }
496     }
497
498     /**
499      * Calculate the length of the distance between the given node and its
500      * parent.
501      * 
502      * @param node
503      * @param ext_node_x
504      * @factor
505      * @return the distance value
506      */
507     final private float calculateBranchLengthToParent( final PhylogenyNode node, final float factor ) {
508         if ( getControlPanel().isDrawPhylogram() ) {
509             if ( node.getDistanceToParent() < 0.0 ) {
510                 return 0.0f;
511             }
512             return ( float ) ( getXcorrectionFactor() * node.getDistanceToParent() );
513         }
514         else {
515             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
516                 return getXdistance();
517             }
518             return getXdistance() * factor;
519         }
520     }
521
522     final private Color calculateColorForAnnotation( final PhylogenyData ann ) {
523         Color c = getTreeColorSet().getAnnotationColor();
524         if ( getControlPanel().isColorAccordingToAnnotation() && ( getControlPanel().getAnnotationColors() != null ) ) {
525             c = getControlPanel().getAnnotationColors().get( ann.asSimpleText().toString() );
526             if ( c == null ) {
527                 c = getTreeColorSet().getAnnotationColor();
528             }
529         }
530         return c;
531     }
532
533     final void calculateLongestExtNodeInfo() {
534         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
535             return;
536         }
537         int longest = 20;
538         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
539             int sum = 0;
540             if ( node.isCollapse() ) {
541                 continue;
542             }
543             if ( getControlPanel().isShowNodeNames() ) {
544                 sum += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
545             }
546             if ( node.getNodeData().isHasSequence() ) {
547                 if ( getControlPanel().isShowSequenceAcc()
548                         && ( node.getNodeData().getSequence().getAccession() != null ) ) {
549                     sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getAccession()
550                             .getValue()
551                             + " " );
552                 }
553                 if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
554                     sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName() + " " );
555                 }
556                 if ( getControlPanel().isShowGeneSymbols()
557                         && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
558                     sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getSymbol() + " " );
559                 }
560                 if ( getControlPanel().isShowAnnotation()
561                         && ( node.getNodeData().getSequence().getAnnotations() != null )
562                         && !node.getNodeData().getSequence().getAnnotations().isEmpty() ) {
563                     sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getAnnotation( 0 )
564                             .asSimpleText()
565                             + " " );
566                 }
567             }
568             if ( node.getNodeData().isHasTaxonomy() ) {
569                 final Taxonomy tax = node.getNodeData().getTaxonomy();
570                 if ( getControlPanel().isShowTaxonomyCode() && !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
571                     sum += getTreeFontSet()._fm_large_italic.stringWidth( tax.getTaxonomyCode() + " " );
572                 }
573                 if ( getControlPanel().isShowTaxonomyScientificNames()
574                         && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
575                     sum += getTreeFontSet()._fm_large_italic.stringWidth( tax.getScientificName() + " " );
576                 }
577                 if ( getControlPanel().isShowTaxonomyCommonNames() && !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
578                     sum += getTreeFontSet()._fm_large_italic.stringWidth( tax.getCommonName() + " ()" );
579                 }
580             }
581             if ( getControlPanel().isShowBinaryCharacters() && node.getNodeData().isHasBinaryCharacters() ) {
582                 sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getBinaryCharacters()
583                         .getGainedCharactersAsStringBuffer().toString() );
584             }
585             if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
586                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
587                 sum += ( ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture() )
588                         .getRenderingSize().getWidth();
589             }
590             if ( sum >= Constants.EXT_NODE_INFO_LENGTH_MAX ) {
591                 setLongestExtNodeInfo( Constants.EXT_NODE_INFO_LENGTH_MAX );
592                 return;
593             }
594             if ( sum > longest ) {
595                 longest = sum;
596             }
597         }
598         if ( longest >= Constants.EXT_NODE_INFO_LENGTH_MAX ) {
599             setLongestExtNodeInfo( Constants.EXT_NODE_INFO_LENGTH_MAX );
600         }
601         else {
602             setLongestExtNodeInfo( longest );
603         }
604     }
605
606     final private float calculateOvBranchLengthToParent( final PhylogenyNode node, final int factor ) {
607         if ( getControlPanel().isDrawPhylogram() ) {
608             if ( node.getDistanceToParent() < 0.0 ) {
609                 return 0.0f;
610             }
611             return ( float ) ( getOvXcorrectionFactor() * node.getDistanceToParent() );
612         }
613         else {
614             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
615                 return getOvXDistance();
616             }
617             return getOvXDistance() * factor;
618         }
619     }
620
621     final void calculateScaleDistance() {
622         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
623             return;
624         }
625         final double height = getMaxDistanceToRoot();
626         if ( height > 0 ) {
627             if ( ( height <= 0.5 ) ) {
628                 setScaleDistance( 0.01 );
629             }
630             else if ( height <= 5.0 ) {
631                 setScaleDistance( 0.1 );
632             }
633             else if ( height <= 50.0 ) {
634                 setScaleDistance( 1 );
635             }
636             else if ( height <= 500.0 ) {
637                 setScaleDistance( 10 );
638             }
639             else {
640                 setScaleDistance( 100 );
641             }
642         }
643         else {
644             setScaleDistance( 0.0 );
645         }
646         String scale_label = String.valueOf( getScaleDistance() );
647         if ( !ForesterUtil.isEmpty( _phylogeny.getDistanceUnit() ) ) {
648             scale_label += " [" + _phylogeny.getDistanceUnit() + "]";
649         }
650         setScaleLabel( scale_label );
651     }
652
653     final Color calculateTaxonomyBasedColor( final Taxonomy tax ) {
654         String species = tax.getTaxonomyCode();
655         if ( ForesterUtil.isEmpty( species ) ) {
656             species = tax.getScientificName();
657             if ( ForesterUtil.isEmpty( species ) ) {
658                 species = tax.getCommonName();
659             }
660         }
661         if ( ForesterUtil.isEmpty( species ) ) {
662             return getTreeColorSet().getTaxonomyColor();
663         }
664         // Look in species hash
665         Color c = getControlPanel().getSpeciesColors().get( species );
666         if ( c == null ) {
667             c = Util.calculateColorFromString( species );
668             getControlPanel().getSpeciesColors().put( species, c );
669         }
670         return c;
671     }
672
673     final private void cannotOpenBrowserWarningMessage( final String type_type ) {
674         JOptionPane.showMessageDialog( this,
675                                        "Cannot launch web browser for " + type_type + " data of this node",
676                                        "Cannot launch web browser",
677                                        JOptionPane.WARNING_MESSAGE );
678     }
679
680     /**
681      * Collapse the tree from the given node
682      * 
683      * @param node
684      *            a PhylogenyNode
685      */
686     final void collapse( final PhylogenyNode node ) {
687         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
688             JOptionPane.showMessageDialog( this,
689                                            "Cannot collapse in unrooted display type",
690                                            "Attempt to collapse in unrooted display",
691                                            JOptionPane.WARNING_MESSAGE );
692             return;
693         }
694         if ( !node.isExternal() && !node.isRoot() ) {
695             final boolean collapse = !node.isCollapse();
696             Util.collapseSubtree( node, collapse );
697             _phylogeny.recalculateNumberOfExternalDescendants( true );
698             resetNodeIdToDistToLeafMap();
699             calculateLongestExtNodeInfo();
700             _nodes_in_preorder = null;
701             resetPreferredSize();
702             updateOvSizes();
703             _main_panel.adjustJScrollPane();
704             repaint();
705         }
706     }
707
708     final void collapseSpeciesSpecificSubtrees() {
709         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
710             return;
711         }
712         setWaitCursor();
713         Util.collapseSpeciesSpecificSubtrees( _phylogeny );
714         _phylogeny.recalculateNumberOfExternalDescendants( true );
715         resetNodeIdToDistToLeafMap();
716         calculateLongestExtNodeInfo();
717         _nodes_in_preorder = null;
718         resetPreferredSize();
719         _main_panel.adjustJScrollPane();
720         setArrowCursor();
721         repaint();
722     }
723
724     final private void colorizeSubtree( final Color c, final PhylogenyNode node ) {
725         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
726             JOptionPane.showMessageDialog( this,
727                                            "Cannot colorize subtree in unrooted display type",
728                                            "Attempt to colorize subtree in unrooted display",
729                                            JOptionPane.WARNING_MESSAGE );
730             return;
731         }
732         _control_panel.setColorBranches( true );
733         if ( _control_panel.getColorBranchesCb() != null ) {
734             _control_panel.getColorBranchesCb().setSelected( true );
735         }
736         for( final PreorderTreeIterator it = new PreorderTreeIterator( node ); it.hasNext(); ) {
737             it.next().getBranchData().setBranchColor( new BranchColor( c ) );
738         }
739         repaint();
740     }
741
742     final private void colorSubtree( final PhylogenyNode node ) {
743         Color intitial_color = null;
744         if ( getControlPanel().isColorBranches() && ( PhylogenyMethods.getBranchColorValue( node ) != null )
745                 && ( ( ( !node.isRoot() && ( node.getParent().getNumberOfDescendants() < 3 ) ) ) || ( node.isRoot() ) ) ) {
746             intitial_color = PhylogenyMethods.getBranchColorValue( node );
747         }
748         else {
749             intitial_color = getTreeColorSet().getBranchColor();
750         }
751         _color_chooser.setColor( intitial_color );
752         _color_chooser.setPreviewPanel( new JPanel() );
753         final JDialog dialog = JColorChooser
754                 .createDialog( this,
755                                "Subtree colorization",
756                                true,
757                                _color_chooser,
758                                new SubtreeColorizationActionListener( _color_chooser, node ),
759                                null );
760         dialog.setVisible( true );
761     }
762
763     final void confColor() {
764         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
765             return;
766         }
767         setWaitCursor();
768         Util.colorPhylogenyAccordingToConfidenceValues( _phylogeny, this );
769         _control_panel.setColorBranches( true );
770         if ( _control_panel.getColorBranchesCb() != null ) {
771             _control_panel.getColorBranchesCb().setSelected( true );
772         }
773         setArrowCursor();
774         repaint();
775     }
776
777     final private void copySubtree( final PhylogenyNode node ) {
778         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
779             errorMessageNoCutCopyPasteInUnrootedDisplay();
780             return;
781         }
782         _nodes_in_preorder = null;
783         setCutOrCopiedTree( _phylogeny.copy( node ) );
784         final List<PhylogenyNode> nodes = PhylogenyMethods.getAllDescendants( node );
785         final Set<Integer> node_ids = new HashSet<Integer>( nodes.size() );
786         for( final PhylogenyNode n : nodes ) {
787             node_ids.add( n.getId() );
788         }
789         node_ids.add( node.getId() );
790         setCopiedAndPastedNodes( node_ids );
791         repaint();
792     }
793
794     final private void cutSubtree( final PhylogenyNode node ) {
795         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
796             errorMessageNoCutCopyPasteInUnrootedDisplay();
797             return;
798         }
799         if ( node.isRoot() ) {
800             JOptionPane.showMessageDialog( this,
801                                            "Cannot cut entire tree as subtree",
802                                            "Attempt to cut entire tree",
803                                            JOptionPane.ERROR_MESSAGE );
804             return;
805         }
806         final String label = getASimpleTextRepresentationOfANode( node );
807         final int r = JOptionPane.showConfirmDialog( null,
808                                                      "Cut subtree" + label + "?",
809                                                      "Confirm Cutting of Subtree",
810                                                      JOptionPane.YES_NO_OPTION );
811         if ( r != JOptionPane.OK_OPTION ) {
812             return;
813         }
814         _nodes_in_preorder = null;
815         setCopiedAndPastedNodes( null );
816         setCutOrCopiedTree( _phylogeny.copy( node ) );
817         _phylogeny.deleteSubtree( node, true );
818         _phylogeny.hashIDs();
819         _phylogeny.recalculateNumberOfExternalDescendants( true );
820         resetNodeIdToDistToLeafMap();
821         setEdited( true );
822         repaint();
823     }
824
825     final private void cycleColors() {
826         getMainPanel().getTreeColorSet().cycleColorScheme();
827         for( final TreePanel tree_panel : getMainPanel().getTreePanels() ) {
828             tree_panel.setBackground( getMainPanel().getTreeColorSet().getBackgroundColor() );
829         }
830     }
831
832     final void decreaseDomainStructureEvalueThreshold() {
833         if ( _domain_structure_e_value_thr_exp > -20 ) {
834             _domain_structure_e_value_thr_exp -= 1;
835         }
836     }
837
838     final private void decreaseOvSize() {
839         if ( ( getOvMaxWidth() > 20 ) && ( getOvMaxHeight() > 20 ) ) {
840             setOvMaxWidth( getOvMaxWidth() - 5 );
841             setOvMaxHeight( getOvMaxHeight() - 5 );
842             updateOvSettings();
843             getControlPanel().displayedPhylogenyMightHaveChanged( false );
844         }
845     }
846
847     final private void deleteNodeOrSubtree( final PhylogenyNode node ) {
848         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
849             errorMessageNoCutCopyPasteInUnrootedDisplay();
850             return;
851         }
852         if ( node.isRoot() ) {
853             JOptionPane.showMessageDialog( this,
854                                            "Cannot delete entire tree",
855                                            "Attempt to delete entire tree",
856                                            JOptionPane.ERROR_MESSAGE );
857             return;
858         }
859         final String label = getASimpleTextRepresentationOfANode( node );
860         final Object[] options = { "Node only", "Entire subtree", "Cancel" };
861         final int r = JOptionPane.showOptionDialog( this,
862                                                     "Delete" + label + "?",
863                                                     "Delete Node/Subtree",
864                                                     JOptionPane.CLOSED_OPTION,
865                                                     JOptionPane.QUESTION_MESSAGE,
866                                                     null,
867                                                     options,
868                                                     options[ 2 ] );
869         _nodes_in_preorder = null;
870         boolean node_only = true;
871         if ( r == 1 ) {
872             node_only = false;
873         }
874         else if ( r != 0 ) {
875             return;
876         }
877         if ( node_only ) {
878             PhylogenyMethods.removeNode( node, _phylogeny );
879         }
880         else {
881             _phylogeny.deleteSubtree( node, true );
882         }
883         _phylogeny.externalNodesHaveChanged();
884         _phylogeny.hashIDs();
885         _phylogeny.recalculateNumberOfExternalDescendants( true );
886         resetNodeIdToDistToLeafMap();
887         setEdited( true );
888         repaint();
889     }
890
891     final private void displayNodePopupMenu( final PhylogenyNode node, final int x, final int y ) {
892         makePopupMenus( node );
893         _node_popup_menu.putClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY, node );
894         _node_popup_menu.show( this, x, y );
895     }
896
897     final private void drawArc( final double x,
898                                 final double y,
899                                 final double width,
900                                 final double heigth,
901                                 final double start_angle,
902                                 final double arc_angle,
903                                 final Graphics2D g ) {
904         _arc.setArc( x, y, width, heigth, _180_OVER_PI * start_angle, _180_OVER_PI * arc_angle, Arc2D.OPEN );
905         g.draw( _arc );
906     }
907
908     final private void drawLine( final double x1, final double y1, final double x2, final double y2, final Graphics2D g ) {
909         if ( ( x1 == x2 ) && ( y1 == y2 ) ) {
910             return;
911         }
912         _line.setLine( x1, y1, x2, y2 );
913         g.draw( _line );
914     }
915
916     final private void drawOval( final double x,
917                                  final double y,
918                                  final double width,
919                                  final double heigth,
920                                  final Graphics2D g ) {
921         _ellipse.setFrame( x, y, width, heigth );
922         g.draw( _ellipse );
923     }
924
925     final private void drawOvalFilled( final double x,
926                                        final double y,
927                                        final double width,
928                                        final double heigth,
929                                        final Graphics2D g ) {
930         _ellipse.setFrame( x, y, width, heigth );
931         g.fill( _ellipse );
932     }
933
934     final private void drawRect( final float x, final float y, final float width, final float heigth, final Graphics2D g ) {
935         _rectangle.setFrame( x, y, width, heigth );
936         g.draw( _rectangle );
937     }
938
939     final private void drawRectFilled( final double x,
940                                        final double y,
941                                        final double width,
942                                        final double heigth,
943                                        final Graphics2D g ) {
944         _rectangle.setFrame( x, y, width, heigth );
945         g.fill( _rectangle );
946     }
947
948     //TODO FIXME
949     final private void drawRectGradient( final double x,
950                                          final double y,
951                                          final double width,
952                                          final double heigth,
953                                          final Graphics2D g,
954                                          final Color color_1,
955                                          final Color color_2,
956                                          final Color color_border ) {
957         _rectangle.setFrame( x, y, width, heigth );
958         g.setPaint( new GradientPaint( ( float ) x,
959                                        ( float ) y,
960                                        color_1,
961                                        ( float ) ( x + width ),
962                                        ( float ) ( y + heigth ),
963                                        color_2,
964                                        false ) );
965         g.fill( _rectangle );
966         if ( color_border != null ) {
967             g.setPaint( color_border );
968             g.draw( _rectangle );
969         }
970     }
971
972     //TODO FIXME
973     final private void drawOvalGradient( final double x,
974                                          final double y,
975                                          final double width,
976                                          final double heigth,
977                                          final Graphics2D g,
978                                          final Color color_1,
979                                          final Color color_2,
980                                          final Color color_border ) {
981         _ellipse.setFrame( x, y, width, heigth );
982         g.setPaint( new GradientPaint( ( float ) x,
983                                        ( float ) y,
984                                        color_1,
985                                        ( float ) ( x + width ),
986                                        ( float ) ( y + heigth ),
987                                        color_2,
988                                        false ) );
989         g.fill( _ellipse );
990         if ( color_border != null ) {
991             g.setPaint( color_border );
992             g.draw( _ellipse );
993         }
994     }
995
996     final private void errorMessageNoCutCopyPasteInUnrootedDisplay() {
997         JOptionPane.showMessageDialog( this,
998                                        "Cannot cut, copy, paste, add, or delete subtrees/nodes in unrooted display",
999                                        "Attempt to cut/copy/paste/add/delete in unrooted display",
1000                                        JOptionPane.ERROR_MESSAGE );
1001     }
1002
1003     /**
1004      * Find the node, if any, at the given location
1005      * 
1006      * @param x
1007      * @param y
1008      * @return pointer to the node at x,y, null if not found
1009      */
1010     final PhylogenyNode findNode( final int x, final int y ) {
1011         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
1012             return null;
1013         }
1014         for( final PhylogenyNodeIterator iter = _phylogeny.iteratorPostorder(); iter.hasNext(); ) {
1015             final PhylogenyNode node = iter.next();
1016             if ( ( _phylogeny.isRooted() || !node.isRoot() || ( node.getNumberOfDescendants() > 2 ) )
1017                     && ( ( node.getXcoord() - _half_box_size_plus_wiggle ) <= x )
1018                     && ( ( node.getXcoord() + _half_box_size_plus_wiggle ) >= x )
1019                     && ( ( node.getYcoord() - _half_box_size_plus_wiggle ) <= y )
1020                     && ( ( node.getYcoord() + _half_box_size_plus_wiggle ) >= y ) ) {
1021                 return node;
1022             }
1023         }
1024         return null;
1025     }
1026
1027     final private String getASimpleTextRepresentationOfANode( final PhylogenyNode node ) {
1028         final String tax = PhylogenyMethods.getSpecies( node );
1029         String label = node.getName();
1030         if ( !ForesterUtil.isEmpty( label ) && !ForesterUtil.isEmpty( tax ) ) {
1031             label = label + " " + tax;
1032         }
1033         else if ( !ForesterUtil.isEmpty( tax ) ) {
1034             label = tax;
1035         }
1036         else {
1037             label = "";
1038         }
1039         if ( !ForesterUtil.isEmpty( label ) ) {
1040             label = " [" + label + "]";
1041         }
1042         return label;
1043     }
1044
1045     final Configuration getConfiguration() {
1046         return _configuration;
1047     }
1048
1049     final ControlPanel getControlPanel() {
1050         return _control_panel;
1051     }
1052
1053     final private Set<Integer> getCopiedAndPastedNodes() {
1054         return getMainPanel().getCopiedAndPastedNodes();
1055     }
1056
1057     final private Phylogeny getCutOrCopiedTree() {
1058         return getMainPanel().getCutOrCopiedTree();
1059     }
1060
1061     final int getDomainStructureEvalueThreshold() {
1062         return _domain_structure_e_value_thr_exp;
1063     }
1064
1065     final Set<Integer> getFoundNodes() {
1066         return _found_nodes;
1067     }
1068
1069     final private float getLastDragPointX() {
1070         return _last_drag_point_x;
1071     }
1072
1073     final private float getLastDragPointY() {
1074         return _last_drag_point_y;
1075     }
1076
1077     final int getLongestExtNodeInfo() {
1078         return _longest_ext_node_info;
1079     }
1080
1081     final public MainPanel getMainPanel() {
1082         return _main_panel;
1083     }
1084
1085     final private short getMaxBranchesToLeaf( final PhylogenyNode node ) {
1086         if ( !_nodeid_dist_to_leaf.containsKey( node.getId() ) ) {
1087             final short m = PhylogenyMethods.calculateMaxBranchesToLeaf( node );
1088             _nodeid_dist_to_leaf.put( node.getId(), m );
1089             return m;
1090         }
1091         else {
1092             return _nodeid_dist_to_leaf.get( node.getId() );
1093         }
1094     }
1095
1096     final private double getMaxDistanceToRoot() {
1097         if ( _max_distance_to_root < 0 ) {
1098             recalculateMaxDistanceToRoot();
1099         }
1100         return _max_distance_to_root;
1101     }
1102
1103     final Options getOptions() {
1104         if ( _options == null ) {
1105             _options = getControlPanel().getOptions();
1106         }
1107         return _options;
1108     }
1109
1110     final private float getOvMaxHeight() {
1111         return _ov_max_height;
1112     }
1113
1114     final private float getOvMaxWidth() {
1115         return _ov_max_width;
1116     }
1117
1118     final Rectangle2D getOvRectangle() {
1119         return _ov_rectangle;
1120     }
1121
1122     final Rectangle getOvVirtualRectangle() {
1123         return _ov_virtual_rectangle;
1124     }
1125
1126     final private float getOvXcorrectionFactor() {
1127         return _ov_x_correction_factor;
1128     }
1129
1130     final private float getOvXDistance() {
1131         return _ov_x_distance;
1132     }
1133
1134     final private int getOvXPosition() {
1135         return _ov_x_position;
1136     }
1137
1138     final private float getOvYDistance() {
1139         return _ov_y_distance;
1140     }
1141
1142     final private int getOvYPosition() {
1143         return _ov_y_position;
1144     }
1145
1146     final private int getOvYStart() {
1147         return _ov_y_start;
1148     }
1149
1150     /**
1151      * Get a pointer to the phylogeny 
1152      * 
1153      * @return a pointer to the phylogeny
1154      */
1155     public final Phylogeny getPhylogeny() {
1156         return _phylogeny;
1157     }
1158
1159     final PHYLOGENY_GRAPHICS_TYPE getPhylogenyGraphicsType() {
1160         return _graphics_type;
1161     }
1162
1163     final private double getScaleDistance() {
1164         return _scale_distance;
1165     }
1166
1167     final private String getScaleLabel() {
1168         return _scale_label;
1169     }
1170
1171     final double getStartingAngle() {
1172         return _urt_starting_angle;
1173     }
1174
1175     /**
1176      * Find a color for this species name.
1177      * 
1178      * @param species
1179      * @return the species color
1180      */
1181     final Color getTaxonomyBasedColor( final PhylogenyNode node ) {
1182         if ( node.getNodeData().isHasTaxonomy() ) {
1183             return calculateTaxonomyBasedColor( node.getNodeData().getTaxonomy() );
1184         }
1185         // return non-colorized color
1186         return getTreeColorSet().getTaxonomyColor();
1187     }
1188
1189     /**
1190      * @return pointer to colorset for tree drawing
1191      */
1192     final TreeColorSet getTreeColorSet() {
1193         return getMainPanel().getTreeColorSet();
1194     }
1195
1196     final File getTreeFile() {
1197         return _treefile;
1198     }
1199
1200     final private TreeFontSet getTreeFontSet() {
1201         return getMainPanel().getTreeFontSet();
1202     }
1203
1204     final private float getUrtFactor() {
1205         return _urt_factor;
1206     }
1207
1208     final private float getUrtFactorOv() {
1209         return _urt_factor_ov;
1210     }
1211
1212     final float getXcorrectionFactor() {
1213         return _x_correction_factor;
1214     }
1215
1216     final float getXdistance() {
1217         return _x_distance;
1218     }
1219
1220     final float getYdistance() {
1221         return _y_distance;
1222     }
1223
1224     final private void handleClickToAction( final NodeClickAction action, final PhylogenyNode node ) {
1225         switch ( action ) {
1226             case SHOW_DATA:
1227                 showNodeFrame( node );
1228                 break;
1229             case COLLAPSE:
1230                 collapse( node );
1231                 break;
1232             case REROOT:
1233                 reRoot( node );
1234                 break;
1235             case SUBTREE:
1236                 subTree( node );
1237                 break;
1238             case SWAP:
1239                 swap( node );
1240                 break;
1241             case COLOR_SUBTREE:
1242                 colorSubtree( node );
1243                 break;
1244             case OPEN_SEQ_WEB:
1245                 openSeqWeb( node );
1246                 break;
1247             case BLAST:
1248                 blast( node );
1249                 break;
1250             case OPEN_TAX_WEB:
1251                 openTaxWeb( node );
1252                 break;
1253             case CUT_SUBTREE:
1254                 cutSubtree( node );
1255                 break;
1256             case COPY_SUBTREE:
1257                 copySubtree( node );
1258                 break;
1259             case PASTE_SUBTREE:
1260                 pasteSubtree( node );
1261                 break;
1262             case DELETE_NODE_OR_SUBTREE:
1263                 deleteNodeOrSubtree( node );
1264                 break;
1265             case ADD_NEW_NODE:
1266                 addEmptyNode( node );
1267                 break;
1268             case EDIT_NODE_DATA:
1269                 showNodeEditFrame( node );
1270                 break;
1271             default:
1272                 throw new IllegalArgumentException( "unknown action: " + action );
1273         }
1274     }
1275
1276     final void increaseDomainStructureEvalueThreshold() {
1277         if ( _domain_structure_e_value_thr_exp < 3 ) {
1278             _domain_structure_e_value_thr_exp += 1;
1279         }
1280     }
1281
1282     final private void increaseOvSize() {
1283         if ( ( getOvMaxWidth() < getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth() / 2 )
1284                 && ( getOvMaxHeight() < getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
1285                         .getHeight() / 2 ) ) {
1286             setOvMaxWidth( getOvMaxWidth() + 5 );
1287             setOvMaxHeight( getOvMaxHeight() + 5 );
1288             updateOvSettings();
1289             getControlPanel().displayedPhylogenyMightHaveChanged( false );
1290         }
1291     }
1292
1293     final void inferCommonPartOfScientificNames() {
1294         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
1295             return;
1296         }
1297         setWaitCursor();
1298         Util.inferCommonPartOfScientificNames( _phylogeny );
1299         setArrowCursor();
1300         repaint();
1301     }
1302
1303     final private void init() {
1304         _color_chooser = new JColorChooser();
1305         _rollover_popup = new JTextArea();
1306         _rollover_popup.setFont( POPUP_FONT );
1307         resetNodeIdToDistToLeafMap();
1308         setTextAntialias();
1309         setTreeFile( null );
1310         setEdited( false );
1311         initializeOvSettings();
1312         setStartingAngle( TWO_PI * 3 / 4 );
1313         final ImageLoader il = new ImageLoader( this );
1314         new Thread( il ).start();
1315     }
1316
1317     final private void initializeOvSettings() {
1318         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
1319         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
1320     }
1321
1322     final void initNodeData() {
1323         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
1324             return;
1325         }
1326         double max_original_domain_structure_width = 0.0;
1327         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
1328             if ( node.getNodeData().isHasSequence()
1329                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1330                 RenderableDomainArchitecture rds = null;
1331                 if ( !( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
1332                     rds = new RenderableDomainArchitecture( node.getNodeData().getSequence().getDomainArchitecture(),
1333                                                             getConfiguration() );
1334                     node.getNodeData().getSequence().setDomainArchitecture( rds );
1335                 }
1336                 else {
1337                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
1338                 }
1339                 if ( getControlPanel().isShowDomainArchitectures() ) {
1340                     final double dsw = rds.getOriginalSize().getWidth();
1341                     if ( dsw > max_original_domain_structure_width ) {
1342                         max_original_domain_structure_width = dsw;
1343                     }
1344                 }
1345             }
1346         }
1347         if ( getControlPanel().isShowDomainArchitectures() ) {
1348             final double ds_factor_width = _domain_structure_width / max_original_domain_structure_width;
1349             for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
1350                 if ( node.getNodeData().isHasSequence()
1351                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1352                     final RenderableDomainArchitecture rds = ( RenderableDomainArchitecture ) node.getNodeData()
1353                             .getSequence().getDomainArchitecture();
1354                     rds.setRenderingFactorWidth( ds_factor_width );
1355                     rds.setParameter( _domain_structure_e_value_thr_exp );
1356                 }
1357             }
1358         }
1359     }
1360
1361     final boolean inOv( final MouseEvent e ) {
1362         return ( ( e.getX() > getVisibleRect().x + getOvXPosition() + 1 )
1363                 && ( e.getX() < getVisibleRect().x + getOvXPosition() + getOvMaxWidth() - 1 )
1364                 && ( e.getY() > getVisibleRect().y + getOvYPosition() + 1 ) && ( e.getY() < getVisibleRect().y
1365                 + getOvYPosition() + getOvMaxHeight() - 1 ) );
1366     }
1367
1368     final boolean inOvRectangle( final MouseEvent e ) {
1369         return ( ( e.getX() >= getOvRectangle().getX() - 1 )
1370                 && ( e.getX() <= getOvRectangle().getX() + getOvRectangle().getWidth() + 1 )
1371                 && ( e.getY() >= getOvRectangle().getY() - 1 ) && ( e.getY() <= getOvRectangle().getY()
1372                 + getOvRectangle().getHeight() + 1 ) );
1373     }
1374
1375     final private boolean inOvVirtualRectangle( final int x, final int y ) {
1376         return ( ( x >= getOvVirtualRectangle().x - 1 )
1377                 && ( x <= getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 )
1378                 && ( y >= getOvVirtualRectangle().y - 1 ) && ( y <= getOvVirtualRectangle().y
1379                 + getOvVirtualRectangle().height + 1 ) );
1380     }
1381
1382     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
1383         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
1384     }
1385
1386     final boolean isApplet() {
1387         return getMainPanel() instanceof MainPanelApplets;
1388     }
1389
1390     final private boolean isCanBlast( final PhylogenyNode node ) {
1391         return ( node.getNodeData().isHasSequence() && ( ( ( node.getNodeData().getSequence().getAccession() != null ) && !ForesterUtil
1392                 .isEmpty( node.getNodeData().getSequence().getAccession().getValue() ) )
1393                 || !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) || !ForesterUtil.isEmpty( node
1394                 .getNodeData().getSequence().getSymbol() ) ) );
1395     }
1396
1397     final boolean isCanCollapse() {
1398         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
1399     }
1400
1401     final boolean isCanColorSubtree() {
1402         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
1403     }
1404
1405     final boolean isCanCopy() {
1406         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
1407     }
1408
1409     final boolean isCanCut( final PhylogenyNode node ) {
1410         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() && !node
1411                 .isRoot() );
1412     }
1413
1414     final boolean isCanDelete() {
1415         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
1416     }
1417
1418     final private boolean isCanOpenSeqWeb( final PhylogenyNode node ) {
1419         if ( node.getNodeData().isHasSequence()
1420                 && ( node.getNodeData().getSequence().getAccession() != null )
1421                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() )
1422                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getValue() )
1423                 && getConfiguration().isHasWebLink( node.getNodeData().getSequence().getAccession().getSource()
1424                         .toLowerCase() ) ) {
1425             return true;
1426         }
1427         return false;
1428     }
1429
1430     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
1431         if ( node.getNodeData().isHasTaxonomy()
1432                 && ( ( ( node.getNodeData().getTaxonomy().getIdentifier() != null )
1433                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getProvider() )
1434                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && getConfiguration()
1435                         .isHasWebLink( node.getNodeData().getTaxonomy().getIdentifier().getProvider().toLowerCase() ) )
1436                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
1437                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
1438                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) || ( ( node
1439                         .getNodeData().getTaxonomy().getIdentifier() != null )
1440                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && node
1441                         .getNodeData().getTaxonomy().getIdentifier().getValue().startsWith( "http://" ) ) ) ) {
1442             return true;
1443         }
1444         else {
1445             return false;
1446         }
1447     }
1448
1449     final boolean isCanPaste() {
1450         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
1451                 && ( getCutOrCopiedTree() != null ) && !getCutOrCopiedTree().isEmpty() );
1452     }
1453
1454     final boolean isCanReroot() {
1455         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && ( _subtree_index < 1 ) );
1456     }
1457
1458     final boolean isCanSubtree( final PhylogenyNode node ) {
1459         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && !node.isExternal() && ( !node
1460                 .isRoot() || ( _subtree_index > 0 ) ) );
1461     }
1462
1463     final boolean isEdited() {
1464         return _edited;
1465     }
1466
1467     final private boolean isInFoundNodes( final PhylogenyNode node ) {
1468         return ( ( getFoundNodes() != null ) && getFoundNodes().contains( node.getId() ) );
1469     }
1470
1471     final private boolean isInOv() {
1472         return _in_ov;
1473     }
1474
1475     final boolean isInOvRect() {
1476         return _in_ov_rect;
1477     }
1478
1479     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
1480         int y_dist = 40;
1481         if ( getControlPanel().isShowTaxonomyImages() ) {
1482             y_dist = 40 + ( int ) getYdistance();
1483         }
1484         return ( ( node.getYcoord() < getVisibleRect().getMinY() - y_dist )
1485                 || ( node.getYcoord() > getVisibleRect().getMaxY() + y_dist ) || ( ( node.getParent() != null ) && ( node
1486                 .getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
1487     }
1488
1489     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
1490         return ( ( node.getYcoord() < getVisibleRect().getMinY() - 20 )
1491                 || ( node.getYcoord() > getVisibleRect().getMaxY() + 20 )
1492                 || ( node.getXcoord() < getVisibleRect().getMinX() - 20 ) || ( node.getXcoord() > getVisibleRect()
1493                 .getMaxX() + 20 ) );
1494     }
1495
1496     final private boolean isNonLinedUpCladogram() {
1497         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
1498     }
1499
1500     final boolean isOvOn() {
1501         return _ov_on;
1502     }
1503
1504     final boolean isPhyHasBranchLengths() {
1505         return _phy_has_branch_lengths;
1506     }
1507
1508     final private boolean isUniformBranchLengthsForCladogram() {
1509         return getOptions().getCladogramType() == CLADOGRAM_TYPE.TOTAL_NODE_SUM_DEP;
1510     }
1511
1512     final private void keyPressedCalls( final KeyEvent e ) {
1513         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
1514             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
1515                 if ( !isInOvRect() ) {
1516                     setInOvRect( true );
1517                 }
1518             }
1519             else if ( isInOvRect() ) {
1520                 setInOvRect( false );
1521             }
1522         }
1523         if ( e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK ) {
1524             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
1525                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
1526                 getMainPanel().getTreeFontSet().mediumFonts();
1527                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1528             }
1529             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
1530                 getMainPanel().getTreeFontSet().decreaseFontSize();
1531                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1532             }
1533             else if ( plusPressed( e.getKeyCode() ) ) {
1534                 getMainPanel().getTreeFontSet().increaseFontSize();
1535                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1536             }
1537         }
1538         else {
1539             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
1540                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
1541                 getControlPanel().showWhole();
1542             }
1543             else if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
1544                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
1545                 if ( e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK ) {
1546                     if ( e.getKeyCode() == KeyEvent.VK_UP ) {
1547                         getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
1548                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1549                     }
1550                     else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
1551                         getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
1552                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1553                     }
1554                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
1555                         getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
1556                                                                    Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
1557                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1558                     }
1559                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
1560                         getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
1561                                                                   Constants.WHEEL_ZOOM_IN_FACTOR );
1562                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1563                     }
1564                 }
1565                 else {
1566                     final int d = 80;
1567                     int dx = 0;
1568                     int dy = -d;
1569                     if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
1570                         dy = d;
1571                     }
1572                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
1573                         dx = -d;
1574                         dy = 0;
1575                     }
1576                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
1577                         dx = d;
1578                         dy = 0;
1579                     }
1580                     final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1581                     scroll_position.x = scroll_position.x + dx;
1582                     scroll_position.y = scroll_position.y + dy;
1583                     if ( scroll_position.x <= 0 ) {
1584                         scroll_position.x = 0;
1585                     }
1586                     else {
1587                         final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1588                                 - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1589                         if ( scroll_position.x >= max_x ) {
1590                             scroll_position.x = max_x;
1591                         }
1592                     }
1593                     if ( scroll_position.y <= 0 ) {
1594                         scroll_position.y = 0;
1595                     }
1596                     else {
1597                         final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1598                                 - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1599                         if ( scroll_position.y >= max_y ) {
1600                             scroll_position.y = max_y;
1601                         }
1602                     }
1603                     repaint();
1604                     getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1605                 }
1606             }
1607             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
1608                 getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
1609                 getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
1610                                                            Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
1611                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1612             }
1613             else if ( plusPressed( e.getKeyCode() ) ) {
1614                 getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
1615                                                           Constants.WHEEL_ZOOM_IN_FACTOR );
1616                 getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
1617                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1618             }
1619             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
1620                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1621                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1622                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
1623                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
1624                 }
1625             }
1626             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
1627                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1628                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1629                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
1630                     if ( getStartingAngle() < 0 ) {
1631                         setStartingAngle( TWO_PI + getStartingAngle() );
1632                     }
1633                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
1634                 }
1635             }
1636             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
1637                 boolean selected = false;
1638                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
1639                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
1640                     selected = true;
1641                 }
1642                 else {
1643                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
1644                 }
1645                 if ( getMainPanel().getMainFrame() == null ) {
1646                     // Must be "E" applet version.
1647                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
1648                     if ( ae.getlabelDirectionCbmi() != null ) {
1649                         ae.getlabelDirectionCbmi().setSelected( selected );
1650                     }
1651                 }
1652                 else {
1653                     getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
1654                 }
1655                 repaint();
1656             }
1657             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
1658                 switchDisplaygetPhylogenyGraphicsType();
1659                 repaint();
1660             }
1661             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
1662                 cycleColors();
1663                 repaint();
1664             }
1665             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
1666                 MainFrame.cycleOverview( getOptions(), this );
1667                 repaint();
1668             }
1669             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
1670                 increaseOvSize();
1671             }
1672             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
1673                 decreaseOvSize();
1674             }
1675             e.consume();
1676         }
1677     }
1678
1679     final private void makePopupMenus( final PhylogenyNode node ) {
1680         _node_popup_menu = new JPopupMenu();
1681         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
1682         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
1683         for( int i = 0; i < clickto_names.size(); i++ ) {
1684             final String title = clickto_names.get( i );
1685             _node_popup_menu_items[ i ] = new JMenuItem( title );
1686             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
1687                 _node_popup_menu_items[ i ].setEnabled( isCanOpenSeqWeb( node ) );
1688             }
1689             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
1690                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
1691             }
1692             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
1693                 if ( Constants.__RELEASE || Constants.__SNAPSHOT_RELEASE ) {
1694                     continue;
1695                 }
1696                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
1697             }
1698             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
1699                 if ( !getOptions().isEditable() ) {
1700                     continue;
1701                 }
1702                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
1703             }
1704             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
1705                 if ( !getOptions().isEditable() ) {
1706                     continue;
1707                 }
1708                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
1709             }
1710             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
1711                 if ( !getOptions().isEditable() ) {
1712                     continue;
1713                 }
1714                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
1715             }
1716             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
1717                 if ( !getOptions().isEditable() ) {
1718                     continue;
1719                 }
1720                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
1721             }
1722             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
1723                 if ( !getOptions().isEditable() ) {
1724                     continue;
1725                 }
1726             }
1727             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
1728                 if ( !getOptions().isEditable() ) {
1729                     continue;
1730                 }
1731             }
1732             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
1733                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
1734             }
1735             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
1736                 _node_popup_menu_items[ i ].setEnabled( isCanCollapse() );
1737             }
1738             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
1739                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
1740             }
1741             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
1742                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
1743             }
1744             _node_popup_menu_items[ i ].addActionListener( this );
1745             _node_popup_menu.add( _node_popup_menu_items[ i ] );
1746         }
1747     }
1748
1749     final void midpointRoot() {
1750         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
1751             return;
1752         }
1753         if ( !_phylogeny.isRerootable() ) {
1754             JOptionPane.showMessageDialog( this,
1755                                            "This is not rerootable",
1756                                            "Not rerootable",
1757                                            JOptionPane.WARNING_MESSAGE );
1758             return;
1759         }
1760         _nodes_in_preorder = null;
1761         setWaitCursor();
1762         PhylogenyMethods.midpointRoot( _phylogeny );
1763         resetNodeIdToDistToLeafMap();
1764         setArrowCursor();
1765         repaint();
1766     }
1767
1768     final void mouseClicked( final MouseEvent e ) {
1769         if ( getOptions().isShowOverview() && isOvOn() && isInOv() ) {
1770             final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
1771             final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
1772             double x = ( e.getX() - getVisibleRect().x - getOvXPosition() - getOvRectangle().getWidth() / 2.0 )
1773                     * w_ratio;
1774             double y = ( e.getY() - getVisibleRect().y - getOvYPosition() - getOvRectangle().getHeight() / 2.0 )
1775                     * h_ratio;
1776             if ( x < 0 ) {
1777                 x = 0;
1778             }
1779             if ( y < 0 ) {
1780                 y = 0;
1781             }
1782             final double max_x = getWidth() - getVisibleRect().width;
1783             final double max_y = getHeight() - getVisibleRect().height;
1784             if ( x > max_x ) {
1785                 x = max_x;
1786             }
1787             if ( y > max_y ) {
1788                 y = max_y;
1789             }
1790             getMainPanel().getCurrentScrollPane().getViewport()
1791                     .setViewPosition( new Point( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ) ) );
1792             setInOvRect( true );
1793             repaint();
1794         }
1795         else {
1796             final PhylogenyNode node = findNode( e.getX(), e.getY() );
1797             if ( node != null ) {
1798                 if ( !node.isRoot() && node.getParent().isCollapse() ) {
1799                     return;
1800                 }
1801                 _highlight_node = node;
1802                 // Check if shift key is down
1803                 if ( ( e.getModifiers() & InputEvent.SHIFT_MASK ) != 0 ) {
1804                     // Yes, so add to _found_nodes
1805                     if ( getFoundNodes() == null ) {
1806                         setFoundNodes( new HashSet<Integer>() );
1807                     }
1808                     getFoundNodes().add( node.getId() );
1809                     // Check if control key is down
1810                 }
1811                 else if ( ( e.getModifiers() & InputEvent.CTRL_MASK ) != 0 ) {
1812                     // Yes, so pop-up menu
1813                     displayNodePopupMenu( node, e.getX(), e.getY() );
1814                     // Handle unadorned click
1815                 }
1816                 else {
1817                     // Check for right mouse button
1818                     if ( e.getModifiers() == 4 ) {
1819                         displayNodePopupMenu( node, e.getX(), e.getY() );
1820                     }
1821                     else {
1822                         // if not in _found_nodes, clear _found_nodes
1823                         handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
1824                     }
1825                 }
1826             }
1827             else {
1828                 // no node was clicked
1829                 _highlight_node = null;
1830             }
1831         }
1832         repaint();
1833     }
1834
1835     final void mouseDragInBrowserPanel( final MouseEvent e ) {
1836         setCursor( MOVE_CURSOR );
1837         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1838         scroll_position.x -= ( e.getX() - getLastDragPointX() );
1839         scroll_position.y -= ( e.getY() - getLastDragPointY() );
1840         if ( scroll_position.x < 0 ) {
1841             scroll_position.x = 0;
1842         }
1843         else {
1844             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1845                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1846             if ( scroll_position.x > max_x ) {
1847                 scroll_position.x = max_x;
1848             }
1849         }
1850         if ( scroll_position.y < 0 ) {
1851             scroll_position.y = 0;
1852         }
1853         else {
1854             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1855                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1856             if ( scroll_position.y > max_y ) {
1857                 scroll_position.y = max_y;
1858             }
1859         }
1860         if ( isOvOn() || getOptions().isShowScale() ) {
1861             repaint();
1862         }
1863         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1864     }
1865
1866     final void mouseDragInOvRectangle( final MouseEvent e ) {
1867         setCursor( HAND_CURSOR );
1868         final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
1869         final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
1870         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1871         double dx = ( w_ratio * e.getX() - w_ratio * getLastDragPointX() );
1872         double dy = ( h_ratio * e.getY() - h_ratio * getLastDragPointY() );
1873         scroll_position.x = ForesterUtil.roundToInt( scroll_position.x + dx );
1874         scroll_position.y = ForesterUtil.roundToInt( scroll_position.y + dy );
1875         if ( scroll_position.x <= 0 ) {
1876             scroll_position.x = 0;
1877             dx = 0;
1878         }
1879         else {
1880             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1881                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1882             if ( scroll_position.x >= max_x ) {
1883                 dx = 0;
1884                 scroll_position.x = max_x;
1885             }
1886         }
1887         if ( scroll_position.y <= 0 ) {
1888             dy = 0;
1889             scroll_position.y = 0;
1890         }
1891         else {
1892             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1893                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1894             if ( scroll_position.y >= max_y ) {
1895                 dy = 0;
1896                 scroll_position.y = max_y;
1897             }
1898         }
1899         repaint();
1900         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1901         setLastMouseDragPointX( ( float ) ( e.getX() + dx ) );
1902         setLastMouseDragPointY( ( float ) ( e.getY() + dy ) );
1903     }
1904
1905     final void mouseMoved( final MouseEvent e ) {
1906         requestFocusInWindow();
1907         if ( getControlPanel().isNodeDescPopup() ) {
1908             if ( _node_desc_popup != null ) {
1909                 _node_desc_popup.hide();
1910                 _node_desc_popup = null;
1911             }
1912         }
1913         if ( getOptions().isShowOverview() && isOvOn() ) {
1914             if ( inOvVirtualRectangle( e ) ) {
1915                 if ( !isInOvRect() ) {
1916                     setInOvRect( true );
1917                     repaint();
1918                 }
1919             }
1920             else {
1921                 if ( isInOvRect() ) {
1922                     setInOvRect( false );
1923                     repaint();
1924                 }
1925             }
1926         }
1927         if ( inOv( e ) && getOptions().isShowOverview() && isOvOn() ) {
1928             if ( !isInOv() ) {
1929                 setInOv( true );
1930             }
1931         }
1932         else {
1933             if ( isInOv() ) {
1934                 setInOv( false );
1935             }
1936             final PhylogenyNode node = findNode( e.getX(), e.getY() );
1937             if ( ( node != null ) && ( node.isRoot() || !node.getParent().isCollapse() ) ) {
1938                 // cursor is over a tree node
1939                 if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.CUT_SUBTREE )
1940                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.COPY_SUBTREE )
1941                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.PASTE_SUBTREE )
1942                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.DELETE_NODE_OR_SUBTREE )
1943                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.REROOT )
1944                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.ADD_NEW_NODE ) ) {
1945                     setCursor( CUT_CURSOR );
1946                 }
1947                 else {
1948                     setCursor( HAND_CURSOR );
1949                     if ( getControlPanel().isNodeDescPopup() ) {
1950                         showNodeDataPopup( e, node );
1951                     }
1952                 }
1953             }
1954             else {
1955                 setCursor( ARROW_CURSOR );
1956             }
1957         }
1958     }
1959
1960     final void mouseReleasedInBrowserPanel( final MouseEvent e ) {
1961         setCursor( ARROW_CURSOR );
1962     }
1963
1964     final public void mouseWheelMoved( final MouseWheelEvent e ) {
1965         final int notches = e.getWheelRotation();
1966         if ( inOvVirtualRectangle( e ) ) {
1967             if ( !isInOvRect() ) {
1968                 setInOvRect( true );
1969                 repaint();
1970             }
1971         }
1972         else {
1973             if ( isInOvRect() ) {
1974                 setInOvRect( false );
1975                 repaint();
1976             }
1977         }
1978         if ( e.isControlDown() ) {
1979             if ( notches < 0 ) {
1980                 getTreeFontSet().increaseFontSize();
1981                 getControlPanel().displayedPhylogenyMightHaveChanged( true );
1982             }
1983             else {
1984                 getTreeFontSet().decreaseFontSize();
1985                 getControlPanel().displayedPhylogenyMightHaveChanged( true );
1986             }
1987         }
1988         else if ( e.isShiftDown() ) {
1989             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1990                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1991                 if ( notches < 0 ) {
1992                     for( int i = 0; i < ( -notches ); ++i ) {
1993                         setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
1994                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
1995                     }
1996                 }
1997                 else {
1998                     for( int i = 0; i < notches; ++i ) {
1999                         setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
2000                         if ( getStartingAngle() < 0 ) {
2001                             setStartingAngle( TWO_PI + getStartingAngle() );
2002                         }
2003                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2004                     }
2005                 }
2006             }
2007             else {
2008                 if ( notches < 0 ) {
2009                     for( int i = 0; i < ( -notches ); ++i ) {
2010                         getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2011                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2012                     }
2013                 }
2014                 else {
2015                     for( int i = 0; i < notches; ++i ) {
2016                         getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2017                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2018                     }
2019                 }
2020             }
2021         }
2022         else {
2023             if ( notches < 0 ) {
2024                 for( int i = 0; i < ( -notches ); ++i ) {
2025                     getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
2026                                                Constants.WHEEL_ZOOM_IN_X_CORRECTION_FACTOR );
2027                     getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2028                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2029                 }
2030             }
2031             else {
2032                 for( int i = 0; i < notches; ++i ) {
2033                     getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2034                     getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
2035                                                 Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
2036                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2037                 }
2038             }
2039         }
2040         requestFocus();
2041         requestFocusInWindow();
2042         requestFocus();
2043     }
2044
2045     final void multiplyUrtFactor( final float f ) {
2046         _urt_factor *= f;
2047     }
2048
2049     final JApplet obtainApplet() {
2050         return ( ( MainPanelApplets ) getMainPanel() ).getApplet();
2051     }
2052
2053     final private void openSeqWeb( final PhylogenyNode node ) {
2054         if ( !isCanOpenSeqWeb( node ) ) {
2055             cannotOpenBrowserWarningMessage( "sequence" );
2056             return;
2057         }
2058         String uri_str = null;
2059         final Sequence seq = node.getNodeData().getSequence();
2060         final String source = seq.getAccession().getSource().toLowerCase();
2061         String url;
2062         if ( source.toLowerCase().equals( "ncbi" ) ) {
2063             url = Constants.NCBI_ALL_DATABASE_SEARCH;
2064         }
2065         else {
2066             final WebLink weblink = getConfiguration().getWebLink( source );
2067             url = weblink.getUrl().toString();
2068         }
2069         try {
2070             uri_str = url + URLEncoder.encode( seq.getAccession().getValue(), ForesterConstants.UTF8 );
2071         }
2072         catch ( final UnsupportedEncodingException e ) {
2073             Util.showErrorMessage( this, e.toString() );
2074             e.printStackTrace();
2075         }
2076         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2077             try {
2078                 JApplet applet = null;
2079                 if ( isApplet() ) {
2080                     applet = obtainApplet();
2081                 }
2082                 Util.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_seq" );
2083             }
2084             catch ( final IOException e ) {
2085                 Util.showErrorMessage( this, e.toString() );
2086                 e.printStackTrace();
2087             }
2088             catch ( final URISyntaxException e ) {
2089                 Util.showErrorMessage( this, e.toString() );
2090                 e.printStackTrace();
2091             }
2092         }
2093         else {
2094             cannotOpenBrowserWarningMessage( "sequence" );
2095         }
2096     }
2097
2098     final private void openTaxWeb( final PhylogenyNode node ) {
2099         if ( !isCanOpenTaxWeb( node ) ) {
2100             cannotOpenBrowserWarningMessage( "taxonomic" );
2101             return;
2102         }
2103         String uri_str = null;
2104         final Taxonomy tax = node.getNodeData().getTaxonomy();
2105         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
2106                 && getConfiguration().isHasWebLink( tax.getIdentifier().getProvider().toLowerCase() ) ) {
2107             final String type = tax.getIdentifier().getProvider().toLowerCase();
2108             final WebLink weblink = getConfiguration().getWebLink( type );
2109             try {
2110                 uri_str = weblink.getUrl() + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
2111             }
2112             catch ( final UnsupportedEncodingException e ) {
2113                 Util.showErrorMessage( this, e.toString() );
2114                 e.printStackTrace();
2115             }
2116         }
2117         else if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
2118                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
2119             try {
2120                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
2121             }
2122             catch ( final URISyntaxException e ) {
2123                 Util.showErrorMessage( this, e.toString() );
2124                 uri_str = null;
2125                 e.printStackTrace();
2126             }
2127         }
2128         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
2129             try {
2130                 uri_str = "http://www.eol.org/search?q="
2131                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
2132             }
2133             catch ( final UnsupportedEncodingException e ) {
2134                 Util.showErrorMessage( this, e.toString() );
2135                 e.printStackTrace();
2136             }
2137         }
2138         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
2139             try {
2140                 uri_str = "http://www.uniprot.org/taxonomy/?query="
2141                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
2142             }
2143             catch ( final UnsupportedEncodingException e ) {
2144                 Util.showErrorMessage( this, e.toString() );
2145                 e.printStackTrace();
2146             }
2147         }
2148         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
2149             try {
2150                 uri_str = "http://www.eol.org/search?q="
2151                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
2152             }
2153             catch ( final UnsupportedEncodingException e ) {
2154                 Util.showErrorMessage( this, e.toString() );
2155                 e.printStackTrace();
2156             }
2157         }
2158         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2159             try {
2160                 JApplet applet = null;
2161                 if ( isApplet() ) {
2162                     applet = obtainApplet();
2163                 }
2164                 Util.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_tax" );
2165             }
2166             catch ( final IOException e ) {
2167                 Util.showErrorMessage( this, e.toString() );
2168                 e.printStackTrace();
2169             }
2170             catch ( final URISyntaxException e ) {
2171                 Util.showErrorMessage( this, e.toString() );
2172                 e.printStackTrace();
2173             }
2174         }
2175         else {
2176             cannotOpenBrowserWarningMessage( "taxonomic" );
2177         }
2178     }
2179
2180     final void paintBranchCircular( final PhylogenyNode p,
2181                                     final PhylogenyNode c,
2182                                     final Graphics2D g,
2183                                     final boolean radial_labels,
2184                                     final boolean to_pdf,
2185                                     final boolean to_graphics_file ) {
2186         final double angle = _urt_nodeid_angle_map.get( c.getId() );
2187         final double root_x = _root.getXcoord();
2188         final double root_y = _root.getYcoord();
2189         final double dx = root_x - p.getXcoord();
2190         final double dy = root_y - p.getYcoord();
2191         final double parent_radius = Math.sqrt( dx * dx + dy * dy );
2192         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
2193         assignGraphicsForBranchWithColorForParentBranch( c, false, g, to_pdf, to_graphics_file );
2194         if ( ( c.isFirstChildNode() || c.isLastChildNode() )
2195                 && ( ( Math.abs( parent_radius * arc ) > 1.5 ) || to_pdf || to_graphics_file ) ) {
2196             final double r2 = 2.0 * parent_radius;
2197             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
2198         }
2199         drawLine( c.getXcoord(),
2200                   c.getYcoord(),
2201                   root_x + ( Math.cos( angle ) * parent_radius ),
2202                   root_y + ( Math.sin( angle ) * parent_radius ),
2203                   g );
2204         paintNodeBox( c.getXcoord(), c.getYcoord(), c, g, to_pdf, to_graphics_file, isInFoundNodes( c ) );
2205         if ( c.isExternal() ) {
2206             final boolean is_in_found_nodes = isInFoundNodes( c );
2207             if ( ( _dynamic_hiding_factor > 1 ) && !is_in_found_nodes
2208                     && ( _urt_nodeid_index_map.get( c.getId() ) % _dynamic_hiding_factor != 1 ) ) {
2209                 return;
2210             }
2211             paintNodeDataUnrootedCirc( g, c, to_pdf, to_graphics_file, radial_labels, 0, is_in_found_nodes );
2212         }
2213     }
2214
2215     final void paintBranchCircularLite( final PhylogenyNode p, final PhylogenyNode c, final Graphics2D g ) {
2216         final double angle = _urt_nodeid_angle_map.get( c.getId() );
2217         final double root_x = _root.getXSecondary();
2218         final double root_y = _root.getYSecondary();
2219         final double dx = root_x - p.getXSecondary();
2220         final double dy = root_y - p.getYSecondary();
2221         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
2222         final double parent_radius = Math.sqrt( dx * dx + dy * dy );
2223         g.setColor( getTreeColorSet().getOvColor() );
2224         if ( ( c.isFirstChildNode() || c.isLastChildNode() ) && ( Math.abs( arc ) > 0.02 ) ) {
2225             final double r2 = 2.0 * parent_radius;
2226             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
2227         }
2228         drawLine( c.getXSecondary(),
2229                   c.getYSecondary(),
2230                   root_x + ( Math.cos( angle ) * parent_radius ),
2231                   root_y + ( Math.sin( angle ) * parent_radius ),
2232                   g );
2233         if ( isInFoundNodes( c ) ) {
2234             g.setColor( getTreeColorSet().getFoundColor() );
2235             drawRectFilled( c.getXSecondary() - 1, c.getYSecondary() - 1, 3, 3, g );
2236         }
2237     }
2238
2239     final private void paintBranchLength( final Graphics2D g,
2240                                           final PhylogenyNode node,
2241                                           final boolean to_pdf,
2242                                           final boolean to_graphics_file ) {
2243         g.setFont( getTreeFontSet().getSmallFont() );
2244         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2245             g.setColor( Color.BLACK );
2246         }
2247         else {
2248             g.setColor( getTreeColorSet().getBranchLengthColor() );
2249         }
2250         if ( !node.isRoot() ) {
2251             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2252                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2253                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2254             }
2255             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2256                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2257                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2258             }
2259             else {
2260                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2261                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2262             }
2263         }
2264         else {
2265             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
2266                     - getTreeFontSet()._small_max_descent, g );
2267         }
2268     }
2269
2270     final private void paintBranchLite( final Graphics2D g,
2271                                         final float x1,
2272                                         final float x2,
2273                                         final float y1,
2274                                         final float y2,
2275                                         final PhylogenyNode node ) {
2276         g.setColor( getTreeColorSet().getOvColor() );
2277         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2278             drawLine( x1, y1, x2, y2, g );
2279         }
2280         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2281             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2282             ( g ).draw( _quad_curve );
2283         }
2284         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2285             final float dx = x2 - x1;
2286             final float dy = y2 - y1;
2287             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
2288                     + ( dy * 0.8f ), x2, y2 );
2289             ( g ).draw( _cubic_curve );
2290         }
2291         else {
2292             final float x2a = x2;
2293             final float x1a = x1;
2294             // draw the vertical line
2295             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
2296                 drawLine( x1, y1, x1, y2, g );
2297             }
2298             // draw the horizontal line
2299             drawLine( x1a, y2, x2a, y2, g );
2300         }
2301     }
2302
2303     /**
2304      * Paint a branch which consists of a vertical and a horizontal bar
2305      * @param is_ind_found_nodes 
2306      */
2307     final private void paintBranchRectangular( final Graphics2D g,
2308                                                final float x1,
2309                                                final float x2,
2310                                                final float y1,
2311                                                final float y2,
2312                                                final PhylogenyNode node,
2313                                                final boolean to_pdf,
2314                                                final boolean to_graphics_file ) {
2315         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
2316         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2317             drawLine( x1, y1, x2, y2, g );
2318         }
2319         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2320             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2321             g.draw( _quad_curve );
2322         }
2323         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2324             final float dx = x2 - x1;
2325             final float dy = y2 - y1;
2326             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
2327                     + ( dy * 0.8f ), x2, y2 );
2328             g.draw( _cubic_curve );
2329         }
2330         else {
2331             final float x2a = x2;
2332             final float x1a = x1;
2333             // draw the vertical line
2334             // boolean draw_horizontal = true;
2335             float y2_r = 0;
2336             if ( node.isFirstChildNode() || node.isLastChildNode()
2337                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
2338                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2339                 //boolean draw_vertical = true;
2340                 //   final PhylogenyNode parent = node.getParent();
2341                 //TODO fix below -- actually this might not be necessay anymore? test pdf, though!
2342                 //                if ( ( ( ( getOptions().isShowDefaultNodeShapes() ) && !to_pdf && !to_graphics_file ) || ( ( getControlPanel()
2343                 //                        .isEvents() ) && ( parent != null ) && parent.isHasAssignedEvent() ) )
2344                 //                        && ( _phylogeny.isRooted() || !( ( parent != null ) && parent.isRoot() ) )
2345                 //                        && !( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() && !parent
2346                 //                                .isDuplication() ) ) {
2347                 //                    if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
2348                 //                            && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2349                 //                        if ( Math.abs( y2 - y1 ) <= _half_box_size ) {
2350                 //                            draw_vertical = false;
2351                 //                        }
2352                 //                        else {
2353                 //                            if ( y1 < y2 ) {
2354                 //                                y1 += _half_box_size;
2355                 //                            }
2356                 //                            else {
2357                 //                                if ( !to_pdf ) {
2358                 //                                    y1 -= _half_box_size + 1;
2359                 //                                }
2360                 //                                else {
2361                 //                                    y1 -= _half_box_size;
2362                 //                                }
2363                 //                            }
2364                 //                        }
2365                 //                    }
2366                 //                    if ( ( x2 - x1 ) <= _half_box_size ) {
2367                 //                        draw_horizontal = false;
2368                 //                    }
2369                 //                    else if ( !draw_vertical ) {
2370                 //                        x1a += _half_box_size;
2371                 //                    }
2372                 //                    if ( ( ( x2 - x1a ) > _half_box_size )
2373                 //                            && !( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() && !node
2374                 //                                    .isDuplication() ) ) {
2375                 //                        x2a -= _half_box_size;
2376                 //                    }
2377                 //                }
2378                 //if ( draw_vertical ) {
2379                 if ( !to_graphics_file
2380                         && !to_pdf
2381                         && ( ( ( y2 < getVisibleRect().getMinY() - 20 ) && ( y1 < getVisibleRect().getMinY() - 20 ) ) || ( ( y2 > getVisibleRect()
2382                                 .getMaxY() + 20 ) && ( y1 > getVisibleRect().getMaxY() + 20 ) ) ) ) {
2383                     // Do nothing.
2384                 }
2385                 else {
2386                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2387                         float x2c = x1 + EURO_D;
2388                         if ( x2c > x2a ) {
2389                             x2c = x2a;
2390                         }
2391                         drawLine( x1, y1, x2c, y2, g );
2392                     }
2393                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2394                         if ( y2 > y1 ) {
2395                             y2_r = y2 - ROUNDED_D;
2396                             if ( y2_r < y1 ) {
2397                                 y2_r = y1;
2398                             }
2399                             drawLine( x1, y1, x1, y2_r, g );
2400                         }
2401                         else {
2402                             y2_r = y2 + ROUNDED_D;
2403                             if ( y2_r > y1 ) {
2404                                 y2_r = y1;
2405                             }
2406                             drawLine( x1, y1, x1, y2_r, g );
2407                         }
2408                     }
2409                     else {
2410                         drawLine( x1, y1, x1, y2, g );
2411                     }
2412                 }
2413                 //}
2414             }
2415             // draw the horizontal line
2416             if ( !to_graphics_file && !to_pdf
2417                     && ( ( y2 < getVisibleRect().getMinY() - 20 ) || ( y2 > getVisibleRect().getMaxY() + 20 ) ) ) {
2418                 return;
2419             }
2420             float x1_r = 0;
2421             //  if ( draw_horizontal ) {
2422             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
2423                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2424                     x1_r = x1a + ROUNDED_D;
2425                     if ( x1_r < x2a ) {
2426                         drawLine( x1_r, y2, x2a, y2, g );
2427                     }
2428                 }
2429                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2430                     final float x1c = x1a + EURO_D;
2431                     if ( x1c < x2a ) {
2432                         drawLine( x1c, y2, x2a, y2, g );
2433                     }
2434                 }
2435                 else {
2436                     drawLine( x1a, y2, x2a, y2, g );
2437                 }
2438             }
2439             else {
2440                 final double w = PhylogenyMethods.getBranchWidthValue( node );
2441                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2442                     x1_r = x1a + ROUNDED_D;
2443                     if ( x1_r < x2a ) {
2444                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
2445                     }
2446                 }
2447                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2448                     final float x1c = x1a + EURO_D;
2449                     if ( x1c < x2a ) {
2450                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
2451                     }
2452                 }
2453                 else {
2454                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
2455                 }
2456             }
2457             //}
2458             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2459                 if ( x1_r > x2a ) {
2460                     x1_r = x2a;
2461                 }
2462                 if ( y2 > y2_r ) {
2463                     final double diff = y2 - y2_r;
2464                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
2465                 }
2466                 else {
2467                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
2468                 }
2469                 g.draw( _arc );
2470             }
2471         }
2472         //TODO new *****
2473         if ( node.isExternal() ) {
2474             //TODO new *****
2475             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
2476         }
2477     }
2478
2479     final void paintCircular( final Phylogeny phy,
2480                               final double starting_angle,
2481                               final int center_x,
2482                               final int center_y,
2483                               final int radius,
2484                               final Graphics2D g,
2485                               final boolean to_pdf,
2486                               final boolean to_graphics_file ) {
2487         _circ_num_ext_nodes = phy.getNumberOfExternalNodes();
2488         _root = phy.getRoot();
2489         _root.setXcoord( center_x );
2490         _root.setYcoord( center_y );
2491         final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
2492         double current_angle = starting_angle;
2493         int i = 0;
2494         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2495             final PhylogenyNode n = it.next();
2496             n.setXcoord( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
2497             n.setYcoord( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
2498             _urt_nodeid_angle_map.put( n.getId(), current_angle );
2499             _urt_nodeid_index_map.put( n.getId(), i++ );
2500             current_angle += ( TWO_PI / _circ_num_ext_nodes );
2501         }
2502         paintCirculars( phy.getRoot(), phy, center_x, center_y, radius, radial_labels, g, to_pdf, to_graphics_file );
2503         paintNodeBox( _root.getXcoord(), _root.getYcoord(), _root, g, to_pdf, to_graphics_file, isInFoundNodes( _root ) );
2504     }
2505
2506     final void paintCircularLite( final Phylogeny phy,
2507                                   final double starting_angle,
2508                                   final int center_x,
2509                                   final int center_y,
2510                                   final int radius,
2511                                   final Graphics2D g ) {
2512         _circ_num_ext_nodes = phy.getNumberOfExternalNodes();
2513         _root = phy.getRoot();
2514         _root.setXSecondary( center_x );
2515         _root.setYSecondary( center_y );
2516         double current_angle = starting_angle;
2517         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2518             final PhylogenyNode n = it.next();
2519             n.setXSecondary( ( float ) ( center_x + radius * Math.cos( current_angle ) ) );
2520             n.setYSecondary( ( float ) ( center_y + radius * Math.sin( current_angle ) ) );
2521             _urt_nodeid_angle_map.put( n.getId(), current_angle );
2522             current_angle += ( TWO_PI / _circ_num_ext_nodes );
2523         }
2524         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
2525     }
2526
2527     final private double paintCirculars( final PhylogenyNode n,
2528                                          final Phylogeny phy,
2529                                          final float center_x,
2530                                          final float center_y,
2531                                          final double radius,
2532                                          final boolean radial_labels,
2533                                          final Graphics2D g,
2534                                          final boolean to_pdf,
2535                                          final boolean to_graphics_file ) {
2536         if ( n.isExternal() ) {
2537             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
2538                 System.out.println( "no " + n + ", ERROR!" );//TODO
2539             }
2540             return _urt_nodeid_angle_map.get( n.getId() );
2541         }
2542         else {
2543             final List<PhylogenyNode> descs = n.getDescendants();
2544             double sum = 0;
2545             for( final PhylogenyNode desc : descs ) {
2546                 sum += paintCirculars( desc,
2547                                        phy,
2548                                        center_x,
2549                                        center_y,
2550                                        radius,
2551                                        radial_labels,
2552                                        g,
2553                                        to_pdf,
2554                                        to_graphics_file );
2555             }
2556             double r = 0;
2557             if ( !n.isRoot() ) {
2558                 r = 1 - ( ( ( double ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2559             }
2560             final double theta = sum / descs.size();
2561             n.setXcoord( ( float ) ( center_x + r * radius * Math.cos( theta ) ) );
2562             n.setYcoord( ( float ) ( center_y + r * radius * Math.sin( theta ) ) );
2563             _urt_nodeid_angle_map.put( n.getId(), theta );
2564             for( final PhylogenyNode desc : descs ) {
2565                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
2566             }
2567             return theta;
2568         }
2569     }
2570
2571     final private void paintCircularsLite( final PhylogenyNode n,
2572                                            final Phylogeny phy,
2573                                            final int center_x,
2574                                            final int center_y,
2575                                            final int radius,
2576                                            final Graphics2D g ) {
2577         if ( n.isExternal() ) {
2578             return;
2579         }
2580         else {
2581             final List<PhylogenyNode> descs = n.getDescendants();
2582             for( final PhylogenyNode desc : descs ) {
2583                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
2584             }
2585             float r = 0;
2586             if ( !n.isRoot() ) {
2587                 r = 1 - ( ( ( float ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2588             }
2589             final double theta = _urt_nodeid_angle_map.get( n.getId() );
2590             n.setXSecondary( ( float ) ( center_x + radius * r * Math.cos( theta ) ) );
2591             n.setYSecondary( ( float ) ( center_y + radius * r * Math.sin( theta ) ) );
2592             for( final PhylogenyNode desc : descs ) {
2593                 paintBranchCircularLite( n, desc, g );
2594             }
2595         }
2596     }
2597
2598     final private void paintCollapsedNode( final Graphics2D g,
2599                                            final PhylogenyNode node,
2600                                            final boolean to_graphics_file,
2601                                            final boolean to_pdf,
2602                                            final boolean is_in_found_nodes ) {
2603         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2604             g.setColor( Color.BLACK );
2605         }
2606         else if ( is_in_found_nodes ) {
2607             g.setColor( getTreeColorSet().getFoundColor() );
2608         }
2609         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2610             g.setColor( getTaxonomyBasedColor( node ) );
2611         }
2612         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
2613                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2614             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
2615         }
2616         else {
2617             g.setColor( getTreeColorSet().getCollapseFillColor() );
2618         }
2619         double d = node.getAllExternalDescendants().size();
2620         if ( d > 1000 ) {
2621             d = ( 3 * _y_distance ) / 3;
2622         }
2623         else {
2624             d = ( Math.log10( d ) * _y_distance ) / 2.5;
2625         }
2626         if ( d < _box_size ) {
2627             d = _box_size;
2628         }
2629         _polygon.reset();
2630         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() - _box_size ),
2631                            ForesterUtil.roundToInt( node.getYcoord() ) );
2632         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + _box_size ),
2633                            ForesterUtil.roundToInt( node.getYcoord() - d ) );
2634         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + _box_size ),
2635                            ForesterUtil.roundToInt( node.getYcoord() + d ) );
2636         g.fillPolygon( _polygon );
2637         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
2638     }
2639
2640     @Override
2641     final public void paintComponent( final Graphics g ) {
2642         // Dimension currentSize = getSize();
2643         //  if ( offscreenImage == null || !currentSize.equals( offscreenDimension ) ) {
2644         // call the 'java.awt.Component.createImage(...)' method to get an
2645         // image
2646         //   offscreenImage = createImage( currentSize.width, currentSize.height );
2647         //  offscreenGraphics = offscreenImage.getGraphics();
2648         //  offscreenDimension = currentSize;
2649         // }
2650         // super.paintComponent( g ); //why?
2651         //final Graphics2D g2d = ( Graphics2D ) offscreenGraphics;
2652         final Graphics2D g2d = ( Graphics2D ) g;
2653         g2d.setRenderingHints( _rendering_hints );
2654         paintPhylogeny( g2d, false, false, 0, 0, 0, 0 );
2655         //g.drawImage( offscreenImage, 0, 0, this );
2656     }
2657
2658     @Override
2659     public void update( final Graphics g ) {
2660         paint( g );
2661     }
2662
2663     final private void paintConfidenceValues( final Graphics2D g,
2664                                               final PhylogenyNode node,
2665                                               final boolean to_pdf,
2666                                               final boolean to_graphics_file ) {
2667         String conf_str = "";
2668         final List<Confidence> confidences = node.getBranchData().getConfidences();
2669         if ( confidences.size() == 1 ) {
2670             final double value = node.getBranchData().getConfidence( 0 ).getValue();
2671             if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
2672                 return;
2673             }
2674             conf_str = FORMATTER_CONFIDENCE.format( value );
2675         }
2676         else if ( confidences.size() > 1 ) {
2677             boolean one_ok = false;
2678             boolean not_first = false;
2679             Collections.sort( confidences );
2680             final StringBuilder sb = new StringBuilder();
2681             for( final Confidence confidence : confidences ) {
2682                 final double value = confidence.getValue();
2683                 if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2684                     if ( value >= getOptions().getMinConfidenceValue() ) {
2685                         one_ok = true;
2686                     }
2687                     if ( not_first ) {
2688                         sb.append( "/" );
2689                     }
2690                     else {
2691                         not_first = true;
2692                     }
2693                     sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
2694                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2695                 }
2696             }
2697             if ( one_ok ) {
2698                 conf_str = sb.toString();
2699             }
2700         }
2701         if ( conf_str.length() > 0 ) {
2702             final double parent_x = node.getParent().getXcoord();
2703             double x = node.getXcoord();
2704             g.setFont( getTreeFontSet().getSmallFont() );
2705             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2706                 x += EURO_D;
2707             }
2708             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2709                 x += ROUNDED_D;
2710             }
2711             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2712                 g.setColor( Color.BLACK );
2713             }
2714             else {
2715                 g.setColor( getTreeColorSet().getConfidenceColor() );
2716             }
2717             TreePanel
2718                     .drawString( conf_str,
2719                                  parent_x
2720                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
2721                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
2722                                  g );
2723         }
2724     }
2725
2726     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
2727         g.setColor( getTreeColorSet().getFoundColor() );
2728         g.fillRect( x - _half_box_size, y - _half_box_size, _box_size, _box_size );
2729     }
2730
2731     final private void paintGainedAndLostCharacters( final Graphics2D g,
2732                                                      final PhylogenyNode node,
2733                                                      final String gained,
2734                                                      final String lost ) {
2735         if ( node.getParent() != null ) {
2736             final double parent_x = node.getParent().getXcoord();
2737             final double x = node.getXcoord();
2738             g.setFont( getTreeFontSet().getLargeFont() );
2739             g.setColor( getTreeColorSet().getGainedCharactersColor() );
2740             if ( Constants.SPECIAL_CUSTOM ) {
2741                 g.setColor( Color.BLUE );
2742             }
2743             TreePanel
2744                     .drawString( gained,
2745                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
2746                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
2747                                  g );
2748             g.setColor( getTreeColorSet().getLostCharactersColor() );
2749             TreePanel.drawString( lost,
2750                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
2751                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
2752                                   g );
2753         }
2754     }
2755
2756     /**
2757      * Draw a box at the indicated node.
2758      * 
2759      * @param x
2760      * @param y
2761      * @param node
2762      * @param g
2763      */
2764     final private void paintNodeBox( final double x,
2765                                      final double y,
2766                                      final PhylogenyNode node,
2767                                      final Graphics2D g,
2768                                      final boolean to_pdf,
2769                                      final boolean to_graphics_file,
2770                                      final boolean is_in_found_nodes ) {
2771         if ( node.isCollapse() ) {
2772             return;
2773         }
2774         // if this node should be highlighted, do so
2775         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
2776             g.setColor( getTreeColorSet().getFoundColor() );
2777             drawOval( x - 8, y - 8, 16, 16, g );
2778             drawOval( x - 9, y - 8, 17, 17, g );
2779             drawOval( x - 9, y - 9, 18, 18, g );
2780         }
2781         if ( is_in_found_nodes ) {
2782             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
2783         }
2784         else {
2785             Color outline_color = null;
2786             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2787                 outline_color = Color.BLACK;
2788             }
2789             else if ( getControlPanel().isEvents() && Util.isHasAssignedEvent( node ) ) {
2790                 final Event event = node.getNodeData().getEvent();
2791                 if ( event.isDuplication() ) {
2792                     outline_color = getTreeColorSet().getDuplicationBoxColor();
2793                 }
2794                 else if ( event.isSpeciation() ) {
2795                     outline_color = getTreeColorSet().getSpecBoxColor();
2796                 }
2797                 else if ( event.isSpeciationOrDuplication() ) {
2798                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
2799                 }
2800             }
2801             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
2802                 outline_color = getTaxonomyBasedColor( node );
2803             }
2804             else {
2805                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
2806             }
2807             if ( ( ( getOptions().isShowDefaultNodeShapes() ) /*&& !to_pdf && !to_graphics_file*/)
2808                     || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
2809                 if ( to_pdf || to_graphics_file ) {
2810                     if ( node.isDuplication() || !getOptions().isPrintBlackAndWhite() ) {
2811                         //TODO FIXME
2812                         //drawOvalFilled( x - _half_box_size, y - _half_box_size, _box_size, _box_size, g );
2813                         drawOvalGradient( x - _half_box_size,
2814                                           y - _half_box_size,
2815                                           _box_size,
2816                                           _box_size,
2817                                           g,
2818                                           outline_color,
2819                                           getBackground(),
2820                                           outline_color );
2821                     }
2822                 }
2823                 else {
2824                     //TODO FIXME
2825                     //drawRectFilled( x - HALF_BOX_SIZE, y - HALF_BOX_SIZE, BOX_SIZE, BOX_SIZE, g );
2826                     if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
2827                         if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2828                             drawOvalGradient( x - _half_box_size,
2829                                               y - _half_box_size,
2830                                               _box_size,
2831                                               _box_size,
2832                                               g,
2833                                               outline_color,
2834                                               getBackground(),
2835                                               outline_color );
2836                         }
2837                         else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
2838                             drawOvalGradient( x - _half_box_size,
2839                                               y - _half_box_size,
2840                                               _box_size,
2841                                               _box_size,
2842                                               g,
2843                                               getBackground(),
2844                                               getBackground(),
2845                                               outline_color );
2846                         }
2847                         else if ( getOptions().getDefaultNodeFill() == NodeFill.SOLID ) {
2848                             g.setColor( outline_color );
2849                             drawOvalFilled( x - _half_box_size, y - _half_box_size, _box_size, _box_size, g );
2850                         }
2851                     }
2852                     else if ( getOptions().getDefaultNodeShape() == NodeShape.RECTANGLE ) {
2853                         if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2854                             drawRectGradient( x - _half_box_size,
2855                                               y - _half_box_size,
2856                                               _box_size,
2857                                               _box_size,
2858                                               g,
2859                                               outline_color,
2860                                               getBackground(),
2861                                               outline_color );
2862                         }
2863                         else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
2864                             drawRectGradient( x - _half_box_size,
2865                                               y - _half_box_size,
2866                                               _box_size,
2867                                               _box_size,
2868                                               g,
2869                                               getBackground(),
2870                                               getBackground(),
2871                                               outline_color );
2872                         }
2873                         else if ( getOptions().getDefaultNodeFill() == NodeFill.SOLID ) {
2874                             g.setColor( outline_color );
2875                             drawRectFilled( x - _half_box_size, y - _half_box_size, _box_size, _box_size, g );
2876                         }
2877                     }
2878                 }
2879             }
2880         }
2881     }
2882
2883     final private void paintNodeData( final Graphics2D g,
2884                                       final PhylogenyNode node,
2885                                       final boolean to_graphics_file,
2886                                       final boolean to_pdf,
2887                                       final boolean is_in_found_nodes ) {
2888         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
2889             return;
2890         }
2891         if ( getOptions().isShowBranchLengthValues()
2892                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
2893                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
2894                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyNode.DISTANCE_DEFAULT ) ) {
2895             paintBranchLength( g, node, to_pdf, to_graphics_file );
2896         }
2897         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
2898             return;
2899         }
2900         int x = 0;
2901         if ( getControlPanel().isShowTaxonomyImages()
2902                 && ( getImageMap() != null )
2903                 && !getImageMap().isEmpty()
2904                 && node.getNodeData().isHasTaxonomy()
2905                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
2906                         .getUris().isEmpty() ) ) {
2907             x += drawTaxonomyImage( node.getXcoord() + 2 + _half_box_size, node.getYcoord(), node, g );
2908         }
2909         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
2910                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
2911             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
2912         }
2913         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2914             g.setColor( Color.BLACK );
2915         }
2916         else if ( is_in_found_nodes ) {
2917             g.setColor( getTreeColorSet().getFoundColor() );
2918         }
2919         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2920             g.setColor( getTaxonomyBasedColor( node ) );
2921         }
2922         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
2923                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2924             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
2925         }
2926         else if ( to_pdf ) {
2927             g.setColor( Color.BLACK );
2928         }
2929         else {
2930             g.setColor( getTreeColorSet().getSequenceColor() );
2931         }
2932         _sb.setLength( 0 );
2933         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
2934             _sb.append( " [" );
2935             _sb.append( node.getAllExternalDescendants().size() );
2936             _sb.append( "]" );
2937         }
2938         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
2939             if ( _sb.length() > 0 ) {
2940                 _sb.append( " " );
2941             }
2942             _sb.append( node.getName() );
2943         }
2944         if ( node.getNodeData().isHasSequence() ) {
2945             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
2946                 if ( _sb.length() > 0 ) {
2947                     _sb.append( " " );
2948                 }
2949                 _sb.append( node.getNodeData().getSequence().getSymbol() );
2950             }
2951             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
2952                 if ( _sb.length() > 0 ) {
2953                     _sb.append( " " );
2954                 }
2955                 _sb.append( node.getNodeData().getSequence().getName() );
2956             }
2957             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
2958                 if ( _sb.length() > 0 ) {
2959                     _sb.append( " " );
2960                 }
2961                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
2962                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
2963                     _sb.append( ":" );
2964                 }
2965                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
2966             }
2967         }
2968         g.setFont( getTreeFontSet().getLargeFont() );
2969         if ( is_in_found_nodes ) {
2970             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
2971         }
2972         double down_shift_factor = 3.0;
2973         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
2974             down_shift_factor = 1;
2975         }
2976         final double pos_x = node.getXcoord() + x + 2 + _half_box_size;
2977         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
2978         final String sb_str = _sb.toString();
2979         // GUILHEM_BEG ______________
2980         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
2981                 && ( _query_sequence != null ) ) {
2982             int nodeTextBoundsWidth = 0;
2983             if ( sb_str.length() > 0 ) {
2984                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
2985                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
2986             }
2987             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
2988                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
2989                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
2990                     g.setColor( getTreeColorSet().getBackgroundColor() );
2991                 }
2992             }
2993             else {
2994                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
2995                 for( final SequenceRelation seqRelation : seqRelations ) {
2996                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
2997                             .getRef1().isEqual( _query_sequence ) )
2998                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
2999                                     .getSelectedItem() );
3000                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
3001                         final double linePosX = node.getXcoord() + 2 + _half_box_size;
3002                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
3003                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
3004                                 + ")";
3005                         if ( sConfidence != null ) {
3006                             double confidenceX = pos_x;
3007                             if ( sb_str.length() > 0 ) {
3008                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
3009                                         + CONFIDENCE_LEFT_MARGIN;
3010                             }
3011                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
3012                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
3013                                         .getBounds().getWidth();
3014                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
3015                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
3016                             }
3017                         }
3018                         if ( x + nodeTextBoundsWidth > 0 ) /* we only underline if there is something displayed */
3019                         {
3020                             if ( nodeTextBoundsWidth == 0 ) {
3021                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
3022                             }
3023                             else {
3024                                 nodeTextBoundsWidth += 2;
3025                             }
3026                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
3027                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
3028                             break;
3029                         }
3030                     }
3031                 }
3032             }
3033         }
3034         if ( sb_str.length() > 0 ) {
3035             TreePanel.drawString( sb_str, pos_x, pos_y, g );
3036         }
3037         // GUILHEM_END _____________
3038         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
3039         // TODO FIXME need to check this one!
3040         //if ( _sb.length() > 0 ) {
3041         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
3042         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3043         //}
3044         // COMMENTED_OUT_BY_GUILHEM_END ________________
3045         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
3046                 && ( node.getNodeData().getSequence().getAnnotations() != null )
3047                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
3048             if ( _sb.length() > 0 ) {
3049                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3050             }
3051             final Annotation ann = node.getNodeData().getSequence().getAnnotation( 0 );
3052             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3053                 g.setColor( Color.BLACK );
3054             }
3055             else {
3056                 g.setColor( calculateColorForAnnotation( ann ) );
3057             }
3058             final String ann_str = ann.asSimpleText().toString();
3059             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + _half_box_size, node.getYcoord()
3060                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3061             _sb.setLength( 0 );
3062             _sb.append( ann_str );
3063         }
3064         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3065                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3066                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3067             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
3068                     && node.getNodeData().isHasBinaryCharacters() ) {
3069                 if ( _sb.length() > 0 ) {
3070                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3071                 }
3072                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3073                     g.setColor( Color.BLACK );
3074                 }
3075                 else {
3076                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
3077                 }
3078                 if ( getControlPanel().isShowBinaryCharacters() ) {
3079                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
3080                             .toString(), node.getXcoord() + x + 1 + _half_box_size, node.getYcoord()
3081                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3082                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
3083                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
3084                             .getLostCharactersAsStringBuffer().toString() );
3085                 }
3086                 else {
3087                     if ( DRAW_MEAN_COUNTS && node.isInternal() ) {
3088                         final List<PhylogenyNode> ec = node.getAllExternalDescendants();
3089                         double sum = 0;
3090                         int count = 0;
3091                         for( final PhylogenyNode phylogenyNode : ec ) {
3092                             count++;
3093                             if ( phylogenyNode.getNodeData().getBinaryCharacters() != null ) {
3094                                 sum += phylogenyNode.getNodeData().getBinaryCharacters().getPresentCount();
3095                             }
3096                         }
3097                         final double mean = ForesterUtil.round( sum / count, 1 );
3098                         TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount() + " ["
3099                                 + mean + "]", node.getXcoord() + x + 4 + _half_box_size, node.getYcoord()
3100                                 + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3101                     }
3102                     else {
3103                         TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
3104                                               node.getXcoord() + x + 4 + _half_box_size,
3105                                               node.getYcoord()
3106                                                       + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
3107                                               g );
3108                     }
3109                     paintGainedAndLostCharacters( g, node, "+"
3110                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
3111                             + node.getNodeData().getBinaryCharacters().getLostCount() );
3112                 }
3113             }
3114         }
3115     }
3116
3117     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
3118         final List<Uri> us = new ArrayList<Uri>();
3119         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
3120             for( final Uri uri : t.getUris() ) {
3121                 us.add( uri );
3122             }
3123         }
3124         double offset = 0;
3125         for( final Uri uri : us ) {
3126             if ( uri != null ) {
3127                 final String uri_str = uri.getValue().toString().toLowerCase();
3128                 if ( getImageMap().containsKey( uri_str ) ) {
3129                     final BufferedImage bi = getImageMap().get( uri_str );
3130                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
3131                         double scaling_factor = 1;
3132                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
3133                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
3134                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
3135                         }
3136                         // y = y - ( 0.9 * getYdistance() );
3137                         final double hs = bi.getHeight() * scaling_factor;
3138                         double ws = bi.getWidth() * scaling_factor + offset;
3139                         final double my_y = y - ( 0.5 * hs );
3140                         final int x_w = ( int ) ( x + ws + 0.5 );
3141                         final int y_h = ( int ) ( my_y + hs + 0.5 );
3142                         if ( ( x_w - x > 7 ) && ( y_h - my_y > 7 ) ) {
3143                             g.drawImage( bi,
3144                                          ( int ) ( x + 0.5 + offset ),
3145                                          ( int ) ( my_y + 0.5 ),
3146                                          x_w,
3147                                          y_h,
3148                                          0,
3149                                          0,
3150                                          bi.getWidth(),
3151                                          bi.getHeight(),
3152                                          null );
3153                             ws += 8;
3154                         }
3155                         else {
3156                             ws = 0.0;
3157                         }
3158                         offset = ws;
3159                     }
3160                 }
3161             }
3162         }
3163         return offset;
3164     }
3165
3166     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
3167                                                   final PhylogenyNode node,
3168                                                   final boolean to_pdf,
3169                                                   final boolean to_graphics_file,
3170                                                   final boolean radial_labels,
3171                                                   final double ur_angle,
3172                                                   final boolean is_in_found_nodes ) {
3173         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
3174             return;
3175         }
3176         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3177             g.setColor( Color.BLACK );
3178         }
3179         else if ( is_in_found_nodes ) {
3180             g.setColor( getTreeColorSet().getFoundColor() );
3181         }
3182         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3183             g.setColor( getTaxonomyBasedColor( node ) );
3184         }
3185         else {
3186             g.setColor( getTreeColorSet().getSequenceColor() );
3187         }
3188         _sb.setLength( 0 );
3189         _sb.append( " " );
3190         if ( node.getNodeData().isHasTaxonomy()
3191                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3192                         .isShowTaxonomyCommonNames() ) ) {
3193             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3194             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3195                 _sb.append( taxonomy.getTaxonomyCode() );
3196                 _sb.append( " " );
3197             }
3198             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3199                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3200                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3201                     _sb.append( taxonomy.getScientificName() );
3202                     _sb.append( " (" );
3203                     _sb.append( taxonomy.getCommonName() );
3204                     _sb.append( ") " );
3205                 }
3206                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3207                     _sb.append( taxonomy.getScientificName() );
3208                     _sb.append( " " );
3209                 }
3210                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3211                     _sb.append( taxonomy.getCommonName() );
3212                     _sb.append( " " );
3213                 }
3214             }
3215             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3216                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3217                     _sb.append( taxonomy.getScientificName() );
3218                     _sb.append( " " );
3219                 }
3220             }
3221             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3222                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3223                     _sb.append( taxonomy.getCommonName() );
3224                     _sb.append( " " );
3225                 }
3226             }
3227         }
3228         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3229             _sb.append( " [" );
3230             _sb.append( node.getAllExternalDescendants().size() );
3231             _sb.append( "]" );
3232         }
3233         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3234             if ( _sb.length() > 0 ) {
3235                 _sb.append( " " );
3236             }
3237             _sb.append( node.getName() );
3238         }
3239         if ( node.getNodeData().isHasSequence() ) {
3240             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3241                 if ( _sb.length() > 0 ) {
3242                     _sb.append( " " );
3243                 }
3244                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3245                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3246                     _sb.append( ":" );
3247                 }
3248                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3249             }
3250             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3251                 if ( _sb.length() > 0 ) {
3252                     _sb.append( " " );
3253                 }
3254                 _sb.append( node.getNodeData().getSequence().getName() );
3255             }
3256         }
3257         g.setFont( getTreeFontSet().getLargeFont() );
3258         if ( is_in_found_nodes ) {
3259             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3260         }
3261         if ( _sb.length() > 1 ) {
3262             final String sb_str = _sb.toString();
3263             double m = 0;
3264             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3265                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
3266             }
3267             else {
3268                 m = ( float ) ( ur_angle % TWO_PI );
3269             }
3270             _at = g.getTransform();
3271             boolean need_to_reset = false;
3272             final float x_coord = node.getXcoord();
3273             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
3274             if ( radial_labels ) {
3275                 need_to_reset = true;
3276                 boolean left = false;
3277                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3278                     m -= PI;
3279                     left = true;
3280                 }
3281                 g.rotate( m, x_coord, node.getYcoord() );
3282                 if ( left ) {
3283                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
3284                 }
3285             }
3286             else {
3287                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3288                     need_to_reset = true;
3289                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
3290                 }
3291             }
3292             TreePanel.drawString( sb_str, x_coord, y_coord, g );
3293             if ( need_to_reset ) {
3294                 g.setTransform( _at );
3295             }
3296         }
3297     }
3298
3299     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
3300         if ( node.isCollapse() ) {
3301             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3302                 paintCollapsedNode( g, node, false, false, false );
3303             }
3304             return;
3305         }
3306         if ( isInFoundNodes( node ) ) {
3307             g.setColor( getTreeColorSet().getFoundColor() );
3308             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
3309         }
3310         float new_x = 0;
3311         if ( !node.isExternal() && !node.isCollapse() ) {
3312             boolean first_child = true;
3313             float y2 = 0.0f;
3314             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3315             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3316                 final PhylogenyNode child_node = node.getChildNode( i );
3317                 int factor_x;
3318                 if ( !isUniformBranchLengthsForCladogram() ) {
3319                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3320                 }
3321                 else {
3322                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3323                 }
3324                 if ( first_child ) {
3325                     first_child = false;
3326                     y2 = node.getYSecondary()
3327                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
3328                                     .getNumberOfExternalNodes() ) );
3329                 }
3330                 else {
3331                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3332                 }
3333                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
3334                 new_x = x2 + node.getXSecondary();
3335                 final float diff_y = node.getYSecondary() - y2;
3336                 final float diff_x = node.getXSecondary() - new_x;
3337                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
3338                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
3339                 }
3340                 child_node.setXSecondary( new_x );
3341                 child_node.setYSecondary( y2 );
3342                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3343             }
3344         }
3345     }
3346
3347     final private void paintNodeRectangular( final Graphics2D g,
3348                                              final PhylogenyNode node,
3349                                              final boolean to_pdf,
3350                                              final boolean dynamically_hide,
3351                                              final int dynamic_hiding_factor,
3352                                              final boolean to_graphics_file ) {
3353         final boolean is_in_found_nodes = isInFoundNodes( node );
3354         if ( node.isCollapse() ) {
3355             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3356                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3357             }
3358             return;
3359         }
3360         if ( node.isExternal() ) {
3361             ++_external_node_index;
3362         }
3363         // Confidence values
3364         if ( getControlPanel().isShowBootstrapValues()
3365                 && !node.isExternal()
3366                 && !node.isRoot()
3367                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
3368                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3369                 && node.getBranchData().isHasConfidences() ) {
3370             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
3371         }
3372         // Draw a line to root:
3373         if ( node.isRoot() && _phylogeny.isRooted() ) {
3374             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
3375         }
3376         float new_x = 0;
3377         float new_x_min = Float.MAX_VALUE;
3378         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
3379         float min_dist = 1.5f;
3380         if ( !disallow_shortcutting ) {
3381             //   System.out.println( dynamic_hiding_factor );
3382             if ( dynamic_hiding_factor > 4000 ) {
3383                 min_dist = 4;
3384             }
3385             else if ( dynamic_hiding_factor > 1000 ) {
3386                 min_dist = 3;
3387             }
3388             else if ( dynamic_hiding_factor > 100 ) {
3389                 min_dist = 2;
3390             }
3391         }
3392         if ( !node.isExternal() && !node.isCollapse() ) {
3393             boolean first_child = true;
3394             float y2 = 0.0f;
3395             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3396             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3397                 final PhylogenyNode child_node = node.getChildNode( i );
3398                 int factor_x;
3399                 if ( !isUniformBranchLengthsForCladogram() ) {
3400                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3401                 }
3402                 else {
3403                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3404                 }
3405                 if ( first_child ) {
3406                     first_child = false;
3407                     y2 = node.getYcoord()
3408                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3409                 }
3410                 else {
3411                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
3412                 }
3413                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
3414                 new_x = x2 + node.getXcoord();
3415                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
3416                     new_x_min = x2;
3417                 }
3418                 final float diff_y = node.getYcoord() - y2;
3419                 final float diff_x = node.getXcoord() - new_x;
3420                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
3421                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
3422                     paintBranchRectangular( g,
3423                                             node.getXcoord(),
3424                                             new_x,
3425                                             node.getYcoord(),
3426                                             y2,
3427                                             child_node,
3428                                             to_pdf,
3429                                             to_graphics_file );
3430                 }
3431                 child_node.setXcoord( new_x );
3432                 child_node.setYcoord( y2 );
3433                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
3434             }
3435             //TODO new *****
3436             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
3437             //TODO new *****
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 + _half_box_size + 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 }