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