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