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