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