fixed issue with cutting off of long names in pdf export + 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     }
4509
4510     final void calculateScaleDistance() {
4511         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4512             return;
4513         }
4514         final double height = getMaxDistanceToRoot();
4515         if ( height > 0 ) {
4516             if ( ( height <= 0.5 ) ) {
4517                 setScaleDistance( 0.01 );
4518             }
4519             else if ( height <= 5.0 ) {
4520                 setScaleDistance( 0.1 );
4521             }
4522             else if ( height <= 50.0 ) {
4523                 setScaleDistance( 1 );
4524             }
4525             else if ( height <= 500.0 ) {
4526                 setScaleDistance( 10 );
4527             }
4528             else {
4529                 setScaleDistance( 100 );
4530             }
4531         }
4532         else {
4533             setScaleDistance( 0.0 );
4534         }
4535         String scale_label = String.valueOf( getScaleDistance() );
4536         if ( !ForesterUtil.isEmpty( _phylogeny.getDistanceUnit() ) ) {
4537             scale_label += " [" + _phylogeny.getDistanceUnit() + "]";
4538         }
4539         setScaleLabel( scale_label );
4540     }
4541
4542     final Color calculateSequenceBasedColor( final Sequence seq ) {
4543         if ( ForesterUtil.isEmpty( seq.getName() ) ) {
4544             return getTreeColorSet().getSequenceColor();
4545         }
4546         Color c = null;
4547         final String seq_name = seq.getName();
4548         c = getControlPanel().getSequenceColors().get( seq_name );
4549         if ( c == null ) {
4550             c = AptxUtil.calculateColorFromString( seq_name, false );
4551             getControlPanel().getSequenceColors().put( seq_name, c );
4552         }
4553         return c;
4554     }
4555
4556     final Color calculateTaxonomyBasedColor( final Taxonomy tax ) {
4557         if ( getOptions().isColorByTaxonomicGroup() ) {
4558             if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4559                 boolean ex = false;
4560                 String group = null;
4561                 try {
4562                     group = TaxonomyUtil.getTaxGroupByTaxCode( tax.getTaxonomyCode() );
4563                 }
4564                 catch ( final Exception e ) {
4565                     ex = true;
4566                 }
4567                 if ( !ex && !ForesterUtil.isEmpty( group ) ) {
4568                     final Color c = ForesterUtil.obtainColorDependingOnTaxonomyGroup( group );
4569                     if ( c != null ) {
4570                         return c;
4571                     }
4572                 }
4573             }
4574             return getTreeColorSet().getTaxonomyColor();
4575         }
4576         else {
4577             if ( ForesterUtil.isEmpty( tax.getTaxonomyCode() ) && ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4578                 return getTreeColorSet().getTaxonomyColor();
4579             }
4580             Color c = null;
4581             if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4582                 c = getControlPanel().getSpeciesColors().get( tax.getTaxonomyCode() );
4583             }
4584             if ( ( c == null ) && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4585                 c = getControlPanel().getSpeciesColors().get( tax.getScientificName() );
4586             }
4587             if ( c == null ) {
4588                 if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4589                     c = AptxUtil.calculateColorFromString( tax.getTaxonomyCode(), true );
4590                     getControlPanel().getSpeciesColors().put( tax.getTaxonomyCode(), c );
4591                 }
4592                 else {
4593                     c = AptxUtil.calculateColorFromString( tax.getScientificName(), true );
4594                     getControlPanel().getSpeciesColors().put( tax.getScientificName(), c );
4595                 }
4596             }
4597             return c;
4598         }
4599     }
4600
4601     void checkForVectorProperties( final Phylogeny phy ) {
4602         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
4603         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
4604             final PhylogenyNode node = iter.next();
4605             if ( node.getNodeData().getProperties() != null ) {
4606                 final PropertiesMap pm = node.getNodeData().getProperties();
4607                 final double[] vector = new double[ pm.getProperties().size() ];
4608                 int counter = 0;
4609                 for( final String ref : pm.getProperties().keySet() ) {
4610                     if ( ref.startsWith( PhyloXmlUtil.VECTOR_PROPERTY_REF ) ) {
4611                         final Property p = pm.getProperty( ref );
4612                         final String value_str = p.getValue();
4613                         final String index_str = ref
4614                                 .substring( PhyloXmlUtil.VECTOR_PROPERTY_REF.length(), ref.length() );
4615                         double d = -100;
4616                         try {
4617                             d = Double.parseDouble( value_str );
4618                         }
4619                         catch ( final NumberFormatException e ) {
4620                             JOptionPane.showMessageDialog( this, "Could not parse \"" + value_str
4621                                                            + "\" into a decimal value", "Problem with Vector Data", JOptionPane.ERROR_MESSAGE );
4622                             return;
4623                         }
4624                         int i = -1;
4625                         try {
4626                             i = Integer.parseInt( index_str );
4627                         }
4628                         catch ( final NumberFormatException e ) {
4629                             JOptionPane.showMessageDialog( this,
4630                                                            "Could not parse \"" + index_str
4631                                                            + "\" into index for vector data",
4632                                                            "Problem with Vector Data",
4633                                                            JOptionPane.ERROR_MESSAGE );
4634                             return;
4635                         }
4636                         if ( i < 0 ) {
4637                             JOptionPane.showMessageDialog( this,
4638                                                            "Attempt to use negative index for vector data",
4639                                                            "Problem with Vector Data",
4640                                                            JOptionPane.ERROR_MESSAGE );
4641                             return;
4642                         }
4643                         vector[ i ] = d;
4644                         ++counter;
4645                         stats.addValue( d );
4646                     }
4647                 }
4648                 final List<Double> vector_l = new ArrayList<Double>( counter );
4649                 for( int i = 0; i < counter; ++i ) {
4650                     vector_l.add( vector[ i ] );
4651                 }
4652                 node.getNodeData().setVector( vector_l );
4653             }
4654         }
4655         if ( stats.getN() > 0 ) {
4656             _statistics_for_vector_data = stats;
4657         }
4658     }
4659
4660     void clearCurrentExternalNodesDataBuffer() {
4661         setCurrentExternalNodesDataBuffer( new StringBuilder() );
4662     }
4663
4664     /**
4665      * Collapse the tree from the given node
4666      *
4667      * @param node
4668      *            a PhylogenyNode
4669      */
4670     final void collapse( final PhylogenyNode node ) {
4671         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4672             JOptionPane.showMessageDialog( this,
4673                                            "Cannot collapse in unrooted display type",
4674                                            "Attempt to collapse in unrooted display",
4675                                            JOptionPane.WARNING_MESSAGE );
4676             return;
4677         }
4678         if ( !node.isExternal() && !node.isRoot() ) {
4679             final boolean collapse = !node.isCollapse();
4680             TreePanelUtil.collapseSubtree( node, collapse );
4681             updateSetOfCollapsedExternalNodes();
4682             _phylogeny.recalculateNumberOfExternalDescendants( true );
4683             resetNodeIdToDistToLeafMap();
4684             calculateLongestExtNodeInfo();
4685             setNodeInPreorderToNull();
4686             _control_panel.displayedPhylogenyMightHaveChanged( true );
4687             resetPreferredSize();
4688             updateOvSizes();
4689             _main_panel.adjustJScrollPane();
4690             repaint();
4691         }
4692     }
4693
4694     final void collapseSpeciesSpecificSubtrees() {
4695         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4696             return;
4697         }
4698         setWaitCursor();
4699         TreePanelUtil.collapseSpeciesSpecificSubtrees( _phylogeny );
4700         updateSetOfCollapsedExternalNodes();
4701         _phylogeny.recalculateNumberOfExternalDescendants( true );
4702         resetNodeIdToDistToLeafMap();
4703         calculateLongestExtNodeInfo();
4704         setNodeInPreorderToNull();
4705         resetPreferredSize();
4706         _main_panel.adjustJScrollPane();
4707         setArrowCursor();
4708         repaint();
4709     }
4710
4711     final void colorRank( final String rank ) {
4712         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4713             return;
4714         }
4715         setWaitCursor();
4716         AptxUtil.removeBranchColors( _phylogeny );
4717         final int colorizations = TreePanelUtil.colorPhylogenyAccordingToRanks( _phylogeny, rank, this );
4718         if ( colorizations > 0 ) {
4719             _control_panel.setColorBranches( true );
4720             if ( _control_panel.getUseVisualStylesCb() != null ) {
4721                 _control_panel.getUseVisualStylesCb().setSelected( true );
4722             }
4723             if ( _control_panel.getColorAccSpeciesCb() != null ) {
4724                 _control_panel.getColorAccSpeciesCb().setSelected( false );
4725             }
4726             _options.setColorLabelsSameAsParentBranch( true );
4727             if ( getMainPanel().getMainFrame()._color_labels_same_as_parent_branch != null ) {
4728                 getMainPanel().getMainFrame()._color_labels_same_as_parent_branch.setSelected( true );
4729             }
4730             _control_panel.repaint();
4731         }
4732         setArrowCursor();
4733         repaint();
4734         if ( colorizations > 0 ) {
4735             String msg = "Taxonomy colorization via " + rank + " completed:\n";
4736             if ( colorizations > 1 ) {
4737                 msg += "colorized " + colorizations + " subtrees";
4738             }
4739             else {
4740                 msg += "colorized one subtree";
4741             }
4742             setEdited( true );
4743             JOptionPane.showMessageDialog( this,
4744                                            msg,
4745                                            "Taxonomy Colorization Completed (" + rank + ")",
4746                                            JOptionPane.INFORMATION_MESSAGE );
4747         }
4748         else {
4749             String msg = "Could not taxonomy colorize any subtree via " + rank + ".\n";
4750             msg += "Possible solutions (given that suitable taxonomic information is present):\n";
4751             msg += "select a different rank (e.g. phylum, genus, ...)\n";
4752             msg += "  and/or\n";
4753             msg += "execute:\n";
4754             msg += "1. \"" + MainFrameApplication.OBTAIN_DETAILED_TAXONOMIC_INFORMATION + "\" (Tools)\n";
4755             msg += "2. \"" + MainFrameApplication.INFER_ANCESTOR_TAXONOMIES + "\" (Analysis)";
4756             JOptionPane.showMessageDialog( this, msg, "Taxonomy Colorization Failed", JOptionPane.WARNING_MESSAGE );
4757         }
4758     }
4759
4760     final void confColor() {
4761         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4762             return;
4763         }
4764         setWaitCursor();
4765         AptxUtil.removeBranchColors( _phylogeny );
4766         TreePanelUtil.colorPhylogenyAccordingToConfidenceValues( _phylogeny, this );
4767         _control_panel.setColorBranches( true );
4768         if ( _control_panel.getUseVisualStylesCb() != null ) {
4769             _control_panel.getUseVisualStylesCb().setSelected( true );
4770         }
4771         setArrowCursor();
4772         repaint();
4773     }
4774
4775     final void decreaseDomainStructureEvalueThresholdExp() {
4776         if ( _domain_structure_e_value_thr_exp > -20 ) {
4777             _domain_structure_e_value_thr_exp -= 1;
4778         }
4779     }
4780
4781     /**
4782      * Find the node, if any, at the given location
4783      *
4784      * @param x
4785      * @param y
4786      * @return pointer to the node at x,y, null if not found
4787      */
4788     final PhylogenyNode findNode( final int x, final int y ) {
4789         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4790             return null;
4791         }
4792         final int half_box_size_plus_wiggle = ( getOptions().getDefaultNodeShapeSize() / 2 ) + WIGGLE;
4793         for( final PhylogenyNodeIterator iter = _phylogeny.iteratorPostorder(); iter.hasNext(); ) {
4794             final PhylogenyNode node = iter.next();
4795             if ( ( _phylogeny.isRooted() || !node.isRoot() || ( node.getNumberOfDescendants() > 2 ) )
4796                     && ( ( node.getXcoord() - half_box_size_plus_wiggle ) <= x )
4797                     && ( ( node.getXcoord() + half_box_size_plus_wiggle ) >= x )
4798                     && ( ( node.getYcoord() - half_box_size_plus_wiggle ) <= y )
4799                     && ( ( node.getYcoord() + half_box_size_plus_wiggle ) >= y ) ) {
4800                 return node;
4801             }
4802         }
4803         return null;
4804     }
4805
4806     final Configuration getConfiguration() {
4807         return _configuration;
4808     }
4809
4810     final ControlPanel getControlPanel() {
4811         return _control_panel;
4812     }
4813
4814     String getCurrentExternalNodesDataBufferAsString() {
4815         return _current_external_nodes_data_buffer.toString();
4816     }
4817
4818     int getCurrentExternalNodesDataBufferChangeCounter() {
4819         return _current_external_nodes_data_buffer_change_counter;
4820     }
4821
4822     final int getDomainStructureEvalueThresholdExp() {
4823         return _domain_structure_e_value_thr_exp;
4824     }
4825
4826     final Set<Long> getFoundNodes0() {
4827         return _found_nodes_0;
4828     }
4829
4830     final Set<Long> getFoundNodes1() {
4831         return _found_nodes_1;
4832     }
4833
4834     List<PhylogenyNode> getFoundNodesAsListOfPhylogenyNodes() {
4835         final List<PhylogenyNode> additional_nodes = new ArrayList<PhylogenyNode>();
4836         if ( getFoundNodes0() != null ) {
4837             for( final Long id : getFoundNodes0() ) {
4838                 final PhylogenyNode n = _phylogeny.getNode( id );
4839                 if ( n != null ) {
4840                     additional_nodes.add( n );
4841                 }
4842             }
4843         }
4844         if ( getFoundNodes1() != null ) {
4845             for( final Long id : getFoundNodes1() ) {
4846                 if ( ( getFoundNodes0() == null ) || !getFoundNodes0().contains( id ) ) {
4847                     final PhylogenyNode n = _phylogeny.getNode( id );
4848                     if ( n != null ) {
4849                         additional_nodes.add( n );
4850                     }
4851                 }
4852             }
4853         }
4854         return additional_nodes;
4855     }
4856
4857     final Color getGraphicsForNodeBoxWithColorForParentBranch( final PhylogenyNode node ) {
4858         if ( getControlPanel().isUseVisualStyles() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
4859             return ( PhylogenyMethods.getBranchColorValue( node ) );
4860         }
4861         else {
4862             return ( getTreeColorSet().getBranchColor() );
4863         }
4864     }
4865
4866     final int getLongestExtNodeInfo() {
4867         return _longest_ext_node_info;
4868     }
4869
4870     final Options getOptions() {
4871         if ( _options == null ) {
4872             _options = getControlPanel().getOptions();
4873         }
4874         return _options;
4875     }
4876
4877     final Rectangle2D getOvRectangle() {
4878         return _ov_rectangle;
4879     }
4880
4881     final Rectangle getOvVirtualRectangle() {
4882         return _ov_virtual_rectangle;
4883     }
4884
4885     final PHYLOGENY_GRAPHICS_TYPE getPhylogenyGraphicsType() {
4886         return _graphics_type;
4887     }
4888
4889     final Color getSequenceBasedColor( final PhylogenyNode node ) {
4890         if ( node.getNodeData().isHasSequence() ) {
4891             return calculateSequenceBasedColor( node.getNodeData().getSequence() );
4892         }
4893         // return non-colorized color
4894         return getTreeColorSet().getSequenceColor();
4895     }
4896
4897     final double getStartingAngle() {
4898         return _urt_starting_angle;
4899     }
4900
4901     DescriptiveStatistics getStatisticsForExpressionValues() {
4902         return _statistics_for_vector_data;
4903     }
4904
4905     final Color getTaxonomyBasedColor( final PhylogenyNode node ) {
4906         if ( node.isExternal() && node.getNodeData().isHasTaxonomy() ) {
4907             return calculateTaxonomyBasedColor( node.getNodeData().getTaxonomy() );
4908         }
4909         // return non-colorized color
4910         return getTreeColorSet().getTaxonomyColor();
4911     }
4912
4913     final File getTreeFile() {
4914         return _treefile;
4915     }
4916
4917     final float getXcorrectionFactor() {
4918         return _x_correction_factor;
4919     }
4920
4921     final float getXdistance() {
4922         return _x_distance;
4923     }
4924
4925     final float getYdistance() {
4926         return _y_distance;
4927     }
4928
4929     final void increaseDomainStructureEvalueThresholdExp() {
4930         if ( _domain_structure_e_value_thr_exp < 3 ) {
4931             _domain_structure_e_value_thr_exp += 1;
4932         }
4933     }
4934
4935     final void initNodeData() {
4936         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4937             return;
4938         }
4939         double _max_original_domain_structure_width = 0.0;
4940         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
4941             if ( node.getNodeData().isHasSequence()
4942                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4943                 RenderableDomainArchitecture rds = null;
4944                 if ( !( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
4945                     if ( SPECIAL_DOMAIN_COLORING ) {
4946                         rds = new RenderableDomainArchitecture( node.getNodeData().getSequence()
4947                                                                 .getDomainArchitecture(), node.getName() );
4948                     }
4949                     else {
4950                         rds = new RenderableDomainArchitecture( node.getNodeData().getSequence()
4951                                                                 .getDomainArchitecture() );
4952                     }
4953                     node.getNodeData().getSequence().setDomainArchitecture( rds );
4954                 }
4955                 else {
4956                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
4957                 }
4958                 if ( getControlPanel().isShowDomainArchitectures() ) {
4959                     final double dsw = rds.getOriginalSize().getWidth();
4960                     if ( dsw > _max_original_domain_structure_width ) {
4961                         _max_original_domain_structure_width = dsw;
4962                     }
4963                 }
4964             }
4965         }
4966         if ( getControlPanel().isShowDomainArchitectures() ) {
4967             final float ds_factor_width = ( float ) ( _domain_structure_width / _max_original_domain_structure_width );
4968             for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
4969                 if ( node.getNodeData().isHasSequence()
4970                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4971                     final RenderableDomainArchitecture rds = ( RenderableDomainArchitecture ) node.getNodeData()
4972                             .getSequence().getDomainArchitecture();
4973                     rds.setRenderingFactorWidth( ds_factor_width );
4974                     rds.setParameter( _domain_structure_e_value_thr_exp );
4975                 }
4976             }
4977         }
4978     }
4979
4980     final boolean inOv( final MouseEvent e ) {
4981         return ( ( e.getX() > ( getVisibleRect().x + getOvXPosition() + 1 ) )
4982                 && ( e.getX() < ( ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() ) - 1 ) )
4983                 && ( e.getY() > ( getVisibleRect().y + getOvYPosition() + 1 ) ) && ( e.getY() < ( ( getVisibleRect().y
4984                         + getOvYPosition() + getOvMaxHeight() ) - 1 ) ) );
4985     }
4986
4987     final boolean inOvRectangle( final MouseEvent e ) {
4988         return ( ( e.getX() >= ( getOvRectangle().getX() - 1 ) )
4989                 && ( e.getX() <= ( getOvRectangle().getX() + getOvRectangle().getWidth() + 1 ) )
4990                 && ( e.getY() >= ( getOvRectangle().getY() - 1 ) ) && ( e.getY() <= ( getOvRectangle().getY()
4991                         + getOvRectangle().getHeight() + 1 ) ) );
4992     }
4993
4994     final boolean isApplet() {
4995         return getMainPanel() instanceof MainPanelApplets;
4996     }
4997
4998     final boolean isCanCollapse() {
4999         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5000     }
5001
5002     final boolean isCanColorSubtree() {
5003         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5004     }
5005
5006     final boolean isCanCopy() {
5007         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
5008     }
5009
5010     final boolean isCanCut( final PhylogenyNode node ) {
5011         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() && !node
5012                 .isRoot() );
5013     }
5014
5015     final boolean isCanDelete() {
5016         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
5017     }
5018
5019     final boolean isCanPaste() {
5020         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
5021                 && ( getCutOrCopiedTree() != null ) && !getCutOrCopiedTree().isEmpty() );
5022     }
5023
5024     final boolean isCanReroot() {
5025         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && ( _subtree_index < 1 ) );
5026     }
5027
5028     final boolean isCanSubtree( final PhylogenyNode node ) {
5029         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && !node.isExternal() && ( !node
5030                 .isRoot() || ( _subtree_index > 0 ) ) );
5031     }
5032
5033     final boolean isCurrentTreeIsSubtree() {
5034         return ( _subtree_index > 0 );
5035     }
5036
5037     final boolean isEdited() {
5038         return _edited;
5039     }
5040
5041     final boolean isInOvRect() {
5042         return _in_ov_rect;
5043     }
5044
5045     final boolean isOvOn() {
5046         return _ov_on;
5047     }
5048
5049     final boolean isPhyHasBranchLengths() {
5050         return _phy_has_branch_lengths;
5051     }
5052
5053     final void midpointRoot() {
5054         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5055             return;
5056         }
5057         if ( !_phylogeny.isRerootable() ) {
5058             JOptionPane.showMessageDialog( this,
5059                                            "This is not rerootable",
5060                                            "Not rerootable",
5061                                            JOptionPane.WARNING_MESSAGE );
5062             return;
5063         }
5064         setNodeInPreorderToNull();
5065         setWaitCursor();
5066         PhylogenyMethods.midpointRoot( _phylogeny );
5067         resetNodeIdToDistToLeafMap();
5068         setArrowCursor();
5069         setEdited( true );
5070         repaint();
5071     }
5072
5073     final void mouseClicked( final MouseEvent e ) {
5074         if ( getOptions().isShowOverview() && isOvOn() && isInOv() ) {
5075             final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
5076             final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
5077             double x = ( e.getX() - getVisibleRect().x - getOvXPosition() - ( getOvRectangle().getWidth() / 2.0 ) )
5078                     * w_ratio;
5079             double y = ( e.getY() - getVisibleRect().y - getOvYPosition() - ( getOvRectangle().getHeight() / 2.0 ) )
5080                     * h_ratio;
5081             if ( x < 0 ) {
5082                 x = 0;
5083             }
5084             if ( y < 0 ) {
5085                 y = 0;
5086             }
5087             final double max_x = getWidth() - getVisibleRect().width;
5088             final double max_y = getHeight() - getVisibleRect().height;
5089             if ( x > max_x ) {
5090                 x = max_x;
5091             }
5092             if ( y > max_y ) {
5093                 y = max_y;
5094             }
5095             getMainPanel().getCurrentScrollPane().getViewport()
5096             .setViewPosition( new Point( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ) ) );
5097             setInOvRect( true );
5098             repaint();
5099         }
5100         else {
5101             final PhylogenyNode node = findNode( e.getX(), e.getY() );
5102             if ( node != null ) {
5103                 if ( !node.isRoot() && node.getParent().isCollapse() ) {
5104                     return;
5105                 }
5106                 _highlight_node = node;
5107                 // Check if shift key is down
5108                 if ( ( e.getModifiers() & InputEvent.SHIFT_MASK ) != 0 ) {
5109                     // Yes, so add to _found_nodes
5110                     if ( getFoundNodes0() == null ) {
5111                         setFoundNodes0( new HashSet<Long>() );
5112                     }
5113                     getFoundNodes0().add( node.getId() );
5114                     // Check if control key is down
5115                 }
5116                 else if ( ( e.getModifiers() & InputEvent.CTRL_MASK ) != 0 ) {
5117                     // Yes, so pop-up menu
5118                     displayNodePopupMenu( node, e.getX(), e.getY() );
5119                     // Handle unadorned click
5120                 }
5121                 else {
5122                     // Check for right mouse button
5123                     if ( e.getModifiers() == 4 ) {
5124                         displayNodePopupMenu( node, e.getX(), e.getY() );
5125                     }
5126                     else {
5127                         // if not in _found_nodes, clear _found_nodes
5128                         handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
5129                     }
5130                 }
5131             }
5132             else {
5133                 // no node was clicked
5134                 _highlight_node = null;
5135             }
5136         }
5137         repaint();
5138     }
5139
5140     final void mouseDragInBrowserPanel( final MouseEvent e ) {
5141         setCursor( MOVE_CURSOR );
5142         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
5143         scroll_position.x -= ( e.getX() - getLastDragPointX() );
5144         scroll_position.y -= ( e.getY() - getLastDragPointY() );
5145         if ( scroll_position.x < 0 ) {
5146             scroll_position.x = 0;
5147         }
5148         else {
5149             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
5150                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
5151             if ( scroll_position.x > max_x ) {
5152                 scroll_position.x = max_x;
5153             }
5154         }
5155         if ( scroll_position.y < 0 ) {
5156             scroll_position.y = 0;
5157         }
5158         else {
5159             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
5160                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
5161             if ( scroll_position.y > max_y ) {
5162                 scroll_position.y = max_y;
5163             }
5164         }
5165         if ( isOvOn() || getOptions().isShowScale() ) {
5166             repaint();
5167         }
5168         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
5169     }
5170
5171     final void mouseDragInOvRectangle( final MouseEvent e ) {
5172         setCursor( HAND_CURSOR );
5173         final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
5174         final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
5175         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
5176         double dx = ( ( w_ratio * e.getX() ) - ( w_ratio * getLastDragPointX() ) );
5177         double dy = ( ( h_ratio * e.getY() ) - ( h_ratio * getLastDragPointY() ) );
5178         scroll_position.x = ForesterUtil.roundToInt( scroll_position.x + dx );
5179         scroll_position.y = ForesterUtil.roundToInt( scroll_position.y + dy );
5180         if ( scroll_position.x <= 0 ) {
5181             scroll_position.x = 0;
5182             dx = 0;
5183         }
5184         else {
5185             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
5186                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
5187             if ( scroll_position.x >= max_x ) {
5188                 dx = 0;
5189                 scroll_position.x = max_x;
5190             }
5191         }
5192         if ( scroll_position.y <= 0 ) {
5193             dy = 0;
5194             scroll_position.y = 0;
5195         }
5196         else {
5197             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
5198                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
5199             if ( scroll_position.y >= max_y ) {
5200                 dy = 0;
5201                 scroll_position.y = max_y;
5202             }
5203         }
5204         repaint();
5205         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
5206         setLastMouseDragPointX( ( float ) ( e.getX() + dx ) );
5207         setLastMouseDragPointY( ( float ) ( e.getY() + dy ) );
5208     }
5209
5210     final void mouseMoved( final MouseEvent e ) {
5211         requestFocusInWindow();
5212         if ( _current_external_nodes != null ) {
5213             _current_external_nodes = null;
5214             repaint();
5215         }
5216         if ( getControlPanel().isNodeDescPopup() ) {
5217             if ( _node_desc_popup != null ) {
5218                 _node_desc_popup.hide();
5219                 _node_desc_popup = null;
5220             }
5221         }
5222         if ( getOptions().isShowOverview() && isOvOn() ) {
5223             if ( inOvVirtualRectangle( e ) ) {
5224                 if ( !isInOvRect() ) {
5225                     setInOvRect( true );
5226                     repaint();
5227                 }
5228             }
5229             else {
5230                 if ( isInOvRect() ) {
5231                     setInOvRect( false );
5232                     repaint();
5233                 }
5234             }
5235         }
5236         if ( inOv( e ) && getOptions().isShowOverview() && isOvOn() ) {
5237             if ( !isInOv() ) {
5238                 setInOv( true );
5239             }
5240         }
5241         else {
5242             if ( isInOv() ) {
5243                 setInOv( false );
5244             }
5245             final PhylogenyNode node = findNode( e.getX(), e.getY() );
5246             if ( ( node != null ) && ( node.isRoot() || !node.getParent().isCollapse() ) ) {
5247                 if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.GET_EXT_DESC_DATA ) ) {
5248                     for( final PhylogenyNode n : node.getAllExternalDescendants() ) {
5249                         addToCurrentExternalNodes( n.getId() );
5250                     }
5251                     setCursor( HAND_CURSOR );
5252                     repaint();
5253                 }
5254                 else if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.CUT_SUBTREE )
5255                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.COPY_SUBTREE )
5256                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.PASTE_SUBTREE )
5257                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.DELETE_NODE_OR_SUBTREE )
5258                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.REROOT )
5259                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.ADD_NEW_NODE ) ) {
5260                     setCursor( CUT_CURSOR );
5261                 }
5262                 else {
5263                     setCursor( HAND_CURSOR );
5264                     if ( getControlPanel().isNodeDescPopup() ) {
5265                         showNodeDataPopup( e, node );
5266                     }
5267                 }
5268             }
5269             else {
5270                 setCursor( ARROW_CURSOR );
5271             }
5272         }
5273     }
5274
5275     final void mouseReleasedInBrowserPanel( final MouseEvent e ) {
5276         setCursor( ARROW_CURSOR );
5277     }
5278
5279     final void multiplyUrtFactor( final float f ) {
5280         _urt_factor *= f;
5281     }
5282
5283     final JApplet obtainApplet() {
5284         return ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5285     }
5286
5287     final void paintBranchCircular( final PhylogenyNode p,
5288                                     final PhylogenyNode c,
5289                                     final Graphics2D g,
5290                                     final boolean radial_labels,
5291                                     final boolean to_pdf,
5292                                     final boolean to_graphics_file ) {
5293         final double angle = _urt_nodeid_angle_map.get( c.getId() );
5294         final double root_x = _root.getXcoord();
5295         final double root_y = _root.getYcoord();
5296         final double dx = root_x - p.getXcoord();
5297         final double dy = root_y - p.getYcoord();
5298         final double parent_radius = Math.sqrt( ( dx * dx ) + ( dy * dy ) );
5299         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
5300         assignGraphicsForBranchWithColorForParentBranch( c, false, g, to_pdf, to_graphics_file );
5301         if ( ( c.isFirstChildNode() || c.isLastChildNode() )
5302                 && ( ( Math.abs( parent_radius * arc ) > 1.5 ) || to_pdf || to_graphics_file ) ) {
5303             final double r2 = 2.0 * parent_radius;
5304             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
5305         }
5306         drawLine( c.getXcoord(),
5307                   c.getYcoord(),
5308                   root_x + ( Math.cos( angle ) * parent_radius ),
5309                   root_y + ( Math.sin( angle ) * parent_radius ),
5310                   g );
5311         paintNodeBox( c.getXcoord(), c.getYcoord(), c, g, to_pdf, to_graphics_file );
5312         if ( c.isExternal() ) {
5313             final boolean is_in_found_nodes = isInFoundNodes0( c ) || isInFoundNodes1( c )
5314                     || isInCurrentExternalNodes( c );
5315             if ( ( _dynamic_hiding_factor > 1 ) && !is_in_found_nodes
5316                     && ( ( _urt_nodeid_index_map.get( c.getId() ) % _dynamic_hiding_factor ) != 1 ) ) {
5317                 return;
5318             }
5319             paintNodeDataUnrootedCirc( g, c, to_pdf, to_graphics_file, radial_labels, 0, is_in_found_nodes );
5320         }
5321     }
5322
5323     final void paintBranchCircularLite( final PhylogenyNode p, final PhylogenyNode c, final Graphics2D g ) {
5324         final double angle = _urt_nodeid_angle_map.get( c.getId() );
5325         final double root_x = _root.getXSecondary();
5326         final double root_y = _root.getYSecondary();
5327         final double dx = root_x - p.getXSecondary();
5328         final double dy = root_y - p.getYSecondary();
5329         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
5330         final double parent_radius = Math.sqrt( ( dx * dx ) + ( dy * dy ) );
5331         g.setColor( getTreeColorSet().getOvColor() );
5332         if ( ( c.isFirstChildNode() || c.isLastChildNode() ) && ( Math.abs( arc ) > 0.02 ) ) {
5333             final double r2 = 2.0 * parent_radius;
5334             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
5335         }
5336         drawLine( c.getXSecondary(),
5337                   c.getYSecondary(),
5338                   root_x + ( Math.cos( angle ) * parent_radius ),
5339                   root_y + ( Math.sin( angle ) * parent_radius ),
5340                   g );
5341         if ( isInFoundNodes( c ) || isInCurrentExternalNodes( c ) ) {
5342             g.setColor( getColorForFoundNode( c ) );
5343             drawRectFilled( c.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, c.getYSecondary()
5344                             - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF, OVERVIEW_FOUND_NODE_BOX_SIZE, OVERVIEW_FOUND_NODE_BOX_SIZE, g );
5345         }
5346     }
5347
5348     final void paintCircular( final Phylogeny phy,
5349                               final double starting_angle,
5350                               final int center_x,
5351                               final int center_y,
5352                               final int radius,
5353                               final Graphics2D g,
5354                               final boolean to_pdf,
5355                               final boolean to_graphics_file ) {
5356         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes() - _collapsed_external_nodeid_set.size();
5357         System.out.println( "# collapsed external = " + _collapsed_external_nodeid_set.size() );
5358         _root = phy.getRoot();
5359         _root.setXcoord( center_x );
5360         _root.setYcoord( center_y );
5361         final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
5362         double current_angle = starting_angle;
5363         int i = 0;
5364         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
5365             final PhylogenyNode n = it.next();
5366             if ( !n.isCollapse() ) {
5367                 n.setXcoord( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
5368                 n.setYcoord( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
5369                 _urt_nodeid_angle_map.put( n.getId(), current_angle );
5370                 _urt_nodeid_index_map.put( n.getId(), i++ );
5371                 current_angle += ( TWO_PI / circ_num_ext_nodes );
5372             }
5373             else {
5374                 //TODO remove me
5375                 System.out.println( "is collapse" + n.getName() );
5376             }
5377         }
5378         paintCirculars( phy.getRoot(), phy, center_x, center_y, radius, radial_labels, g, to_pdf, to_graphics_file );
5379         paintNodeBox( _root.getXcoord(), _root.getYcoord(), _root, g, to_pdf, to_graphics_file );
5380     }
5381
5382     final void paintCircularLite( final Phylogeny phy,
5383                                   final double starting_angle,
5384                                   final int center_x,
5385                                   final int center_y,
5386                                   final int radius,
5387                                   final Graphics2D g ) {
5388         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes();
5389         _root = phy.getRoot();
5390         _root.setXSecondary( center_x );
5391         _root.setYSecondary( center_y );
5392         double current_angle = starting_angle;
5393         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
5394             final PhylogenyNode n = it.next();
5395             n.setXSecondary( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
5396             n.setYSecondary( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
5397             _urt_nodeid_angle_map.put( n.getId(), current_angle );
5398             current_angle += ( TWO_PI / circ_num_ext_nodes );
5399         }
5400         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
5401     }
5402
5403     final void paintPhylogeny( final Graphics2D g,
5404                                final boolean to_pdf,
5405                                final boolean to_graphics_file,
5406                                final int graphics_file_width,
5407                                final int graphics_file_height,
5408                                final int graphics_file_x,
5409                                final int graphics_file_y ) {
5410         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5411             return;
5412         }
5413         if ( _control_panel.isShowSequenceRelations() ) {
5414             _query_sequence = _control_panel.getSelectedQuerySequence();
5415         }
5416         // Color the background
5417         if ( !to_pdf ) {
5418             final Rectangle r = getVisibleRect();
5419             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
5420                 g.setColor( getTreeColorSet().getBackgroundColor() );
5421                 if ( !to_graphics_file ) {
5422                     g.fill( r );
5423                 }
5424                 else {
5425                     if ( getOptions().isPrintBlackAndWhite() ) {
5426                         g.setColor( Color.WHITE );
5427                     }
5428                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
5429                 }
5430             }
5431             else {
5432                 if ( !to_graphics_file ) {
5433                     g.setPaint( new GradientPaint( r.x, r.y, getTreeColorSet().getBackgroundColor(), r.x, r.y
5434                                                    + r.height, getTreeColorSet().getBackgroundColorGradientBottom() ) );
5435                     g.fill( r );
5436                 }
5437                 else {
5438                     g.setPaint( new GradientPaint( graphics_file_x,
5439                                                    graphics_file_y,
5440                                                    getTreeColorSet().getBackgroundColor(),
5441                                                    graphics_file_x,
5442                                                    graphics_file_y + graphics_file_height,
5443                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
5444                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
5445                 }
5446             }
5447             setupStroke( g );
5448         }
5449         else {
5450             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
5451         }
5452         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
5453                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5454             _external_node_index = 0;
5455             // Position starting X of tree
5456             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/) {
5457                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
5458             }
5459             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
5460                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE + ( _phylogeny.getRoot()
5461                         .getDistanceToParent() * getXcorrectionFactor() ) ) );
5462             }
5463             else {
5464                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
5465             }
5466             // Position starting Y of tree
5467             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
5468                                             + ( TreePanel.MOVE / 2.0f ) );
5469             final int dynamic_hiding_factor = calcDynamicHidingFactor();
5470             if ( getControlPanel().isDynamicallyHideData() ) {
5471                 if ( dynamic_hiding_factor > 1 ) {
5472                     getControlPanel().setDynamicHidingIsOn( true );
5473                 }
5474                 else {
5475                     getControlPanel().setDynamicHidingIsOn( false );
5476                 }
5477             }
5478             if ( _nodes_in_preorder == null ) {
5479                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
5480                 int i = 0;
5481                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
5482                     _nodes_in_preorder[ i++ ] = it.next();
5483                 }
5484             }
5485             final boolean disallow_shortcutting = ( dynamic_hiding_factor < 40 )
5486                     || getControlPanel().isUseVisualStyles() || getOptions().isShowDefaultNodeShapesForMarkedNodes()
5487                     || ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() )
5488                     || ( ( getFoundNodes1() != null ) && !getFoundNodes1().isEmpty() )
5489                     || ( ( getCurrentExternalNodes() != null ) && !getCurrentExternalNodes().isEmpty() )
5490                     || to_graphics_file || to_pdf;
5491             for( final PhylogenyNode element : _nodes_in_preorder ) {
5492                 paintNodeRectangular( g,
5493                                       element,
5494                                       to_pdf,
5495                                       getControlPanel().isDynamicallyHideData() && ( dynamic_hiding_factor > 1 ),
5496                                       dynamic_hiding_factor,
5497                                       to_graphics_file,
5498                                       disallow_shortcutting );
5499             }
5500             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
5501                 if ( !( to_graphics_file || to_pdf ) ) {
5502                     paintScale( g,
5503                                 getVisibleRect().x,
5504                                 getVisibleRect().y + getVisibleRect().height,
5505                                 to_pdf,
5506                                 to_graphics_file );
5507                 }
5508                 else {
5509                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
5510                 }
5511             }
5512             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5513                 paintPhylogenyLite( g );
5514             }
5515         }
5516         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5517             if ( getControlPanel().getDynamicallyHideData() != null ) {
5518                 getControlPanel().setDynamicHidingIsOn( false );
5519             }
5520             final double angle = getStartingAngle();
5521             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
5522             _dynamic_hiding_factor = 0;
5523             if ( getControlPanel().isDynamicallyHideData() ) {
5524                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5 * getPhylogeny()
5525                         .getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
5526             }
5527             if ( getControlPanel().getDynamicallyHideData() != null ) {
5528                 if ( _dynamic_hiding_factor > 1 ) {
5529                     getControlPanel().setDynamicHidingIsOn( true );
5530                 }
5531                 else {
5532                     getControlPanel().setDynamicHidingIsOn( false );
5533                 }
5534             }
5535             paintUnrooted( _phylogeny.getRoot(),
5536                            angle,
5537                            ( float ) ( angle + ( 2 * Math.PI ) ),
5538                            radial_labels,
5539                            g,
5540                            to_pdf,
5541                            to_graphics_file );
5542             if ( getOptions().isShowScale() ) {
5543                 if ( !( to_graphics_file || to_pdf ) ) {
5544                     paintScale( g,
5545                                 getVisibleRect().x,
5546                                 getVisibleRect().y + getVisibleRect().height,
5547                                 to_pdf,
5548                                 to_graphics_file );
5549                 }
5550                 else {
5551                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
5552                 }
5553             }
5554             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5555                 g.setColor( getTreeColorSet().getOvColor() );
5556                 paintUnrootedLite( _phylogeny.getRoot(),
5557                                    angle,
5558                                    angle + ( 2 * Math.PI ),
5559                                    g,
5560                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
5561                 paintOvRectangle( g );
5562             }
5563         }
5564         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
5565             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() ) / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
5566             final int d = radius + MOVE + getLongestExtNodeInfo();
5567             _dynamic_hiding_factor = 0;
5568             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
5569                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5 * getPhylogeny()
5570                         .getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
5571             }
5572             if ( getControlPanel().getDynamicallyHideData() != null ) {
5573                 if ( _dynamic_hiding_factor > 1 ) {
5574                     getControlPanel().setDynamicHidingIsOn( true );
5575                 }
5576                 else {
5577                     getControlPanel().setDynamicHidingIsOn( false );
5578                 }
5579             }
5580             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
5581             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5582                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
5583                         : getOvMaxWidth() / 2 );
5584                 double x_scale = 1.0;
5585                 double y_scale = 1.0;
5586                 int x_pos = getVisibleRect().x + getOvXPosition();
5587                 int y_pos = getVisibleRect().y + getOvYPosition();
5588                 if ( getWidth() > getHeight() ) {
5589                     x_scale = ( double ) getHeight() / getWidth();
5590                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
5591                 }
5592                 else {
5593                     y_scale = ( double ) getWidth() / getHeight();
5594                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
5595                 }
5596                 _at = g.getTransform();
5597                 g.scale( x_scale, y_scale );
5598                 paintCircularLite( _phylogeny,
5599                                    getStartingAngle(),
5600                                    x_pos + radius_ov,
5601                                    y_pos + radius_ov,
5602                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo() / ( getVisibleRect().width / getOvRectangle()
5603                                            .getWidth() ) ) ),
5604                                            g );
5605                 g.setTransform( _at );
5606                 paintOvRectangle( g );
5607             }
5608         }
5609     }
5610
5611     final void recalculateMaxDistanceToRoot() {
5612         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
5613     }
5614
5615     /**
5616      * Remove all edit-node frames
5617      */
5618     final void removeAllEditNodeJFrames() {
5619         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
5620             if ( _node_frames[ i ] != null ) {
5621                 _node_frames[ i ].dispose();
5622                 _node_frames[ i ] = null;
5623             }
5624         }
5625         _node_frame_index = 0;
5626     }
5627
5628     /**
5629      * Remove a node-edit frame.
5630      */
5631     final void removeEditNodeFrame( final int i ) {
5632         _node_frame_index--;
5633         _node_frames[ i ] = null;
5634         if ( i < _node_frame_index ) {
5635             for( int j = 0; j < ( _node_frame_index - 1 ); j++ ) {
5636                 _node_frames[ j ] = _node_frames[ j + 1 ];
5637             }
5638             _node_frames[ _node_frame_index ] = null;
5639         }
5640     }
5641
5642     final void reRoot( final PhylogenyNode node ) {
5643         if ( !getPhylogeny().isRerootable() ) {
5644             JOptionPane.showMessageDialog( this,
5645                                            "This is not rerootable",
5646                                            "Not rerootable",
5647                                            JOptionPane.WARNING_MESSAGE );
5648             return;
5649         }
5650         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5651             JOptionPane.showMessageDialog( this,
5652                                            "Cannot reroot in unrooted display type",
5653                                            "Attempt to reroot tree in unrooted display",
5654                                            JOptionPane.WARNING_MESSAGE );
5655             return;
5656         }
5657         getPhylogeny().reRoot( node );
5658         getPhylogeny().recalculateNumberOfExternalDescendants( true );
5659         resetNodeIdToDistToLeafMap();
5660         setNodeInPreorderToNull();
5661         resetPreferredSize();
5662         getMainPanel().adjustJScrollPane();
5663         setEdited( true );
5664         repaint();
5665         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
5666             getControlPanel().showWhole();
5667         }
5668     }
5669
5670     final void resetNodeIdToDistToLeafMap() {
5671         _nodeid_dist_to_leaf = new HashMap<Long, Short>();
5672     }
5673
5674     final void resetPreferredSize() {
5675         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
5676             return;
5677         }
5678         int x = 0;
5679         int y = 0;
5680         y = TreePanel.MOVE
5681                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
5682         if ( getControlPanel().isDrawPhylogram() ) {
5683             x = TreePanel.MOVE
5684                     + getLongestExtNodeInfo()
5685                     + ForesterUtil
5686                     .roundToInt( ( getXcorrectionFactor() * getPhylogeny().getHeight() ) + getXdistance() );
5687             
5688         }
5689         else {
5690             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
5691                 x = TreePanel.MOVE
5692                         + getLongestExtNodeInfo()
5693                         + ForesterUtil.roundToInt( getXdistance()
5694                                                    * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
5695             }
5696             else {
5697                 x = TreePanel.MOVE
5698                         + getLongestExtNodeInfo()
5699                         + ForesterUtil.roundToInt( getXdistance()
5700                                                    * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
5701             }
5702         }
5703         setPreferredSize( new Dimension( x, y ) );
5704     }
5705
5706     final void selectNode( final PhylogenyNode node ) {
5707         if ( ( getFoundNodes0() != null ) && getFoundNodes0().contains( node.getId() ) ) {
5708             getFoundNodes0().remove( node.getId() );
5709             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
5710             if ( getFoundNodes0().size() < 1 ) {
5711                 getControlPanel().searchReset0();
5712             }
5713         }
5714         else {
5715             getControlPanel().getSearchFoundCountsLabel0().setVisible( true );
5716             getControlPanel().getSearchResetButton0().setEnabled( true );
5717             getControlPanel().getSearchResetButton0().setVisible( true );
5718             if ( getFoundNodes0() == null ) {
5719                 setFoundNodes0( new HashSet<Long>() );
5720             }
5721             getFoundNodes0().add( node.getId() );
5722             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
5723         }
5724     }
5725
5726     final void setArrowCursor() {
5727         setCursor( ARROW_CURSOR );
5728         repaint();
5729     }
5730
5731     final void setControlPanel( final ControlPanel atv_control ) {
5732         _control_panel = atv_control;
5733     }
5734
5735     void setCurrentExternalNodesDataBuffer( final StringBuilder sb ) {
5736         increaseCurrentExternalNodesDataBufferChangeCounter();
5737         _current_external_nodes_data_buffer = sb;
5738     }
5739
5740     final void setFoundNodes0( final Set<Long> found_nodes ) {
5741         _found_nodes_0 = found_nodes;
5742     }
5743
5744     final void setFoundNodes1( final Set<Long> found_nodes ) {
5745         _found_nodes_1 = found_nodes;
5746     }
5747
5748     final void setInOvRect( final boolean in_ov_rect ) {
5749         _in_ov_rect = in_ov_rect;
5750     }
5751
5752     final void setLargeFonts() {
5753         getTreeFontSet().largeFonts();
5754     }
5755
5756     final void setLastMouseDragPointX( final float x ) {
5757         _last_drag_point_x = x;
5758     }
5759
5760     final void setLastMouseDragPointY( final float y ) {
5761         _last_drag_point_y = y;
5762     }
5763
5764     final void setMediumFonts() {
5765         getTreeFontSet().mediumFonts();
5766     }
5767
5768     final void setNodeInPreorderToNull() {
5769         _nodes_in_preorder = null;
5770     }
5771
5772     final void setOvOn( final boolean ov_on ) {
5773         _ov_on = ov_on;
5774     }
5775
5776     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
5777         _graphics_type = graphics_type;
5778         setTextAntialias();
5779     }
5780
5781     final void setSmallFonts() {
5782         getTreeFontSet().smallFonts();
5783     }
5784
5785     final void setStartingAngle( final double starting_angle ) {
5786         _urt_starting_angle = starting_angle;
5787     }
5788
5789     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
5790         _statistics_for_vector_data = statistics_for_expression_values;
5791     }
5792
5793     final void setSuperTinyFonts() {
5794         getTreeFontSet().superTinyFonts();
5795     }
5796
5797     final void setTextAntialias() {
5798         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
5799             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
5800                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
5801             }
5802             else {
5803                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
5804             }
5805         }
5806         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
5807             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
5808             // try {
5809             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
5810             // }
5811             // catch ( final Throwable e ) {
5812             //    _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
5813             //}
5814         }
5815         else {
5816             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
5817             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
5818         }
5819     }
5820
5821     final void setTinyFonts() {
5822         getTreeFontSet().tinyFonts();
5823     }
5824
5825     final void setTreeFile( final File treefile ) {
5826         _treefile = treefile;
5827     }
5828
5829     final void setXcorrectionFactor( final float f ) {
5830         _x_correction_factor = f;
5831     }
5832
5833     final void setXdistance( final float x ) {
5834         _x_distance = x;
5835     }
5836
5837     final void setYdistance( final float y ) {
5838         _y_distance = y;
5839     }
5840
5841     final void sortDescendants( final PhylogenyNode node ) {
5842         if ( !node.isExternal() ) {
5843             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
5844             if ( getControlPanel().isShowTaxonomyScientificNames() || getControlPanel().isShowTaxonomyCode() ) {
5845                 pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
5846             }
5847             else if ( getControlPanel().isShowSeqNames() || getControlPanel().isShowSeqSymbols()
5848                     || getControlPanel().isShowGeneNames() ) {
5849                 pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
5850             }
5851             PhylogenyMethods.sortNodeDescendents( node, pri );
5852             setNodeInPreorderToNull();
5853             _phylogeny.externalNodesHaveChanged();
5854             _phylogeny.clearHashIdToNodeMap();
5855             _phylogeny.recalculateNumberOfExternalDescendants( true );
5856             resetNodeIdToDistToLeafMap();
5857             setEdited( true );
5858         }
5859         repaint();
5860     }
5861
5862     final void subTree( final PhylogenyNode node ) {
5863         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5864             JOptionPane.showMessageDialog( this,
5865                                            "Cannot get a sub/super tree in unrooted display",
5866                                            "Attempt to get sub/super tree in unrooted display",
5867                                            JOptionPane.WARNING_MESSAGE );
5868             return;
5869         }
5870         if ( node.isExternal() ) {
5871             JOptionPane.showMessageDialog( this,
5872                                            "Cannot get a subtree of a external node",
5873                                            "Attempt to get subtree of external node",
5874                                            JOptionPane.WARNING_MESSAGE );
5875             return;
5876         }
5877         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
5878             JOptionPane.showMessageDialog( this,
5879                                            "Cannot get a subtree of the root node",
5880                                            "Attempt to get subtree of root node",
5881                                            JOptionPane.WARNING_MESSAGE );
5882             return;
5883         }
5884         setNodeInPreorderToNull();
5885         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
5886             _sub_phylogenies[ _subtree_index ] = _phylogeny;
5887             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
5888             ++_subtree_index;
5889             _phylogeny = TreePanelUtil.subTree( node, _phylogeny );
5890             updateSubSuperTreeButton();
5891         }
5892         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
5893             superTree();
5894         }
5895         _main_panel.getControlPanel().showWhole();
5896         repaint();
5897     }
5898
5899     final void superTree() {
5900         setNodeInPreorderToNull();
5901         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
5902         for( final PhylogenyNode n : temp_root.getDescendants() ) {
5903             n.setParent( temp_root );
5904         }
5905         _sub_phylogenies[ _subtree_index ] = null;
5906         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
5907         _phylogeny = _sub_phylogenies[ --_subtree_index ];
5908         updateSubSuperTreeButton();
5909     }
5910
5911     final void swap( final PhylogenyNode node ) {
5912         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
5913             return;
5914         }
5915         if ( node.getNumberOfDescendants() > 2 ) {
5916             JOptionPane.showMessageDialog( this,
5917                                            "Cannot swap descendants of nodes with more than 2 descendants",
5918                                            "Cannot swap descendants",
5919                                            JOptionPane.ERROR_MESSAGE );
5920             return;
5921         }
5922         if ( !node.isExternal() ) {
5923             node.swapChildren();
5924             setNodeInPreorderToNull();
5925             _phylogeny.externalNodesHaveChanged();
5926             _phylogeny.clearHashIdToNodeMap();
5927             _phylogeny.recalculateNumberOfExternalDescendants( true );
5928             resetNodeIdToDistToLeafMap();
5929             setEdited( true );
5930         }
5931         repaint();
5932     }
5933
5934     final void taxColor() {
5935         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5936             return;
5937         }
5938         setWaitCursor();
5939         TreePanelUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
5940         _control_panel.setColorBranches( true );
5941         if ( _control_panel.getUseVisualStylesCb() != null ) {
5942             _control_panel.getUseVisualStylesCb().setSelected( true );
5943         }
5944         setEdited( true );
5945         setArrowCursor();
5946         repaint();
5947     }
5948
5949     final void updateOvSettings() {
5950         switch ( getOptions().getOvPlacement() ) {
5951             case LOWER_LEFT:
5952                 setOvXPosition( OV_BORDER );
5953                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5954                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5955                 break;
5956             case LOWER_RIGHT:
5957                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5958                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5959                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5960                 break;
5961             case UPPER_RIGHT:
5962                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5963                 setOvYPosition( OV_BORDER );
5964                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5965                 break;
5966             default:
5967                 setOvXPosition( OV_BORDER );
5968                 setOvYPosition( OV_BORDER );
5969                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5970                 break;
5971         }
5972     }
5973
5974     final void updateOvSizes() {
5975         if ( ( getWidth() > ( 1.05 * getVisibleRect().width ) ) || ( getHeight() > ( 1.05 * getVisibleRect().height ) ) ) {
5976             setOvOn( true );
5977             float l = getLongestExtNodeInfo();
5978             final float w_ratio = getOvMaxWidth() / getWidth();
5979             l *= w_ratio;
5980             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
5981             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
5982             float ov_xdist = 0;
5983             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
5984                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
5985             }
5986             else {
5987                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
5988             }
5989             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
5990             if ( ov_xdist < 0.0 ) {
5991                 ov_xdist = 0.0f;
5992             }
5993             if ( ydist < 0.0 ) {
5994                 ydist = 0.0f;
5995             }
5996             setOvXDistance( ov_xdist );
5997             final double height = _phylogeny.getHeight();
5998             if ( height > 0 ) {
5999                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
6000                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
6001             }
6002             else {
6003                 setOvXcorrectionFactor( 0 );
6004             }
6005         }
6006         else {
6007             setOvOn( false );
6008         }
6009     }
6010
6011     void updateSetOfCollapsedExternalNodes() {
6012         final Phylogeny phy = getPhylogeny();
6013         _collapsed_external_nodeid_set.clear();
6014         if ( phy != null ) {
6015             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
6016                 final PhylogenyNode ext_node = it.next();
6017                 PhylogenyNode n = ext_node;
6018                 while ( !n.isRoot() ) {
6019                     if ( n.isCollapse() ) {
6020                         _collapsed_external_nodeid_set.add( ext_node.getId() );
6021                         ext_node.setCollapse( true );
6022                         continue E;
6023                     }
6024                     n = n.getParent();
6025                 }
6026             }
6027         }
6028     }
6029
6030     final void updateSubSuperTreeButton() {
6031         if ( _subtree_index < 1 ) {
6032             getControlPanel().deactivateButtonToReturnToSuperTree();
6033         }
6034         else {
6035             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
6036         }
6037     }
6038
6039     final void zoomInDomainStructure() {
6040         if ( _domain_structure_width < 2000 ) {
6041             _domain_structure_width *= 1.2;
6042         }
6043     }
6044
6045     final void zoomOutDomainStructure() {
6046         if ( _domain_structure_width > 20 ) {
6047             _domain_structure_width *= 0.8;
6048         }
6049     }
6050
6051     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
6052         if ( node.getNodeData().getNodeVisualData() == null ) {
6053             node.getNodeData().setNodeVisualData( new NodeVisualData() );
6054         }
6055         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
6056     }
6057
6058     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
6059         g.drawString( str, x, y );
6060     }
6061
6062     final private static boolean plusPressed( final int key_code ) {
6063         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
6064                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
6065     }
6066 }