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