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