Skip to content
Snippets Groups Projects
EditMenu.java 118 KiB
Newer Older
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.
 *
 * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/EditMenu.java,v 1.62 2009-08-17 12:29:04 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.BasicGeneBuilderFrame;
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.components.genebuilder.gff.PropertiesPanel;
tjc's avatar
tjc committed
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;
import uk.ac.sanger.artemis.io.RawStreamSequence;
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;
import java.util.Hashtable;
tjc's avatar
tjc committed
import java.util.Vector;
tjc's avatar
tjc committed

/**
 *  A menu with editing commands.
 *
 *  @author Kim Rutherford
 *  @version $Id: EditMenu.java,v 1.62 2009-08-17 12:29:04 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);
  
  /** records the gene builders that are open */
  private static Hashtable geneBuilderHash;
  
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");
tjc's avatar
tjc committed
    getEntryGroup().getActionController().addUndoMenu(undo_item);
    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 redo_item = new JMenuItem("Redo");
tjc's avatar
tjc committed
    //redo_item.setAccelerator(REDO_KEY);
tjc's avatar
tjc committed
    getEntryGroup().getActionController().addRedoMenu(redo_item);
tjc's avatar
tjc committed
    redo_item.addActionListener(new ActionListener() 
    {
      public void actionPerformed(ActionEvent event) 
      {
        redo(getParentFrame(), getSelection(), getEntryGroup());
      }
    });
tjc's avatar
tjc committed

tjc's avatar
tjc committed
    final JMenuItem contig_reordering = new JMenuItem("Contig Reordering");
    if(GeneUtils.isDatabaseEntry(entry_group))
      contig_reordering.setEnabled(false);
    
tjc's avatar
tjc committed
    contig_reordering.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent event)
      {
        FeatureDisplay display = (FeatureDisplay)owner;
        FeatureVector contig_features = display.getContigs();
        
        if(contig_features == null || contig_features.size() < 1)
        {
          final Vector contigKeys = FeatureDisplay.getContigKeys();
          String msg = "No contig feature keys found:\n";
          for(int i=0; i<contigKeys.size(); i++)
            msg = msg+(String)contigKeys.get(i)+"\n";
          JOptionPane.showMessageDialog(display, 
              msg, "No Contigs Found", JOptionPane.ERROR_MESSAGE);
          return;
        }
        
        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("Selected Features in Editor");
    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("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());
      }
    });
    final JMenuItem find_and_replace_qualifier_item = new JMenuItem("Find/Replace Qualifier Text ...");
    find_and_replace_qualifier_item.addActionListener(new ActionListener() 
    {
      public void actionPerformed(ActionEvent event) 
      {
        new FindAndReplace(getSelection(), goto_event_source, 
                           entry_group, base_plot_group);
      }
    });
    
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
      }
    });
tjc's avatar
tjc committed
    
    final JMenuItem convert_keys_item = new JMenuItem("Convert Keys ...");
    convert_keys_item.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent event) 
      {
        convertKeys(getParentFrame(), getSelection());
      }
    });
tjc's avatar
tjc committed

    final JMenuItem edit_header_item = new JMenuItem("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);
          final Range range = selection_feature.getMaxRawRange();
tjc's avatar
tjc committed
          final YesNoDialog dialog =
                new YesNoDialog (getParentFrame (),
                                 "Are you sure you want to reverse complement this " +
                                 "region "+ range.getStart()+".."+
                                            range.getEnd()+"?");
tjc's avatar
tjc committed
         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

tjc's avatar
tjc committed
    
    if(Options.getOptions().getPropertyTruthValue("val_mode"))
    {
      add(edit_feature_item);
      add(edit_subsequence_item);
      addSeparator();
    }
    
    if(Options.getOptions().getUndoLevels() > 0) 
    {
      add(undo_item);
tjc's avatar
tjc committed
      add(redo_item);
tjc's avatar
tjc committed
    if(!Options.getOptions().getPropertyTruthValue("val_mode"))
    {
      add(edit_feature_item);
      add(edit_subsequence_item);
      addSeparator();
    }
    add(find_and_replace_qualifier_item);
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);
tjc's avatar
tjc committed
    feature_menu.add(convert_keys_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);
    }
    
    addSeparator();
    add(edit_header_item);

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().
   **/
tjc's avatar
tjc committed
  protected 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
  }

tjc's avatar
tjc committed
  private static void redo(final JFrame frame, final Selection selection,
      final EntryGroup entry_group)
  {
    // undo disabled
    if(Options.getOptions().getUndoLevels() == 0)
      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();

    if(!entry_group.getActionController().redo())
      new MessageDialog(frame, "sorry - no further redo 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));
    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)
    {
      final JPanel msgPanel = new JPanel(new BorderLayout());
      msgPanel.add(new JLabel("warning: only editing the first " +
          MAX_SELECTED_FEATURES + " selected features"), BorderLayout.CENTER);
      final JCheckBox allFeatures = new JCheckBox("ignore this and show all",false);
      msgPanel.add(allFeatures, BorderLayout.SOUTH);
      
      int val = JOptionPane.showConfirmDialog(frame, 
          msgPanel, 
          features_to_edit.size()+" features selected", 
          JOptionPane.OK_CANCEL_OPTION, 
          JOptionPane.WARNING_MESSAGE);

      if(val == JOptionPane.CANCEL_OPTION)
      {
        frame.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        return;
      }
      if(allFeatures.isSelected())
      {
        if(features_to_edit.size() > 50)
        {
          val = JOptionPane.showConfirmDialog(frame, 
              "warning: about to open "+features_to_edit.size()+" edit windows", 
            features_to_edit.size()+" features selected", 
            JOptionPane.OK_CANCEL_OPTION, 
            JOptionPane.WARNING_MESSAGE);
          if(val == JOptionPane.CANCEL_OPTION)
          {
            frame.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            return;
          }
        }
        MAX_SELECTED_FEATURES = features_to_edit.size();
      }
    }
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)
    {
      if(geneBuilderHash == null)
         geneBuilderHash = new Hashtable();
      
      final String gene = 
        ((GFFStreamFeature)selection_feature.getEmblFeature()).getChadoGene().getGeneUniqueName();
      
      
      if(geneBuilderHash.containsKey(gene) &&
         JOptionPane.showConfirmDialog(null, 
         "Show gene builder already open\nfor this gene model?", gene, 
         JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION)
      {
        ((GeneBuilderFrame)geneBuilderHash.get(gene)).toFront();
      }
      else
      {
tjc's avatar
tjc committed
        if(System.getProperty("basic") == null ||
           System.getProperty("basic").equals("false"))
tjc's avatar
tjc committed
        {
          final GeneBuilderFrame gbFrame = 
            new GeneBuilderFrame(selection_feature, entry_group,
                                 selection, goto_event_source);
          gbFrame.addGeneBuilderHash(geneBuilderHash);
          geneBuilderHash.put(gene, gbFrame);
        }
        else
          new BasicGeneBuilderFrame(selection_feature, entry_group,
              selection, null);
      return false;
    }
    else
    {
      final JFrame edit_frame = new JFrame("Artemis Feature Edit: " + 
           selection_feature.getIDString() +
           (selection_feature.isReadOnly() ?
               "  -  (read only)" :