c64a82047f836799b6195b00d1b01abcfd6b61e4
[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();
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();
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() {
2536         final Phylogeny phy = getPhylogeny();
2537         _collapsed_external_nodeid_set.clear();
2538         if ( phy != null ) {
2539             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2540                 final PhylogenyNode ext_node = it.next();
2541                 PhylogenyNode n = ext_node;
2542                 while ( !n.isRoot() ) {
2543                     if ( n.isCollapse() ) {
2544                         _collapsed_external_nodeid_set.add( ext_node.getId() );
2545                         ext_node.setCollapse( true );
2546                         continue E;
2547                     }
2548                     n = n.getParent();
2549                 }
2550             }
2551         }
2552     }
2553
2554     final void paintCircularLite( final Phylogeny phy,
2555                                   final double starting_angle,
2556                                   final int center_x,
2557                                   final int center_y,
2558                                   final int radius,
2559                                   final Graphics2D g ) {
2560         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes();
2561         _root = phy.getRoot();
2562         _root.setXSecondary( center_x );
2563         _root.setYSecondary( center_y );
2564         double current_angle = starting_angle;
2565         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2566             final PhylogenyNode n = it.next();
2567             n.setXSecondary( ( float ) ( center_x + radius * Math.cos( current_angle ) ) );
2568             n.setYSecondary( ( float ) ( center_y + radius * Math.sin( current_angle ) ) );
2569             _urt_nodeid_angle_map.put( n.getId(), current_angle );
2570             current_angle += ( TWO_PI / circ_num_ext_nodes );
2571         }
2572         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
2573     }
2574
2575     final private double paintCirculars( final PhylogenyNode n,
2576                                          final Phylogeny phy,
2577                                          final float center_x,
2578                                          final float center_y,
2579                                          final double radius,
2580                                          final boolean radial_labels,
2581                                          final Graphics2D g,
2582                                          final boolean to_pdf,
2583                                          final boolean to_graphics_file ) {
2584         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
2585             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
2586                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
2587             }
2588             return _urt_nodeid_angle_map.get( n.getId() );
2589         }
2590         else {
2591             final List<PhylogenyNode> descs = n.getDescendants();
2592             double sum = 0;
2593             for( final PhylogenyNode desc : descs ) {
2594                 sum += paintCirculars( desc,
2595                                        phy,
2596                                        center_x,
2597                                        center_y,
2598                                        radius,
2599                                        radial_labels,
2600                                        g,
2601                                        to_pdf,
2602                                        to_graphics_file );
2603             }
2604             double r = 0;
2605             if ( !n.isRoot() ) {
2606                 r = 1 - ( ( ( double ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2607             }
2608             final double theta = sum / descs.size();
2609             n.setXcoord( ( float ) ( center_x + r * radius * Math.cos( theta ) ) );
2610             n.setYcoord( ( float ) ( center_y + r * radius * Math.sin( theta ) ) );
2611             _urt_nodeid_angle_map.put( n.getId(), theta );
2612             for( final PhylogenyNode desc : descs ) {
2613                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
2614             }
2615             return theta;
2616         }
2617     }
2618
2619     final private void paintCircularsLite( final PhylogenyNode n,
2620                                            final Phylogeny phy,
2621                                            final int center_x,
2622                                            final int center_y,
2623                                            final int radius,
2624                                            final Graphics2D g ) {
2625         if ( n.isExternal() ) {
2626             return;
2627         }
2628         else {
2629             final List<PhylogenyNode> descs = n.getDescendants();
2630             for( final PhylogenyNode desc : descs ) {
2631                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
2632             }
2633             float r = 0;
2634             if ( !n.isRoot() ) {
2635                 r = 1 - ( ( ( float ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2636             }
2637             final double theta = _urt_nodeid_angle_map.get( n.getId() );
2638             n.setXSecondary( ( float ) ( center_x + radius * r * Math.cos( theta ) ) );
2639             n.setYSecondary( ( float ) ( center_y + radius * r * Math.sin( theta ) ) );
2640             for( final PhylogenyNode desc : descs ) {
2641                 paintBranchCircularLite( n, desc, g );
2642             }
2643         }
2644     }
2645
2646     final private void paintCollapsedNode( final Graphics2D g,
2647                                            final PhylogenyNode node,
2648                                            final boolean to_graphics_file,
2649                                            final boolean to_pdf,
2650                                            final boolean is_in_found_nodes ) {
2651         Color c = null;
2652         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2653             c = Color.BLACK;
2654         }
2655         else if ( is_in_found_nodes ) {
2656             c = getTreeColorSet().getFoundColor();
2657         }
2658         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2659             c = getTaxonomyBasedColor( node );
2660         }
2661         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
2662                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2663             c = PhylogenyMethods.getBranchColorValue( node );
2664         }
2665         else {
2666             c = getTreeColorSet().getCollapseFillColor();
2667         }
2668         double d = node.getAllExternalDescendants().size();
2669         if ( d > 1000 ) {
2670             d = ( 3 * _y_distance ) / 3;
2671         }
2672         else {
2673             d = ( Math.log10( d ) * _y_distance ) / 2.5;
2674         }
2675         final int box_size = getOptions().getDefaultNodeShapeSize();
2676         if ( d < box_size ) {
2677             d = box_size;
2678         }
2679         _polygon.reset();
2680         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() - box_size ),
2681                            ForesterUtil.roundToInt( node.getYcoord() ) );
2682         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
2683                            ForesterUtil.roundToInt( node.getYcoord() - d ) );
2684         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
2685                            ForesterUtil.roundToInt( node.getYcoord() + d ) );
2686         if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2687             g.setColor( c );
2688             g.fillPolygon( _polygon );
2689         }
2690         else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
2691             g.setColor( getBackground() );
2692             g.fillPolygon( _polygon );
2693             g.setColor( c );
2694             g.drawPolygon( _polygon );
2695         }
2696         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2697             g.setPaint( new GradientPaint( node.getXcoord() - box_size, node.getYcoord(), getBackground(), ( node
2698                     .getXcoord() + box_size ), ( float ) ( node.getYcoord() - d ), c, false ) );
2699             g.fill( _polygon );
2700             g.setPaint( c );
2701             g.draw( _polygon );
2702         }
2703         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
2704     }
2705
2706     @Override
2707     final public void paintComponent( final Graphics g ) {
2708         // Dimension currentSize = getSize();
2709         //  if ( offscreenImage == null || !currentSize.equals( offscreenDimension ) ) {
2710         // call the 'java.awt.Component.createImage(...)' method to get an
2711         // image
2712         //   offscreenImage = createImage( currentSize.width, currentSize.height );
2713         //  offscreenGraphics = offscreenImage.getGraphics();
2714         //  offscreenDimension = currentSize;
2715         // }
2716         // super.paintComponent( g ); //why?
2717         //final Graphics2D g2d = ( Graphics2D ) offscreenGraphics;
2718         final Graphics2D g2d = ( Graphics2D ) g;
2719         g2d.setRenderingHints( _rendering_hints );
2720         paintPhylogeny( g2d, false, false, 0, 0, 0, 0 );
2721         //g.drawImage( offscreenImage, 0, 0, this );
2722     }
2723
2724     @Override
2725     public void update( final Graphics g ) {
2726         paint( g );
2727     }
2728
2729     final private void paintConfidenceValues( final Graphics2D g,
2730                                               final PhylogenyNode node,
2731                                               final boolean to_pdf,
2732                                               final boolean to_graphics_file ) {
2733         final List<Confidence> confidences = node.getBranchData().getConfidences();
2734         //        if ( confidences.size() == 1 ) {
2735         //            final double value = node.getBranchData().getConfidence( 0 ).getValue();
2736         //            if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
2737         //                return;
2738         //            }
2739         //            conf_str = FORMATTER_CONFIDENCE.format( value );
2740         //        }
2741         //        else if ( confidences.size() > 1 ) {
2742         boolean one_ok = false;
2743         boolean not_first = false;
2744         Collections.sort( confidences );
2745         final StringBuilder sb = new StringBuilder();
2746         String conf_str = "";
2747         for( final Confidence confidence : confidences ) {
2748             final double value = confidence.getValue();
2749             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2750                 if ( value >= getOptions().getMinConfidenceValue() ) {
2751                     one_ok = true;
2752                 }
2753                 if ( not_first ) {
2754                     sb.append( "/" );
2755                 }
2756                 else {
2757                     not_first = true;
2758                 }
2759                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
2760                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2761                 if ( getOptions().isShowConfidenceStddev() ) {
2762                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2763                         sb.append( "(" );
2764                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
2765                                                                                     getOptions()
2766                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2767                         sb.append( ")" );
2768                     }
2769                 }
2770             }
2771             //}
2772             if ( one_ok ) {
2773                 conf_str = sb.toString();
2774             }
2775         }
2776         if ( conf_str.length() > 0 ) {
2777             final double parent_x = node.getParent().getXcoord();
2778             double x = node.getXcoord();
2779             g.setFont( getTreeFontSet().getSmallFont() );
2780             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2781                 x += EURO_D;
2782             }
2783             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2784                 x += ROUNDED_D;
2785             }
2786             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2787                 g.setColor( Color.BLACK );
2788             }
2789             else {
2790                 g.setColor( getTreeColorSet().getConfidenceColor() );
2791             }
2792             TreePanel
2793                     .drawString( conf_str,
2794                                  parent_x
2795                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
2796                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
2797                                  g );
2798         }
2799     }
2800
2801     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
2802         final int box_size = getOptions().getDefaultNodeShapeSize();
2803         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2804         g.setColor( getTreeColorSet().getFoundColor() );
2805         g.fillRect( x - half_box_size, y - half_box_size, box_size, box_size );
2806     }
2807
2808     final private void paintGainedAndLostCharacters( final Graphics2D g,
2809                                                      final PhylogenyNode node,
2810                                                      final String gained,
2811                                                      final String lost ) {
2812         if ( node.getParent() != null ) {
2813             final double parent_x = node.getParent().getXcoord();
2814             final double x = node.getXcoord();
2815             g.setFont( getTreeFontSet().getLargeFont() );
2816             g.setColor( getTreeColorSet().getGainedCharactersColor() );
2817             if ( Constants.SPECIAL_CUSTOM ) {
2818                 g.setColor( Color.BLUE );
2819             }
2820             TreePanel
2821                     .drawString( gained,
2822                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
2823                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
2824                                  g );
2825             g.setColor( getTreeColorSet().getLostCharactersColor() );
2826             TreePanel.drawString( lost,
2827                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
2828                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
2829                                   g );
2830         }
2831     }
2832
2833     /**
2834      * Draw a box at the indicated node.
2835      * 
2836      * @param x
2837      * @param y
2838      * @param node
2839      * @param g
2840      */
2841     final private void paintNodeBox( final double x,
2842                                      final double y,
2843                                      final PhylogenyNode node,
2844                                      final Graphics2D g,
2845                                      final boolean to_pdf,
2846                                      final boolean to_graphics_file,
2847                                      final boolean is_in_found_nodes ) {
2848         if ( node.isCollapse() ) {
2849             return;
2850         }
2851         // if this node should be highlighted, do so
2852         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
2853             g.setColor( getTreeColorSet().getFoundColor() );
2854             drawOval( x - 8, y - 8, 16, 16, g );
2855             drawOval( x - 9, y - 8, 17, 17, g );
2856             drawOval( x - 9, y - 9, 18, 18, g );
2857         }
2858         if ( is_in_found_nodes ) {
2859             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
2860         }
2861         else {
2862             Color outline_color = null;
2863             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2864                 outline_color = Color.BLACK;
2865             }
2866             else if ( getControlPanel().isEvents() && AptxUtil.isHasAssignedEvent( node ) ) {
2867                 final Event event = node.getNodeData().getEvent();
2868                 if ( event.isDuplication() ) {
2869                     outline_color = getTreeColorSet().getDuplicationBoxColor();
2870                 }
2871                 else if ( event.isSpeciation() ) {
2872                     outline_color = getTreeColorSet().getSpecBoxColor();
2873                 }
2874                 else if ( event.isSpeciationOrDuplication() ) {
2875                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
2876                 }
2877             }
2878             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
2879                 outline_color = getTaxonomyBasedColor( node );
2880             }
2881             else {
2882                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
2883                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
2884                     outline_color = getTreeColorSet().getBranchColorForPdf();
2885                 }
2886             }
2887             final int box_size = getOptions().getDefaultNodeShapeSize();
2888             final int half_box_size = box_size / 2;
2889             if ( getOptions().isShowDefaultNodeShapes() || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
2890                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
2891                     if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2892                         drawOvalGradient( x - half_box_size,
2893                                           y - half_box_size,
2894                                           box_size,
2895                                           box_size,
2896                                           g,
2897                                           to_pdf ? Color.WHITE : outline_color,
2898                                           to_pdf ? outline_color : getBackground(),
2899                                           outline_color );
2900                     }
2901                     else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
2902                         Color background = getBackground();
2903                         if ( to_pdf ) {
2904                             background = Color.WHITE;
2905                         }
2906                         drawOvalGradient( x - half_box_size,
2907                                           y - half_box_size,
2908                                           box_size,
2909                                           box_size,
2910                                           g,
2911                                           background,
2912                                           background,
2913                                           outline_color );
2914                     }
2915                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2916                         g.setColor( outline_color );
2917                         drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2918                     }
2919                 }
2920                 else if ( getOptions().getDefaultNodeShape() == NodeVisualization.NodeShape.RECTANGLE ) {
2921                     if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.GRADIENT ) {
2922                         drawRectGradient( x - half_box_size,
2923                                           y - half_box_size,
2924                                           box_size,
2925                                           box_size,
2926                                           g,
2927                                           to_pdf ? Color.WHITE : outline_color,
2928                                           to_pdf ? outline_color : getBackground(),
2929                                           outline_color );
2930                     }
2931                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
2932                         Color background = getBackground();
2933                         if ( to_pdf ) {
2934                             background = Color.WHITE;
2935                         }
2936                         drawRectGradient( x - half_box_size,
2937                                           y - half_box_size,
2938                                           box_size,
2939                                           box_size,
2940                                           g,
2941                                           background,
2942                                           background,
2943                                           outline_color );
2944                     }
2945                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2946                         g.setColor( outline_color );
2947                         drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2948                     }
2949                 }
2950             }
2951         }
2952     }
2953
2954     final private void paintNodeData( final Graphics2D g,
2955                                       final PhylogenyNode node,
2956                                       final boolean to_graphics_file,
2957                                       final boolean to_pdf,
2958                                       final boolean is_in_found_nodes ) {
2959         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
2960             return;
2961         }
2962         if ( getOptions().isShowBranchLengthValues()
2963                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
2964                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
2965                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
2966             paintBranchLength( g, node, to_pdf, to_graphics_file );
2967         }
2968         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
2969             return;
2970         }
2971         int x = 0;
2972         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2973         if ( getControlPanel().isShowTaxonomyImages()
2974                 && ( getImageMap() != null )
2975                 && !getImageMap().isEmpty()
2976                 && node.getNodeData().isHasTaxonomy()
2977                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
2978                         .getUris().isEmpty() ) ) {
2979             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
2980         }
2981         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
2982                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
2983             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
2984         }
2985         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2986             g.setColor( Color.BLACK );
2987         }
2988         else if ( is_in_found_nodes ) {
2989             g.setColor( getTreeColorSet().getFoundColor() );
2990         }
2991         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2992             g.setColor( getTaxonomyBasedColor( node ) );
2993         }
2994         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
2995                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2996             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
2997         }
2998         else if ( to_pdf ) {
2999             g.setColor( Color.BLACK );
3000         }
3001         else {
3002             g.setColor( getTreeColorSet().getSequenceColor() );
3003         }
3004         _sb.setLength( 0 );
3005         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3006             _sb.append( " [" );
3007             _sb.append( node.getAllExternalDescendants().size() );
3008             _sb.append( "]" );
3009         }
3010         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3011             if ( _sb.length() > 0 ) {
3012                 _sb.append( " " );
3013             }
3014             _sb.append( node.getName() );
3015         }
3016         if ( node.getNodeData().isHasSequence() ) {
3017             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3018                 if ( _sb.length() > 0 ) {
3019                     _sb.append( " " );
3020                 }
3021                 _sb.append( node.getNodeData().getSequence().getSymbol() );
3022             }
3023             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3024                 if ( _sb.length() > 0 ) {
3025                     _sb.append( " " );
3026                 }
3027                 _sb.append( node.getNodeData().getSequence().getName() );
3028             }
3029             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3030                 if ( _sb.length() > 0 ) {
3031                     _sb.append( " " );
3032                 }
3033                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3034                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3035                     _sb.append( ":" );
3036                 }
3037                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3038             }
3039         }
3040         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
3041             if ( _sb.length() > 0 ) {
3042                 _sb.append( " " );
3043             }
3044             _sb.append( propertiesToString( node ) );
3045         }
3046         g.setFont( getTreeFontSet().getLargeFont() );
3047         if ( is_in_found_nodes ) {
3048             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3049         }
3050         double down_shift_factor = 3.0;
3051         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
3052             down_shift_factor = 1;
3053         }
3054         final double pos_x = node.getXcoord() + x + 2 + half_box_size;
3055         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
3056         final String sb_str = _sb.toString();
3057         // GUILHEM_BEG ______________
3058         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
3059                 && ( _query_sequence != null ) ) {
3060             int nodeTextBoundsWidth = 0;
3061             if ( sb_str.length() > 0 ) {
3062                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
3063                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
3064             }
3065             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
3066                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
3067                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
3068                     g.setColor( getTreeColorSet().getBackgroundColor() );
3069                 }
3070             }
3071             else {
3072                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
3073                 for( final SequenceRelation seqRelation : seqRelations ) {
3074                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
3075                             .getRef1().isEqual( _query_sequence ) )
3076                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
3077                                     .getSelectedItem() );
3078                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
3079                         final double linePosX = node.getXcoord() + 2 + half_box_size;
3080                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
3081                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
3082                                 + ")";
3083                         if ( sConfidence != null ) {
3084                             double confidenceX = pos_x;
3085                             if ( sb_str.length() > 0 ) {
3086                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
3087                                         + CONFIDENCE_LEFT_MARGIN;
3088                             }
3089                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
3090                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
3091                                         .getBounds().getWidth();
3092                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
3093                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
3094                             }
3095                         }
3096                         if ( x + nodeTextBoundsWidth > 0 ) /* we only underline if there is something displayed */
3097                         {
3098                             if ( nodeTextBoundsWidth == 0 ) {
3099                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
3100                             }
3101                             else {
3102                                 nodeTextBoundsWidth += 2;
3103                             }
3104                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
3105                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
3106                             break;
3107                         }
3108                     }
3109                 }
3110             }
3111         }
3112         if ( sb_str.length() > 0 ) {
3113             TreePanel.drawString( sb_str, pos_x, pos_y, g );
3114         }
3115         // GUILHEM_END _____________
3116         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
3117         // TODO FIXME need to check this one!
3118         //if ( _sb.length() > 0 ) {
3119         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
3120         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3121         //}
3122         // COMMENTED_OUT_BY_GUILHEM_END ________________
3123         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
3124                 && ( node.getNodeData().getSequence().getAnnotations() != null )
3125                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
3126             if ( _sb.length() > 0 ) {
3127                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3128             }
3129             final Annotation ann = node.getNodeData().getSequence().getAnnotation( 0 );
3130             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3131                 g.setColor( Color.BLACK );
3132             }
3133             else {
3134                 g.setColor( calculateColorForAnnotation( ann ) );
3135             }
3136             final String ann_str = ann.asSimpleText().toString();
3137             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
3138                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3139             _sb.setLength( 0 );
3140             _sb.append( ann_str );
3141         }
3142         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3143                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3144                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3145             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
3146                     && node.getNodeData().isHasBinaryCharacters() ) {
3147                 if ( _sb.length() > 0 ) {
3148                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3149                 }
3150                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3151                     g.setColor( Color.BLACK );
3152                 }
3153                 else {
3154                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
3155                 }
3156                 if ( getControlPanel().isShowBinaryCharacters() ) {
3157                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
3158                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
3159                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3160                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
3161                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
3162                             .getLostCharactersAsStringBuffer().toString() );
3163                 }
3164                 else {
3165                     if ( DRAW_MEAN_COUNTS && node.isInternal() ) {
3166                         final List<PhylogenyNode> ec = node.getAllExternalDescendants();
3167                         double sum = 0;
3168                         int count = 0;
3169                         for( final PhylogenyNode phylogenyNode : ec ) {
3170                             count++;
3171                             if ( phylogenyNode.getNodeData().getBinaryCharacters() != null ) {
3172                                 sum += phylogenyNode.getNodeData().getBinaryCharacters().getPresentCount();
3173                             }
3174                         }
3175                         final double mean = ForesterUtil.round( sum / count, 1 );
3176                         TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount() + " ["
3177                                 + mean + "]", node.getXcoord() + x + 4 + half_box_size, node.getYcoord()
3178                                 + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3179                     }
3180                     else {
3181                         TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
3182                                               node.getXcoord() + x + 4 + half_box_size,
3183                                               node.getYcoord()
3184                                                       + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
3185                                               g );
3186                     }
3187                     paintGainedAndLostCharacters( g, node, "+"
3188                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
3189                             + node.getNodeData().getBinaryCharacters().getLostCount() );
3190                 }
3191             }
3192         }
3193     }
3194
3195     private StringBuffer propertiesToString( final PhylogenyNode node ) {
3196         final PropertiesMap properties = node.getNodeData().getProperties();
3197         final StringBuffer sb = new StringBuffer();
3198         boolean first = true;
3199         for( final String ref : properties.getPropertyRefs() ) {
3200             if ( first ) {
3201                 first = false;
3202             }
3203             else {
3204                 sb.append( " " );
3205             }
3206             final Property p = properties.getProperty( ref );
3207             sb.append( getPartAfterColon( p.getRef() ) );
3208             sb.append( "=" );
3209             sb.append( p.getValue() );
3210             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
3211                 sb.append( getPartAfterColon( p.getUnit() ) );
3212             }
3213         }
3214         return sb;
3215     }
3216
3217     final private static String getPartAfterColon( final String s ) {
3218         final int i = s.indexOf( ':' );
3219         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
3220             return s;
3221         }
3222         return s.substring( i + 1, s.length() );
3223     }
3224
3225     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
3226         final List<Uri> us = new ArrayList<Uri>();
3227         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
3228             for( final Uri uri : t.getUris() ) {
3229                 us.add( uri );
3230             }
3231         }
3232         double offset = 0;
3233         for( final Uri uri : us ) {
3234             if ( uri != null ) {
3235                 final String uri_str = uri.getValue().toString().toLowerCase();
3236                 if ( getImageMap().containsKey( uri_str ) ) {
3237                     final BufferedImage bi = getImageMap().get( uri_str );
3238                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
3239                         double scaling_factor = 1;
3240                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
3241                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
3242                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
3243                         }
3244                         // y = y - ( 0.9 * getYdistance() );
3245                         final double hs = bi.getHeight() * scaling_factor;
3246                         double ws = bi.getWidth() * scaling_factor + offset;
3247                         final double my_y = y - ( 0.5 * hs );
3248                         final int x_w = ( int ) ( x + ws + 0.5 );
3249                         final int y_h = ( int ) ( my_y + hs + 0.5 );
3250                         if ( ( x_w - x > 7 ) && ( y_h - my_y > 7 ) ) {
3251                             g.drawImage( bi,
3252                                          ( int ) ( x + 0.5 + offset ),
3253                                          ( int ) ( my_y + 0.5 ),
3254                                          x_w,
3255                                          y_h,
3256                                          0,
3257                                          0,
3258                                          bi.getWidth(),
3259                                          bi.getHeight(),
3260                                          null );
3261                             ws += 8;
3262                         }
3263                         else {
3264                             ws = 0.0;
3265                         }
3266                         offset = ws;
3267                     }
3268                 }
3269             }
3270         }
3271         return offset;
3272     }
3273
3274     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
3275                                                   final PhylogenyNode node,
3276                                                   final boolean to_pdf,
3277                                                   final boolean to_graphics_file,
3278                                                   final boolean radial_labels,
3279                                                   final double ur_angle,
3280                                                   final boolean is_in_found_nodes ) {
3281         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
3282             return;
3283         }
3284         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3285             g.setColor( Color.BLACK );
3286         }
3287         else if ( is_in_found_nodes ) {
3288             g.setColor( getTreeColorSet().getFoundColor() );
3289         }
3290         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3291             g.setColor( getTaxonomyBasedColor( node ) );
3292         }
3293         else {
3294             g.setColor( getTreeColorSet().getSequenceColor() );
3295         }
3296         _sb.setLength( 0 );
3297         _sb.append( " " );
3298         if ( node.getNodeData().isHasTaxonomy()
3299                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3300                         .isShowTaxonomyCommonNames() ) ) {
3301             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3302             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3303                 _sb.append( taxonomy.getTaxonomyCode() );
3304                 _sb.append( " " );
3305             }
3306             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3307                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3308                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3309                     _sb.append( taxonomy.getScientificName() );
3310                     _sb.append( " (" );
3311                     _sb.append( taxonomy.getCommonName() );
3312                     _sb.append( ") " );
3313                 }
3314                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3315                     _sb.append( taxonomy.getScientificName() );
3316                     _sb.append( " " );
3317                 }
3318                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3319                     _sb.append( taxonomy.getCommonName() );
3320                     _sb.append( " " );
3321                 }
3322             }
3323             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3324                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3325                     _sb.append( taxonomy.getScientificName() );
3326                     _sb.append( " " );
3327                 }
3328             }
3329             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3330                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3331                     _sb.append( taxonomy.getCommonName() );
3332                     _sb.append( " " );
3333                 }
3334             }
3335         }
3336         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3337             _sb.append( " [" );
3338             _sb.append( node.getAllExternalDescendants().size() );
3339             _sb.append( "]" );
3340         }
3341         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3342             if ( _sb.length() > 0 ) {
3343                 _sb.append( " " );
3344             }
3345             _sb.append( node.getName() );
3346         }
3347         if ( node.getNodeData().isHasSequence() ) {
3348             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3349                 if ( _sb.length() > 0 ) {
3350                     _sb.append( " " );
3351                 }
3352                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3353                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3354                     _sb.append( ":" );
3355                 }
3356                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3357             }
3358             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3359                 if ( _sb.length() > 0 ) {
3360                     _sb.append( " " );
3361                 }
3362                 _sb.append( node.getNodeData().getSequence().getName() );
3363             }
3364         }
3365         g.setFont( getTreeFontSet().getLargeFont() );
3366         if ( is_in_found_nodes ) {
3367             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3368         }
3369         if ( _sb.length() > 1 ) {
3370             final String sb_str = _sb.toString();
3371             double m = 0;
3372             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3373                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
3374             }
3375             else {
3376                 m = ( float ) ( ur_angle % TWO_PI );
3377             }
3378             _at = g.getTransform();
3379             boolean need_to_reset = false;
3380             final float x_coord = node.getXcoord();
3381             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
3382             if ( radial_labels ) {
3383                 need_to_reset = true;
3384                 boolean left = false;
3385                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3386                     m -= PI;
3387                     left = true;
3388                 }
3389                 g.rotate( m, x_coord, node.getYcoord() );
3390                 if ( left ) {
3391                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
3392                 }
3393             }
3394             else {
3395                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3396                     need_to_reset = true;
3397                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
3398                 }
3399             }
3400             TreePanel.drawString( sb_str, x_coord, y_coord, g );
3401             if ( need_to_reset ) {
3402                 g.setTransform( _at );
3403             }
3404         }
3405     }
3406
3407     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
3408         if ( node.isCollapse() ) {
3409             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3410                 paintCollapsedNode( g, node, false, false, false );
3411             }
3412             return;
3413         }
3414         if ( isInFoundNodes( node ) ) {
3415             g.setColor( getTreeColorSet().getFoundColor() );
3416             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
3417         }
3418         float new_x = 0;
3419         if ( !node.isExternal() && !node.isCollapse() ) {
3420             boolean first_child = true;
3421             float y2 = 0.0f;
3422             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3423             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3424                 final PhylogenyNode child_node = node.getChildNode( i );
3425                 int factor_x;
3426                 if ( !isUniformBranchLengthsForCladogram() ) {
3427                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3428                 }
3429                 else {
3430                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3431                 }
3432                 if ( first_child ) {
3433                     first_child = false;
3434                     y2 = node.getYSecondary()
3435                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
3436                                     .getNumberOfExternalNodes() ) );
3437                 }
3438                 else {
3439                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3440                 }
3441                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
3442                 new_x = x2 + node.getXSecondary();
3443                 final float diff_y = node.getYSecondary() - y2;
3444                 final float diff_x = node.getXSecondary() - new_x;
3445                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
3446                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
3447                 }
3448                 child_node.setXSecondary( new_x );
3449                 child_node.setYSecondary( y2 );
3450                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3451             }
3452         }
3453     }
3454
3455     final private void paintNodeRectangular( final Graphics2D g,
3456                                              final PhylogenyNode node,
3457                                              final boolean to_pdf,
3458                                              final boolean dynamically_hide,
3459                                              final int dynamic_hiding_factor,
3460                                              final boolean to_graphics_file ) {
3461         final boolean is_in_found_nodes = isInFoundNodes( node );
3462         if ( node.isCollapse() ) {
3463             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3464                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3465             }
3466             return;
3467         }
3468         if ( node.isExternal() ) {
3469             ++_external_node_index;
3470         }
3471         // Confidence values
3472         if ( getControlPanel().isShowConfidenceValues()
3473                 && !node.isExternal()
3474                 && !node.isRoot()
3475                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
3476                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3477                 && node.getBranchData().isHasConfidences() ) {
3478             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
3479         }
3480         // Draw a line to root:
3481         if ( node.isRoot() && _phylogeny.isRooted() ) {
3482             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
3483         }
3484         float new_x = 0;
3485         float new_x_min = Float.MAX_VALUE;
3486         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
3487         float min_dist = 1.5f;
3488         if ( !disallow_shortcutting ) {
3489             //   System.out.println( dynamic_hiding_factor );
3490             if ( dynamic_hiding_factor > 4000 ) {
3491                 min_dist = 4;
3492             }
3493             else if ( dynamic_hiding_factor > 1000 ) {
3494                 min_dist = 3;
3495             }
3496             else if ( dynamic_hiding_factor > 100 ) {
3497                 min_dist = 2;
3498             }
3499         }
3500         if ( !node.isExternal() && !node.isCollapse() ) {
3501             boolean first_child = true;
3502             float y2 = 0.0f;
3503             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3504             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3505                 final PhylogenyNode child_node = node.getChildNode( i );
3506                 int factor_x;
3507                 if ( !isUniformBranchLengthsForCladogram() ) {
3508                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3509                 }
3510                 else {
3511                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3512                 }
3513                 if ( first_child ) {
3514                     first_child = false;
3515                     y2 = node.getYcoord()
3516                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3517                 }
3518                 else {
3519                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
3520                 }
3521                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
3522                 new_x = x2 + node.getXcoord();
3523                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
3524                     new_x_min = x2;
3525                 }
3526                 final float diff_y = node.getYcoord() - y2;
3527                 final float diff_x = node.getXcoord() - new_x;
3528                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
3529                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
3530                     paintBranchRectangular( g,
3531                                             node.getXcoord(),
3532                                             new_x,
3533                                             node.getYcoord(),
3534                                             y2,
3535                                             child_node,
3536                                             to_pdf,
3537                                             to_graphics_file );
3538                 }
3539                 child_node.setXcoord( new_x );
3540                 child_node.setYcoord( y2 );
3541                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
3542             }
3543             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
3544         }
3545         if ( dynamically_hide
3546                 && !is_in_found_nodes
3547                 && ( ( node.isExternal() && ( _external_node_index % dynamic_hiding_factor != 1 ) ) || ( !node
3548                         .isExternal() && ( ( new_x_min < 20 ) || ( _y_distance * node.getNumberOfExternalNodes() < getTreeFontSet()._fm_large
3549                         .getHeight() ) ) ) ) ) {
3550             return;
3551         }
3552         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3553         paintNodeWithRenderableData( g, node, to_graphics_file, to_pdf );
3554     }
3555
3556     final private void paintNodeWithRenderableData( final Graphics2D g,
3557                                                     final PhylogenyNode node,
3558                                                     final boolean to_graphics_file,
3559                                                     final boolean to_pdf ) {
3560         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
3561             return;
3562         }
3563         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
3564             return;
3565         }
3566         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3567                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
3568             RenderableDomainArchitecture rds = null;
3569             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
3570                 try {
3571                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
3572                 }
3573                 catch ( final ClassCastException cce ) {
3574                     cce.printStackTrace();
3575                 }
3576                 if ( rds != null ) {
3577                     rds.setRenderingHeight( 6 );
3578                     int x = 0;
3579                     if ( getControlPanel().isShowTaxonomyCode()
3580                             && ( !ForesterUtil.isEmpty( PhylogenyMethods.getSpecies( node ) ) ) ) {
3581                         x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( node ) + " " );
3582                     }
3583                     if ( getControlPanel().isShowGeneNames()
3584                             && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) ) {
3585                         x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName() + " " );
3586                     }
3587                     if ( getControlPanel().isShowGeneSymbols()
3588                             && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) ) {
3589                         x += getTreeFontSet()._fm_large
3590                                 .stringWidth( node.getNodeData().getSequence().getSymbol() + " " );
3591                     }
3592                     if ( getControlPanel().isShowSequenceAcc()
3593                             && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3594                         x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getAccession()
3595                                 .toString()
3596                                 + " " );
3597                     }
3598                     if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3599                         x += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
3600                     }
3601                     rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
3602                 }
3603             }
3604         }
3605         //////////////
3606         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
3607                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
3608             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
3609                                                                          getStatisticsForExpressionValues(),
3610                                                                          getConfiguration() );
3611             if ( rv != null ) {
3612                 int x = 0;
3613                 PhylogenyNode my_node = node;
3614                 if ( !getControlPanel().isDrawPhylogram() ) {
3615                     my_node = getPhylogeny().getFirstExternalNode();
3616                 }
3617                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
3618                     x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
3619                 }
3620                 if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
3621                     x += getTreeFontSet()._fm_large.stringWidth( my_node.getName() + " " );
3622                 }
3623                 rv.render( my_node.getXcoord() + x, node.getYcoord() - 5, g, this, to_pdf );
3624             }
3625         }
3626         //////////////
3627     }
3628
3629     final private void paintOvRectangle( final Graphics2D g ) {
3630         final float w_ratio = ( float ) getWidth() / getVisibleRect().width;
3631         final float h_ratio = ( float ) getHeight() / getVisibleRect().height;
3632         final float x_ratio = ( float ) getWidth() / getVisibleRect().x;
3633         final float y_ratio = ( float ) getHeight() / getVisibleRect().y;
3634         final float width = getOvMaxWidth() / w_ratio;
3635         final float height = getOvMaxHeight() / h_ratio;
3636         final float x = getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / x_ratio;
3637         final float y = getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / y_ratio;
3638         g.setColor( getTreeColorSet().getFoundColor() );
3639         getOvRectangle().setRect( x, y, width, height );
3640         if ( ( width < 6 ) && ( height < 6 ) ) {
3641             drawRectFilled( x, y, 6, 6, g );
3642             getOvVirtualRectangle().setRect( x, y, 6, 6 );
3643         }
3644         else if ( width < 6 ) {
3645             drawRectFilled( x, y, 6, height, g );
3646             getOvVirtualRectangle().setRect( x, y, 6, height );
3647         }
3648         else if ( height < 6 ) {
3649             drawRectFilled( x, y, width, 6, g );
3650             getOvVirtualRectangle().setRect( x, y, width, 6 );
3651         }
3652         else {
3653             drawRect( x, y, width, height, g );
3654             if ( isInOvRect() ) {
3655                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
3656             }
3657             getOvVirtualRectangle().setRect( x, y, width, height );
3658         }
3659     }
3660
3661     final void paintPhylogeny( final Graphics2D g,
3662                                final boolean to_pdf,
3663                                final boolean to_graphics_file,
3664                                final int graphics_file_width,
3665                                final int graphics_file_height,
3666                                final int graphics_file_x,
3667                                final int graphics_file_y ) {
3668         if ( _control_panel.isShowSequenceRelations() ) {
3669             _query_sequence = _control_panel.getSelectedQuerySequence();
3670         }
3671         // Color the background
3672         if ( !to_pdf ) {
3673             final Rectangle r = getVisibleRect();
3674             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
3675                 g.setColor( getTreeColorSet().getBackgroundColor() );
3676                 if ( !to_graphics_file ) {
3677                     g.fill( r );
3678                 }
3679                 else {
3680                     if ( getOptions().isPrintBlackAndWhite() ) {
3681                         g.setColor( Color.WHITE );
3682                     }
3683                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3684                 }
3685             }
3686             else {
3687                 if ( !to_graphics_file ) {
3688                     g.setPaint( new GradientPaint( r.x, r.y, getTreeColorSet().getBackgroundColor(), r.x, r.y
3689                             + r.height, getTreeColorSet().getBackgroundColorGradientBottom() ) );
3690                     g.fill( r );
3691                 }
3692                 else {
3693                     g.setPaint( new GradientPaint( graphics_file_x,
3694                                                    graphics_file_y,
3695                                                    getTreeColorSet().getBackgroundColor(),
3696                                                    graphics_file_x,
3697                                                    graphics_file_y + graphics_file_height,
3698                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
3699                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3700                 }
3701             }
3702             g.setStroke( new BasicStroke( 1 ) );
3703         }
3704         else {
3705             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
3706         }
3707         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3708                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3709             _external_node_index = 0;
3710             // Position starting X of tree
3711             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/) {
3712                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
3713             }
3714             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
3715                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE + ( _phylogeny.getRoot()
3716                         .getDistanceToParent() * getXcorrectionFactor() ) ) );
3717             }
3718             else {
3719                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
3720             }
3721             // Position starting Y of tree
3722             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
3723                     + ( TreePanel.MOVE / 2.0f ) );
3724             final int dynamic_hiding_factor = ( int ) ( getTreeFontSet()._fm_large.getHeight() / ( 1.5 * getYdistance() ) );
3725             if ( getControlPanel().isDynamicallyHideData() ) {
3726                 if ( dynamic_hiding_factor > 1 ) {
3727                     getControlPanel().setDynamicHidingIsOn( true );
3728                 }
3729                 else {
3730                     getControlPanel().setDynamicHidingIsOn( false );
3731                 }
3732             }
3733             if ( _nodes_in_preorder == null ) {
3734                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
3735                 int i = 0;
3736                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3737                     _nodes_in_preorder[ i++ ] = it.next();
3738                 }
3739             }
3740             //final PhylogenyNodeIterator it;
3741             //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3742             //    paintNodeRectangular( g, it.next(), to_pdf, getControlPanel().isDynamicallyHideData()
3743             //            && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3744             //}
3745             for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3746                 paintNodeRectangular( g, _nodes_in_preorder[ i ], to_pdf, getControlPanel().isDynamicallyHideData()
3747                         && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3748             }
3749             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
3750                 if ( !( to_graphics_file || to_pdf ) ) {
3751                     paintScale( g,
3752                                 getVisibleRect().x,
3753                                 getVisibleRect().y + getVisibleRect().height,
3754                                 to_pdf,
3755                                 to_graphics_file );
3756                 }
3757                 else {
3758                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3759                 }
3760             }
3761             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3762                 paintPhylogenyLite( g );
3763             }
3764         }
3765         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
3766             if ( getControlPanel().getDynamicallyHideData() != null ) {
3767                 getControlPanel().setDynamicHidingIsOn( false );
3768             }
3769             final double angle = getStartingAngle();
3770             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
3771             _dynamic_hiding_factor = 0;
3772             if ( getControlPanel().isDynamicallyHideData() ) {
3773                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3774                         .getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
3775             }
3776             if ( getControlPanel().getDynamicallyHideData() != null ) {
3777                 if ( _dynamic_hiding_factor > 1 ) {
3778                     getControlPanel().setDynamicHidingIsOn( true );
3779                 }
3780                 else {
3781                     getControlPanel().setDynamicHidingIsOn( false );
3782                 }
3783             }
3784             paintUnrooted( _phylogeny.getRoot(),
3785                            angle,
3786                            ( float ) ( angle + 2 * Math.PI ),
3787                            radial_labels,
3788                            g,
3789                            to_pdf,
3790                            to_graphics_file );
3791             if ( getOptions().isShowScale() ) {
3792                 if ( !( to_graphics_file || to_pdf ) ) {
3793                     paintScale( g,
3794                                 getVisibleRect().x,
3795                                 getVisibleRect().y + getVisibleRect().height,
3796                                 to_pdf,
3797                                 to_graphics_file );
3798                 }
3799                 else {
3800                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3801                 }
3802             }
3803             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3804                 g.setColor( getTreeColorSet().getOvColor() );
3805                 paintUnrootedLite( _phylogeny.getRoot(),
3806                                    angle,
3807                                    angle + 2 * Math.PI,
3808                                    g,
3809                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
3810                 paintOvRectangle( g );
3811             }
3812         }
3813         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3814             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() ) / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
3815             final int d = radius + MOVE + getLongestExtNodeInfo();
3816             _dynamic_hiding_factor = 0;
3817             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
3818                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3819                         .getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
3820             }
3821             if ( getControlPanel().getDynamicallyHideData() != null ) {
3822                 if ( _dynamic_hiding_factor > 1 ) {
3823                     getControlPanel().setDynamicHidingIsOn( true );
3824                 }
3825                 else {
3826                     getControlPanel().setDynamicHidingIsOn( false );
3827                 }
3828             }
3829             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
3830             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3831                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
3832                         : getOvMaxWidth() / 2 );
3833                 double x_scale = 1.0;
3834                 double y_scale = 1.0;
3835                 int x_pos = getVisibleRect().x + getOvXPosition();
3836                 int y_pos = getVisibleRect().y + getOvYPosition();
3837                 if ( getWidth() > getHeight() ) {
3838                     x_scale = ( double ) getHeight() / getWidth();
3839                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
3840                 }
3841                 else {
3842                     y_scale = ( double ) getWidth() / getHeight();
3843                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
3844                 }
3845                 _at = g.getTransform();
3846                 g.scale( x_scale, y_scale );
3847                 paintCircularLite( _phylogeny,
3848                                    getStartingAngle(),
3849                                    x_pos + radius_ov,
3850                                    y_pos + radius_ov,
3851                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo() / ( getVisibleRect().width / getOvRectangle()
3852                                            .getWidth() ) ) ),
3853                                    g );
3854                 g.setTransform( _at );
3855                 paintOvRectangle( g );
3856             }
3857         }
3858     }
3859
3860     final private void paintPhylogenyLite( final Graphics2D g ) {
3861         //System.out.println( getVisibleRect().x + " " + getVisibleRect().y );
3862         _phylogeny
3863                 .getRoot()
3864                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
3865                         .getWidth() ) ) ) );
3866         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
3867         //final PhylogenyNodeIterator it;
3868         //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3869         //    paintNodeLite( g, it.next() );
3870         //}
3871         for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3872             paintNodeLite( g, _nodes_in_preorder[ i ] );
3873         }
3874         paintOvRectangle( g );
3875     }
3876
3877     /**
3878      * Paint the root branch. (Differs from others because it will always be a
3879      * single horizontal line).
3880      * @param to_graphics_file 
3881      * 
3882      * @return new x1 value
3883      */
3884     final private void paintRootBranch( final Graphics2D g,
3885                                         final float x1,
3886                                         final float y1,
3887                                         final PhylogenyNode root,
3888                                         final boolean to_pdf,
3889                                         final boolean to_graphics_file ) {
3890         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
3891         float d = getXdistance();
3892         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
3893             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
3894         }
3895         if ( d < MIN_ROOT_LENGTH ) {
3896             d = MIN_ROOT_LENGTH;
3897         }
3898         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
3899             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
3900         }
3901         else {
3902             final double w = PhylogenyMethods.getBranchWidthValue( root );
3903             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
3904         }
3905         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file, isInFoundNodes( root ) );
3906     }
3907
3908     final private void paintScale( final Graphics2D g,
3909                                    int x1,
3910                                    int y1,
3911                                    final boolean to_pdf,
3912                                    final boolean to_graphics_file ) {
3913         x1 += MOVE;
3914         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
3915         y1 -= 12;
3916         final int y2 = y1 - 8;
3917         final int y3 = y1 - 4;
3918         g.setFont( getTreeFontSet().getSmallFont() );
3919         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3920             g.setColor( Color.BLACK );
3921         }
3922         else {
3923             g.setColor( getTreeColorSet().getBranchLengthColor() );
3924         }
3925         drawLine( x1, y1, x1, y2, g );
3926         drawLine( x2, y1, x2, y2, g );
3927         drawLine( x1, y3, x2, y3, g );
3928         if ( getScaleLabel() != null ) {
3929             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
3930         }
3931     }
3932
3933     final private int paintTaxonomy( final Graphics2D g,
3934                                      final PhylogenyNode node,
3935                                      final boolean is_in_found_nodes,
3936                                      final boolean to_pdf,
3937                                      final boolean to_graphics_file,
3938                                      final double x_shift ) {
3939         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3940         g.setFont( getTreeFontSet().getLargeItalicFont() );
3941         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3942             g.setColor( Color.BLACK );
3943         }
3944         else if ( is_in_found_nodes ) {
3945             g.setFont( getTreeFontSet().getLargeItalicFont().deriveFont( TreeFontSet.BOLD_AND_ITALIC ) );
3946             g.setColor( getTreeColorSet().getFoundColor() );
3947         }
3948         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3949             g.setColor( getTaxonomyBasedColor( node ) );
3950         }
3951         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3952                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3953             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3954         }
3955         else if ( to_pdf ) {
3956             g.setColor( Color.BLACK );
3957         }
3958         else {
3959             g.setColor( getTreeColorSet().getTaxonomyColor() );
3960         }
3961         final double start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
3962         final double start_y = node.getYcoord()
3963                 + ( getTreeFontSet()._fm_large.getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0 ) );
3964         _sb.setLength( 0 );
3965         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3966             _sb.append( taxonomy.getTaxonomyCode() );
3967             _sb.append( " " );
3968         }
3969         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3970             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3971                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3972                 if ( getOptions().isAbbreviateScientificTaxonNames()
3973                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3974                     abbreviateScientificName( taxonomy.getScientificName() );
3975                 }
3976                 else {
3977                     _sb.append( taxonomy.getScientificName() );
3978                 }
3979                 _sb.append( " (" );
3980                 _sb.append( taxonomy.getCommonName() );
3981                 _sb.append( ") " );
3982             }
3983             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3984                 if ( getOptions().isAbbreviateScientificTaxonNames()
3985                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3986                     abbreviateScientificName( taxonomy.getScientificName() );
3987                 }
3988                 else {
3989                     _sb.append( taxonomy.getScientificName() );
3990                 }
3991                 _sb.append( " " );
3992             }
3993             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3994                 _sb.append( taxonomy.getCommonName() );
3995                 _sb.append( " " );
3996             }
3997         }
3998         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3999             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4000                 if ( getOptions().isAbbreviateScientificTaxonNames()
4001                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4002                     abbreviateScientificName( taxonomy.getScientificName() );
4003                 }
4004                 else {
4005                     _sb.append( taxonomy.getScientificName() );
4006                 }
4007                 _sb.append( " " );
4008             }
4009         }
4010         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4011             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4012                 _sb.append( taxonomy.getCommonName() );
4013                 _sb.append( " " );
4014             }
4015         }
4016         final String label = _sb.toString();
4017         /* GUILHEM_BEG */
4018         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
4019                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
4020             // invert font color and background color to show that this is the query sequence
4021             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
4022                                                                                                           false,
4023                                                                                                           false ) )
4024                     .getBounds();
4025             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
4026             g.setColor( getTreeColorSet().getBackgroundColor() );
4027         }
4028         /* GUILHEM_END */
4029         TreePanel.drawString( label, start_x, start_y, g );
4030         if ( is_in_found_nodes ) {
4031             return getTreeFontSet()._fm_large_italic_bold.stringWidth( label );
4032         }
4033         else {
4034             return getTreeFontSet()._fm_large_italic.stringWidth( label );
4035         }
4036     }
4037
4038     private void abbreviateScientificName( final String sn ) {
4039         final String[] a = sn.split( "\\s+" );
4040         _sb.append( a[ 0 ].substring( 0, 1 ) );
4041         _sb.append( a[ 1 ].substring( 0, 2 ) );
4042         if ( a.length > 2 ) {
4043             for( int i = 2; i < a.length; i++ ) {
4044                 _sb.append( " " );
4045                 _sb.append( a[ i ] );
4046             }
4047         }
4048     }
4049
4050     final private void paintUnrooted( final PhylogenyNode n,
4051                                       final double low_angle,
4052                                       final double high_angle,
4053                                       final boolean radial_labels,
4054                                       final Graphics2D g,
4055                                       final boolean to_pdf,
4056                                       final boolean to_graphics_file ) {
4057         if ( n.isRoot() ) {
4058             n.setXcoord( getWidth() / 2 );
4059             n.setYcoord( getHeight() / 2 );
4060         }
4061         if ( n.isExternal() ) {
4062             paintNodeDataUnrootedCirc( g,
4063                                        n,
4064                                        to_pdf,
4065                                        to_graphics_file,
4066                                        radial_labels,
4067                                        ( high_angle + low_angle ) / 2,
4068                                        isInFoundNodes( n ) );
4069             return;
4070         }
4071         final float num_enclosed = n.getNumberOfExternalNodes();
4072         final float x = n.getXcoord();
4073         final float y = n.getYcoord();
4074         double current_angle = low_angle;
4075         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4076         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4077         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4078         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4079         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4080             final PhylogenyNode desc = n.getChildNode( i );
4081             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4082             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4083             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4084             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4085             //     continue;
4086             // }
4087             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4088             //    continue;
4089             //}
4090             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4091             //    continue;
4092             // }
4093             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4094             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4095             float length;
4096             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4097                 if ( desc.getDistanceToParent() < 0 ) {
4098                     length = 0;
4099                 }
4100                 else {
4101                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4102                 }
4103             }
4104             else {
4105                 length = getUrtFactor();
4106             }
4107             final double mid_angle = current_angle + arc_size / 2;
4108             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4109             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4110             desc.setXcoord( new_x );
4111             desc.setYcoord( new_y );
4112             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
4113             current_angle += arc_size;
4114             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
4115             drawLine( x, y, new_x, new_y, g );
4116             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file, isInFoundNodes( desc ) );
4117         }
4118         if ( n.isRoot() ) {
4119             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file, isInFoundNodes( n ) );
4120         }
4121     }
4122
4123     final private void paintUnrootedLite( final PhylogenyNode n,
4124                                           final double low_angle,
4125                                           final double high_angle,
4126                                           final Graphics2D g,
4127                                           final float urt_ov_factor ) {
4128         if ( n.isRoot() ) {
4129             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / 2 );
4130             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / 2 );
4131             n.setXSecondary( x_pos );
4132             n.setYSecondary( y_pos );
4133         }
4134         if ( n.isExternal() ) {
4135             return;
4136         }
4137         final float num_enclosed = n.getNumberOfExternalNodes();
4138         final float x = n.getXSecondary();
4139         final float y = n.getYSecondary();
4140         double current_angle = low_angle;
4141         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4142             final PhylogenyNode desc = n.getChildNode( i );
4143             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4144             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4145             float length;
4146             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4147                 if ( desc.getDistanceToParent() < 0 ) {
4148                     length = 0;
4149                 }
4150                 else {
4151                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
4152                 }
4153             }
4154             else {
4155                 length = urt_ov_factor;
4156             }
4157             final double mid_angle = current_angle + arc_size / 2;
4158             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4159             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4160             desc.setXSecondary( new_x );
4161             desc.setYSecondary( new_y );
4162             if ( isInFoundNodes( desc ) ) {
4163                 g.setColor( getTreeColorSet().getFoundColor() );
4164                 drawRectFilled( desc.getXSecondary() - 1, desc.getYSecondary() - 1, 3, 3, g );
4165                 g.setColor( getTreeColorSet().getOvColor() );
4166             }
4167             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
4168             current_angle += arc_size;
4169             drawLine( x, y, new_x, new_y, g );
4170         }
4171     }
4172
4173     final private void pasteSubtree( final PhylogenyNode node ) {
4174         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4175             errorMessageNoCutCopyPasteInUnrootedDisplay();
4176             return;
4177         }
4178         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
4179             JOptionPane.showMessageDialog( this,
4180                                            "No tree in buffer (need to copy or cut a subtree first)",
4181                                            "Attempt to paste with empty buffer",
4182                                            JOptionPane.ERROR_MESSAGE );
4183             return;
4184         }
4185         final String label = getASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
4186         final Object[] options = { "As sibling", "As descendant", "Cancel" };
4187         final int r = JOptionPane.showOptionDialog( this,
4188                                                     "How to paste subtree" + label + "?",
4189                                                     "Paste Subtree",
4190                                                     JOptionPane.CLOSED_OPTION,
4191                                                     JOptionPane.QUESTION_MESSAGE,
4192                                                     null,
4193                                                     options,
4194                                                     options[ 2 ] );
4195         boolean paste_as_sibling = true;
4196         if ( r == 1 ) {
4197             paste_as_sibling = false;
4198         }
4199         else if ( r != 0 ) {
4200             return;
4201         }
4202         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
4203         buffer_phy.setAllNodesToNotCollapse();
4204         buffer_phy.preOrderReId();
4205         buffer_phy.setRooted( true );
4206         boolean need_to_show_whole = false;
4207         if ( paste_as_sibling ) {
4208             if ( node.isRoot() ) {
4209                 JOptionPane.showMessageDialog( this,
4210                                                "Cannot paste sibling to root",
4211                                                "Attempt to paste sibling to root",
4212                                                JOptionPane.ERROR_MESSAGE );
4213                 return;
4214             }
4215             buffer_phy.addAsSibling( node );
4216         }
4217         else {
4218             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
4219                 need_to_show_whole = true;
4220                 _phylogeny = buffer_phy;
4221             }
4222             else {
4223                 buffer_phy.addAsChild( node );
4224             }
4225         }
4226         if ( getCopiedAndPastedNodes() == null ) {
4227             setCopiedAndPastedNodes( new HashSet<Integer>() );
4228         }
4229         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
4230         final Set<Integer> node_ids = new HashSet<Integer>( nodes.size() );
4231         for( final PhylogenyNode n : nodes ) {
4232             node_ids.add( n.getId() );
4233         }
4234         node_ids.add( node.getId() );
4235         getCopiedAndPastedNodes().addAll( node_ids );
4236         setNodeInPreorderToNull();
4237         _phylogeny.externalNodesHaveChanged();
4238         _phylogeny.hashIDs();
4239         _phylogeny.recalculateNumberOfExternalDescendants( true );
4240         resetNodeIdToDistToLeafMap();
4241         setEdited( true );
4242         if ( need_to_show_whole ) {
4243             getControlPanel().showWhole();
4244         }
4245         repaint();
4246     }
4247
4248     final public int print( final Graphics g, final PageFormat page_format, final int page_index )
4249             throws PrinterException {
4250         if ( page_index > 0 ) {
4251             return ( NO_SUCH_PAGE );
4252         }
4253         else {
4254             final Graphics2D g2d = ( Graphics2D ) g;
4255             g2d.translate( page_format.getImageableX(), page_format.getImageableY() );
4256             // Turn off double buffering !?
4257             paintPhylogeny( g2d, true, false, 0, 0, 0, 0 );
4258             // Turn double buffering back on !?
4259             return ( PAGE_EXISTS );
4260         }
4261     }
4262
4263     final void recalculateMaxDistanceToRoot() {
4264         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
4265     }
4266
4267     /**
4268      * Remove all edit-node frames
4269      */
4270     final void removeAllEditNodeJFrames() {
4271         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
4272             if ( _node_frames[ i ] != null ) {
4273                 _node_frames[ i ].dispose();
4274                 _node_frames[ i ] = null;
4275             }
4276         }
4277         _node_frame_index = 0;
4278     }
4279
4280     /**
4281      * Remove a node-edit frame.
4282      */
4283     final void removeEditNodeFrame( final int i ) {
4284         _node_frame_index--;
4285         _node_frames[ i ] = null;
4286         if ( i < _node_frame_index ) {
4287             for( int j = 0; j < _node_frame_index - 1; j++ ) {
4288                 _node_frames[ j ] = _node_frames[ j + 1 ];
4289             }
4290             _node_frames[ _node_frame_index ] = null;
4291         }
4292     }
4293
4294     final void reRoot( final PhylogenyNode node ) {
4295         if ( !getPhylogeny().isRerootable() ) {
4296             JOptionPane.showMessageDialog( this,
4297                                            "This is not rerootable",
4298                                            "Not rerootable",
4299                                            JOptionPane.WARNING_MESSAGE );
4300             return;
4301         }
4302         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4303             JOptionPane.showMessageDialog( this,
4304                                            "Cannot reroot in unrooted display type",
4305                                            "Attempt to reroot tree in unrooted display",
4306                                            JOptionPane.WARNING_MESSAGE );
4307             return;
4308         }
4309         getPhylogeny().reRoot( node );
4310         getPhylogeny().recalculateNumberOfExternalDescendants( true );
4311         resetNodeIdToDistToLeafMap();
4312         setNodeInPreorderToNull();
4313         resetPreferredSize();
4314         getMainPanel().adjustJScrollPane();
4315         repaint();
4316         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4317             getControlPanel().showWhole();
4318         }
4319     }
4320
4321     final void resetNodeIdToDistToLeafMap() {
4322         _nodeid_dist_to_leaf = new HashMap<Integer, Short>();
4323     }
4324
4325     final void resetPreferredSize() {
4326         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
4327             return;
4328         }
4329         int x = 0;
4330         int y = 0;
4331         y = TreePanel.MOVE
4332                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
4333         if ( getControlPanel().isDrawPhylogram() ) {
4334             x = TreePanel.MOVE
4335                     + getLongestExtNodeInfo()
4336                     + ForesterUtil
4337                             .roundToInt( ( getXcorrectionFactor() * getPhylogeny().getHeight() ) + getXdistance() );
4338         }
4339         else {
4340             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4341                 x = TreePanel.MOVE
4342                         + getLongestExtNodeInfo()
4343                         + ForesterUtil.roundToInt( getXdistance()
4344                                 * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
4345             }
4346             else {
4347                 x = TreePanel.MOVE
4348                         + getLongestExtNodeInfo()
4349                         + ForesterUtil.roundToInt( getXdistance()
4350                                 * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
4351             }
4352         }
4353         setPreferredSize( new Dimension( x, y ) );
4354     }
4355
4356     public final void setArrowCursor() {
4357         setCursor( ARROW_CURSOR );
4358         repaint();
4359     }
4360
4361     final void setControlPanel( final ControlPanel atv_control ) {
4362         _control_panel = atv_control;
4363     }
4364
4365     final private void setCopiedAndPastedNodes( final Set<Integer> nodeIds ) {
4366         getMainPanel().setCopiedAndPastedNodes( nodeIds );
4367     }
4368
4369     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
4370         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
4371     }
4372
4373     public final void setEdited( final boolean edited ) {
4374         _edited = edited;
4375     }
4376
4377     final void setFoundNodes( final Set<Integer> found_nodes ) {
4378         _found_nodes = found_nodes;
4379     }
4380
4381     final private void setInOv( final boolean in_ov ) {
4382         _in_ov = in_ov;
4383     }
4384
4385     final void setInOvRect( final boolean in_ov_rect ) {
4386         _in_ov_rect = in_ov_rect;
4387     }
4388
4389     final void setLargeFonts() {
4390         getTreeFontSet().largeFonts();
4391     }
4392
4393     final void setLastMouseDragPointX( final float x ) {
4394         _last_drag_point_x = x;
4395     }
4396
4397     final void setLastMouseDragPointY( final float y ) {
4398         _last_drag_point_y = y;
4399     }
4400
4401     final void setLongestExtNodeInfo( final int i ) {
4402         _longest_ext_node_info = i;
4403     }
4404
4405     final void setMediumFonts() {
4406         getTreeFontSet().mediumFonts();
4407     }
4408
4409     final private void setOvMaxHeight( final float ov_max_height ) {
4410         _ov_max_height = ov_max_height;
4411     }
4412
4413     final private void setOvMaxWidth( final float ov_max_width ) {
4414         _ov_max_width = ov_max_width;
4415     }
4416
4417     final void setOvOn( final boolean ov_on ) {
4418         _ov_on = ov_on;
4419     }
4420
4421     final private void setOvXcorrectionFactor( final float f ) {
4422         _ov_x_correction_factor = f;
4423     }
4424
4425     final private void setOvXDistance( final float ov_x_distance ) {
4426         _ov_x_distance = ov_x_distance;
4427     }
4428
4429     final private void setOvXPosition( final int ov_x_position ) {
4430         _ov_x_position = ov_x_position;
4431     }
4432
4433     final private void setOvYDistance( final float ov_y_distance ) {
4434         _ov_y_distance = ov_y_distance;
4435     }
4436
4437     final private void setOvYPosition( final int ov_y_position ) {
4438         _ov_y_position = ov_y_position;
4439     }
4440
4441     final private void setOvYStart( final int ov_y_start ) {
4442         _ov_y_start = ov_y_start;
4443     }
4444
4445     /**
4446      * Set parameters for printing the displayed tree
4447      * 
4448      * @param x
4449      * @param y
4450      */
4451     public final void setParametersForPainting( final int x, final int y, final boolean recalc_longest_ext_node_info ) {
4452         // updateStyle(); not needed?
4453         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4454             initNodeData();
4455             if ( recalc_longest_ext_node_info ) {
4456                 calculateLongestExtNodeInfo();
4457             }
4458             int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4459             final int max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4460             if ( ext_nodes == 1 ) {
4461                 ext_nodes = max_depth;
4462                 if ( ext_nodes < 1 ) {
4463                     ext_nodes = 1;
4464                 }
4465             }
4466             updateOvSizes();
4467             float xdist = 0;
4468             float ov_xdist = 0;
4469             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4470                 xdist = ( float ) ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( ext_nodes + 3.0 ) );
4471                 ov_xdist = ( float ) ( getOvMaxWidth() / ( ext_nodes + 3.0 ) );
4472             }
4473             else {
4474                 xdist = ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( max_depth + 1 ) );
4475                 ov_xdist = ( getOvMaxWidth() / ( max_depth + 1 ) );
4476             }
4477             float ydist = ( float ) ( ( y - TreePanel.MOVE ) / ( ext_nodes * 2.0 ) );
4478             if ( xdist < 0.0 ) {
4479                 xdist = 0.0f;
4480             }
4481             if ( ov_xdist < 0.0 ) {
4482                 ov_xdist = 0.0f;
4483             }
4484             if ( ydist < 0.0 ) {
4485                 ydist = 0.0f;
4486             }
4487             setXdistance( xdist );
4488             setYdistance( ydist );
4489             setOvXDistance( ov_xdist );
4490             final double height = _phylogeny.getHeight();
4491             if ( height > 0 ) {
4492                 final float corr = ( float ) ( ( x - TreePanel.MOVE - getLongestExtNodeInfo() - getXdistance() ) / height );
4493                 setXcorrectionFactor( corr > 0 ? corr : 0 );
4494                 final float ov_corr = ( float ) ( ( getOvMaxWidth() - getOvXDistance() ) / height );
4495                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
4496             }
4497             else {
4498                 setXcorrectionFactor( 0 );
4499                 setOvXcorrectionFactor( 0 );
4500             }
4501             _circ_max_depth = max_depth;
4502             setUpUrtFactor();
4503         }
4504     }
4505
4506     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
4507         _graphics_type = graphics_type;
4508         setTextAntialias();
4509     }
4510
4511     final private void setScaleDistance( final double scale_distance ) {
4512         _scale_distance = scale_distance;
4513     }
4514
4515     final private void setScaleLabel( final String scale_label ) {
4516         _scale_label = scale_label;
4517     }
4518
4519     final void setSmallFonts() {
4520         getTreeFontSet().smallFonts();
4521     }
4522
4523     final void setStartingAngle( final double starting_angle ) {
4524         _urt_starting_angle = starting_angle;
4525     }
4526
4527     final void setSuperTinyFonts() {
4528         getTreeFontSet().superTinyFonts();
4529     }
4530
4531     final void setTextAntialias() {
4532         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4533             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
4534                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
4535             }
4536             else {
4537                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
4538             }
4539         }
4540         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
4541             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) {
4542                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4543             }
4544             else {
4545                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
4546             }
4547             try {
4548                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING,
4549                                       RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
4550             }
4551             catch ( final Throwable e ) {
4552                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
4553             }
4554         }
4555         else {
4556             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
4557             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4558         }
4559     }
4560
4561     final void setTinyFonts() {
4562         getTreeFontSet().tinyFonts();
4563     }
4564
4565     /**
4566      * Set a phylogeny tree.
4567      * 
4568      * @param t
4569      *            an instance of a Phylogeny
4570      */
4571     public final void setTree( final Phylogeny t ) {
4572         setNodeInPreorderToNull();
4573         _phylogeny = t;
4574     }
4575
4576     final void setNodeInPreorderToNull() {
4577         _nodes_in_preorder = null;
4578     }
4579
4580     final void setTreeFile( final File treefile ) {
4581         _treefile = treefile;
4582     }
4583
4584     final private void setUpUrtFactor() {
4585         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
4586                 : getVisibleRect().height;
4587         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4588             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
4589         }
4590         else {
4591             final int max_depth = _circ_max_depth;
4592             if ( max_depth > 0 ) {
4593                 setUrtFactor( d / ( 2 * max_depth ) );
4594             }
4595             else {
4596                 setUrtFactor( d / 2 );
4597             }
4598         }
4599         setUrtFactorOv( getUrtFactor() );
4600     }
4601
4602     final private void setUrtFactor( final float urt_factor ) {
4603         _urt_factor = urt_factor;
4604     }
4605
4606     final private void setUrtFactorOv( final float urt_factor_ov ) {
4607         _urt_factor_ov = urt_factor_ov;
4608     }
4609
4610     public final void setWaitCursor() {
4611         setCursor( WAIT_CURSOR );
4612         repaint();
4613     }
4614
4615     final void setXcorrectionFactor( final float f ) {
4616         _x_correction_factor = f;
4617     }
4618
4619     final void setXdistance( final float x ) {
4620         _x_distance = x;
4621     }
4622
4623     final void setYdistance( final float y ) {
4624         _y_distance = y;
4625     }
4626
4627     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
4628         try {
4629             if ( ( node.getName().length() > 0 )
4630                     || ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
4631                     || ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) )
4632                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
4633                     || node.getBranchData().isHasConfidences() ) {
4634                 _popup_buffer.setLength( 0 );
4635                 short lines = 0;
4636                 if ( node.getName().length() > 0 ) {
4637                     lines++;
4638                     _popup_buffer.append( node.getName() );
4639                 }
4640                 if ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
4641                     lines++;
4642                     boolean enc_data = false;
4643                     final Taxonomy tax = node.getNodeData().getTaxonomy();
4644                     if ( _popup_buffer.length() > 0 ) {
4645                         _popup_buffer.append( "\n" );
4646                     }
4647                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4648                         _popup_buffer.append( "[" );
4649                         _popup_buffer.append( tax.getTaxonomyCode() );
4650                         _popup_buffer.append( "]" );
4651                         enc_data = true;
4652                     }
4653                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4654                         if ( enc_data ) {
4655                             _popup_buffer.append( " " );
4656                         }
4657                         _popup_buffer.append( tax.getScientificName() );
4658                         enc_data = true;
4659                     }
4660                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
4661                         if ( enc_data ) {
4662                             _popup_buffer.append( " (" );
4663                         }
4664                         else {
4665                             _popup_buffer.append( "(" );
4666                         }
4667                         _popup_buffer.append( tax.getCommonName() );
4668                         _popup_buffer.append( ")" );
4669                         enc_data = true;
4670                     }
4671                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
4672                         if ( enc_data ) {
4673                             _popup_buffer.append( " (" );
4674                         }
4675                         else {
4676                             _popup_buffer.append( "(" );
4677                         }
4678                         _popup_buffer.append( tax.getAuthority() );
4679                         _popup_buffer.append( ")" );
4680                         enc_data = true;
4681                     }
4682                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
4683                         if ( enc_data ) {
4684                             _popup_buffer.append( " [" );
4685                         }
4686                         else {
4687                             _popup_buffer.append( "[" );
4688                         }
4689                         _popup_buffer.append( tax.getRank() );
4690                         _popup_buffer.append( "]" );
4691                         enc_data = true;
4692                     }
4693                     if ( tax.getSynonyms().size() > 0 ) {
4694                         if ( enc_data ) {
4695                             _popup_buffer.append( " " );
4696                         }
4697                         _popup_buffer.append( "[" );
4698                         int counter = 1;
4699                         for( final String syn : tax.getSynonyms() ) {
4700                             if ( !ForesterUtil.isEmpty( syn ) ) {
4701                                 enc_data = true;
4702                                 _popup_buffer.append( syn );
4703                                 if ( counter < tax.getSynonyms().size() ) {
4704                                     _popup_buffer.append( ", " );
4705                                 }
4706                             }
4707                             counter++;
4708                         }
4709                         _popup_buffer.append( "]" );
4710                     }
4711                     if ( !enc_data ) {
4712                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
4713                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
4714                                 _popup_buffer.append( "[" );
4715                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
4716                                 _popup_buffer.append( "] " );
4717                             }
4718                             _popup_buffer.append( tax.getIdentifier().getValue() );
4719                         }
4720                     }
4721                 }
4722                 if ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) ) {
4723                     lines++;
4724                     boolean enc_data = false;
4725                     if ( _popup_buffer.length() > 0 ) {
4726                         _popup_buffer.append( "\n" );
4727                     }
4728                     final Sequence seq = node.getNodeData().getSequence();
4729                     if ( seq.getAccession() != null ) {
4730                         _popup_buffer.append( "[" );
4731                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
4732                             _popup_buffer.append( seq.getAccession().getSource() );
4733                             _popup_buffer.append( ":" );
4734                         }
4735                         _popup_buffer.append( seq.getAccession().getValue() );
4736                         _popup_buffer.append( "]" );
4737                         enc_data = true;
4738                     }
4739                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
4740                         if ( enc_data ) {
4741                             _popup_buffer.append( " [" );
4742                         }
4743                         else {
4744                             _popup_buffer.append( "[" );
4745                         }
4746                         _popup_buffer.append( seq.getSymbol() );
4747                         _popup_buffer.append( "]" );
4748                         enc_data = true;
4749                     }
4750                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
4751                         if ( enc_data ) {
4752                             _popup_buffer.append( " " );
4753                         }
4754                         _popup_buffer.append( seq.getName() );
4755                     }
4756                 }
4757                 if ( node.getNodeData().isHasDate() ) {
4758                     lines++;
4759                     if ( _popup_buffer.length() > 0 ) {
4760                         _popup_buffer.append( "\n" );
4761                     }
4762                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
4763                 }
4764                 if ( node.getNodeData().isHasDistribution() ) {
4765                     lines++;
4766                     if ( _popup_buffer.length() > 0 ) {
4767                         _popup_buffer.append( "\n" );
4768                     }
4769                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
4770                 }
4771                 if ( node.getBranchData().isHasConfidences() ) {
4772                     final List<Confidence> confs = node.getBranchData().getConfidences();
4773                     for( final Confidence confidence : confs ) {
4774                         lines++;
4775                         if ( _popup_buffer.length() > 0 ) {
4776                             _popup_buffer.append( "\n" );
4777                         }
4778                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
4779                             _popup_buffer.append( "[" );
4780                             _popup_buffer.append( confidence.getType() );
4781                             _popup_buffer.append( "] " );
4782                         }
4783                         _popup_buffer
4784                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
4785                                                                                           getOptions()
4786                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4787                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4788                             _popup_buffer.append( " (sd=" );
4789                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
4790                                     .getStandardDeviation(), getOptions()
4791                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4792                             _popup_buffer.append( ")" );
4793                         }
4794                     }
4795                 }
4796                 if ( node.getNodeData().isHasProperties() ) {
4797                     final PropertiesMap properties = node.getNodeData().getProperties();
4798                     for( final String ref : properties.getPropertyRefs() ) {
4799                         _popup_buffer.append( "\n" );
4800                         final Property p = properties.getProperty( ref );
4801                         _popup_buffer.append( getPartAfterColon( p.getRef() ) );
4802                         _popup_buffer.append( "=" );
4803                         _popup_buffer.append( p.getValue() );
4804                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
4805                             _popup_buffer.append( getPartAfterColon( p.getUnit() ) );
4806                         }
4807                     }
4808                 }
4809                 if ( _popup_buffer.length() > 0 ) {
4810                     if ( !getConfiguration().isUseNativeUI() ) {
4811                         _rollover_popup
4812                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
4813                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
4814                         if ( isInFoundNodes( node ) ) {
4815                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
4816                         }
4817                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4818                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
4819                         }
4820                         else {
4821                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
4822                         }
4823                     }
4824                     else {
4825                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
4826                     }
4827                     _rollover_popup.setText( _popup_buffer.toString() );
4828                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
4829                                                                                   _rollover_popup,
4830                                                                                   e.getLocationOnScreen().x + 10,
4831                                                                                   e.getLocationOnScreen().y
4832                                                                                           - ( lines * 20 ) );
4833                     _node_desc_popup.show();
4834                 }
4835             }
4836         }
4837         catch ( final Exception ex ) {
4838             // Do nothing.
4839         }
4840     }
4841
4842     final private void showNodeEditFrame( final PhylogenyNode n ) {
4843         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4844             // pop up edit box for single node
4845             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
4846             _node_frame_index++;
4847         }
4848         else {
4849             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4850         }
4851     }
4852
4853     final private void showNodeFrame( final PhylogenyNode n ) {
4854         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4855             // pop up edit box for single node
4856             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
4857             _node_frame_index++;
4858         }
4859         else {
4860             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4861         }
4862     }
4863
4864     final void subTree( final PhylogenyNode node ) {
4865         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4866             JOptionPane.showMessageDialog( this,
4867                                            "Cannot get a sub/super tree in unrooted display",
4868                                            "Attempt to get sub/super tree in unrooted display",
4869                                            JOptionPane.WARNING_MESSAGE );
4870             return;
4871         }
4872         if ( node.isExternal() ) {
4873             JOptionPane.showMessageDialog( this,
4874                                            "Cannot get a subtree of a external node",
4875                                            "Attempt to get subtree of external node",
4876                                            JOptionPane.WARNING_MESSAGE );
4877             return;
4878         }
4879         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
4880             JOptionPane.showMessageDialog( this,
4881                                            "Cannot get a subtree of the root node",
4882                                            "Attempt to get subtree of root node",
4883                                            JOptionPane.WARNING_MESSAGE );
4884             return;
4885         }
4886         setNodeInPreorderToNull();
4887         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
4888             _sub_phylogenies[ _subtree_index ] = _phylogeny;
4889             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
4890             ++_subtree_index;
4891             _phylogeny = subTree( node, _phylogeny );
4892             updateSubSuperTreeButton();
4893         }
4894         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
4895             superTree();
4896         }
4897         _main_panel.getControlPanel().showWhole();
4898         repaint();
4899     }
4900
4901     final boolean isCurrentTreeIsSubtree() {
4902         return ( _subtree_index > 0 );
4903     }
4904
4905     final private static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
4906         final Phylogeny new_phy = new Phylogeny();
4907         new_phy.setRooted( true );
4908         new_phy.setName( source_phy.getName() );
4909         new_phy.setDescription( source_phy.getDescription() );
4910         new_phy.setType( source_phy.getType() );
4911         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
4912         new_phy.setConfidence( source_phy.getConfidence() );
4913         new_phy.setIdentifier( source_phy.getIdentifier() );
4914         new_phy.setRoot( new_root.copyNodeDataShallow() );
4915         int i = 0;
4916         for( final PhylogenyNode n : new_root.getDescendants() ) {
4917             new_phy.getRoot().setChildNode( i++, n );
4918         }
4919         return new_phy;
4920     }
4921
4922     final void superTree() {
4923         setNodeInPreorderToNull();
4924         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
4925         for( final PhylogenyNode n : temp_root.getDescendants() ) {
4926             n.setParent( temp_root );
4927         }
4928         _sub_phylogenies[ _subtree_index ] = null;
4929         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
4930         _phylogeny = _sub_phylogenies[ --_subtree_index ];
4931         updateSubSuperTreeButton();
4932     }
4933
4934     final void swap( final PhylogenyNode node ) {
4935         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
4936             return;
4937         }
4938         if ( node.getNumberOfDescendants() > 2 ) {
4939             JOptionPane.showMessageDialog( this,
4940                                            "Cannot swap descendants of nodes with more than 2 descendants",
4941                                            "Cannot swap descendants",
4942                                            JOptionPane.ERROR_MESSAGE );
4943             return;
4944         }
4945         if ( !node.isExternal() ) {
4946             node.swapChildren();
4947             setNodeInPreorderToNull();
4948             _phylogeny.externalNodesHaveChanged();
4949             _phylogeny.hashIDs();
4950             _phylogeny.recalculateNumberOfExternalDescendants( true );
4951             resetNodeIdToDistToLeafMap();
4952             setEdited( true );
4953         }
4954         repaint();
4955     }
4956
4957     final void sortDescendants( final PhylogenyNode node ) {
4958         if ( !node.isExternal() ) {
4959             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
4960             if ( ( !getControlPanel().isShowTaxonomyScientificNames() && !getControlPanel().isShowTaxonomyCode() && !getControlPanel()
4961                     .isShowTaxonomyCommonNames() ) ) {
4962                 if ( ( getControlPanel().isShowSequenceAcc() || getControlPanel().isShowGeneNames() || getControlPanel()
4963                         .isShowGeneSymbols() ) ) {
4964                     pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
4965                 }
4966                 else if ( getControlPanel().isShowNodeNames() ) {
4967                     pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
4968                 }
4969             }
4970             PhylogenyMethods.sortNodeDescendents( node, pri );
4971             setNodeInPreorderToNull();
4972             _phylogeny.externalNodesHaveChanged();
4973             _phylogeny.hashIDs();
4974             _phylogeny.recalculateNumberOfExternalDescendants( true );
4975             resetNodeIdToDistToLeafMap();
4976             setEdited( true );
4977         }
4978         repaint();
4979     }
4980
4981     final private void switchDisplaygetPhylogenyGraphicsType() {
4982         switch ( getPhylogenyGraphicsType() ) {
4983             case RECTANGULAR:
4984                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4985                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4986                 break;
4987             case EURO_STYLE:
4988                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4989                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4990                 break;
4991             case ROUNDED:
4992                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4993                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4994                 break;
4995             case CURVED:
4996                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4997                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4998                 break;
4999             case TRIANGULAR:
5000                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5001                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5002                 break;
5003             case CONVEX:
5004                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5005                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5006                 break;
5007             case UNROOTED:
5008                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5009                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5010                 break;
5011             case CIRCULAR:
5012                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5013                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5014                 break;
5015             default:
5016                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5017         }
5018         if ( getControlPanel().getDynamicallyHideData() != null ) {
5019             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5020                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5021             }
5022             else {
5023                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5024             }
5025         }
5026         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5027             getControlPanel().setDrawPhylogramEnabled( true );
5028         }
5029         else {
5030             getControlPanel().setDrawPhylogramEnabled( false );
5031         }
5032         if ( getMainPanel().getMainFrame() == null ) {
5033             // Must be "E" applet version.
5034             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5035                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5036         }
5037         else {
5038             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5039         }
5040     }
5041
5042     final void taxColor() {
5043         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5044             return;
5045         }
5046         setWaitCursor();
5047         AptxUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
5048         _control_panel.setColorBranches( true );
5049         if ( _control_panel.getColorBranchesCb() != null ) {
5050             _control_panel.getColorBranchesCb().setSelected( true );
5051         }
5052         setArrowCursor();
5053         repaint();
5054     }
5055
5056     final void updateOvSettings() {
5057         switch ( getOptions().getOvPlacement() ) {
5058             case LOWER_LEFT:
5059                 setOvXPosition( OV_BORDER );
5060                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5061                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5062                 break;
5063             case LOWER_RIGHT:
5064                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5065                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5066                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5067                 break;
5068             case UPPER_RIGHT:
5069                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5070                 setOvYPosition( OV_BORDER );
5071                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5072                 break;
5073             default:
5074                 setOvXPosition( OV_BORDER );
5075                 setOvYPosition( OV_BORDER );
5076                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5077                 break;
5078         }
5079     }
5080
5081     final void updateOvSizes() {
5082         if ( ( getWidth() > 1.05 * getVisibleRect().width ) || ( getHeight() > 1.05 * getVisibleRect().height ) ) {
5083             setOvOn( true );
5084             float l = getLongestExtNodeInfo();
5085             final float w_ratio = getOvMaxWidth() / getWidth();
5086             l *= w_ratio;
5087             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
5088             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
5089             float ov_xdist = 0;
5090             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
5091                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
5092             }
5093             else {
5094                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
5095             }
5096             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
5097             if ( ov_xdist < 0.0 ) {
5098                 ov_xdist = 0.0f;
5099             }
5100             if ( ydist < 0.0 ) {
5101                 ydist = 0.0f;
5102             }
5103             setOvXDistance( ov_xdist );
5104             final double height = _phylogeny.getHeight();
5105             if ( height > 0 ) {
5106                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
5107                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
5108             }
5109             else {
5110                 setOvXcorrectionFactor( 0 );
5111             }
5112         }
5113         else {
5114             setOvOn( false );
5115         }
5116     }
5117
5118     final void updateSubSuperTreeButton() {
5119         if ( _subtree_index < 1 ) {
5120             getControlPanel().deactivateButtonToReturnToSuperTree();
5121         }
5122         else {
5123             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
5124         }
5125     }
5126
5127     final void zoomInDomainStructure() {
5128         if ( _domain_structure_width < 2000 ) {
5129             _domain_structure_width *= 1.2;
5130         }
5131     }
5132
5133     final void zoomOutDomainStructure() {
5134         if ( _domain_structure_width > 20 ) {
5135             _domain_structure_width *= 0.8;
5136         }
5137     }
5138
5139     final private static void drawString( final int i, final double x, final double y, final Graphics2D g ) {
5140         g.drawString( String.valueOf( i ), ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5141     }
5142
5143     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5144         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5145     }
5146
5147     final private static boolean isSequenceEmpty( final Sequence seq ) {
5148         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
5149                 && ForesterUtil.isEmpty( seq.getSymbol() );
5150     }
5151
5152     final private static boolean isTaxonomyEmpty( final Taxonomy tax ) {
5153         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
5154                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
5155                 .getSynonyms().isEmpty() );
5156     }
5157
5158     final private static boolean plusPressed( final int key_code ) {
5159         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5160                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5161     }
5162
5163     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
5164         _statistics_for_vector_data = statistics_for_expression_values;
5165     }
5166
5167     DescriptiveStatistics getStatisticsForExpressionValues() {
5168         return _statistics_for_vector_data;
5169     }
5170
5171     final private class SubtreeColorizationActionListener implements ActionListener {
5172
5173         JColorChooser _chooser;
5174         PhylogenyNode _node;
5175
5176         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5177             _chooser = chooser;
5178             _node = node;
5179         }
5180
5181         @Override
5182         public void actionPerformed( final ActionEvent e ) {
5183             final Color c = _chooser.getColor();
5184             if ( c != null ) {
5185                 colorizeSubtree( c, _node );
5186             }
5187         }
5188     }
5189
5190     public synchronized void setImageMap( final Hashtable<String, BufferedImage> image_map ) {
5191         getMainPanel().setImageMap( image_map );
5192     }
5193
5194     public synchronized Hashtable<String, BufferedImage> getImageMap() {
5195         return getMainPanel().getImageMap();
5196     }
5197 }