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