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