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