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