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