Skip to content
Snippets Groups Projects
EditMenu.java 109 KiB
Newer Older
  • Learn to ignore specific revisions
  • tjc's avatar
    tjc committed
    /* EditMenu.java
     *
     * created: Thu Dec  3 1998
     *
     * This file is part of Artemis
     *
     * Copyright (C) 1998,1999,2000,2001,2002  Genome Research Limited
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * as published by the Free Software Foundation; either version 2
     * of the License, or (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     *
    
    tjc's avatar
    tjc committed
     * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/EditMenu.java,v 1.44 2008-01-17 10:15:56 tjc Exp $
    
    tjc's avatar
    tjc committed
     **/
    
    package uk.ac.sanger.artemis.components;
    
    import uk.ac.sanger.artemis.*;
    import uk.ac.sanger.artemis.sequence.*;
    import uk.ac.sanger.artemis.util.*;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.components.genebuilder.GeneBuilderFrame;
    
    import uk.ac.sanger.artemis.components.genebuilder.GeneUtils;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.components.genebuilder.GeneViewerPanel;
    import uk.ac.sanger.artemis.io.ChadoCanonicalGene;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.Range;
    import uk.ac.sanger.artemis.io.RangeVector;
    import uk.ac.sanger.artemis.io.Key;
    import uk.ac.sanger.artemis.io.Location;
    import uk.ac.sanger.artemis.io.Qualifier;
    import uk.ac.sanger.artemis.io.QualifierVector;
    import uk.ac.sanger.artemis.io.InvalidRelationException;
    import uk.ac.sanger.artemis.io.EntryInformation;
    import uk.ac.sanger.artemis.io.EntryInformationException;
    import uk.ac.sanger.artemis.io.OutOfDateException;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.GFFStreamFeature;
    
    tjc's avatar
    tjc committed
    
    import java.awt.*;
    import java.awt.event.*;
    import java.io.IOException;
    
    import javax.swing.*;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
    import java.text.DecimalFormat;
    import java.text.NumberFormat;
    
    tjc's avatar
    tjc committed
    import java.util.Vector;
    
    tjc's avatar
    tjc committed
    
    /**
     *  A menu with editing commands.
     *
     *  @author Kim Rutherford
    
    tjc's avatar
    tjc committed
     *  @version $Id: EditMenu.java,v 1.44 2008-01-17 10:15:56 tjc Exp $
    
    tjc's avatar
    tjc committed
     **/
    
    public class EditMenu extends SelectionMenu
    
        implements EntryGroupChangeListener, EntryChangeListener 
    {
    
    
    tjc's avatar
    tjc committed
      /**
       * 
       */
      private static final long serialVersionUID = 1L;
    
    
      /**
       *  The GotoEventSource object that was passed to the constructor.
       **/
      private GotoEventSource goto_event_source = null;
    
      /**
       *  The EntryGroup object that was passed to the constructor.
       **/
      private EntryGroup entry_group = null;
    
      /**
       *  The BasePlotGroup object that was passed to the constructor.
       **/
      private BasePlotGroup base_plot_group = null;
    
    
    tjc's avatar
    tjc committed
      /** FeatureDisplay */
      private DisplayComponent owner;
    
    
    tjc's avatar
    tjc committed
    
      public static org.apache.log4j.Logger logger4j = 
        org.apache.log4j.Logger.getLogger(EditMenu.class);
    
    tjc's avatar
    tjc committed
      /**
       *  Create a new EditMenu object.
       *  @param frame The JFrame that owns this JMenu.
       *  @param selection The Selection that the commands in the menu will
       *    operate on.
    
       *  @param goto_event_source The object the we will call makeBaseVisible()
    
    tjc's avatar
    tjc committed
       *    on.
       *  @param entry_group The EntryGroup object where new features/entries will
       *    be added.
       *  @param base_plot_group The BasePlotGroup associated with this JMenu -
       *    needed to call getCodonUsageAlgorithm()
       *  @param menu_name The name of the new menu.
       **/
    
      public EditMenu(final JFrame frame,
                      final Selection selection,
                      final GotoEventSource goto_event_source,
                      final EntryGroup entry_group,
                      final BasePlotGroup base_plot_group,
    
    tjc's avatar
    tjc committed
                      final String menu_name,
                      final DisplayComponent owner)
    
      {
        super(frame, menu_name, selection);
    
    tjc's avatar
    tjc committed
    
        this.entry_group = entry_group;
        this.goto_event_source = goto_event_source;
        this.base_plot_group = base_plot_group;
    
    tjc's avatar
    tjc committed
        this.owner = owner;
    
    tjc's avatar
    tjc committed
    
    
        getEntryGroup().addEntryGroupChangeListener(this);
        getEntryGroup().addEntryChangeListener(this);
        refreshMenu();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Create a new EditMenu object and use "Edit" as the menu name.
       *  @param frame The JFrame that owns this JMenu.
       *  @param selection The Selection that the commands in the menu will
       *    operate on.
    
       *  @param goto_event_source The object the we will call makeBaseVisible()
    
    tjc's avatar
    tjc committed
       *    on.
       *  @param entry_group The EntryGroup object where new features/entries will
       *    be added.
       *  @param base_plot_group The BasePlotGroup associated with this JMenu -
       *    needed to call getCodonUsageAlgorithm()
       **/
    
      public EditMenu(final JFrame frame,
                      final Selection selection,
                      final GotoEventSource goto_event_source,
                      final EntryGroup entry_group,
    
    tjc's avatar
    tjc committed
                      final BasePlotGroup base_plot_group,
                      final DisplayComponent owner) 
    
      {
        this(frame, selection, goto_event_source, entry_group,
    
    tjc's avatar
    tjc committed
             base_plot_group, "Edit", owner);
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  The shortcut for Edit Selected Features.
       **/
      final static KeyStroke EDIT_FEATURES_KEY =
    
        KeyStroke.getKeyStroke(KeyEvent.VK_E, 
                               Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); //InputEvent.CTRL_MASK);
    
    tjc's avatar
    tjc committed
      final static public int EDIT_FEATURES_KEY_CODE = KeyEvent.VK_E;
    
      /**
       *  The shortcut for Merge Selected Features.
       **/
      final static KeyStroke MERGE_FEATURES_KEY =
    
        KeyStroke.getKeyStroke(KeyEvent.VK_M,
                               Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); // InputEvent.CTRL_MASK);
    
    tjc's avatar
    tjc committed
      final static public int MERGE_FEATURES_KEY_CODE = KeyEvent.VK_M;
    
      /**
       *  The shortcut for Duplicate Selected Features.
       **/
      final static KeyStroke DUPLICATE_KEY =
    
        KeyStroke.getKeyStroke(KeyEvent.VK_D,
                               Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); // InputEvent.CTRL_MASK);
    
    tjc's avatar
    tjc committed
      final static public int DUPLICATE_KEY_CODE = KeyEvent.VK_D;
    
      /**
       *  The shortcut for Delete Selected Features.
       **/
      final static KeyStroke DELETE_FEATURES_KEY =
    
        KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,
                               Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); // InputEvent.CTRL_MASK);
    
    tjc's avatar
    tjc committed
      final static public int DELETE_FEATURES_KEY_CODE = KeyEvent.VK_DELETE;
    
      /**
       *  The shortcut for Trim Selected Features.
       **/
      final static KeyStroke TRIM_FEATURES_KEY =
    
        KeyStroke.getKeyStroke(KeyEvent.VK_T,
                               Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); // InputEvent.CTRL_MASK);
    
    tjc's avatar
    tjc committed
      final static public int TRIM_FEATURES_KEY_CODE = KeyEvent.VK_T;
    
      /**
       *  The shortcut for Trim Selected Features To Next Any.
       **/
      final static KeyStroke TRIM_FEATURES_TO_NEXT_ANY_KEY =
    
        KeyStroke.getKeyStroke(KeyEvent.VK_Y,
                               Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); // InputEvent.CTRL_MASK);
    
    tjc's avatar
    tjc committed
      final static public int TRIM_FEATURES_TO_NEXT_ANY_KEY_CODE = KeyEvent.VK_Y;
    
      /**
       *  The shortcut for Extend to Previous Stop Codon.
       **/
      final static public int EXTEND_TO_PREVIOUS_STOP_CODON_KEY_CODE =
        KeyEvent.VK_Q;
      final static KeyStroke EXTEND_TO_PREVIOUS_STOP_CODON_KEY =
    
        makeMenuKeyStroke(EXTEND_TO_PREVIOUS_STOP_CODON_KEY_CODE);
    
    tjc's avatar
    tjc committed
    
      /**
       *  The shortcut for Undo.
       **/
      final static public int UNDO_KEY_CODE = KeyEvent.VK_U;
      final static KeyStroke UNDO_KEY =
    
        KeyStroke.getKeyStroke(UNDO_KEY_CODE,
                               Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); // InputEvent.CTRL_MASK);
    
    tjc's avatar
    tjc committed
    
      /**
       *  Implementation of the EntryGroupChangeListener interface.  We listen to
       *  EntryGroupChange events so that we can update the display if entries
       *  are added or deleted.
       **/
    
      public void entryGroupChanged(final EntryGroupChangeEvent event) 
      {
        switch(event.getType()) 
        {
          case EntryGroupChangeEvent.ENTRY_ADDED:
          case EntryGroupChangeEvent.ENTRY_DELETED:
          case EntryGroupChangeEvent.ENTRY_INACTIVE:
          case EntryGroupChangeEvent.ENTRY_ACTIVE:
          case EntryGroupChangeEvent.NEW_DEFAULT_ENTRY:
            refreshMenu();
            break;
    
    tjc's avatar
    tjc committed
        }
      }
    
      /**
       *  Implementation of the EntryChangeListener interface.
       **/
    
      public void entryChanged(final EntryChangeEvent event) 
      {
        if(event.getType() == EntryChangeEvent.NAME_CHANGED) 
          refreshMenu();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Update the menus to the reflect the current contents of the EntryGroup.
       **/
    
      private void refreshMenu() 
      {
        removeAll();
    
        final JMenuItem undo_item = new JMenuItem("Undo");
        undo_item.setAccelerator(UNDO_KEY);
        undo_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            undo(getParentFrame(), getSelection(), getEntryGroup());
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem contig_reordering = new JMenuItem("Contig Reordering");
        contig_reordering.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event)
          {
            FeatureDisplay display = (FeatureDisplay)owner;
            FeatureVector contig_features = display.getContigs();
             
    
            final JFrame frame = new JFrame("Contig Tool");
    
    tjc's avatar
    tjc committed
    
            JScrollPane jsp = new JScrollPane();
    
            final ContigTool ct = new ContigTool(contig_features, 
                                     (FeatureDisplay)owner, jsp,
                                     getSelection());
    
    tjc's avatar
    tjc committed
            jsp.setViewportView(ct);
    
            jsp.getViewport().setBackground(Color.white);
            jsp.setPreferredSize(new Dimension(display.getWidth(),
                     ct.getPreferredSize().height+
                     jsp.getVerticalScrollBar().getPreferredSize().height));
            frame.getContentPane().add(jsp, BorderLayout.CENTER);
            frame.getContentPane().add(ct.getStatusBar(), 
                                     BorderLayout.SOUTH);
    
            frame.pack();
    
            frame.addWindowListener(new WindowAdapter()
            {
              public void windowClosing(WindowEvent event)
              {
                getSelection().removeSelectionChangeListener(ct);
                frame.dispose();
              }
            });
    
    
    tjc's avatar
    tjc committed
            Utilities.centreJustifyFrame(frame,0);
    
    tjc's avatar
    tjc committed
            frame.setVisible(true);
          }
        });
    
    
        final JMenuItem edit_feature_item = new JMenuItem("Edit Selected Features");
        edit_feature_item.setAccelerator(EDIT_FEATURES_KEY);
        edit_feature_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            editSelectedFeatures(getParentFrame(), getEntryGroup(),
                                 getSelection(), goto_event_source);
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem edit_subsequence_item = new JMenuItem("Edit Subsequence (and Features)");
        edit_subsequence_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            editSubSequence();
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenu qualifier_menu = new JMenu("Qualifier of Selected Feature(s)");
    
    tjc's avatar
    tjc committed
        final JMenuItem add_qualifiers_item = new JMenuItem("Change ...");
    
        add_qualifiers_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            addQualifiers(getParentFrame(), getSelection());
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem remove_qualifier_item = new JMenuItem("Remove ...");
    
        remove_qualifier_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            removeQualifier(getParentFrame(), getSelection());
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem convert_qualifier_item = new JMenuItem("Convert ...");
    
    tjc's avatar
    tjc committed
        convert_qualifier_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            convertQualifier(getParentFrame(), getSelection());
          }
        });
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final JMenu feature_menu = new JMenu("Selected Feature(s)");
        final JMenuItem merge_features_item = new JMenuItem("Merge");
    
        merge_features_item.setAccelerator(MERGE_FEATURES_KEY);
        merge_features_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            mergeFeatures(getParentFrame(), getSelection(), getEntryGroup());
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem unmerge_feature_item = new JMenuItem("Unmerge");
    
    tjc's avatar
    tjc committed
          
    
        unmerge_feature_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            unmergeFeature(getParentFrame(), getSelection(), getEntryGroup());
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem unmerge_all_feature_item = new JMenuItem("Unmerge All Segments");
    
    tjc's avatar
    tjc committed
        if(GeneUtils.isDatabaseEntry(entry_group))
          unmerge_all_feature_item.setEnabled(false);
    
    tjc's avatar
    tjc committed
        unmerge_all_feature_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event)
          {
            unmergeAllFeature(getParentFrame(), getSelection(), getEntryGroup());
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem duplicate_item  = new JMenuItem("Duplicate");
    
    tjc's avatar
    tjc committed
        duplicate_item.setAccelerator(DUPLICATE_KEY);
        duplicate_item.addActionListener(new ActionListener() 
    
    tjc's avatar
    tjc committed
          public void actionPerformed(ActionEvent event) 
    
    tjc's avatar
    tjc committed
            duplicateFeatures(getParentFrame(), getSelection(),
                              getEntryGroup());
          }
        });
       
    
    tjc's avatar
    tjc committed
        final JMenuItem delete_features_item = new JMenuItem("Delete");
    
        delete_features_item.setAccelerator(DELETE_FEATURES_KEY);
        delete_features_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            deleteSelectedFeatures(getParentFrame(), getSelection(),
                                   getEntryGroup());
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem delete_segments_item = new JMenuItem("Delete Exons");
    
        delete_segments_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            deleteSelectedSegments();
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem delete_introns_item =
    
    tjc's avatar
    tjc committed
          new JMenuItem("Remove Introns");
    
        delete_introns_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event) 
          {
            removeIntrons();
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem edit_header_item = new JMenuItem("Edit Header Of Default Entry");
        edit_header_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event)
          {
            editHeader();
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenu move_features_menu = new JMenu("Move Selected Features To");
        final JMenu copy_features_menu = new JMenu("Copy Selected Features To");
    
    tjc's avatar
    tjc committed
    
    
        if(entry_group == null || getEntryGroup().size() == 0) 
        {
          move_features_menu.add(new JMenuItem("(No Entries Currently)"));
          copy_features_menu.add(new JMenuItem("(No Entries Currently)"));
        }
        else
        {
          for(int i = 0 ; i < getEntryGroup().size() ; ++i) 
          {
            final Entry this_entry = getEntryGroup().elementAt(i);
    
    tjc's avatar
    tjc committed
    
    
            String entry_name = this_entry.getName();
            if(entry_name == null)
    
    tjc's avatar
    tjc committed
              entry_name = "no name";
    
    
            final JMenuItem move_to_item = new JMenuItem(entry_name);
            move_to_item.addActionListener(new ActionListener() 
            {
              public void actionPerformed(ActionEvent event) 
              {
    
    tjc's avatar
    tjc committed
                // unselect, move, then reselect (for speed)
                final FeatureVector selected_features =
    
                  getSelection().getAllFeatures();
                getSelection().clear();
                moveFeatures(selected_features, this_entry);
                getSelection().set(selected_features);
    
    tjc's avatar
    tjc committed
              }
            });
    
            move_features_menu.add(move_to_item);
    
    tjc's avatar
    tjc committed
    
    
            final JMenuItem copy_to_item = new JMenuItem(entry_name);
    
    tjc's avatar
    tjc committed
    
    
            copy_to_item.addActionListener(new ActionListener() 
            {
              public void actionPerformed(ActionEvent event) 
              {
                copyFeatures(getSelection().getAllFeatures(), this_entry);
    
    tjc's avatar
    tjc committed
              }
            });
    
            copy_features_menu.add(copy_to_item);
    
    tjc's avatar
    tjc committed
          }
        }
    
    
    tjc's avatar
    tjc committed
        final JMenu trim_menu = new JMenu("Trim Selected Features");
        final JMenuItem trim_to_any_item = new JMenuItem("To Any");
    
        trim_to_any_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) {
            EditMenu.trimSelected(getParentFrame(), getSelection(),
                                  getEntryGroup(), true, false);
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem trim_item = new JMenuItem("To Met");
    
        trim_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event) 
          {
            EditMenu.trimSelected(getParentFrame(), getSelection(),
                                  getEntryGroup(), false, false);
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem trim_to_next_any_item =
    
    tjc's avatar
    tjc committed
          new JMenuItem("To Next Any");
    
        trim_to_next_any_item.setAccelerator(TRIM_FEATURES_TO_NEXT_ANY_KEY);
        trim_to_next_any_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event) 
          {
            EditMenu.trimSelected(getParentFrame(), getSelection(),
                                  getEntryGroup(), true, true);
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem trim_to_next_item = new JMenuItem("To Next Met");
    
        trim_to_next_item.setAccelerator(TRIM_FEATURES_KEY);
        trim_to_next_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event) 
          {
            EditMenu.trimSelected(getParentFrame(), getSelection(),
                                  getEntryGroup(), false, true);
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenu extend_menu = new JMenu("Extend Selected Features");
    
        final JMenuItem extend_to_prev_stop_item =
    
    tjc's avatar
    tjc committed
          new JMenuItem("To Previous Stop Codon");
    
        extend_to_prev_stop_item.setAccelerator(EXTEND_TO_PREVIOUS_STOP_CODON_KEY);
        extend_to_prev_stop_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event) 
          {
            extendToORF(getParentFrame(), getSelection(),
                        getEntryGroup(), false);
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem extend_to_next_stop_item = new JMenuItem("To Next Stop Codon");
    
        extend_to_next_stop_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event) 
          {
            extendToORF(getParentFrame(), getSelection(),
                        getEntryGroup(), true);
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem fix_stop_codons_item = new JMenuItem("Fix Stop Codons");
        fix_stop_codons_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event) 
          {
            fixStopCodons();
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenuItem extend_to_next_stop_and_fix_item = new JMenuItem("To Next Stop Codon and Fix");
    
        extend_to_next_stop_and_fix_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event) 
          {
            extendToORF(getParentFrame(), getSelection(),
                        getEntryGroup(), true);
            fixStopCodons();
          }
        });
        
    
        final JMenuItem auto_gene_name_item = new JMenuItem("Automatically Create Gene Names");
        auto_gene_name_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            autoGeneName();
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem fix_gene_names_item = new JMenuItem("Fix Gene Names");
        fix_gene_names_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            fixGeneNames(getParentFrame(), getEntryGroup(),
                         getSelection());
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem reverse_complement_item = new JMenuItem("Reverse And Complement");
        reverse_complement_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            reverseAndComplement();
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem reverse_complement_range_item = new JMenuItem("Reverse And Complement Selected Contig");
        reverse_complement_range_item.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent event)
          {
    
            if(getEntryGroup().isReadOnly()) 
            {
              final String message =
                "one or more of the entries or features are read only - " +
                "cannot continue";
              new MessageDialog(getParentFrame(), message);
              return;
            }
    
    
            final FeatureVector selected_features = getSelection().getAllFeatures();
    
            if(selected_features.size() == 1)
            {
              final Feature selection_feature = selected_features.elementAt(0);
    
    tjc's avatar
    tjc committed
    
              final YesNoDialog dialog =
                    new YesNoDialog (getParentFrame (),
                                     "Are you sure you want to reverse complement this " +
                                     "region "+ selection_feature.getFirstBase()+".."+
                                                selection_feature.getLastBase()+"?");
             if(!dialog.getResult())
               return;
    
    
              try 
              {
                getEntryGroup().getBases().reverseComplement(selection_feature);
              }
              catch(ReadOnlyException roe)
              {
                final String message =
                  "one or more of the features is read-only or is in a " +
                  "read-only entry - cannot continue";
                new MessageDialog(null, message);
                return;
              }
            }
            else
            {
              final String message =
                  "Select a single contig to reverse and complement";
              new MessageDialog(null, message);
              return;   
            }
          }
        });
    
    
    tjc's avatar
    tjc committed
        final JMenu bases_item = new JMenu("Bases");
        
    
        final JMenuItem delete_bases_item = new JMenuItem("Delete Selected Bases");
        delete_bases_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event)
          {
    
    tjc's avatar
    tjc committed
            deleteSelectedBases("Are you sure you want to delete the " +
                                "selected bases?");
    
    tjc's avatar
    tjc committed
          }
        });
    
    
        final JMenuItem add_bases_item = new JMenuItem("Add Bases At Selection");
        add_bases_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            addBases();
    
    tjc's avatar
    tjc committed
          }
        });
    
    tjc's avatar
    tjc committed
        
        
        final JMenuItem replace_bases_item = new JMenuItem("Replace Bases At Selection");
        replace_bases_item.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent event) 
          {
            MarkerRange marker_range = getSelection ().getMarkerRange ();
            int start = getSelection().getHighestBaseOfSelection().getPosition();
    
    tjc's avatar
    tjc committed
            boolean hasDeleted = deleteSelectedBases(
                "Are you sure you want to replace the " +
                "selected bases?");
    
    tjc's avatar
    tjc committed
            
            if(!hasDeleted)
              return;
            
            if(!marker_range.isForwardMarker())
            {
              try
              {
                marker_range = new MarkerRange(
                    getEntryGroup().getBases().getReverseStrand(),
                    start,start+1);
              }
              catch(OutOfRangeException e)
              {
                e.printStackTrace();
                return;
              }
            }
            getSelection ().setMarkerRange (marker_range);
            addBases();
            getSelection ().setMarkerRange (null);
          }
        });
    
    tjc's avatar
    tjc committed
    
    
        if(Options.getOptions().getPropertyTruthValue("val_mode")) 
        {
          add(edit_feature_item);
          add(edit_subsequence_item);
          addSeparator();
          add(edit_header_item);
          addSeparator();
        }
    
        if(Options.getOptions().getUndoLevels() > 0) 
        {
          add(undo_item);
          addSeparator();
        }
    
        if(!Options.getOptions().getPropertyTruthValue("val_mode")) 
        {
          add(edit_feature_item);
          add(edit_subsequence_item);
          addSeparator();
          add(edit_header_item);
          addSeparator();
        }
    
    
    tjc's avatar
    tjc committed
        add(qualifier_menu);
        qualifier_menu.add(add_qualifiers_item);
        qualifier_menu.add(remove_qualifier_item);
        qualifier_menu.add(convert_qualifier_item);
        add(feature_menu);
        feature_menu.add(duplicate_item);
        feature_menu.add(merge_features_item);
        feature_menu.add(unmerge_feature_item);
        feature_menu.add(unmerge_all_feature_item);
        feature_menu.add(delete_features_item);
        feature_menu.add(delete_segments_item);
        feature_menu.add(delete_introns_item);
    
        addSeparator();
        add(move_features_menu);
        add(copy_features_menu);
        addSeparator();
    
    tjc's avatar
    tjc committed
        add(trim_menu);
        trim_menu.add(trim_item);
        trim_menu.add(trim_to_any_item);
        trim_menu.add(trim_to_next_item);
        trim_menu.add(trim_to_next_any_item);
        add(extend_menu);
        extend_menu.add(extend_to_prev_stop_item);
        extend_menu.add(extend_to_next_stop_item);
    
        add(fix_stop_codons_item);
    
    tjc's avatar
    tjc committed
        extend_menu.add(extend_to_next_stop_and_fix_item);
    
        addSeparator();
        add(auto_gene_name_item);
        add(fix_gene_names_item);
    
    tjc's avatar
    tjc committed
        add(bases_item);
        bases_item.add(reverse_complement_item); 
        bases_item.add(reverse_complement_range_item);
        bases_item.add(delete_bases_item);
        bases_item.add(add_bases_item);
    
    tjc's avatar
    tjc committed
          // only the standalone version can save or read
    
          final JMenuItem add_bases_from_file_item = new JMenuItem("Add Bases From File ...");
          add_bases_from_file_item.addActionListener(new ActionListener()
          {
            public void actionPerformed(ActionEvent event)
            {
              addBasesFromFile();
    
    tjc's avatar
    tjc committed
            }
          });
    
    
    tjc's avatar
    tjc committed
          bases_item.add(add_bases_from_file_item);
    
    tjc's avatar
    tjc committed
        }
    
    tjc's avatar
    tjc committed
        bases_item.add(replace_bases_item);
    
    tjc's avatar
    tjc committed
        
    
        if(owner instanceof FeatureDisplay)
        {
    
    tjc's avatar
    tjc committed
          addSeparator();
    
          add(contig_reordering);
        }
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Undo the last change by calling ActionController.undo().
       *  @param frame The Frame to use for MessageDialog components.
       *  @param selection The current Selection - needs to be cleared before undo
       *  @param entry_group Used to get the ActionController for calling
       *    ActionController.undo().
       **/
    
      private static void undo(final JFrame frame,
                              final Selection selection,
                              final EntryGroup entry_group) 
      {
        // undo disabled
        if(Options.getOptions().getUndoLevels() == 0)
    
    tjc's avatar
    tjc committed
          return;
    
    
        // clear the selection because something in the selection might
        // disappear after the undo() eg. create a feature, select it then undo
        if(entry_group.getActionController().canUndo())
          selection.clear();
    
    tjc's avatar
    tjc committed
    
    
        if(!entry_group.getActionController().undo()) 
          new MessageDialog(frame, "sorry - no further undo information");
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Open an edit window (FeatureEdit) for each of the selected features.
       *  The edit component will listen for feature change events and update
       *  itself.
       *  @param frame The JFrame to use for MessageDialog components.
       *  @param selection The selected features to edit.
       *  @param entry_group Used to get the ActionController for calling
       *    startAction() and endAction().
       **/
    
      protected static void editSelectedFeatures(final JFrame frame,
                                       final EntryGroup entry_group,
                                       final Selection selection,
                                       final GotoEventSource goto_event_source) 
      {
    
    tjc's avatar
    tjc committed
        frame.setCursor(new Cursor(Cursor.WAIT_CURSOR));
    
        final int MAX_SELECTED_FEATURES = 25;
        final FeatureVector features_to_edit = selection.getAllFeatures();
    
    tjc's avatar
    tjc committed
        boolean featureEdit = true;
        
    
        if(features_to_edit.size() > MAX_SELECTED_FEATURES)
          new MessageDialog(frame, "warning: only editing the first " +
                            MAX_SELECTED_FEATURES + " selected features");
    
    tjc's avatar
    tjc committed
    
    
        for(int i = 0; i < features_to_edit.size() && i < MAX_SELECTED_FEATURES;
            ++i)
        {
          final Feature selection_feature = features_to_edit.elementAt(i);
    
    tjc's avatar
    tjc committed
    
    
          featureEdit = editSelectedFeatures(entry_group, selection, goto_event_source,
              selection_feature, null, null);
    
    tjc's avatar
    tjc committed
        }
    
    
    tjc's avatar
    tjc committed
        if(featureEdit)
          selection.set(features_to_edit);
    
    tjc's avatar
    tjc committed
        frame.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    
    tjc's avatar
    tjc committed
      }
    
    
      public static boolean editSelectedFeatures(
          final EntryGroup entry_group,
          final Selection selection,
          final GotoEventSource goto_event_source,
          final Feature selection_feature,
          final ActionListener cancel_listener,
          final ActionListener apply_listener) 
      {
        if(selection_feature.getEmblFeature() instanceof GFFStreamFeature &&
            ((GFFStreamFeature)selection_feature.getEmblFeature()).getChadoGene() != null)
        {
          new GeneBuilderFrame(selection_feature, entry_group,
                               selection, goto_event_source);
          return false;
        }
        else
        {
          final JFrame edit_frame = new JFrame("Artemis Feature Edit: " + 
               selection_feature.getIDString() +
               (selection_feature.isReadOnly() ?
                   "  -  (read only)" :
                   ""));
           
           final FeatureEdit fe = new FeatureEdit(selection_feature, entry_group,
                                       selection, goto_event_source, edit_frame);
           
           edit_frame.addWindowListener(new WindowAdapter() 
           {
             public void windowClosing(WindowEvent event) 
             {
               fe.stopListening();
               edit_frame.dispose();
             }
           });
           
           if(cancel_listener != null)
             fe.addCancelActionListener(cancel_listener);
           if(apply_listener != null)
             fe.addApplyActionListener(apply_listener);
           edit_frame.getContentPane().add(fe);
           edit_frame.pack();
    
           Utilities.centreFrame(edit_frame);
           edit_frame.setVisible(true);
           return true;
         } 
      }
      
    
    tjc's avatar
    tjc committed
      /**
       *  Create a new EntryEdit component that contains only the selected
       *  sequence and the features in the selected range.
       **/
    
      private void editSubSequence() 
      {
        if(getSelection().isEmpty()) 
          new MessageDialog(getParentFrame(), "nothing selected");
    
        final Range range = getSelection().getSelectionRange();
        final EntryGroup new_entry_group = getEntryGroup().truncate(range);
        new EntryEdit(new_entry_group).setVisible(true);
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Open a EntryHeaderEdit window for the default entry.
       **/
    
      private void editHeader()
      {
        final Entry default_entry = getEntryGroup().getDefaultEntry();
    
        if(default_entry == null)
        {
          final String message = "there is no default entry";
          new MessageDialog(getParentFrame(), message);
        }
        else 
        {
          if(default_entry.isReadOnly()) 
          {
            new MessageDialog(getParentFrame(),
                              "the default entry is read-only " +
                              "- cannot continue");
    
    tjc's avatar
    tjc committed
            return;
          }
    
    
          new EntryHeaderEdit(entry_group, default_entry);
    
    tjc's avatar
    tjc committed
        }
      }
    
      /**
       *  Merge the selected features into one Feature.  If there are selected
       *  segments then the owning Feature of each segment will be the Feature
       *  that is merged.  This method will create a new Feature.
       *  @param frame The JFrame to use for MessageDialog components.
       *  @param selection The Selection containing the features to merge.
       *  @param entry_group Used to get the ActionController for calling
       *    startAction() and endAction().
       **/
    
      protected static void mergeFeatures(final JFrame frame,
                                final Selection selection,
                                final EntryGroup entry_group) 
      {
        try 
        {
          entry_group.getActionController().startAction();
    
          if(!checkForSelectionFeatures(frame, selection, 10,
                     "really merge all (>10) " + "selected features?"))
    
    tjc's avatar
    tjc committed
            return;
    
    
          final FeatureVector features_to_merge = selection.getAllFeatures();
    
    tjc's avatar
    tjc committed
    
    
          if(features_to_merge.size() < 2) 
          {
            new MessageDialog(frame,
                              "nothing to merge - select more than one feature");
    
    tjc's avatar
    tjc committed
            return;
          }
    
    
          final Feature merge_feature = features_to_merge.elementAt(0);
    
    tjc's avatar
    tjc committed
    
          // make sure all the features are on the same strand
    
          for(int i = 1; i < features_to_merge.size(); ++i) 
          {
            final Feature this_feature = features_to_merge.elementAt(i);
    
            if(this_feature.isForwardFeature() !=
               merge_feature.isForwardFeature()) 
            {
              new MessageDialog(frame,
                                "all the features in a merge must be on the " +
                                "same strand");
    
    tjc's avatar
    tjc committed
              return;
            }
    
    
            if(!this_feature.getKey().equals(merge_feature.getKey())) 
            {
              new MessageDialog(frame,
                                "all the features in a merge must have the " +
                                "same key");
    
    tjc's avatar
    tjc committed
              return;
            }
          }
    
    
          if(Options.getOptions().isNoddyMode()) 
          {
    
    tjc's avatar
    tjc committed
            final YesNoDialog dialog =
    
              new YesNoDialog(frame, "Are you sure you want to merge the selected " +
                                     "features?");
            if(!dialog.getResult())
    
    tjc's avatar
    tjc committed
              return;
          }
    
    
          final Feature new_feature;
    
    tjc's avatar
    tjc committed
          //
          //  GFF merge
          if(merge_feature.getEmblFeature() instanceof GFFStreamFeature)
          {
    
            if(!merge_feature.getKey().equals(DatabaseDocument.EXONMODEL) &&
               !merge_feature.getKey().equals("pseudogenic_exon")) 
            {