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