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