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