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