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