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