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