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