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