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