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