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