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