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