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