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