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