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