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