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