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 + 50 //TODO why plus 50?
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 + 50 ),
3504                                         node.getYcoord() - ( h / 2.0f ),
3505                                         g,
3506                                         this,
3507                                         to_pdf );
3508                         }
3509                     }
3510                     else {
3511                         rds.render( node.getXcoord() + x, node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
3512                     }
3513                 }
3514                 else {
3515                     if ( getOptions().isRightLineUpDomains() ) {
3516                         rds.render( ( ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text )
3517                                 - 20 ) + ( ( _longest_domain - rds.getTotalLength() ) * rds.getRenderingFactorWidth() ),
3518                                     node.getYcoord() - ( h / 2.0f ),
3519                                     g,
3520                                     this,
3521                                     to_pdf );
3522                     }
3523                     else {
3524                         rds.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
3525                                     node.getYcoord() - ( h / 2.0f ),
3526                                     g,
3527                                     this,
3528                                     to_pdf );
3529                     }
3530                 }
3531             }
3532         }
3533         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
3534                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
3535             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
3536                                                                          getStatisticsForExpressionValues(),
3537                                                                          getConfiguration() );
3538             if ( rv != null ) {
3539                 double domain_add = 0;
3540                 if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3541                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
3542                     domain_add = _domain_structure_width + 10;
3543                 }
3544                 if ( getControlPanel().isDrawPhylogram() ) {
3545                     rv.render( ( float ) ( node.getXcoord() + x + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
3546                 }
3547                 else {
3548                     rv.render( ( float ) ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text
3549                             + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
3550                 }
3551             }
3552         }
3553         //if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
3554         //        && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
3555         //        && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
3556         //   paintMolecularSequences( g, node, to_pdf );
3557         //}
3558     }
3559
3560     final private void paintOvRectangle( final Graphics2D g ) {
3561         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
3562         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
3563         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
3564         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
3565         final float width = getOvMaxWidth() / w_ratio;
3566         final float height = getOvMaxHeight() / h_ratio;
3567         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
3568         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
3569         g.setColor( getTreeColorSet().getFoundColor0() );
3570         getOvRectangle().setRect( x, y, width, height );
3571         final Stroke s = g.getStroke();
3572         g.setStroke( STROKE_1 );
3573         if ( ( width < 6 ) && ( height < 6 ) ) {
3574             drawRectFilled( x, y, 6, 6, g );
3575             getOvVirtualRectangle().setRect( x, y, 6, 6 );
3576         }
3577         else if ( width < 6 ) {
3578             drawRectFilled( x, y, 6, height, g );
3579             getOvVirtualRectangle().setRect( x, y, 6, height );
3580         }
3581         else if ( height < 6 ) {
3582             drawRectFilled( x, y, width, 6, g );
3583             getOvVirtualRectangle().setRect( x, y, width, 6 );
3584         }
3585         else {
3586             drawRect( x, y, width, height, g );
3587             if ( isInOvRect() ) {
3588                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
3589             }
3590             getOvVirtualRectangle().setRect( x, y, width, height );
3591         }
3592         g.setStroke( s );
3593     }
3594
3595     final private void paintPhylogenyLite( final Graphics2D g ) {
3596         _phylogeny.getRoot().setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition()
3597                 + ( MOVE / ( getVisibleRect().width / getOvRectangle().getWidth() ) ) ) );
3598         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
3599         final Stroke s = g.getStroke();
3600         g.setStroke( STROKE_05 );
3601         for( final PhylogenyNode element : _nodes_in_preorder ) {
3602             paintNodeLite( g, element );
3603         }
3604         g.setStroke( s );
3605         paintOvRectangle( g );
3606     }
3607
3608     /**
3609      * Paint the root branch. (Differs from others because it will always be a
3610      * single horizontal line).
3611      * @param to_graphics_file
3612      *
3613      * @return new x1 value
3614      */
3615     final private void paintRootBranch( final Graphics2D g,
3616                                         final float x1,
3617                                         final float y1,
3618                                         final PhylogenyNode root,
3619                                         final boolean to_pdf,
3620                                         final boolean to_graphics_file ) {
3621         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
3622         float d = getXdistance();
3623         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
3624             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
3625         }
3626         if ( d < MIN_ROOT_LENGTH ) {
3627             d = MIN_ROOT_LENGTH;
3628         }
3629         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
3630             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
3631         }
3632         else {
3633             final double w = PhylogenyMethods.getBranchWidthValue( root );
3634             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
3635         }
3636         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file );
3637     }
3638
3639     final private void paintScale( final Graphics2D g,
3640                                    int x1,
3641                                    int y1,
3642                                    final boolean to_pdf,
3643                                    final boolean to_graphics_file ) {
3644         x1 += MOVE;
3645         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
3646         y1 -= 12;
3647         final int y2 = y1 - 8;
3648         final int y3 = y1 - 4;
3649         g.setFont( getTreeFontSet().getSmallFont() );
3650         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3651             g.setColor( Color.BLACK );
3652         }
3653         else {
3654             g.setColor( getTreeColorSet().getBranchLengthColor() );
3655         }
3656         final Stroke s = g.getStroke();
3657         g.setStroke( STROKE_1 );
3658         drawLine( x1, y1, x1, y2, g );
3659         drawLine( x2, y1, x2, y2, g );
3660         drawLine( x1, y3, x2, y3, g );
3661         if ( getScaleLabel() != null ) {
3662             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
3663         }
3664         g.setStroke( s );
3665     }
3666
3667     final private int paintTaxonomy( final Graphics2D g,
3668                                      final PhylogenyNode node,
3669                                      final boolean is_in_found_nodes,
3670                                      final boolean to_pdf,
3671                                      final boolean to_graphics_file,
3672                                      final float x_shift ) {
3673         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3674         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
3675         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getTaxonomyColor() );
3676         float start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
3677         if ( getControlPanel().getTreeDisplayType() == Options.PHYLOGENY_DISPLAY_TYPE.ALIGNED_PHYLOGRAM
3678                 && node.isExternal() ) {
3679             start_x = ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
3680                     + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift + ( 2 * TreePanel.MOVE ) + getXdistance()
3681                     + 3 );
3682         }
3683         float start_y;
3684         if ( !using_visual_font ) {
3685             start_y = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent()
3686                     / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
3687         }
3688         else {
3689             start_y = node.getYcoord()
3690                     + ( getFontMetrics( g.getFont() ).getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
3691         }
3692         _sb.setLength( 0 );
3693         nodeTaxonomyDataAsSB( taxonomy, _sb );
3694         final String label = _sb.toString();
3695         /* GUILHEM_BEG */
3696         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
3697                 && ( node.getNodeData().isHasSequence() )
3698                 && node.getNodeData().getSequence().equals( _query_sequence ) ) {
3699             // invert font color and background color to show that this is the query sequence
3700             final Rectangle2D nodeTextBounds = new TextLayout( label,
3701                                                                g.getFont(),
3702                                                                new FontRenderContext( null, false, false ) )
3703                                                                        .getBounds();
3704             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
3705             g.setColor( getTreeColorSet().getBackgroundColor() );
3706         }
3707         /* GUILHEM_END */
3708         TreePanel.drawString( label, start_x, start_y, g );
3709         if ( !using_visual_font && !is_in_found_nodes ) {
3710             return getFontMetricsForLargeDefaultFont().stringWidth( label );
3711         }
3712         return getFontMetrics( g.getFont() ).stringWidth( label );
3713     }
3714
3715     final private void paintUnrooted( final PhylogenyNode n,
3716                                       final double low_angle,
3717                                       final double high_angle,
3718                                       final boolean radial_labels,
3719                                       final Graphics2D g,
3720                                       final boolean to_pdf,
3721                                       final boolean to_graphics_file ) {
3722         if ( n.isRoot() ) {
3723             n.setXcoord( getWidth() / 2 );
3724             n.setYcoord( getHeight() / 2 );
3725         }
3726         if ( n.isExternal() ) {
3727             paintNodeDataUnrootedCirc( g,
3728                                        n,
3729                                        to_pdf,
3730                                        to_graphics_file,
3731                                        radial_labels,
3732                                        ( high_angle + low_angle ) / 2,
3733                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
3734             return;
3735         }
3736         final float num_enclosed = n.getNumberOfExternalNodes();
3737         final float x = n.getXcoord();
3738         final float y = n.getYcoord();
3739         double current_angle = low_angle;
3740         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
3741         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
3742         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
3743         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
3744         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
3745             final PhylogenyNode desc = n.getChildNode( i );
3746             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
3747             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
3748             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
3749             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
3750             //     continue;
3751             // }
3752             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
3753             //    continue;
3754             //}
3755             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
3756             //    continue;
3757             // }
3758             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
3759             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
3760             float length;
3761             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
3762                 if ( desc.getDistanceToParent() < 0 ) {
3763                     length = 0;
3764                 }
3765                 else {
3766                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
3767                 }
3768             }
3769             else {
3770                 length = getUrtFactor();
3771             }
3772             final double mid_angle = current_angle + ( arc_size / 2 );
3773             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
3774             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
3775             desc.setXcoord( new_x );
3776             desc.setYcoord( new_y );
3777             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
3778             current_angle += arc_size;
3779             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
3780             drawLine( x, y, new_x, new_y, g );
3781             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file );
3782         }
3783         if ( n.isRoot() ) {
3784             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file );
3785         }
3786     }
3787
3788     final private void paintUnrootedLite( final PhylogenyNode n,
3789                                           final double low_angle,
3790                                           final double high_angle,
3791                                           final Graphics2D g,
3792                                           final float urt_ov_factor ) {
3793         if ( n.isRoot() ) {
3794             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
3795             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
3796             n.setXSecondary( x_pos );
3797             n.setYSecondary( y_pos );
3798         }
3799         if ( n.isExternal() ) {
3800             return;
3801         }
3802         final float num_enclosed = n.getNumberOfExternalNodes();
3803         final float x = n.getXSecondary();
3804         final float y = n.getYSecondary();
3805         double current_angle = low_angle;
3806         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
3807             final PhylogenyNode desc = n.getChildNode( i );
3808             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
3809             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
3810             float length;
3811             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
3812                 if ( desc.getDistanceToParent() < 0 ) {
3813                     length = 0;
3814                 }
3815                 else {
3816                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
3817                 }
3818             }
3819             else {
3820                 length = urt_ov_factor;
3821             }
3822             final double mid_angle = current_angle + ( arc_size / 2 );
3823             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
3824             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
3825             desc.setXSecondary( new_x );
3826             desc.setYSecondary( new_y );
3827             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
3828                 g.setColor( getColorForFoundNode( desc ) );
3829                 drawRectFilled( desc.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3830                                 desc.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3831                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
3832                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
3833                                 g );
3834                 g.setColor( getTreeColorSet().getOvColor() );
3835             }
3836             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
3837             current_angle += arc_size;
3838             drawLine( x, y, new_x, new_y, g );
3839         }
3840     }
3841
3842     final private void pasteSubtree( final PhylogenyNode node ) {
3843         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
3844             errorMessageNoCutCopyPasteInUnrootedDisplay();
3845             return;
3846         }
3847         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
3848             JOptionPane.showMessageDialog( this,
3849                                            "No tree in buffer (need to copy or cut a subtree first)",
3850                                            "Attempt to paste with empty buffer",
3851                                            JOptionPane.ERROR_MESSAGE );
3852             return;
3853         }
3854         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
3855         final Object[] options = { "As sibling", "As descendant", "Cancel" };
3856         final int r = JOptionPane.showOptionDialog( this,
3857                                                     "How to paste subtree" + label + "?",
3858                                                     "Paste Subtree",
3859                                                     JOptionPane.CLOSED_OPTION,
3860                                                     JOptionPane.QUESTION_MESSAGE,
3861                                                     null,
3862                                                     options,
3863                                                     options[ 2 ] );
3864         boolean paste_as_sibling = true;
3865         if ( r == 1 ) {
3866             paste_as_sibling = false;
3867         }
3868         else if ( r != 0 ) {
3869             return;
3870         }
3871         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
3872         buffer_phy.setAllNodesToNotCollapse();
3873         PhylogenyMethods.preOrderReId( buffer_phy );
3874         buffer_phy.setRooted( true );
3875         boolean need_to_show_whole = false;
3876         if ( paste_as_sibling ) {
3877             if ( node.isRoot() ) {
3878                 JOptionPane.showMessageDialog( this,
3879                                                "Cannot paste sibling to root",
3880                                                "Attempt to paste sibling to root",
3881                                                JOptionPane.ERROR_MESSAGE );
3882                 return;
3883             }
3884             buffer_phy.addAsSibling( node );
3885         }
3886         else {
3887             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
3888                 need_to_show_whole = true;
3889                 _phylogeny = buffer_phy;
3890             }
3891             else {
3892                 buffer_phy.addAsChild( node );
3893             }
3894         }
3895         if ( getCopiedAndPastedNodes() == null ) {
3896             setCopiedAndPastedNodes( new HashSet<Long>() );
3897         }
3898         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
3899         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
3900         for( final PhylogenyNode n : nodes ) {
3901             node_ids.add( n.getId() );
3902         }
3903         node_ids.add( node.getId() );
3904         getCopiedAndPastedNodes().addAll( node_ids );
3905         setNodeInPreorderToNull();
3906         _phylogeny.externalNodesHaveChanged();
3907         _phylogeny.clearHashIdToNodeMap();
3908         _phylogeny.recalculateNumberOfExternalDescendants( true );
3909         resetNodeIdToDistToLeafMap();
3910         setEdited( true );
3911         if ( need_to_show_whole ) {
3912             getControlPanel().showWhole();
3913         }
3914         repaint();
3915     }
3916
3917     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
3918         return node.getNodeData().getProperties().asText();
3919     }
3920
3921     private void setColor( final Graphics2D g,
3922                            final PhylogenyNode node,
3923                            final boolean to_graphics_file,
3924                            final boolean to_pdf,
3925                            final boolean is_in_found_nodes,
3926                            final Color default_color ) {
3927         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3928             g.setColor( Color.BLACK );
3929         }
3930         else if ( is_in_found_nodes ) {
3931             g.setColor( getColorForFoundNode( node ) );
3932         }
3933         else if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
3934                 && ( node.getNodeData().getNodeVisualData().getFontColor() != null ) ) {
3935             g.setColor( node.getNodeData().getNodeVisualData().getFontColor() );
3936         }
3937         else if ( getControlPanel().isColorAccordingToSequence() ) {
3938             g.setColor( getSequenceBasedColor( node ) );
3939         }
3940         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3941             g.setColor( getTaxonomyBasedColor( node ) );
3942         }
3943         else if ( getControlPanel().isColorAccordingToAnnotation()
3944                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null )
3945                         && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
3946             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
3947         }
3948         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
3949                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3950             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3951         }
3952         else if ( to_pdf ) {
3953             g.setColor( Color.BLACK );
3954         }
3955         else {
3956             g.setColor( default_color );
3957         }
3958     }
3959
3960     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
3961         getMainPanel().setCopiedAndPastedNodes( nodeIds );
3962     }
3963
3964     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
3965         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
3966     }
3967
3968     private boolean setFont( final Graphics2D g, final PhylogenyNode node, final boolean is_in_found_nodes ) {
3969         Font visual_font = null;
3970         if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null ) ) {
3971             visual_font = node.getNodeData().getNodeVisualData().getFont();
3972             g.setFont( visual_font != null ? visual_font : getTreeFontSet().getLargeFont() );
3973         }
3974         else {
3975             g.setFont( getTreeFontSet().getLargeFont() );
3976         }
3977         if ( is_in_found_nodes ) {
3978             g.setFont( g.getFont().deriveFont( Font.BOLD ) );
3979         }
3980         return visual_font != null;
3981     }
3982
3983     final private void setInOv( final boolean in_ov ) {
3984         _in_ov = in_ov;
3985     }
3986
3987     final private void setOvMaxHeight( final float ov_max_height ) {
3988         _ov_max_height = ov_max_height;
3989     }
3990
3991     final private void setOvMaxWidth( final float ov_max_width ) {
3992         _ov_max_width = ov_max_width;
3993     }
3994
3995     final private void setOvXcorrectionFactor( final float f ) {
3996         _ov_x_correction_factor = f;
3997     }
3998
3999     final private void setOvXDistance( final float ov_x_distance ) {
4000         _ov_x_distance = ov_x_distance;
4001     }
4002
4003     final private void setOvXPosition( final int ov_x_position ) {
4004         _ov_x_position = ov_x_position;
4005     }
4006
4007     final private void setOvYDistance( final float ov_y_distance ) {
4008         _ov_y_distance = ov_y_distance;
4009     }
4010
4011     final private void setOvYPosition( final int ov_y_position ) {
4012         _ov_y_position = ov_y_position;
4013     }
4014
4015     final private void setOvYStart( final int ov_y_start ) {
4016         _ov_y_start = ov_y_start;
4017     }
4018
4019     final private void setScaleDistance( final double scale_distance ) {
4020         _scale_distance = scale_distance;
4021     }
4022
4023     final private void setScaleLabel( final String scale_label ) {
4024         _scale_label = scale_label;
4025     }
4026
4027     private final void setupStroke( final Graphics2D g ) {
4028         if ( getYdistance() < 0.0001 ) {
4029             g.setStroke( STROKE_0025 );
4030         }
4031         if ( getYdistance() < 0.001 ) {
4032             g.setStroke( STROKE_005 );
4033         }
4034         else if ( getYdistance() < 0.01 ) {
4035             g.setStroke( STROKE_01 );
4036         }
4037         else if ( getYdistance() < 0.5 ) {
4038             g.setStroke( STROKE_025 );
4039         }
4040         else if ( getYdistance() < 1 ) {
4041             g.setStroke( STROKE_05 );
4042         }
4043         else if ( getYdistance() < 2 ) {
4044             g.setStroke( STROKE_075 );
4045         }
4046         else if ( ( getYdistance() < 20 ) || !getConfiguration().isAllowThickStrokes() ) {
4047             g.setStroke( STROKE_1 );
4048         }
4049         else {
4050             g.setStroke( STROKE_2 );
4051         }
4052     }
4053
4054     final private void setUpUrtFactor() {
4055         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
4056                 : getVisibleRect().height;
4057         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4058             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
4059         }
4060         else {
4061             final int max_depth = _circ_max_depth;
4062             if ( max_depth > 0 ) {
4063                 setUrtFactor( d / ( 2 * max_depth ) );
4064             }
4065             else {
4066                 setUrtFactor( d / 2 );
4067             }
4068         }
4069         setUrtFactorOv( getUrtFactor() );
4070     }
4071
4072     final private void setUrtFactor( final float urt_factor ) {
4073         _urt_factor = urt_factor;
4074     }
4075
4076     final private void setUrtFactorOv( final float urt_factor_ov ) {
4077         _urt_factor_ov = urt_factor_ov;
4078     }
4079
4080     private void showExtDescNodeData( final PhylogenyNode node, final char separator ) {
4081         final List<String> data = new ArrayList<String>();
4082         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
4083         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
4084             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
4085                 if ( !nodes.contains( n ) ) {
4086                     nodes.add( n );
4087                 }
4088             }
4089         }
4090         for( final PhylogenyNode n : nodes ) {
4091             switch ( getOptions().getExtDescNodeDataToReturn() ) {
4092                 case NODE_NAME:
4093                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
4094                         data.add( n.getName() );
4095                     }
4096                     break;
4097                 case SEQUENCE_NAME:
4098                     if ( n.getNodeData().isHasSequence()
4099                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
4100                         data.add( n.getNodeData().getSequence().getName() );
4101                     }
4102                     break;
4103                 case GENE_NAME:
4104                     if ( n.getNodeData().isHasSequence()
4105                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
4106                         data.add( n.getNodeData().getSequence().getGeneName() );
4107                     }
4108                     break;
4109                 case SEQUENCE_SYMBOL:
4110                     if ( n.getNodeData().isHasSequence()
4111                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
4112                         data.add( n.getNodeData().getSequence().getSymbol() );
4113                     }
4114                     break;
4115                 case SEQUENCE_MOL_SEQ_FASTA:
4116                     final StringBuilder sb = new StringBuilder();
4117                     if ( n.getNodeData().isHasSequence()
4118                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
4119                         final StringBuilder ann = new StringBuilder();
4120                         if ( getControlPanel().isShowNodeNames() && !ForesterUtil.isEmpty( n.getName() ) ) {
4121                             ann.append( n.getName() );
4122                             ann.append( separator );
4123                         }
4124                         if ( getControlPanel().isShowSeqSymbols()
4125                                 && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
4126                             ann.append( n.getNodeData().getSequence().getSymbol() );
4127                             ann.append( separator );
4128                         }
4129                         if ( getControlPanel().isShowSeqNames()
4130                                 && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
4131                             ann.append( n.getNodeData().getSequence().getName() );
4132                             ann.append( separator );
4133                         }
4134                         if ( getControlPanel().isShowGeneNames()
4135                                 && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
4136                             ann.append( n.getNodeData().getSequence().getGeneName() );
4137                             ann.append( separator );
4138                         }
4139                         if ( getControlPanel().isShowSequenceAcc()
4140                                 && n.getNodeData().getSequence().getAccession() != null ) {
4141                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
4142                             ann.append( separator );
4143                         }
4144                         if ( n.getNodeData().isHasTaxonomy() ) {
4145                             if ( getControlPanel().isShowTaxonomyCode()
4146                                     && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
4147                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
4148                                 ann.append( separator );
4149                             }
4150                             if ( getControlPanel().isShowTaxonomyScientificNames()
4151                                     && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
4152                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
4153                                 ann.append( separator );
4154                             }
4155                             if ( getControlPanel().isShowTaxonomyCommonNames()
4156                                     && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) ) {
4157                                 ann.append( n.getNodeData().getTaxonomy().getCommonName() );
4158                                 ann.append( separator );
4159                             }
4160                         }
4161                         String ann_str;
4162                         if ( ann.length() > 0 && ann.charAt( ann.length() - 1 ) == separator ) {
4163                             ann_str = ann.substring( 0, ann.length() - 1 );
4164                         }
4165                         else {
4166                             ann_str = ann.toString();
4167                         }
4168                         sb.append( SequenceWriter.toFasta( ann_str,
4169                                                            n.getNodeData().getSequence().getMolecularSequence(),
4170                                                            60 ) );
4171                         data.add( sb.toString() );
4172                     }
4173                     break;
4174                 case SEQUENCE_ACC:
4175                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
4176                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
4177                         data.add( n.getNodeData().getSequence().getAccession().toString() );
4178                     }
4179                     break;
4180                 case TAXONOMY_SCIENTIFIC_NAME:
4181                     if ( n.getNodeData().isHasTaxonomy()
4182                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
4183                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
4184                     }
4185                     break;
4186                 case TAXONOMY_CODE:
4187                     if ( n.getNodeData().isHasTaxonomy()
4188                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
4189                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
4190                     }
4191                     break;
4192                 case DOMAINS_ALL:
4193                 case DOMAINS_COLLAPSED_PER_PROTEIN:
4194                     if ( n.getNodeData().isHasSequence()
4195                             && ( n.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4196                         final DomainArchitecture da = n.getNodeData().getSequence().getDomainArchitecture();
4197                         final Set<String> s = new HashSet<String>();
4198                         for( int i = 0; i < da.getDomains().size(); ++i ) {
4199                             final ProteinDomain d = da.getDomain( i );
4200                             if ( d.getConfidence() <= Math.pow( 10, getDomainStructureEvalueThresholdExp() ) ) {
4201                                 final String name = d.getName();
4202                                 if ( !( s.contains( name ) ) ) {
4203                                     data.add( name );
4204                                     if ( getOptions()
4205                                             .getExtDescNodeDataToReturn() == NodeDataField.DOMAINS_COLLAPSED_PER_PROTEIN ) {
4206                                         s.add( name );
4207                                     }
4208                                 }
4209                             }
4210                         }
4211                     }
4212                     break;
4213                 case SEQ_ANNOTATIONS:
4214                     if ( n.getNodeData().isHasSequence() ) {
4215                         if ( n.getNodeData().isHasSequence()
4216                                 && ( n.getNodeData().getSequence().getAnnotations() != null ) ) {
4217                             final SortedSet<Annotation> a = n.getNodeData().getSequence().getAnnotations();
4218                             for( int i = 0; i < a.size(); ++i ) {
4219                                 data.add( n.getNodeData().getSequence().getAnnotation( i ).toString() );
4220                             }
4221                         }
4222                     }
4223                     break;
4224                 case GO_TERM_IDS:
4225                     if ( n.getNodeData().isHasSequence() ) {
4226                         if ( n.getNodeData().isHasSequence()
4227                                 && ( n.getNodeData().getSequence().getAnnotations() != null ) ) {
4228                             final SortedSet<Annotation> a = n.getNodeData().getSequence().getAnnotations();
4229                             for( int i = 0; i < a.size(); ++i ) {
4230                                 final Annotation ann = n.getNodeData().getSequence().getAnnotation( i );
4231                                 final String ref = ann.getRef();
4232                                 if ( ref.toUpperCase().startsWith( "GO:" ) ) {
4233                                     data.add( ref );
4234                                 }
4235                             }
4236                         }
4237                     }
4238                     break;
4239                 case UNKNOWN:
4240                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
4241                     break;
4242                 default:
4243                     throw new IllegalArgumentException( "unknown data element: "
4244                             + getOptions().getExtDescNodeDataToReturn() );
4245             }
4246         } // for loop
4247         final StringBuilder sb = new StringBuilder();
4248         final int size = TreePanelUtil.nodeDataIntoStringBuffer( data, getOptions(), sb );
4249         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
4250                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
4251             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
4252                 System.out.println( sb );
4253             }
4254             if ( sb.length() < 1 ) {
4255                 clearCurrentExternalNodesDataBuffer();
4256             }
4257             else {
4258                 setCurrentExternalNodesDataBuffer( sb );
4259             }
4260         }
4261         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
4262             if ( sb.length() < 1 ) {
4263                 TreePanelUtil.showInformationMessage( this,
4264                                                       "No Appropriate Data (" + obtainTitleForExtDescNodeData() + ")",
4265                                                       "Descendants of selected node do not contain selected data" );
4266                 clearCurrentExternalNodesDataBuffer();
4267             }
4268             else {
4269                 setCurrentExternalNodesDataBuffer( sb );
4270                 String title;
4271                 if ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() ) {
4272                     title = ( getOptions().getExtDescNodeDataToReturn() == NodeDataField.UNKNOWN ? "Data"
4273                             : obtainTitleForExtDescNodeData() ) + " for " + data.size() + " nodes, unique entries: "
4274                             + size;
4275                 }
4276                 else {
4277                     title = ( getOptions().getExtDescNodeDataToReturn() == NodeDataField.UNKNOWN ? "Data"
4278                             : obtainTitleForExtDescNodeData() ) + " for " + data.size() + "/"
4279                             + node.getNumberOfExternalNodes() + " external descendats of node " + node
4280                             + ", unique entries: " + size;
4281                 }
4282                 final String s = sb.toString().trim();
4283                 getMainPanel().getMainFrame().showTextFrame( s, title );
4284             }
4285         }
4286     }
4287
4288     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
4289         try {
4290             if ( ( node.getName().length() > 0 )
4291                     || ( node.getNodeData().isHasTaxonomy()
4292                             && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
4293                     || ( node.getNodeData().isHasSequence()
4294                             && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) )
4295                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
4296                     || node.getBranchData().isHasConfidences() ) {
4297                 _popup_buffer.setLength( 0 );
4298                 short lines = 0;
4299                 if ( node.getName().length() > 0 ) {
4300                     lines++;
4301                     _popup_buffer.append( node.getName() );
4302                 }
4303                 if ( node.getNodeData().isHasTaxonomy()
4304                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
4305                     lines++;
4306                     boolean enc_data = false;
4307                     final Taxonomy tax = node.getNodeData().getTaxonomy();
4308                     if ( _popup_buffer.length() > 0 ) {
4309                         _popup_buffer.append( "\n" );
4310                     }
4311                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4312                         _popup_buffer.append( "[" );
4313                         _popup_buffer.append( tax.getTaxonomyCode() );
4314                         _popup_buffer.append( "]" );
4315                         enc_data = true;
4316                     }
4317                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4318                         if ( enc_data ) {
4319                             _popup_buffer.append( " " );
4320                         }
4321                         _popup_buffer.append( tax.getScientificName() );
4322                         enc_data = true;
4323                     }
4324                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
4325                         if ( enc_data ) {
4326                             _popup_buffer.append( " (" );
4327                         }
4328                         else {
4329                             _popup_buffer.append( "(" );
4330                         }
4331                         _popup_buffer.append( tax.getCommonName() );
4332                         _popup_buffer.append( ")" );
4333                         enc_data = true;
4334                     }
4335                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
4336                         if ( enc_data ) {
4337                             _popup_buffer.append( " (" );
4338                         }
4339                         else {
4340                             _popup_buffer.append( "(" );
4341                         }
4342                         _popup_buffer.append( tax.getAuthority() );
4343                         _popup_buffer.append( ")" );
4344                         enc_data = true;
4345                     }
4346                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
4347                         if ( enc_data ) {
4348                             _popup_buffer.append( " [" );
4349                         }
4350                         else {
4351                             _popup_buffer.append( "[" );
4352                         }
4353                         _popup_buffer.append( tax.getRank() );
4354                         _popup_buffer.append( "]" );
4355                         enc_data = true;
4356                     }
4357                     if ( tax.getSynonyms().size() > 0 ) {
4358                         if ( enc_data ) {
4359                             _popup_buffer.append( " " );
4360                         }
4361                         _popup_buffer.append( "[" );
4362                         int counter = 1;
4363                         for( final String syn : tax.getSynonyms() ) {
4364                             if ( !ForesterUtil.isEmpty( syn ) ) {
4365                                 enc_data = true;
4366                                 _popup_buffer.append( syn );
4367                                 if ( counter < tax.getSynonyms().size() ) {
4368                                     _popup_buffer.append( ", " );
4369                                 }
4370                             }
4371                             counter++;
4372                         }
4373                         _popup_buffer.append( "]" );
4374                     }
4375                     if ( !enc_data ) {
4376                         if ( ( tax.getIdentifier() != null )
4377                                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
4378                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
4379                                 _popup_buffer.append( "[" );
4380                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
4381                                 _popup_buffer.append( "] " );
4382                             }
4383                             _popup_buffer.append( tax.getIdentifier().getValue() );
4384                         }
4385                     }
4386                 }
4387                 if ( node.getNodeData().isHasSequence()
4388                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
4389                     lines++;
4390                     boolean enc_data = false;
4391                     if ( _popup_buffer.length() > 0 ) {
4392                         _popup_buffer.append( "\n" );
4393                     }
4394                     final Sequence seq = node.getNodeData().getSequence();
4395                     if ( seq.getAccession() != null ) {
4396                         _popup_buffer.append( "[" );
4397                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
4398                             _popup_buffer.append( seq.getAccession().getSource() );
4399                             _popup_buffer.append( ":" );
4400                         }
4401                         _popup_buffer.append( seq.getAccession().getValue() );
4402                         _popup_buffer.append( "]" );
4403                         enc_data = true;
4404                     }
4405                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
4406                         if ( enc_data ) {
4407                             _popup_buffer.append( " [" );
4408                         }
4409                         else {
4410                             _popup_buffer.append( "[" );
4411                         }
4412                         _popup_buffer.append( seq.getSymbol() );
4413                         _popup_buffer.append( "]" );
4414                         enc_data = true;
4415                     }
4416                     if ( !ForesterUtil.isEmpty( seq.getGeneName() ) ) {
4417                         if ( enc_data ) {
4418                             _popup_buffer.append( " [" );
4419                         }
4420                         else {
4421                             _popup_buffer.append( "[" );
4422                         }
4423                         _popup_buffer.append( seq.getGeneName() );
4424                         _popup_buffer.append( "]" );
4425                         enc_data = true;
4426                     }
4427                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
4428                         if ( enc_data ) {
4429                             _popup_buffer.append( " " );
4430                         }
4431                         _popup_buffer.append( seq.getName() );
4432                     }
4433                 }
4434                 if ( node.getNodeData().isHasDate() ) {
4435                     lines++;
4436                     if ( _popup_buffer.length() > 0 ) {
4437                         _popup_buffer.append( "\n" );
4438                     }
4439                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
4440                 }
4441                 if ( node.getNodeData().isHasDistribution() ) {
4442                     lines++;
4443                     if ( _popup_buffer.length() > 0 ) {
4444                         _popup_buffer.append( "\n" );
4445                     }
4446                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
4447                 }
4448                 if ( node.getBranchData().isHasConfidences() ) {
4449                     final List<Confidence> confs = node.getBranchData().getConfidences();
4450                     for( final Confidence confidence : confs ) {
4451                         lines++;
4452                         if ( _popup_buffer.length() > 0 ) {
4453                             _popup_buffer.append( "\n" );
4454                         }
4455                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
4456                             _popup_buffer.append( "[" );
4457                             _popup_buffer.append( confidence.getType() );
4458                             _popup_buffer.append( "] " );
4459                         }
4460                         _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
4461                                                                                                getOptions()
4462                                                                                                        .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4463                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4464                             _popup_buffer.append( " (sd=" );
4465                             _popup_buffer.append( FORMATTER_CONFIDENCE
4466                                     .format( ForesterUtil.round( confidence.getStandardDeviation(),
4467                                                                  getOptions()
4468                                                                          .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4469                             _popup_buffer.append( ")" );
4470                         }
4471                     }
4472                 }
4473                 if ( node.getNodeData().isHasProperties() ) {
4474                     if ( _popup_buffer.length() > 0 ) {
4475                         _popup_buffer.append( "\n" );
4476                     }
4477                     _popup_buffer.append( node.getNodeData().getProperties().asText() );
4478                 }
4479                 if ( _popup_buffer.length() > 0 ) {
4480                     if ( !getConfiguration().isUseNativeUI() ) {
4481                         _rollover_popup
4482                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
4483                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
4484                         if ( isInFoundNodes0( node ) && !isInFoundNodes1( node ) ) {
4485                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0() );
4486                         }
4487                         else if ( !isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
4488                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor1() );
4489                         }
4490                         else if ( isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
4491                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0and1() );
4492                         }
4493                         else {
4494                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
4495                         }
4496                     }
4497                     else {
4498                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
4499                     }
4500                     _rollover_popup.setText( _popup_buffer.toString() );
4501                     _node_desc_popup = PopupFactory.getSharedInstance()
4502                             .getPopup( null,
4503                                        _rollover_popup,
4504                                        e.getLocationOnScreen().x + 10,
4505                                        e.getLocationOnScreen().y - ( lines * 20 ) );
4506                     _node_desc_popup.show();
4507                 }
4508             }
4509         }
4510         catch ( final Exception ex ) {
4511             // Do nothing.
4512         }
4513     }
4514
4515     final private void showNodeEditFrame( final PhylogenyNode n ) {
4516         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4517             // pop up edit box for single node
4518             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
4519             _node_frame_index++;
4520         }
4521         else {
4522             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4523         }
4524     }
4525
4526     final private void showNodeFrame( final PhylogenyNode n ) {
4527         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4528             // pop up edit box for single node
4529             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
4530             _node_frame_index++;
4531         }
4532         else {
4533             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4534         }
4535     }
4536
4537     final private void switchDisplaygetPhylogenyGraphicsType() {
4538         switch ( getPhylogenyGraphicsType() ) {
4539             case RECTANGULAR:
4540                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4541                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4542                 break;
4543             case EURO_STYLE:
4544                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4545                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4546                 break;
4547             case ROUNDED:
4548                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4549                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4550                 break;
4551             case CURVED:
4552                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4553                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4554                 break;
4555             case TRIANGULAR:
4556                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4557                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4558                 break;
4559             case CONVEX:
4560                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4561                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4562                 break;
4563             case UNROOTED:
4564                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4565                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4566                 break;
4567             case CIRCULAR:
4568                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4569                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4570                 break;
4571             default:
4572                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
4573         }
4574         if ( getControlPanel().getDynamicallyHideData() != null ) {
4575             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4576                 getControlPanel().getDynamicallyHideData().setEnabled( false );
4577             }
4578             else {
4579                 getControlPanel().getDynamicallyHideData().setEnabled( true );
4580             }
4581         }
4582         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
4583             getControlPanel().setDrawPhylogramEnabled( true );
4584         }
4585         else {
4586             getControlPanel().setDrawPhylogramEnabled( false );
4587         }
4588         getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
4589     }
4590
4591     final void calcMaxDepth() {
4592         if ( _phylogeny != null ) {
4593             _circ_max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4594         }
4595     }
4596
4597     /**
4598      * Set parameters for printing the displayed tree
4599      *
4600      */
4601     final void calcParametersForPainting( final int x, final int y ) {
4602         // updateStyle(); not needed?
4603         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4604             initNodeData();
4605             calculateLongestExtNodeInfo();
4606             if ( ( getLongestExtNodeInfo() > ( x * 0.6 ) )
4607                     && ( getTreeFontSet().getLargeFont().getSize() > ( 2 + TreeFontSet.FONT_SIZE_CHANGE_STEP ) ) ) {
4608                 while ( ( getLongestExtNodeInfo() > ( x * 0.7 ) )
4609                         && ( getTreeFontSet().getLargeFont().getSize() > 2 ) ) {
4610                     getMainPanel().getTreeFontSet().decreaseFontSize( getConfiguration().getMinBaseFontSize(), true );
4611                     calculateLongestExtNodeInfo();
4612                 }
4613             }
4614             else {
4615                 while ( ( getLongestExtNodeInfo() < ( x * 0.6 ) ) && ( getTreeFontSet().getLargeFont()
4616                         .getSize() <= ( getTreeFontSet().getLargeFontMemory().getSize()
4617                                 - TreeFontSet.FONT_SIZE_CHANGE_STEP ) ) ) {
4618                     getMainPanel().getTreeFontSet().increaseFontSize();
4619                     calculateLongestExtNodeInfo();
4620                 }
4621             }
4622             //_length_of_longest_text = calcLengthOfLongestText();
4623             int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4624             final int max_depth = PhylogenyMethods.calculateMaxDepthConsiderCollapsed( _phylogeny ) + 1;
4625             if ( ext_nodes == 1 ) {
4626                 ext_nodes = max_depth;
4627                 if ( ext_nodes < 1 ) {
4628                     ext_nodes = 1;
4629                 }
4630             }
4631             updateOvSizes();
4632             float xdist = 0;
4633             float ov_xdist = 0;
4634             if ( !isNonLinedUpCladogram() ) {
4635                 xdist = ( float ) ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( ext_nodes + 3.0 ) );
4636                 ov_xdist = ( float ) ( getOvMaxWidth() / ( ext_nodes + 3.0 ) );
4637             }
4638             else {
4639                 xdist = ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( max_depth + 1 ) );
4640                 ov_xdist = ( getOvMaxWidth() / ( max_depth + 1 ) );
4641             }
4642             float ydist = ( float ) ( ( y - TreePanel.MOVE ) / ( ext_nodes * 2.0 ) );
4643             if ( xdist < 0.0 ) {
4644                 xdist = 0.0f;
4645             }
4646             if ( ov_xdist < 0.0 ) {
4647                 ov_xdist = 0.0f;
4648             }
4649             if ( ydist < 0.0 ) {
4650                 ydist = 0.0f;
4651             }
4652             setXdistance( xdist );
4653             setYdistance( ydist );
4654             setOvXDistance( ov_xdist );
4655             final double height = _phylogeny.calculateHeight( !_options.isCollapsedWithAverageHeigh() );
4656             //final double height = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4657             if ( height > 0 ) {
4658                 final float corr = ( float ) ( ( x - ( 2.0 * TreePanel.MOVE ) - getLongestExtNodeInfo()
4659                         - getXdistance() ) / height );
4660                 setXcorrectionFactor( corr > 0 ? corr : 0 );
4661                 final float ov_corr = ( float ) ( ( getOvMaxWidth() - getOvXDistance() ) / height );
4662                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
4663             }
4664             else {
4665                 setXcorrectionFactor( 0 );
4666                 setOvXcorrectionFactor( 0 );
4667             }
4668             _circ_max_depth = max_depth;
4669             setUpUrtFactor();
4670             //
4671             if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
4672                     && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
4673                 //                int dynamic_hiding_factor = calcDynamicHidingFactor();
4674                 //                if ( dynamic_hiding_factor > 1 ) {
4675                 //                    while ( dynamic_hiding_factor > 1
4676                 //                            && getTreeFontSet()._fm_large.getHeight() > TreeFontSet.SMALL_FONTS_BASE ) {
4677                 //                        getTreeFontSet().decreaseFontSize( 1, true );
4678                 //                        dynamic_hiding_factor = calcDynamicHidingFactor();
4679                 //                    }
4680                 //                }
4681                 //                else if ( getTreeFontSet().isDecreasedSizeBySystem() ) {
4682                 //                    while ( dynamic_hiding_factor < 1 && getTreeFontSet()._fm_large.getHeight() < 12 ) {
4683                 //                        getTreeFontSet().increaseFontSize();
4684                 //                        dynamic_hiding_factor = calcDynamicHidingFactor();
4685                 //                    }
4686                 //                }
4687             }
4688             //
4689         }
4690     }
4691
4692     final void calculateLongestExtNodeInfo() {
4693         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4694             return;
4695         }
4696         int max_possible_length = ForesterUtil
4697                 .roundToInt( ( getSize().getWidth() - ( 2 * MOVE ) ) * AptxConstants.EXT_NODE_INFO_LENGTH_MAX_RATIO );
4698         if ( max_possible_length < 20 ) {
4699             max_possible_length = 20;
4700         }
4701         int longest = 30;
4702         int longest_txt = 0;
4703         _longest_domain = 0;
4704         PhylogenyNode longest_txt_node = _phylogeny.getFirstExternalNode();
4705         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
4706             int sum = 0;
4707             if ( node.isCollapse() ) {
4708                 continue;
4709             }
4710             final StringBuilder sb = new StringBuilder();
4711             nodeDataAsSB( node, sb );
4712             if ( node.getNodeData().isHasTaxonomy() ) {
4713                 nodeTaxonomyDataAsSB( node.getNodeData().getTaxonomy(), sb );
4714             }
4715             final int txt = sb.length();
4716             if ( txt > longest_txt ) {
4717                 longest_txt = txt;
4718                 longest_txt_node = node;
4719             }
4720             boolean use_vis = false;
4721             final Graphics2D g = ( Graphics2D ) getGraphics();
4722             if ( getControlPanel().isUseVisualStyles() ) {
4723                 use_vis = setFont( g, node, false );
4724             }
4725             if ( !use_vis ) {
4726                 sum = getFontMetricsForLargeDefaultFont().stringWidth( sb.toString() );
4727             }
4728             else {
4729                 sum = getFontMetrics( g.getFont() ).stringWidth( sb.toString() );
4730             }
4731             if ( getControlPanel().isShowBinaryCharacters() && node.getNodeData().isHasBinaryCharacters() ) {
4732                 sum += getFontMetricsForLargeDefaultFont().stringWidth( node.getNodeData().getBinaryCharacters()
4733                         .getGainedCharactersAsStringBuffer().toString() );
4734             }
4735             if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4736                     && ( node.getNodeData().getVector().size() > 0 ) ) {
4737                 if ( getConfiguration() != null ) {
4738                     sum += getConfiguration().getVectorDataWidth() + 10;
4739                 }
4740                 else {
4741                     sum += RenderableVector.VECTOR_DEFAULT_WIDTH + 10;
4742                 }
4743             }
4744             if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4745                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4746                 // FIXME
4747                 // TODO this might need some clean up
4748                 final DomainArchitecture d = node.getNodeData().getSequence().getDomainArchitecture();
4749                 sum += ( ( _domain_structure_width
4750                         / ( ( RenderableDomainArchitecture ) d ).getOriginalSize().getWidth() ) * d.getTotalLength() )
4751                         + 10;
4752                 if ( d.getTotalLength() > _longest_domain ) {
4753                     _longest_domain = d.getTotalLength();
4754                 }
4755             }
4756             if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
4757                     && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
4758                     && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
4759                 // FIXME
4760                 sum += RenderableMsaSequence.DEFAULT_WIDTH + 30;
4761             }
4762             if ( sum >= max_possible_length ) {
4763                 _longest_ext_node_info = max_possible_length;
4764                 // return; //FIXME why?
4765             }
4766             if ( sum > longest ) {
4767                 longest = sum;
4768             }
4769         }
4770         _ext_node_with_longest_txt_info = longest_txt_node;
4771         if ( longest >= max_possible_length ) {
4772             _longest_ext_node_info = max_possible_length;
4773         }
4774         else {
4775             _longest_ext_node_info = longest;
4776         }
4777         _length_of_longest_text = calcLengthOfLongestText();
4778     }
4779
4780     final void calculateScaleDistance() {
4781         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4782             return;
4783         }
4784         final double height = getMaxDistanceToRoot();
4785         if ( height > 0 ) {
4786             if ( ( height <= 0.5 ) ) {
4787                 setScaleDistance( 0.01 );
4788             }
4789             else if ( height <= 5.0 ) {
4790                 setScaleDistance( 0.1 );
4791             }
4792             else if ( height <= 50.0 ) {
4793                 setScaleDistance( 1 );
4794             }
4795             else if ( height <= 500.0 ) {
4796                 setScaleDistance( 10 );
4797             }
4798             else {
4799                 setScaleDistance( 100 );
4800             }
4801         }
4802         else {
4803             setScaleDistance( 0.0 );
4804         }
4805         String scale_label = String.valueOf( getScaleDistance() );
4806         if ( !ForesterUtil.isEmpty( _phylogeny.getDistanceUnit() ) ) {
4807             scale_label += " [" + _phylogeny.getDistanceUnit() + "]";
4808         }
4809         setScaleLabel( scale_label );
4810     }
4811
4812     final Color calculateSequenceBasedColor( final Sequence seq ) {
4813         if ( ForesterUtil.isEmpty( seq.getName() ) ) {
4814             return getTreeColorSet().getSequenceColor();
4815         }
4816         Color c = null;
4817         final String seq_name = seq.getName();
4818         c = getControlPanel().getSequenceColors().get( seq_name );
4819         if ( c == null ) {
4820             c = AptxUtil.calculateColorFromString( seq_name, false );
4821             getControlPanel().getSequenceColors().put( seq_name, c );
4822         }
4823         return c;
4824     }
4825
4826     final Color calculateTaxonomyBasedColor( final Taxonomy tax ) {
4827         if ( getOptions().isColorByTaxonomicGroup() ) {
4828             if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4829                 boolean ex = false;
4830                 String group = null;
4831                 try {
4832                     group = TaxonomyUtil.getTaxGroupByTaxCode( tax.getTaxonomyCode() );
4833                 }
4834                 catch ( final Exception e ) {
4835                     ex = true;
4836                 }
4837                 if ( !ex && !ForesterUtil.isEmpty( group ) ) {
4838                     final Color c = ForesterUtil.obtainColorDependingOnTaxonomyGroup( group );
4839                     if ( c != null ) {
4840                         return c;
4841                     }
4842                 }
4843             }
4844             return getTreeColorSet().getTaxonomyColor();
4845         }
4846         else {
4847             if ( ForesterUtil.isEmpty( tax.getTaxonomyCode() ) && ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4848                 return getTreeColorSet().getTaxonomyColor();
4849             }
4850             Color c = null;
4851             if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4852                 c = getControlPanel().getSpeciesColors().get( tax.getTaxonomyCode() );
4853             }
4854             if ( ( c == null ) && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4855                 c = getControlPanel().getSpeciesColors().get( tax.getScientificName() );
4856             }
4857             if ( c == null ) {
4858                 if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4859                     c = AptxUtil.calculateColorFromString( tax.getTaxonomyCode(), true );
4860                     getControlPanel().getSpeciesColors().put( tax.getTaxonomyCode(), c );
4861                 }
4862                 else {
4863                     c = AptxUtil.calculateColorFromString( tax.getScientificName(), true );
4864                     getControlPanel().getSpeciesColors().put( tax.getScientificName(), c );
4865                 }
4866             }
4867             return c;
4868         }
4869     }
4870
4871     void clearCurrentExternalNodesDataBuffer() {
4872         setCurrentExternalNodesDataBuffer( new StringBuilder() );
4873     }
4874
4875     /**
4876      * Collapse the tree from the given node
4877      *
4878      * @param node
4879      *            a PhylogenyNode
4880      */
4881     final void collapse( final PhylogenyNode node ) {
4882         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4883             JOptionPane.showMessageDialog( this,
4884                                            "Cannot collapse in unrooted display type",
4885                                            "Attempt to collapse in unrooted display",
4886                                            JOptionPane.WARNING_MESSAGE );
4887             return;
4888         }
4889         if ( !node.isExternal() && !node.isRoot() ) {
4890             final boolean collapse = !node.isCollapse();
4891             TreePanelUtil.collapseSubtree( node, collapse );
4892             updateSetOfCollapsedExternalNodes();
4893             _phylogeny.recalculateNumberOfExternalDescendants( true );
4894             resetNodeIdToDistToLeafMap();
4895             calculateLongestExtNodeInfo();
4896             setNodeInPreorderToNull();
4897             _control_panel.displayedPhylogenyMightHaveChanged( true );
4898             resetPreferredSize();
4899             updateOvSizes();
4900             _main_panel.adjustJScrollPane();
4901             repaint();
4902         }
4903     }
4904
4905     final void uncollapseAll( final PhylogenyNode node ) {
4906         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4907             JOptionPane.showMessageDialog( this,
4908                                            "Cannot uncollapse in unrooted display type",
4909                                            "Attempt to uncollapse in unrooted display",
4910                                            JOptionPane.WARNING_MESSAGE );
4911             return;
4912         }
4913         if ( !node.isExternal() ) {
4914             TreePanelUtil.uncollapseSubtree( node );
4915             updateSetOfCollapsedExternalNodes();
4916             _phylogeny.recalculateNumberOfExternalDescendants( true );
4917             resetNodeIdToDistToLeafMap();
4918             calculateLongestExtNodeInfo();
4919             setNodeInPreorderToNull();
4920             _control_panel.displayedPhylogenyMightHaveChanged( true );
4921             resetPreferredSize();
4922             updateOvSizes();
4923             _main_panel.adjustJScrollPane();
4924             repaint();
4925         }
4926     }
4927
4928     final void collapseSpeciesSpecificSubtrees() {
4929         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4930             return;
4931         }
4932         setWaitCursor();
4933         TreePanelUtil.collapseSpeciesSpecificSubtrees( _phylogeny );
4934         updateSetOfCollapsedExternalNodes();
4935         _phylogeny.recalculateNumberOfExternalDescendants( true );
4936         resetNodeIdToDistToLeafMap();
4937         calculateLongestExtNodeInfo();
4938         setNodeInPreorderToNull();
4939         resetPreferredSize();
4940         resetDepthCollapseDepthValue();
4941         resetRankCollapseRankValue();
4942         _main_panel.adjustJScrollPane();
4943         getControlPanel().showWhole();
4944         setArrowCursor();
4945     }
4946
4947     final void colorRank( final String rank ) {
4948         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4949             return;
4950         }
4951         setWaitCursor();
4952         AptxUtil.removeBranchColors( _phylogeny );
4953         final int colorizations = TreePanelUtil.colorPhylogenyAccordingToRanks( _phylogeny, rank, this );
4954         if ( colorizations > 0 ) {
4955             _control_panel.setColorBranches( true );
4956             if ( _control_panel.getUseVisualStylesCb() != null ) {
4957                 _control_panel.getUseVisualStylesCb().setSelected( true );
4958             }
4959             if ( _control_panel.getColorAccSpeciesCb() != null ) {
4960                 _control_panel.getColorAccSpeciesCb().setSelected( false );
4961             }
4962             _options.setColorLabelsSameAsParentBranch( true );
4963             if ( getMainPanel().getMainFrame()._color_labels_same_as_parent_branch != null ) {
4964                 getMainPanel().getMainFrame()._color_labels_same_as_parent_branch.setSelected( true );
4965             }
4966             _control_panel.repaint();
4967         }
4968         setArrowCursor();
4969         repaint();
4970         if ( colorizations > 0 ) {
4971             String msg = "Taxonomy colorization via " + rank + " completed:\n";
4972             if ( colorizations > 1 ) {
4973                 msg += "colorized " + colorizations + " subtrees";
4974             }
4975             else {
4976                 msg += "colorized one subtree";
4977             }
4978             setEdited( true );
4979             JOptionPane.showMessageDialog( this,
4980                                            msg,
4981                                            "Taxonomy Rank-Colorization Completed (" + rank + ")",
4982                                            JOptionPane.INFORMATION_MESSAGE );
4983         }
4984         else {
4985             String msg = "Could not taxonomy rank-colorize any subtree via " + rank + ".\n";
4986             msg += "Possible solutions (given that suitable taxonomic information is present):\n";
4987             msg += "select a different rank (e.g. phylum, genus, ...)\n";
4988             msg += "  and/or\n";
4989             msg += "execute:\n";
4990             msg += "1. \"" + MainFrame.OBTAIN_DETAILED_TAXONOMIC_INFORMATION + "\" (Tools)\n";
4991             msg += "2. \"" + MainFrame.INFER_ANCESTOR_TAXONOMIES + "\" (Analysis)";
4992             JOptionPane.showMessageDialog( this,
4993                                            msg,
4994                                            "Taxonomy Rank-Colorization Failed",
4995                                            JOptionPane.WARNING_MESSAGE );
4996         }
4997     }
4998
4999     final void confColor() {
5000         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5001             return;
5002         }
5003         setWaitCursor();
5004         AptxUtil.removeBranchColors( _phylogeny );
5005         TreePanelUtil.colorPhylogenyAccordingToConfidenceValues( _phylogeny, this );
5006         _control_panel.setColorBranches( true );
5007         if ( _control_panel.getUseVisualStylesCb() != null ) {
5008             _control_panel.getUseVisualStylesCb().setSelected( true );
5009         }
5010         setArrowCursor();
5011         repaint();
5012     }
5013
5014     final void decreaseDomainStructureEvalueThresholdExp() {
5015         if ( _domain_structure_e_value_thr_exp > -20 ) {
5016             _domain_structure_e_value_thr_exp -= 1;
5017         }
5018     }
5019
5020     /**
5021      * Find the node, if any, at the given location
5022      *
5023      * @param x
5024      * @param y
5025      * @return pointer to the node at x,y, null if not found
5026      */
5027     final PhylogenyNode findNode( final int x, final int y ) {
5028         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5029             return null;
5030         }
5031         final int half_box_size_plus_wiggle = ( getOptions().getDefaultNodeShapeSize() / 2 ) + WIGGLE;
5032         for( final PhylogenyNodeIterator iter = _phylogeny.iteratorPostorder(); iter.hasNext(); ) {
5033             final PhylogenyNode node = iter.next();
5034             if ( ( _phylogeny.isRooted() || !node.isRoot() || ( node.getNumberOfDescendants() > 2 ) )
5035                     && ( ( node.getXcoord() - half_box_size_plus_wiggle ) <= x )
5036                     && ( ( node.getXcoord() + half_box_size_plus_wiggle ) >= x )
5037                     && ( ( node.getYcoord() - half_box_size_plus_wiggle ) <= y )
5038                     && ( ( node.getYcoord() + half_box_size_plus_wiggle ) >= y ) ) {
5039                 return node;
5040             }
5041         }
5042         return null;
5043     }
5044
5045     final Configuration getConfiguration() {
5046         return _configuration;
5047     }
5048
5049     final ControlPanel getControlPanel() {
5050         return _control_panel;
5051     }
5052
5053     String getCurrentExternalNodesDataBufferAsString() {
5054         return _current_external_nodes_data_buffer.toString();
5055     }
5056
5057     int getCurrentExternalNodesDataBufferChangeCounter() {
5058         return _current_external_nodes_data_buffer_change_counter;
5059     }
5060
5061     final int getDomainStructureEvalueThresholdExp() {
5062         return _domain_structure_e_value_thr_exp;
5063     }
5064
5065     final Set<Long> getFoundNodes0() {
5066         return _found_nodes_0;
5067     }
5068
5069     final Set<Long> getFoundNodes1() {
5070         return _found_nodes_1;
5071     }
5072
5073     List<PhylogenyNode> getFoundNodesAsListOfPhylogenyNodes() {
5074         final List<PhylogenyNode> additional_nodes = new ArrayList<PhylogenyNode>();
5075         if ( getFoundNodes0() != null ) {
5076             for( final Long id : getFoundNodes0() ) {
5077                 final PhylogenyNode n = _phylogeny.getNode( id );
5078                 if ( n != null ) {
5079                     additional_nodes.add( n );
5080                 }
5081             }
5082         }
5083         if ( getFoundNodes1() != null ) {
5084             for( final Long id : getFoundNodes1() ) {
5085                 if ( ( getFoundNodes0() == null ) || !getFoundNodes0().contains( id ) ) {
5086                     final PhylogenyNode n = _phylogeny.getNode( id );
5087                     if ( n != null ) {
5088                         additional_nodes.add( n );
5089                     }
5090                 }
5091             }
5092         }
5093         return additional_nodes;
5094     }
5095
5096     final Color getGraphicsForNodeBoxWithColorForParentBranch( final PhylogenyNode node ) {
5097         if ( getControlPanel().isUseVisualStyles() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
5098             return ( PhylogenyMethods.getBranchColorValue( node ) );
5099         }
5100         else {
5101             return ( getTreeColorSet().getBranchColor() );
5102         }
5103     }
5104
5105     final int getLongestExtNodeInfo() {
5106         return _longest_ext_node_info;
5107     }
5108
5109     final Options getOptions() {
5110         if ( _options == null ) {
5111             _options = getControlPanel().getOptions();
5112         }
5113         return _options;
5114     }
5115
5116     final Rectangle2D getOvRectangle() {
5117         return _ov_rectangle;
5118     }
5119
5120     final Rectangle getOvVirtualRectangle() {
5121         return _ov_virtual_rectangle;
5122     }
5123
5124     final PHYLOGENY_GRAPHICS_TYPE getPhylogenyGraphicsType() {
5125         return _graphics_type;
5126     }
5127
5128     final Color getSequenceBasedColor( final PhylogenyNode node ) {
5129         if ( node.getNodeData().isHasSequence() ) {
5130             return calculateSequenceBasedColor( node.getNodeData().getSequence() );
5131         }
5132         // return non-colorized color
5133         return getTreeColorSet().getSequenceColor();
5134     }
5135
5136     final double getStartingAngle() {
5137         return _urt_starting_angle;
5138     }
5139
5140     DescriptiveStatistics getStatisticsForExpressionValues() {
5141         return _statistics_for_vector_data;
5142     }
5143
5144     final Color getTaxonomyBasedColor( final PhylogenyNode node ) {
5145         if ( node.isExternal() && node.getNodeData().isHasTaxonomy() ) {
5146             return calculateTaxonomyBasedColor( node.getNodeData().getTaxonomy() );
5147         }
5148         // return non-colorized color
5149         return getTreeColorSet().getTaxonomyColor();
5150     }
5151
5152     final File getTreeFile() {
5153         return _treefile;
5154     }
5155
5156     final float getXcorrectionFactor() {
5157         return _x_correction_factor;
5158     }
5159
5160     final float getXdistance() {
5161         return _x_distance;
5162     }
5163
5164     final float getYdistance() {
5165         return _y_distance;
5166     }
5167
5168     final void increaseDomainStructureEvalueThresholdExp() {
5169         if ( _domain_structure_e_value_thr_exp < 3 ) {
5170             _domain_structure_e_value_thr_exp += 1;
5171         }
5172     }
5173
5174     final void initNodeData() {
5175         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5176             return;
5177         }
5178         double _max_original_domain_structure_width = 0.0;
5179         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
5180             if ( node.getNodeData().isHasSequence()
5181                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
5182                 RenderableDomainArchitecture rds = null;
5183                 if ( !( node.getNodeData().getSequence()
5184                         .getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
5185                     if ( SPECIAL_DOMAIN_COLORING ) {
5186                         rds = new RenderableDomainArchitecture( node.getNodeData().getSequence()
5187                                 .getDomainArchitecture(), node.getName() );
5188                     }
5189                     else {
5190                         rds = new RenderableDomainArchitecture( node.getNodeData().getSequence()
5191                                 .getDomainArchitecture() );
5192                     }
5193                     node.getNodeData().getSequence().setDomainArchitecture( rds );
5194                 }
5195                 else {
5196                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
5197                 }
5198                 if ( getControlPanel().isShowDomainArchitectures() ) {
5199                     final double dsw = rds.getOriginalSize().getWidth();
5200                     if ( dsw > _max_original_domain_structure_width ) {
5201                         _max_original_domain_structure_width = dsw;
5202                     }
5203                 }
5204             }
5205         }
5206         if ( getControlPanel().isShowDomainArchitectures() ) {
5207             final float ds_factor_width = ( float ) ( _domain_structure_width / _max_original_domain_structure_width );
5208             for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
5209                 if ( node.getNodeData().isHasSequence()
5210                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
5211                     final RenderableDomainArchitecture rds = ( RenderableDomainArchitecture ) node.getNodeData()
5212                             .getSequence().getDomainArchitecture();
5213                     rds.setRenderingFactorWidth( ds_factor_width );
5214                     rds.setParameter( _domain_structure_e_value_thr_exp );
5215                 }
5216             }
5217         }
5218     }
5219
5220     final boolean inOv( final MouseEvent e ) {
5221         return ( ( e.getX() > ( getVisibleRect().x + getOvXPosition() + 1 ) )
5222                 && ( e.getX() < ( ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() ) - 1 ) )
5223                 && ( e.getY() > ( getVisibleRect().y + getOvYPosition() + 1 ) )
5224                 && ( e.getY() < ( ( getVisibleRect().y + getOvYPosition() + getOvMaxHeight() ) - 1 ) ) );
5225     }
5226
5227     final boolean inOvRectangle( final MouseEvent e ) {
5228         return ( ( e.getX() >= ( getOvRectangle().getX() - 1 ) )
5229                 && ( e.getX() <= ( getOvRectangle().getX() + getOvRectangle().getWidth() + 1 ) )
5230                 && ( e.getY() >= ( getOvRectangle().getY() - 1 ) )
5231                 && ( e.getY() <= ( getOvRectangle().getY() + getOvRectangle().getHeight() + 1 ) ) );
5232     }
5233
5234     final boolean isCanCollapse() {
5235         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5236     }
5237
5238     final boolean isCanUncollapseAll( final PhylogenyNode node ) {
5239         if ( node.isExternal() || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) ) {
5240             return false;
5241         }
5242         if ( node.isCollapse() ) {
5243             return true;
5244         }
5245         final PhylogenyNodeIterator it = new PreorderTreeIterator( node );
5246         while ( it.hasNext() ) {
5247             if ( it.next().isCollapse() ) {
5248                 return true;
5249             }
5250         }
5251         return false;
5252     }
5253
5254     final boolean isCanColorSubtree() {
5255         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5256     }
5257
5258     final boolean isCanCopy() {
5259         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
5260     }
5261
5262     final boolean isCanCut( final PhylogenyNode node ) {
5263         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
5264                 && !node.isRoot() );
5265     }
5266
5267     final boolean isCanDelete() {
5268         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
5269     }
5270
5271     final boolean isCanPaste() {
5272         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
5273                 && ( getCutOrCopiedTree() != null ) && !getCutOrCopiedTree().isEmpty() );
5274     }
5275
5276     final boolean isCanReroot() {
5277         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && ( _subtree_index < 1 ) );
5278     }
5279
5280     final boolean isCanSubtree( final PhylogenyNode node ) {
5281         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && !node.isExternal()
5282                 && ( !node.isRoot() || ( _subtree_index > 0 ) ) );
5283     }
5284
5285     final boolean isCurrentTreeIsSubtree() {
5286         return ( _subtree_index > 0 );
5287     }
5288
5289     final boolean isEdited() {
5290         return _edited;
5291     }
5292
5293     final boolean isInOvRect() {
5294         return _in_ov_rect;
5295     }
5296
5297     final boolean isOvOn() {
5298         return _ov_on;
5299     }
5300
5301     final boolean isPhyHasBranchLengths() {
5302         return _phy_has_branch_lengths;
5303     }
5304
5305     final void midpointRoot() {
5306         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5307             return;
5308         }
5309         if ( !_phylogeny.isRerootable() ) {
5310             JOptionPane.showMessageDialog( this,
5311                                            "This is not rerootable",
5312                                            "Not rerootable",
5313                                            JOptionPane.WARNING_MESSAGE );
5314             return;
5315         }
5316         setNodeInPreorderToNull();
5317         setWaitCursor();
5318         PhylogenyMethods.midpointRoot( _phylogeny );
5319         resetNodeIdToDistToLeafMap();
5320         setArrowCursor();
5321         setEdited( true );
5322         repaint();
5323     }
5324
5325     final void mouseClicked( final MouseEvent e ) {
5326         if ( getOptions().isShowOverview() && isOvOn() && isInOv() ) {
5327             final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
5328             final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
5329             double x = ( e.getX() - getVisibleRect().x - getOvXPosition() - ( getOvRectangle().getWidth() / 2.0 ) )
5330                     * w_ratio;
5331             double y = ( e.getY() - getVisibleRect().y - getOvYPosition() - ( getOvRectangle().getHeight() / 2.0 ) )
5332                     * h_ratio;
5333             if ( x < 0 ) {
5334                 x = 0;
5335             }
5336             if ( y < 0 ) {
5337                 y = 0;
5338             }
5339             final double max_x = getWidth() - getVisibleRect().width;
5340             final double max_y = getHeight() - getVisibleRect().height;
5341             if ( x > max_x ) {
5342                 x = max_x;
5343             }
5344             if ( y > max_y ) {
5345                 y = max_y;
5346             }
5347             getMainPanel().getCurrentScrollPane().getViewport()
5348                     .setViewPosition( new Point( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ) ) );
5349             setInOvRect( true );
5350             repaint();
5351         }
5352         else {
5353             final PhylogenyNode node = findNode( e.getX(), e.getY() );
5354             if ( node != null ) {
5355                 if ( !node.isRoot() && node.getParent().isCollapse() ) {
5356                     return;
5357                 }
5358                 _highlight_node = node;
5359                 // Check if shift key is down
5360                 if ( ( e.getModifiers() & InputEvent.SHIFT_MASK ) != 0 ) {
5361                     // Yes, so add to _found_nodes
5362                     if ( getFoundNodes0() == null ) {
5363                         setFoundNodes0( new HashSet<Long>() );
5364                     }
5365                     getFoundNodes0().add( node.getId() );
5366                     // Check if control key is down
5367                 }
5368                 else if ( ( e.getModifiers() & InputEvent.CTRL_MASK ) != 0 ) {
5369                     // Yes, so pop-up menu
5370                     displayNodePopupMenu( node, e.getX(), e.getY() );
5371                     // Handle unadorned click
5372                 }
5373                 else {
5374                     // Check for right mouse button
5375                     if ( e.getModifiers() == 4 ) {
5376                         displayNodePopupMenu( node, e.getX(), e.getY() );
5377                     }
5378                     else {
5379                         // if not in _found_nodes, clear _found_nodes
5380                         handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
5381                     }
5382                 }
5383             }
5384             else {
5385                 // no node was clicked
5386                 _highlight_node = null;
5387             }
5388         }
5389         repaint();
5390     }
5391
5392     final void mouseDragInBrowserPanel( final MouseEvent e ) {
5393         setCursor( MOVE_CURSOR );
5394         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
5395         scroll_position.x -= ( e.getX() - getLastDragPointX() );
5396         scroll_position.y -= ( e.getY() - getLastDragPointY() );
5397         if ( scroll_position.x < 0 ) {
5398             scroll_position.x = 0;
5399         }
5400         else {
5401             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
5402                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
5403             if ( scroll_position.x > max_x ) {
5404                 scroll_position.x = max_x;
5405             }
5406         }
5407         if ( scroll_position.y < 0 ) {
5408             scroll_position.y = 0;
5409         }
5410         else {
5411             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
5412                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
5413             if ( scroll_position.y > max_y ) {
5414                 scroll_position.y = max_y;
5415             }
5416         }
5417         if ( isOvOn() || getOptions().isShowScale() ) {
5418             repaint();
5419         }
5420         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
5421     }
5422
5423     final void mouseDragInOvRectangle( final MouseEvent e ) {
5424         setCursor( HAND_CURSOR );
5425         final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
5426         final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
5427         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
5428         double dx = ( ( w_ratio * e.getX() ) - ( w_ratio * getLastDragPointX() ) );
5429         double dy = ( ( h_ratio * e.getY() ) - ( h_ratio * getLastDragPointY() ) );
5430         scroll_position.x = ForesterUtil.roundToInt( scroll_position.x + dx );
5431         scroll_position.y = ForesterUtil.roundToInt( scroll_position.y + dy );
5432         if ( scroll_position.x <= 0 ) {
5433             scroll_position.x = 0;
5434             dx = 0;
5435         }
5436         else {
5437             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
5438                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
5439             if ( scroll_position.x >= max_x ) {
5440                 dx = 0;
5441                 scroll_position.x = max_x;
5442             }
5443         }
5444         if ( scroll_position.y <= 0 ) {
5445             dy = 0;
5446             scroll_position.y = 0;
5447         }
5448         else {
5449             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
5450                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
5451             if ( scroll_position.y >= max_y ) {
5452                 dy = 0;
5453                 scroll_position.y = max_y;
5454             }
5455         }
5456         repaint();
5457         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
5458         setLastMouseDragPointX( ( float ) ( e.getX() + dx ) );
5459         setLastMouseDragPointY( ( float ) ( e.getY() + dy ) );
5460     }
5461
5462     final void mouseMoved( final MouseEvent e ) {
5463         requestFocusInWindow();
5464         if ( _current_external_nodes != null ) {
5465             _current_external_nodes = null;
5466             repaint();
5467         }
5468         if ( getControlPanel().isNodeDescPopup() ) {
5469             if ( _node_desc_popup != null ) {
5470                 _node_desc_popup.hide();
5471                 _node_desc_popup = null;
5472             }
5473         }
5474         if ( getOptions().isShowOverview() && isOvOn() ) {
5475             if ( inOvVirtualRectangle( e ) ) {
5476                 if ( !isInOvRect() ) {
5477                     setInOvRect( true );
5478                     repaint();
5479                 }
5480             }
5481             else {
5482                 if ( isInOvRect() ) {
5483                     setInOvRect( false );
5484                     repaint();
5485                 }
5486             }
5487         }
5488         if ( inOv( e ) && getOptions().isShowOverview() && isOvOn() ) {
5489             if ( !isInOv() ) {
5490                 setInOv( true );
5491             }
5492         }
5493         else {
5494             if ( isInOv() ) {
5495                 setInOv( false );
5496             }
5497             final PhylogenyNode node = findNode( e.getX(), e.getY() );
5498             if ( ( node != null ) && ( node.isRoot() || !node.getParent().isCollapse() ) ) {
5499                 if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.GET_EXT_DESC_DATA ) ) {
5500                     for( final PhylogenyNode n : node.getAllExternalDescendants() ) {
5501                         addToCurrentExternalNodes( n.getId() );
5502                     }
5503                     setCursor( HAND_CURSOR );
5504                     repaint();
5505                 }
5506                 else if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.CUT_SUBTREE )
5507                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.COPY_SUBTREE )
5508                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.PASTE_SUBTREE )
5509                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.DELETE_NODE_OR_SUBTREE )
5510                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.REROOT )
5511                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.ADD_NEW_NODE ) ) {
5512                     setCursor( CUT_CURSOR );
5513                 }
5514                 else {
5515                     setCursor( HAND_CURSOR );
5516                     if ( getControlPanel().isNodeDescPopup() ) {
5517                         showNodeDataPopup( e, node );
5518                     }
5519                 }
5520             }
5521             else {
5522                 setCursor( ARROW_CURSOR );
5523             }
5524         }
5525     }
5526
5527     final void mouseReleasedInBrowserPanel( final MouseEvent e ) {
5528         setCursor( ARROW_CURSOR );
5529     }
5530
5531     final void multiplyUrtFactor( final float f ) {
5532         _urt_factor *= f;
5533     }
5534
5535     final void paintBranchCircular( final PhylogenyNode p,
5536                                     final PhylogenyNode c,
5537                                     final Graphics2D g,
5538                                     final boolean radial_labels,
5539                                     final boolean to_pdf,
5540                                     final boolean to_graphics_file ) {
5541         final double angle = _urt_nodeid_angle_map.get( c.getId() );
5542         final double root_x = _root.getXcoord();
5543         final double root_y = _root.getYcoord();
5544         final double dx = root_x - p.getXcoord();
5545         final double dy = root_y - p.getYcoord();
5546         final double parent_radius = Math.sqrt( ( dx * dx ) + ( dy * dy ) );
5547         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
5548         assignGraphicsForBranchWithColorForParentBranch( c, false, g, to_pdf, to_graphics_file );
5549         if ( ( c.isFirstChildNode() || c.isLastChildNode() )
5550                 && ( ( Math.abs( parent_radius * arc ) > 1.5 ) || to_pdf || to_graphics_file ) ) {
5551             final double r2 = 2.0 * parent_radius;
5552             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
5553         }
5554         drawLine( c.getXcoord(),
5555                   c.getYcoord(),
5556                   root_x + ( Math.cos( angle ) * parent_radius ),
5557                   root_y + ( Math.sin( angle ) * parent_radius ),
5558                   g );
5559         paintNodeBox( c.getXcoord(), c.getYcoord(), c, g, to_pdf, to_graphics_file );
5560         if ( c.isExternal() ) {
5561             final boolean is_in_found_nodes = isInFoundNodes0( c ) || isInFoundNodes1( c )
5562                     || isInCurrentExternalNodes( c );
5563             if ( ( _dynamic_hiding_factor > 1 ) && !is_in_found_nodes
5564                     && ( ( _urt_nodeid_index_map.get( c.getId() ) % _dynamic_hiding_factor ) != 1 ) ) {
5565                 return;
5566             }
5567             paintNodeDataUnrootedCirc( g, c, to_pdf, to_graphics_file, radial_labels, 0, is_in_found_nodes );
5568         }
5569     }
5570
5571     final void paintBranchCircularLite( final PhylogenyNode p, final PhylogenyNode c, final Graphics2D g ) {
5572         final double angle = _urt_nodeid_angle_map.get( c.getId() );
5573         final double root_x = _root.getXSecondary();
5574         final double root_y = _root.getYSecondary();
5575         final double dx = root_x - p.getXSecondary();
5576         final double dy = root_y - p.getYSecondary();
5577         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
5578         final double parent_radius = Math.sqrt( ( dx * dx ) + ( dy * dy ) );
5579         g.setColor( getTreeColorSet().getOvColor() );
5580         if ( ( c.isFirstChildNode() || c.isLastChildNode() ) && ( Math.abs( arc ) > 0.02 ) ) {
5581             final double r2 = 2.0 * parent_radius;
5582             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
5583         }
5584         drawLine( c.getXSecondary(),
5585                   c.getYSecondary(),
5586                   root_x + ( Math.cos( angle ) * parent_radius ),
5587                   root_y + ( Math.sin( angle ) * parent_radius ),
5588                   g );
5589         if ( isInFoundNodes( c ) || isInCurrentExternalNodes( c ) ) {
5590             g.setColor( getColorForFoundNode( c ) );
5591             drawRectFilled( c.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5592                             c.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5593                             OVERVIEW_FOUND_NODE_BOX_SIZE,
5594                             OVERVIEW_FOUND_NODE_BOX_SIZE,
5595                             g );
5596         }
5597     }
5598
5599     final void paintCircular( final Phylogeny phy,
5600                               final double starting_angle,
5601                               final int center_x,
5602                               final int center_y,
5603                               final int radius,
5604                               final Graphics2D g,
5605                               final boolean to_pdf,
5606                               final boolean to_graphics_file ) {
5607         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes() - _collapsed_external_nodeid_set.size();
5608         System.out.println( "# collapsed external = " + _collapsed_external_nodeid_set.size() );
5609         _root = phy.getRoot();
5610         _root.setXcoord( center_x );
5611         _root.setYcoord( center_y );
5612         final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
5613         double current_angle = starting_angle;
5614         int i = 0;
5615         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
5616             final PhylogenyNode n = it.next();
5617             if ( !n.isCollapse() ) {
5618                 n.setXcoord( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
5619                 n.setYcoord( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
5620                 _urt_nodeid_angle_map.put( n.getId(), current_angle );
5621                 _urt_nodeid_index_map.put( n.getId(), i++ );
5622                 current_angle += ( TWO_PI / circ_num_ext_nodes );
5623             }
5624             else {
5625                 //TODO remove me
5626                 System.out.println( "is collapse" + n.getName() );
5627             }
5628         }
5629         paintCirculars( phy.getRoot(), phy, center_x, center_y, radius, radial_labels, g, to_pdf, to_graphics_file );
5630         paintNodeBox( _root.getXcoord(), _root.getYcoord(), _root, g, to_pdf, to_graphics_file );
5631     }
5632
5633     final void paintCircularLite( final Phylogeny phy,
5634                                   final double starting_angle,
5635                                   final int center_x,
5636                                   final int center_y,
5637                                   final int radius,
5638                                   final Graphics2D g ) {
5639         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes();
5640         _root = phy.getRoot();
5641         _root.setXSecondary( center_x );
5642         _root.setYSecondary( center_y );
5643         double current_angle = starting_angle;
5644         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
5645             final PhylogenyNode n = it.next();
5646             n.setXSecondary( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
5647             n.setYSecondary( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
5648             _urt_nodeid_angle_map.put( n.getId(), current_angle );
5649             current_angle += ( TWO_PI / circ_num_ext_nodes );
5650         }
5651         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
5652     }
5653
5654     final void paintPhylogeny( final Graphics2D g,
5655                                final boolean to_pdf,
5656                                final boolean to_graphics_file,
5657                                final int graphics_file_width,
5658                                final int graphics_file_height,
5659                                final int graphics_file_x,
5660                                final int graphics_file_y ) {
5661         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5662             return;
5663         }
5664         if ( _control_panel.isShowSequenceRelations() ) {
5665             _query_sequence = _control_panel.getSelectedQuerySequence();
5666         }
5667         // Color the background
5668         if ( !to_pdf ) {
5669             final Rectangle r = getVisibleRect();
5670             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
5671                 g.setColor( getTreeColorSet().getBackgroundColor() );
5672                 if ( !to_graphics_file ) {
5673                     g.fill( r );
5674                 }
5675                 else {
5676                     if ( getOptions().isPrintBlackAndWhite() ) {
5677                         g.setColor( Color.WHITE );
5678                     }
5679                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
5680                 }
5681             }
5682             else {
5683                 if ( !to_graphics_file ) {
5684                     g.setPaint( new GradientPaint( r.x,
5685                                                    r.y,
5686                                                    getTreeColorSet().getBackgroundColor(),
5687                                                    r.x,
5688                                                    r.y + r.height,
5689                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
5690                     g.fill( r );
5691                 }
5692                 else {
5693                     g.setPaint( new GradientPaint( graphics_file_x,
5694                                                    graphics_file_y,
5695                                                    getTreeColorSet().getBackgroundColor(),
5696                                                    graphics_file_x,
5697                                                    graphics_file_y + graphics_file_height,
5698                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
5699                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
5700                 }
5701             }
5702             setupStroke( g );
5703         }
5704         else {
5705             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
5706         }
5707         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
5708                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5709             _external_node_index = 0;
5710             // Position starting X of tree
5711             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/ ) {
5712                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
5713             }
5714             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
5715                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE
5716                         + ( _phylogeny.getRoot().getDistanceToParent() * getXcorrectionFactor() ) ) );
5717             }
5718             else {
5719                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
5720             }
5721             // Position starting Y of tree
5722             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
5723                     + ( TreePanel.MOVE / 2.0f ) );
5724             final int dynamic_hiding_factor = calcDynamicHidingFactor();
5725             if ( getControlPanel().isDynamicallyHideData() ) {
5726                 if ( dynamic_hiding_factor > 1 ) {
5727                     getControlPanel().setDynamicHidingIsOn( true );
5728                 }
5729                 else {
5730                     getControlPanel().setDynamicHidingIsOn( false );
5731                 }
5732             }
5733             if ( _nodes_in_preorder == null ) {
5734                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
5735                 int i = 0;
5736                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
5737                     _nodes_in_preorder[ i++ ] = it.next();
5738                 }
5739             }
5740             final boolean disallow_shortcutting = ( dynamic_hiding_factor < 40 )
5741                     /* || getControlPanel().isUseVisualStyles() || getOptions().isShowDefaultNodeShapesForMarkedNodes()*/ //TODO check if this is really not needed.
5742                     || to_graphics_file || to_pdf;
5743             for( final PhylogenyNode element : _nodes_in_preorder ) {
5744                 paintNodeRectangular( g,
5745                                       element,
5746                                       to_pdf,
5747                                       getControlPanel().isDynamicallyHideData() && ( dynamic_hiding_factor > 1 ),
5748                                       dynamic_hiding_factor,
5749                                       to_graphics_file,
5750                                       disallow_shortcutting );
5751             }
5752             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
5753                 if ( !( to_graphics_file || to_pdf ) ) {
5754                     paintScale( g,
5755                                 getVisibleRect().x,
5756                                 getVisibleRect().y + getVisibleRect().height,
5757                                 to_pdf,
5758                                 to_graphics_file );
5759                 }
5760                 else {
5761                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
5762                 }
5763             }
5764             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5765                 paintPhylogenyLite( g );
5766             }
5767         }
5768         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5769             if ( getControlPanel().getDynamicallyHideData() != null ) {
5770                 getControlPanel().setDynamicHidingIsOn( false );
5771             }
5772             final double angle = getStartingAngle();
5773             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
5774             _dynamic_hiding_factor = 0;
5775             if ( getControlPanel().isDynamicallyHideData() ) {
5776                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5
5777                         * getPhylogeny().getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
5778             }
5779             if ( getControlPanel().getDynamicallyHideData() != null ) {
5780                 if ( _dynamic_hiding_factor > 1 ) {
5781                     getControlPanel().setDynamicHidingIsOn( true );
5782                 }
5783                 else {
5784                     getControlPanel().setDynamicHidingIsOn( false );
5785                 }
5786             }
5787             paintUnrooted( _phylogeny.getRoot(),
5788                            angle,
5789                            ( float ) ( angle + ( 2 * Math.PI ) ),
5790                            radial_labels,
5791                            g,
5792                            to_pdf,
5793                            to_graphics_file );
5794             if ( getOptions().isShowScale() ) {
5795                 if ( !( to_graphics_file || to_pdf ) ) {
5796                     paintScale( g,
5797                                 getVisibleRect().x,
5798                                 getVisibleRect().y + getVisibleRect().height,
5799                                 to_pdf,
5800                                 to_graphics_file );
5801                 }
5802                 else {
5803                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
5804                 }
5805             }
5806             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5807                 g.setColor( getTreeColorSet().getOvColor() );
5808                 paintUnrootedLite( _phylogeny.getRoot(),
5809                                    angle,
5810                                    angle + ( 2 * Math.PI ),
5811                                    g,
5812                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
5813                 paintOvRectangle( g );
5814             }
5815         }
5816         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
5817             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() )
5818                     / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
5819             final int d = radius + MOVE + getLongestExtNodeInfo();
5820             _dynamic_hiding_factor = 0;
5821             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
5822                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5
5823                         * getPhylogeny().getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
5824             }
5825             if ( getControlPanel().getDynamicallyHideData() != null ) {
5826                 if ( _dynamic_hiding_factor > 1 ) {
5827                     getControlPanel().setDynamicHidingIsOn( true );
5828                 }
5829                 else {
5830                     getControlPanel().setDynamicHidingIsOn( false );
5831                 }
5832             }
5833             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
5834             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5835                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
5836                         : getOvMaxWidth() / 2 );
5837                 double x_scale = 1.0;
5838                 double y_scale = 1.0;
5839                 int x_pos = getVisibleRect().x + getOvXPosition();
5840                 int y_pos = getVisibleRect().y + getOvYPosition();
5841                 if ( getWidth() > getHeight() ) {
5842                     x_scale = ( double ) getHeight() / getWidth();
5843                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
5844                 }
5845                 else {
5846                     y_scale = ( double ) getWidth() / getHeight();
5847                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
5848                 }
5849                 _at = g.getTransform();
5850                 g.scale( x_scale, y_scale );
5851                 paintCircularLite( _phylogeny,
5852                                    getStartingAngle(),
5853                                    x_pos + radius_ov,
5854                                    y_pos + radius_ov,
5855                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo()
5856                                            / ( getVisibleRect().width / getOvRectangle().getWidth() ) ) ),
5857                                    g );
5858                 g.setTransform( _at );
5859                 paintOvRectangle( g );
5860             }
5861         }
5862     }
5863
5864     final void recalculateMaxDistanceToRoot() {
5865         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
5866         if ( getPhylogeny().getRoot().getDistanceToParent() > 0 ) {
5867             _max_distance_to_root += getPhylogeny().getRoot().getDistanceToParent();
5868         }
5869     }
5870
5871     /**
5872      * Remove all edit-node frames
5873      */
5874     final void removeAllEditNodeJFrames() {
5875         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
5876             if ( _node_frames[ i ] != null ) {
5877                 _node_frames[ i ].dispose();
5878                 _node_frames[ i ] = null;
5879             }
5880         }
5881         _node_frame_index = 0;
5882     }
5883
5884     /**
5885      * Remove a node-edit frame.
5886      */
5887     final void removeEditNodeFrame( final int i ) {
5888         _node_frame_index--;
5889         _node_frames[ i ] = null;
5890         if ( i < _node_frame_index ) {
5891             for( int j = 0; j < ( _node_frame_index - 1 ); j++ ) {
5892                 _node_frames[ j ] = _node_frames[ j + 1 ];
5893             }
5894             _node_frames[ _node_frame_index ] = null;
5895         }
5896     }
5897
5898     final void reRoot( final PhylogenyNode node ) {
5899         if ( !getPhylogeny().isRerootable() ) {
5900             JOptionPane.showMessageDialog( this,
5901                                            "This is not rerootable",
5902                                            "Not rerootable",
5903                                            JOptionPane.WARNING_MESSAGE );
5904             return;
5905         }
5906         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5907             JOptionPane.showMessageDialog( this,
5908                                            "Cannot reroot in unrooted display type",
5909                                            "Attempt to reroot tree in unrooted display",
5910                                            JOptionPane.WARNING_MESSAGE );
5911             return;
5912         }
5913         getPhylogeny().reRoot( node );
5914         getPhylogeny().recalculateNumberOfExternalDescendants( true );
5915         resetNodeIdToDistToLeafMap();
5916         setNodeInPreorderToNull();
5917         resetPreferredSize();
5918         getMainPanel().adjustJScrollPane();
5919         setEdited( true );
5920         repaint();
5921         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
5922             getControlPanel().showWhole();
5923         }
5924     }
5925
5926     final void resetNodeIdToDistToLeafMap() {
5927         _nodeid_dist_to_leaf = new HashMap<Long, Short>();
5928     }
5929
5930     final void resetPreferredSize() {
5931         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
5932             return;
5933         }
5934         int x = 0;
5935         int y = 0;
5936         y = TreePanel.MOVE
5937                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
5938         if ( getControlPanel().isDrawPhylogram() ) {
5939             x = TreePanel.MOVE + getLongestExtNodeInfo()
5940                     + ForesterUtil.roundToInt( ( getXcorrectionFactor()
5941                             * getPhylogeny().calculateHeight( !_options.isCollapsedWithAverageHeigh() ) )
5942                             + getXdistance() );
5943         }
5944         else {
5945             if ( !isNonLinedUpCladogram() ) {
5946                 x = TreePanel.MOVE + getLongestExtNodeInfo() + ForesterUtil
5947                         .roundToInt( getXdistance() * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
5948             }
5949             else {
5950                 x = TreePanel.MOVE + getLongestExtNodeInfo() + ForesterUtil
5951                         .roundToInt( getXdistance() * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
5952             }
5953         }
5954         setPreferredSize( new Dimension( x, y ) );
5955     }
5956
5957     final void selectNode( final PhylogenyNode node ) {
5958         if ( ( getFoundNodes0() != null ) && getFoundNodes0().contains( node.getId() ) ) {
5959             getFoundNodes0().remove( node.getId() );
5960             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
5961             if ( getFoundNodes0().size() < 1 ) {
5962                 getControlPanel().searchReset0();
5963             }
5964         }
5965         else {
5966             getControlPanel().getSearchFoundCountsLabel0().setVisible( true );
5967             getControlPanel().getSearchResetButton0().setEnabled( true );
5968             getControlPanel().getSearchResetButton0().setVisible( true );
5969             if ( getFoundNodes0() == null ) {
5970                 setFoundNodes0( new HashSet<Long>() );
5971             }
5972             getFoundNodes0().add( node.getId() );
5973             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
5974         }
5975     }
5976
5977     final void setArrowCursor() {
5978         setCursor( ARROW_CURSOR );
5979         repaint();
5980     }
5981
5982     final void setControlPanel( final ControlPanel atv_control ) {
5983         _control_panel = atv_control;
5984     }
5985
5986     void setCurrentExternalNodesDataBuffer( final StringBuilder sb ) {
5987         increaseCurrentExternalNodesDataBufferChangeCounter();
5988         _current_external_nodes_data_buffer = sb;
5989     }
5990
5991     final void setFoundNodes0( final Set<Long> found_nodes ) {
5992         _found_nodes_0 = found_nodes;
5993     }
5994
5995     final void setFoundNodes1( final Set<Long> found_nodes ) {
5996         _found_nodes_1 = found_nodes;
5997     }
5998
5999     final void setInOvRect( final boolean in_ov_rect ) {
6000         _in_ov_rect = in_ov_rect;
6001     }
6002
6003     final void setLargeFonts() {
6004         getTreeFontSet().largeFonts();
6005     }
6006
6007     final void setLastMouseDragPointX( final float x ) {
6008         _last_drag_point_x = x;
6009     }
6010
6011     final void setLastMouseDragPointY( final float y ) {
6012         _last_drag_point_y = y;
6013     }
6014
6015     final void setMediumFonts() {
6016         getTreeFontSet().mediumFonts();
6017     }
6018
6019     final void setNodeInPreorderToNull() {
6020         _nodes_in_preorder = null;
6021     }
6022
6023     final void setOvOn( final boolean ov_on ) {
6024         _ov_on = ov_on;
6025     }
6026
6027     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
6028         _graphics_type = graphics_type;
6029         setTextAntialias();
6030     }
6031
6032     final void setSmallFonts() {
6033         getTreeFontSet().smallFonts();
6034     }
6035
6036     final void setStartingAngle( final double starting_angle ) {
6037         _urt_starting_angle = starting_angle;
6038     }
6039
6040     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
6041         _statistics_for_vector_data = statistics_for_expression_values;
6042     }
6043
6044     final void setSuperTinyFonts() {
6045         getTreeFontSet().superTinyFonts();
6046     }
6047
6048     final void setTextAntialias() {
6049         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
6050             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
6051                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
6052             }
6053             else {
6054                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
6055             }
6056         }
6057         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
6058             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
6059             // try {
6060             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
6061             // }
6062             // catch ( final Throwable e ) {
6063             //    _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
6064             //}
6065         }
6066         else {
6067             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
6068             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
6069         }
6070     }
6071
6072     final void setTinyFonts() {
6073         getTreeFontSet().tinyFonts();
6074     }
6075
6076     final void setTreeFile( final File treefile ) {
6077         _treefile = treefile;
6078     }
6079
6080     final void setXcorrectionFactor( final float f ) {
6081         _x_correction_factor = f;
6082     }
6083
6084     final void setXdistance( final float x ) {
6085         _x_distance = x;
6086     }
6087
6088     final void setYdistance( final float y ) {
6089         _y_distance = y;
6090     }
6091
6092     final void sortDescendants( final PhylogenyNode node ) {
6093         if ( !node.isExternal() ) {
6094             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
6095             if ( getControlPanel().isShowTaxonomyScientificNames() || getControlPanel().isShowTaxonomyCode() ) {
6096                 pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
6097             }
6098             else if ( getControlPanel().isShowSeqNames() || getControlPanel().isShowSeqSymbols()
6099                     || getControlPanel().isShowGeneNames() ) {
6100                 pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
6101             }
6102             PhylogenyMethods.sortNodeDescendents( node, pri );
6103             setNodeInPreorderToNull();
6104             _phylogeny.externalNodesHaveChanged();
6105             _phylogeny.clearHashIdToNodeMap();
6106             _phylogeny.recalculateNumberOfExternalDescendants( true );
6107             resetNodeIdToDistToLeafMap();
6108             setEdited( true );
6109         }
6110         repaint();
6111     }
6112
6113     final void subTree( final PhylogenyNode node ) {
6114         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
6115             JOptionPane.showMessageDialog( this,
6116                                            "Cannot get a sub/super tree in unrooted display",
6117                                            "Attempt to get sub/super tree in unrooted display",
6118                                            JOptionPane.WARNING_MESSAGE );
6119             return;
6120         }
6121         if ( node.isExternal() ) {
6122             JOptionPane.showMessageDialog( this,
6123                                            "Cannot get a subtree of a external node",
6124                                            "Attempt to get subtree of external node",
6125                                            JOptionPane.WARNING_MESSAGE );
6126             return;
6127         }
6128         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
6129             JOptionPane.showMessageDialog( this,
6130                                            "Cannot get a subtree of the root node",
6131                                            "Attempt to get subtree of root node",
6132                                            JOptionPane.WARNING_MESSAGE );
6133             return;
6134         }
6135         setNodeInPreorderToNull();
6136         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
6137             _sub_phylogenies[ _subtree_index ] = _phylogeny;
6138             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
6139             ++_subtree_index;
6140             _phylogeny = TreePanelUtil.subTree( node, _phylogeny );
6141             if ( _phylogeny.getRoot().isCollapse() ) {
6142                 _phylogeny.getRoot().setCollapse( false );
6143             }
6144             _phylogeny.externalNodesHaveChanged();
6145             _phylogeny.clearHashIdToNodeMap();
6146             _phylogeny.recalculateNumberOfExternalDescendants( true );
6147             updateSubSuperTreeButton();
6148             getMainPanel().getControlPanel().search0();
6149             getMainPanel().getControlPanel().search1();
6150             resetRankCollapseRankValue();
6151             resetDepthCollapseDepthValue();
6152             getMainPanel().getControlPanel().updateDomainStructureEvaluethresholdDisplay();
6153             getMainPanel().getControlPanel().updateDepthCollapseDepthDisplay();
6154             getMainPanel().getControlPanel().updateRankCollapseRankDisplay();
6155         }
6156         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
6157             superTree();
6158         }
6159         _main_panel.getControlPanel().showWhole();
6160         repaint();
6161     }
6162
6163     final void superTree() {
6164         setNodeInPreorderToNull();
6165         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
6166         for( final PhylogenyNode n : temp_root.getDescendants() ) {
6167             n.setParent( temp_root );
6168         }
6169         _sub_phylogenies[ _subtree_index ] = null;
6170         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
6171         _phylogeny = _sub_phylogenies[ --_subtree_index ];
6172         _phylogeny.externalNodesHaveChanged();
6173         _phylogeny.clearHashIdToNodeMap();
6174         _phylogeny.recalculateNumberOfExternalDescendants( true );
6175         getMainPanel().getControlPanel().search0();
6176         getMainPanel().getControlPanel().search1();
6177         resetRankCollapseRankValue();
6178         resetDepthCollapseDepthValue();
6179         getMainPanel().getControlPanel().updateDomainStructureEvaluethresholdDisplay();
6180         getMainPanel().getControlPanel().updateDepthCollapseDepthDisplay();
6181         getMainPanel().getControlPanel().updateRankCollapseRankDisplay();
6182         updateSubSuperTreeButton();
6183     }
6184
6185     final void orderSubtree( final PhylogenyNode node ) {
6186         if ( node.isExternal() ) {
6187             return;
6188         }
6189         DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
6190         if ( getControlPanel().isShowTaxonomyScientificNames() || getControlPanel().isShowTaxonomyCode() ) {
6191             pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
6192         }
6193         else if ( getControlPanel().isShowSeqNames() || getControlPanel().isShowSeqSymbols()
6194                 || getControlPanel().isShowGeneNames() ) {
6195             pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
6196         }
6197         PhylogenyMethods.orderAppearanceX( node, true, pri );
6198         setNodeInPreorderToNull();
6199         getPhylogeny().externalNodesHaveChanged();
6200         getPhylogeny().clearHashIdToNodeMap();
6201         getPhylogeny().recalculateNumberOfExternalDescendants( true );
6202         resetNodeIdToDistToLeafMap();
6203         setEdited( true );
6204         getControlPanel().displayedPhylogenyMightHaveChanged( true );
6205         repaint();
6206     }
6207
6208     final void swap( final PhylogenyNode node ) {
6209         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
6210             return;
6211         }
6212         if ( node.getNumberOfDescendants() > 2 ) {
6213             JOptionPane.showMessageDialog( this,
6214                                            "Cannot swap descendants of nodes with more than 2 descendants",
6215                                            "Cannot swap descendants",
6216                                            JOptionPane.ERROR_MESSAGE );
6217             return;
6218         }
6219         if ( !node.isExternal() ) {
6220             node.swapChildren();
6221             setNodeInPreorderToNull();
6222             _phylogeny.externalNodesHaveChanged();
6223             _phylogeny.clearHashIdToNodeMap();
6224             _phylogeny.recalculateNumberOfExternalDescendants( true );
6225             resetNodeIdToDistToLeafMap();
6226             setEdited( true );
6227         }
6228         repaint();
6229     }
6230
6231     final void taxColor() {
6232         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
6233             return;
6234         }
6235         setWaitCursor();
6236         TreePanelUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
6237         _control_panel.setColorBranches( true );
6238         if ( _control_panel.getUseVisualStylesCb() != null ) {
6239             _control_panel.getUseVisualStylesCb().setSelected( true );
6240         }
6241         setEdited( true );
6242         setArrowCursor();
6243         repaint();
6244     }
6245
6246     final void updateOvSettings() {
6247         switch ( getOptions().getOvPlacement() ) {
6248             case LOWER_LEFT:
6249                 setOvXPosition( OV_BORDER );
6250                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
6251                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
6252                 break;
6253             case LOWER_RIGHT:
6254                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
6255                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
6256                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
6257                 break;
6258             case UPPER_RIGHT:
6259                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
6260                 setOvYPosition( OV_BORDER );
6261                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
6262                 break;
6263             default:
6264                 setOvXPosition( OV_BORDER );
6265                 setOvYPosition( OV_BORDER );
6266                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
6267                 break;
6268         }
6269     }
6270
6271     final void updateOvSizes() {
6272         if ( ( getWidth() > ( 1.05 * getVisibleRect().width ) )
6273                 || ( getHeight() > ( 1.05 * getVisibleRect().height ) ) ) {
6274             setOvOn( true );
6275             float l = getLongestExtNodeInfo();
6276             final float w_ratio = getOvMaxWidth() / getWidth();
6277             l *= w_ratio;
6278             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
6279             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
6280             float ov_xdist = 0;
6281             if ( !isNonLinedUpCladogram() ) {
6282                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
6283             }
6284             else {
6285                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
6286             }
6287             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
6288             if ( ov_xdist < 0.0 ) {
6289                 ov_xdist = 0.0f;
6290             }
6291             if ( ydist < 0.0 ) {
6292                 ydist = 0.0f;
6293             }
6294             setOvXDistance( ov_xdist );
6295             final double height = _phylogeny.calculateHeight( !_options.isCollapsedWithAverageHeigh() );
6296             if ( height > 0 ) {
6297                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
6298                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
6299             }
6300             else {
6301                 setOvXcorrectionFactor( 0 );
6302             }
6303         }
6304         else {
6305             setOvOn( false );
6306         }
6307     }
6308
6309     void updateSetOfCollapsedExternalNodes() {
6310         final Phylogeny phy = getPhylogeny();
6311         _collapsed_external_nodeid_set.clear();
6312         if ( phy != null ) {
6313             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
6314                 final PhylogenyNode ext_node = it.next();
6315                 PhylogenyNode n = ext_node;
6316                 while ( !n.isRoot() ) {
6317                     if ( n.isCollapse() ) {
6318                         _collapsed_external_nodeid_set.add( ext_node.getId() );
6319                         ext_node.setCollapse( true );
6320                         continue E;
6321                     }
6322                     n = n.getParent();
6323                 }
6324             }
6325         }
6326     }
6327
6328     final void updateSubSuperTreeButton() {
6329         if ( _subtree_index < 1 ) {
6330             getControlPanel().deactivateButtonToReturnToSuperTree();
6331         }
6332         else {
6333             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
6334         }
6335     }
6336
6337     final void updateButtonToUncollapseAll() {
6338         if ( PhylogenyMethods.isHasCollapsedNodes( _phylogeny ) ) {
6339             getControlPanel().activateButtonToUncollapseAll();
6340         }
6341         else {
6342             getControlPanel().deactivateButtonToUncollapseAll();
6343         }
6344     }
6345
6346     final void zoomInDomainStructure() {
6347         if ( _domain_structure_width < 2000 ) {
6348             _domain_structure_width *= 1.2;
6349         }
6350     }
6351
6352     final void zoomOutDomainStructure() {
6353         if ( _domain_structure_width > 20 ) {
6354             _domain_structure_width *= 0.8;
6355         }
6356     }
6357
6358     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
6359         if ( node.getNodeData().getNodeVisualData() == null ) {
6360             node.getNodeData().setNodeVisualData( new NodeVisualData() );
6361         }
6362         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
6363     }
6364
6365     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
6366         g.drawString( str, x, y );
6367     }
6368
6369     final private void drawStringX( final String str, final float x, final float y, final Graphics2D g ) {
6370         //TODO
6371         //FIXME
6372         if ( getAttributedStringMap() == null /*&& getAttributedStringMap().containsKey(str) */ ) {
6373             final AttributedString as = new AttributedString( str );
6374             //Font plainFont = new Font("Times New Roman", Font.PLAIN, 24);
6375             as.addAttribute( TextAttribute.FONT, g.getFont() );
6376             as.addAttribute( TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 1, 3 );
6377             as.addAttribute( TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, 3, 4 );
6378             as.addAttribute( TextAttribute.FOREGROUND, Color.BLUE, 1, 2 );
6379             as.addAttribute( TextAttribute.FOREGROUND, Color.PINK, 3, 5 );
6380             as.addAttribute( TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, 2, 4 );
6381             g.drawString( as.getIterator(), x, y );
6382         }
6383         else {
6384             g.drawString( str, x, y );
6385         }
6386     }
6387
6388     private final Map<String, AttributedString> getAttributedStringMap() {
6389         return _attributed_string_map;
6390     }
6391
6392     private final void setAttributedStringMap( final Map<String, AttributedString> attributed_string_map ) {
6393         _attributed_string_map = attributed_string_map;
6394     }
6395
6396     final private static boolean plusPressed( final int key_code ) {
6397         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
6398                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON )
6399                 || ( key_code == KeyEvent.VK_1 ) );
6400     }
6401
6402     public void decreaseDepthCollapseLevel() {
6403         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6404             if ( _depth_collapse_level <= 1 ) {
6405                 _depth_collapse_level = PhylogenyMethods.calculateMaxDepth( _phylogeny );
6406                 uncollapseAll();
6407             }
6408             else {
6409                 --_depth_collapse_level;
6410                 PhylogenyMethods.collapseToDepth( _phylogeny, _depth_collapse_level );
6411             }
6412         }
6413     }
6414
6415     public void increaseDepthCollapseLevel() {
6416         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6417             final int max = PhylogenyMethods.calculateMaxDepth( _phylogeny );
6418             if ( _depth_collapse_level >= max ) {
6419                 _depth_collapse_level = 1;
6420             }
6421             else {
6422                 ++_depth_collapse_level;
6423             }
6424             PhylogenyMethods.collapseToDepth( _phylogeny, _depth_collapse_level );
6425         }
6426     }
6427
6428     public void decreaseRankCollapseLevel() {
6429         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6430             final String ranks[] = PhylogenyMethods.obtainPresentRanksSorted( _phylogeny );
6431             if ( ranks.length > 1 ) {
6432                 if ( _rank_collapse_level <= 0 ) {
6433                     _rank_collapse_level = ranks.length - 1;
6434                     uncollapseAll();
6435                 }
6436                 else {
6437                     --_rank_collapse_level;
6438                     PhylogenyMethods.collapseToRank( _phylogeny,
6439                                                      mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6440                 }
6441             }
6442         }
6443     }
6444
6445     public void increaseRankCollapseLevel() {
6446         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6447             final String ranks[] = PhylogenyMethods.obtainPresentRanksSorted( _phylogeny );
6448             if ( ranks.length > 1 ) {
6449                 if ( _rank_collapse_level >= ( ranks.length - 1 ) ) {
6450                     _rank_collapse_level = 0;
6451                     PhylogenyMethods.collapseToRank( _phylogeny,
6452                                                      mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6453                 }
6454                 else if ( _rank_collapse_level == ( ranks.length - 2 ) ) {
6455                     ++_rank_collapse_level;
6456                     uncollapseAll();
6457                 }
6458                 else {
6459                     ++_rank_collapse_level;
6460                     PhylogenyMethods.collapseToRank( _phylogeny,
6461                                                      mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6462                 }
6463             }
6464         }
6465     }
6466
6467     private final static int mapToAbsoluteRankLevel( final String present_ranks_sorted[],
6468                                                      final int rank_collapse_level ) {
6469         final String rank_str = present_ranks_sorted[ rank_collapse_level ];
6470         if ( !TaxonomyUtil.RANK_TO_INT.containsKey( rank_str ) ) {
6471             throw new IllegalStateException( "unexpected exception: cannot find rank " + rank_str );
6472         }
6473         return TaxonomyUtil.RANK_TO_INT.get( rank_str );
6474     }
6475
6476     private final void uncollapseAll() {
6477         final PhylogenyNodeIterator it = new PreorderTreeIterator( _phylogeny );
6478         while ( it.hasNext() ) {
6479             it.next().setCollapse( false );
6480         }
6481     }
6482
6483     final int resetDepthCollapseDepthValue() {
6484         return _depth_collapse_level = -1;
6485     }
6486
6487     final int getDepthCollapseDepthValue() {
6488         return _depth_collapse_level;
6489     }
6490
6491     final void setDepthCollapseDepthValue( final int depth_collapse_level ) {
6492         _depth_collapse_level = depth_collapse_level;
6493     }
6494
6495     final int resetRankCollapseRankValue() {
6496         return _rank_collapse_level = -1;
6497     }
6498
6499     final int getRankCollapseRankValue() {
6500         return _rank_collapse_level;
6501     }
6502
6503     final void setRankCollapseRankValue( final int rank_collapse_level ) {
6504         _rank_collapse_level = rank_collapse_level;
6505     }
6506 }