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