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