JAL-2797 added the frame and listener interfaces
[jalview.git] / forester / java / src / org / forester / archaeopteryx / NodeEditPanel.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.Component;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.awt.event.KeyEvent;
32 import java.awt.event.KeyListener;
33 import java.math.BigDecimal;
34 import java.net.URL;
35 import java.text.ParseException;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40
41 import javax.swing.BoxLayout;
42 import javax.swing.JButton;
43 import javax.swing.JEditorPane;
44 import javax.swing.JOptionPane;
45 import javax.swing.JPanel;
46 import javax.swing.JScrollPane;
47 import javax.swing.JTree;
48 import javax.swing.event.TreeSelectionEvent;
49 import javax.swing.event.TreeSelectionListener;
50 import javax.swing.text.Position;
51 import javax.swing.tree.DefaultMutableTreeNode;
52 import javax.swing.tree.TreePath;
53 import javax.swing.tree.TreeSelectionModel;
54
55 import org.forester.archaeopteryx.tools.ImageLoader;
56 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
57 import org.forester.phylogeny.PhylogenyNode;
58 import org.forester.phylogeny.data.Accession;
59 import org.forester.phylogeny.data.BranchWidth;
60 import org.forester.phylogeny.data.Confidence;
61 import org.forester.phylogeny.data.Date;
62 import org.forester.phylogeny.data.Distribution;
63 import org.forester.phylogeny.data.Event;
64 import org.forester.phylogeny.data.Identifier;
65 import org.forester.phylogeny.data.MultipleUris;
66 import org.forester.phylogeny.data.PhylogenyData;
67 import org.forester.phylogeny.data.PhylogenyDataUtil;
68 import org.forester.phylogeny.data.Point;
69 import org.forester.phylogeny.data.Reference;
70 import org.forester.phylogeny.data.Sequence;
71 import org.forester.phylogeny.data.Taxonomy;
72 import org.forester.phylogeny.data.Uri;
73 import org.forester.util.FailedConditionCheckException;
74 import org.forester.util.ForesterUtil;
75
76 class NodeEditPanel extends JPanel {
77
78     private enum PHYLOXML_TAG {
79         NODE_NAME,
80         NODE_BRANCH_LENGTH,
81         NODE_BRANCH_WIDTH,
82         TAXONOMY_CODE,
83         TAXONOMY_SCIENTIFIC_NAME,
84         TAXONOMY_AUTHORITY,
85         TAXONOMY_COMMON_NAME,
86         TAXONOMY_SYNONYM,
87         TAXONOMY_RANK,
88         TAXONOMY_URI,
89         SEQ_SYMBOL,
90         SEQ_NAME,
91         SEQ_GENE_NAME,
92         SEQ_LOCATION,
93         SEQ_TYPE,
94         SEQ_MOL_SEQ,
95         SEQ_URI,
96         DATE_DESCRIPTION,
97         DATE_VALUE,
98         DATE_MIN,
99         DATE_MAX,
100         DATE_UNIT,
101         TAXONOMY_ID_VALUE,
102         TAXONOMY_ID_PROVIDER,
103         SEQ_ACC_VALUE,
104         SEQ_ACC_SOURCE,
105         CONFIDENCE_VALUE,
106         CONFIDENCE_TYPE,
107         LIT_REFERENCE_DESC,
108         LIT_REFERENCE_DOI,
109         EVENTS_DUPLICATIONS,
110         EVENTS_SPECIATIONS,
111         EVENTS_GENE_LOSSES,
112         DIST_DESC,
113         DIST_GEODETIC,
114         DIST_LAT,
115         DIST_LONG,
116         DIST_ALT,
117         DIST_ALT_UNIT
118     }
119
120     private class TagNumber {
121
122         final private PHYLOXML_TAG _tag;
123         final private int          _number;
124
125         TagNumber( final PHYLOXML_TAG tag, final int number ) {
126             _tag = tag;
127             _number = number;
128         }
129
130         @Override
131         public String toString() {
132             return getTag() + "_" + getNumber();
133         }
134
135         int getNumber() {
136             return _number;
137         }
138
139         PHYLOXML_TAG getTag() {
140             return _tag;
141         }
142     }
143     private static final long                            serialVersionUID = 5120159904388100771L;
144     private final JTree                                  _tree;
145     private final JEditorPane                            _pane;
146     private final PhylogenyNode                          _my_node;
147     private final TreePanel                              _tree_panel;
148     private final Map<DefaultMutableTreeNode, TagNumber> _map;
149
150     public NodeEditPanel( final PhylogenyNode phylogeny_node,
151                           final TreePanel tree_panel,
152                           final NodeFrame parent ) {
153         _map = new HashMap<DefaultMutableTreeNode, TagNumber>();
154         _my_node = phylogeny_node;
155         _tree_panel = tree_panel;
156         String node_name = "";
157         if ( !ForesterUtil.isEmpty( phylogeny_node.getName() ) ) {
158             node_name = phylogeny_node.getName() + " ";
159         }
160         final DefaultMutableTreeNode top = new DefaultMutableTreeNode( "Node " + node_name );
161         createNodes( top, phylogeny_node );
162         _tree = new JTree( top );
163         getJTree().setEditable( true );
164         getJTree().setFocusable( true );
165         getJTree().setToggleClickCount( 1 );
166         getJTree().setInvokesStopCellEditing( true );
167         final JScrollPane tree_view = new JScrollPane( getJTree() );
168         
169         final JButton close_button = new JButton( "Close" );
170         close_button.setToolTipText( "This only closes this window; to write values back to the phylogeny, press ENTER after editing a field." );
171         close_button.setEnabled( true );
172         
173         close_button.addActionListener( new ActionListener() {
174             public void actionPerformed( final ActionEvent e ) {
175                 writeAll();
176                 parent.close();
177            }
178         } );
179         
180         _pane = new JEditorPane();
181         _pane.setEditable( true );
182         tree_view.setMinimumSize( AptxConstants.NODE_PANEL_SPLIT_MINIMUM_SIZE );
183         tree_view.setPreferredSize( AptxConstants.NODE_PANEL_SIZE );
184         
185         close_button.setAlignmentX( Component.CENTER_ALIGNMENT );
186         tree_view.setAlignmentX( Component.CENTER_ALIGNMENT );
187         
188         final JPanel panel = new JPanel();
189         panel.setLayout( new BoxLayout( panel, BoxLayout.Y_AXIS ) );
190         panel.add( tree_view );
191         panel.add( close_button );
192         add( panel );
193         
194         getJTree().getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION );
195         getJTree().addKeyListener( new KeyListener() {
196
197             @Override
198             public void keyPressed( final KeyEvent e ) {
199                 keyEvent( e );
200             }
201
202             @Override
203             public void keyReleased( final KeyEvent e ) {
204                 keyEvent( e );
205             }
206
207             @Override
208             public void keyTyped( final KeyEvent e ) {
209                 keyEvent( e );
210             }
211         } );
212         for( int i = 0; i < getJTree().getRowCount(); i++ ) {
213             getJTree().expandRow( i );
214         }
215         collapsePath( NodePanel.BASIC );
216         collapsePath( NodePanel.TAXONOMY );
217         collapsePath( NodePanel.SEQUENCE );
218         collapsePath( NodePanel.EVENTS );
219         collapsePath( NodePanel.DATE );
220         collapsePath( NodePanel.DISTRIBUTION );
221         collapsePath( NodePanel.LIT_REFERENCE );
222         getJTree().addTreeSelectionListener( new TreeSelectionListener() {
223
224             @Override
225             public void valueChanged( final TreeSelectionEvent e ) {
226                 final TreePath new_path = e.getNewLeadSelectionPath();
227                 final TreePath old_path = e.getOldLeadSelectionPath();
228                 if ( new_path != null ) {
229                     writeBack( ( DefaultMutableTreeNode ) new_path.getLastPathComponent() );
230                 }
231                 if ( old_path != null ) {
232                     writeBack( ( DefaultMutableTreeNode ) old_path.getLastPathComponent() );
233                 }
234                 AptxUtil.lookAtRealBranchLengthsForAptxControlSettings( tree_panel.getPhylogeny(),
235                                                                         tree_panel.getControlPanel() );
236                 getTreePanel().repaint();
237             }
238         } );
239         
240         
241        
242     }
243
244     private void addBasics( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node, final String name ) {
245         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
246         top.add( category );
247         addSubelementEditable( category, NodePanel.NODE_NAME, phylogeny_node.getName(), PHYLOXML_TAG.NODE_NAME );
248         String bl = "";
249         if ( phylogeny_node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) {
250             bl = ForesterUtil.FORMATTER_6.format( phylogeny_node.getDistanceToParent() );
251         }
252         addSubelementEditable( category, NodePanel.NODE_BRANCH_LENGTH, bl, PHYLOXML_TAG.NODE_BRANCH_LENGTH );
253         int counter = 0;
254         if ( phylogeny_node.getBranchData().isHasConfidences() ) {
255             for( int i = phylogeny_node.getBranchData().getConfidences().size() - 1; i >= 0; i-- ) {
256                 if ( phylogeny_node.getBranchData().getConfidences().get( i ).getValue() == Confidence.CONFIDENCE_DEFAULT_VALUE ) {
257                     phylogeny_node.getBranchData().getConfidences().remove( i );
258                 }
259             }
260             for( final PhylogenyData conf : phylogeny_node.getBranchData().getConfidences() ) {
261                 final Confidence my_conf = ( Confidence ) ( conf );
262                 addSubelementEditable( category,
263                                        NodePanel.CONFIDENCE + " [" + counter + "]",
264                                        ForesterUtil.FORMATTER_6.format( my_conf.getValue() ),
265                                        PHYLOXML_TAG.CONFIDENCE_VALUE,
266                                        NodePanel.CONFIDENCE_TYPE,
267                                        my_conf.getType(),
268                                        PHYLOXML_TAG.CONFIDENCE_TYPE,
269                                        counter++ );
270             }
271         }
272         addSubelementEditable( category,
273                                NodePanel.CONFIDENCE + " [" + counter + "]",
274                                "",
275                                PHYLOXML_TAG.CONFIDENCE_VALUE,
276                                NodePanel.CONFIDENCE_TYPE,
277                                "",
278                                PHYLOXML_TAG.CONFIDENCE_TYPE,
279                                counter );
280         String bw = "1";
281         if ( ( phylogeny_node.getBranchData().getBranchWidth() != null )
282                 && ( phylogeny_node.getBranchData().getBranchWidth().getValue() != BranchWidth.BRANCH_WIDTH_DEFAULT_VALUE ) ) {
283             bw = ForesterUtil.FORMATTER_3.format( phylogeny_node.getBranchData().getBranchWidth().getValue() );
284         }
285         addSubelementEditable( category, NodePanel.NODE_BRANCH_WIDTH, bw, PHYLOXML_TAG.NODE_BRANCH_WIDTH );
286     }
287
288     //    private void addAnnotation( final DefaultMutableTreeNode top, final Annotation ann, final String name ) {
289     //        DefaultMutableTreeNode category;
290     //        category = new DefaultMutableTreeNode( name );
291     //        top.add( category );
292     //        addSubelementEditable( category, "Reference", ann.getRef() , PHYLOXML_TAG.);
293     //        addSubelementEditable( category, "Description", ann.getDesc() , PHYLOXML_TAG.);
294     //        addSubelementEditable( category, "Source", ann.getSource(), PHYLOXML_TAG. );
295     //        addSubelementEditable( category, "Type", ann.getType(), PHYLOXML_TAG. );
296     //        addSubelementEditable( category, "Evidence", ann.getEvidence() , PHYLOXML_TAG.);
297     //        if ( ann.getConfidence() != null ) {
298     //            addSubelementEditable( category, "Confidence", ann.getConfidence().asText().toString() , PHYLOXML_TAG.);
299     //        }
300     //        if ( ann.getProperties() != null ) {
301     //            addProperties( category, ann.getProperties(), "Properties", PHYLOXML_TAG. );
302     //        }
303     //    }
304     //    private void addAnnotations( final DefaultMutableTreeNode top,
305     //                                 final List<PhylogenyData> annotations,
306     //                                 final DefaultMutableTreeNode category ) {
307     //        if ( ( annotations != null ) && ( annotations.size() > 0 ) ) {
308     //            category.add( new DefaultMutableTreeNode( "Annotations" ) );
309     //            final DefaultMutableTreeNode last = top.getLastLeaf();
310     //            int i = 0;
311     //            for( final PhylogenyData ann : annotations ) {
312     //                addAnnotation( last, ( Annotation ) ann, "Annotation " + ( i++ ) );
313     //            }
314     //        }
315     //    }
316     private void addDate( final DefaultMutableTreeNode top, Date date, final String name ) {
317         if ( date == null ) {
318             date = new Date();
319         }
320         DefaultMutableTreeNode category;
321         category = new DefaultMutableTreeNode( name );
322         top.add( category );
323         addSubelementEditable( category, NodePanel.DATE_DESCRIPTION, date.getDesc(), PHYLOXML_TAG.DATE_DESCRIPTION );
324         addSubelementEditable( category,
325                                NodePanel.DATE_VALUE,
326                                String.valueOf( date.getValue() != null ? date.getValue() : "" ),
327                                PHYLOXML_TAG.DATE_VALUE );
328         addSubelementEditable( category,
329                                NodePanel.DATE_MIN,
330                                String.valueOf( date.getMin() != null ? date.getMin() : "" ),
331                                PHYLOXML_TAG.DATE_MIN );
332         addSubelementEditable( category,
333                                NodePanel.DATE_MAX,
334                                String.valueOf( date.getMax() != null ? date.getMax() : "" ),
335                                PHYLOXML_TAG.DATE_MAX );
336         addSubelementEditable( category, NodePanel.DATE_UNIT, date.getUnit(), PHYLOXML_TAG.DATE_UNIT );
337     }
338
339     private void addDistribution( final DefaultMutableTreeNode top, Distribution dist, final String name ) {
340         if ( dist == null ) {
341             dist = new Distribution( "" );
342         }
343         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
344         top.add( category );
345         Point p0 = null;
346         if ( ( dist.getPoints() != null ) && ( dist.getPoints().size() > 0 ) ) {
347             p0 = dist.getPoints().get( 0 );
348         }
349         else {
350             p0 = new Point();
351         }
352         addSubelementEditable( category, NodePanel.DIST_DESCRIPTION, dist.getDesc(), PHYLOXML_TAG.DIST_DESC );
353         addSubelementEditable( category,
354                                NodePanel.DIST_GEODETIC_DATUM,
355                                p0.getGeodeticDatum(),
356                                PHYLOXML_TAG.DIST_GEODETIC );
357         addSubelementEditable( category,
358                                NodePanel.DIST_LATITUDE,
359                                String.valueOf( p0.getLatitude() != null ? p0.getLatitude() : "" ),
360                                PHYLOXML_TAG.DIST_LAT );
361         addSubelementEditable( category,
362                                NodePanel.DIST_LONGITUDE,
363                                String.valueOf( p0.getLongitude() != null ? p0.getLongitude() : "" ),
364                                PHYLOXML_TAG.DIST_LONG );
365         addSubelementEditable( category,
366                                NodePanel.DIST_ALTITUDE,
367                                String.valueOf( p0.getAltitude() != null ? p0.getAltitude() : "" ),
368                                PHYLOXML_TAG.DIST_ALT );
369         addSubelementEditable( category,
370                                NodePanel.DIST_ALT_UNIT,
371                                String.valueOf( p0.getAltiudeUnit() != null ? p0.getAltiudeUnit() : "" ),
372                                PHYLOXML_TAG.DIST_ALT_UNIT );
373     }
374
375     private void addEvents( final DefaultMutableTreeNode top, Event events, final String name ) {
376         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
377         if ( events == null ) {
378             events = new Event();
379         }
380         top.add( category );
381         addSubelementEditable( category,
382                                NodePanel.EVENTS_DUPLICATIONS,
383                                String.valueOf( events.getNumberOfDuplications() >= 0 ? events.getNumberOfDuplications()
384                                        : 0 ),
385                                PHYLOXML_TAG.EVENTS_DUPLICATIONS );
386         addSubelementEditable( category,
387                                NodePanel.EVENTS_SPECIATIONS,
388                                String.valueOf( events.getNumberOfSpeciations() >= 0 ? events.getNumberOfSpeciations()
389                                        : 0 ),
390                                PHYLOXML_TAG.EVENTS_SPECIATIONS );
391         addSubelementEditable( category,
392                                NodePanel.EVENTS_GENE_LOSSES,
393                                String.valueOf( events.getNumberOfGeneLosses() >= 0 ? events.getNumberOfGeneLosses() : 0 ),
394                                PHYLOXML_TAG.EVENTS_GENE_LOSSES );
395     }
396
397     private void addMapping( final DefaultMutableTreeNode mtn, final TagNumber tag ) {
398         if ( getMap().containsKey( mtn ) ) {
399             throw new IllegalArgumentException( "key " + mtn + " already present" );
400         }
401         if ( getMap().containsValue( tag ) ) {
402             throw new IllegalArgumentException( "value " + tag + " already present" );
403         }
404         getMap().put( mtn, tag );
405     }
406
407     private void addReference( final DefaultMutableTreeNode top, Reference ref, final String name ) {
408         if ( ref == null ) {
409             ref = new Reference( "" );
410         }
411         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
412         top.add( category );
413         addSubelementEditable( category,
414                                NodePanel.LIT_REFERENCE_DESC,
415                                ref.getDescription(),
416                                PHYLOXML_TAG.LIT_REFERENCE_DESC );
417         addSubelementEditable( category, NodePanel.LIT_REFERENCE_DOI, ref.getDoi(), PHYLOXML_TAG.LIT_REFERENCE_DOI );
418     }
419
420     private void addSequence( final DefaultMutableTreeNode top, Sequence seq, final String name ) {
421         if ( seq == null ) {
422             seq = new Sequence();
423         }
424         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
425         top.add( category );
426         Accession acc = seq.getAccession();
427         if ( acc == null ) {
428             acc = new Accession( "", "" );
429         }
430         addSubelementEditable( category, NodePanel.SEQ_NAME, seq.getName(), PHYLOXML_TAG.SEQ_NAME );
431         addSubelementEditable( category, NodePanel.SEQ_SYMBOL, seq.getSymbol(), PHYLOXML_TAG.SEQ_SYMBOL );
432         addSubelementEditable( category, NodePanel.SEQ_GENE_NAME, seq.getGeneName(), PHYLOXML_TAG.SEQ_GENE_NAME );
433         addSubelementEditable( category,
434                                NodePanel.SEQ_ACCESSION,
435                                acc.getValue(),
436                                PHYLOXML_TAG.SEQ_ACC_VALUE,
437                                "Source",
438                                acc.getSource(),
439                                PHYLOXML_TAG.SEQ_ACC_SOURCE );
440         addSubelementEditable( category, NodePanel.SEQ_LOCATION, seq.getLocation(), PHYLOXML_TAG.SEQ_LOCATION );
441         addSubelementEditable( category, NodePanel.SEQ_TYPE, seq.getType(), PHYLOXML_TAG.SEQ_TYPE );
442         addSubelementEditable( category, NodePanel.SEQ_MOL_SEQ, seq.getMolecularSequence(), PHYLOXML_TAG.SEQ_MOL_SEQ );
443         int uri_counter = 0;
444         if ( seq.getUris() != null ) {
445             for( final Uri uri : seq.getUris() ) {
446                 if ( uri != null ) {
447                     addSubelementEditable( category, NodePanel.SEQ_URI + " [" + uri_counter + "]", uri.getValue()
448                             .toString(), PHYLOXML_TAG.SEQ_URI, uri_counter++ );
449                 }
450             }
451         }
452         addSubelementEditable( category,
453                                NodePanel.SEQ_URI + " [" + uri_counter + "]",
454                                "",
455                                PHYLOXML_TAG.SEQ_URI,
456                                uri_counter );
457         //  addAnnotations( top, seq.getAnnotations(), category );
458     }
459
460     private void addSubelementEditable( final DefaultMutableTreeNode node,
461                                         final String name,
462                                         final String value,
463                                         final PHYLOXML_TAG phyloxml_tag ) {
464         addSubelementEditable( node, name, value, phyloxml_tag, 0 );
465     }
466
467     private void addSubelementEditable( final DefaultMutableTreeNode node,
468                                         final String name,
469                                         final String value,
470                                         final PHYLOXML_TAG phyloxml_tag,
471                                         final int number ) {
472         String my_value = value;
473         if ( ForesterUtil.isEmpty( my_value ) ) {
474             my_value = "";
475         }
476         final DefaultMutableTreeNode name_node = new DefaultMutableTreeNode( name );
477         final DefaultMutableTreeNode value_node = new DefaultMutableTreeNode( my_value );
478         name_node.add( value_node );
479         node.add( name_node );
480         addMapping( name_node, new TagNumber( phyloxml_tag, number ) );
481     }
482
483     private void addSubelementEditable( final DefaultMutableTreeNode node,
484                                         final String name,
485                                         final String value,
486                                         final PHYLOXML_TAG phyloxml_value_tag,
487                                         final String source_name,
488                                         final String source_value,
489                                         final PHYLOXML_TAG phyloxml_source_tag ) {
490         addSubelementEditable( node, name, value, phyloxml_value_tag, source_name, source_value, phyloxml_source_tag, 0 );
491     }
492
493     private void addSubelementEditable( final DefaultMutableTreeNode node,
494                                         final String name,
495                                         final String value,
496                                         final PHYLOXML_TAG phyloxml_value_tag,
497                                         final String source_name,
498                                         final String source_value,
499                                         final PHYLOXML_TAG phyloxml_source_tag,
500                                         final int number ) {
501         String my_value = value;
502         if ( ForesterUtil.isEmpty( my_value ) ) {
503             my_value = "";
504         }
505         String my_source_value = source_value;
506         if ( ForesterUtil.isEmpty( my_source_value ) ) {
507             my_source_value = "";
508         }
509         final DefaultMutableTreeNode name_node = new DefaultMutableTreeNode( name );
510         final DefaultMutableTreeNode source_name_node = new DefaultMutableTreeNode( source_name );
511         final DefaultMutableTreeNode source_value_node = new DefaultMutableTreeNode( my_source_value );
512         final DefaultMutableTreeNode value_node = new DefaultMutableTreeNode( my_value );
513         name_node.add( source_name_node );
514         source_name_node.add( source_value_node );
515         name_node.add( value_node );
516         node.add( name_node );
517         addMapping( name_node, new TagNumber( phyloxml_value_tag, number ) );
518         addMapping( source_name_node, new TagNumber( phyloxml_source_tag, number ) );
519     }
520
521     private void addTaxonomy( final DefaultMutableTreeNode top, Taxonomy tax, final String name ) {
522         if ( tax == null ) {
523             tax = new Taxonomy();
524         }
525         final DefaultMutableTreeNode category = new DefaultMutableTreeNode( name );
526         top.add( category );
527         Identifier id = tax.getIdentifier();
528         if ( id == null ) {
529             id = new Identifier();
530         }
531         addSubelementEditable( category,
532                                NodePanel.TAXONOMY_IDENTIFIER,
533                                id.getValue(),
534                                PHYLOXML_TAG.TAXONOMY_ID_VALUE,
535                                "Provider",
536                                id.getProvider(),
537                                PHYLOXML_TAG.TAXONOMY_ID_PROVIDER );
538         addSubelementEditable( category, NodePanel.TAXONOMY_CODE, tax.getTaxonomyCode(), PHYLOXML_TAG.TAXONOMY_CODE );
539         addSubelementEditable( category,
540                                NodePanel.TAXONOMY_SCIENTIFIC_NAME,
541                                tax.getScientificName(),
542                                PHYLOXML_TAG.TAXONOMY_SCIENTIFIC_NAME );
543         addSubelementEditable( category,
544                                NodePanel.TAXONOMY_AUTHORITY,
545                                tax.getAuthority(),
546                                PHYLOXML_TAG.TAXONOMY_AUTHORITY );
547         addSubelementEditable( category,
548                                NodePanel.TAXONOMY_COMMON_NAME,
549                                tax.getCommonName(),
550                                PHYLOXML_TAG.TAXONOMY_COMMON_NAME );
551         for( int i = tax.getSynonyms().size() - 1; i >= 0; i-- ) {
552             if ( ForesterUtil.isEmpty( tax.getSynonyms().get( i ) ) ) {
553                 tax.getSynonyms().remove( i );
554             }
555         }
556         int syn_counter = 0;
557         for( final String syn : tax.getSynonyms() ) {
558             addSubelementEditable( category,
559                                    NodePanel.TAXONOMY_SYNONYM + " [" + syn_counter + "]",
560                                    syn,
561                                    PHYLOXML_TAG.TAXONOMY_SYNONYM,
562                                    syn_counter++ );
563         }
564         addSubelementEditable( category,
565                                NodePanel.TAXONOMY_SYNONYM + " [" + syn_counter + "]",
566                                "",
567                                PHYLOXML_TAG.TAXONOMY_SYNONYM,
568                                syn_counter );
569         addSubelementEditable( category, NodePanel.TAXONOMY_RANK, tax.getRank(), PHYLOXML_TAG.TAXONOMY_RANK );
570         int uri_counter = 0;
571         if ( tax.getUris() != null ) {
572             for( final Uri uri : tax.getUris() ) {
573                 if ( uri != null ) {
574                     addSubelementEditable( category, NodePanel.TAXONOMY_URI + " [" + uri_counter + "]", uri.getValue()
575                             .toString(), PHYLOXML_TAG.TAXONOMY_URI, uri_counter++ );
576                 }
577             }
578         }
579         addSubelementEditable( category,
580                                NodePanel.TAXONOMY_URI + " [" + uri_counter + "]",
581                                "",
582                                PHYLOXML_TAG.TAXONOMY_URI,
583                                uri_counter );
584     }
585
586     private void addUri( final DefaultMutableTreeNode mtn, final Uri uri, final int number, final MultipleUris mu ) {
587         if ( uri != null ) {
588             if ( mu.getUris() == null ) {
589                 mu.setUris( new ArrayList<Uri>() );
590             }
591         }
592         if ( ( uri != null ) && ( mu.getUris() == null ) ) {
593             mu.setUris( new ArrayList<Uri>() );
594         }
595         if ( ( uri != null ) && ( mu.getUris().size() == number ) ) {
596             mu.getUris().add( uri );
597         }
598         if ( ( mu.getUris() != null ) && ( mu.getUris().size() != number ) ) {
599             mu.getUris().set( number, uri );
600         }
601         final ImageLoader il = new ImageLoader( getTreePanel() );
602         new Thread( il ).start();
603     }
604
605     private void collapsePath( final String name ) {
606         final TreePath tp = getJTree().getNextMatch( name, 0, Position.Bias.Forward );
607         if ( tp != null ) {
608             getJTree().collapsePath( tp );
609         }
610     }
611
612     private void createNodes( final DefaultMutableTreeNode top, final PhylogenyNode phylogeny_node ) {
613         if ( !phylogeny_node.getNodeData().isHasTaxonomy() ) {
614             phylogeny_node.getNodeData().addTaxonomy( new Taxonomy() );
615         }
616         if ( !phylogeny_node.getNodeData().isHasSequence() ) {
617             phylogeny_node.getNodeData().addSequence( new Sequence() );
618         }
619         if ( !phylogeny_node.getNodeData().isHasDistribution() ) {
620             phylogeny_node.getNodeData().addDistribution( new Distribution( "" ) );
621         }
622         if ( !phylogeny_node.getNodeData().isHasReference() ) {
623             phylogeny_node.getNodeData().addReference( new Reference( "" ) );
624         }
625         addBasics( top, phylogeny_node, NodePanel.BASIC );
626         addTaxonomy( top, phylogeny_node.getNodeData().getTaxonomy(), NodePanel.TAXONOMY );
627         addSequence( top, phylogeny_node.getNodeData().getSequence(), NodePanel.SEQUENCE );
628         if ( !phylogeny_node.isExternal() ) {
629             addEvents( top, phylogeny_node.getNodeData().getEvent(), NodePanel.EVENTS );
630         }
631         addDate( top, phylogeny_node.getNodeData().getDate(), NodePanel.DATE );
632         addDistribution( top, phylogeny_node.getNodeData().getDistribution(), NodePanel.DISTRIBUTION );
633         addReference( top, phylogeny_node.getNodeData().getReference(), NodePanel.LIT_REFERENCE );
634         //  addProperties( top, phylogeny_node.getNodeData().getProperties(), "Properties" );
635     }
636
637     private void formatError( final DefaultMutableTreeNode mtn, final PhyloXmlDataFormatException e ) {
638         JOptionPane.showMessageDialog( this, e.getMessage(), "Format error", JOptionPane.ERROR_MESSAGE );
639         mtn.setUserObject( "" );
640         getJTree().repaint();
641     }
642
643     private JTree getJTree() {
644         return _tree;
645     }
646
647     private Map<DefaultMutableTreeNode, TagNumber> getMap() {
648         return _map;
649     }
650
651     private TagNumber getMapping( final DefaultMutableTreeNode mtn ) {
652         return getMap().get( mtn );
653     }
654
655     private DefaultMutableTreeNode getSelectedTreeNode() {
656         final TreePath selectionPath = getJTree().getSelectionPath();
657         if ( selectionPath != null ) {
658             final Object[] path = selectionPath.getPath();
659             if ( path.length > 0 ) {
660                 return ( DefaultMutableTreeNode ) path[ path.length - 1 ]; // Last node
661             }
662         }
663         return null;
664     }
665
666     private TreePanel getTreePanel() {
667         return _tree_panel;
668     }
669
670     private void keyEvent( final KeyEvent e ) {
671         if ( e.getKeyCode() == KeyEvent.VK_ENTER ) {
672             writeBack( getSelectedTreeNode() );
673         }
674     }
675
676     private List<Point> obtainPoints() {
677         ForesterUtil.ensurePresenceOfDistribution( getMyNode() );
678         Distribution d = getMyNode().getNodeData().getDistribution();
679         if ( d.getPoints() == null ) {
680             d = new Distribution( d.getDesc(), new ArrayList<Point>(), d.getPolygons() );
681             getMyNode().getNodeData().setDistribution( d );
682         }
683         final List<Point> ps = d.getPoints();
684         if ( ps.isEmpty() ) {
685             ps.add( new Point() );
686         }
687         else if ( ps.get( 0 ) == null ) {
688             ps.set( 0, new Point() );
689         }
690         return ps;
691     }
692
693     private BigDecimal parseBigDecimal( final DefaultMutableTreeNode mtn, final String value ) {
694         if ( ForesterUtil.isEmpty( value ) ) {
695             return new BigDecimal( 0 );
696         }
697         BigDecimal i = null;
698         try {
699             i = new BigDecimal( value );
700         }
701         catch ( final NumberFormatException e ) {
702             JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
703             mtn.setUserObject( "" );
704         }
705         return i;
706     }
707
708     private int parsePositiveInt( final DefaultMutableTreeNode mtn, final String value ) {
709         if ( ForesterUtil.isEmpty( value ) ) {
710             return 0;
711         }
712         int i = -1;
713         try {
714             i = ForesterUtil.parseInt( value );
715         }
716         catch ( final ParseException e ) {
717             JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
718             mtn.setUserObject( "" );
719         }
720         if ( i < 0 ) {
721             JOptionPane.showMessageDialog( this, "illegal value: " + value, "Error", JOptionPane.ERROR_MESSAGE );
722             mtn.setUserObject( "" );
723         }
724         return i;
725     }
726
727     private void writeBack( final DefaultMutableTreeNode mtn ) {
728         if ( !getMap().containsKey( mtn ) ) {
729             final DefaultMutableTreeNode parent = ( DefaultMutableTreeNode ) mtn.getParent();
730             if ( getMap().containsKey( parent ) ) {
731                 writeBack( mtn, getMapping( parent ) );
732             }
733         }
734     }
735
736     private void writeBack( final DefaultMutableTreeNode mtn, final TagNumber tag_number ) {
737         if ( tag_number == null ) {
738             return;
739         }
740         String value = mtn.toString();
741         if ( value == null ) {
742             value = "";
743         }
744         value = value.replaceAll( "\\s+", " " );
745         value = value.trim();
746         mtn.setUserObject( value );
747         getJTree().repaint();
748         final PHYLOXML_TAG tag = tag_number.getTag();
749         final int number = tag_number.getNumber();
750         switch ( tag ) {
751             case NODE_NAME:
752                 getMyNode().setName( value );
753                 break;
754             case NODE_BRANCH_LENGTH:
755                 if ( ForesterUtil.isEmpty( value ) ) {
756                     getMyNode().setDistanceToParent( PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT );
757                 }
758                 else {
759                     try {
760                         getMyNode().setDistanceToParent( ForesterUtil.parseDouble( value ) );
761                     }
762                     catch ( final ParseException e ) {
763                         JOptionPane.showMessageDialog( this,
764                                                        "failed to parse branch length from: " + value,
765                                                        "Error",
766                                                        JOptionPane.ERROR_MESSAGE );
767                         mtn.setUserObject( "" );
768                     }
769                 }
770                 break;
771             case NODE_BRANCH_WIDTH:
772                 if ( ForesterUtil.isEmpty( value ) || value.equals( "1" ) ) {
773                     if ( getMyNode().getBranchData().getBranchWidth() != null ) {
774                         getMyNode().getBranchData().setBranchWidth( new BranchWidth() );
775                     }
776                 }
777                 else {
778                     try {
779                         final double bw = ForesterUtil.parseDouble( value );
780                         if ( bw >= 0 ) {
781                             getMyNode().getBranchData().setBranchWidth( new BranchWidth( bw ) );
782                         }
783                     }
784                     catch ( final ParseException e ) {
785                         JOptionPane.showMessageDialog( this,
786                                                        "failed to parse branch width from: " + value,
787                                                        "Error",
788                                                        JOptionPane.ERROR_MESSAGE );
789                         mtn.setUserObject( "" );
790                     }
791                 }
792                 break;
793             case CONFIDENCE_VALUE:
794                 double confidence = Confidence.CONFIDENCE_DEFAULT_VALUE;
795                 if ( !ForesterUtil.isEmpty( value ) ) {
796                     try {
797                         confidence = ForesterUtil.parseDouble( value );
798                     }
799                     catch ( final ParseException e ) {
800                         JOptionPane.showMessageDialog( this,
801                                                        "failed to parse confidence value from: " + value,
802                                                        "Error",
803                                                        JOptionPane.ERROR_MESSAGE );
804                         mtn.setUserObject( "" );
805                         break;
806                     }
807                 }
808                 if ( getMyNode().getBranchData().getConfidences().size() < number ) {
809                     throw new FailedConditionCheckException();
810                 }
811                 else if ( getMyNode().getBranchData().getConfidences().size() == number ) {
812                     if ( confidence >= 0 ) {
813                         getMyNode().getBranchData().getConfidences().add( new Confidence( confidence, "unknown" ) );
814                     }
815                 }
816                 else {
817                     final String type = getMyNode().getBranchData().getConfidences().get( number ).getType();
818                     final double sd = getMyNode().getBranchData().getConfidences().get( number ).getStandardDeviation();
819                     getMyNode().getBranchData().getConfidences().set( number, new Confidence( confidence, type, sd ) );
820                 }
821                 break;
822             case CONFIDENCE_TYPE:
823                 if ( getMyNode().getBranchData().getConfidences().size() < number ) {
824                     throw new FailedConditionCheckException();
825                 }
826                 else if ( getMyNode().getBranchData().getConfidences().size() == number ) {
827                     if ( !ForesterUtil.isEmpty( value ) ) {
828                         getMyNode().getBranchData().getConfidences().add( new Confidence( 0, value ) );
829                     }
830                 }
831                 else {
832                     final double v = getMyNode().getBranchData().getConfidences().get( number ).getValue();
833                     final double sd = getMyNode().getBranchData().getConfidences().get( number ).getStandardDeviation();
834                     getMyNode().getBranchData().getConfidences().set( number, new Confidence( v, value, sd ) );
835                 }
836                 break;
837             case TAXONOMY_CODE:
838                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
839                 try {
840                     getMyNode().getNodeData().getTaxonomy().setTaxonomyCode( value );
841                 }
842                 catch ( final PhyloXmlDataFormatException e ) {
843                     formatError( mtn, e );
844                     break;
845                 }
846                 break;
847             case TAXONOMY_SCIENTIFIC_NAME:
848                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
849                 getMyNode().getNodeData().getTaxonomy().setScientificName( value );
850                 break;
851             case TAXONOMY_COMMON_NAME:
852                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
853                 getMyNode().getNodeData().getTaxonomy().setCommonName( value );
854                 break;
855             case TAXONOMY_RANK:
856                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
857                 try {
858                     getMyNode().getNodeData().getTaxonomy().setRank( value.toLowerCase() );
859                 }
860                 catch ( final PhyloXmlDataFormatException e ) {
861                     formatError( mtn, e );
862                     break;
863                 }
864                 break;
865             case TAXONOMY_AUTHORITY:
866                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
867                 getMyNode().getNodeData().getTaxonomy().setAuthority( value );
868                 break;
869             case TAXONOMY_URI: {
870                 Uri uri = null;
871                 if ( !ForesterUtil.isEmpty( value ) ) {
872                     try {
873                         uri = new Uri( new URL( value ).toURI() );
874                     }
875                     catch ( final Exception e ) {
876                         JOptionPane.showMessageDialog( this,
877                                                        "failed to parse URL from: " + value,
878                                                        "Error",
879                                                        JOptionPane.ERROR_MESSAGE );
880                         mtn.setUserObject( "" );
881                     }
882                 }
883                 if ( uri != null ) {
884                     ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
885                 }
886                 addUri( mtn, uri, number, getMyNode().getNodeData().getTaxonomy() );
887                 break;
888             }
889             case TAXONOMY_SYNONYM:
890                 if ( getMyNode().getNodeData().getTaxonomy().getSynonyms().size() < number ) {
891                     throw new FailedConditionCheckException();
892                 }
893                 else if ( getMyNode().getNodeData().getTaxonomy().getSynonyms().size() == number ) {
894                     if ( !ForesterUtil.isEmpty( value ) ) {
895                         ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
896                         getMyNode().getNodeData().getTaxonomy().getSynonyms().add( value );
897                     }
898                 }
899                 else {
900                     getMyNode().getNodeData().getTaxonomy().getSynonyms().set( number, value );
901                 }
902                 break;
903             case TAXONOMY_ID_VALUE:
904                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
905                 if ( getMyNode().getNodeData().getTaxonomy().getIdentifier() == null ) {
906                     getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( value ) );
907                 }
908                 else {
909                     final String provider = getMyNode().getNodeData().getTaxonomy().getIdentifier().getProvider();
910                     getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( value, provider ) );
911                 }
912                 break;
913             case TAXONOMY_ID_PROVIDER:
914                 ForesterUtil.ensurePresenceOfTaxonomy( getMyNode() );
915                 if ( getMyNode().getNodeData().getTaxonomy().getIdentifier() == null ) {
916                     getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( "", value ) );
917                 }
918                 else {
919                     final String v = getMyNode().getNodeData().getTaxonomy().getIdentifier().getValue();
920                     getMyNode().getNodeData().getTaxonomy().setIdentifier( new Identifier( v, value ) );
921                 }
922                 break;
923             case SEQ_LOCATION:
924                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
925                 getMyNode().getNodeData().getSequence().setLocation( value );
926                 break;
927             case SEQ_MOL_SEQ:
928                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
929                 getMyNode().getNodeData().getSequence().setMolecularSequence( value.replaceAll( "[^a-zA-Z-]", "" ) );
930                 break;
931             case SEQ_NAME:
932                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
933                 getMyNode().getNodeData().getSequence().setName( value );
934                 break;
935             case SEQ_SYMBOL:
936                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
937                 try {
938                     getMyNode().getNodeData().getSequence().setSymbol( value );
939                 }
940                 catch ( final PhyloXmlDataFormatException e ) {
941                     formatError( mtn, e );
942                     break;
943                 }
944                 break;
945             case SEQ_GENE_NAME:
946                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
947                 getMyNode().getNodeData().getSequence().setGeneName( value );
948                 break;
949             case SEQ_TYPE:
950                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
951                 try {
952                     getMyNode().getNodeData().getSequence().setType( value.toLowerCase() );
953                 }
954                 catch ( final PhyloXmlDataFormatException e ) {
955                     formatError( mtn, e );
956                     break;
957                 }
958                 break;
959             case SEQ_ACC_SOURCE:
960                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
961                 if ( getMyNode().getNodeData().getSequence().getAccession() == null ) {
962                     getMyNode().getNodeData().getSequence().setAccession( new Accession( "", value ) );
963                 }
964                 else {
965                     final String v = getMyNode().getNodeData().getSequence().getAccession().getValue();
966                     getMyNode().getNodeData().getSequence().setAccession( new Accession( v, value ) );
967                 }
968                 break;
969             case SEQ_ACC_VALUE:
970                 ForesterUtil.ensurePresenceOfSequence( getMyNode() );
971                 if ( getMyNode().getNodeData().getSequence().getAccession() == null ) {
972                     getMyNode().getNodeData().getSequence().setAccession( new Accession( value, "" ) );
973                 }
974                 else {
975                     final String source = getMyNode().getNodeData().getSequence().getAccession().getSource();
976                     getMyNode().getNodeData().getSequence().setAccession( new Accession( value, source ) );
977                 }
978                 break;
979             case SEQ_URI: {
980                 Uri uri = null;
981                 if ( !ForesterUtil.isEmpty( value ) ) {
982                     try {
983                         uri = new Uri( new URL( value ).toURI() );
984                     }
985                     catch ( final Exception e ) {
986                         JOptionPane.showMessageDialog( this,
987                                                        "failed to parse URL from: " + value,
988                                                        "Error",
989                                                        JOptionPane.ERROR_MESSAGE );
990                         mtn.setUserObject( "" );
991                     }
992                 }
993                 if ( uri != null ) {
994                     ForesterUtil.ensurePresenceOfSequence( getMyNode() );
995                 }
996                 addUri( mtn, uri, number, getMyNode().getNodeData().getSequence() );
997                 break;
998             }
999             case LIT_REFERENCE_DESC:
1000                 if ( !getMyNode().getNodeData().isHasReference() ) {
1001                     getMyNode().getNodeData().setReference( new Reference( "" ) );
1002                 }
1003                 getMyNode().getNodeData().getReference().setValue( value );
1004                 break;
1005             case LIT_REFERENCE_DOI:
1006                 if ( !getMyNode().getNodeData().isHasReference() ) {
1007                     getMyNode().getNodeData().setReference( new Reference( "" ) );
1008                 }
1009                 try {
1010                     getMyNode().getNodeData().getReference().setDoi( value );
1011                 }
1012                 catch ( final PhyloXmlDataFormatException e ) {
1013                     formatError( mtn, e );
1014                     break;
1015                 }
1016                 break;
1017             case EVENTS_DUPLICATIONS:
1018                 if ( !getMyNode().getNodeData().isHasEvent() ) {
1019                     getMyNode().getNodeData().setEvent( new Event() );
1020                 }
1021                 getMyNode().getNodeData().getEvent().setDuplications( parsePositiveInt( mtn, value ) );
1022                 break;
1023             case EVENTS_SPECIATIONS:
1024                 if ( !getMyNode().getNodeData().isHasEvent() ) {
1025                     getMyNode().getNodeData().setEvent( new Event() );
1026                 }
1027                 getMyNode().getNodeData().getEvent().setSpeciations( parsePositiveInt( mtn, value ) );
1028                 break;
1029             case EVENTS_GENE_LOSSES:
1030                 if ( !getMyNode().getNodeData().isHasEvent() ) {
1031                     getMyNode().getNodeData().setEvent( new Event() );
1032                 }
1033                 getMyNode().getNodeData().getEvent().setGeneLosses( parsePositiveInt( mtn, value ) );
1034                 break;
1035             case DATE_DESCRIPTION:
1036                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1037                 getMyNode().getNodeData().getDate().setDesc( value );
1038                 break;
1039             case DATE_MAX:
1040                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1041                 getMyNode().getNodeData().getDate().setMax( parseBigDecimal( mtn, value ) );
1042                 break;
1043             case DATE_MIN:
1044                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1045                 getMyNode().getNodeData().getDate().setMin( parseBigDecimal( mtn, value ) );
1046                 break;
1047             case DATE_UNIT:
1048                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1049                 getMyNode().getNodeData().getDate().setUnit( value );
1050                 break;
1051             case DATE_VALUE:
1052                 ForesterUtil.ensurePresenceOfDate( getMyNode() );
1053                 getMyNode().getNodeData().getDate().setValue( parseBigDecimal( mtn, value ) );
1054                 break;
1055             case DIST_ALT: {
1056                 final BigDecimal new_value = parseBigDecimal( mtn, value );
1057                 if ( new_value != null ) {
1058                     final List<Point> ps = obtainPoints();
1059                     final Point p = ps.get( 0 );
1060                     final Point p_new = new Point( p.getGeodeticDatum(),
1061                                                    p.getLatitude(),
1062                                                    p.getLongitude(),
1063                                                    new_value,
1064                                                    ForesterUtil.isEmpty( p.getAltiudeUnit() ) ? "?"
1065                                                            : p.getAltiudeUnit() );
1066                     ps.set( 0, p_new );
1067                 }
1068                 break;
1069             }
1070             case DIST_DESC: {
1071                 ForesterUtil.ensurePresenceOfDistribution( getMyNode() );
1072                 final Distribution d = getMyNode().getNodeData().getDistribution();
1073                 getMyNode().getNodeData().setDistribution( new Distribution( value, d.getPoints(), d.getPolygons() ) );
1074                 break;
1075             }
1076             case DIST_GEODETIC: {
1077                 if ( !ForesterUtil.isEmpty( value ) ) {
1078                     final List<Point> ps = obtainPoints();
1079                     final Point p = ps.get( 0 );
1080                     final Point p_new = new Point( value,
1081                                                    p.getLatitude(),
1082                                                    p.getLongitude(),
1083                                                    p.getAltitude(),
1084                                                    p.getAltiudeUnit() );
1085                     ps.set( 0, p_new );
1086                 }
1087                 break;
1088             }
1089             case DIST_ALT_UNIT: {
1090                 if ( !ForesterUtil.isEmpty( value ) ) {
1091                     final List<Point> ps = obtainPoints();
1092                     final Point p = ps.get( 0 );
1093                     final Point p_new = new Point( p.getGeodeticDatum(),
1094                                                    p.getLatitude(),
1095                                                    p.getLongitude(),
1096                                                    p.getAltitude(),
1097                                                    value );
1098                     ps.set( 0, p_new );
1099                 }
1100                 break;
1101             }
1102             case DIST_LAT: {
1103                 final BigDecimal new_value = parseBigDecimal( mtn, value );
1104                 if ( new_value != null ) {
1105                     final List<Point> ps = obtainPoints();
1106                     final Point p = ps.get( 0 );
1107                     final Point p_new = new Point( p.getGeodeticDatum(),
1108                                                    new_value,
1109                                                    p.getLongitude(),
1110                                                    p.getAltitude(),
1111                                                    p.getAltiudeUnit() );
1112                     ps.set( 0, p_new );
1113                 }
1114                 break;
1115             }
1116             case DIST_LONG: {
1117                 final BigDecimal new_value = parseBigDecimal( mtn, value );
1118                 if ( new_value != null ) {
1119                     final List<Point> ps = obtainPoints();
1120                     final Point p = ps.get( 0 );
1121                     final Point p_new = new Point( p.getGeodeticDatum(),
1122                                                    p.getLatitude(),
1123                                                    new_value,
1124                                                    p.getAltitude(),
1125                                                    p.getAltiudeUnit() );
1126                     ps.set( 0, p_new );
1127                 }
1128                 break;
1129             }
1130             default:
1131                 throw new IllegalArgumentException( "unknown: " + tag );
1132         }
1133        
1134         getJTree().repaint();
1135         getTreePanel().setEdited( true );
1136         getTreePanel().repaint();
1137     }
1138
1139     PhylogenyNode getMyNode() {
1140         return _my_node;
1141     }
1142
1143     void writeAll() {
1144         //TODO this does not do what it should do.
1145         for( int i = 0; i < getJTree().getRowCount(); i++ ) {
1146             final TreePath p = getJTree().getPathForRow( i );
1147             writeBack( ( DefaultMutableTreeNode ) p.getLastPathComponent() );
1148         }
1149     }
1150 }