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