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