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