in progress
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanel.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.BasicStroke;
29 import java.awt.Color;
30 import java.awt.Cursor;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.FontMetrics;
34 import java.awt.GradientPaint;
35 import java.awt.Graphics;
36 import java.awt.Graphics2D;
37 import java.awt.Point;
38 import java.awt.Rectangle;
39 import java.awt.RenderingHints;
40 import java.awt.Stroke;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.awt.event.FocusAdapter;
44 import java.awt.event.FocusEvent;
45 import java.awt.event.InputEvent;
46 import java.awt.event.KeyAdapter;
47 import java.awt.event.KeyEvent;
48 import java.awt.event.MouseEvent;
49 import java.awt.event.MouseWheelEvent;
50 import java.awt.event.MouseWheelListener;
51 import java.awt.font.FontRenderContext;
52 import java.awt.font.TextAttribute;
53 import java.awt.font.TextLayout;
54 import java.awt.geom.AffineTransform;
55 import java.awt.geom.Arc2D;
56 import java.awt.geom.CubicCurve2D;
57 import java.awt.geom.Ellipse2D;
58 import java.awt.geom.Line2D;
59 import java.awt.geom.Path2D;
60 import java.awt.geom.QuadCurve2D;
61 import java.awt.geom.Rectangle2D;
62 import java.awt.image.BufferedImage;
63 import java.awt.print.PageFormat;
64 import java.awt.print.Printable;
65 import java.awt.print.PrinterException;
66 import java.io.File;
67 import java.io.IOException;
68 import java.io.UnsupportedEncodingException;
69 import java.net.URI;
70 import java.net.URISyntaxException;
71 import java.net.URLEncoder;
72 import java.text.AttributedString;
73 import java.text.DecimalFormat;
74 import java.text.DecimalFormatSymbols;
75 import java.text.NumberFormat;
76 import java.util.ArrayList;
77 import java.util.Collections;
78 import java.util.HashMap;
79 import java.util.HashSet;
80 import java.util.Hashtable;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.Set;
84 import java.util.SortedSet;
85
86 import javax.swing.BorderFactory;
87 import javax.swing.JApplet;
88 import javax.swing.JColorChooser;
89 import javax.swing.JDialog;
90 import javax.swing.JMenuItem;
91 import javax.swing.JOptionPane;
92 import javax.swing.JPanel;
93 import javax.swing.JPopupMenu;
94 import javax.swing.JTextArea;
95 import javax.swing.Popup;
96 import javax.swing.PopupFactory;
97
98 import org.forester.archaeopteryx.Configuration.EXT_NODE_DATA_RETURN_ON;
99 import org.forester.archaeopteryx.ControlPanel.NodeClickAction;
100 import org.forester.archaeopteryx.Options.CLADOGRAM_TYPE;
101 import org.forester.archaeopteryx.Options.NODE_LABEL_DIRECTION;
102 import org.forester.archaeopteryx.Options.PHYLOGENY_GRAPHICS_TYPE;
103 import org.forester.archaeopteryx.phylogeny.data.RenderableDomainArchitecture;
104 import org.forester.archaeopteryx.phylogeny.data.RenderableMsaSequence;
105 import org.forester.archaeopteryx.phylogeny.data.RenderableVector;
106 import org.forester.archaeopteryx.tools.Blast;
107 import org.forester.archaeopteryx.tools.ImageLoader;
108 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
109 import org.forester.io.writers.SequenceWriter;
110 import org.forester.phylogeny.Phylogeny;
111 import org.forester.phylogeny.PhylogenyMethods;
112 import org.forester.phylogeny.PhylogenyMethods.DESCENDANT_SORT_PRIORITY;
113 import org.forester.phylogeny.PhylogenyNode;
114 import org.forester.phylogeny.data.Accession;
115 import org.forester.phylogeny.data.Annotation;
116 import org.forester.phylogeny.data.BranchColor;
117 import org.forester.phylogeny.data.Confidence;
118 import org.forester.phylogeny.data.DomainArchitecture;
119 import org.forester.phylogeny.data.Event;
120 import org.forester.phylogeny.data.NodeDataField;
121 import org.forester.phylogeny.data.NodeVisualData;
122 import org.forester.phylogeny.data.NodeVisualData.NodeFill;
123 import org.forester.phylogeny.data.NodeVisualData.NodeShape;
124 import org.forester.phylogeny.data.PhylogenyDataUtil;
125 import org.forester.phylogeny.data.PropertiesMap;
126 import org.forester.phylogeny.data.Property;
127 import org.forester.phylogeny.data.ProteinDomain;
128 import org.forester.phylogeny.data.Sequence;
129 import org.forester.phylogeny.data.SequenceRelation;
130 import org.forester.phylogeny.data.Taxonomy;
131 import org.forester.phylogeny.data.Uri;
132 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
133 import org.forester.phylogeny.iterators.PreorderTreeIterator;
134 import org.forester.util.BasicDescriptiveStatistics;
135 import org.forester.util.DescriptiveStatistics;
136 import org.forester.util.ForesterConstants;
137 import org.forester.util.ForesterUtil;
138 import org.forester.util.SequenceAccessionTools;
139 import org.forester.util.TaxonomyUtil;
140
141 public final class TreePanel extends JPanel implements ActionListener, MouseWheelListener, Printable {
142
143     final private class NodeColorizationActionListener implements ActionListener {
144
145         List<PhylogenyNode> _additional_nodes = null;
146         JColorChooser       _chooser          = null;
147         PhylogenyNode       _node             = null;
148
149         NodeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
150             _chooser = chooser;
151             _node = node;
152         }
153
154         NodeColorizationActionListener( final JColorChooser chooser,
155                                         final PhylogenyNode node,
156                                         final List<PhylogenyNode> additional_nodes ) {
157             _chooser = chooser;
158             _node = node;
159             _additional_nodes = additional_nodes;
160         }
161
162         @Override
163         public void actionPerformed( final ActionEvent e ) {
164             final Color c = _chooser.getColor();
165             if ( c != null ) {
166                 colorizeNodes( c, _node, _additional_nodes );
167             }
168         }
169     }
170
171     final private class SubtreeColorizationActionListener implements ActionListener {
172
173         List<PhylogenyNode> _additional_nodes = null;
174         JColorChooser       _chooser          = null;
175         PhylogenyNode       _node             = null;
176
177         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
178             _chooser = chooser;
179             _node = node;
180         }
181
182         SubtreeColorizationActionListener( final JColorChooser chooser,
183                                            final PhylogenyNode node,
184                                            final List<PhylogenyNode> additional_nodes ) {
185             _chooser = chooser;
186             _node = node;
187             _additional_nodes = additional_nodes;
188         }
189
190         @Override
191         public void actionPerformed( final ActionEvent e ) {
192             final Color c = _chooser.getColor();
193             if ( c != null ) {
194                 colorizeSubtree( c, _node, _additional_nodes );
195             }
196         }
197     }
198     public final static boolean           SPECIAL_DOMAIN_COLORING                            = true;
199     final static Cursor                   ARROW_CURSOR                                       = Cursor
200             .getPredefinedCursor( Cursor.DEFAULT_CURSOR );
201     final static Cursor                   CUT_CURSOR                                         = Cursor
202             .getPredefinedCursor( Cursor.CROSSHAIR_CURSOR );
203     final static Cursor                   HAND_CURSOR                                        = Cursor
204             .getPredefinedCursor( Cursor.HAND_CURSOR );
205     final static Cursor                   MOVE_CURSOR                                        = Cursor
206             .getPredefinedCursor( Cursor.MOVE_CURSOR );
207     final static Cursor                   WAIT_CURSOR                                        = Cursor
208             .getPredefinedCursor( Cursor.WAIT_CURSOR );
209     final private static double           _180_OVER_PI                                       = 180.0 / Math.PI;
210     private static final float            ANGLE_ROTATION_UNIT                                = ( float ) ( Math.PI
211             / 32 );
212     private final static int              CONFIDENCE_LEFT_MARGIN                             = 4;
213     private final static int              EURO_D                                             = 10;
214     private final static NumberFormat     FORMATTER_BRANCH_LENGTH;
215     private final static NumberFormat     FORMATTER_CONFIDENCE;
216     private static final float            HALF_PI                                            = ( float ) ( Math.PI
217             / 2.0 );
218     private final static int              LIMIT_FOR_HQ_RENDERING                             = 2000;
219     private final static int              MAX_NODE_FRAMES                                    = 10;
220     private final static int              MAX_SUBTREES                                       = 100;
221     private final static int              MIN_ROOT_LENGTH                                    = 3;
222     private final static int              MOVE                                               = 20;
223     private final static String           NODE_POPMENU_NODE_CLIENT_PROPERTY                  = "node";
224     private static final float            ONEHALF_PI                                         = ( float ) ( 1.5
225             * Math.PI );
226     private static final short            OV_BORDER                                          = 10;
227     private final static double           OVERVIEW_FOUND_NODE_BOX_SIZE                       = 2;
228     private final static double           OVERVIEW_FOUND_NODE_BOX_SIZE_HALF                  = 1;
229     private static final float            PI                                                 = ( float ) ( Math.PI );
230     final private static Font             POPUP_FONT                                         = new Font( Configuration
231             .getDefaultFontFamilyName(), Font.PLAIN, 12 );
232     private static final float            ROUNDED_D                                          = 8;
233     private final static long             serialVersionUID                                   = -978349745916505029L;
234     private static final BasicStroke      STROKE_0025                                        = new BasicStroke( 0.025f );
235     private static final BasicStroke      STROKE_005                                         = new BasicStroke( 0.05f );
236     private static final BasicStroke      STROKE_01                                          = new BasicStroke( 0.1f );
237     private static final BasicStroke      STROKE_025                                         = new BasicStroke( 0.25f );
238     private static final BasicStroke      STROKE_05                                          = new BasicStroke( 0.5f );
239     private static final BasicStroke      STROKE_075                                         = new BasicStroke( 0.75f );
240     private static final BasicStroke      STROKE_1                                           = new BasicStroke( 1f );
241     private static final BasicStroke      STROKE_2                                           = new BasicStroke( 2f );
242     private static final 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 {
2403             c = getTreeColorSet().getCollapseFillColor();
2404         }
2405         double d = node.getAllExternalDescendants().size();
2406         float xxx;
2407         double s = 0;
2408         if ( getControlPanel().isDrawPhylogram() ) {
2409             if ( d > 1000 ) {
2410                 d = 0.75 * _y_distance;
2411             }
2412             else {
2413                 d = 0.25 * Math.log10( d ) * _y_distance;
2414             }
2415             final float half_box_size = 0.5f * getOptions().getDefaultNodeShapeSize();
2416             if ( d < half_box_size ) {
2417                 d = half_box_size;
2418             }
2419             _polygon.reset();
2420             final float xx = node.getXcoord() - ( getOptions().getDefaultNodeShapeSize() );
2421             xxx = xx > ( node.getParent().getXcoord() + 1 ) ? xx : node.getParent().getXcoord() + 1;
2422             _polygon.moveTo( xxx, node.getYcoord() + 0.5 );
2423             _polygon.lineTo( xxx, node.getYcoord() - 0.5 );
2424             s = _options.isCollapsedWithAverageHeigh()
2425                     ? PhylogenyMethods.calculateAverageTreeHeight( node ) * _x_correction_factor : 1;
2426             _polygon.lineTo( node.getXcoord() + s, node.getYcoord() - d );
2427             _polygon.lineTo( node.getXcoord() + s, node.getYcoord() + d );
2428             _polygon.closePath();
2429         }
2430         else {
2431             if ( d > 1000 ) {
2432                 d = _y_distance;
2433             }
2434             else {
2435                 d = ( Math.log10( d ) * _y_distance ) / 2.5;
2436             }
2437             final int box_size = getOptions().getDefaultNodeShapeSize() + 1;
2438             if ( d < box_size ) {
2439                 d = box_size;
2440             }
2441             final float xx = node.getXcoord() - ( 2 * box_size );
2442             xxx = xx > ( node.getParent().getXcoord() + 1 ) ? xx : node.getParent().getXcoord() + 1;
2443             _polygon.reset();
2444             _polygon.moveTo( xxx, node.getYcoord() );
2445             _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() - d );
2446             _polygon.lineTo( node.getXcoord() + 1, node.getYcoord() + d );
2447             _polygon.closePath();
2448         }
2449         if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.SOLID ) {
2450             g.setColor( c );
2451             g.fill( _polygon );
2452         }
2453         else if ( getOptions().getDefaultNodeFill() == NodeVisualData.NodeFill.NONE ) {
2454             g.setColor( getBackground() );
2455             g.fill( _polygon );
2456             g.setColor( c );
2457             g.draw( _polygon );
2458         }
2459         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2460             g.setPaint( new GradientPaint( xxx,
2461                                            node.getYcoord(),
2462                                            getBackground(),
2463                                            node.getXcoord(),
2464                                            ( float ) ( node.getYcoord() - d ),
2465                                            c,
2466                                            false ) );
2467             g.fill( _polygon );
2468             g.setPaint( c );
2469             g.draw( _polygon );
2470         }
2471         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes, s );
2472     }
2473
2474     final private void paintConfidenceValues( final Graphics2D g,
2475                                               final PhylogenyNode node,
2476                                               final boolean to_pdf,
2477                                               final boolean to_graphics_file ) {
2478         final List<Confidence> confidences = node.getBranchData().getConfidences();
2479         boolean not_first = false;
2480         Collections.sort( confidences );
2481         final StringBuilder sb = new StringBuilder();
2482         for( final Confidence confidence : confidences ) {
2483             if ( ForesterUtil.isEmpty( SHOW_ONLY_THIS_CONF_TYPE ) || ( !ForesterUtil.isEmpty( confidence.getType() )
2484                     && confidence.getType().equalsIgnoreCase( SHOW_ONLY_THIS_CONF_TYPE ) ) ) {
2485                 final double value = confidence.getValue();
2486                 if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2487                     if ( value < getOptions().getMinConfidenceValue() ) {
2488                         return;
2489                     }
2490                     if ( not_first ) {
2491                         sb.append( "/" );
2492                     }
2493                     else {
2494                         not_first = true;
2495                     }
2496                     sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil
2497                             .round( value, getOptions().getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2498                     if ( getOptions().isShowConfidenceStddev() ) {
2499                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2500                             sb.append( "(" );
2501                             sb.append( FORMATTER_CONFIDENCE
2502                                     .format( ForesterUtil.round( confidence.getStandardDeviation(),
2503                                                                  getOptions()
2504                                                                          .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2505                             sb.append( ")" );
2506                         }
2507                     }
2508                 }
2509             }
2510         }
2511         if ( sb.length() > 0 ) {
2512             final float parent_x = node.getParent().getXcoord();
2513             float x = node.getXcoord();
2514             g.setFont( getTreeFontSet().getSmallFont() );
2515             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2516                 x += EURO_D;
2517             }
2518             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2519                 x += ROUNDED_D;
2520             }
2521             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2522                 g.setColor( Color.BLACK );
2523             }
2524             else {
2525                 g.setColor( getTreeColorSet().getConfidenceColor() );
2526             }
2527             final String conf_str = sb.toString();
2528             TreePanel.drawString( conf_str,
2529                                   parent_x + ( ( x - parent_x
2530                                           - getTreeFontSet().getFontMetricsSmall().stringWidth( conf_str ) ) / 2 ),
2531                                   ( node.getYcoord() + getTreeFontSet().getSmallMaxAscent() ) - 1,
2532                                   g );
2533         }
2534     }
2535
2536     final private void paintGainedAndLostCharacters( final Graphics2D g,
2537                                                      final PhylogenyNode node,
2538                                                      final String gained,
2539                                                      final String lost ) {
2540         if ( node.getParent() != null ) {
2541             final float parent_x = node.getParent().getXcoord();
2542             final float x = node.getXcoord();
2543             g.setFont( getTreeFontSet().getLargeFont() );
2544             g.setColor( getTreeColorSet().getGainedCharactersColor() );
2545             if ( AptxConstants.SPECIAL_CUSTOM ) {
2546                 g.setColor( Color.BLUE );
2547             }
2548             TreePanel.drawString( gained,
2549                                   parent_x + ( ( x - parent_x
2550                                           - getFontMetricsForLargeDefaultFont().stringWidth( gained ) ) / 2 ),
2551                                   ( node.getYcoord() - getFontMetricsForLargeDefaultFont().getMaxDescent() ),
2552                                   g );
2553             g.setColor( getTreeColorSet().getLostCharactersColor() );
2554             TreePanel.drawString( lost,
2555                                   parent_x + ( ( x - parent_x
2556                                           - getFontMetricsForLargeDefaultFont().stringWidth( lost ) ) / 2 ),
2557                                   ( node.getYcoord() + getFontMetricsForLargeDefaultFont().getMaxAscent() ),
2558                                   g );
2559         }
2560     }
2561
2562     private void paintMolecularSequences( final Graphics2D g, final PhylogenyNode node, final boolean to_pdf ) {
2563         final RenderableMsaSequence rs = RenderableMsaSequence
2564                 .createInstance( node.getNodeData().getSequence().getMolecularSequence(),
2565                                  node.getNodeData().getSequence().getType(),
2566                                  getConfiguration() );
2567         if ( rs != null ) {
2568             final int default_height = 8;
2569             final float y = getYdistance();
2570             final int h = ( y / 2 ) < default_height ? ForesterUtil.roundToInt( y * 2 ) : default_height;
2571             rs.setRenderingHeight( h > 1 ? h : 1 );
2572             if ( getControlPanel().isDrawPhylogram() ) {
2573                 rs.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() ) + _length_of_longest_text ),
2574                            node.getYcoord() - ( h / 2.0f ),
2575                            g,
2576                            this,
2577                            to_pdf );
2578             }
2579             else {
2580                 rs.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
2581                            node.getYcoord() - ( h / 2.0f ),
2582                            g,
2583                            this,
2584                            to_pdf );
2585             }
2586         }
2587     }
2588
2589     /**
2590      * Draw a box at the indicated node.
2591      *
2592      * @param x
2593      * @param y
2594      * @param node
2595      * @param g
2596      */
2597     final private void paintNodeBox( final float x,
2598                                      final float y,
2599                                      final PhylogenyNode node,
2600                                      final Graphics2D g,
2601                                      final boolean to_pdf,
2602                                      final boolean to_graphics_file ) {
2603         if ( node.isCollapse() ) {
2604             return;
2605         }
2606         // if this node should be highlighted, do so
2607         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
2608             g.setColor( getTreeColorSet().getFoundColor0() );
2609             drawOval( x - 8, y - 8, 16, 16, g );
2610             drawOval( x - 9, y - 8, 17, 17, g );
2611             drawOval( x - 9, y - 9, 18, 18, g );
2612         }
2613         if ( ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) )
2614                 || ( getOptions().isShowDefaultNodeShapesExternal() && node.isExternal() )
2615                 || ( getOptions().isShowDefaultNodeShapesInternal() && node.isInternal() )
2616                 || ( getOptions().isShowDefaultNodeShapesForMarkedNodes()
2617                         && ( node.getNodeData().getNodeVisualData() != null )
2618                         && ( !node.getNodeData().getNodeVisualData().isEmpty() ) )
2619                 || ( getControlPanel().isUseVisualStyles()
2620                         && ( ( node.getNodeData().getNodeVisualData() != null )
2621                                 && ( ( node.getNodeData().getNodeVisualData().getNodeColor() != null )
2622                                         || ( node.getNodeData().getNodeVisualData()
2623                                                 .getSize() != NodeVisualData.DEFAULT_SIZE )
2624                                         || ( node.getNodeData().getNodeVisualData().getFillType() != NodeFill.DEFAULT )
2625                                         || ( node.getNodeData().getNodeVisualData()
2626                                                 .getShape() != NodeShape.DEFAULT ) ) ) )
2627                 || ( getControlPanel().isEvents() && node.isHasAssignedEvent()
2628                         && ( node.getNodeData().getEvent().isDuplication()
2629                                 || node.getNodeData().getEvent().isSpeciation()
2630                                 || node.getNodeData().getEvent().isSpeciationOrDuplication() ) ) ) {
2631             NodeVisualData vis = null;
2632             if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
2633                     && ( !node.getNodeData().getNodeVisualData().isEmpty() ) ) {
2634                 vis = node.getNodeData().getNodeVisualData();
2635             }
2636             float box_size = getOptions().getDefaultNodeShapeSize();
2637             if ( ( vis != null ) && ( vis.getSize() != NodeVisualData.DEFAULT_SIZE ) ) {
2638                 box_size = vis.getSize();
2639             }
2640             final float half_box_size = box_size / 2.0f;
2641             Color outline_color = null;
2642             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2643                 outline_color = Color.BLACK;
2644             }
2645             else if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
2646                 outline_color = getColorForFoundNode( node );
2647             }
2648             else if ( vis != null ) {
2649                 if ( vis.getNodeColor() != null ) {
2650                     outline_color = vis.getNodeColor();
2651                 }
2652                 else if ( vis.getFontColor() != null ) {
2653                     outline_color = vis.getFontColor();
2654                 }
2655             }
2656             else if ( getControlPanel().isEvents() && TreePanelUtil.isHasAssignedEvent( node ) ) {
2657                 final Event event = node.getNodeData().getEvent();
2658                 if ( event.isDuplication() ) {
2659                     outline_color = getTreeColorSet().getDuplicationBoxColor();
2660                 }
2661                 else if ( event.isSpeciation() ) {
2662                     outline_color = getTreeColorSet().getSpecBoxColor();
2663                 }
2664                 else if ( event.isSpeciationOrDuplication() ) {
2665                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
2666                 }
2667             }
2668             if ( outline_color == null ) {
2669                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
2670                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
2671                     outline_color = getTreeColorSet().getBranchColorForPdf();
2672                 }
2673             }
2674             NodeShape shape = null;
2675             if ( vis != null ) {
2676                 if ( vis.getShape() == NodeShape.CIRCLE ) {
2677                     shape = NodeShape.CIRCLE;
2678                 }
2679                 else if ( vis.getShape() == NodeShape.RECTANGLE ) {
2680                     shape = NodeShape.RECTANGLE;
2681                 }
2682             }
2683             if ( shape == null ) {
2684                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
2685                     shape = NodeShape.CIRCLE;
2686                 }
2687                 else if ( getOptions().getDefaultNodeShape() == NodeShape.RECTANGLE ) {
2688                     shape = NodeShape.RECTANGLE;
2689                 }
2690             }
2691             NodeFill fill = null;
2692             if ( vis != null ) {
2693                 if ( vis.getFillType() == NodeFill.SOLID ) {
2694                     fill = NodeFill.SOLID;
2695                 }
2696                 else if ( vis.getFillType() == NodeFill.NONE ) {
2697                     fill = NodeFill.NONE;
2698                 }
2699                 else if ( vis.getFillType() == NodeFill.GRADIENT ) {
2700                     fill = NodeFill.GRADIENT;
2701                 }
2702             }
2703             if ( fill == null ) {
2704                 if ( getOptions().getDefaultNodeFill() == NodeFill.SOLID ) {
2705                     fill = NodeFill.SOLID;
2706                 }
2707                 else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
2708                     fill = NodeFill.NONE;
2709                 }
2710                 else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2711                     fill = NodeFill.GRADIENT;
2712                 }
2713             }
2714             Color vis_fill_color = null;
2715             if ( ( vis != null ) && ( vis.getNodeColor() != null ) ) {
2716                 vis_fill_color = vis.getNodeColor();
2717             }
2718             if ( shape == NodeShape.CIRCLE ) {
2719                 if ( fill == NodeFill.GRADIENT ) {
2720                     drawOvalGradient( x - half_box_size,
2721                                       y - half_box_size,
2722                                       box_size,
2723                                       box_size,
2724                                       g,
2725                                       to_pdf ? Color.WHITE : outline_color,
2726                                       to_pdf ? outline_color : getBackground(),
2727                                       outline_color );
2728                 }
2729                 else if ( fill == NodeFill.NONE ) {
2730                     Color background = getBackground();
2731                     if ( to_pdf ) {
2732                         background = Color.WHITE;
2733                     }
2734                     drawOvalGradient( x - half_box_size,
2735                                       y - half_box_size,
2736                                       box_size,
2737                                       box_size,
2738                                       g,
2739                                       background,
2740                                       background,
2741                                       outline_color );
2742                 }
2743                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
2744                     if ( vis_fill_color != null ) {
2745                         g.setColor( vis_fill_color );
2746                     }
2747                     else {
2748                         g.setColor( outline_color );
2749                     }
2750                     drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2751                 }
2752             }
2753             else if ( shape == NodeVisualData.NodeShape.RECTANGLE ) {
2754                 if ( fill == NodeVisualData.NodeFill.GRADIENT ) {
2755                     drawRectGradient( x - half_box_size,
2756                                       y - half_box_size,
2757                                       box_size,
2758                                       box_size,
2759                                       g,
2760                                       to_pdf ? Color.WHITE : outline_color,
2761                                       to_pdf ? outline_color : getBackground(),
2762                                       outline_color );
2763                 }
2764                 else if ( fill == NodeVisualData.NodeFill.NONE ) {
2765                     Color background = getBackground();
2766                     if ( to_pdf ) {
2767                         background = Color.WHITE;
2768                     }
2769                     drawRectGradient( x - half_box_size,
2770                                       y - half_box_size,
2771                                       box_size,
2772                                       box_size,
2773                                       g,
2774                                       background,
2775                                       background,
2776                                       outline_color );
2777                 }
2778                 else if ( fill == NodeVisualData.NodeFill.SOLID ) {
2779                     if ( vis_fill_color != null ) {
2780                         g.setColor( vis_fill_color );
2781                     }
2782                     else {
2783                         g.setColor( outline_color );
2784                     }
2785                     drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2786                 }
2787             }
2788         }
2789     }
2790
2791     final private int paintNodeData( final Graphics2D g,
2792                                      final PhylogenyNode node,
2793                                      final boolean to_graphics_file,
2794                                      final boolean to_pdf,
2795                                      final boolean is_in_found_nodes,
2796                                      final double add ) {
2797         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
2798             return 0;
2799         }
2800         if ( getControlPanel().isWriteBranchLengthValues()
2801                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
2802                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
2803                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
2804                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
2805             paintBranchLength( g, node, to_pdf, to_graphics_file );
2806         }
2807         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
2808             return 0;
2809         }
2810         _sb.setLength( 0 );
2811         int x = 0;
2812         if ( add > 0 ) {
2813             x += add;
2814         }
2815         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2816         if ( getControlPanel().isShowTaxonomyImages() && ( getImageMap() != null ) && !getImageMap().isEmpty()
2817                 && node.getNodeData().isHasTaxonomy() && ( ( node.getNodeData().getTaxonomy().getUris() != null )
2818                         && !node.getNodeData().getTaxonomy().getUris().isEmpty() ) ) {
2819             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
2820         }
2821         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames()
2822                 || getControlPanel().isShowTaxonomyCommonNames() || getControlPanel().isShowTaxonomyRank() )
2823                 && node.getNodeData().isHasTaxonomy() ) {
2824             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
2825         }
2826         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
2827         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
2828             if ( _sb.length() > 0 ) {
2829                 _sb.setLength( 0 );
2830                 _sb.append( " (" );
2831                 _sb.append( node.getAllExternalDescendants().size() );
2832                 _sb.append( ")" );
2833             }
2834         }
2835         else {
2836             _sb.setLength( 0 );
2837         }
2838         nodeDataAsSB( node, _sb );
2839         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
2840         float down_shift_factor = 3.0f;
2841         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
2842             down_shift_factor = 1;
2843         }
2844         final float pos_x = node.getXcoord() + x + 2 + half_box_size;
2845         float pos_y;
2846         if ( !using_visual_font ) {
2847             pos_y = ( node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ) );
2848         }
2849         else {
2850             pos_y = ( node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / down_shift_factor ) );
2851         }
2852         final String sb_str = _sb.toString();
2853         // GUILHEM_BEG ______________
2854         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
2855                 && ( _query_sequence != null ) ) {
2856             int nodeTextBoundsWidth = 0;
2857             if ( sb_str.length() > 0 ) {
2858                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
2859                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
2860             }
2861             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
2862                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
2863                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
2864                     g.setColor( getTreeColorSet().getBackgroundColor() );
2865                 }
2866             }
2867             else {
2868                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
2869                 for( final SequenceRelation seqRelation : seqRelations ) {
2870                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence )
2871                             || seqRelation.getRef1().isEqual( _query_sequence ) )
2872                             && seqRelation.getType()
2873                                     .equals( getControlPanel().getSequenceRelationTypeBox().getSelectedItem() );
2874                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
2875                         final double linePosX = node.getXcoord() + 2 + half_box_size;
2876                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence()
2877                                 || ( seqRelation.getConfidence() == null ) ) ? null
2878                                         : " (" + seqRelation.getConfidence().getValue() + ")";
2879                         if ( sConfidence != null ) {
2880                             float confidenceX = pos_x;
2881                             if ( sb_str.length() > 0 ) {
2882                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
2883                                         + CONFIDENCE_LEFT_MARGIN;
2884                             }
2885                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code
2886                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
2887                                         .getBounds().getWidth();
2888                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
2889                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
2890                             }
2891                         }
2892                         if ( ( x + nodeTextBoundsWidth ) > 0 ) /* we only underline if there is something displayed */
2893                         {
2894                             if ( nodeTextBoundsWidth == 0 ) {
2895                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
2896                             }
2897                             else {
2898                                 nodeTextBoundsWidth += 2;
2899                             }
2900                             g.drawLine( ( int ) linePosX + 1,
2901                                         3 + ( int ) pos_y,
2902                                         ( int ) linePosX + x + nodeTextBoundsWidth,
2903                                         3 + ( int ) pos_y );
2904                             break;
2905                         }
2906                     }
2907                 }
2908             }
2909         }
2910         if ( sb_str.length() > 0 ) {
2911             if ( !isAllowAttributedStrings() ) {
2912                 TreePanel.drawString( sb_str, pos_x, pos_y, g );
2913             }
2914             else {
2915                 drawStringX( sb_str, pos_x, pos_y, g );
2916             }
2917         }
2918         // GUILHEM_END _____________
2919         if ( _sb.length() > 0 ) {
2920             if ( !using_visual_font && !is_in_found_nodes ) {
2921                 x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
2922             }
2923             else {
2924                 x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
2925             }
2926         }
2927         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
2928                 && ( node.getNodeData().getSequence().getAnnotations() != null )
2929                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
2930             final SortedSet<Annotation> ann = node.getNodeData().getSequence().getAnnotations();
2931             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2932                 g.setColor( Color.BLACK );
2933             }
2934             else if ( getControlPanel().isColorAccordingToAnnotation() ) {
2935                 g.setColor( calculateColorForAnnotation( ann ) );
2936             }
2937             final String ann_str = TreePanelUtil.createAnnotationString( ann,
2938                                                                          getOptions().isShowAnnotationRefSource() );
2939             TreePanel.drawString( ann_str,
2940                                   node.getXcoord() + x + 3 + half_box_size,
2941                                   node.getYcoord()
2942                                           + ( getFontMetricsForLargeDefaultFont().getAscent() / down_shift_factor ),
2943                                   g );
2944             _sb.setLength( 0 );
2945             _sb.append( ann_str );
2946             if ( _sb.length() > 0 ) {
2947                 if ( !using_visual_font && !is_in_found_nodes ) {
2948                     x += getFontMetricsForLargeDefaultFont().stringWidth( _sb.toString() ) + 5;
2949                 }
2950                 else {
2951                     x += getFontMetrics( g.getFont() ).stringWidth( _sb.toString() ) + 5;
2952                 }
2953             }
2954         }
2955         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
2956                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
2957                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2958             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
2959                     && node.getNodeData().isHasBinaryCharacters() ) {
2960                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2961                     g.setColor( Color.BLACK );
2962                 }
2963                 else {
2964                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
2965                 }
2966                 if ( getControlPanel().isShowBinaryCharacters() ) {
2967                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
2968                             .toString(),
2969                                           node.getXcoord() + x + 1 + half_box_size,
2970                                           node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent()
2971                                                   / down_shift_factor ),
2972                                           g );
2973                     paintGainedAndLostCharacters( g,
2974                                                   node,
2975                                                   node.getNodeData().getBinaryCharacters()
2976                                                           .getGainedCharactersAsStringBuffer().toString(),
2977                                                   node.getNodeData().getBinaryCharacters()
2978                                                           .getLostCharactersAsStringBuffer().toString() );
2979                 }
2980                 else {
2981                     TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
2982                                           node.getXcoord() + x + 4 + half_box_size,
2983                                           node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent()
2984                                                   / down_shift_factor ),
2985                                           g );
2986                     paintGainedAndLostCharacters( g,
2987                                                   node,
2988                                                   "+" + node.getNodeData().getBinaryCharacters().getGainedCount(),
2989                                                   "-" + node.getNodeData().getBinaryCharacters().getLostCount() );
2990                 }
2991             }
2992         }
2993         return x;
2994     }
2995
2996     private final boolean isAllowAttributedStrings() {
2997         return false;
2998     }
2999
3000     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
3001                                                   final PhylogenyNode node,
3002                                                   final boolean to_pdf,
3003                                                   final boolean to_graphics_file,
3004                                                   final boolean radial_labels,
3005                                                   final double ur_angle,
3006                                                   final boolean is_in_found_nodes ) {
3007         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
3008             return;
3009         }
3010         _sb.setLength( 0 );
3011         _sb.append( " " );
3012         if ( node.getNodeData().isHasTaxonomy()
3013                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames()
3014                         || getControlPanel().isShowTaxonomyCommonNames() ) ) {
3015             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3016             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3017                 _sb.append( taxonomy.getTaxonomyCode() );
3018                 _sb.append( " " );
3019             }
3020             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3021                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3022                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3023                     _sb.append( taxonomy.getScientificName() );
3024                     _sb.append( " (" );
3025                     _sb.append( taxonomy.getCommonName() );
3026                     _sb.append( ") " );
3027                 }
3028                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3029                     _sb.append( taxonomy.getScientificName() );
3030                     _sb.append( " " );
3031                 }
3032                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3033                     _sb.append( taxonomy.getCommonName() );
3034                     _sb.append( " " );
3035                 }
3036             }
3037             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3038                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3039                     _sb.append( taxonomy.getScientificName() );
3040                     _sb.append( " " );
3041                 }
3042             }
3043             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3044                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3045                     _sb.append( taxonomy.getCommonName() );
3046                     _sb.append( " " );
3047                 }
3048             }
3049         }
3050         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3051             _sb.append( " [" );
3052             _sb.append( node.getAllExternalDescendants().size() );
3053             _sb.append( "]" );
3054         }
3055         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3056             if ( _sb.length() > 0 ) {
3057                 _sb.append( " " );
3058             }
3059             _sb.append( node.getName() );
3060         }
3061         if ( node.getNodeData().isHasSequence() ) {
3062             if ( getControlPanel().isShowSequenceAcc()
3063                     && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3064                 if ( _sb.length() > 0 ) {
3065                     _sb.append( " " );
3066                 }
3067                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3068                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3069                     _sb.append( ":" );
3070                 }
3071                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3072             }
3073             if ( getControlPanel().isShowSeqNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3074                 if ( _sb.length() > 0 ) {
3075                     _sb.append( " " );
3076                 }
3077                 _sb.append( node.getNodeData().getSequence().getName() );
3078             }
3079         }
3080         //g.setFont( getTreeFontSet().getLargeFont() );
3081         //if ( is_in_found_nodes ) {
3082         //    g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3083         // }
3084         if ( _sb.length() > 1 ) {
3085             setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getSequenceColor() );
3086             final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
3087             final String sb_str = _sb.toString();
3088             double m = 0;
3089             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3090                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
3091             }
3092             else {
3093                 m = ( float ) ( ur_angle % TWO_PI );
3094             }
3095             _at = g.getTransform();
3096             boolean need_to_reset = false;
3097             final float x_coord = node.getXcoord();
3098             float y_coord;
3099             if ( !using_visual_font ) {
3100                 y_coord = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent() / 3.0f );
3101             }
3102             else {
3103                 y_coord = node.getYcoord() + ( getFontMetrics( g.getFont() ).getAscent() / 3.0f );
3104             }
3105             if ( radial_labels ) {
3106                 need_to_reset = true;
3107                 boolean left = false;
3108                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3109                     m -= PI;
3110                     left = true;
3111                 }
3112                 g.rotate( m, x_coord, node.getYcoord() );
3113                 if ( left ) {
3114                     if ( !using_visual_font ) {
3115                         g.translate( -( getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth() ),
3116                                      0 );
3117                     }
3118                     else {
3119                         g.translate( -( getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth() ), 0 );
3120                     }
3121                 }
3122             }
3123             else {
3124                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3125                     need_to_reset = true;
3126                     if ( !using_visual_font ) {
3127                         g.translate( -getFontMetricsForLargeDefaultFont().getStringBounds( sb_str, g ).getWidth(), 0 );
3128                     }
3129                     else {
3130                         g.translate( -getFontMetrics( g.getFont() ).getStringBounds( sb_str, g ).getWidth(), 0 );
3131                     }
3132                 }
3133             }
3134             TreePanel.drawString( sb_str, x_coord, y_coord, g );
3135             if ( need_to_reset ) {
3136                 g.setTransform( _at );
3137             }
3138         }
3139     }
3140
3141     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
3142         if ( node.isCollapse() ) {
3143             return;
3144         }
3145         if ( isInFoundNodes( node ) || isInCurrentExternalNodes( node ) ) {
3146             g.setColor( getColorForFoundNode( node ) );
3147             drawRectFilled( node.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3148                             node.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3149                             OVERVIEW_FOUND_NODE_BOX_SIZE,
3150                             OVERVIEW_FOUND_NODE_BOX_SIZE,
3151                             g );
3152         }
3153         float new_x = 0;
3154         if ( !node.isExternal() && !node.isCollapse() ) {
3155             boolean first_child = true;
3156             float y2 = 0.0f;
3157             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3158             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3159                 final PhylogenyNode child_node = node.getChildNode( i );
3160                 final int factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3161                 if ( first_child ) {
3162                     first_child = false;
3163                     y2 = node.getYSecondary() - ( getOvYDistance()
3164                             * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3165                 }
3166                 else {
3167                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3168                 }
3169                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
3170                 new_x = x2 + node.getXSecondary();
3171                 final float diff_y = node.getYSecondary() - y2;
3172                 final float diff_x = node.getXSecondary() - new_x;
3173                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
3174                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
3175                 }
3176                 child_node.setXSecondary( new_x );
3177                 child_node.setYSecondary( y2 );
3178                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3179             }
3180         }
3181     }
3182
3183     final private void paintNodeRectangular( final Graphics2D g,
3184                                              final PhylogenyNode node,
3185                                              final boolean to_pdf,
3186                                              final boolean dynamically_hide,
3187                                              final int dynamic_hiding_factor,
3188                                              final boolean to_graphics_file,
3189                                              final boolean disallow_shortcutting ) {
3190         final boolean is_in_found_nodes = isInFoundNodes( node ) || isInCurrentExternalNodes( node );
3191         if ( node.isCollapse() ) {
3192             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) ) {
3193                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3194             }
3195             return;
3196         }
3197         if ( node.isExternal() ) {
3198             ++_external_node_index;
3199         }
3200         // Confidence values
3201         if ( getControlPanel().isShowConfidenceValues() && !node.isExternal() && !node.isRoot()
3202                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
3203                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3204                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3205                 && node.getBranchData().isHasConfidences() ) {
3206             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
3207         }
3208         // Draw a line to root:
3209         if ( node.isRoot() && _phylogeny.isRooted() ) {
3210             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
3211         }
3212         float new_x = 0;
3213         float new_x_min = Float.MAX_VALUE;
3214         float min_dist = 1.5f;
3215         if ( !disallow_shortcutting ) {
3216             if ( dynamic_hiding_factor > 4000 ) {
3217                 min_dist = 4;
3218             }
3219             else if ( dynamic_hiding_factor > 1000 ) {
3220                 min_dist = 3;
3221             }
3222             else if ( dynamic_hiding_factor > 100 ) {
3223                 min_dist = 2;
3224             }
3225         }
3226         if ( !node.isExternal() && !node.isCollapse() ) {
3227             boolean first_child = true;
3228             float y2 = 0.0f;
3229             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3230                 final PhylogenyNode child_node = node.getChildNode( i );
3231                 final int factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3232                 if ( first_child ) {
3233                     first_child = false;
3234                     y2 = node.getYcoord() - ( _y_distance
3235                             * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3236                 }
3237                 else {
3238                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
3239                 }
3240                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
3241                 new_x = x2 + node.getXcoord();
3242                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
3243                     new_x_min = x2;
3244                 }
3245                 final float diff_y = node.getYcoord() - y2;
3246                 final float diff_x = node.getXcoord() - new_x;
3247                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
3248                         || ( diff_x < -min_dist ) ) {
3249                     paintBranchRectangular( g,
3250                                             node.getXcoord(),
3251                                             new_x,
3252                                             node.getYcoord(),
3253                                             y2,
3254                                             child_node,
3255                                             to_pdf,
3256                                             to_graphics_file );
3257                 }
3258                 child_node.setXcoord( new_x );
3259                 child_node.setYcoord( y2 );
3260                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
3261             }
3262             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file );
3263         }
3264         if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
3265                 && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
3266                 && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
3267             paintMolecularSequences( g, node, to_pdf );
3268         }
3269         if ( dynamically_hide && !is_in_found_nodes && ( ( node.isExternal()
3270                 && ( ( _external_node_index % dynamic_hiding_factor ) != 1 ) )
3271                 || ( !node.isExternal() && ( ( new_x_min < 20 )
3272                         || ( ( _y_distance * node.getNumberOfExternalNodes() ) < getFontMetricsForLargeDefaultFont()
3273                                 .getHeight() ) ) ) ) ) {
3274             return;
3275         }
3276         final int x = paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes, 0 );
3277         paintNodeWithRenderableData( x, g, node, to_graphics_file, to_pdf );
3278     }
3279
3280     final private void paintNodeWithRenderableData( final int x,
3281                                                     final Graphics2D g,
3282                                                     final PhylogenyNode node,
3283                                                     final boolean to_graphics_file,
3284                                                     final boolean to_pdf ) {
3285         if ( isNodeDataInvisible( node ) && !( to_graphics_file || to_pdf ) ) {
3286             return;
3287         }
3288         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
3289             return;
3290         }
3291         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3292                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) && ( node.getNodeData()
3293                         .getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
3294             RenderableDomainArchitecture rds = null;
3295             try {
3296                 rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
3297             }
3298             catch ( final ClassCastException cce ) {
3299                 cce.printStackTrace();
3300             }
3301             if ( rds != null ) {
3302                 final int default_height = 7;
3303                 float y = getYdistance();
3304                 if ( getControlPanel().isDynamicallyHideData() ) {
3305                     y = getTreeFontSet().getFontMetricsLarge().getHeight();
3306                 }
3307                 final int h = y < default_height ? ForesterUtil.roundToInt( y ) : default_height;
3308                 rds.setRenderingHeight( h > 1 ? h : 2 );
3309                 if ( getControlPanel().isDrawPhylogram() ) {
3310                     if ( getOptions().isLineUpRendarableNodeData() ) {
3311                         if ( getOptions().isRightLineUpDomains() ) {
3312                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
3313                                     + _length_of_longest_text
3314                                     + ( ( _longest_domain - rds.getTotalLength() ) * rds.getRenderingFactorWidth() ) ),
3315                                         node.getYcoord() - ( h / 2.0f ),
3316                                         g,
3317                                         this,
3318                                         to_pdf );
3319                         }
3320                         else {
3321                             rds.render( ( float ) ( ( getMaxDistanceToRoot() * getXcorrectionFactor() )
3322                                     + _length_of_longest_text ), node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
3323                         }
3324                     }
3325                     else {
3326                         rds.render( node.getXcoord() + x, node.getYcoord() - ( h / 2.0f ), g, this, to_pdf );
3327                     }
3328                 }
3329                 else {
3330                     if ( getOptions().isRightLineUpDomains() ) {
3331                         rds.render( ( ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text )
3332                                 - 20 ) + ( ( _longest_domain - rds.getTotalLength() ) * rds.getRenderingFactorWidth() ),
3333                                     node.getYcoord() - ( h / 2.0f ),
3334                                     g,
3335                                     this,
3336                                     to_pdf );
3337                     }
3338                     else {
3339                         rds.render( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text,
3340                                     node.getYcoord() - ( h / 2.0f ),
3341                                     g,
3342                                     this,
3343                                     to_pdf );
3344                     }
3345                 }
3346             }
3347         }
3348         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
3349                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
3350             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
3351                                                                          getStatisticsForExpressionValues(),
3352                                                                          getConfiguration() );
3353             if ( rv != null ) {
3354                 double domain_add = 0;
3355                 if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3356                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
3357                     domain_add = _domain_structure_width + 10;
3358                 }
3359                 if ( getControlPanel().isDrawPhylogram() ) {
3360                     rv.render( ( float ) ( node.getXcoord() + x + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
3361                 }
3362                 else {
3363                     rv.render( ( float ) ( getPhylogeny().getFirstExternalNode().getXcoord() + _length_of_longest_text
3364                             + domain_add ), node.getYcoord() - 3, g, this, to_pdf );
3365                 }
3366             }
3367         }
3368         //if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
3369         //        && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
3370         //        && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
3371         //    paintMolecularSequences( g, node, to_pdf );
3372         //}
3373     }
3374
3375     final private void paintOvRectangle( final Graphics2D g ) {
3376         final float w_ratio = ( ( float ) getWidth() ) / getVisibleRect().width;
3377         final float h_ratio = ( ( float ) getHeight() ) / getVisibleRect().height;
3378         final float x_ratio = ( ( float ) getWidth() ) / getVisibleRect().x;
3379         final float y_ratio = ( ( float ) getHeight() ) / getVisibleRect().y;
3380         final float width = getOvMaxWidth() / w_ratio;
3381         final float height = getOvMaxHeight() / h_ratio;
3382         final float x = getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / x_ratio );
3383         final float y = getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / y_ratio );
3384         g.setColor( getTreeColorSet().getFoundColor0() );
3385         getOvRectangle().setRect( x, y, width, height );
3386         final Stroke s = g.getStroke();
3387         g.setStroke( STROKE_1 );
3388         if ( ( width < 6 ) && ( height < 6 ) ) {
3389             drawRectFilled( x, y, 6, 6, g );
3390             getOvVirtualRectangle().setRect( x, y, 6, 6 );
3391         }
3392         else if ( width < 6 ) {
3393             drawRectFilled( x, y, 6, height, g );
3394             getOvVirtualRectangle().setRect( x, y, 6, height );
3395         }
3396         else if ( height < 6 ) {
3397             drawRectFilled( x, y, width, 6, g );
3398             getOvVirtualRectangle().setRect( x, y, width, 6 );
3399         }
3400         else {
3401             drawRect( x, y, width, height, g );
3402             if ( isInOvRect() ) {
3403                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
3404             }
3405             getOvVirtualRectangle().setRect( x, y, width, height );
3406         }
3407         g.setStroke( s );
3408     }
3409
3410     final private void paintPhylogenyLite( final Graphics2D g ) {
3411         _phylogeny.getRoot().setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition()
3412                 + ( MOVE / ( getVisibleRect().width / getOvRectangle().getWidth() ) ) ) );
3413         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
3414         final Stroke s = g.getStroke();
3415         g.setStroke( STROKE_05 );
3416         for( final PhylogenyNode element : _nodes_in_preorder ) {
3417             paintNodeLite( g, element );
3418         }
3419         g.setStroke( s );
3420         paintOvRectangle( g );
3421     }
3422
3423     /**
3424      * Paint the root branch. (Differs from others because it will always be a
3425      * single horizontal line).
3426      * @param to_graphics_file
3427      *
3428      * @return new x1 value
3429      */
3430     final private void paintRootBranch( final Graphics2D g,
3431                                         final float x1,
3432                                         final float y1,
3433                                         final PhylogenyNode root,
3434                                         final boolean to_pdf,
3435                                         final boolean to_graphics_file ) {
3436         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
3437         float d = getXdistance();
3438         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
3439             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
3440         }
3441         if ( d < MIN_ROOT_LENGTH ) {
3442             d = MIN_ROOT_LENGTH;
3443         }
3444         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
3445             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
3446         }
3447         else {
3448             final double w = PhylogenyMethods.getBranchWidthValue( root );
3449             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
3450         }
3451         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file );
3452     }
3453
3454     final private void paintScale( final Graphics2D g,
3455                                    int x1,
3456                                    int y1,
3457                                    final boolean to_pdf,
3458                                    final boolean to_graphics_file ) {
3459         x1 += MOVE;
3460         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
3461         y1 -= 12;
3462         final int y2 = y1 - 8;
3463         final int y3 = y1 - 4;
3464         g.setFont( getTreeFontSet().getSmallFont() );
3465         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3466             g.setColor( Color.BLACK );
3467         }
3468         else {
3469             g.setColor( getTreeColorSet().getBranchLengthColor() );
3470         }
3471         final Stroke s = g.getStroke();
3472         g.setStroke( STROKE_1 );
3473         drawLine( x1, y1, x1, y2, g );
3474         drawLine( x2, y1, x2, y2, g );
3475         drawLine( x1, y3, x2, y3, g );
3476         if ( getScaleLabel() != null ) {
3477             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
3478         }
3479         g.setStroke( s );
3480     }
3481
3482     final private int paintTaxonomy( final Graphics2D g,
3483                                      final PhylogenyNode node,
3484                                      final boolean is_in_found_nodes,
3485                                      final boolean to_pdf,
3486                                      final boolean to_graphics_file,
3487                                      final float x_shift ) {
3488         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3489         final boolean using_visual_font = setFont( g, node, is_in_found_nodes );
3490         setColor( g, node, to_graphics_file, to_pdf, is_in_found_nodes, getTreeColorSet().getTaxonomyColor() );
3491         final float start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
3492         float start_y;
3493         if ( !using_visual_font ) {
3494             start_y = node.getYcoord() + ( getFontMetricsForLargeDefaultFont().getAscent()
3495                     / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
3496         }
3497         else {
3498             start_y = node.getYcoord()
3499                     + ( getFontMetrics( g.getFont() ).getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0f ) );
3500         }
3501         _sb.setLength( 0 );
3502         nodeTaxonomyDataAsSB( taxonomy, _sb );
3503         final String label = _sb.toString();
3504         /* GUILHEM_BEG */
3505         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
3506                 && ( node.getNodeData().isHasSequence() )
3507                 && node.getNodeData().getSequence().equals( _query_sequence ) ) {
3508             // invert font color and background color to show that this is the query sequence
3509             final Rectangle2D nodeTextBounds = new TextLayout( label,
3510                                                                g.getFont(),
3511                                                                new FontRenderContext( null, false, false ) )
3512                                                                        .getBounds();
3513             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
3514             g.setColor( getTreeColorSet().getBackgroundColor() );
3515         }
3516         /* GUILHEM_END */
3517         TreePanel.drawString( label, start_x, start_y, g );
3518         if ( !using_visual_font && !is_in_found_nodes ) {
3519             return getFontMetricsForLargeDefaultFont().stringWidth( label );
3520         }
3521         return getFontMetrics( g.getFont() ).stringWidth( label );
3522     }
3523
3524     final private void paintUnrooted( final PhylogenyNode n,
3525                                       final double low_angle,
3526                                       final double high_angle,
3527                                       final boolean radial_labels,
3528                                       final Graphics2D g,
3529                                       final boolean to_pdf,
3530                                       final boolean to_graphics_file ) {
3531         if ( n.isRoot() ) {
3532             n.setXcoord( getWidth() / 2 );
3533             n.setYcoord( getHeight() / 2 );
3534         }
3535         if ( n.isExternal() ) {
3536             paintNodeDataUnrootedCirc( g,
3537                                        n,
3538                                        to_pdf,
3539                                        to_graphics_file,
3540                                        radial_labels,
3541                                        ( high_angle + low_angle ) / 2,
3542                                        isInFoundNodes( n ) || isInCurrentExternalNodes( n ) );
3543             return;
3544         }
3545         final float num_enclosed = n.getNumberOfExternalNodes();
3546         final float x = n.getXcoord();
3547         final float y = n.getYcoord();
3548         double current_angle = low_angle;
3549         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
3550         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
3551         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
3552         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
3553         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
3554             final PhylogenyNode desc = n.getChildNode( i );
3555             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
3556             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
3557             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
3558             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
3559             //     continue;
3560             // }
3561             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
3562             //    continue;
3563             //}
3564             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
3565             //    continue;
3566             // }
3567             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
3568             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
3569             float length;
3570             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
3571                 if ( desc.getDistanceToParent() < 0 ) {
3572                     length = 0;
3573                 }
3574                 else {
3575                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
3576                 }
3577             }
3578             else {
3579                 length = getUrtFactor();
3580             }
3581             final double mid_angle = current_angle + ( arc_size / 2 );
3582             final float new_x = ( float ) ( x + ( Math.cos( mid_angle ) * length ) );
3583             final float new_y = ( float ) ( y + ( Math.sin( mid_angle ) * length ) );
3584             desc.setXcoord( new_x );
3585             desc.setYcoord( new_y );
3586             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
3587             current_angle += arc_size;
3588             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
3589             drawLine( x, y, new_x, new_y, g );
3590             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file );
3591         }
3592         if ( n.isRoot() ) {
3593             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file );
3594         }
3595     }
3596
3597     final private void paintUnrootedLite( final PhylogenyNode n,
3598                                           final double low_angle,
3599                                           final double high_angle,
3600                                           final Graphics2D g,
3601                                           final float urt_ov_factor ) {
3602         if ( n.isRoot() ) {
3603             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + ( getOvMaxWidth() / 2 ) );
3604             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + ( getOvMaxHeight() / 2 ) );
3605             n.setXSecondary( x_pos );
3606             n.setYSecondary( y_pos );
3607         }
3608         if ( n.isExternal() ) {
3609             return;
3610         }
3611         final float num_enclosed = n.getNumberOfExternalNodes();
3612         final float x = n.getXSecondary();
3613         final float y = n.getYSecondary();
3614         double current_angle = low_angle;
3615         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
3616             final PhylogenyNode desc = n.getChildNode( i );
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() * urt_ov_factor );
3626                 }
3627             }
3628             else {
3629                 length = urt_ov_factor;
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.setXSecondary( new_x );
3635             desc.setYSecondary( new_y );
3636             if ( isInFoundNodes( desc ) || isInCurrentExternalNodes( desc ) ) {
3637                 g.setColor( getColorForFoundNode( desc ) );
3638                 drawRectFilled( desc.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3639                                 desc.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
3640                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
3641                                 OVERVIEW_FOUND_NODE_BOX_SIZE,
3642                                 g );
3643                 g.setColor( getTreeColorSet().getOvColor() );
3644             }
3645             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
3646             current_angle += arc_size;
3647             drawLine( x, y, new_x, new_y, g );
3648         }
3649     }
3650
3651     final private void pasteSubtree( final PhylogenyNode node ) {
3652         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
3653             errorMessageNoCutCopyPasteInUnrootedDisplay();
3654             return;
3655         }
3656         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
3657             JOptionPane.showMessageDialog( this,
3658                                            "No tree in buffer (need to copy or cut a subtree first)",
3659                                            "Attempt to paste with empty buffer",
3660                                            JOptionPane.ERROR_MESSAGE );
3661             return;
3662         }
3663         final String label = createASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
3664         final Object[] options = { "As sibling", "As descendant", "Cancel" };
3665         final int r = JOptionPane.showOptionDialog( this,
3666                                                     "How to paste subtree" + label + "?",
3667                                                     "Paste Subtree",
3668                                                     JOptionPane.CLOSED_OPTION,
3669                                                     JOptionPane.QUESTION_MESSAGE,
3670                                                     null,
3671                                                     options,
3672                                                     options[ 2 ] );
3673         boolean paste_as_sibling = true;
3674         if ( r == 1 ) {
3675             paste_as_sibling = false;
3676         }
3677         else if ( r != 0 ) {
3678             return;
3679         }
3680         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
3681         buffer_phy.setAllNodesToNotCollapse();
3682         PhylogenyMethods.preOrderReId( buffer_phy );
3683         buffer_phy.setRooted( true );
3684         boolean need_to_show_whole = false;
3685         if ( paste_as_sibling ) {
3686             if ( node.isRoot() ) {
3687                 JOptionPane.showMessageDialog( this,
3688                                                "Cannot paste sibling to root",
3689                                                "Attempt to paste sibling to root",
3690                                                JOptionPane.ERROR_MESSAGE );
3691                 return;
3692             }
3693             buffer_phy.addAsSibling( node );
3694         }
3695         else {
3696             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
3697                 need_to_show_whole = true;
3698                 _phylogeny = buffer_phy;
3699             }
3700             else {
3701                 buffer_phy.addAsChild( node );
3702             }
3703         }
3704         if ( getCopiedAndPastedNodes() == null ) {
3705             setCopiedAndPastedNodes( new HashSet<Long>() );
3706         }
3707         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
3708         final Set<Long> node_ids = new HashSet<Long>( nodes.size() );
3709         for( final PhylogenyNode n : nodes ) {
3710             node_ids.add( n.getId() );
3711         }
3712         node_ids.add( node.getId() );
3713         getCopiedAndPastedNodes().addAll( node_ids );
3714         setNodeInPreorderToNull();
3715         _phylogeny.externalNodesHaveChanged();
3716         _phylogeny.clearHashIdToNodeMap();
3717         _phylogeny.recalculateNumberOfExternalDescendants( true );
3718         resetNodeIdToDistToLeafMap();
3719         setEdited( true );
3720         if ( need_to_show_whole ) {
3721             getControlPanel().showWhole();
3722         }
3723         repaint();
3724     }
3725
3726     private final StringBuffer propertiesToString( final PhylogenyNode node ) {
3727         final PropertiesMap properties = node.getNodeData().getProperties();
3728         final StringBuffer sb = new StringBuffer();
3729         boolean first = true;
3730         for( final String ref : properties.getPropertyRefs() ) {
3731             if ( first ) {
3732                 first = false;
3733             }
3734             else {
3735                 sb.append( " " );
3736             }
3737             final Property p = properties.getProperty( ref );
3738             sb.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
3739             sb.append( "=" );
3740             sb.append( p.getValue() );
3741             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
3742                 sb.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
3743             }
3744         }
3745         return sb;
3746     }
3747
3748     private void setColor( final Graphics2D g,
3749                            final PhylogenyNode node,
3750                            final boolean to_graphics_file,
3751                            final boolean to_pdf,
3752                            final boolean is_in_found_nodes,
3753                            final Color default_color ) {
3754         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3755             g.setColor( Color.BLACK );
3756         }
3757         else if ( is_in_found_nodes ) {
3758             g.setColor( getColorForFoundNode( node ) );
3759         }
3760         else if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null )
3761                 && ( node.getNodeData().getNodeVisualData().getFontColor() != null ) ) {
3762             g.setColor( node.getNodeData().getNodeVisualData().getFontColor() );
3763         }
3764         else if ( getControlPanel().isColorAccordingToSequence() ) {
3765             g.setColor( getSequenceBasedColor( node ) );
3766         }
3767         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3768             g.setColor( getTaxonomyBasedColor( node ) );
3769         }
3770         else if ( getControlPanel().isColorAccordingToAnnotation()
3771                 && ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAnnotations() != null )
3772                         && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) ) {
3773             g.setColor( calculateColorForAnnotation( node.getNodeData().getSequence().getAnnotations() ) );
3774         }
3775         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isUseVisualStyles()
3776                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3777             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3778         }
3779         else if ( to_pdf ) {
3780             g.setColor( Color.BLACK );
3781         }
3782         else {
3783             g.setColor( default_color );
3784         }
3785     }
3786
3787     final private void setCopiedAndPastedNodes( final Set<Long> nodeIds ) {
3788         getMainPanel().setCopiedAndPastedNodes( nodeIds );
3789     }
3790
3791     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
3792         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
3793     }
3794
3795     private boolean setFont( final Graphics2D g, final PhylogenyNode node, final boolean is_in_found_nodes ) {
3796         Font visual_font = null;
3797         if ( getControlPanel().isUseVisualStyles() && ( node.getNodeData().getNodeVisualData() != null ) ) {
3798             visual_font = node.getNodeData().getNodeVisualData().getFont();
3799             g.setFont( visual_font != null ? visual_font : getTreeFontSet().getLargeFont() );
3800         }
3801         else {
3802             g.setFont( getTreeFontSet().getLargeFont() );
3803         }
3804         if ( is_in_found_nodes ) {
3805             g.setFont( g.getFont().deriveFont( Font.BOLD ) );
3806         }
3807         return visual_font != null;
3808     }
3809
3810     final private void setInOv( final boolean in_ov ) {
3811         _in_ov = in_ov;
3812     }
3813
3814     final private void setOvMaxHeight( final float ov_max_height ) {
3815         _ov_max_height = ov_max_height;
3816     }
3817
3818     final private void setOvMaxWidth( final float ov_max_width ) {
3819         _ov_max_width = ov_max_width;
3820     }
3821
3822     final private void setOvXcorrectionFactor( final float f ) {
3823         _ov_x_correction_factor = f;
3824     }
3825
3826     final private void setOvXDistance( final float ov_x_distance ) {
3827         _ov_x_distance = ov_x_distance;
3828     }
3829
3830     final private void setOvXPosition( final int ov_x_position ) {
3831         _ov_x_position = ov_x_position;
3832     }
3833
3834     final private void setOvYDistance( final float ov_y_distance ) {
3835         _ov_y_distance = ov_y_distance;
3836     }
3837
3838     final private void setOvYPosition( final int ov_y_position ) {
3839         _ov_y_position = ov_y_position;
3840     }
3841
3842     final private void setOvYStart( final int ov_y_start ) {
3843         _ov_y_start = ov_y_start;
3844     }
3845
3846     final private void setScaleDistance( final double scale_distance ) {
3847         _scale_distance = scale_distance;
3848     }
3849
3850     final private void setScaleLabel( final String scale_label ) {
3851         _scale_label = scale_label;
3852     }
3853
3854     private final void setupStroke( final Graphics2D g ) {
3855         if ( getYdistance() < 0.0001 ) {
3856             g.setStroke( STROKE_0025 );
3857         }
3858         if ( getYdistance() < 0.001 ) {
3859             g.setStroke( STROKE_005 );
3860         }
3861         else if ( getYdistance() < 0.01 ) {
3862             g.setStroke( STROKE_01 );
3863         }
3864         else if ( getYdistance() < 0.5 ) {
3865             g.setStroke( STROKE_025 );
3866         }
3867         else if ( getYdistance() < 1 ) {
3868             g.setStroke( STROKE_05 );
3869         }
3870         else if ( getYdistance() < 2 ) {
3871             g.setStroke( STROKE_075 );
3872         }
3873         else if ( ( getYdistance() < 20 ) || !getConfiguration().isAllowThickStrokes() ) {
3874             g.setStroke( STROKE_1 );
3875         }
3876         else {
3877             g.setStroke( STROKE_2 );
3878         }
3879     }
3880
3881     final private void setUpUrtFactor() {
3882         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
3883                 : getVisibleRect().height;
3884         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
3885             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
3886         }
3887         else {
3888             final int max_depth = _circ_max_depth;
3889             if ( max_depth > 0 ) {
3890                 setUrtFactor( d / ( 2 * max_depth ) );
3891             }
3892             else {
3893                 setUrtFactor( d / 2 );
3894             }
3895         }
3896         setUrtFactorOv( getUrtFactor() );
3897     }
3898
3899     final private void setUrtFactor( final float urt_factor ) {
3900         _urt_factor = urt_factor;
3901     }
3902
3903     final private void setUrtFactorOv( final float urt_factor_ov ) {
3904         _urt_factor_ov = urt_factor_ov;
3905     }
3906
3907     private void showExtDescNodeData( final PhylogenyNode node ) {
3908         final List<String> data = new ArrayList<String>();
3909         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
3910         if ( ( getFoundNodes0() != null ) || ( getFoundNodes1() != null ) ) {
3911             for( final PhylogenyNode n : getFoundNodesAsListOfPhylogenyNodes() ) {
3912                 if ( !nodes.contains( n ) ) {
3913                     nodes.add( n );
3914                 }
3915             }
3916         }
3917         for( final PhylogenyNode n : nodes ) {
3918             switch ( getOptions().getExtDescNodeDataToReturn() ) {
3919                 case NODE_NAME:
3920                     if ( !ForesterUtil.isEmpty( n.getName() ) ) {
3921                         data.add( n.getName() );
3922                     }
3923                     break;
3924                 case SEQUENCE_NAME:
3925                     if ( n.getNodeData().isHasSequence()
3926                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
3927                         data.add( n.getNodeData().getSequence().getName() );
3928                     }
3929                     break;
3930                 case GENE_NAME:
3931                     if ( n.getNodeData().isHasSequence()
3932                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
3933                         data.add( n.getNodeData().getSequence().getGeneName() );
3934                     }
3935                     break;
3936                 case SEQUENCE_SYMBOL:
3937                     if ( n.getNodeData().isHasSequence()
3938                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
3939                         data.add( n.getNodeData().getSequence().getSymbol() );
3940                     }
3941                     break;
3942                 case SEQUENCE_MOL_SEQ_FASTA:
3943                     final StringBuilder sb = new StringBuilder();
3944                     if ( n.getNodeData().isHasSequence()
3945                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getMolecularSequence() ) ) {
3946                         final StringBuilder ann = new StringBuilder();
3947                         if ( !ForesterUtil.isEmpty( n.getName() ) ) {
3948                             ann.append( n.getName() );
3949                             ann.append( "|" );
3950                         }
3951                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getSymbol() ) ) {
3952                             ann.append( "SYM=" );
3953                             ann.append( n.getNodeData().getSequence().getSymbol() );
3954                             ann.append( "|" );
3955                         }
3956                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getName() ) ) {
3957                             ann.append( "NAME=" );
3958                             ann.append( n.getNodeData().getSequence().getName() );
3959                             ann.append( "|" );
3960                         }
3961                         if ( !ForesterUtil.isEmpty( n.getNodeData().getSequence().getGeneName() ) ) {
3962                             ann.append( "GN=" );
3963                             ann.append( n.getNodeData().getSequence().getGeneName() );
3964                             ann.append( "|" );
3965                         }
3966                         if ( n.getNodeData().getSequence().getAccession() != null ) {
3967                             ann.append( "ACC=" );
3968                             ann.append( n.getNodeData().getSequence().getAccession().asText() );
3969                             ann.append( "|" );
3970                         }
3971                         if ( n.getNodeData().isHasTaxonomy() ) {
3972                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
3973                                 ann.append( "TAXID=" );
3974                                 ann.append( n.getNodeData().getTaxonomy().getTaxonomyCode() );
3975                                 ann.append( "|" );
3976                             }
3977                             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
3978                                 ann.append( "SN=" );
3979                                 ann.append( n.getNodeData().getTaxonomy().getScientificName() );
3980                                 ann.append( "|" );
3981                             }
3982                         }
3983                         String ann_str;
3984                         if ( ann.charAt( ann.length() - 1 ) == '|' ) {
3985                             ann_str = ann.substring( 0, ann.length() - 1 );
3986                         }
3987                         else {
3988                             ann_str = ann.toString();
3989                         }
3990                         sb.append( SequenceWriter.toFasta( ann_str,
3991                                                            n.getNodeData().getSequence().getMolecularSequence(),
3992                                                            60 ) );
3993                         data.add( sb.toString() );
3994                     }
3995                     break;
3996                 case SEQUENCE_ACC:
3997                     if ( n.getNodeData().isHasSequence() && ( n.getNodeData().getSequence().getAccession() != null )
3998                             && !ForesterUtil.isEmpty( n.getNodeData().getSequence().getAccession().toString() ) ) {
3999                         data.add( n.getNodeData().getSequence().getAccession().toString() );
4000                     }
4001                     break;
4002                 case TAXONOMY_SCIENTIFIC_NAME:
4003                     if ( n.getNodeData().isHasTaxonomy()
4004                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
4005                         data.add( n.getNodeData().getTaxonomy().getScientificName() );
4006                     }
4007                     break;
4008                 case TAXONOMY_CODE:
4009                     if ( n.getNodeData().isHasTaxonomy()
4010                             && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
4011                         data.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
4012                     }
4013                     break;
4014                 case DOMAINS_ALL:
4015                 case DOMAINS_COLLAPSED_PER_PROTEIN:
4016                     if ( n.getNodeData().isHasSequence()
4017                             && ( n.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4018                         final DomainArchitecture da = n.getNodeData().getSequence().getDomainArchitecture();
4019                         final Set<String> s = new HashSet<String>();
4020                         for( int i = 0; i < da.getDomains().size(); ++i ) {
4021                             final ProteinDomain d = da.getDomain( i );
4022                             if ( d.getConfidence() <= Math.pow( 10, getDomainStructureEvalueThresholdExp() ) ) {
4023                                 final String name = d.getName();
4024                                 if ( !( s.contains( name ) ) ) {
4025                                     data.add( name );
4026                                     if ( getOptions()
4027                                             .getExtDescNodeDataToReturn() == NodeDataField.DOMAINS_COLLAPSED_PER_PROTEIN ) {
4028                                         s.add( name );
4029                                     }
4030                                 }
4031                             }
4032                         }
4033                     }
4034                     break;
4035                 case SEQ_ANNOTATIONS:
4036                     if ( n.getNodeData().isHasSequence() ) {
4037                         if ( n.getNodeData().isHasSequence()
4038                                 && ( n.getNodeData().getSequence().getAnnotations() != null ) ) {
4039                             final SortedSet<Annotation> a = n.getNodeData().getSequence().getAnnotations();
4040                             for( int i = 0; i < a.size(); ++i ) {
4041                                 data.add( n.getNodeData().getSequence().getAnnotation( i ).toString() );
4042                             }
4043                         }
4044                     }
4045                     break;
4046                 case GO_TERM_IDS:
4047                     if ( n.getNodeData().isHasSequence() ) {
4048                         if ( n.getNodeData().isHasSequence()
4049                                 && ( n.getNodeData().getSequence().getAnnotations() != null ) ) {
4050                             final SortedSet<Annotation> a = n.getNodeData().getSequence().getAnnotations();
4051                             for( int i = 0; i < a.size(); ++i ) {
4052                                 final Annotation ann = n.getNodeData().getSequence().getAnnotation( i );
4053                                 final String ref = ann.getRef();
4054                                 if ( ref.toUpperCase().startsWith( "GO:" ) ) {
4055                                     data.add( ref );
4056                                 }
4057                             }
4058                         }
4059                     }
4060                     break;
4061                 case UNKNOWN:
4062                     TreePanelUtil.showExtDescNodeDataUserSelectedHelper( getControlPanel(), n, data );
4063                     break;
4064                 default:
4065                     throw new IllegalArgumentException( "unknown data element: "
4066                             + getOptions().getExtDescNodeDataToReturn() );
4067             }
4068         } // for loop
4069         final StringBuilder sb = new StringBuilder();
4070         final int size = TreePanelUtil.nodeDataIntoStringBuffer( data, getOptions(), sb );
4071         if ( ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE )
4072                 || ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.BUFFER_ONLY ) ) {
4073             if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.CONSOLE ) {
4074                 System.out.println( sb );
4075             }
4076             if ( sb.length() < 1 ) {
4077                 clearCurrentExternalNodesDataBuffer();
4078             }
4079             else {
4080                 setCurrentExternalNodesDataBuffer( sb );
4081             }
4082         }
4083         else if ( getConfiguration().getExtNodeDataReturnOn() == EXT_NODE_DATA_RETURN_ON.WINODW ) {
4084             if ( sb.length() < 1 ) {
4085                 TreePanelUtil.showInformationMessage( this,
4086                                                       "No Appropriate Data (" + obtainTitleForExtDescNodeData() + ")",
4087                                                       "Descendants of selected node do not contain selected data" );
4088                 clearCurrentExternalNodesDataBuffer();
4089             }
4090             else {
4091                 setCurrentExternalNodesDataBuffer( sb );
4092                 String title;
4093                 if ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() ) {
4094                     title = ( getOptions().getExtDescNodeDataToReturn() == NodeDataField.UNKNOWN ? "Data"
4095                             : obtainTitleForExtDescNodeData() ) + " for " + data.size() + " nodes, unique entries: "
4096                             + size;
4097                 }
4098                 else {
4099                     title = ( getOptions().getExtDescNodeDataToReturn() == NodeDataField.UNKNOWN ? "Data"
4100                             : obtainTitleForExtDescNodeData() ) + " for " + data.size() + "/"
4101                             + node.getNumberOfExternalNodes() + " external descendats of node " + node
4102                             + ", unique entries: " + size;
4103                 }
4104                 final String s = sb.toString().trim();
4105                 if ( getMainPanel().getMainFrame() == null ) {
4106                     // Must be "E" applet version.
4107                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
4108                     ae.showTextFrame( s, title );
4109                 }
4110                 else {
4111                     getMainPanel().getMainFrame().showTextFrame( s, title );
4112                 }
4113             }
4114         }
4115     }
4116
4117     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
4118         try {
4119             if ( ( node.getName().length() > 0 )
4120                     || ( node.getNodeData().isHasTaxonomy()
4121                             && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
4122                     || ( node.getNodeData().isHasSequence()
4123                             && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) )
4124                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
4125                     || node.getBranchData().isHasConfidences() ) {
4126                 _popup_buffer.setLength( 0 );
4127                 short lines = 0;
4128                 if ( node.getName().length() > 0 ) {
4129                     lines++;
4130                     _popup_buffer.append( node.getName() );
4131                 }
4132                 if ( node.getNodeData().isHasTaxonomy()
4133                         && !TreePanelUtil.isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
4134                     lines++;
4135                     boolean enc_data = false;
4136                     final Taxonomy tax = node.getNodeData().getTaxonomy();
4137                     if ( _popup_buffer.length() > 0 ) {
4138                         _popup_buffer.append( "\n" );
4139                     }
4140                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4141                         _popup_buffer.append( "[" );
4142                         _popup_buffer.append( tax.getTaxonomyCode() );
4143                         _popup_buffer.append( "]" );
4144                         enc_data = true;
4145                     }
4146                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4147                         if ( enc_data ) {
4148                             _popup_buffer.append( " " );
4149                         }
4150                         _popup_buffer.append( tax.getScientificName() );
4151                         enc_data = true;
4152                     }
4153                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
4154                         if ( enc_data ) {
4155                             _popup_buffer.append( " (" );
4156                         }
4157                         else {
4158                             _popup_buffer.append( "(" );
4159                         }
4160                         _popup_buffer.append( tax.getCommonName() );
4161                         _popup_buffer.append( ")" );
4162                         enc_data = true;
4163                     }
4164                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
4165                         if ( enc_data ) {
4166                             _popup_buffer.append( " (" );
4167                         }
4168                         else {
4169                             _popup_buffer.append( "(" );
4170                         }
4171                         _popup_buffer.append( tax.getAuthority() );
4172                         _popup_buffer.append( ")" );
4173                         enc_data = true;
4174                     }
4175                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
4176                         if ( enc_data ) {
4177                             _popup_buffer.append( " [" );
4178                         }
4179                         else {
4180                             _popup_buffer.append( "[" );
4181                         }
4182                         _popup_buffer.append( tax.getRank() );
4183                         _popup_buffer.append( "]" );
4184                         enc_data = true;
4185                     }
4186                     if ( tax.getSynonyms().size() > 0 ) {
4187                         if ( enc_data ) {
4188                             _popup_buffer.append( " " );
4189                         }
4190                         _popup_buffer.append( "[" );
4191                         int counter = 1;
4192                         for( final String syn : tax.getSynonyms() ) {
4193                             if ( !ForesterUtil.isEmpty( syn ) ) {
4194                                 enc_data = true;
4195                                 _popup_buffer.append( syn );
4196                                 if ( counter < tax.getSynonyms().size() ) {
4197                                     _popup_buffer.append( ", " );
4198                                 }
4199                             }
4200                             counter++;
4201                         }
4202                         _popup_buffer.append( "]" );
4203                     }
4204                     if ( !enc_data ) {
4205                         if ( ( tax.getIdentifier() != null )
4206                                 && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
4207                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
4208                                 _popup_buffer.append( "[" );
4209                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
4210                                 _popup_buffer.append( "] " );
4211                             }
4212                             _popup_buffer.append( tax.getIdentifier().getValue() );
4213                         }
4214                     }
4215                 }
4216                 if ( node.getNodeData().isHasSequence()
4217                         && !TreePanelUtil.isSequenceEmpty( node.getNodeData().getSequence() ) ) {
4218                     lines++;
4219                     boolean enc_data = false;
4220                     if ( _popup_buffer.length() > 0 ) {
4221                         _popup_buffer.append( "\n" );
4222                     }
4223                     final Sequence seq = node.getNodeData().getSequence();
4224                     if ( seq.getAccession() != null ) {
4225                         _popup_buffer.append( "[" );
4226                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
4227                             _popup_buffer.append( seq.getAccession().getSource() );
4228                             _popup_buffer.append( ":" );
4229                         }
4230                         _popup_buffer.append( seq.getAccession().getValue() );
4231                         _popup_buffer.append( "]" );
4232                         enc_data = true;
4233                     }
4234                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
4235                         if ( enc_data ) {
4236                             _popup_buffer.append( " [" );
4237                         }
4238                         else {
4239                             _popup_buffer.append( "[" );
4240                         }
4241                         _popup_buffer.append( seq.getSymbol() );
4242                         _popup_buffer.append( "]" );
4243                         enc_data = true;
4244                     }
4245                     if ( !ForesterUtil.isEmpty( seq.getGeneName() ) ) {
4246                         if ( enc_data ) {
4247                             _popup_buffer.append( " [" );
4248                         }
4249                         else {
4250                             _popup_buffer.append( "[" );
4251                         }
4252                         _popup_buffer.append( seq.getGeneName() );
4253                         _popup_buffer.append( "]" );
4254                         enc_data = true;
4255                     }
4256                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
4257                         if ( enc_data ) {
4258                             _popup_buffer.append( " " );
4259                         }
4260                         _popup_buffer.append( seq.getName() );
4261                     }
4262                 }
4263                 if ( node.getNodeData().isHasDate() ) {
4264                     lines++;
4265                     if ( _popup_buffer.length() > 0 ) {
4266                         _popup_buffer.append( "\n" );
4267                     }
4268                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
4269                 }
4270                 if ( node.getNodeData().isHasDistribution() ) {
4271                     lines++;
4272                     if ( _popup_buffer.length() > 0 ) {
4273                         _popup_buffer.append( "\n" );
4274                     }
4275                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
4276                 }
4277                 if ( node.getBranchData().isHasConfidences() ) {
4278                     final List<Confidence> confs = node.getBranchData().getConfidences();
4279                     for( final Confidence confidence : confs ) {
4280                         lines++;
4281                         if ( _popup_buffer.length() > 0 ) {
4282                             _popup_buffer.append( "\n" );
4283                         }
4284                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
4285                             _popup_buffer.append( "[" );
4286                             _popup_buffer.append( confidence.getType() );
4287                             _popup_buffer.append( "] " );
4288                         }
4289                         _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
4290                                                                                                getOptions()
4291                                                                                                        .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4292                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4293                             _popup_buffer.append( " (sd=" );
4294                             _popup_buffer.append( FORMATTER_CONFIDENCE
4295                                     .format( ForesterUtil.round( confidence.getStandardDeviation(),
4296                                                                  getOptions()
4297                                                                          .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4298                             _popup_buffer.append( ")" );
4299                         }
4300                     }
4301                 }
4302                 if ( node.getNodeData().isHasProperties() ) {
4303                     final PropertiesMap properties = node.getNodeData().getProperties();
4304                     for( final String ref : properties.getPropertyRefs() ) {
4305                         _popup_buffer.append( "\n" );
4306                         final Property p = properties.getProperty( ref );
4307                         _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getRef() ) );
4308                         _popup_buffer.append( "=" );
4309                         _popup_buffer.append( p.getValue() );
4310                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
4311                             _popup_buffer.append( TreePanelUtil.getPartAfterColon( p.getUnit() ) );
4312                         }
4313                     }
4314                 }
4315                 if ( _popup_buffer.length() > 0 ) {
4316                     if ( !getConfiguration().isUseNativeUI() ) {
4317                         _rollover_popup
4318                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
4319                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
4320                         if ( isInFoundNodes0( node ) && !isInFoundNodes1( node ) ) {
4321                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0() );
4322                         }
4323                         else if ( !isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
4324                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor1() );
4325                         }
4326                         else if ( isInFoundNodes0( node ) && isInFoundNodes1( node ) ) {
4327                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor0and1() );
4328                         }
4329                         else {
4330                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
4331                         }
4332                     }
4333                     else {
4334                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
4335                     }
4336                     _rollover_popup.setText( _popup_buffer.toString() );
4337                     _node_desc_popup = PopupFactory.getSharedInstance()
4338                             .getPopup( null,
4339                                        _rollover_popup,
4340                                        e.getLocationOnScreen().x + 10,
4341                                        e.getLocationOnScreen().y - ( lines * 20 ) );
4342                     _node_desc_popup.show();
4343                 }
4344             }
4345         }
4346         catch ( final Exception ex ) {
4347             // Do nothing.
4348         }
4349     }
4350
4351     final private void showNodeEditFrame( final PhylogenyNode n ) {
4352         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4353             // pop up edit box for single node
4354             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
4355             _node_frame_index++;
4356         }
4357         else {
4358             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4359         }
4360     }
4361
4362     final private void showNodeFrame( final PhylogenyNode n ) {
4363         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4364             // pop up edit box for single node
4365             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
4366             _node_frame_index++;
4367         }
4368         else {
4369             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4370         }
4371     }
4372
4373     final private void switchDisplaygetPhylogenyGraphicsType() {
4374         switch ( getPhylogenyGraphicsType() ) {
4375             case RECTANGULAR:
4376                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4377                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
4378                 break;
4379             case EURO_STYLE:
4380                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4381                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
4382                 break;
4383             case ROUNDED:
4384                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4385                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
4386                 break;
4387             case CURVED:
4388                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4389                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
4390                 break;
4391             case TRIANGULAR:
4392                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4393                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
4394                 break;
4395             case CONVEX:
4396                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4397                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
4398                 break;
4399             case UNROOTED:
4400                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4401                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
4402                 break;
4403             case CIRCULAR:
4404                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4405                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
4406                 break;
4407             default:
4408                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
4409         }
4410         if ( getControlPanel().getDynamicallyHideData() != null ) {
4411             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4412                 getControlPanel().getDynamicallyHideData().setEnabled( false );
4413             }
4414             else {
4415                 getControlPanel().getDynamicallyHideData().setEnabled( true );
4416             }
4417         }
4418         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
4419             getControlPanel().setDrawPhylogramEnabled( true );
4420         }
4421         else {
4422             getControlPanel().setDrawPhylogramEnabled( false );
4423         }
4424         if ( getMainPanel().getMainFrame() == null ) {
4425             // Must be "E" applet version.
4426             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
4427                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
4428         }
4429         else {
4430             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
4431         }
4432     }
4433
4434     final void calcMaxDepth() {
4435         if ( _phylogeny != null ) {
4436             _circ_max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4437         }
4438     }
4439
4440     /**
4441      * Set parameters for printing the displayed tree
4442      *
4443      */
4444     final void calcParametersForPainting( final int x, final int y ) {
4445         // updateStyle(); not needed?
4446         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4447             initNodeData();
4448             calculateLongestExtNodeInfo();
4449             if ( ( getLongestExtNodeInfo() > ( x * 0.6 ) )
4450                     && ( getTreeFontSet().getLargeFont().getSize() > ( 2 + TreeFontSet.FONT_SIZE_CHANGE_STEP ) ) ) {
4451                 while ( ( getLongestExtNodeInfo() > ( x * 0.7 ) )
4452                         && ( getTreeFontSet().getLargeFont().getSize() > 2 ) ) {
4453                     getMainPanel().getTreeFontSet().decreaseFontSize( getConfiguration().getMinBaseFontSize(), true );
4454                     calculateLongestExtNodeInfo();
4455                 }
4456             }
4457             else {
4458                 while ( ( getLongestExtNodeInfo() < ( x * 0.6 ) ) && ( getTreeFontSet().getLargeFont()
4459                         .getSize() <= ( getTreeFontSet().getLargeFontMemory().getSize()
4460                                 - TreeFontSet.FONT_SIZE_CHANGE_STEP ) ) ) {
4461                     getMainPanel().getTreeFontSet().increaseFontSize();
4462                     calculateLongestExtNodeInfo();
4463                 }
4464             }
4465             //_length_of_longest_text = calcLengthOfLongestText();
4466             int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4467             final int max_depth = PhylogenyMethods.calculateMaxDepthConsiderCollapsed( _phylogeny ) + 1;
4468             if ( ext_nodes == 1 ) {
4469                 ext_nodes = max_depth;
4470                 if ( ext_nodes < 1 ) {
4471                     ext_nodes = 1;
4472                 }
4473             }
4474             updateOvSizes();
4475             float xdist = 0;
4476             float ov_xdist = 0;
4477             if ( !isNonLinedUpCladogram() ) {
4478                 xdist = ( float ) ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( ext_nodes + 3.0 ) );
4479                 ov_xdist = ( float ) ( getOvMaxWidth() / ( ext_nodes + 3.0 ) );
4480             }
4481             else {
4482                 xdist = ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( max_depth + 1 ) );
4483                 ov_xdist = ( getOvMaxWidth() / ( max_depth + 1 ) );
4484             }
4485             float ydist = ( float ) ( ( y - TreePanel.MOVE ) / ( ext_nodes * 2.0 ) );
4486             if ( xdist < 0.0 ) {
4487                 xdist = 0.0f;
4488             }
4489             if ( ov_xdist < 0.0 ) {
4490                 ov_xdist = 0.0f;
4491             }
4492             if ( ydist < 0.0 ) {
4493                 ydist = 0.0f;
4494             }
4495             setXdistance( xdist );
4496             setYdistance( ydist );
4497             setOvXDistance( ov_xdist );
4498             final double height = _phylogeny.calculateHeight( !_options.isCollapsedWithAverageHeigh() );
4499             if ( height > 0 ) {
4500                 final float corr = ( float ) ( ( x - ( 2.0 * TreePanel.MOVE ) - getLongestExtNodeInfo()
4501                         - getXdistance() ) / height );
4502                 setXcorrectionFactor( corr > 0 ? corr : 0 );
4503                 final float ov_corr = ( float ) ( ( getOvMaxWidth() - getOvXDistance() ) / height );
4504                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
4505             }
4506             else {
4507                 setXcorrectionFactor( 0 );
4508                 setOvXcorrectionFactor( 0 );
4509             }
4510             _circ_max_depth = max_depth;
4511             setUpUrtFactor();
4512             //
4513             if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
4514                     && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
4515                 //                int dynamic_hiding_factor = calcDynamicHidingFactor();
4516                 //                if ( dynamic_hiding_factor > 1 ) {
4517                 //                    while ( dynamic_hiding_factor > 1
4518                 //                            && getTreeFontSet()._fm_large.getHeight() > TreeFontSet.SMALL_FONTS_BASE ) {
4519                 //                        getTreeFontSet().decreaseFontSize( 1, true );
4520                 //                        dynamic_hiding_factor = calcDynamicHidingFactor();
4521                 //                    }
4522                 //                }
4523                 //                else if ( getTreeFontSet().isDecreasedSizeBySystem() ) {
4524                 //                    while ( dynamic_hiding_factor < 1 && getTreeFontSet()._fm_large.getHeight() < 12 ) {
4525                 //                        getTreeFontSet().increaseFontSize();
4526                 //                        dynamic_hiding_factor = calcDynamicHidingFactor();
4527                 //                    }
4528                 //                }
4529             }
4530             //
4531         }
4532     }
4533
4534     final void calculateLongestExtNodeInfo() {
4535         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4536             return;
4537         }
4538         int max_possible_length = ForesterUtil
4539                 .roundToInt( ( getSize().getWidth() - ( 2 * MOVE ) ) * AptxConstants.EXT_NODE_INFO_LENGTH_MAX_RATIO );
4540         if ( max_possible_length < 20 ) {
4541             max_possible_length = 20;
4542         }
4543         int longest = 30;
4544         int longest_txt = 0;
4545         _longest_domain = 0;
4546         PhylogenyNode longest_txt_node = _phylogeny.getFirstExternalNode();
4547         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
4548             int sum = 0;
4549             if ( node.isCollapse() ) {
4550                 continue;
4551             }
4552             final StringBuilder sb = new StringBuilder();
4553             nodeDataAsSB( node, sb );
4554             if ( node.getNodeData().isHasTaxonomy() ) {
4555                 nodeTaxonomyDataAsSB( node.getNodeData().getTaxonomy(), sb );
4556             }
4557             final int txt = sb.length();
4558             if ( txt > longest_txt ) {
4559                 longest_txt = txt;
4560                 longest_txt_node = node;
4561             }
4562             boolean use_vis = false;
4563             final Graphics2D g = ( Graphics2D ) getGraphics();
4564             if ( getControlPanel().isUseVisualStyles() ) {
4565                 use_vis = setFont( g, node, false );
4566             }
4567             if ( !use_vis ) {
4568                 sum = getFontMetricsForLargeDefaultFont().stringWidth( sb.toString() );
4569             }
4570             else {
4571                 sum = getFontMetrics( g.getFont() ).stringWidth( sb.toString() );
4572             }
4573             if ( getControlPanel().isShowBinaryCharacters() && node.getNodeData().isHasBinaryCharacters() ) {
4574                 sum += getFontMetricsForLargeDefaultFont().stringWidth( node.getNodeData().getBinaryCharacters()
4575                         .getGainedCharactersAsStringBuffer().toString() );
4576             }
4577             if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
4578                     && ( node.getNodeData().getVector().size() > 0 ) ) {
4579                 if ( getConfiguration() != null ) {
4580                     sum += getConfiguration().getVectorDataWidth() + 10;
4581                 }
4582                 else {
4583                     sum += RenderableVector.VECTOR_DEFAULT_WIDTH + 10;
4584                 }
4585             }
4586             if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
4587                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
4588                 // FIXME
4589                 // TODO this might need some clean up
4590                 final DomainArchitecture d = node.getNodeData().getSequence().getDomainArchitecture();
4591                 sum += ( ( _domain_structure_width
4592                         / ( ( RenderableDomainArchitecture ) d ).getOriginalSize().getWidth() ) * d.getTotalLength() )
4593                         + 10;
4594                 if ( d.getTotalLength() > _longest_domain ) {
4595                     _longest_domain = d.getTotalLength();
4596                 }
4597             }
4598             if ( getControlPanel().isShowMolSequences() && ( node.getNodeData().isHasSequence() )
4599                     && ( node.getNodeData().getSequence().isMolecularSequenceAligned() )
4600                     && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) ) {
4601                 // FIXME
4602                 sum += RenderableMsaSequence.DEFAULT_WIDTH + 30;
4603             }
4604             if ( sum >= max_possible_length ) {
4605                 _longest_ext_node_info = max_possible_length;
4606                 // return; //FIXME why?
4607             }
4608             if ( sum > longest ) {
4609                 longest = sum;
4610             }
4611         }
4612         _ext_node_with_longest_txt_info = longest_txt_node;
4613         if ( longest >= max_possible_length ) {
4614             _longest_ext_node_info = max_possible_length;
4615         }
4616         else {
4617             _longest_ext_node_info = longest;
4618         }
4619         _length_of_longest_text = calcLengthOfLongestText();
4620     }
4621
4622     final void calculateScaleDistance() {
4623         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4624             return;
4625         }
4626         final double height = getMaxDistanceToRoot();
4627         if ( height > 0 ) {
4628             if ( ( height <= 0.5 ) ) {
4629                 setScaleDistance( 0.01 );
4630             }
4631             else if ( height <= 5.0 ) {
4632                 setScaleDistance( 0.1 );
4633             }
4634             else if ( height <= 50.0 ) {
4635                 setScaleDistance( 1 );
4636             }
4637             else if ( height <= 500.0 ) {
4638                 setScaleDistance( 10 );
4639             }
4640             else {
4641                 setScaleDistance( 100 );
4642             }
4643         }
4644         else {
4645             setScaleDistance( 0.0 );
4646         }
4647         String scale_label = String.valueOf( getScaleDistance() );
4648         if ( !ForesterUtil.isEmpty( _phylogeny.getDistanceUnit() ) ) {
4649             scale_label += " [" + _phylogeny.getDistanceUnit() + "]";
4650         }
4651         setScaleLabel( scale_label );
4652     }
4653
4654     final Color calculateSequenceBasedColor( final Sequence seq ) {
4655         if ( ForesterUtil.isEmpty( seq.getName() ) ) {
4656             return getTreeColorSet().getSequenceColor();
4657         }
4658         Color c = null;
4659         final String seq_name = seq.getName();
4660         c = getControlPanel().getSequenceColors().get( seq_name );
4661         if ( c == null ) {
4662             c = AptxUtil.calculateColorFromString( seq_name, false );
4663             getControlPanel().getSequenceColors().put( seq_name, c );
4664         }
4665         return c;
4666     }
4667
4668     final Color calculateTaxonomyBasedColor( final Taxonomy tax ) {
4669         if ( getOptions().isColorByTaxonomicGroup() ) {
4670             if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4671                 boolean ex = false;
4672                 String group = null;
4673                 try {
4674                     group = TaxonomyUtil.getTaxGroupByTaxCode( tax.getTaxonomyCode() );
4675                 }
4676                 catch ( final Exception e ) {
4677                     ex = true;
4678                 }
4679                 if ( !ex && !ForesterUtil.isEmpty( group ) ) {
4680                     final Color c = ForesterUtil.obtainColorDependingOnTaxonomyGroup( group );
4681                     if ( c != null ) {
4682                         return c;
4683                     }
4684                 }
4685             }
4686             return getTreeColorSet().getTaxonomyColor();
4687         }
4688         else {
4689             if ( ForesterUtil.isEmpty( tax.getTaxonomyCode() ) && ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4690                 return getTreeColorSet().getTaxonomyColor();
4691             }
4692             Color c = null;
4693             if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4694                 c = getControlPanel().getSpeciesColors().get( tax.getTaxonomyCode() );
4695             }
4696             if ( ( c == null ) && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4697                 c = getControlPanel().getSpeciesColors().get( tax.getScientificName() );
4698             }
4699             if ( c == null ) {
4700                 if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4701                     c = AptxUtil.calculateColorFromString( tax.getTaxonomyCode(), true );
4702                     getControlPanel().getSpeciesColors().put( tax.getTaxonomyCode(), c );
4703                 }
4704                 else {
4705                     c = AptxUtil.calculateColorFromString( tax.getScientificName(), true );
4706                     getControlPanel().getSpeciesColors().put( tax.getScientificName(), c );
4707                 }
4708             }
4709             return c;
4710         }
4711     }
4712
4713     void checkForVectorProperties( final Phylogeny phy ) {
4714         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
4715         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
4716             final PhylogenyNode node = iter.next();
4717             if ( node.getNodeData().getProperties() != null ) {
4718                 final PropertiesMap pm = node.getNodeData().getProperties();
4719                 final double[] vector = new double[ pm.getProperties().size() ];
4720                 int counter = 0;
4721                 for( final String ref : pm.getProperties().keySet() ) {
4722                     if ( ref.startsWith( PhyloXmlUtil.VECTOR_PROPERTY_REF ) ) {
4723                         final Property p = pm.getProperty( ref );
4724                         final String value_str = p.getValue();
4725                         final String index_str = ref.substring( PhyloXmlUtil.VECTOR_PROPERTY_REF.length(),
4726                                                                 ref.length() );
4727                         double d = -100;
4728                         try {
4729                             d = Double.parseDouble( value_str );
4730                         }
4731                         catch ( final NumberFormatException e ) {
4732                             JOptionPane.showMessageDialog( this,
4733                                                            "Could not parse \"" + value_str + "\" into a decimal value",
4734                                                            "Problem with Vector Data",
4735                                                            JOptionPane.ERROR_MESSAGE );
4736                             return;
4737                         }
4738                         int i = -1;
4739                         try {
4740                             i = Integer.parseInt( index_str );
4741                         }
4742                         catch ( final NumberFormatException e ) {
4743                             JOptionPane.showMessageDialog( this,
4744                                                            "Could not parse \"" + index_str
4745                                                                    + "\" into index for vector data",
4746                                                            "Problem with Vector Data",
4747                                                            JOptionPane.ERROR_MESSAGE );
4748                             return;
4749                         }
4750                         if ( i < 0 ) {
4751                             JOptionPane.showMessageDialog( this,
4752                                                            "Attempt to use negative index for vector data",
4753                                                            "Problem with Vector Data",
4754                                                            JOptionPane.ERROR_MESSAGE );
4755                             return;
4756                         }
4757                         vector[ i ] = d;
4758                         ++counter;
4759                         stats.addValue( d );
4760                     }
4761                 }
4762                 final List<Double> vector_l = new ArrayList<Double>( counter );
4763                 for( int i = 0; i < counter; ++i ) {
4764                     vector_l.add( vector[ i ] );
4765                 }
4766                 node.getNodeData().setVector( vector_l );
4767             }
4768         }
4769         if ( stats.getN() > 0 ) {
4770             _statistics_for_vector_data = stats;
4771         }
4772     }
4773
4774     void clearCurrentExternalNodesDataBuffer() {
4775         setCurrentExternalNodesDataBuffer( new StringBuilder() );
4776     }
4777
4778     /**
4779      * Collapse the tree from the given node
4780      *
4781      * @param node
4782      *            a PhylogenyNode
4783      */
4784     final void collapse( final PhylogenyNode node ) {
4785         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4786             JOptionPane.showMessageDialog( this,
4787                                            "Cannot collapse in unrooted display type",
4788                                            "Attempt to collapse in unrooted display",
4789                                            JOptionPane.WARNING_MESSAGE );
4790             return;
4791         }
4792         if ( !node.isExternal() && !node.isRoot() ) {
4793             final boolean collapse = !node.isCollapse();
4794             TreePanelUtil.collapseSubtree( node, collapse );
4795             updateSetOfCollapsedExternalNodes();
4796             _phylogeny.recalculateNumberOfExternalDescendants( true );
4797             resetNodeIdToDistToLeafMap();
4798             calculateLongestExtNodeInfo();
4799             setNodeInPreorderToNull();
4800             _control_panel.displayedPhylogenyMightHaveChanged( true );
4801             resetPreferredSize();
4802             updateOvSizes();
4803             _main_panel.adjustJScrollPane();
4804             repaint();
4805         }
4806     }
4807
4808     final void uncollapseAll( final PhylogenyNode node ) {
4809         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4810             JOptionPane.showMessageDialog( this,
4811                                            "Cannot uncollapse in unrooted display type",
4812                                            "Attempt to uncollapse in unrooted display",
4813                                            JOptionPane.WARNING_MESSAGE );
4814             return;
4815         }
4816         if ( !node.isExternal() ) {
4817             TreePanelUtil.uncollapseSubtree( node );
4818             updateSetOfCollapsedExternalNodes();
4819             _phylogeny.recalculateNumberOfExternalDescendants( true );
4820             resetNodeIdToDistToLeafMap();
4821             calculateLongestExtNodeInfo();
4822             setNodeInPreorderToNull();
4823             _control_panel.displayedPhylogenyMightHaveChanged( true );
4824             resetPreferredSize();
4825             updateOvSizes();
4826             _main_panel.adjustJScrollPane();
4827             repaint();
4828         }
4829     }
4830
4831     final void collapseSpeciesSpecificSubtrees() {
4832         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4833             return;
4834         }
4835         setWaitCursor();
4836         TreePanelUtil.collapseSpeciesSpecificSubtrees( _phylogeny );
4837         updateSetOfCollapsedExternalNodes();
4838         _phylogeny.recalculateNumberOfExternalDescendants( true );
4839         resetNodeIdToDistToLeafMap();
4840         calculateLongestExtNodeInfo();
4841         setNodeInPreorderToNull();
4842         resetPreferredSize();
4843         resetDepthCollapseDepthValue();
4844         resetRankCollapseRankValue();
4845         _main_panel.adjustJScrollPane();
4846         getControlPanel().showWhole();
4847         setArrowCursor();
4848     }
4849
4850   
4851
4852     final void colorRank( final String rank ) {
4853         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4854             return;
4855         }
4856         setWaitCursor();
4857         AptxUtil.removeBranchColors( _phylogeny );
4858         final int colorizations = TreePanelUtil.colorPhylogenyAccordingToRanks( _phylogeny, rank, this );
4859         if ( colorizations > 0 ) {
4860             _control_panel.setColorBranches( true );
4861             if ( _control_panel.getUseVisualStylesCb() != null ) {
4862                 _control_panel.getUseVisualStylesCb().setSelected( true );
4863             }
4864             if ( _control_panel.getColorAccSpeciesCb() != null ) {
4865                 _control_panel.getColorAccSpeciesCb().setSelected( false );
4866             }
4867             _options.setColorLabelsSameAsParentBranch( true );
4868             if ( getMainPanel().getMainFrame()._color_labels_same_as_parent_branch != null ) {
4869                 getMainPanel().getMainFrame()._color_labels_same_as_parent_branch.setSelected( true );
4870             }
4871             _control_panel.repaint();
4872         }
4873         setArrowCursor();
4874         repaint();
4875         if ( colorizations > 0 ) {
4876             String msg = "Taxonomy colorization via " + rank + " completed:\n";
4877             if ( colorizations > 1 ) {
4878                 msg += "colorized " + colorizations + " subtrees";
4879             }
4880             else {
4881                 msg += "colorized one subtree";
4882             }
4883             setEdited( true );
4884             JOptionPane.showMessageDialog( this,
4885                                            msg,
4886                                            "Taxonomy Rank-Colorization Completed (" + rank + ")",
4887                                            JOptionPane.INFORMATION_MESSAGE );
4888         }
4889         else {
4890             String msg = "Could not taxonomy rank-colorize any subtree via " + rank + ".\n";
4891             msg += "Possible solutions (given that suitable taxonomic information is present):\n";
4892             msg += "select a different rank (e.g. phylum, genus, ...)\n";
4893             msg += "  and/or\n";
4894             msg += "execute:\n";
4895             msg += "1. \"" + MainFrame.OBTAIN_DETAILED_TAXONOMIC_INFORMATION + "\" (Tools)\n";
4896             msg += "2. \"" + MainFrame.INFER_ANCESTOR_TAXONOMIES + "\" (Analysis)";
4897             JOptionPane.showMessageDialog( this,
4898                                            msg,
4899                                            "Taxonomy Rank-Colorization Failed",
4900                                            JOptionPane.WARNING_MESSAGE );
4901         }
4902     }
4903
4904     final void confColor() {
4905         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
4906             return;
4907         }
4908         setWaitCursor();
4909         AptxUtil.removeBranchColors( _phylogeny );
4910         TreePanelUtil.colorPhylogenyAccordingToConfidenceValues( _phylogeny, this );
4911         _control_panel.setColorBranches( true );
4912         if ( _control_panel.getUseVisualStylesCb() != null ) {
4913             _control_panel.getUseVisualStylesCb().setSelected( true );
4914         }
4915         setArrowCursor();
4916         repaint();
4917     }
4918
4919     final void decreaseDomainStructureEvalueThresholdExp() {
4920         if ( _domain_structure_e_value_thr_exp > -20 ) {
4921             _domain_structure_e_value_thr_exp -= 1;
4922         }
4923     }
4924
4925     /**
4926      * Find the node, if any, at the given location
4927      *
4928      * @param x
4929      * @param y
4930      * @return pointer to the node at x,y, null if not found
4931      */
4932     final PhylogenyNode findNode( final int x, final int y ) {
4933         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
4934             return null;
4935         }
4936         final int half_box_size_plus_wiggle = ( getOptions().getDefaultNodeShapeSize() / 2 ) + WIGGLE;
4937         for( final PhylogenyNodeIterator iter = _phylogeny.iteratorPostorder(); iter.hasNext(); ) {
4938             final PhylogenyNode node = iter.next();
4939             if ( ( _phylogeny.isRooted() || !node.isRoot() || ( node.getNumberOfDescendants() > 2 ) )
4940                     && ( ( node.getXcoord() - half_box_size_plus_wiggle ) <= x )
4941                     && ( ( node.getXcoord() + half_box_size_plus_wiggle ) >= x )
4942                     && ( ( node.getYcoord() - half_box_size_plus_wiggle ) <= y )
4943                     && ( ( node.getYcoord() + half_box_size_plus_wiggle ) >= y ) ) {
4944                 return node;
4945             }
4946         }
4947         return null;
4948     }
4949
4950     final Configuration getConfiguration() {
4951         return _configuration;
4952     }
4953
4954     final ControlPanel getControlPanel() {
4955         return _control_panel;
4956     }
4957
4958     String getCurrentExternalNodesDataBufferAsString() {
4959         return _current_external_nodes_data_buffer.toString();
4960     }
4961
4962     int getCurrentExternalNodesDataBufferChangeCounter() {
4963         return _current_external_nodes_data_buffer_change_counter;
4964     }
4965
4966     final int getDomainStructureEvalueThresholdExp() {
4967         return _domain_structure_e_value_thr_exp;
4968     }
4969
4970     final Set<Long> getFoundNodes0() {
4971         return _found_nodes_0;
4972     }
4973
4974     final Set<Long> getFoundNodes1() {
4975         return _found_nodes_1;
4976     }
4977
4978     List<PhylogenyNode> getFoundNodesAsListOfPhylogenyNodes() {
4979         final List<PhylogenyNode> additional_nodes = new ArrayList<PhylogenyNode>();
4980         if ( getFoundNodes0() != null ) {
4981             for( final Long id : getFoundNodes0() ) {
4982                 final PhylogenyNode n = _phylogeny.getNode( id );
4983                 if ( n != null ) {
4984                     additional_nodes.add( n );
4985                 }
4986             }
4987         }
4988         if ( getFoundNodes1() != null ) {
4989             for( final Long id : getFoundNodes1() ) {
4990                 if ( ( getFoundNodes0() == null ) || !getFoundNodes0().contains( id ) ) {
4991                     final PhylogenyNode n = _phylogeny.getNode( id );
4992                     if ( n != null ) {
4993                         additional_nodes.add( n );
4994                     }
4995                 }
4996             }
4997         }
4998         return additional_nodes;
4999     }
5000
5001     final Color getGraphicsForNodeBoxWithColorForParentBranch( final PhylogenyNode node ) {
5002         if ( getControlPanel().isUseVisualStyles() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
5003             return ( PhylogenyMethods.getBranchColorValue( node ) );
5004         }
5005         else {
5006             return ( getTreeColorSet().getBranchColor() );
5007         }
5008     }
5009
5010     final int getLongestExtNodeInfo() {
5011         return _longest_ext_node_info;
5012     }
5013
5014     final Options getOptions() {
5015         if ( _options == null ) {
5016             _options = getControlPanel().getOptions();
5017         }
5018         return _options;
5019     }
5020
5021     final Rectangle2D getOvRectangle() {
5022         return _ov_rectangle;
5023     }
5024
5025     final Rectangle getOvVirtualRectangle() {
5026         return _ov_virtual_rectangle;
5027     }
5028
5029     final PHYLOGENY_GRAPHICS_TYPE getPhylogenyGraphicsType() {
5030         return _graphics_type;
5031     }
5032
5033     final Color getSequenceBasedColor( final PhylogenyNode node ) {
5034         if ( node.getNodeData().isHasSequence() ) {
5035             return calculateSequenceBasedColor( node.getNodeData().getSequence() );
5036         }
5037         // return non-colorized color
5038         return getTreeColorSet().getSequenceColor();
5039     }
5040
5041     final double getStartingAngle() {
5042         return _urt_starting_angle;
5043     }
5044
5045     DescriptiveStatistics getStatisticsForExpressionValues() {
5046         return _statistics_for_vector_data;
5047     }
5048
5049     final Color getTaxonomyBasedColor( final PhylogenyNode node ) {
5050         if ( node.isExternal() && node.getNodeData().isHasTaxonomy() ) {
5051             return calculateTaxonomyBasedColor( node.getNodeData().getTaxonomy() );
5052         }
5053         // return non-colorized color
5054         return getTreeColorSet().getTaxonomyColor();
5055     }
5056
5057     final File getTreeFile() {
5058         return _treefile;
5059     }
5060
5061     final float getXcorrectionFactor() {
5062         return _x_correction_factor;
5063     }
5064
5065     final float getXdistance() {
5066         return _x_distance;
5067     }
5068
5069     final float getYdistance() {
5070         return _y_distance;
5071     }
5072
5073     final void increaseDomainStructureEvalueThresholdExp() {
5074         if ( _domain_structure_e_value_thr_exp < 3 ) {
5075             _domain_structure_e_value_thr_exp += 1;
5076         }
5077     }
5078
5079     final void initNodeData() {
5080         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5081             return;
5082         }
5083         double _max_original_domain_structure_width = 0.0;
5084         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
5085             if ( node.getNodeData().isHasSequence()
5086                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
5087                 RenderableDomainArchitecture rds = null;
5088                 if ( !( node.getNodeData().getSequence()
5089                         .getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
5090                     if ( SPECIAL_DOMAIN_COLORING ) {
5091                         rds = new RenderableDomainArchitecture( node.getNodeData().getSequence()
5092                                 .getDomainArchitecture(), node.getName() );
5093                     }
5094                     else {
5095                         rds = new RenderableDomainArchitecture( node.getNodeData().getSequence()
5096                                 .getDomainArchitecture() );
5097                     }
5098                     node.getNodeData().getSequence().setDomainArchitecture( rds );
5099                 }
5100                 else {
5101                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
5102                 }
5103                 if ( getControlPanel().isShowDomainArchitectures() ) {
5104                     final double dsw = rds.getOriginalSize().getWidth();
5105                     if ( dsw > _max_original_domain_structure_width ) {
5106                         _max_original_domain_structure_width = dsw;
5107                     }
5108                 }
5109             }
5110         }
5111         if ( getControlPanel().isShowDomainArchitectures() ) {
5112             final float ds_factor_width = ( float ) ( _domain_structure_width / _max_original_domain_structure_width );
5113             for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
5114                 if ( node.getNodeData().isHasSequence()
5115                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
5116                     final RenderableDomainArchitecture rds = ( RenderableDomainArchitecture ) node.getNodeData()
5117                             .getSequence().getDomainArchitecture();
5118                     rds.setRenderingFactorWidth( ds_factor_width );
5119                     rds.setParameter( _domain_structure_e_value_thr_exp );
5120                 }
5121             }
5122         }
5123     }
5124
5125     final boolean inOv( final MouseEvent e ) {
5126         return ( ( e.getX() > ( getVisibleRect().x + getOvXPosition() + 1 ) )
5127                 && ( e.getX() < ( ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() ) - 1 ) )
5128                 && ( e.getY() > ( getVisibleRect().y + getOvYPosition() + 1 ) )
5129                 && ( e.getY() < ( ( getVisibleRect().y + getOvYPosition() + getOvMaxHeight() ) - 1 ) ) );
5130     }
5131
5132     final boolean inOvRectangle( final MouseEvent e ) {
5133         return ( ( e.getX() >= ( getOvRectangle().getX() - 1 ) )
5134                 && ( e.getX() <= ( getOvRectangle().getX() + getOvRectangle().getWidth() + 1 ) )
5135                 && ( e.getY() >= ( getOvRectangle().getY() - 1 ) )
5136                 && ( e.getY() <= ( getOvRectangle().getY() + getOvRectangle().getHeight() + 1 ) ) );
5137     }
5138
5139     final boolean isApplet() {
5140         return getMainPanel() instanceof MainPanelApplets;
5141     }
5142
5143     final boolean isCanCollapse() {
5144         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5145     }
5146
5147     final boolean isCanUncollapseAll( final PhylogenyNode node ) {
5148         if ( node.isExternal() || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) ) {
5149             return false;
5150         }
5151         if ( node.isCollapse() ) {
5152             return true;
5153         }
5154         final PhylogenyNodeIterator it = new PreorderTreeIterator( node );
5155         while ( it.hasNext() ) {
5156             if ( it.next().isCollapse() ) {
5157                 return true;
5158             }
5159         }
5160         return false;
5161     }
5162
5163     final boolean isCanColorSubtree() {
5164         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5165     }
5166
5167     final boolean isCanCopy() {
5168         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
5169     }
5170
5171     final boolean isCanCut( final PhylogenyNode node ) {
5172         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
5173                 && !node.isRoot() );
5174     }
5175
5176     final boolean isCanDelete() {
5177         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
5178     }
5179
5180     final boolean isCanPaste() {
5181         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
5182                 && ( getCutOrCopiedTree() != null ) && !getCutOrCopiedTree().isEmpty() );
5183     }
5184
5185     final boolean isCanReroot() {
5186         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && ( _subtree_index < 1 ) );
5187     }
5188
5189     final boolean isCanSubtree( final PhylogenyNode node ) {
5190         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && !node.isExternal()
5191                 && ( !node.isRoot() || ( _subtree_index > 0 ) ) );
5192     }
5193
5194     final boolean isCurrentTreeIsSubtree() {
5195         return ( _subtree_index > 0 );
5196     }
5197
5198     final boolean isEdited() {
5199         return _edited;
5200     }
5201
5202     final boolean isInOvRect() {
5203         return _in_ov_rect;
5204     }
5205
5206     final boolean isOvOn() {
5207         return _ov_on;
5208     }
5209
5210     final boolean isPhyHasBranchLengths() {
5211         return _phy_has_branch_lengths;
5212     }
5213
5214     final void midpointRoot() {
5215         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5216             return;
5217         }
5218         if ( !_phylogeny.isRerootable() ) {
5219             JOptionPane.showMessageDialog( this,
5220                                            "This is not rerootable",
5221                                            "Not rerootable",
5222                                            JOptionPane.WARNING_MESSAGE );
5223             return;
5224         }
5225         setNodeInPreorderToNull();
5226         setWaitCursor();
5227         PhylogenyMethods.midpointRoot( _phylogeny );
5228         resetNodeIdToDistToLeafMap();
5229         setArrowCursor();
5230         setEdited( true );
5231         repaint();
5232     }
5233
5234     final void mouseClicked( final MouseEvent e ) {
5235         if ( getOptions().isShowOverview() && isOvOn() && isInOv() ) {
5236             final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
5237             final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
5238             double x = ( e.getX() - getVisibleRect().x - getOvXPosition() - ( getOvRectangle().getWidth() / 2.0 ) )
5239                     * w_ratio;
5240             double y = ( e.getY() - getVisibleRect().y - getOvYPosition() - ( getOvRectangle().getHeight() / 2.0 ) )
5241                     * h_ratio;
5242             if ( x < 0 ) {
5243                 x = 0;
5244             }
5245             if ( y < 0 ) {
5246                 y = 0;
5247             }
5248             final double max_x = getWidth() - getVisibleRect().width;
5249             final double max_y = getHeight() - getVisibleRect().height;
5250             if ( x > max_x ) {
5251                 x = max_x;
5252             }
5253             if ( y > max_y ) {
5254                 y = max_y;
5255             }
5256             getMainPanel().getCurrentScrollPane().getViewport()
5257                     .setViewPosition( new Point( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ) ) );
5258             setInOvRect( true );
5259             repaint();
5260         }
5261         else {
5262             final PhylogenyNode node = findNode( e.getX(), e.getY() );
5263             if ( node != null ) {
5264                 if ( !node.isRoot() && node.getParent().isCollapse() ) {
5265                     return;
5266                 }
5267                 _highlight_node = node;
5268                 // Check if shift key is down
5269                 if ( ( e.getModifiers() & InputEvent.SHIFT_MASK ) != 0 ) {
5270                     // Yes, so add to _found_nodes
5271                     if ( getFoundNodes0() == null ) {
5272                         setFoundNodes0( new HashSet<Long>() );
5273                     }
5274                     getFoundNodes0().add( node.getId() );
5275                     // Check if control key is down
5276                 }
5277                 else if ( ( e.getModifiers() & InputEvent.CTRL_MASK ) != 0 ) {
5278                     // Yes, so pop-up menu
5279                     displayNodePopupMenu( node, e.getX(), e.getY() );
5280                     // Handle unadorned click
5281                 }
5282                 else {
5283                     // Check for right mouse button
5284                     if ( e.getModifiers() == 4 ) {
5285                         displayNodePopupMenu( node, e.getX(), e.getY() );
5286                     }
5287                     else {
5288                         // if not in _found_nodes, clear _found_nodes
5289                         handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
5290                     }
5291                 }
5292             }
5293             else {
5294                 // no node was clicked
5295                 _highlight_node = null;
5296             }
5297         }
5298         repaint();
5299     }
5300
5301     final void mouseDragInBrowserPanel( final MouseEvent e ) {
5302         setCursor( MOVE_CURSOR );
5303         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
5304         scroll_position.x -= ( e.getX() - getLastDragPointX() );
5305         scroll_position.y -= ( e.getY() - getLastDragPointY() );
5306         if ( scroll_position.x < 0 ) {
5307             scroll_position.x = 0;
5308         }
5309         else {
5310             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
5311                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
5312             if ( scroll_position.x > max_x ) {
5313                 scroll_position.x = max_x;
5314             }
5315         }
5316         if ( scroll_position.y < 0 ) {
5317             scroll_position.y = 0;
5318         }
5319         else {
5320             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
5321                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
5322             if ( scroll_position.y > max_y ) {
5323                 scroll_position.y = max_y;
5324             }
5325         }
5326         if ( isOvOn() || getOptions().isShowScale() ) {
5327             repaint();
5328         }
5329         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
5330     }
5331
5332     final void mouseDragInOvRectangle( final MouseEvent e ) {
5333         setCursor( HAND_CURSOR );
5334         final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
5335         final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
5336         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
5337         double dx = ( ( w_ratio * e.getX() ) - ( w_ratio * getLastDragPointX() ) );
5338         double dy = ( ( h_ratio * e.getY() ) - ( h_ratio * getLastDragPointY() ) );
5339         scroll_position.x = ForesterUtil.roundToInt( scroll_position.x + dx );
5340         scroll_position.y = ForesterUtil.roundToInt( scroll_position.y + dy );
5341         if ( scroll_position.x <= 0 ) {
5342             scroll_position.x = 0;
5343             dx = 0;
5344         }
5345         else {
5346             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
5347                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
5348             if ( scroll_position.x >= max_x ) {
5349                 dx = 0;
5350                 scroll_position.x = max_x;
5351             }
5352         }
5353         if ( scroll_position.y <= 0 ) {
5354             dy = 0;
5355             scroll_position.y = 0;
5356         }
5357         else {
5358             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
5359                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
5360             if ( scroll_position.y >= max_y ) {
5361                 dy = 0;
5362                 scroll_position.y = max_y;
5363             }
5364         }
5365         repaint();
5366         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
5367         setLastMouseDragPointX( ( float ) ( e.getX() + dx ) );
5368         setLastMouseDragPointY( ( float ) ( e.getY() + dy ) );
5369     }
5370
5371     final void mouseMoved( final MouseEvent e ) {
5372         requestFocusInWindow();
5373         if ( _current_external_nodes != null ) {
5374             _current_external_nodes = null;
5375             repaint();
5376         }
5377         if ( getControlPanel().isNodeDescPopup() ) {
5378             if ( _node_desc_popup != null ) {
5379                 _node_desc_popup.hide();
5380                 _node_desc_popup = null;
5381             }
5382         }
5383         if ( getOptions().isShowOverview() && isOvOn() ) {
5384             if ( inOvVirtualRectangle( e ) ) {
5385                 if ( !isInOvRect() ) {
5386                     setInOvRect( true );
5387                     repaint();
5388                 }
5389             }
5390             else {
5391                 if ( isInOvRect() ) {
5392                     setInOvRect( false );
5393                     repaint();
5394                 }
5395             }
5396         }
5397         if ( inOv( e ) && getOptions().isShowOverview() && isOvOn() ) {
5398             if ( !isInOv() ) {
5399                 setInOv( true );
5400             }
5401         }
5402         else {
5403             if ( isInOv() ) {
5404                 setInOv( false );
5405             }
5406             final PhylogenyNode node = findNode( e.getX(), e.getY() );
5407             if ( ( node != null ) && ( node.isRoot() || !node.getParent().isCollapse() ) ) {
5408                 if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.GET_EXT_DESC_DATA ) ) {
5409                     for( final PhylogenyNode n : node.getAllExternalDescendants() ) {
5410                         addToCurrentExternalNodes( n.getId() );
5411                     }
5412                     setCursor( HAND_CURSOR );
5413                     repaint();
5414                 }
5415                 else if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.CUT_SUBTREE )
5416                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.COPY_SUBTREE )
5417                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.PASTE_SUBTREE )
5418                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.DELETE_NODE_OR_SUBTREE )
5419                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.REROOT )
5420                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.ADD_NEW_NODE ) ) {
5421                     setCursor( CUT_CURSOR );
5422                 }
5423                 else {
5424                     setCursor( HAND_CURSOR );
5425                     if ( getControlPanel().isNodeDescPopup() ) {
5426                         showNodeDataPopup( e, node );
5427                     }
5428                 }
5429             }
5430             else {
5431                 setCursor( ARROW_CURSOR );
5432             }
5433         }
5434     }
5435
5436     final void mouseReleasedInBrowserPanel( final MouseEvent e ) {
5437         setCursor( ARROW_CURSOR );
5438     }
5439
5440     final void multiplyUrtFactor( final float f ) {
5441         _urt_factor *= f;
5442     }
5443
5444     final JApplet obtainApplet() {
5445         return ( ( MainPanelApplets ) getMainPanel() ).getApplet();
5446     }
5447
5448     final void paintBranchCircular( final PhylogenyNode p,
5449                                     final PhylogenyNode c,
5450                                     final Graphics2D g,
5451                                     final boolean radial_labels,
5452                                     final boolean to_pdf,
5453                                     final boolean to_graphics_file ) {
5454         final double angle = _urt_nodeid_angle_map.get( c.getId() );
5455         final double root_x = _root.getXcoord();
5456         final double root_y = _root.getYcoord();
5457         final double dx = root_x - p.getXcoord();
5458         final double dy = root_y - p.getYcoord();
5459         final double parent_radius = Math.sqrt( ( dx * dx ) + ( dy * dy ) );
5460         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
5461         assignGraphicsForBranchWithColorForParentBranch( c, false, g, to_pdf, to_graphics_file );
5462         if ( ( c.isFirstChildNode() || c.isLastChildNode() )
5463                 && ( ( Math.abs( parent_radius * arc ) > 1.5 ) || to_pdf || to_graphics_file ) ) {
5464             final double r2 = 2.0 * parent_radius;
5465             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
5466         }
5467         drawLine( c.getXcoord(),
5468                   c.getYcoord(),
5469                   root_x + ( Math.cos( angle ) * parent_radius ),
5470                   root_y + ( Math.sin( angle ) * parent_radius ),
5471                   g );
5472         paintNodeBox( c.getXcoord(), c.getYcoord(), c, g, to_pdf, to_graphics_file );
5473         if ( c.isExternal() ) {
5474             final boolean is_in_found_nodes = isInFoundNodes0( c ) || isInFoundNodes1( c )
5475                     || isInCurrentExternalNodes( c );
5476             if ( ( _dynamic_hiding_factor > 1 ) && !is_in_found_nodes
5477                     && ( ( _urt_nodeid_index_map.get( c.getId() ) % _dynamic_hiding_factor ) != 1 ) ) {
5478                 return;
5479             }
5480             paintNodeDataUnrootedCirc( g, c, to_pdf, to_graphics_file, radial_labels, 0, is_in_found_nodes );
5481         }
5482     }
5483
5484     final void paintBranchCircularLite( final PhylogenyNode p, final PhylogenyNode c, final Graphics2D g ) {
5485         final double angle = _urt_nodeid_angle_map.get( c.getId() );
5486         final double root_x = _root.getXSecondary();
5487         final double root_y = _root.getYSecondary();
5488         final double dx = root_x - p.getXSecondary();
5489         final double dy = root_y - p.getYSecondary();
5490         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
5491         final double parent_radius = Math.sqrt( ( dx * dx ) + ( dy * dy ) );
5492         g.setColor( getTreeColorSet().getOvColor() );
5493         if ( ( c.isFirstChildNode() || c.isLastChildNode() ) && ( Math.abs( arc ) > 0.02 ) ) {
5494             final double r2 = 2.0 * parent_radius;
5495             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
5496         }
5497         drawLine( c.getXSecondary(),
5498                   c.getYSecondary(),
5499                   root_x + ( Math.cos( angle ) * parent_radius ),
5500                   root_y + ( Math.sin( angle ) * parent_radius ),
5501                   g );
5502         if ( isInFoundNodes( c ) || isInCurrentExternalNodes( c ) ) {
5503             g.setColor( getColorForFoundNode( c ) );
5504             drawRectFilled( c.getXSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5505                             c.getYSecondary() - OVERVIEW_FOUND_NODE_BOX_SIZE_HALF,
5506                             OVERVIEW_FOUND_NODE_BOX_SIZE,
5507                             OVERVIEW_FOUND_NODE_BOX_SIZE,
5508                             g );
5509         }
5510     }
5511
5512     final void paintCircular( final Phylogeny phy,
5513                               final double starting_angle,
5514                               final int center_x,
5515                               final int center_y,
5516                               final int radius,
5517                               final Graphics2D g,
5518                               final boolean to_pdf,
5519                               final boolean to_graphics_file ) {
5520         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes() - _collapsed_external_nodeid_set.size();
5521         System.out.println( "# collapsed external = " + _collapsed_external_nodeid_set.size() );
5522         _root = phy.getRoot();
5523         _root.setXcoord( center_x );
5524         _root.setYcoord( center_y );
5525         final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
5526         double current_angle = starting_angle;
5527         int i = 0;
5528         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
5529             final PhylogenyNode n = it.next();
5530             if ( !n.isCollapse() ) {
5531                 n.setXcoord( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
5532                 n.setYcoord( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
5533                 _urt_nodeid_angle_map.put( n.getId(), current_angle );
5534                 _urt_nodeid_index_map.put( n.getId(), i++ );
5535                 current_angle += ( TWO_PI / circ_num_ext_nodes );
5536             }
5537             else {
5538                 //TODO remove me
5539                 System.out.println( "is collapse" + n.getName() );
5540             }
5541         }
5542         paintCirculars( phy.getRoot(), phy, center_x, center_y, radius, radial_labels, g, to_pdf, to_graphics_file );
5543         paintNodeBox( _root.getXcoord(), _root.getYcoord(), _root, g, to_pdf, to_graphics_file );
5544     }
5545
5546     final void paintCircularLite( final Phylogeny phy,
5547                                   final double starting_angle,
5548                                   final int center_x,
5549                                   final int center_y,
5550                                   final int radius,
5551                                   final Graphics2D g ) {
5552         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes();
5553         _root = phy.getRoot();
5554         _root.setXSecondary( center_x );
5555         _root.setYSecondary( center_y );
5556         double current_angle = starting_angle;
5557         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
5558             final PhylogenyNode n = it.next();
5559             n.setXSecondary( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
5560             n.setYSecondary( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
5561             _urt_nodeid_angle_map.put( n.getId(), current_angle );
5562             current_angle += ( TWO_PI / circ_num_ext_nodes );
5563         }
5564         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
5565     }
5566
5567     final void paintPhylogeny( final Graphics2D g,
5568                                final boolean to_pdf,
5569                                final boolean to_graphics_file,
5570                                final int graphics_file_width,
5571                                final int graphics_file_height,
5572                                final int graphics_file_x,
5573                                final int graphics_file_y ) {
5574         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
5575             return;
5576         }
5577         if ( _control_panel.isShowSequenceRelations() ) {
5578             _query_sequence = _control_panel.getSelectedQuerySequence();
5579         }
5580         // Color the background
5581         if ( !to_pdf ) {
5582             final Rectangle r = getVisibleRect();
5583             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
5584                 g.setColor( getTreeColorSet().getBackgroundColor() );
5585                 if ( !to_graphics_file ) {
5586                     g.fill( r );
5587                 }
5588                 else {
5589                     if ( getOptions().isPrintBlackAndWhite() ) {
5590                         g.setColor( Color.WHITE );
5591                     }
5592                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
5593                 }
5594             }
5595             else {
5596                 if ( !to_graphics_file ) {
5597                     g.setPaint( new GradientPaint( r.x,
5598                                                    r.y,
5599                                                    getTreeColorSet().getBackgroundColor(),
5600                                                    r.x,
5601                                                    r.y + r.height,
5602                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
5603                     g.fill( r );
5604                 }
5605                 else {
5606                     g.setPaint( new GradientPaint( graphics_file_x,
5607                                                    graphics_file_y,
5608                                                    getTreeColorSet().getBackgroundColor(),
5609                                                    graphics_file_x,
5610                                                    graphics_file_y + graphics_file_height,
5611                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
5612                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
5613                 }
5614             }
5615             setupStroke( g );
5616         }
5617         else {
5618             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
5619         }
5620         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
5621                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5622             _external_node_index = 0;
5623             // Position starting X of tree
5624             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/ ) {
5625                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
5626             }
5627             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
5628                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE
5629                         + ( _phylogeny.getRoot().getDistanceToParent() * getXcorrectionFactor() ) ) );
5630             }
5631             else {
5632                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
5633             }
5634             // Position starting Y of tree
5635             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
5636                     + ( TreePanel.MOVE / 2.0f ) );
5637             final int dynamic_hiding_factor = calcDynamicHidingFactor();
5638             if ( getControlPanel().isDynamicallyHideData() ) {
5639                 if ( dynamic_hiding_factor > 1 ) {
5640                     getControlPanel().setDynamicHidingIsOn( true );
5641                 }
5642                 else {
5643                     getControlPanel().setDynamicHidingIsOn( false );
5644                 }
5645             }
5646             if ( _nodes_in_preorder == null ) {
5647                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
5648                 int i = 0;
5649                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
5650                     _nodes_in_preorder[ i++ ] = it.next();
5651                 }
5652             }
5653             final boolean disallow_shortcutting = ( dynamic_hiding_factor < 40 )
5654                     || getControlPanel().isUseVisualStyles() || getOptions().isShowDefaultNodeShapesForMarkedNodes()
5655                     || ( ( getFoundNodes0() != null ) && !getFoundNodes0().isEmpty() )
5656                     || ( ( getFoundNodes1() != null ) && !getFoundNodes1().isEmpty() )
5657                     || ( ( getCurrentExternalNodes() != null ) && !getCurrentExternalNodes().isEmpty() )
5658                     || to_graphics_file || to_pdf;
5659             for( final PhylogenyNode element : _nodes_in_preorder ) {
5660                 paintNodeRectangular( g,
5661                                       element,
5662                                       to_pdf,
5663                                       getControlPanel().isDynamicallyHideData() && ( dynamic_hiding_factor > 1 ),
5664                                       dynamic_hiding_factor,
5665                                       to_graphics_file,
5666                                       disallow_shortcutting );
5667             }
5668             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
5669                 if ( !( to_graphics_file || to_pdf ) ) {
5670                     paintScale( g,
5671                                 getVisibleRect().x,
5672                                 getVisibleRect().y + getVisibleRect().height,
5673                                 to_pdf,
5674                                 to_graphics_file );
5675                 }
5676                 else {
5677                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
5678                 }
5679             }
5680             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5681                 paintPhylogenyLite( g );
5682             }
5683         }
5684         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5685             if ( getControlPanel().getDynamicallyHideData() != null ) {
5686                 getControlPanel().setDynamicHidingIsOn( false );
5687             }
5688             final double angle = getStartingAngle();
5689             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
5690             _dynamic_hiding_factor = 0;
5691             if ( getControlPanel().isDynamicallyHideData() ) {
5692                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5
5693                         * getPhylogeny().getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
5694             }
5695             if ( getControlPanel().getDynamicallyHideData() != null ) {
5696                 if ( _dynamic_hiding_factor > 1 ) {
5697                     getControlPanel().setDynamicHidingIsOn( true );
5698                 }
5699                 else {
5700                     getControlPanel().setDynamicHidingIsOn( false );
5701                 }
5702             }
5703             paintUnrooted( _phylogeny.getRoot(),
5704                            angle,
5705                            ( float ) ( angle + ( 2 * Math.PI ) ),
5706                            radial_labels,
5707                            g,
5708                            to_pdf,
5709                            to_graphics_file );
5710             if ( getOptions().isShowScale() ) {
5711                 if ( !( to_graphics_file || to_pdf ) ) {
5712                     paintScale( g,
5713                                 getVisibleRect().x,
5714                                 getVisibleRect().y + getVisibleRect().height,
5715                                 to_pdf,
5716                                 to_graphics_file );
5717                 }
5718                 else {
5719                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
5720                 }
5721             }
5722             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5723                 g.setColor( getTreeColorSet().getOvColor() );
5724                 paintUnrootedLite( _phylogeny.getRoot(),
5725                                    angle,
5726                                    angle + ( 2 * Math.PI ),
5727                                    g,
5728                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
5729                 paintOvRectangle( g );
5730             }
5731         }
5732         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
5733             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() )
5734                     / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
5735             final int d = radius + MOVE + getLongestExtNodeInfo();
5736             _dynamic_hiding_factor = 0;
5737             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
5738                 _dynamic_hiding_factor = ( int ) ( ( getFontMetricsForLargeDefaultFont().getHeight() * 1.5
5739                         * getPhylogeny().getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
5740             }
5741             if ( getControlPanel().getDynamicallyHideData() != null ) {
5742                 if ( _dynamic_hiding_factor > 1 ) {
5743                     getControlPanel().setDynamicHidingIsOn( true );
5744                 }
5745                 else {
5746                     getControlPanel().setDynamicHidingIsOn( false );
5747                 }
5748             }
5749             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
5750             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
5751                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
5752                         : getOvMaxWidth() / 2 );
5753                 double x_scale = 1.0;
5754                 double y_scale = 1.0;
5755                 int x_pos = getVisibleRect().x + getOvXPosition();
5756                 int y_pos = getVisibleRect().y + getOvYPosition();
5757                 if ( getWidth() > getHeight() ) {
5758                     x_scale = ( double ) getHeight() / getWidth();
5759                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
5760                 }
5761                 else {
5762                     y_scale = ( double ) getWidth() / getHeight();
5763                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
5764                 }
5765                 _at = g.getTransform();
5766                 g.scale( x_scale, y_scale );
5767                 paintCircularLite( _phylogeny,
5768                                    getStartingAngle(),
5769                                    x_pos + radius_ov,
5770                                    y_pos + radius_ov,
5771                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo()
5772                                            / ( getVisibleRect().width / getOvRectangle().getWidth() ) ) ),
5773                                    g );
5774                 g.setTransform( _at );
5775                 paintOvRectangle( g );
5776             }
5777         }
5778     }
5779
5780     final void recalculateMaxDistanceToRoot() {
5781         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
5782     }
5783
5784     /**
5785      * Remove all edit-node frames
5786      */
5787     final void removeAllEditNodeJFrames() {
5788         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
5789             if ( _node_frames[ i ] != null ) {
5790                 _node_frames[ i ].dispose();
5791                 _node_frames[ i ] = null;
5792             }
5793         }
5794         _node_frame_index = 0;
5795     }
5796
5797     /**
5798      * Remove a node-edit frame.
5799      */
5800     final void removeEditNodeFrame( final int i ) {
5801         _node_frame_index--;
5802         _node_frames[ i ] = null;
5803         if ( i < _node_frame_index ) {
5804             for( int j = 0; j < ( _node_frame_index - 1 ); j++ ) {
5805                 _node_frames[ j ] = _node_frames[ j + 1 ];
5806             }
5807             _node_frames[ _node_frame_index ] = null;
5808         }
5809     }
5810
5811     final void reRoot( final PhylogenyNode node ) {
5812         if ( !getPhylogeny().isRerootable() ) {
5813             JOptionPane.showMessageDialog( this,
5814                                            "This is not rerootable",
5815                                            "Not rerootable",
5816                                            JOptionPane.WARNING_MESSAGE );
5817             return;
5818         }
5819         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5820             JOptionPane.showMessageDialog( this,
5821                                            "Cannot reroot in unrooted display type",
5822                                            "Attempt to reroot tree in unrooted display",
5823                                            JOptionPane.WARNING_MESSAGE );
5824             return;
5825         }
5826         getPhylogeny().reRoot( node );
5827         getPhylogeny().recalculateNumberOfExternalDescendants( true );
5828         resetNodeIdToDistToLeafMap();
5829         setNodeInPreorderToNull();
5830         resetPreferredSize();
5831         getMainPanel().adjustJScrollPane();
5832         setEdited( true );
5833         repaint();
5834         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
5835             getControlPanel().showWhole();
5836         }
5837     }
5838
5839     final void resetNodeIdToDistToLeafMap() {
5840         _nodeid_dist_to_leaf = new HashMap<Long, Short>();
5841     }
5842
5843     final void resetPreferredSize() {
5844         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
5845             return;
5846         }
5847         int x = 0;
5848         int y = 0;
5849         y = TreePanel.MOVE
5850                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
5851         if ( getControlPanel().isDrawPhylogram() ) {
5852             x = TreePanel.MOVE + getLongestExtNodeInfo()
5853                     + ForesterUtil.roundToInt( ( getXcorrectionFactor()
5854                             * getPhylogeny().calculateHeight( !_options.isCollapsedWithAverageHeigh() ) )
5855                             + getXdistance() );
5856         }
5857         else {
5858             if ( !isNonLinedUpCladogram() ) {
5859                 x = TreePanel.MOVE + getLongestExtNodeInfo() + ForesterUtil
5860                         .roundToInt( getXdistance() * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
5861             }
5862             else {
5863                 x = TreePanel.MOVE + getLongestExtNodeInfo() + ForesterUtil
5864                         .roundToInt( getXdistance() * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
5865             }
5866         }
5867         setPreferredSize( new Dimension( x, y ) );
5868     }
5869
5870     final void selectNode( final PhylogenyNode node ) {
5871         if ( ( getFoundNodes0() != null ) && getFoundNodes0().contains( node.getId() ) ) {
5872             getFoundNodes0().remove( node.getId() );
5873             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
5874             if ( getFoundNodes0().size() < 1 ) {
5875                 getControlPanel().searchReset0();
5876             }
5877         }
5878         else {
5879             getControlPanel().getSearchFoundCountsLabel0().setVisible( true );
5880             getControlPanel().getSearchResetButton0().setEnabled( true );
5881             getControlPanel().getSearchResetButton0().setVisible( true );
5882             if ( getFoundNodes0() == null ) {
5883                 setFoundNodes0( new HashSet<Long>() );
5884             }
5885             getFoundNodes0().add( node.getId() );
5886             getControlPanel().setSearchFoundCountsOnLabel0( getFoundNodes0().size() );
5887         }
5888     }
5889
5890     final void setArrowCursor() {
5891         setCursor( ARROW_CURSOR );
5892         repaint();
5893     }
5894
5895     final void setControlPanel( final ControlPanel atv_control ) {
5896         _control_panel = atv_control;
5897     }
5898
5899     void setCurrentExternalNodesDataBuffer( final StringBuilder sb ) {
5900         increaseCurrentExternalNodesDataBufferChangeCounter();
5901         _current_external_nodes_data_buffer = sb;
5902     }
5903
5904     final void setFoundNodes0( final Set<Long> found_nodes ) {
5905         _found_nodes_0 = found_nodes;
5906     }
5907
5908     final void setFoundNodes1( final Set<Long> found_nodes ) {
5909         _found_nodes_1 = found_nodes;
5910     }
5911
5912     final void setInOvRect( final boolean in_ov_rect ) {
5913         _in_ov_rect = in_ov_rect;
5914     }
5915
5916     final void setLargeFonts() {
5917         getTreeFontSet().largeFonts();
5918     }
5919
5920     final void setLastMouseDragPointX( final float x ) {
5921         _last_drag_point_x = x;
5922     }
5923
5924     final void setLastMouseDragPointY( final float y ) {
5925         _last_drag_point_y = y;
5926     }
5927
5928     final void setMediumFonts() {
5929         getTreeFontSet().mediumFonts();
5930     }
5931
5932     final void setNodeInPreorderToNull() {
5933         _nodes_in_preorder = null;
5934     }
5935
5936     final void setOvOn( final boolean ov_on ) {
5937         _ov_on = ov_on;
5938     }
5939
5940     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
5941         _graphics_type = graphics_type;
5942         setTextAntialias();
5943     }
5944
5945     final void setSmallFonts() {
5946         getTreeFontSet().smallFonts();
5947     }
5948
5949     final void setStartingAngle( final double starting_angle ) {
5950         _urt_starting_angle = starting_angle;
5951     }
5952
5953     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
5954         _statistics_for_vector_data = statistics_for_expression_values;
5955     }
5956
5957     final void setSuperTinyFonts() {
5958         getTreeFontSet().superTinyFonts();
5959     }
5960
5961     final void setTextAntialias() {
5962         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
5963             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
5964                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
5965             }
5966             else {
5967                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
5968             }
5969         }
5970         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
5971             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
5972             // try {
5973             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
5974             // }
5975             // catch ( final Throwable e ) {
5976             //    _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
5977             //}
5978         }
5979         else {
5980             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
5981             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
5982         }
5983     }
5984
5985     final void setTinyFonts() {
5986         getTreeFontSet().tinyFonts();
5987     }
5988
5989     final void setTreeFile( final File treefile ) {
5990         _treefile = treefile;
5991     }
5992
5993     final void setXcorrectionFactor( final float f ) {
5994         _x_correction_factor = f;
5995     }
5996
5997     final void setXdistance( final float x ) {
5998         _x_distance = x;
5999     }
6000
6001     final void setYdistance( final float y ) {
6002         _y_distance = y;
6003     }
6004
6005     final void sortDescendants( final PhylogenyNode node ) {
6006         if ( !node.isExternal() ) {
6007             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
6008             if ( getControlPanel().isShowTaxonomyScientificNames() || getControlPanel().isShowTaxonomyCode() ) {
6009                 pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
6010             }
6011             else if ( getControlPanel().isShowSeqNames() || getControlPanel().isShowSeqSymbols()
6012                     || getControlPanel().isShowGeneNames() ) {
6013                 pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
6014             }
6015             PhylogenyMethods.sortNodeDescendents( node, pri );
6016             setNodeInPreorderToNull();
6017             _phylogeny.externalNodesHaveChanged();
6018             _phylogeny.clearHashIdToNodeMap();
6019             _phylogeny.recalculateNumberOfExternalDescendants( true );
6020             resetNodeIdToDistToLeafMap();
6021             setEdited( true );
6022         }
6023         repaint();
6024     }
6025
6026     final void subTree( final PhylogenyNode node ) {
6027         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
6028             JOptionPane.showMessageDialog( this,
6029                                            "Cannot get a sub/super tree in unrooted display",
6030                                            "Attempt to get sub/super tree in unrooted display",
6031                                            JOptionPane.WARNING_MESSAGE );
6032             return;
6033         }
6034         if ( node.isExternal() ) {
6035             JOptionPane.showMessageDialog( this,
6036                                            "Cannot get a subtree of a external node",
6037                                            "Attempt to get subtree of external node",
6038                                            JOptionPane.WARNING_MESSAGE );
6039             return;
6040         }
6041         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
6042             JOptionPane.showMessageDialog( this,
6043                                            "Cannot get a subtree of the root node",
6044                                            "Attempt to get subtree of root node",
6045                                            JOptionPane.WARNING_MESSAGE );
6046             return;
6047         }
6048         setNodeInPreorderToNull();
6049         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
6050             _sub_phylogenies[ _subtree_index ] = _phylogeny;
6051             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
6052             ++_subtree_index;
6053             _phylogeny = TreePanelUtil.subTree( node, _phylogeny );
6054             updateSubSuperTreeButton();
6055         }
6056         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
6057             superTree();
6058         }
6059         _main_panel.getControlPanel().showWhole();
6060         repaint();
6061     }
6062
6063     final void superTree() {
6064         setNodeInPreorderToNull();
6065         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
6066         for( final PhylogenyNode n : temp_root.getDescendants() ) {
6067             n.setParent( temp_root );
6068         }
6069         _sub_phylogenies[ _subtree_index ] = null;
6070         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
6071         _phylogeny = _sub_phylogenies[ --_subtree_index ];
6072         updateSubSuperTreeButton();
6073     }
6074
6075     final void orderSubtree( final PhylogenyNode node ) {
6076         if ( node.isExternal() ) {
6077             return;
6078         }
6079         DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
6080         if ( getControlPanel().isShowTaxonomyScientificNames() || getControlPanel().isShowTaxonomyCode() ) {
6081             pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
6082         }
6083         else if ( getControlPanel().isShowSeqNames() || getControlPanel().isShowSeqSymbols()
6084                 || getControlPanel().isShowGeneNames() ) {
6085             pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
6086         }
6087         PhylogenyMethods.orderAppearanceX( node, true, pri );
6088         setNodeInPreorderToNull();
6089         getPhylogeny().externalNodesHaveChanged();
6090         getPhylogeny().clearHashIdToNodeMap();
6091         getPhylogeny().recalculateNumberOfExternalDescendants( true );
6092         resetNodeIdToDistToLeafMap();
6093         setEdited( true );
6094         getControlPanel().displayedPhylogenyMightHaveChanged( true );
6095         repaint();
6096     }
6097
6098     final void swap( final PhylogenyNode node ) {
6099         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
6100             return;
6101         }
6102         if ( node.getNumberOfDescendants() > 2 ) {
6103             JOptionPane.showMessageDialog( this,
6104                                            "Cannot swap descendants of nodes with more than 2 descendants",
6105                                            "Cannot swap descendants",
6106                                            JOptionPane.ERROR_MESSAGE );
6107             return;
6108         }
6109         if ( !node.isExternal() ) {
6110             node.swapChildren();
6111             setNodeInPreorderToNull();
6112             _phylogeny.externalNodesHaveChanged();
6113             _phylogeny.clearHashIdToNodeMap();
6114             _phylogeny.recalculateNumberOfExternalDescendants( true );
6115             resetNodeIdToDistToLeafMap();
6116             setEdited( true );
6117         }
6118         repaint();
6119     }
6120
6121     final void taxColor() {
6122         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
6123             return;
6124         }
6125         setWaitCursor();
6126         TreePanelUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
6127         _control_panel.setColorBranches( true );
6128         if ( _control_panel.getUseVisualStylesCb() != null ) {
6129             _control_panel.getUseVisualStylesCb().setSelected( true );
6130         }
6131         setEdited( true );
6132         setArrowCursor();
6133         repaint();
6134     }
6135
6136     final void updateOvSettings() {
6137         switch ( getOptions().getOvPlacement() ) {
6138             case LOWER_LEFT:
6139                 setOvXPosition( OV_BORDER );
6140                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
6141                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
6142                 break;
6143             case LOWER_RIGHT:
6144                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
6145                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
6146                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
6147                 break;
6148             case UPPER_RIGHT:
6149                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
6150                 setOvYPosition( OV_BORDER );
6151                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
6152                 break;
6153             default:
6154                 setOvXPosition( OV_BORDER );
6155                 setOvYPosition( OV_BORDER );
6156                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
6157                 break;
6158         }
6159     }
6160
6161     final void updateOvSizes() {
6162         if ( ( getWidth() > ( 1.05 * getVisibleRect().width ) )
6163                 || ( getHeight() > ( 1.05 * getVisibleRect().height ) ) ) {
6164             setOvOn( true );
6165             float l = getLongestExtNodeInfo();
6166             final float w_ratio = getOvMaxWidth() / getWidth();
6167             l *= w_ratio;
6168             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
6169             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
6170             float ov_xdist = 0;
6171             if ( !isNonLinedUpCladogram() ) {
6172                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
6173             }
6174             else {
6175                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
6176             }
6177             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
6178             if ( ov_xdist < 0.0 ) {
6179                 ov_xdist = 0.0f;
6180             }
6181             if ( ydist < 0.0 ) {
6182                 ydist = 0.0f;
6183             }
6184             setOvXDistance( ov_xdist );
6185             final double height = _phylogeny.calculateHeight( !_options.isCollapsedWithAverageHeigh() );
6186             if ( height > 0 ) {
6187                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
6188                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
6189             }
6190             else {
6191                 setOvXcorrectionFactor( 0 );
6192             }
6193         }
6194         else {
6195             setOvOn( false );
6196         }
6197     }
6198
6199     void updateSetOfCollapsedExternalNodes() {
6200         final Phylogeny phy = getPhylogeny();
6201         _collapsed_external_nodeid_set.clear();
6202         if ( phy != null ) {
6203             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
6204                 final PhylogenyNode ext_node = it.next();
6205                 PhylogenyNode n = ext_node;
6206                 while ( !n.isRoot() ) {
6207                     if ( n.isCollapse() ) {
6208                         _collapsed_external_nodeid_set.add( ext_node.getId() );
6209                         ext_node.setCollapse( true );
6210                         continue E;
6211                     }
6212                     n = n.getParent();
6213                 }
6214             }
6215         }
6216     }
6217
6218     final void updateSubSuperTreeButton() {
6219         if ( _subtree_index < 1 ) {
6220             getControlPanel().deactivateButtonToReturnToSuperTree();
6221         }
6222         else {
6223             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
6224         }
6225     }
6226
6227     final void zoomInDomainStructure() {
6228         if ( _domain_structure_width < 2000 ) {
6229             _domain_structure_width *= 1.2;
6230         }
6231     }
6232
6233     final void zoomOutDomainStructure() {
6234         if ( _domain_structure_width > 20 ) {
6235             _domain_structure_width *= 0.8;
6236         }
6237     }
6238
6239     private final static void colorizeNodesHelper( final Color c, final PhylogenyNode node ) {
6240         if ( node.getNodeData().getNodeVisualData() == null ) {
6241             node.getNodeData().setNodeVisualData( new NodeVisualData() );
6242         }
6243         node.getNodeData().getNodeVisualData().setFontColor( new Color( c.getRed(), c.getGreen(), c.getBlue() ) );
6244     }
6245
6246     final private static void drawString( final String str, final float x, final float y, final Graphics2D g ) {
6247         g.drawString( str, x, y );
6248     }
6249
6250     final private void drawStringX( final String str, final float x, final float y, final Graphics2D g ) {
6251         //TODO
6252         //FIXME
6253         if ( getAttributedStringMap() == null /*&& getAttributedStringMap().containsKey(str) */ ) {
6254             final AttributedString as = new AttributedString( str );
6255             //Font plainFont = new Font("Times New Roman", Font.PLAIN, 24);
6256             as.addAttribute( TextAttribute.FONT, g.getFont() );
6257             as.addAttribute( TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 1, 3 );
6258             as.addAttribute( TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, 3, 4 );
6259             as.addAttribute( TextAttribute.FOREGROUND, Color.BLUE, 1, 2 );
6260             as.addAttribute( TextAttribute.FOREGROUND, Color.PINK, 3, 5 );
6261             as.addAttribute( TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, 2, 4 );
6262             g.drawString( as.getIterator(), x, y );
6263         }
6264         else {
6265             g.drawString( str, x, y );
6266         }
6267     }
6268
6269     private final Map<String, AttributedString> getAttributedStringMap() {
6270         return _attributed_string_map;
6271     }
6272
6273     private final void setAttributedStringMap( final Map<String, AttributedString> attributed_string_map ) {
6274         _attributed_string_map = attributed_string_map;
6275     }
6276
6277     final private static boolean plusPressed( final int key_code ) {
6278         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
6279                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON )
6280                 || ( key_code == KeyEvent.VK_1 ) );
6281     }
6282
6283     public void decreaseDepthCollapseLevel() {
6284         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6285             if ( _depth_collapse_level <= 1 ) {
6286                 _depth_collapse_level = PhylogenyMethods.calculateMaxDepth( _phylogeny );
6287                 uncollapseAll();
6288             }
6289             else {
6290                 --_depth_collapse_level;
6291                 PhylogenyMethods.collapseToDepth( _phylogeny, _depth_collapse_level );
6292             }
6293             
6294         }
6295     }
6296
6297     public void increaseDepthCollapseLevel() {
6298         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6299             final int max = PhylogenyMethods.calculateMaxDepth( _phylogeny );
6300             if ( _depth_collapse_level >= max ) {
6301                 _depth_collapse_level = 1;
6302             }
6303             else {
6304                 ++_depth_collapse_level;
6305             }
6306             PhylogenyMethods.collapseToDepth( _phylogeny, _depth_collapse_level );
6307         }
6308     }
6309
6310     public void decreaseRankCollapseLevel() {
6311         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6312             final String ranks[] = PhylogenyMethods.obtainPresentRanksSorted( _phylogeny );
6313             if ( ranks.length > 1 ) {
6314                 if ( _rank_collapse_level <= 0 ) {
6315                     _rank_collapse_level = ranks.length - 1;
6316                     uncollapseAll();
6317                 }
6318                 else {
6319                     --_rank_collapse_level;
6320                     PhylogenyMethods.collapseToRank( _phylogeny, mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6321                 }
6322             }
6323         }
6324     }
6325
6326     
6327     public void increaseRankCollapseLevel() {
6328         if ( ( _phylogeny != null ) && ( _phylogeny.getNumberOfExternalNodes() > 2 ) ) {
6329             final String ranks[] = PhylogenyMethods.obtainPresentRanksSorted( _phylogeny );
6330             if ( ranks.length > 1 ) {
6331                 if ( _rank_collapse_level >= (ranks.length - 1 ) ) {
6332                     _rank_collapse_level = 0;
6333                     PhylogenyMethods.collapseToRank( _phylogeny,mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6334                 }
6335                 else if ( _rank_collapse_level == (ranks.length - 2 ) ) {
6336                     ++_rank_collapse_level;
6337                     uncollapseAll();
6338                 }
6339                 else {
6340                     ++_rank_collapse_level;
6341                     PhylogenyMethods.collapseToRank( _phylogeny,mapToAbsoluteRankLevel( ranks, _rank_collapse_level ) );
6342                 }
6343             }
6344         }
6345     }
6346
6347    
6348     private final static int mapToAbsoluteRankLevel( final String present_ranks_sorted[],
6349                                                      final int rank_collapse_level ) {
6350         final String rank_str = present_ranks_sorted[ rank_collapse_level ];
6351         if ( !TaxonomyUtil.RANK_TO_INT.containsKey( rank_str ) ) {
6352             throw new IllegalStateException( "unexpected exception: cannot find rank " + rank_str );
6353         }
6354         return TaxonomyUtil.RANK_TO_INT.get( rank_str );
6355     }
6356     
6357     private final void uncollapseAll() {
6358         final PhylogenyNodeIterator it = new PreorderTreeIterator(_phylogeny  );
6359         while ( it.hasNext() ) {
6360             it.next().setCollapse( false );
6361         }
6362     }
6363     
6364     final int resetDepthCollapseDepthValue() {
6365         return _depth_collapse_level = -1;
6366     }
6367
6368     final int getDepthCollapseDepthValue() {
6369         return _depth_collapse_level;
6370     }
6371
6372     final void setDepthCollapseDepthValue( final int depth_collapse_level ) {
6373         _depth_collapse_level = depth_collapse_level;
6374     }
6375     
6376     final int resetRankCollapseRankValue() {
6377         return _rank_collapse_level = -1;
6378     }
6379
6380     final int getRankCollapseRankValue() {
6381         return _rank_collapse_level;
6382     }
6383
6384     final void setRankCollapseRankValue( final int rank_collapse_level ) {
6385         _rank_collapse_level = rank_collapse_level;
6386     }
6387 }