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