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