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