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