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         return c;
2375     }
2376
2377     final private float calculateOvBranchLengthToParent( final PhylogenyNode node, final int factor ) {
2378         if ( getControlPanel().isDrawPhylogram() ) {
2379             if ( node.getDistanceToParent() < 0.0 ) {
2380                 return 0.0f;
2381             }
2382             return ( float ) ( getOvXcorrectionFactor() * node.getDistanceToParent() );
2383         }
2384         else {
2385             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
2386                 return getOvXDistance();
2387             }
2388             return getOvXDistance() * factor;
2389         }
2390     }
2391
2392     final private void cannotOpenBrowserWarningMessage( final String type_type ) {
2393         JOptionPane.showMessageDialog( this,
2394                                        "Cannot launch web browser for " + type_type + " data of this node",
2395                                        "Cannot launch web browser",
2396                                        JOptionPane.WARNING_MESSAGE );
2397     }
2398
2399     private void changeNodeFont( final PhylogenyNode node ) {
2400         final FontChooser fc = new FontChooser();
2401         Font f = null;
2402         if ( ( node.getNodeData().getNodeVisualData() != null ) && !node.getNodeData().getNodeVisualData().isEmpty() ) {
2403             f = node.getNodeData().getNodeVisualData().getFont();
2404         }
2405         if ( f != null ) {
2406             fc.setFont( f );
2407         }
2408         else {
2409             fc.setFont( getMainPanel().getTreeFontSet().getLargeFont() );
2410         }
2411         fc.showDialog( this, "Select Font" );
2412         if ( ( fc.getFont() != null ) && !ForesterUtil.isEmpty( fc.getFont().getFamily().trim() ) ) {
2413             List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
2414             if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
2415                 nodes = getFoundNodesAsListOfPhylogenyNodes();
2416             }
2417             nodes.add( node );
2418             for( final PhylogenyNode n : nodes ) {
2419                 if ( n.getNodeData().getNodeVisualData() == null ) {
2420                     n.getNodeData().setNodeVisualData( new NodeVisualData() );
2421                 }
2422                 final NodeVisualData vd = n.getNodeData().getNodeVisualData();
2423                 final Font ff = fc.getFont();
2424                 vd.setFontName( ff.getFamily().trim() );
2425                 int s = ff.getSize();
2426                 if ( s < 0 ) {
2427                     s = 0;
2428                 }
2429                 if ( s > Byte.MAX_VALUE ) {
2430                     s = Byte.MAX_VALUE;
2431                 }
2432                 vd.setFontSize( s );
2433                 vd.setFontStyle( ff.getStyle() );
2434             }
2435             if ( _control_panel.getUseVisualStylesCb() != null ) {
2436                 getControlPanel().getUseVisualStylesCb().setSelected( true );
2437             }
2438         }
2439         repaint();
2440     }
2441
2442     final private void colorizeNodes( final Color c,
2443                                       final PhylogenyNode node,
2444                                       final List<PhylogenyNode> additional_nodes ) {
2445         _control_panel.setColorBranches( true );
2446         if ( _control_panel.getUseVisualStylesCb() != null ) {
2447             _control_panel.getUseVisualStylesCb().setSelected( true );
2448         }
2449         if ( node != null ) {
2450             colorizeNodesHelper( c, node );
2451         }
2452         if ( additional_nodes != null ) {
2453             for( final PhylogenyNode n : additional_nodes ) {
2454                 colorizeNodesHelper( c, n );
2455             }
2456         }
2457         repaint();
2458     }
2459
2460     final private void colorizeSubtree( final Color c,
2461                                         final PhylogenyNode node,
2462                                         final List<PhylogenyNode> additional_nodes ) {
2463         _control_panel.setColorBranches( true );
2464         if ( _control_panel.getUseVisualStylesCb() != null ) {
2465             _control_panel.getUseVisualStylesCb().setSelected( true );
2466         }
2467         if ( node != null ) {
2468             for( final PreorderTreeIterator it = new PreorderTreeIterator( node ); it.hasNext(); ) {
2469                 it.next().getBranchData().setBranchColor( new BranchColor( c ) );
2470             }
2471         }
2472         if ( additional_nodes != null ) {
2473             for( final PhylogenyNode n : additional_nodes ) {
2474                 n.getBranchData().setBranchColor( new BranchColor( c ) );
2475             }
2476         }
2477         repaint();
2478     }
2479
2480     private void colorNodeFont( final PhylogenyNode node ) {
2481         _color_chooser.setPreviewPanel( new JPanel() );
2482         NodeColorizationActionListener al;
2483         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
2484             final List<PhylogenyNode> additional_nodes = getFoundNodesAsListOfPhylogenyNodes();
2485             al = new NodeColorizationActionListener( _color_chooser, node, additional_nodes );
2486         }
2487         else {
2488             al = new NodeColorizationActionListener( _color_chooser, node );
2489         }
2490         final JDialog dialog = JColorChooser.createDialog( this, "Node colorization", true, _color_chooser, al, null );
2491         dialog.setVisible( true );
2492     }
2493
2494     final private void colorSubtree( final PhylogenyNode node ) {
2495         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2496             JOptionPane.showMessageDialog( this,
2497                                            "Cannot colorize subtree in unrooted display type",
2498                                            "Attempt to colorize subtree in unrooted display",
2499                                            JOptionPane.WARNING_MESSAGE );
2500             return;
2501         }
2502         _color_chooser.setPreviewPanel( new JPanel() );
2503         SubtreeColorizationActionListener al;
2504         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
2505             final List<PhylogenyNode> additional_nodes = getFoundNodesAsListOfPhylogenyNodes();
2506             al = new SubtreeColorizationActionListener( _color_chooser, node, additional_nodes );
2507         }
2508         else {
2509             al = new SubtreeColorizationActionListener( _color_chooser, node );
2510         }
2511         final JDialog dialog = JColorChooser
2512                 .createDialog( this, "Subtree colorization", true, _color_chooser, al, null );
2513         dialog.setVisible( true );
2514     }
2515
2516     final private void copySubtree( final PhylogenyNode node ) {
2517         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2518             errorMessageNoCutCopyPasteInUnrootedDisplay();
2519             return;
2520         }
2521         setNodeInPreorderToNull();
2522         setCutOrCopiedTree( _phylogeny.copy( node ) );
2523         final List<PhylogenyNode> nodes = PhylogenyMethods.getAllDescendants( node );
2524         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
2525         for( final PhylogenyNode n : nodes ) {
2526             node_ids.add( n.getId() );
2527         }
2528         node_ids.add( node.getId() );
2529         setCopiedAndPastedNodes( node_ids );
2530         repaint();
2531     }
2532
2533     final private String createASimpleTextRepresentationOfANode( final PhylogenyNode node ) {
2534         final String tax = PhylogenyMethods.getSpecies( node );
2535         String label = node.getName();
2536         if ( !ForesterUtil.isEmpty( label ) && !ForesterUtil.isEmpty( tax ) ) {
2537             label = label + " " + tax;
2538         }
2539         else if ( !ForesterUtil.isEmpty( tax ) ) {
2540             label = tax;
2541         }
2542         else {
2543             label = "";
2544         }
2545         if ( !ForesterUtil.isEmpty( label ) ) {
2546             label = " [" + label + "]";
2547         }
2548         return label;
2549     }
2550
2551     final private void cutSubtree( final PhylogenyNode node ) {
2552         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2553             errorMessageNoCutCopyPasteInUnrootedDisplay();
2554             return;
2555         }
2556         if ( node.isRoot() ) {
2557             JOptionPane.showMessageDialog( this,
2558                                            "Cannot cut entire tree as subtree",
2559                                            "Attempt to cut entire tree",
2560                                            JOptionPane.ERROR_MESSAGE );
2561             return;
2562         }
2563         final String label = createASimpleTextRepresentationOfANode( node );
2564         final int r = JOptionPane.showConfirmDialog( null,
2565                                                      "Cut subtree" + label + "?",
2566                                                      "Confirm Cutting of Subtree",
2567                                                      JOptionPane.YES_NO_OPTION );
2568         if ( r != JOptionPane.OK_OPTION ) {
2569             return;
2570         }
2571         setNodeInPreorderToNull();
2572         setCopiedAndPastedNodes( null );
2573         setCutOrCopiedTree( _phylogeny.copy( node ) );
2574         _phylogeny.deleteSubtree( node, true );
2575         _phylogeny.clearHashIdToNodeMap();
2576         _phylogeny.recalculateNumberOfExternalDescendants( true );
2577         resetNodeIdToDistToLeafMap();
2578         setEdited( true );
2579         repaint();
2580     }
2581
2582     final private void cycleColors() {
2583         getMainPanel().getTreeColorSet().cycleColorScheme();
2584         for( final TreePanel tree_panel : getMainPanel().getTreePanels() ) {
2585             tree_panel.setBackground( getMainPanel().getTreeColorSet().getBackgroundColor() );
2586         }
2587     }
2588
2589     final private void decreaseOvSize() {
2590         if ( ( getOvMaxWidth() > 20 ) && ( getOvMaxHeight() > 20 ) ) {
2591             setOvMaxWidth( getOvMaxWidth() - 5 );
2592             setOvMaxHeight( getOvMaxHeight() - 5 );
2593             updateOvSettings();
2594             getControlPanel().displayedPhylogenyMightHaveChanged( false );
2595         }
2596     }
2597
2598     final private void deleteNodeOrSubtree( final PhylogenyNode node ) {
2599         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
2600             errorMessageNoCutCopyPasteInUnrootedDisplay();
2601             return;
2602         }
2603         if ( node.isRoot() && ( node.getNumberOfDescendants() != 1 ) ) {
2604             JOptionPane.showMessageDialog( this,
2605                                            "Cannot delete entire tree",
2606                                            "Attempt to delete entire tree",
2607                                            JOptionPane.ERROR_MESSAGE );
2608             return;
2609         }
2610         final String label = createASimpleTextRepresentationOfANode( node );
2611         final Object[] options = { "Node only", "Entire subtree", "Cancel" };
2612         final int r = JOptionPane.showOptionDialog( this,
2613                                                     "Delete" + label + "?",
2614                                                     "Delete Node/Subtree",
2615                                                     JOptionPane.CLOSED_OPTION,
2616                                                     JOptionPane.QUESTION_MESSAGE,
2617                                                     null,
2618                                                     options,
2619                                                     options[ 2 ] );
2620         setNodeInPreorderToNull();
2621         boolean node_only = true;
2622         if ( r == 1 ) {
2623             node_only = false;
2624         }
2625         else if ( r != 0 ) {
2626             return;
2627         }
2628         if ( node_only ) {
2629             PhylogenyMethods.removeNode( node, _phylogeny );
2630         }
2631         else {
2632             _phylogeny.deleteSubtree( node, true );
2633         }
2634         _phylogeny.externalNodesHaveChanged();
2635         _phylogeny.clearHashIdToNodeMap();
2636         _phylogeny.recalculateNumberOfExternalDescendants( true );
2637         resetNodeIdToDistToLeafMap();
2638         setEdited( true );
2639         repaint();
2640     }
2641
2642     final private void displayNodePopupMenu( final PhylogenyNode node, final int x, final int y ) {
2643         makePopupMenus( node );
2644         _node_popup_menu.putClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY, node );
2645         _node_popup_menu.show( this, x, y );
2646     }
2647
2648     final private void drawArc( final double x,
2649                                 final double y,
2650                                 final double width,
2651                                 final double heigth,
2652                                 final double start_angle,
2653                                 final double arc_angle,
2654                                 final Graphics2D g ) {
2655         _arc.setArc( x, y, width, heigth, _180_OVER_PI * start_angle, _180_OVER_PI * arc_angle, Arc2D.OPEN );
2656         g.draw( _arc );
2657     }
2658
2659     final private void drawLine( final double x1, final double y1, final double x2, final double y2, final Graphics2D g ) {
2660         if ( ( x1 == x2 ) && ( y1 == y2 ) ) {
2661             return;
2662         }
2663         _line.setLine( x1, y1, x2, y2 );
2664         g.draw( _line );
2665     }
2666
2667     final private void drawOval( final double x,
2668                                  final double y,
2669                                  final double width,
2670                                  final double heigth,
2671                                  final Graphics2D g ) {
2672         _ellipse.setFrame( x, y, width, heigth );
2673         g.draw( _ellipse );
2674     }
2675
2676     final private void drawOvalFilled( final double x,
2677                                        final double y,
2678                                        final double width,
2679                                        final double heigth,
2680                                        final Graphics2D g ) {
2681         _ellipse.setFrame( x, y, width, heigth );
2682         g.fill( _ellipse );
2683     }
2684
2685     final private void drawOvalGradient( final float x,
2686                                          final float y,
2687                                          final float width,
2688                                          final float heigth,
2689                                          final Graphics2D g,
2690                                          final Color color_1,
2691                                          final Color color_2,
2692                                          final Color color_border ) {
2693         _ellipse.setFrame( x, y, width, heigth );
2694         g.setPaint( new GradientPaint( x, y, color_1, ( x + width ), ( y + heigth ), color_2, false ) );
2695         g.fill( _ellipse );
2696         if ( color_border != null ) {
2697             g.setPaint( color_border );
2698             g.draw( _ellipse );
2699         }
2700     }
2701
2702     final private void drawRect( final float x, final float y, final float width, final float heigth, final Graphics2D g ) {
2703         _rectangle.setFrame( x, y, width, heigth );
2704         g.draw( _rectangle );
2705     }
2706
2707     final private void drawRectFilled( final double x,
2708                                        final double y,
2709                                        final double width,
2710                                        final double heigth,
2711                                        final Graphics2D g ) {
2712         _rectangle.setFrame( x, y, width, heigth );
2713         g.fill( _rectangle );
2714     }
2715
2716     final private void drawRectGradient( final float x,
2717                                          final float y,
2718                                          final float width,
2719                                          final float heigth,
2720                                          final Graphics2D g,
2721                                          final Color color_1,
2722                                          final Color color_2,
2723                                          final Color color_border ) {
2724         _rectangle.setFrame( x, y, width, heigth );
2725         g.setPaint( new GradientPaint( x, y, color_1, ( x + width ), ( y + heigth ), color_2, false ) );
2726         g.fill( _rectangle );
2727         if ( color_border != null ) {
2728             g.setPaint( color_border );
2729             g.draw( _rectangle );
2730         }
2731     }
2732
2733     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
2734         final List<Uri> us = new ArrayList<Uri>();
2735         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
2736             for( final Uri uri : t.getUris() ) {
2737                 us.add( uri );
2738             }
2739         }
2740         double offset = 0;
2741         for( final Uri uri : us ) {
2742             if ( uri != null ) {
2743                 final String uri_str = uri.getValue().toString().toLowerCase();
2744                 if ( getImageMap().containsKey( uri_str ) ) {
2745                     final BufferedImage bi = getImageMap().get( uri_str );
2746                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
2747                         double scaling_factor = 1;
2748                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
2749                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
2750                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
2751                         }
2752                         // y = y - ( 0.9 * getYdistance() );
2753                         final double hs = bi.getHeight() * scaling_factor;
2754                         double ws = ( bi.getWidth() * scaling_factor ) + offset;
2755                         final double my_y = y - ( 0.5 * hs );
2756                         final int x_w = ( int ) ( x + ws + 0.5 );
2757                         final int y_h = ( int ) ( my_y + hs + 0.5 );
2758                         if ( ( ( x_w - x ) > 7 ) && ( ( y_h - my_y ) > 7 ) ) {
2759                             g.drawImage( bi,
2760                                          ( int ) ( x + 0.5 + offset ),
2761                                          ( int ) ( my_y + 0.5 ),
2762                                          x_w,
2763                                          y_h,
2764                                          0,
2765                                          0,
2766                                          bi.getWidth(),
2767                                          bi.getHeight(),
2768                                          null );
2769                             ws += 8;
2770                         }
2771                         else {
2772                             ws = 0.0;
2773                         }
2774                         offset = ws;
2775                     }
2776                 }
2777             }
2778         }
2779         return offset;
2780     }
2781
2782     final private void errorMessageNoCutCopyPasteInUnrootedDisplay() {
2783         JOptionPane.showMessageDialog( this,
2784                                        "Cannot cut, copy, paste, add, or delete subtrees/nodes in unrooted display",
2785                                        "Attempt to cut/copy/paste/add/delete in unrooted display",
2786                                        JOptionPane.ERROR_MESSAGE );
2787     }
2788
2789     private final Color getColorForFoundNode( final PhylogenyNode n ) {
2790         if ( isInCurrentExternalNodes( n ) ) {
2791             return getTreeColorSet().getFoundColor0();
2792         }
2793         else if ( isInFoundNodes0( n ) && !isInFoundNodes1( n ) ) {
2794             return getTreeColorSet().getFoundColor0();
2795         }
2796         else if ( !isInFoundNodes0( n ) && isInFoundNodes1( n ) ) {
2797             return getTreeColorSet().getFoundColor1();
2798         }
2799         else {
2800             return getTreeColorSet().getFoundColor0and1();
2801         }
2802     }
2803
2804     final private Set<Long> getCopiedAndPastedNodes() {
2805         return getMainPanel().getCopiedAndPastedNodes();
2806     }
2807
2808     final private Set<Long> getCurrentExternalNodes() {
2809         return _current_external_nodes;
2810     }
2811
2812     final private Phylogeny getCutOrCopiedTree() {
2813         return getMainPanel().getCutOrCopiedTree();
2814     }
2815
2816     private FontMetrics getFontMetricsForLargeDefaultFont() {
2817         return getTreeFontSet().getFontMetricsLarge();
2818     }
2819
2820     private List<PhylogenyNode> getFoundNodesAsListOfPhylogenyNodes() {
2821         final List<PhylogenyNode> additional_nodes = new ArrayList<PhylogenyNode>();
2822         if ( getFoundNodes0() != null ) {
2823             for( final Long id : getFoundNodes0() ) {
2824                 additional_nodes.add( _phylogeny.getNode( id ) );
2825             }
2826         }
2827         if ( getFoundNodes1() != null ) {
2828             for( final Long id : getFoundNodes1() ) {
2829                 if ( ( getFoundNodes0() == null ) || !getFoundNodes0().contains( id ) ) {
2830                     additional_nodes.add( _phylogeny.getNode( id ) );
2831                 }
2832             }
2833         }
2834         return additional_nodes;
2835     }
2836
2837     final private float getLastDragPointX() {
2838         return _last_drag_point_x;
2839     }
2840
2841     final private float getLastDragPointY() {
2842         return _last_drag_point_y;
2843     }
2844
2845     final private short getMaxBranchesToLeaf( final PhylogenyNode node ) {
2846         if ( !_nodeid_dist_to_leaf.containsKey( node.getId() ) ) {
2847             final short m = PhylogenyMethods.calculateMaxBranchesToLeaf( node );
2848             _nodeid_dist_to_leaf.put( node.getId(), m );
2849             return m;
2850         }
2851         else {
2852             return _nodeid_dist_to_leaf.get( node.getId() );
2853         }
2854     }
2855
2856     final private double getMaxDistanceToRoot() {
2857         if ( _max_distance_to_root < 0 ) {
2858             recalculateMaxDistanceToRoot();
2859         }
2860         return _max_distance_to_root;
2861     }
2862
2863     final private float getOvMaxHeight() {
2864         return _ov_max_height;
2865     }
2866
2867     final private float getOvMaxWidth() {
2868         return _ov_max_width;
2869     }
2870
2871     final private float getOvXcorrectionFactor() {
2872         return _ov_x_correction_factor;
2873     }
2874
2875     final private float getOvXDistance() {
2876         return _ov_x_distance;
2877     }
2878
2879     final private int getOvXPosition() {
2880         return _ov_x_position;
2881     }
2882
2883     final private float getOvYDistance() {
2884         return _ov_y_distance;
2885     }
2886
2887     final private int getOvYPosition() {
2888         return _ov_y_position;
2889     }
2890
2891     final private int getOvYStart() {
2892         return _ov_y_start;
2893     }
2894
2895     final private List<Accession> getPdbAccs( final PhylogenyNode node ) {
2896         final List<Accession> pdb_ids = new ArrayList<Accession>();
2897         if ( node.getNodeData().isHasSequence() ) {
2898             final Sequence seq = node.getNodeData().getSequence();
2899             if ( !ForesterUtil.isEmpty( seq.getCrossReferences() ) ) {
2900                 final SortedSet<Accession> cross_refs = seq.getCrossReferences();
2901                 for( final Accession acc : cross_refs ) {
2902                     if ( acc.getSource().equalsIgnoreCase( "pdb" ) ) {
2903                         pdb_ids.add( acc );
2904                     }
2905                 }
2906             }
2907         }
2908         return pdb_ids;
2909     }
2910
2911     final private double getScaleDistance() {
2912         return _scale_distance;
2913     }
2914
2915     final private String getScaleLabel() {
2916         return _scale_label;
2917     }
2918
2919     final private TreeFontSet getTreeFontSet() {
2920         return getMainPanel().getTreeFontSet();
2921     }
2922
2923     final private float getUrtFactor() {
2924         return _urt_factor;
2925     }
2926
2927     final private float getUrtFactorOv() {
2928         return _urt_factor_ov;
2929     }
2930
2931     final private void handleClickToAction( final NodeClickAction action, final PhylogenyNode node ) {
2932         switch ( action ) {
2933             case SHOW_DATA:
2934                 showNodeFrame( node );
2935                 break;
2936             case COLLAPSE:
2937                 collapse( node );
2938                 break;
2939             case REROOT:
2940                 reRoot( node );
2941                 break;
2942             case SUBTREE:
2943                 subTree( node );
2944                 break;
2945             case SWAP:
2946                 swap( node );
2947                 break;
2948             case COLOR_SUBTREE:
2949                 colorSubtree( node );
2950                 break;
2951             case COLOR_NODE_FONT:
2952                 colorNodeFont( node );
2953                 break;
2954             case CHANGE_NODE_FONT:
2955                 changeNodeFont( node );
2956                 break;
2957             case OPEN_SEQ_WEB:
2958                 openSeqWeb( node );
2959                 break;
2960             case BLAST:
2961                 blast( node );
2962                 break;
2963             case OPEN_TAX_WEB:
2964                 openTaxWeb( node );
2965                 break;
2966             case OPEN_PDB_WEB:
2967                 openPdbWeb( node );
2968                 break;
2969             case CUT_SUBTREE:
2970                 cutSubtree( node );
2971                 break;
2972             case COPY_SUBTREE:
2973                 copySubtree( node );
2974                 break;
2975             case PASTE_SUBTREE:
2976                 pasteSubtree( node );
2977                 break;
2978             case DELETE_NODE_OR_SUBTREE:
2979                 deleteNodeOrSubtree( node );
2980                 break;
2981             case ADD_NEW_NODE:
2982                 addEmptyNode( node );
2983                 break;
2984             case EDIT_NODE_DATA:
2985                 showNodeEditFrame( node );
2986                 break;
2987             case SELECT_NODES:
2988                 selectNode( node );
2989                 break;
2990             case SORT_DESCENDENTS:
2991                 sortDescendants( node );
2992                 break;
2993             case GET_EXT_DESC_DATA:
2994                 showExtDescNodeData( node );
2995                 break;
2996             default:
2997                 throw new IllegalArgumentException( "unknown action: " + action );
2998         }
2999     }
3000
3001     final private void increaseCurrentExternalNodesDataBufferChangeCounter() {
3002         _current_external_nodes_data_buffer_change_counter++;
3003     }
3004
3005     final private void increaseOvSize() {
3006         if ( ( getOvMaxWidth() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth() / 2 ) )
3007                 && ( getOvMaxHeight() < ( getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
3008                         .getHeight() / 2 ) ) ) {
3009             setOvMaxWidth( getOvMaxWidth() + 5 );
3010             setOvMaxHeight( getOvMaxHeight() + 5 );
3011             updateOvSettings();
3012             getControlPanel().displayedPhylogenyMightHaveChanged( false );
3013         }
3014     }
3015
3016     final private void init() {
3017         _color_chooser = new JColorChooser();
3018         _rollover_popup = new JTextArea();
3019         _rollover_popup.setFont( POPUP_FONT );
3020         resetNodeIdToDistToLeafMap();
3021         setTextAntialias();
3022         setTreeFile( null );
3023         setEdited( false );
3024         initializeOvSettings();
3025         setStartingAngle( ( TWO_PI * 3 ) / 4 );
3026         final ImageLoader il = new ImageLoader( this );
3027         new Thread( il ).start();
3028     }
3029
3030     final private void initializeOvSettings() {
3031         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
3032         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
3033     }
3034
3035     final private boolean inOvVirtualRectangle( final int x, final int y ) {
3036         return ( ( x >= ( getOvVirtualRectangle().x - 1 ) )
3037                 && ( x <= ( getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 ) )
3038                 && ( y >= ( getOvVirtualRectangle().y - 1 ) ) && ( y <= ( getOvVirtualRectangle().y
3039                 + getOvVirtualRectangle().height + 1 ) ) );
3040     }
3041
3042     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
3043         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
3044     }
3045
3046     final private boolean isCanBlast( final PhylogenyNode node ) {
3047         if ( !node.getNodeData().isHasSequence() && ForesterUtil.isEmpty( node.getName() ) ) {
3048             return false;
3049         }
3050         return Blast.isContainsQueryForBlast( node );
3051     }
3052
3053     final private String isCanOpenSeqWeb( final PhylogenyNode node ) {
3054         final Accession a = SequenceAccessionTools.obtainAccessorFromDataFields( node );
3055         if ( a != null ) {
3056             return a.getValue();
3057         }
3058         return null;
3059     }
3060
3061     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
3062         if ( node.getNodeData().isHasTaxonomy()
3063                 && ( ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
3064                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
3065                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) || ( ( node
3066                         .getNodeData().getTaxonomy().getIdentifier() != null ) && !ForesterUtil.isEmpty( node
3067                         .getNodeData().getTaxonomy().getIdentifier().getValue() ) ) ) ) {
3068             return true;
3069         }
3070         else {
3071             return false;
3072         }
3073     }
3074
3075     final private boolean isInCurrentExternalNodes( final PhylogenyNode node ) {
3076         return ( ( getCurrentExternalNodes() != null ) && getCurrentExternalNodes().contains( node.getId() ) );
3077     }
3078
3079     private boolean isInFoundNodes( final PhylogenyNode n ) {
3080         return isInFoundNodes0( n ) || isInFoundNodes1( n );
3081     }
3082
3083     final private boolean isInFoundNodes0( final PhylogenyNode node ) {
3084         return ( ( getFoundNodes0() != null ) && getFoundNodes0().contains( node.getId() ) );
3085     }
3086
3087     final private boolean isInFoundNodes1( final PhylogenyNode node ) {
3088         return ( ( getFoundNodes1() != null ) && getFoundNodes1().contains( node.getId() ) );
3089     }
3090
3091     final private boolean isInOv() {
3092         return _in_ov;
3093     }
3094
3095     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
3096         int y_dist = 40;
3097         if ( getControlPanel().isShowTaxonomyImages() ) {
3098             y_dist = 40 + ( int ) getYdistance();
3099         }
3100         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - y_dist ) )
3101                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + y_dist ) ) || ( ( node.getParent() != null ) && ( node
3102                 .getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
3103     }
3104
3105     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
3106         return ( ( node.getYcoord() < ( getVisibleRect().getMinY() - 20 ) )
3107                 || ( node.getYcoord() > ( getVisibleRect().getMaxY() + 20 ) )
3108                 || ( node.getXcoord() < ( getVisibleRect().getMinX() - 20 ) ) || ( node.getXcoord() > ( getVisibleRect()
3109                 .getMaxX() + 20 ) ) );
3110     }
3111
3112     final private boolean isNonLinedUpCladogram() {
3113         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
3114     }
3115
3116     final private boolean isUniformBranchLengthsForCladogram() {
3117         return getOptions().getCladogramType() == CLADOGRAM_TYPE.TOTAL_NODE_SUM_DEP;
3118     }
3119
3120     final private void keyPressedCalls( final KeyEvent e ) {
3121         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
3122             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
3123                 if ( !isInOvRect() ) {
3124                     setInOvRect( true );
3125                 }
3126             }
3127             else if ( isInOvRect() ) {
3128                 setInOvRect( false );
3129             }
3130         }
3131         if ( e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK ) {
3132             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
3133                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
3134                 getMainPanel().getTreeFontSet().mediumFonts();
3135                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
3136             }
3137             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
3138                 getMainPanel().getTreeFontSet().decreaseFontSize( 1, false );
3139                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
3140             }
3141             else if ( plusPressed( e.getKeyCode() ) ) {
3142                 getMainPanel().getTreeFontSet().increaseFontSize();
3143                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
3144             }
3145         }
3146         else {
3147             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
3148                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
3149                 getControlPanel().showWhole();
3150             }
3151             else if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
3152                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
3153                 if ( e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK ) {
3154                     if ( e.getKeyCode() == KeyEvent.VK_UP ) {
3155                         getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
3156                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3157                     }
3158                     else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
3159                         getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
3160                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3161                     }
3162                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
3163                         getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
3164                                                                    Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
3165                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3166                     }
3167                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
3168                         getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
3169                                                                   Constants.WHEEL_ZOOM_IN_FACTOR );
3170                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3171                     }
3172                 }
3173                 else {
3174                     final int d = 80;
3175                     int dx = 0;
3176                     int dy = -d;
3177                     if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
3178                         dy = d;
3179                     }
3180                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
3181                         dx = -d;
3182                         dy = 0;
3183                     }
3184                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
3185                         dx = d;
3186                         dy = 0;
3187                     }
3188                     final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
3189                     scroll_position.x = scroll_position.x + dx;
3190                     scroll_position.y = scroll_position.y + dy;
3191                     if ( scroll_position.x <= 0 ) {
3192                         scroll_position.x = 0;
3193                     }
3194                     else {
3195                         final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
3196                                 - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
3197                         if ( scroll_position.x >= max_x ) {
3198                             scroll_position.x = max_x;
3199                         }
3200                     }
3201                     if ( scroll_position.y <= 0 ) {
3202                         scroll_position.y = 0;
3203                     }
3204                     else {
3205                         final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
3206                                 - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
3207                         if ( scroll_position.y >= max_y ) {
3208                             scroll_position.y = max_y;
3209                         }
3210                     }
3211                     repaint();
3212                     getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
3213                 }
3214             }
3215             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
3216                 getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
3217                 getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
3218                                                            Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
3219                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3220             }
3221             else if ( plusPressed( e.getKeyCode() ) ) {
3222                 getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
3223                                                           Constants.WHEEL_ZOOM_IN_FACTOR );
3224                 getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
3225                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
3226             }
3227             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
3228                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3229                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3230                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
3231                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3232                 }
3233             }
3234             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
3235                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3236                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3237                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
3238                     if ( getStartingAngle() < 0 ) {
3239                         setStartingAngle( TWO_PI + getStartingAngle() );
3240                     }
3241                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
3242                 }
3243             }
3244             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
3245                 boolean selected = false;
3246                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
3247                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
3248                     selected = true;
3249                 }
3250                 else {
3251                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
3252                 }
3253                 if ( getMainPanel().getMainFrame() == null ) {
3254                     // Must be "E" applet version.
3255                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
3256                     if ( ae.getlabelDirectionCbmi() != null ) {
3257                         ae.getlabelDirectionCbmi().setSelected( selected );
3258                     }
3259                 }
3260                 else {
3261                     getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
3262                 }
3263                 repaint();
3264             }
3265             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
3266                 switchDisplaygetPhylogenyGraphicsType();
3267                 repaint();
3268             }
3269             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
3270                 cycleColors();
3271                 repaint();
3272             }
3273             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
3274                 MainFrame.cycleOverview( getOptions(), this );
3275                 repaint();
3276             }
3277             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
3278                 increaseOvSize();
3279             }
3280             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
3281                 decreaseOvSize();
3282             }
3283             e.consume();
3284         }
3285     }
3286
3287     final private void makePopupMenus( final PhylogenyNode node ) {
3288         _node_popup_menu = new JPopupMenu();
3289         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
3290         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
3291         for( int i = 0; i < clickto_names.size(); i++ ) {
3292             final String title = clickto_names.get( i );
3293             _node_popup_menu_items[ i ] = new JMenuItem( title );
3294             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
3295                 final String id = isCanOpenSeqWeb( node );
3296                 if ( !ForesterUtil.isEmpty( id ) ) {
3297                     _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " [" + id + "]" );
3298                     _node_popup_menu_items[ i ].setEnabled( true );
3299                 }
3300                 else {
3301                     _node_popup_menu_items[ i ].setEnabled( false );
3302                 }
3303             }
3304             else if ( title.equals( Configuration.clickto_options[ Configuration.open_pdb_web ][ 0 ] ) ) {
3305                 final List<Accession> accs = getPdbAccs( node );
3306                 _node_popup_menu_items[ i ] = new JMenuItem( title );
3307                 if ( !ForesterUtil.isEmpty( accs ) ) {
3308                     if ( accs.size() == 1 ) {
3309                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
3310                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + "]" );
3311                         _node_popup_menu_items[ i ].setEnabled( true );
3312                     }
3313                     else if ( accs.size() == 2 ) {
3314                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
3315                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
3316                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + "]" );
3317                         _node_popup_menu_items[ i ].setEnabled( true );
3318                     }
3319                     else if ( accs.size() == 3 ) {
3320                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
3321                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
3322                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + ", "
3323                                 + TreePanelUtil.pdbAccToString( accs, 2 ) + "]" );
3324                         _node_popup_menu_items[ i ].setEnabled( true );
3325                     }
3326                     else {
3327                         _node_popup_menu_items[ i ].setText( _node_popup_menu_items[ i ].getText() + " ["
3328                                 + TreePanelUtil.pdbAccToString( accs, 0 ) + ", "
3329                                 + TreePanelUtil.pdbAccToString( accs, 1 ) + ", "
3330                                 + TreePanelUtil.pdbAccToString( accs, 2 ) + ", + " + ( accs.size() - 3 ) + " more]" );
3331                         _node_popup_menu_items[ i ].setEnabled( true );
3332                     }
3333                 }
3334                 else {
3335                     _node_popup_menu_items[ i ].setEnabled( false );
3336                 }
3337                 //
3338             }
3339             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
3340                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
3341             }
3342             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
3343                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
3344             }
3345             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
3346                 if ( !getOptions().isEditable() ) {
3347                     continue;
3348                 }
3349                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
3350             }
3351             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
3352                 if ( !getOptions().isEditable() ) {
3353                     continue;
3354                 }
3355                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
3356             }
3357             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
3358                 if ( !getOptions().isEditable() ) {
3359                     continue;
3360                 }
3361                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
3362             }
3363             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
3364                 if ( !getOptions().isEditable() ) {
3365                     continue;
3366                 }
3367                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
3368             }
3369             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
3370                 if ( !getOptions().isEditable() ) {
3371                     continue;
3372                 }
3373             }
3374             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
3375                 if ( !getOptions().isEditable() ) {
3376                     continue;
3377                 }
3378             }
3379             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
3380                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
3381             }
3382             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
3383                 _node_popup_menu_items[ i ].setEnabled( ( isCanCollapse() && !node.isExternal() ) );
3384             }
3385             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
3386                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
3387             }
3388             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
3389                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
3390             }
3391             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
3392                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
3393             }
3394             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
3395                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
3396             }
3397             _node_popup_menu_items[ i ].addActionListener( this );
3398             _node_popup_menu.add( _node_popup_menu_items[ i ] );
3399         }
3400     }
3401
3402     private final void nodeDataAsSB( final PhylogenyNode node, final StringBuilder sb ) {
3403         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3404             if ( sb.length() > 0 ) {
3405                 sb.append( " " );
3406             }
3407             sb.append( node.getName() );
3408         }
3409         if ( node.getNodeData().isHasSequence() ) {
3410             if ( getControlPanel().isShowSeqSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3411                 if ( sb.length() > 0 ) {
3412                     sb.append( " " );
3413                 }
3414                 sb.append( node.getNodeData().getSequence().getSymbol() );
3415             }
3416             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getGeneName().length() > 0 ) ) {
3417                 if ( sb.length() > 0 ) {
3418                     sb.append( " " );
3419                 }
3420                 sb.append( node.getNodeData().getSequence().getGeneName() );
3421             }
3422             if ( getControlPanel().isShowSeqNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3423                 if ( sb.length() > 0 ) {
3424                     sb.append( " " );
3425                 }
3426                 sb.append( node.getNodeData().getSequence().getName() );
3427             }
3428             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3429                 if ( sb.length() > 0 ) {
3430                     sb.append( " " );
3431                 }
3432                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3433                     sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3434                     sb.append( ":" );
3435                 }
3436                 sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3437             }
3438         }
3439         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
3440             if ( sb.length() > 0 ) {
3441                 sb.append( " " );
3442             }
3443             sb.append( propertiesToString( node ) );
3444         }
3445     }
3446
3447     private final void nodeTaxonomyDataAsSB( final Taxonomy taxonomy, final StringBuilder sb ) {
3448         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3449             sb.append( taxonomy.getTaxonomyCode() );
3450             sb.append( " " );
3451         }
3452         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3453             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3454                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3455                 if ( getOptions().isAbbreviateScientificTaxonNames()
3456                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3457                     abbreviateScientificName( taxonomy.getScientificName(), sb );
3458                 }
3459                 else {
3460                     sb.append( taxonomy.getScientificName() );
3461                 }
3462                 sb.append( " (" );
3463                 sb.append( taxonomy.getCommonName() );
3464                 sb.append( ") " );
3465             }
3466             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3467                 if ( getOptions().isAbbreviateScientificTaxonNames()
3468                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3469                     abbreviateScientificName( taxonomy.getScientificName(), sb );
3470                 }
3471                 else {
3472                     sb.append( taxonomy.getScientificName() );
3473                 }
3474                 sb.append( " " );
3475             }
3476             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3477                 sb.append( taxonomy.getCommonName() );
3478                 sb.append( " " );
3479             }
3480         }
3481         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3482             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3483                 if ( getOptions().isAbbreviateScientificTaxonNames()
3484                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3485                     abbreviateScientificName( taxonomy.getScientificName(), sb );
3486                 }
3487                 else {
3488                     sb.append( taxonomy.getScientificName() );
3489                 }
3490                 sb.append( " " );
3491             }
3492         }
3493         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3494             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3495                 sb.append( taxonomy.getCommonName() );
3496                 sb.append( " " );
3497             }
3498         }
3499     }
3500
3501     private final String obtainTitleForExtDescNodeData() {
3502         switch ( getOptions().getExtDescNodeDataToReturn() ) {
3503             case NODE_NAME:
3504                 return "Node Names";
3505             case GENE_NAME:
3506                 return "Gene Names";
3507             case SEQUENCE_NAME:
3508                 return "Sequence Names";
3509             case SEQUENCE_SYMBOL:
3510                 return "Sequence Symbols";
3511             case SEQUENCE_MOL_SEQ:
3512                 return "Molecular Sequences";
3513             case SEQUENCE_MOL_SEQ_FASTA:
3514                 return "Molecular Sequences (Fasta)";
3515             case SEQUENCE_ACC:
3516                 return "Sequence Accessors";
3517             case TAXONOMY_SCIENTIFIC_NAME:
3518                 return "Scientific Names";
3519             case TAXONOMY_CODE:
3520                 return "Taxonomy Codes";
3521             case TAXONOMY_COMM0N_NAME:
3522                 return "Taxonomy Common Names";
3523             case UNKNOWN:
3524                 return "User Selected Data";
3525             default:
3526                 throw new IllegalArgumentException( "unknown data element: "
3527                         + getOptions().getExtDescNodeDataToReturn() );
3528         }
3529     }
3530
3531     final private void openPdbWeb( final PhylogenyNode node ) {
3532         final List<Accession> pdb_ids = getPdbAccs( node );
3533         if ( ForesterUtil.isEmpty( pdb_ids ) ) {
3534             cannotOpenBrowserWarningMessage( "PDB" );
3535             return;
3536         }
3537         final List<String> uri_strs = TreePanelUtil.createUrisForPdbWeb( node, pdb_ids, getConfiguration(), this );
3538         if ( !ForesterUtil.isEmpty( uri_strs ) ) {
3539             for( final String uri_str : uri_strs ) {
3540                 try {
3541                     AptxUtil.launchWebBrowser( new URI( uri_str ),
3542                                                isApplet(),
3543                                                isApplet() ? obtainApplet() : null,
3544                                                "_aptx_seq" );
3545                 }
3546                 catch ( final IOException e ) {
3547                     AptxUtil.showErrorMessage( this, e.toString() );
3548                     e.printStackTrace();
3549                 }
3550                 catch ( final URISyntaxException e ) {
3551                     AptxUtil.showErrorMessage( this, e.toString() );
3552                     e.printStackTrace();
3553                 }
3554             }
3555         }
3556         else {
3557             cannotOpenBrowserWarningMessage( "PDB" );
3558         }
3559     }
3560
3561     final private void openSeqWeb( final PhylogenyNode node ) {
3562         if ( ForesterUtil.isEmpty( isCanOpenSeqWeb( node ) ) ) {
3563             cannotOpenBrowserWarningMessage( "sequence" );
3564             return;
3565         }
3566         final String uri_str = TreePanelUtil.createUriForSeqWeb( node, getConfiguration(), this );
3567         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3568             try {
3569                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3570                                            isApplet(),
3571                                            isApplet() ? obtainApplet() : null,
3572                                            "_aptx_seq" );
3573             }
3574             catch ( final IOException e ) {
3575                 AptxUtil.showErrorMessage( this, e.toString() );
3576                 e.printStackTrace();
3577             }
3578             catch ( final URISyntaxException e ) {
3579                 AptxUtil.showErrorMessage( this, e.toString() );
3580                 e.printStackTrace();
3581             }
3582         }
3583         else {
3584             cannotOpenBrowserWarningMessage( "sequence" );
3585         }
3586     }
3587
3588     final private void openTaxWeb( final PhylogenyNode node ) {
3589         if ( !isCanOpenTaxWeb( node ) ) {
3590             cannotOpenBrowserWarningMessage( "taxonomic" );
3591             return;
3592         }
3593         String uri_str = null;
3594         final Taxonomy tax = node.getNodeData().getTaxonomy();
3595         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3596                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
3597             try {
3598                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
3599             }
3600             catch ( final URISyntaxException e ) {
3601                 AptxUtil.showErrorMessage( this, e.toString() );
3602                 uri_str = null;
3603                 e.printStackTrace();
3604             }
3605         }
3606         else if ( ( tax.getIdentifier() != null )
3607                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
3608                 && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
3609                 && ( tax.getIdentifier().getProvider().equalsIgnoreCase( "ncbi" ) || tax.getIdentifier().getProvider()
3610                         .equalsIgnoreCase( "uniprot" ) ) ) {
3611             try {
3612                 uri_str = "http://www.uniprot.org/taxonomy/"
3613                         + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
3614             }
3615             catch ( final UnsupportedEncodingException e ) {
3616                 AptxUtil.showErrorMessage( this, e.toString() );
3617                 e.printStackTrace();
3618             }
3619         }
3620         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
3621             try {
3622                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3623                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
3624             }
3625             catch ( final UnsupportedEncodingException e ) {
3626                 AptxUtil.showErrorMessage( this, e.toString() );
3627                 e.printStackTrace();
3628             }
3629         }
3630         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
3631             try {
3632                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3633                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
3634             }
3635             catch ( final UnsupportedEncodingException e ) {
3636                 AptxUtil.showErrorMessage( this, e.toString() );
3637                 e.printStackTrace();
3638             }
3639         }
3640         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
3641             try {
3642                 uri_str = "http://www.uniprot.org/taxonomy/?query="
3643                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
3644             }
3645             catch ( final UnsupportedEncodingException e ) {
3646                 AptxUtil.showErrorMessage( this, e.toString() );
3647                 e.printStackTrace();
3648             }
3649         }
3650         if ( !ForesterUtil.isEmpty( uri_str ) ) {
3651             try {
3652                 AptxUtil.launchWebBrowser( new URI( uri_str ),
3653                                            isApplet(),
3654                                            isApplet() ? obtainApplet() : null,
3655                                            "_aptx_tax" );
3656             }
3657             catch ( final IOException e ) {
3658                 AptxUtil.showErrorMessage( this, e.toString() );
3659                 e.printStackTrace();
3660             }
3661             catch ( final URISyntaxException e ) {
3662                 AptxUtil.showErrorMessage( this, e.toString() );
3663                 e.printStackTrace();
3664             }
3665         }
3666         else {
3667             cannotOpenBrowserWarningMessage( "taxonomic" );
3668         }
3669     }
3670
3671     final private void paintBranchLength( final Graphics2D g,
3672                                           final PhylogenyNode node,
3673                                           final boolean to_pdf,
3674                                           final boolean to_graphics_file ) {
3675         g.setFont( getTreeFontSet().getSmallFont() );
3676         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3677             g.setColor( Color.BLACK );
3678         }
3679         else {
3680             g.setColor( getTreeColorSet().getBranchLengthColor() );
3681         }
3682         if ( !node.isRoot() ) {
3683             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3684                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3685                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3686             }
3687             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3688                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3689                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3690             }
3691             else {
3692                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
3693                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet().getSmallMaxDescent(), g );
3694             }
3695         }
3696         else {
3697             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
3698                     - getTreeFontSet().getSmallMaxDescent(), g );
3699         }
3700     }
3701
3702     final private void paintBranchLite( final Graphics2D g,
3703                                         final float x1,
3704                                         final float x2,
3705                                         final float y1,
3706                                         final float y2,
3707                                         final PhylogenyNode node ) {
3708         g.setColor( getTreeColorSet().getOvColor() );
3709         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3710             drawLine( x1, y1, x2, y2, g );
3711         }
3712         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3713             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3714             ( g ).draw( _quad_curve );
3715         }
3716         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3717             final float dx = x2 - x1;
3718             final float dy = y2 - y1;
3719             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3720                     + ( dy * 0.8f ), x2, y2 );
3721             ( g ).draw( _cubic_curve );
3722         }
3723         else {
3724             final float x2a = x2;
3725             final float x1a = x1;
3726             // draw the vertical line
3727             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
3728                 drawLine( x1, y1, x1, y2, g );
3729             }
3730             // draw the horizontal line
3731             drawLine( x1a, y2, x2a, y2, g );
3732         }
3733     }
3734
3735     /**
3736      * Paint a branch which consists of a vertical and a horizontal bar
3737      * @param is_ind_found_nodes 
3738      */
3739     final private void paintBranchRectangular( final Graphics2D g,
3740                                                final float x1,
3741                                                final float x2,
3742                                                final float y1,
3743                                                final float y2,
3744                                                final PhylogenyNode node,
3745                                                final boolean to_pdf,
3746                                                final boolean to_graphics_file ) {
3747         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
3748         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
3749             drawLine( x1, y1, x2, y2, g );
3750         }
3751         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
3752             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
3753             g.draw( _quad_curve );
3754         }
3755         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
3756             final float dx = x2 - x1;
3757             final float dy = y2 - y1;
3758             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
3759                     + ( dy * 0.8f ), x2, y2 );
3760             g.draw( _cubic_curve );
3761         }
3762         else {
3763             final float x2a = x2;
3764             final float x1a = x1;
3765             float y2_r = 0;
3766             if ( node.isFirstChildNode() || node.isLastChildNode()
3767                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3768                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3769                 if ( !to_graphics_file
3770                         && !to_pdf
3771                         && ( ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) && ( y1 < ( getVisibleRect().getMinY() - 20 ) ) ) || ( ( y2 > ( getVisibleRect()
3772                                 .getMaxY() + 20 ) ) && ( y1 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) ) {
3773                     // Do nothing.
3774                 }
3775                 else {
3776                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3777                         float x2c = x1 + EURO_D;
3778                         if ( x2c > x2a ) {
3779                             x2c = x2a;
3780                         }
3781                         drawLine( x1, y1, x2c, y2, g );
3782                     }
3783                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3784                         if ( y2 > y1 ) {
3785                             y2_r = y2 - ROUNDED_D;
3786                             if ( y2_r < y1 ) {
3787                                 y2_r = y1;
3788                             }
3789                             drawLine( x1, y1, x1, y2_r, g );
3790                         }
3791                         else {
3792                             y2_r = y2 + ROUNDED_D;
3793                             if ( y2_r > y1 ) {
3794                                 y2_r = y1;
3795                             }
3796                             drawLine( x1, y1, x1, y2_r, g );
3797                         }
3798                     }
3799                     else {
3800                         drawLine( x1, y1, x1, y2, g );
3801                     }
3802                 }
3803             }
3804             // draw the horizontal line
3805             if ( !to_graphics_file && !to_pdf
3806                     && ( ( y2 < ( getVisibleRect().getMinY() - 20 ) ) || ( y2 > ( getVisibleRect().getMaxY() + 20 ) ) ) ) {
3807                 return;
3808             }
3809             float x1_r = 0;
3810             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
3811                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3812                     x1_r = x1a + ROUNDED_D;
3813                     if ( x1_r < x2a ) {
3814                         drawLine( x1_r, y2, x2a, y2, g );
3815                     }
3816                 }
3817                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3818                     final float x1c = x1a + EURO_D;
3819                     if ( x1c < x2a ) {
3820                         drawLine( x1c, y2, x2a, y2, g );
3821                     }
3822                 }
3823                 else {
3824                     drawLine( x1a, y2, x2a, y2, g );
3825                 }
3826             }
3827             else {
3828                 final double w = PhylogenyMethods.getBranchWidthValue( node );
3829                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
3830                     x1_r = x1a + ROUNDED_D;
3831                     if ( x1_r < x2a ) {
3832                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
3833                     }
3834                 }
3835                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
3836                     final float x1c = x1a + EURO_D;
3837                     if ( x1c < x2a ) {
3838                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
3839                     }
3840                 }
3841                 else {
3842                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
3843                 }
3844             }
3845             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3846                 if ( x1_r > x2a ) {
3847                     x1_r = x2a;
3848                 }
3849                 if ( y2 > y2_r ) {
3850                     final double diff = y2 - y2_r;
3851                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
3852                 }
3853                 else {
3854                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
3855                 }
3856                 g.draw( _arc );
3857             }
3858         }
3859         if ( node.isExternal() ) {
3860             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file );
3861         }
3862     }
3863
3864     final private double paintCirculars( final PhylogenyNode n,
3865                                          final Phylogeny phy,
3866                                          final float center_x,
3867                                          final float center_y,
3868                                          final double radius,
3869                                          final boolean radial_labels,
3870                                          final Graphics2D g,
3871                                          final boolean to_pdf,
3872                                          final boolean to_graphics_file ) {
3873         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
3874             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
3875                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
3876             }
3877             return _urt_nodeid_angle_map.get( n.getId() );
3878         }
3879         else {
3880             final List<PhylogenyNode> descs = n.getDescendants();
3881             double sum = 0;
3882             for( final PhylogenyNode desc : descs ) {
3883                 sum += paintCirculars( desc,
3884                                        phy,
3885                                        center_x,
3886                                        center_y,
3887                                        radius,
3888                                        radial_labels,
3889                                        g,
3890                                        to_pdf,
3891                                        to_graphics_file );
3892             }
3893             double r = 0;
3894             if ( !n.isRoot() ) {
3895                 r = 1 - ( ( ( double ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3896             }
3897             final double theta = sum / descs.size();
3898             n.setXcoord( ( float ) ( center_x + ( r * radius * Math.cos( theta ) ) ) );
3899             n.setYcoord( ( float ) ( center_y + ( r * radius * Math.sin( theta ) ) ) );
3900             _urt_nodeid_angle_map.put( n.getId(), theta );
3901             for( final PhylogenyNode desc : descs ) {
3902                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
3903             }
3904             return theta;
3905         }
3906     }
3907
3908     final private void paintCircularsLite( final PhylogenyNode n,
3909                                            final Phylogeny phy,
3910                                            final int center_x,
3911                                            final int center_y,
3912                                            final int radius,
3913                                            final Graphics2D g ) {
3914         if ( n.isExternal() ) {
3915             return;
3916         }
3917         else {
3918             final List<PhylogenyNode> descs = n.getDescendants();
3919             for( final PhylogenyNode desc : descs ) {
3920                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
3921             }
3922             float r = 0;
3923             if ( !n.isRoot() ) {
3924                 r = 1 - ( ( ( float ) _circ_max_depth - n.calculateDepth() ) / _circ_max_depth );
3925             }
3926             final double theta = _urt_nodeid_angle_map.get( n.getId() );
3927             n.setXSecondary( ( float ) ( center_x + ( radius * r * Math.cos( theta ) ) ) );
3928             n.setYSecondary( ( float ) ( center_y + ( radius * r * Math.sin( theta ) ) ) );
3929             for( final PhylogenyNode desc : descs ) {
3930                 paintBranchCircularLite( n, desc, g );
3931             }
3932         }
3933     }
3934
3935     final private void paintCollapsedNode( final Graphics2D g,
3936                                            final PhylogenyNode node,
3937                                            final boolean to_graphics_file,
3938                                            final boolean to_pdf,
3939                                            final boolean is_in_found_nodes ) {
3940         Color c = null;
3941         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3942             c = Color.BLACK;
3943         }
3944         else if ( is_in_found_nodes ) {
3945             c = getColorForFoundNode( node );
3946         }
3947         else if ( getControlPanel().isColorAccordingToSequence() ) {
3948             c = getSequenceBasedColor( node );
3949         }
3950         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3951             c = getTaxonomyBasedColor( node );
3952         }
3953         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
3954                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3955             c = PhylogenyMethods.getBranchColorValue( node );
3956         }
3957         else {
3958             c = getTreeColorSet().getCollapseFillColor();
3959         }
3960         double d = node.getAllExternalDescendants().size();
3961         if ( d > 1000 ) {
3962             d = ( 3 * _y_distance ) / 3;
3963         }
3964         else {
3965             d = ( Math.log10( d ) * _y_distance ) / 2.5;
3966         }
3967         final int box_size = getOptions().getDefaultNodeShapeSize() + 1;
3968         if ( d < box_size ) {
3969             d = box_size;
3970         }
3971         final float xx = node.getXcoord() - ( 2 * box_size );
3972         final float xxx = xx > node.getParent().getXcoord() + 1 ? xx : node.getParent().getXcoord() + 1;
3973         _polygon.reset();
3974         _polygon.moveTo( xxx, node.getYcoord() );
3975         _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() - d );
3976         _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() + d );
3977         _polygon.closePath();
3978         if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.SOLID ) {
3979             g.setColor( c );
3980             g.fill( _polygon );
3981         }
3982         else if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.NONE ) {
3983             g.setColor( getBackground() );
3984             g.fill( _polygon );
3985             g.setColor( c );
3986             g.draw( _polygon );
3987         }
3988         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
3989             g.setPaint( new GradientPaint( xxx, node.getYcoord(), getBackground(), node.getXcoord(), ( float ) ( node
3990                     .getYcoord() - d ), c, false ) );
3991             g.fill( _polygon );
3992             g.setPaint( c );
3993             g.draw( _polygon );
3994         }
3995         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3996     }
3997
3998     final private void paintConfidenceValues( final Graphics2D g,
3999                                               final PhylogenyNode node,
4000                                               final boolean to_pdf,
4001                                               final boolean to_graphics_file ) {
4002         final List<Confidence> confidences = node.getBranchData().getConfidences();
4003         boolean not_first = false;
4004         Collections.sort( confidences );
4005         final StringBuilder sb = new StringBuilder();
4006         for( final Confidence confidence : confidences ) {
4007             final double value = confidence.getValue();
4008             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4009                 if ( value < getOptions().getMinConfidenceValue() ) {
4010                     return;
4011                 }
4012                 if ( not_first ) {
4013                     sb.append( "/" );
4014                 }
4015                 else {
4016                     not_first = true;
4017                 }
4018                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
4019                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4020                 if ( getOptions().isShowConfidenceStddev() ) {
4021                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4022                         sb.append( "(" );
4023                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
4024                                                                                     getOptions()
4025                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4026                         sb.append( ")" );
4027                     }
4028                 }
4029             }
4030         }
4031         if ( sb.length() > 0 ) {
4032             final float parent_x = node.getParent().getXcoord();
4033             float x = node.getXcoord();
4034             g.setFont( getTreeFontSet().getSmallFont() );
4035             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
4036                 x += EURO_D;
4037             }
4038             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
4039                 x += ROUNDED_D;
4040             }
4041             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4042                 g.setColor( Color.BLACK );
4043             }
4044             else {
4045                 g.setColor( getTreeColorSet().getConfidenceColor() );
4046             }
4047             final String conf_str = sb.toString();
4048             TreePanel.drawString( conf_str,
4049                                   parent_x
4050                                           + ( ( x - parent_x - getTreeFontSet().getFontMetricsSmall()
4051                                                   .stringWidth( conf_str ) ) / 2 ),
4052                                   ( node.getYcoord() + getTreeFontSet().getSmallMaxAscent() ) - 1,
4053                                   g );
4054         }
4055     }
4056
4057     final private void paintGainedAndLostCharacters( final Graphics2D g,
4058                                                      final PhylogenyNode node,
4059                                                      final String gained,
4060                                                      final String lost ) {
4061         if ( node.getParent() != null ) {
4062             final float parent_x = node.getParent().getXcoord();
4063             final float x = node.getXcoord();
4064             g.setFont( getTreeFontSet().getLargeFont() );
4065             g.setColor( getTreeColorSet().getGainedCharactersColor() );
4066             if ( Constants.SPECIAL_CUSTOM ) {
4067                 g.setColor( Color.BLUE );
4068             }
4069             TreePanel
4070                     .drawString( gained,
4071                                  parent_x
4072                                          + ( ( x - parent_x - getFontMetricsForLargeDefaultFont().stringWidth( gained ) ) / 2 ),
4073                                  ( node.getYcoord() - getFontMetricsForLargeDefaultFont().getMaxDescent() ),
4074                                  g );
4075             g.setColor( getTreeColorSet().getLostCharactersColor() );
4076             TreePanel
4077                     .drawString( lost,
4078                                  parent_x
4079                                          + ( ( x - parent_x - getFontMetricsForLargeDefaultFont().stringWidth( lost ) ) / 2 ),
4080                                  ( node.getYcoord() + getFontMetricsForLargeDefaultFont().getMaxAscent() ),
4081                                  g );
4082         }
4083     }
4084
4085     /**
4086      * Draw a box at the indicated node.
4087      * 
4088      * @param x
4089      * @param y
4090      * @param node
4091      * @param g
4092      */
4093     final private void paintNodeBox( final float x,
4094                                      final float y,
4095                                      final PhylogenyNode node,
4096                                      final Graphics2D g,
4097                                      final boolean to_pdf,
4098                                      final boolean to_graphics_file ) {
4099         if ( node.isCollapse() ) {
4100             return;
4101         }
4102         // if this node should be highlighted, do so
4103         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
4104             g.setColor( getTreeColorSet().getFoundColor0() );
4105             drawOval( x - 8, y - 8, 16, 16, g );
4106             drawOval( x - 9, y - 8, 17, 17, g );
4107             drawOval( x - 9, y - 9, 18, 18, g );
4108         }
4109         if ( ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) )
4110                 || ( getOptions().isShowDefaultNodeShapesExternal() && node.isExternal() )
4111                 || ( getOptions().isShowDefaultNodeShapesInternal() && node.isInternal() )
4112                 || ( getControlPanel().isUseVisualStyles() && ( ( node.getNodeData().getNodeVisualData() != null ) && ( ( node
4113                         .getNodeData().getNodeVisualData().getNodeColor() != null )
4114                         || ( node.getNodeData().getNodeVisualData().getSize() != NodeVisualData.DEFAULT_SIZE )
4115                         || ( node.getNodeData().getNodeVisualData().getFillType() != NodeFill.DEFAULT ) || ( node
4116                         .getNodeData().getNodeVisualData().getShape() != NodeShape.DEFAULT ) ) ) )
4117                 || ( getControlPanel().isEvents() && node.isHasAssignedEvent() && ( node.getNodeData().getEvent()
4118                         .isDuplication()
4119                         || node.getNodeData().getEvent().isSpeciation() || node.getNodeData().getEvent()
4120                         .isSpeciationOrDuplication() ) ) ) {
4121             NodeVisualData vis = null;
4122             if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
4123                     && ( !node.getNodeData().getNodeVisualData().isEmpty() ) ) {
4124                 vis = node.getNodeData().getNodeVisualData();
4125             }
4126             float box_size = getOptions().getDefaultNodeShapeSize();
4127             if ( ( vis != null ) && ( vis.getSize() != NodeVisualData.DEFAULT_SIZE ) ) {
4128                 box_size = vis.getSize();
4129             }
4130             final float half_box_size = box_size / 2.0f;
4131             Color outline_color = null;
4132             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4133                 outline_color = Color.BLACK;
4134             }
4135             else if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4136                 outline_color = getColorForFoundNode( node );
4137             }
4138             else if ( vis != null ) {
4139                 if ( vis.getNodeColor() != null ) {
4140                     outline_color = vis.getNodeColor();
4141                 }
4142                 else if ( vis.getFontColor() != null ) {
4143                     outline_color = vis.getFontColor();
4144                 }
4145             }
4146             else if ( getControlPanel().isEvents() && TreePanelUtil.isHasAssignedEvent( node ) ) {
4147                 final Event event = node.getNodeData().getEvent();
4148                 if ( event.isDuplication() ) {
4149                     outline_color = getTreeColorSet().getDuplicationBoxColor();
4150                 }
4151                 else if ( event.isSpeciation() ) {
4152                     outline_color = getTreeColorSet().getSpecBoxColor();
4153                 }
4154                 else if ( event.isSpeciationOrDuplication() ) {
4155                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
4156                 }
4157             }
4158             if ( outline_color == null ) {
4159                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
4160                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
4161                     outline_color = getTreeColorSet().getBranchColorForPdf();
4162                 }
4163             }
4164             NodeShape shape = null;
4165             if ( vis != null ) {
4166                 if ( vis.getShape() == NodeShape.CIRCLE ) {
4167                     shape = NodeShape.CIRCLE;
4168                 }
4169                 else if ( vis.getShape() == NodeShape.RECTANGLE ) {
4170                     shape = NodeShape.RECTANGLE;
4171                 }
4172             }
4173             if ( shape == null ) {
4174                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
4175                     shape = NodeShape.CIRCLE;
4176                 }
4177                 else if ( getOptions().getDefaultNodeShape() == NodeShape.RECTANGLE ) {
4178                     shape = NodeShape.RECTANGLE;
4179                 }
4180             }
4181             NodeFill fill = null;
4182             if ( vis != null ) {
4183                 if ( vis.getFillType() == NodeFill.SOLID ) {
4184                     fill = NodeFill.SOLID;
4185                 }
4186                 else if ( vis.getFillType() == NodeFill.NONE ) {
4187                     fill = NodeFill.NONE;
4188                 }
4189                 else if ( vis.getFillType() == NodeFill.GRADIENT ) {
4190                     fill = NodeFill.GRADIENT;
4191                 }
4192             }
4193             if ( fill == null ) {
4194                 if ( getOptions().getDefaultNodeFill() == NodeFill.SOLID ) {
4195                     fill = NodeFill.SOLID;
4196                 }
4197                 else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
4198                     fill = NodeFill.NONE;
4199                 }
4200                 else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
4201                     fill = NodeFill.GRADIENT;
4202                 }
4203             }
4204             Color vis_fill_color = null;
4205             if ( ( vis != null ) && ( vis.getNodeColor() != null ) ) {
4206                 vis_fill_color = vis.getNodeColor();
4207             }
4208             if ( shape == NodeShape.CIRCLE ) {
4209                 if ( fill == NodeFill.GRADIENT ) {
4210                     drawOvalGradient( x - half_box_size, y - half_box_size, box_size, box_size, g, to_pdf ? Color.WHITE
4211                             : outline_color, to_pdf ? outline_color : getBackground(), outline_color );
4212                 }
4213                 else if ( fill == NodeFill.NONE ) {
4214                     Color background = getBackground();
4215                     if ( to_pdf ) {
4216                         background = Color.WHITE;
4217                     }
4218                     drawOvalGradient( x - half_box_size,
4219                                       y - half_box_size,
4220                                       box_size,
4221                                       box_size,
4222                                       g,
4223                                       background,
4224                                       background,
4225                                       outline_color );
4226                 }
4227                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
4228                     if ( vis_fill_color != null ) {
4229                         g.setColor( vis_fill_color );
4230                     }
4231                     else {
4232                         g.setColor( outline_color );
4233                     }
4234                     drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4235                 }
4236             }
4237             else if ( shape == NodeVisualData.NodeShape.RECTANGLE ) {
4238                 if ( fill == NodeVisualData.NodeFill.GRADIENT ) {
4239                     drawRectGradient( x - half_box_size, y - half_box_size, box_size, box_size, g, to_pdf ? Color.WHITE
4240                             : outline_color, to_pdf ? outline_color : getBackground(), outline_color );
4241                 }
4242                 else if ( fill == NodeVisualData.NodeFill.NONE ) {
4243                     Color background = getBackground();
4244                     if ( to_pdf ) {
4245                         background = Color.WHITE;
4246                     }
4247                     drawRectGradient( x - half_box_size,
4248                                       y - half_box_size,
4249                                       box_size,
4250                                       box_size,
4251                                       g,
4252                                       background,
4253                                       background,
4254                                       outline_color );
4255                 }
4256                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
4257                     if ( vis_fill_color != null ) {
4258                         g.setColor( vis_fill_color );
4259                     }
4260                     else {
4261                         g.setColor( outline_color );
4262                     }
4263                     drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
4264                 }
4265             }
4266         }
4267     }
4268
4269     final private int paintNodeData( final Graphics2D g,
4270                                      final PhylogenyNode node,
4271                                      final boolean to_graphics_file,
4272                                      final boolean to_pdf,
4273                                      final boolean is_in_found_nodes ) {
4274         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
4275             return 0;
4276         }
4277         if ( getOptions().isShowBranchLengthValues()
4278                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4279                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4280                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
4281             paintBranchLength( g, node, to_pdf, to_graphics_file );
4282         }
4283         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
4284             return 0;
4285         }
4286         _sb.setLength( 0 );
4287         int x = 0;
4288         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
4289         if ( getControlPanel().isShowTaxonomyImages()
4290                 && ( getImageMap() != null )
4291                 && !getImageMap().isEmpty()
4292                 && node.getNodeData().isHasTaxonomy()
4293                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
4294                         .getUris().isEmpty() ) ) {
4295             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
4296         }
4297         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4298                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
4299             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
4300         }
4301         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
4302         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4303             if ( _sb.length() > 0 ) {
4304                 _sb.setLength( 0 );
4305                 _sb.append( " (" );
4306                 _sb.append( node.getAllExternalDescendants().size() );
4307                 _sb.append( ")" );
4308             }
4309         }
4310         else {
4311             _sb.setLength( 0 );
4312         }
4313         nodeDataAsSB( node, _sb );
4314         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4315         float down_shift_factor = 3.0f;
4316         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
4317             down_shift_factor = 1;
4318         }
4319         final float pos_x = node.getXcoord() + x + 2 + half_box_size;
4320         float pos_y;
4321         if ( !using_visual_font ) {
4322             pos_y = ( node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ) );
4323         }
4324         else {
4325             pos_y = ( node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / down_shift_factor ) );
4326         }
4327         final String sb_str = _sb.toString();
4328         // GUILHEM_BEG ______________
4329         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
4330                 && ( _query_sequence != null ) ) {
4331             int nodeTextBoundsWidth = 0;
4332             if ( sb_str.length() > 0 ) {
4333                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
4334                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
4335             }
4336             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
4337                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
4338                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
4339                     g.setColor( getTreeColorSet().getBackgroundColor() );
4340                 }
4341             }
4342             else {
4343                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
4344                 for( final SequenceRelation seqRelation : seqRelations ) {
4345                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
4346                             .getRef1().isEqual( _query_sequence ) )
4347                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
4348                                     .getSelectedItem() );
4349                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
4350                         final double linePosX = node.getXcoord() + 2 + half_box_size;
4351                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
4352                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
4353                                 + ")";
4354                         if ( sConfidence != null ) {
4355                             float confidenceX = pos_x;
4356                             if ( sb_str.length() > 0 ) {
4357                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
4358                                         + CONFIDENCE_LEFT_MARGIN;
4359                             }
4360                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
4361                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
4362                                         .getBounds().getWidth();
4363                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
4364                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
4365                             }
4366                         }
4367                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
4368                         {
4369                             if ( nodeTextBoundsWidth == 0 ) {
4370                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
4371                             }
4372                             else {
4373                                 nodeTextBoundsWidth += 2;
4374                             }
4375                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
4376                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
4377                             break;
4378                         }
4379                     }
4380                 }
4381             }
4382         }
4383         if ( sb_str.length() > 0 ) {
4384             TreePanel.drawString( sb_str, pos_x, pos_y, g );
4385         }
4386         // GUILHEM_END _____________
4387         if ( _sb.length() > 0 ) {
4388             if ( !using_visual_font && !is_in_found_nodes ) {
4389                 x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
4390             }
4391             else {
4392                 x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
4393             }
4394         }
4395         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
4396                 && ( node.getNodeData().getSequence().getAnnotations() != null )
4397                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
4398             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
4399             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4400                 g.setColor( Color.BLACK );
4401             }
4402             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
4403                 g.setColor( calculateColorForAnnotation( ann ) );
4404             }
4405             final String ann_str = TreePanelUtil.createAnnotationString( ann, getOptions().isShowAnnotationRefSource() );
4406             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
4407                     + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ), g );
4408             _sb.setLength( 0 );
4409             _sb.append( ann_str );
4410             if ( _sb.length() > 0 ) {
4411                 if ( !using_visual_font && !is_in_found_nodes ) {
4412                     x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
4413                 }
4414                 else {
4415                     x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
4416                 }
4417             }
4418         }
4419         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4420                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
4421                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
4422             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
4423                     && node.getNodeData().isHasBinaryCharacters() ) {
4424                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4425                     g.setColor( Color.BLACK );
4426                 }
4427                 else {
4428                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
4429                 }
4430                 if ( getControlPanel().isShowBinaryCharacters() ) {
4431                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
4432                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
4433                             + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ), g );
4434                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
4435                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
4436                             .getLostCharactersAsStringBuffer().toString() );
4437                 }
4438                 else {
4439                     TreePanel
4440                             .drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
4441                                          node.getXcoord() + x + 4 + half_box_size,
4442                                          node.getYcoord()
4443                                                  + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ),
4444                                          g );
4445                     paintGainedAndLostCharacters( g, node, "+"
4446                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
4447                             + node.getNodeData().getBinaryCharacters().getLostCount() );
4448                 }
4449             }
4450         }
4451         return x;
4452     }
4453
4454     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
4455                                                   final PhylogenyNode node,
4456                                                   final boolean to_pdf,
4457                                                   final boolean to_graphics_file,
4458                                                   final boolean radial_labels,
4459                                                   final double ur_angle,
4460                                                   final boolean is_in_found_nodes ) {
4461         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
4462             return;
4463         }
4464         _sb.setLength( 0 );
4465         _sb.append( " " );
4466         if ( node.getNodeData().isHasTaxonomy()
4467                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
4468                         .isShowTaxonomyCommonNames() ) ) {
4469             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4470             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
4471                 _sb.append( taxonomy.getTaxonomyCode() );
4472                 _sb.append( " " );
4473             }
4474             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
4475                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
4476                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4477                     _sb.append( taxonomy.getScientificName() );
4478                     _sb.append( " (" );
4479                     _sb.append( taxonomy.getCommonName() );
4480                     _sb.append( ") " );
4481                 }
4482                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4483                     _sb.append( taxonomy.getScientificName() );
4484                     _sb.append( " " );
4485                 }
4486                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4487                     _sb.append( taxonomy.getCommonName() );
4488                     _sb.append( " " );
4489                 }
4490             }
4491             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4492                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4493                     _sb.append( taxonomy.getScientificName() );
4494                     _sb.append( " " );
4495                 }
4496             }
4497             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4498                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4499                     _sb.append( taxonomy.getCommonName() );
4500                     _sb.append( " " );
4501                 }
4502             }
4503         }
4504         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
4505             _sb.append( " [" );
4506             _sb.append( node.getAllExternalDescendants().size() );
4507             _sb.append( "]" );
4508         }
4509         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
4510             if ( _sb.length() > 0 ) {
4511                 _sb.append( " " );
4512             }
4513             _sb.append( node.getName() );
4514         }
4515         if ( node.getNodeData().isHasSequence() ) {
4516             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
4517                 if ( _sb.length() > 0 ) {
4518                     _sb.append( " " );
4519                 }
4520                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
4521                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
4522                     _sb.append( ":" );
4523                 }
4524                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
4525             }
4526             if ( getControlPanel().isShowSeqNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
4527                 if ( _sb.length() > 0 ) {
4528                     _sb.append( " " );
4529                 }
4530                 _sb.append( node.getNodeData().getSequence().getName() );
4531             }
4532         }
4533         //g.setFont( getTreeFontSet().getLargeFont() );
4534         //if ( is_in_found_nodes ) {
4535         //    g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
4536         // }
4537         if ( _sb.length() > 1 ) {
4538             setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
4539             final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4540             final String sb_str = _sb.toString();
4541             double m = 0;
4542             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4543                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
4544             }
4545             else {
4546                 m = ( float ) ( ur_angle % TWO_PI );
4547             }
4548             _at = g.getTransform();
4549             boolean need_to_reset = false;
4550             final float x_coord = node.getXcoord();
4551             float y_coord;
4552             if ( !using_visual_font ) {
4553                 y_coord = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / 3.0f );
4554             }
4555             else {
4556                 y_coord = node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / 3.0f );
4557             }
4558             if ( radial_labels ) {
4559                 need_to_reset = true;
4560                 boolean left = false;
4561                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4562                     m -= PI;
4563                     left = true;
4564                 }
4565                 g.rotate( m, x_coord, node.getYcoord() );
4566                 if ( left ) {
4567                     if ( !using_visual_font ) {
4568                         g.translate( -( getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth() ),
4569                                      0 );
4570                     }
4571                     else {
4572                         g.translate( -( getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth() ), 0 );
4573                     }
4574                 }
4575             }
4576             else {
4577                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
4578                     need_to_reset = true;
4579                     if ( !using_visual_font ) {
4580                         g.translate( -getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth(), 0 );
4581                     }
4582                     else {
4583                         g.translate( -getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth(), 0 );
4584                     }
4585                 }
4586             }
4587             TreePanel.drawString( sb_str, x_coord, y_coord, g );
4588             if ( need_to_reset ) {
4589                 g.setTransform( _at );
4590             }
4591         }
4592     }
4593
4594     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
4595         if ( node.isCollapse() ) {
4596             if ( !node.isRoot() && !node.getParent().isCollapse() ) {
4597                 paintCollapsedNode( g, node, false, false, false );
4598             }
4599             return;
4600         }
4601         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
4602             g.setColor( getColorForFoundNode( node ) );
4603             drawRectFilled( node.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, node.getYSecondary()
4604                     - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, OVERVIEW_FOUND_NODE_BOX_SIZE, OVERVIEW_FOUND_NODE_BOX_SIZE, g );
4605         }
4606         float new_x = 0;
4607         if ( !node.isExternal() && !node.isCollapse() ) {
4608             boolean first_child = true;
4609             float y2 = 0.0f;
4610             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4611             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4612                 final PhylogenyNode child_node = node.getChildNode( i );
4613                 int factor_x;
4614                 if ( !isUniformBranchLengthsForCladogram() ) {
4615                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4616                 }
4617                 else {
4618                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4619                 }
4620                 if ( first_child ) {
4621                     first_child = false;
4622                     y2 = node.getYSecondary()
4623                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
4624                                     .getNumberOfExternalNodes() ) );
4625                 }
4626                 else {
4627                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4628                 }
4629                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
4630                 new_x = x2 + node.getXSecondary();
4631                 final float diff_y = node.getYSecondary() - y2;
4632                 final float diff_x = node.getXSecondary() - new_x;
4633                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
4634                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
4635                 }
4636                 child_node.setXSecondary( new_x );
4637                 child_node.setYSecondary( y2 );
4638                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
4639             }
4640         }
4641     }
4642
4643     final private void paintNodeRectangular( final Graphics2D g,
4644                                              final PhylogenyNode node,
4645                                              final boolean to_pdf,
4646                                              final boolean dynamically_hide,
4647                                              final int dynamic_hiding_factor,
4648                                              final boolean to_graphics_file ) {
4649         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
4650         if ( node.isCollapse() ) {
4651             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) ) {
4652                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4653             }
4654             return;
4655         }
4656         if ( node.isExternal() ) {
4657             ++_external_node_index;
4658         }
4659         // Confidence values
4660         if ( getControlPanel().isShowConfidenceValues()
4661                 && !node.isExternal()
4662                 && !node.isRoot()
4663                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
4664                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
4665                 && node.getBranchData().isHasConfidences() ) {
4666             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
4667         }
4668         // Draw a line to root:
4669         if ( node.isRoot() && _phylogeny.isRooted() ) {
4670             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
4671         }
4672         float new_x = 0;
4673         float new_x_min = Float.MAX_VALUE;
4674         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
4675         float min_dist = 1.5f;
4676         if ( !disallow_shortcutting ) {
4677             //   System.out.println( dynamic_hiding_factor );
4678             if ( dynamic_hiding_factor > 4000 ) {
4679                 min_dist = 4;
4680             }
4681             else if ( dynamic_hiding_factor > 1000 ) {
4682                 min_dist = 3;
4683             }
4684             else if ( dynamic_hiding_factor > 100 ) {
4685                 min_dist = 2;
4686             }
4687         }
4688         if ( !node.isExternal() && !node.isCollapse() ) {
4689             boolean first_child = true;
4690             float y2 = 0.0f;
4691             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
4692             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
4693                 final PhylogenyNode child_node = node.getChildNode( i );
4694                 int factor_x;
4695                 if ( !isUniformBranchLengthsForCladogram() ) {
4696                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
4697                 }
4698                 else {
4699                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
4700                 }
4701                 if ( first_child ) {
4702                     first_child = false;
4703                     y2 = node.getYcoord()
4704                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
4705                 }
4706                 else {
4707                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
4708                 }
4709                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
4710                 new_x = x2 + node.getXcoord();
4711                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
4712                     new_x_min = x2;
4713                 }
4714                 final float diff_y = node.getYcoord() - y2;
4715                 final float diff_x = node.getXcoord() - new_x;
4716                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
4717                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
4718                     paintBranchRectangular( g,
4719                                             node.getXcoord(),
4720                                             new_x,
4721                                             node.getYcoord(),
4722                                             y2,
4723                                             child_node,
4724                                             to_pdf,
4725                                             to_graphics_file );
4726                 }
4727                 child_node.setXcoord( new_x );
4728                 child_node.setYcoord( y2 );
4729                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
4730             }
4731             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file );
4732         }
4733         if ( dynamically_hide
4734                 && !is_in_found_nodes
4735                 && ( ( node.isExternal() && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) ) || ( !node
4736                         .isExternal() && ( ( new_x_min < 20 ) || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getFontMetricsForLargeDefaultFont()
4737                         .getHeight() ) ) ) ) ) {
4738             return;
4739         }
4740         final int x = paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
4741         paintNodeWithRenderableData( x, g, node, to_graphics_file, to_pdf );
4742     }
4743
4744     final private void paintNodeWithRenderableData( final int x,
4745                                                     final Graphics2D g,
4746                                                     final PhylogenyNode node,
4747                                                     final boolean to_graphics_file,
4748                                                     final boolean to_pdf ) {
4749         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
4750             return;
4751         }
4752         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
4753             return;
4754         }
4755         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4756                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4757             RenderableDomainArchitecture rds = null;
4758             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
4759                 try {
4760                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
4761                 }
4762                 catch ( final ClassCastException cce ) {
4763                     cce.printStackTrace();
4764                 }
4765                 if ( rds != null ) {
4766                     rds.setRenderingHeight( 6 );
4767                     rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
4768                 }
4769             }
4770         }
4771         //////////////
4772         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4773                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
4774             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
4775                                                                          getStatisticsForExpressionValues(),
4776                                                                          getConfiguration() );
4777             if ( rv != null ) {
4778                 int xx = 0;
4779                 PhylogenyNode my_node = node;
4780                 if ( !getControlPanel().isDrawPhylogram() ) {
4781                     my_node = getPhylogeny().getFirstExternalNode();
4782                 }
4783                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
4784                     xx += getFontMetricsForLargeDefaultFont()
4785                             .stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
4786                 }
4787                 if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
4788                     xx += getFontMetricsForLargeDefaultFont().stringWidth( my_node.getName() + " " );
4789                 }
4790                 rv.render( my_node.getXcoord() + xx, node.getYcoord() - 5, g, this, to_pdf );
4791             }
4792         }
4793         //////////////
4794     }
4795
4796     final private void paintOvRectangle( final Graphics2D g ) {
4797         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
4798         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
4799         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
4800         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
4801         final float width = getOvMaxWidth() / w_ratio;
4802         final float height = getOvMaxHeight() / h_ratio;
4803         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
4804         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
4805         g.setColor( getTreeColorSet().getFoundColor0() );
4806         getOvRectangle().setRect( x, y, width, height );
4807         final Stroke s = g.getStroke();
4808         g.setStroke( STROKE_1 );
4809         if ( ( width < 6 ) && ( height < 6 ) ) {
4810             drawRectFilled( x, y, 6, 6, g );
4811             getOvVirtualRectangle().setRect( x, y, 6, 6 );
4812         }
4813         else if ( width < 6 ) {
4814             drawRectFilled( x, y, 6, height, g );
4815             getOvVirtualRectangle().setRect( x, y, 6, height );
4816         }
4817         else if ( height < 6 ) {
4818             drawRectFilled( x, y, width, 6, g );
4819             getOvVirtualRectangle().setRect( x, y, width, 6 );
4820         }
4821         else {
4822             drawRect( x, y, width, height, g );
4823             if ( isInOvRect() ) {
4824                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
4825             }
4826             getOvVirtualRectangle().setRect( x, y, width, height );
4827         }
4828         g.setStroke( s );
4829     }
4830
4831     final private void paintPhylogenyLite( final Graphics2D g ) {
4832         _phylogeny
4833                 .getRoot()
4834                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
4835                         .getWidth() ) ) ) );
4836         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
4837         final Stroke s = g.getStroke();
4838         g.setStroke( STROKE_05 );
4839         for( final PhylogenyNode element : _nodes_in_preorder ) {
4840             paintNodeLite( g, element );
4841         }
4842         g.setStroke( s );
4843         paintOvRectangle( g );
4844     }
4845
4846     /**
4847      * Paint the root branch. (Differs from others because it will always be a
4848      * single horizontal line).
4849      * @param to_graphics_file 
4850      * 
4851      * @return new x1 value
4852      */
4853     final private void paintRootBranch( final Graphics2D g,
4854                                         final float x1,
4855                                         final float y1,
4856                                         final PhylogenyNode root,
4857                                         final boolean to_pdf,
4858                                         final boolean to_graphics_file ) {
4859         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
4860         float d = getXdistance();
4861         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
4862             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
4863         }
4864         if ( d < MIN_ROOT_LENGTH ) {
4865             d = MIN_ROOT_LENGTH;
4866         }
4867         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
4868             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
4869         }
4870         else {
4871             final double w = PhylogenyMethods.getBranchWidthValue( root );
4872             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
4873         }
4874         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file );
4875     }
4876
4877     final private void paintScale( final Graphics2D g,
4878                                    int x1,
4879                                    int y1,
4880                                    final boolean to_pdf,
4881                                    final boolean to_graphics_file ) {
4882         x1 += MOVE;
4883         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
4884         y1 -= 12;
4885         final int y2 = y1 - 8;
4886         final int y3 = y1 - 4;
4887         g.setFont( getTreeFontSet().getSmallFont() );
4888         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
4889             g.setColor( Color.BLACK );
4890         }
4891         else {
4892             g.setColor( getTreeColorSet().getBranchLengthColor() );
4893         }
4894         final Stroke s = g.getStroke();
4895         g.setStroke( STROKE_1 );
4896         drawLine( x1, y1, x1, y2, g );
4897         drawLine( x2, y1, x2, y2, g );
4898         drawLine( x1, y3, x2, y3, g );
4899         if ( getScaleLabel() != null ) {
4900             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
4901         }
4902         g.setStroke( s );
4903     }
4904
4905     final private int paintTaxonomy( final Graphics2D g,
4906                                      final PhylogenyNode node,
4907                                      final boolean is_in_found_nodes,
4908                                      final boolean to_pdf,
4909                                      final boolean to_graphics_file,
4910                                      final float x_shift ) {
4911         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
4912         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
4913         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getTaxonomyColor() );
4914         final float start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
4915         float start_y;
4916         if ( !using_visual_font ) {
4917             start_y = node.getYcoord()
4918                     + ( getFontMetricsForLargeDefaultFont().getAscent() / ( node.getNumberOfDescendants() == 1 ? 1
4919                             : 3.0f ) );
4920         }
4921         else {
4922             start_y = node.getYcoord()
4923                     + ( getFontMetrics( g.getFont() ).getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
4924         }
4925         _sb.setLength( 0 );
4926         nodeTaxonomyDataAsSB( taxonomy, _sb );
4927         final String label = _sb.toString();
4928         /* GUILHEM_BEG */
4929         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
4930                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
4931             // invert font color and background color to show that this is the query sequence
4932             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
4933                                                                                                           false,
4934                                                                                                           false ) )
4935                     .getBounds();
4936             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
4937             g.setColor( getTreeColorSet().getBackgroundColor() );
4938         }
4939         /* GUILHEM_END */
4940         TreePanel.drawString( label, start_x, start_y, g );
4941         if ( !using_visual_font && !is_in_found_nodes ) {
4942             return getFontMetricsForLargeDefaultFont().stringWidth( label );
4943         }
4944         return getFontMetrics( g.getFont() ).stringWidth( label );
4945     }
4946
4947     final private void paintUnrooted( final PhylogenyNode n,
4948                                       final double low_angle,
4949                                       final double high_angle,
4950                                       final boolean radial_labels,
4951                                       final Graphics2D g,
4952                                       final boolean to_pdf,
4953                                       final boolean to_graphics_file ) {
4954         if ( n.isRoot() ) {
4955             n.setXcoord( getWidth() / 2 );
4956             n.setYcoord( getHeight() / 2 );
4957         }
4958         if ( n.isExternal() ) {
4959             paintNodeDataUnrootedCirc( g,
4960                                        n,
4961                                        to_pdf,
4962                                        to_graphics_file,
4963                                        radial_labels,
4964                                        ( high_angle + low_angle ) / 2,
4965                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
4966             return;
4967         }
4968         final float num_enclosed = n.getNumberOfExternalNodes();
4969         final float x = n.getXcoord();
4970         final float y = n.getYcoord();
4971         double current_angle = low_angle;
4972         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4973         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4974         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4975         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4976         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4977             final PhylogenyNode desc = n.getChildNode( i );
4978             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4979             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4980             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4981             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4982             //     continue;
4983             // }
4984             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4985             //    continue;
4986             //}
4987             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4988             //    continue;
4989             // }
4990             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4991             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4992             float length;
4993             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4994                 if ( desc.getDistanceToParent() < 0 ) {
4995                     length = 0;
4996                 }
4997                 else {
4998                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4999                 }
5000             }
5001             else {
5002                 length = getUrtFactor();
5003             }
5004             final double mid_angle = current_angle + ( arc_size / 2 );
5005             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5006             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5007             desc.setXcoord( new_x );
5008             desc.setYcoord( new_y );
5009             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
5010             current_angle += arc_size;
5011             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
5012             drawLine( x, y, new_x, new_y, g );
5013             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file );
5014         }
5015         if ( n.isRoot() ) {
5016             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file );
5017         }
5018     }
5019
5020     final private void paintUnrootedLite( final PhylogenyNode n,
5021                                           final double low_angle,
5022                                           final double high_angle,
5023                                           final Graphics2D g,
5024                                           final float urt_ov_factor ) {
5025         if ( n.isRoot() ) {
5026             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
5027             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
5028             n.setXSecondary( x_pos );
5029             n.setYSecondary( y_pos );
5030         }
5031         if ( n.isExternal() ) {
5032             return;
5033         }
5034         final float num_enclosed = n.getNumberOfExternalNodes();
5035         final float x = n.getXSecondary();
5036         final float y = n.getYSecondary();
5037         double current_angle = low_angle;
5038         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
5039             final PhylogenyNode desc = n.getChildNode( i );
5040             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
5041             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
5042             float length;
5043             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5044                 if ( desc.getDistanceToParent() < 0 ) {
5045                     length = 0;
5046                 }
5047                 else {
5048                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
5049                 }
5050             }
5051             else {
5052                 length = urt_ov_factor;
5053             }
5054             final double mid_angle = current_angle + ( arc_size / 2 );
5055             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
5056             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
5057             desc.setXSecondary( new_x );
5058             desc.setYSecondary( new_y );
5059             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
5060                 g.setColor( getColorForFoundNode( desc ) );
5061                 drawRectFilled( desc.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5062                                 desc.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5063                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5064                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
5065                                 g );
5066                 g.setColor( getTreeColorSet().getOvColor() );
5067             }
5068             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
5069             current_angle += arc_size;
5070             drawLine( x, y, new_x, new_y, g );
5071         }
5072     }
5073
5074     final private void pasteSubtree( final PhylogenyNode node ) {
5075         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5076             errorMessageNoCutCopyPasteInUnrootedDisplay();
5077             return;
5078         }
5079         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
5080             JOptionPane.showMessageDialog( this,
5081                                            "No tree in buffer (need to copy or cut a subtree first)",
5082                                            "Attempt to paste with empty buffer",
5083                                            JOptionPane.ERROR_MESSAGE );
5084             return;
5085         }
5086         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
5087         final Object[] options = { "As sibling", "As descendant", "Cancel" };
5088         final int r = JOptionPane.showOptionDialog( this,
5089                                                     "How to paste subtree" + label + "?",
5090                                                     "Paste Subtree",
5091                                                     JOptionPane.CLOSED_OPTION,
5092                                                     JOptionPane.QUESTION_MESSAGE,
5093                                                     null,
5094                                                     options,
5095                                                     options[ 2 ] );
5096         boolean paste_as_sibling = true;
5097         if ( r == 1 ) {
5098             paste_as_sibling = false;
5099         }
5100         else if ( r != 0 ) {
5101             return;
5102         }
5103         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
5104         buffer_phy.setAllNodesToNotCollapse();
5105         PhylogenyMethods.preOrderReId( buffer_phy );
5106         buffer_phy.setRooted( true );
5107         boolean need_to_show_whole = false;
5108         if ( paste_as_sibling ) {
5109             if ( node.isRoot() ) {
5110                 JOptionPane.showMessageDialog( this,
5111                                                "Cannot paste sibling to root",
5112                                                "Attempt to paste sibling to root",
5113                                                JOptionPane.ERROR_MESSAGE );
5114                 return;
5115             }
5116             buffer_phy.addAsSibling( node );
5117         }
5118         else {
5119             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
5120                 need_to_show_whole = true;
5121                 _phylogeny = buffer_phy;
5122             }
5123             else {
5124                 buffer_phy.addAsChild( node );
5125             }
5126         }
5127         if ( getCopiedAndPastedNodes() == null ) {
5128             setCopiedAndPastedNodes( new HashSet<Long>() );
5129         }
5130         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
5131         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
5132         for( final PhylogenyNode n : nodes ) {
5133             node_ids.add( n.getId() );
5134         }
5135         node_ids.add( node.getId() );
5136         getCopiedAndPastedNodes().addAll( node_ids );
5137         setNodeInPreorderToNull();
5138         _phylogeny.externalNodesHaveChanged();
5139         _phylogeny.clearHashIdToNodeMap();
5140         _phylogeny.recalculateNumberOfExternalDescendants( true );
5141         resetNodeIdToDistToLeafMap();
5142         setEdited( true );
5143         if ( need_to_show_whole ) {
5144             getControlPanel().showWhole();
5145         }
5146         repaint();
5147     }
5148
5149     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
5150         final PropertiesMap properties = node.getNodeData().getProperties();
5151         final StringBuffer sb = new StringBuffer();
5152         boolean first = true;
5153         for( final String ref : properties.getPropertyRefs() ) {
5154             if ( first ) {
5155                 first = false;
5156             }
5157             else {
5158                 sb.append( " " );
5159             }
5160             final Property p = properties.getProperty( ref );
5161             sb.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5162             sb.append( "=" );
5163             sb.append( p.getValue() );
5164             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5165                 sb.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5166             }
5167         }
5168         return sb;
5169     }
5170
5171     private void setColor( final Graphics2D g,
5172                            final PhylogenyNode node,
5173                            final boolean to_graphics_file,
5174                            final boolean to_pdf,
5175                            final boolean is_in_found_nodes,
5176                            final Color default_color ) {
5177         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
5178             g.setColor( Color.BLACK );
5179         }
5180         else if ( is_in_found_nodes ) {
5181             g.setColor( getColorForFoundNode( node ) );
5182         }
5183         else if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
5184                 && ( node.getNodeData().getNodeVisualData().getFontColor() != null ) ) {
5185             g.setColor( node.getNodeData().getNodeVisualData().getFontColor() );
5186         }
5187         else if ( getControlPanel().isColorAccordingToSequence() ) {
5188             g.setColor( getSequenceBasedColor( node ) );
5189         }
5190         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
5191             g.setColor( getTaxonomyBasedColor( node ) );
5192         }
5193         else if ( getControlPanel().isColorAccordingToAnnotation()
5194                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null ) && ( !node
5195                         .getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
5196             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
5197         }
5198         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
5199                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
5200             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
5201         }
5202         else if ( to_pdf ) {
5203             g.setColor( Color.BLACK );
5204         }
5205         else {
5206             g.setColor( default_color );
5207         }
5208     }
5209
5210     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
5211         getMainPanel().setCopiedAndPastedNodes( nodeIds );
5212     }
5213
5214     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
5215         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
5216     }
5217
5218     private boolean setFont( final Graphics2D g, final PhylogenyNode node, final boolean is_in_found_nodes ) {
5219         Font visual_font = null;
5220         if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null ) ) {
5221             visual_font = node.getNodeData().getNodeVisualData().getFont();
5222             g.setFont( visual_font != null ? visual_font : getTreeFontSet().getLargeFont() );
5223         }
5224         else {
5225             g.setFont( getTreeFontSet().getLargeFont() );
5226         }
5227         if ( is_in_found_nodes ) {
5228             g.setFont( g.getFont().deriveFont( Font.BOLD ) );
5229         }
5230         return visual_font != null;
5231     }
5232
5233     final private void setInOv( final boolean in_ov ) {
5234         _in_ov = in_ov;
5235     }
5236
5237     final private void setOvMaxHeight( final float ov_max_height ) {
5238         _ov_max_height = ov_max_height;
5239     }
5240
5241     final private void setOvMaxWidth( final float ov_max_width ) {
5242         _ov_max_width = ov_max_width;
5243     }
5244
5245     final private void setOvXcorrectionFactor( final float f ) {
5246         _ov_x_correction_factor = f;
5247     }
5248
5249     final private void setOvXDistance( final float ov_x_distance ) {
5250         _ov_x_distance = ov_x_distance;
5251     }
5252
5253     final private void setOvXPosition( final int ov_x_position ) {
5254         _ov_x_position = ov_x_position;
5255     }
5256
5257     final private void setOvYDistance( final float ov_y_distance ) {
5258         _ov_y_distance = ov_y_distance;
5259     }
5260
5261     final private void setOvYPosition( final int ov_y_position ) {
5262         _ov_y_position = ov_y_position;
5263     }
5264
5265     final private void setOvYStart( final int ov_y_start ) {
5266         _ov_y_start = ov_y_start;
5267     }
5268
5269     final private void setScaleDistance( final double scale_distance ) {
5270         _scale_distance = scale_distance;
5271     }
5272
5273     final private void setScaleLabel( final String scale_label ) {
5274         _scale_label = scale_label;
5275     }
5276
5277     private final void setupStroke( final Graphics2D g ) {
5278         if ( getYdistance() < 0.001 ) {
5279             g.setStroke( STROKE_005 );
5280         }
5281         else if ( getYdistance() < 0.01 ) {
5282             g.setStroke( STROKE_01 );
5283         }
5284         else if ( getYdistance() < 0.5 ) {
5285             g.setStroke( STROKE_025 );
5286         }
5287         else if ( getYdistance() < 1 ) {
5288             g.setStroke( STROKE_05 );
5289         }
5290         else if ( getYdistance() < 2 ) {
5291             g.setStroke( STROKE_075 );
5292         }
5293         else if ( getYdistance() < 20 ) {
5294             g.setStroke( STROKE_1 );
5295         }
5296         else {
5297             g.setStroke( STROKE_2 );
5298         }
5299     }
5300
5301     final private void setUpUrtFactor() {
5302         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
5303                 : getVisibleRect().height;
5304         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
5305             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
5306         }
5307         else {
5308             final int max_depth = _circ_max_depth;
5309             if ( max_depth > 0 ) {
5310                 setUrtFactor( d / ( 2 * max_depth ) );
5311             }
5312             else {
5313                 setUrtFactor( d / 2 );
5314             }
5315         }
5316         setUrtFactorOv( getUrtFactor() );
5317     }
5318
5319     final private void setUrtFactor( final float urt_factor ) {
5320         _urt_factor = urt_factor;
5321     }
5322
5323     final private void setUrtFactorOv( final float urt_factor_ov ) {
5324         _urt_factor_ov = urt_factor_ov;
5325     }
5326
5327     private void showExtDescNodeData( final PhylogenyNode node ) {
5328         final List<String> data = new ArrayList<String>();
5329         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
5330         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
5331             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
5332                 if ( !nodes.contains( n ) ) {
5333                     nodes.add( n );
5334                 }
5335             }
5336         }
5337         for( final PhylogenyNode n : nodes ) {
5338             switch ( getOptions().getExtDescNodeDataToReturn() ) {
5339                 case NODE_NAME:
5340                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5341                         data.add( n.getName() );
5342                     }
5343                     break;
5344                 case SEQUENCE_NAME:
5345                     if ( n.getNodeData().isHasSequence()
5346                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5347                         data.add( n.getNodeData().getSequence().getName() );
5348                     }
5349                     break;
5350                 case GENE_NAME:
5351                     if ( n.getNodeData().isHasSequence()
5352                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5353                         data.add( n.getNodeData().getSequence().getGeneName() );
5354                     }
5355                     break;
5356                 case SEQUENCE_SYMBOL:
5357                     if ( n.getNodeData().isHasSequence()
5358                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5359                         data.add( n.getNodeData().getSequence().getSymbol() );
5360                     }
5361                     break;
5362                 case SEQUENCE_MOL_SEQ:
5363                     if ( n.getNodeData().isHasSequence()
5364                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5365                         data.add( n.getNodeData().getSequence().getMolecularSequence() );
5366                     }
5367                     break;
5368                 case SEQUENCE_MOL_SEQ_FASTA:
5369                     final StringBuilder sb = new StringBuilder();
5370                     if ( n.getNodeData().isHasSequence()
5371                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
5372                         final StringBuilder ann = new StringBuilder();
5373                         if ( !ForesterUtil.isEmpty( n.getName() ) ) {
5374                             ann.append( n.getName() );
5375                             ann.append( "|" );
5376                         }
5377                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
5378                             ann.append( "SYM=" );
5379                             ann.append( n.getNodeData().getSequence().getSymbol() );
5380                             ann.append( "|" );
5381                         }
5382                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
5383                             ann.append( "NAME=" );
5384                             ann.append( n.getNodeData().getSequence().getName() );
5385                             ann.append( "|" );
5386                         }
5387                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
5388                             ann.append( "GN=" );
5389                             ann.append( n.getNodeData().getSequence().getGeneName() );
5390                             ann.append( "|" );
5391                         }
5392                         if ( n.getNodeData().getSequence().getAccession() != null ) {
5393                             ann.append( "ACC=" );
5394                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
5395                             ann.append( "|" );
5396                         }
5397                         if ( n.getNodeData().isHasTaxonomy() ) {
5398                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5399                                 ann.append( "TAXID=" );
5400                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5401                                 ann.append( "|" );
5402                             }
5403                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5404                                 ann.append( "SN=" );
5405                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
5406                                 ann.append( "|" );
5407                             }
5408                         }
5409                         String ann_str;
5410                         if ( ann.charAt( ann.length() - 1 ) == '|' ) {
5411                             ann_str = ann.substring( 0, ann.length() - 1 );
5412                         }
5413                         else {
5414                             ann_str = ann.toString();
5415                         }
5416                         sb.append( SequenceWriter.toFasta( ann_str, n.getNodeData().getSequence()
5417                                 .getMolecularSequence(), 60 ) );
5418                         data.add( sb.toString() );
5419                     }
5420                     break;
5421                 case SEQUENCE_ACC:
5422                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
5423                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
5424                         data.add( n.getNodeData().getSequence().getAccession().toString() );
5425                     }
5426                     break;
5427                 case TAXONOMY_SCIENTIFIC_NAME:
5428                     if ( n.getNodeData().isHasTaxonomy()
5429                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
5430                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
5431                     }
5432                     break;
5433                 case TAXONOMY_COMM0N_NAME:
5434                     if ( n.getNodeData().isHasTaxonomy()
5435                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) ) {
5436                         data.add( n.getNodeData().getTaxonomy().getCommonName() );
5437                     }
5438                     break;
5439                 case TAXONOMY_CODE:
5440                     if ( n.getNodeData().isHasTaxonomy()
5441                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
5442                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
5443                     }
5444                     break;
5445                 case UNKNOWN:
5446                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
5447                     break;
5448                 default:
5449                     throw new IllegalArgumentException( "unknown data element: "
5450                             + getOptions().getExtDescNodeDataToReturn() );
5451             }
5452         } // for loop
5453         final StringBuilder sb = new StringBuilder();
5454         final int size = TreePanelUtil.makeSB( data, getOptions(), sb );
5455         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
5456                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
5457             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
5458                 System.out.println( sb );
5459             }
5460             if ( sb.length() < 1 ) {
5461                 clearCurrentExternalNodesDataBuffer();
5462             }
5463             else {
5464                 setCurrentExternalNodesDataBuffer( sb );
5465             }
5466         }
5467         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
5468             if ( sb.length() < 1 ) {
5469                 TreePanelUtil.showInformationMessage( this, "No Appropriate Data (" + obtainTitleForExtDescNodeData()
5470                         + ")", "Descendants of selected node do not contain selected data" );
5471                 clearCurrentExternalNodesDataBuffer();
5472             }
5473             else {
5474                 setCurrentExternalNodesDataBuffer( sb );
5475                 String title;
5476                 if ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() ) {
5477                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5478                             : obtainTitleForExtDescNodeData() )
5479                             + " for "
5480                             + data.size()
5481                             + " nodes, unique entries: "
5482                             + size;
5483                 }
5484                 else {
5485                     title = ( getOptions().getExtDescNodeDataToReturn() == NODE_DATA.UNKNOWN ? "Data"
5486                             : obtainTitleForExtDescNodeData() )
5487                             + " for "
5488                             + data.size()
5489                             + "/"
5490                             + node.getNumberOfExternalNodes()
5491                             + " external descendats of node "
5492                             + node
5493                             + ", unique entries: " + size;
5494                 }
5495                 final String s = sb.toString().trim();
5496                 if ( getMainPanel().getMainFrame() == null ) {
5497                     // Must be "E" applet version.
5498                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5499                     ae.showTextFrame( s, title );
5500                 }
5501                 else {
5502                     getMainPanel().getMainFrame().showTextFrame( s, title );
5503                 }
5504             }
5505         }
5506     }
5507
5508     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
5509         try {
5510             if ( ( node.getName().length() > 0 )
5511                     || ( node.getNodeData().isHasTaxonomy() && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData()
5512                             .getTaxonomy() ) )
5513                     || ( node.getNodeData().isHasSequence() && !TreePanelUtil.isSequenceEmpty( node.getNodeData()
5514                             .getSequence() ) ) || ( node.getNodeData().isHasDate() )
5515                     || ( node.getNodeData().isHasDistribution() ) || node.getBranchData().isHasConfidences() ) {
5516                 _popup_buffer.setLength( 0 );
5517                 short lines = 0;
5518                 if ( node.getName().length() > 0 ) {
5519                     lines++;
5520                     _popup_buffer.append( node.getName() );
5521                 }
5522                 if ( node.getNodeData().isHasTaxonomy()
5523                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
5524                     lines++;
5525                     boolean enc_data = false;
5526                     final Taxonomy tax = node.getNodeData().getTaxonomy();
5527                     if ( _popup_buffer.length() > 0 ) {
5528                         _popup_buffer.append( "\n" );
5529                     }
5530                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
5531                         _popup_buffer.append( "[" );
5532                         _popup_buffer.append( tax.getTaxonomyCode() );
5533                         _popup_buffer.append( "]" );
5534                         enc_data = true;
5535                     }
5536                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
5537                         if ( enc_data ) {
5538                             _popup_buffer.append( " " );
5539                         }
5540                         _popup_buffer.append( tax.getScientificName() );
5541                         enc_data = true;
5542                     }
5543                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
5544                         if ( enc_data ) {
5545                             _popup_buffer.append( " (" );
5546                         }
5547                         else {
5548                             _popup_buffer.append( "(" );
5549                         }
5550                         _popup_buffer.append( tax.getCommonName() );
5551                         _popup_buffer.append( ")" );
5552                         enc_data = true;
5553                     }
5554                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
5555                         if ( enc_data ) {
5556                             _popup_buffer.append( " (" );
5557                         }
5558                         else {
5559                             _popup_buffer.append( "(" );
5560                         }
5561                         _popup_buffer.append( tax.getAuthority() );
5562                         _popup_buffer.append( ")" );
5563                         enc_data = true;
5564                     }
5565                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
5566                         if ( enc_data ) {
5567                             _popup_buffer.append( " [" );
5568                         }
5569                         else {
5570                             _popup_buffer.append( "[" );
5571                         }
5572                         _popup_buffer.append( tax.getRank() );
5573                         _popup_buffer.append( "]" );
5574                         enc_data = true;
5575                     }
5576                     if ( tax.getSynonyms().size() > 0 ) {
5577                         if ( enc_data ) {
5578                             _popup_buffer.append( " " );
5579                         }
5580                         _popup_buffer.append( "[" );
5581                         int counter = 1;
5582                         for( final String syn : tax.getSynonyms() ) {
5583                             if ( !ForesterUtil.isEmpty( syn ) ) {
5584                                 enc_data = true;
5585                                 _popup_buffer.append( syn );
5586                                 if ( counter < tax.getSynonyms().size() ) {
5587                                     _popup_buffer.append( ", " );
5588                                 }
5589                             }
5590                             counter++;
5591                         }
5592                         _popup_buffer.append( "]" );
5593                     }
5594                     if ( !enc_data ) {
5595                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
5596                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
5597                                 _popup_buffer.append( "[" );
5598                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
5599                                 _popup_buffer.append( "] " );
5600                             }
5601                             _popup_buffer.append( tax.getIdentifier().getValue() );
5602                         }
5603                     }
5604                 }
5605                 if ( node.getNodeData().isHasSequence()
5606                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
5607                     lines++;
5608                     boolean enc_data = false;
5609                     if ( _popup_buffer.length() > 0 ) {
5610                         _popup_buffer.append( "\n" );
5611                     }
5612                     final Sequence seq = node.getNodeData().getSequence();
5613                     if ( seq.getAccession() != null ) {
5614                         _popup_buffer.append( "[" );
5615                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
5616                             _popup_buffer.append( seq.getAccession().getSource() );
5617                             _popup_buffer.append( ":" );
5618                         }
5619                         _popup_buffer.append( seq.getAccession().getValue() );
5620                         _popup_buffer.append( "]" );
5621                         enc_data = true;
5622                     }
5623                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
5624                         if ( enc_data ) {
5625                             _popup_buffer.append( " [" );
5626                         }
5627                         else {
5628                             _popup_buffer.append( "[" );
5629                         }
5630                         _popup_buffer.append( seq.getSymbol() );
5631                         _popup_buffer.append( "]" );
5632                         enc_data = true;
5633                     }
5634                     if ( !ForesterUtil.isEmpty( seq.getGeneName() ) ) {
5635                         if ( enc_data ) {
5636                             _popup_buffer.append( " [" );
5637                         }
5638                         else {
5639                             _popup_buffer.append( "[" );
5640                         }
5641                         _popup_buffer.append( seq.getGeneName() );
5642                         _popup_buffer.append( "]" );
5643                         enc_data = true;
5644                     }
5645                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
5646                         if ( enc_data ) {
5647                             _popup_buffer.append( " " );
5648                         }
5649                         _popup_buffer.append( seq.getName() );
5650                     }
5651                 }
5652                 if ( node.getNodeData().isHasDate() ) {
5653                     lines++;
5654                     if ( _popup_buffer.length() > 0 ) {
5655                         _popup_buffer.append( "\n" );
5656                     }
5657                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
5658                 }
5659                 if ( node.getNodeData().isHasDistribution() ) {
5660                     lines++;
5661                     if ( _popup_buffer.length() > 0 ) {
5662                         _popup_buffer.append( "\n" );
5663                     }
5664                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
5665                 }
5666                 if ( node.getBranchData().isHasConfidences() ) {
5667                     final List<Confidence> confs = node.getBranchData().getConfidences();
5668                     for( final Confidence confidence : confs ) {
5669                         lines++;
5670                         if ( _popup_buffer.length() > 0 ) {
5671                             _popup_buffer.append( "\n" );
5672                         }
5673                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
5674                             _popup_buffer.append( "[" );
5675                             _popup_buffer.append( confidence.getType() );
5676                             _popup_buffer.append( "] " );
5677                         }
5678                         _popup_buffer
5679                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
5680                                                                                           getOptions()
5681                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5682                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
5683                             _popup_buffer.append( " (sd=" );
5684                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
5685                                     .getStandardDeviation(), getOptions()
5686                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
5687                             _popup_buffer.append( ")" );
5688                         }
5689                     }
5690                 }
5691                 if ( node.getNodeData().isHasProperties() ) {
5692                     final PropertiesMap properties = node.getNodeData().getProperties();
5693                     for( final String ref : properties.getPropertyRefs() ) {
5694                         _popup_buffer.append( "\n" );
5695                         final Property p = properties.getProperty( ref );
5696                         _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
5697                         _popup_buffer.append( "=" );
5698                         _popup_buffer.append( p.getValue() );
5699                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
5700                             _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
5701                         }
5702                     }
5703                 }
5704                 if ( _popup_buffer.length() > 0 ) {
5705                     if ( !getConfiguration().isUseNativeUI() ) {
5706                         _rollover_popup
5707                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
5708                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
5709                         if ( isInFoundNodes0( node ) && !isInFoundNodes1( node ) ) {
5710                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0() );
5711                         }
5712                         else if ( !isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5713                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor1() );
5714                         }
5715                         else if ( isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
5716                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0and1() );
5717                         }
5718                         else {
5719                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
5720                         }
5721                     }
5722                     else {
5723                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
5724                     }
5725                     _rollover_popup.setText( _popup_buffer.toString() );
5726                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
5727                                                                                   _rollover_popup,
5728                                                                                   e.getLocationOnScreen().x + 10,
5729                                                                                   e.getLocationOnScreen().y
5730                                                                                           - ( lines * 20 ) );
5731                     _node_desc_popup.show();
5732                 }
5733             }
5734         }
5735         catch ( final Exception ex ) {
5736             // Do nothing.
5737         }
5738     }
5739
5740     final private void showNodeEditFrame( final PhylogenyNode n ) {
5741         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5742             // pop up edit box for single node
5743             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
5744             _node_frame_index++;
5745         }
5746         else {
5747             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5748         }
5749     }
5750
5751     final private void showNodeFrame( final PhylogenyNode n ) {
5752         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
5753             // pop up edit box for single node
5754             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
5755             _node_frame_index++;
5756         }
5757         else {
5758             JOptionPane.showMessageDialog( this, "too many node windows are open" );
5759         }
5760     }
5761
5762     final private void switchDisplaygetPhylogenyGraphicsType() {
5763         switch ( getPhylogenyGraphicsType() ) {
5764             case RECTANGULAR:
5765                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5766                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5767                 break;
5768             case EURO_STYLE:
5769                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5770                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5771                 break;
5772             case ROUNDED:
5773                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5774                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5775                 break;
5776             case CURVED:
5777                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5778                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5779                 break;
5780             case TRIANGULAR:
5781                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5782                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5783                 break;
5784             case CONVEX:
5785                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5786                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5787                 break;
5788             case UNROOTED:
5789                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5790                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5791                 break;
5792             case CIRCULAR:
5793                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5794                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5795                 break;
5796             default:
5797                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5798         }
5799         if ( getControlPanel().getDynamicallyHideData() != null ) {
5800             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5801                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5802             }
5803             else {
5804                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5805             }
5806         }
5807         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5808             getControlPanel().setDrawPhylogramEnabled( true );
5809         }
5810         else {
5811             getControlPanel().setDrawPhylogramEnabled( false );
5812         }
5813         if ( getMainPanel().getMainFrame() == null ) {
5814             // Must be "E" applet version.
5815             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5816                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5817         }
5818         else {
5819             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5820         }
5821     }
5822
5823     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
5824         if ( node.getNodeData().getNodeVisualData() == null ) {
5825             node.getNodeData().setNodeVisualData( new NodeVisualData() );
5826         }
5827         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
5828     }
5829
5830     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
5831         g.drawString( str, x, y );
5832     }
5833
5834     final private static boolean plusPressed( final int key_code ) {
5835         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5836                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5837     }
5838
5839     final private class NodeColorizationActionListener implements ActionListener {
5840
5841         List<PhylogenyNode> _additional_nodes = null;
5842         JColorChooser       _chooser          = null;
5843         PhylogenyNode       _node             = null;
5844
5845         NodeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5846             _chooser = chooser;
5847             _node = node;
5848         }
5849
5850         NodeColorizationActionListener( final JColorChooser chooser,
5851                                         final PhylogenyNode node,
5852                                         final List<PhylogenyNode> additional_nodes ) {
5853             _chooser = chooser;
5854             _node = node;
5855             _additional_nodes = additional_nodes;
5856         }
5857
5858         @Override
5859         public void actionPerformed( final ActionEvent e ) {
5860             final Color c = _chooser.getColor();
5861             if ( c != null ) {
5862                 colorizeNodes( c, _node, _additional_nodes );
5863             }
5864         }
5865     }
5866
5867     final private class SubtreeColorizationActionListener implements ActionListener {
5868
5869         List<PhylogenyNode> _additional_nodes = null;
5870         JColorChooser       _chooser          = null;
5871         PhylogenyNode       _node             = null;
5872
5873         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5874             _chooser = chooser;
5875             _node = node;
5876         }
5877
5878         SubtreeColorizationActionListener( final JColorChooser chooser,
5879                                            final PhylogenyNode node,
5880                                            final List<PhylogenyNode> additional_nodes ) {
5881             _chooser = chooser;
5882             _node = node;
5883             _additional_nodes = additional_nodes;
5884         }
5885
5886         @Override
5887         public void actionPerformed( final ActionEvent e ) {
5888             final Color c = _chooser.getColor();
5889             if ( c != null ) {
5890                 colorizeSubtree( c, _node, _additional_nodes );
5891             }
5892         }
5893     }
5894 }