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