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