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.
   **/
tjc's avatar
tjc committed
  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)" :
               ""));
       
       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")) 
        {
          new MessageDialog(frame,"The features in a merge should be "+
                            DatabaseDocument.EXONMODEL+
                            " or pseudogenic_exon features");
          return;
        }
        
tjc's avatar
tjc committed
        gffMergeFeatures(features_to_merge, merge_feature, 
                         selection, entry_group);
tjc's avatar
tjc committed
        
        entry_group.getActionController().endAction();
tjc's avatar
tjc committed
        return;
tjc's avatar
tjc committed
        
      try 
      {
        new_feature = merge_feature.duplicate();
      }
      catch(ReadOnlyException e) 
      {
tjc's avatar
tjc committed
        final String message =
          "one or more of the features is read-only or is in a " +
          "read-only entry - cannot continue";
        new MessageDialog(frame, message);
tjc's avatar
tjc committed
        return;
      }

      for(int i = 1; i < features_to_merge.size(); ++i) 
      {
        final Feature this_feature = features_to_merge.elementAt(i);
        final QualifierVector qualifiers = this_feature.getQualifiers();
tjc's avatar
tjc committed

        for(int j = 0; j < qualifiers.size(); ++j)
        {
tjc's avatar
tjc committed
          final Qualifier this_qualifier = (Qualifier)qualifiers.elementAt(j);
tjc's avatar
tjc committed

          try 
          {
            new_feature.addQualifierValues(this_qualifier);
          }
          catch(EntryInformationException e) 
          {
            try 
            {
              new_feature.removeFromEntry();
            } 
            catch(ReadOnlyException _) 
            {
tjc's avatar
tjc committed
              // give up ...
            }
            final String message =
              "destination entry does not support all the qualifiers " +
              "needed by " + this_feature.getIDString();
            new MessageDialog(frame, message);
          } 
          catch(ReadOnlyException e) 
          {
tjc's avatar
tjc committed
            final String message = "the new feature is read-only so " +
              "some qualifiers have been lost";
            new MessageDialog(frame, message);
tjc's avatar
tjc committed
          }
        }

        final FeatureSegmentVector segments = this_feature.getSegments();
tjc's avatar
tjc committed

        for(int j = 0; j < segments.size(); ++j)
        {
tjc's avatar
tjc committed
          final FeatureSegment this_segment =
tjc's avatar
tjc committed

          final Range this_range = this_segment.getRawRange();
tjc's avatar
tjc committed

          try 
          {
            new_feature.addSegment(this_range);
          }
          catch(ReadOnlyException e) 
          {
tjc's avatar
tjc committed
            final String message =
              "merging failed because the entry is read-only";
            new MessageDialog(frame, message);
            try 
            {
              new_feature.removeFromEntry();
tjc's avatar
tjc committed
            }
            catch(ReadOnlyException _) {}
tjc's avatar
tjc committed
          }
        }
      }

      // this is set to true when a merge is done in the next (inner) loop
      boolean keep_looping = true;

      // this is a bit inefficient, but there aren't normally many segments
  LOOP:
tjc's avatar
tjc committed
        final FeatureSegmentVector feature_segments =
tjc's avatar
tjc committed

        keep_looping = false;

        // now merge overlapping ranges
        for(int i = 0; i < feature_segments.size() - 1; ++i)
        {
tjc's avatar
tjc committed
          final FeatureSegment this_segment =
                                         feature_segments.elementAt(i);
          final MarkerRange this_range = this_segment.getMarkerRange();
tjc's avatar
tjc committed

          final FeatureSegment next_segment =
                                     feature_segments.elementAt(i + 1);
          final MarkerRange next_range = next_segment.getMarkerRange();
tjc's avatar
tjc committed

          // if it overlaps the next Range then merge it
          if(this_range.overlaps(next_range) &&
              this_segment.getFrameID() == next_segment.getFrameID()) 
          {
            try 
            {
tjc's avatar
tjc committed
              final Range new_range =
                this_range.combineRanges(next_range, false).getRawRange();
              new_feature.addSegment(new_range);
              new_feature.removeSegment(this_segment);
              new_feature.removeSegment(next_segment);
tjc's avatar
tjc committed

              // start again
              keep_looping = true;
              continue LOOP;
tjc's avatar
tjc committed
              final String message =
                "merging failed because the entry is read-only";
              new MessageDialog(frame, message);
            } 
            catch(LastSegmentException e) 
            {
              throw new Error("internal error - tried to remove " +
                              "last segment: " + e);
tjc's avatar
tjc committed
            }
          }
        }
      }

      boolean delete_old_features;

      if(Options.getOptions().isNoddyMode()) 
      {
tjc's avatar
tjc committed
        final YesNoDialog delete_old_dialog =
          new YesNoDialog(frame, "delete old features?");
tjc's avatar
tjc committed

        delete_old_features = delete_old_dialog.getResult();
      } 
      else
tjc's avatar
tjc committed
        delete_old_features = true;

      if(delete_old_features) 
      {
        if(getReadOnlyFeatures(features_to_merge).size() > 0)
          new MessageDialog(frame, "deletion failed because the features " +
                            "are read-only");
        else
        {
          for(int i = 0; i < features_to_merge.size(); ++i) 
          {
            try 
            {
              features_to_merge.elementAt(i).removeFromEntry();
            }
            catch(ReadOnlyException e) 
            {
              new MessageDialog(frame, "deletion failed one or more of the " +
                                "features are read-only");
tjc's avatar
tjc committed
            }
          }
        }
      }
      selection.set(new_feature);
    } 
    finally 
    {
      entry_group.getActionController().endAction();
tjc's avatar
tjc committed
    }
  }
tjc's avatar
tjc committed
  
  /**
   * Merge features / gene model - creating gene model if not already present
   * @param features_to_merge
   * @param merge_feature
   * @param selection
   * @param entry_group
   */
tjc's avatar
tjc committed
  public static void gffMergeFeatures(final FeatureVector features_to_merge,
tjc's avatar
tjc committed
                                final Feature merge_feature,
                                final Selection selection,
                                final EntryGroup entry_group)
  {
    try
    {
tjc's avatar
tjc committed
      Qualifier parentQualifier;
tjc's avatar
tjc committed
      ChadoCanonicalGene chadoGene = null;
tjc's avatar
tjc committed
      ChadoCanonicalGene chadoGene2 = null;
tjc's avatar
tjc committed
      String transcriptId = null;
tjc's avatar
tjc committed
      java.util.List geneModels = getGeneModels(features_to_merge);
      
      if(geneModels.size() == 0)
tjc's avatar
tjc committed
      {
        // create gene model
tjc's avatar
tjc committed
        final Location geneLocation = new Location(selection.getSelectionRange());
tjc's avatar
tjc committed
       
tjc's avatar
tjc committed
        final String parentId = GeneUtils.getUniqueName(
                 merge_feature.getEmblFeature())+":gene";
        final QualifierVector qualifiers = new QualifierVector();
tjc's avatar
tjc committed
        qualifiers.setQualifier(new Qualifier("ID", parentId));
        
        // create gene
        final Key key;
        if(merge_feature.getKey().getKeyString().equals("pseudogenic_exon"))
          key = new Key("pseudogene");
        else
          key = new Key("gene");
        
tjc's avatar
tjc committed
        Feature parentGene = merge_feature.getEntry().createFeature(key, 
tjc's avatar
tjc committed
                                                 geneLocation, qualifiers);
        
        chadoGene = new ChadoCanonicalGene();
        chadoGene.setGene(parentGene.getEmblFeature());
        ((uk.ac.sanger.artemis.io.GFFStreamFeature)
            (parentGene.getEmblFeature())).setChadoGene(chadoGene);
        
        // create transcript
        Feature transcript = GeneViewerPanel.createTranscript(chadoGene, entry_group);
        ((uk.ac.sanger.artemis.io.GFFStreamFeature)
            (transcript.getEmblFeature())).setChadoGene(chadoGene);
tjc's avatar
tjc committed
        transcriptId = GeneUtils.getUniqueName(transcript.getEmblFeature());
tjc's avatar
tjc committed
        
        parentQualifier = new Qualifier("Parent", transcriptId);
        merge_feature.setQualifier(parentQualifier);
      }
      else
tjc's avatar
tjc committed
      { 
        boolean isMultipleTranscript = false;
        for(int i=0; i<geneModels.size(); i++)
        {
          if(((ChadoCanonicalGene)geneModels.get(i)).getTranscripts().size() != 1)
            isMultipleTranscript = true;
        }
tjc's avatar
tjc committed
        
tjc's avatar
tjc committed
        if(features_to_merge.size() > 2 || 
           geneModels.size() > 2 ||
           isMultipleTranscript)
        {
          JOptionPane.showMessageDialog(null,
              "This option cannot be used to merge more than 2 gene models.\n"+
              "Select two exons in two gene models to be merged.\n"+
              "The gene models must have just one transcript.");
          return;
        }
        
        logger4j.debug("Found "+geneModels.size()+" gene models for merging");
        
        if(geneModels.size() == 2)
        {
          parentQualifier = merge_feature.getQualifierByName("Parent");
          transcriptId = (String)parentQualifier.getValues().get(0);
          chadoGene = ((GFFStreamFeature)merge_feature.getEmblFeature()).getChadoGene();
          
          final String chadoGeneName = chadoGene.getGeneUniqueName();
          for(int i=0; i<geneModels.size(); i++)
          {
            final ChadoCanonicalGene thisChadoGene = (ChadoCanonicalGene)geneModels.get(i);
            if(!thisChadoGene.equals(chadoGeneName))
              chadoGene2 = thisChadoGene;
          }
        }
        else
        {
          chadoGene = (ChadoCanonicalGene)geneModels.get(0);
          transcriptId = GeneUtils.getUniqueName(
              (uk.ac.sanger.artemis.io.Feature)chadoGene.getTranscripts().get(0));
          parentQualifier = new Qualifier("Parent", transcriptId);
          merge_feature.setQualifier(parentQualifier);
        }
        
        // TODO - merge transcript / peptide qualifiers into chadoGene ??
tjc's avatar
tjc committed
      }
      
      final RangeVector ranges = new RangeVector();
      java.util.Hashtable id_range_store = 
             ((GFFStreamFeature)merge_feature.getEmblFeature()).getSegmentRangeStore();
      
      for(int i=1; i< features_to_merge.size(); i++)
      {
        final Feature this_feature = features_to_merge.elementAt(i);
        final FeatureSegmentVector segments = this_feature.getSegments();
        
tjc's avatar
tjc committed
        this_feature.setQualifier(parentQualifier);
tjc's avatar
tjc committed
        
        for(int j = 0; j < segments.size(); ++j)
        {
          final FeatureSegment this_segment = segments.elementAt(j);
          ranges.add(this_segment.getRawRange());
        }         
      }
      
      for(int i=0; i<features_to_merge.size(); i++)
      {
        final Feature this_feature = features_to_merge.elementAt(i);
tjc's avatar
tjc committed
        // remove the duplicate feature
        if(i > 0)
tjc's avatar
tjc committed
          this_feature.getEntry().remove(this_feature, false);
tjc's avatar
tjc committed
      }

tjc's avatar
tjc committed
      // add the segments
tjc's avatar
tjc committed
      //uk.ac.sanger.artemis.chado.ChadoTransactionManager.addSegments = false;
tjc's avatar
tjc committed
      for(int i = 0; i < ranges.size(); i++)
      {
        final Range range = (Range)ranges.get(i);
tjc's avatar
tjc committed
        final String segId = chadoGene.autoGenerateSplicedFeatureName(transcriptId);
        id_range_store.put(segId,range);
        ((GFFStreamFeature)merge_feature.getEmblFeature()).setSegmentRangeStore(id_range_store);
        
tjc's avatar
tjc committed
        merge_feature.addSegment(range);
      }
tjc's avatar
tjc committed
      //uk.ac.sanger.artemis.chado.ChadoTransactionManager.addSegments = true;
tjc's avatar
tjc committed
      
      // set the new ID for the joined feature
      final String ID = ((GFFStreamFeature)merge_feature.getEmblFeature()).getSegmentID(
tjc's avatar
tjc committed
                                         merge_feature.getLocation().getRanges());
      final Qualifier qualifier = new Qualifier("ID", ID);
tjc's avatar
tjc committed
      merge_feature.getEmblFeature().setQualifier(qualifier);
      chadoGene.addSplicedFeatures(transcriptId, merge_feature.getEmblFeature(), true);
      ((GFFStreamFeature)merge_feature.getEmblFeature()).setChadoGene(chadoGene);
tjc's avatar
tjc committed
      
      if(chadoGene2 != null)
      {
        logger4j.debug("Now DELETE "+chadoGene2.getGeneUniqueName());
        GeneUtils.deleteAllFeature((Feature)chadoGene2.getGene().getUserData(), chadoGene2);
      }
      if(chadoGene != null)
      {
        logger4j.debug("Check gene boundaries of: "+chadoGene.getGeneUniqueName());
        GeneUtils.checkGeneBoundary(chadoGene);
      }
tjc's avatar
tjc committed
    }
    catch(ReadOnlyException e)
    {
      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;
    }
    catch(InvalidRelationException ire){ ire.printStackTrace(); }
    catch(EntryInformationException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    catch(OutOfRangeException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
tjc's avatar
tjc committed

tjc's avatar
tjc committed
  /**
   * Get the chado gene models for a list of features
   * @param features
   * @return
   */
  private static java.util.List getGeneModels(final FeatureVector features)
  {
    final java.util.List geneModels = new Vector();
    final java.util.List geneModelNames = new Vector();
    for(int i=0; i< features.size(); i++)
    {
      if(features.elementAt(i).getEmblFeature() instanceof GFFStreamFeature)
      {
        final GFFStreamFeature this_feature = 
           (GFFStreamFeature)features.elementAt(i).getEmblFeature();
      
        if(this_feature.getChadoGene() != null)
        {
          final String this_name = 
            this_feature.getChadoGene().getGeneUniqueName();
          if(!geneModelNames.contains(this_name))
          {
            geneModels.add(this_feature.getChadoGene());
            geneModelNames.add(this_name);
          }
        }
      }
    }
    return geneModels;
  }
  
tjc's avatar
tjc committed
  /**
   *  If the selection contains exactly two segments and those segments are
   *  adjacent in the same feature, split the feature into two pieces.  The
   *  orignal feature is truncated and a new feature is created.  The
   *  qualifiers of the old feature are copied to new feature.
   *  @param frame The JFrame to use for MessageDialog components.
   *  @param selection The Selection containing the segments to unmerge.
   *  @param entry_group Used to get the ActionController for calling
   *    startAction() and endAction().
   **/
tjc's avatar
tjc committed
  private static void unmergeFeature(final JFrame frame,
tjc's avatar
tjc committed
                              final Selection selection,
tjc's avatar
tjc committed
                              final EntryGroup entry_group) 
  {
    try 
    {
tjc's avatar
tjc committed
      entry_group.getActionController ().startAction ();

      final FeatureSegmentVector selected_segments =
        selection.getSelectedSegments ();

tjc's avatar
tjc committed
      if (selected_segments.size () != 2) 
      {
tjc's avatar
tjc committed
        final String message =
          "you need to select exactly two exons use unmerge";
tjc's avatar
tjc committed
        new MessageDialog(frame, message);
tjc's avatar
tjc committed
        return;
      }

      FeatureSegment first_segment = selected_segments.elementAt (0);
      FeatureSegment second_segment = selected_segments.elementAt (1);

tjc's avatar
tjc committed
      if(first_segment.getFeature () != second_segment.getFeature ()) 
      {
tjc's avatar
tjc committed
        final String message =
          "you need to select two exons from the same feature to use unmerge";
        new MessageDialog (frame, message);
        return;
      }

      final Feature segment_feature = first_segment.getFeature ();

      final FeatureSegmentVector all_feature_segments =
        segment_feature.getSegments ();

      int index_of_first_segment =
        all_feature_segments.indexOf (first_segment);
      int index_of_second_segment =
        all_feature_segments.indexOf (second_segment);

tjc's avatar
tjc committed
      if(index_of_first_segment - index_of_second_segment < -1 ||
         index_of_first_segment - index_of_second_segment > 1) 
      {
tjc's avatar
tjc committed
        final String message =
          "you need to select two adjacent exons to use unmerge";
        new MessageDialog (frame, message);
        return;
      }

tjc's avatar
tjc committed
      if(index_of_second_segment <  index_of_first_segment) 
      {
tjc's avatar
tjc committed
        // swap the segments for consistency
        final FeatureSegment temp_segment = first_segment;
        final int temp_segment_index = index_of_first_segment;

        first_segment = second_segment;
        index_of_first_segment = index_of_second_segment;

        second_segment = temp_segment;
        index_of_second_segment = temp_segment_index;
      }

tjc's avatar
tjc committed
      try 
      {
tjc's avatar
tjc committed
        final Feature new_feature;
        if(segment_feature.getEmblFeature() instanceof GFFStreamFeature)
        {
          final FeatureVector chadoGenes = new FeatureVector();
          chadoGenes.add(segment_feature);
          final Vector duplicateGenes = duplicateGeneFeatures(frame, chadoGenes, entry_group);
          
          // get the new duplicate spliced feature
          ChadoCanonicalGene chado_gene = (ChadoCanonicalGene)duplicateGenes.get(0);
          // assumes single transcript
          uk.ac.sanger.artemis.io.Feature transcript = 
            (uk.ac.sanger.artemis.io.Feature)chado_gene.getTranscripts().get(0);
          // get the spliced feature with the same key as the segment
          // selected to unmerge
          uk.ac.sanger.artemis.io.Feature spliced = (uk.ac.sanger.artemis.io.Feature)
            chado_gene.getSpliceSitesOfTranscript(GeneUtils.getUniqueName(transcript), 
              first_segment.getFeature().getKey().getKeyString()).get(0);
          new_feature = (Feature)spliced.getUserData();
        }
        else
          new_feature = segment_feature.duplicate(true);
tjc's avatar
tjc committed
        // we set the Selection later
        selection.clear ();

        // delete the segments starting at index_of_second_segment from
        // segment_feature and delete the segments up to (and including)
        // index_of_first_segment from new_feature

tjc's avatar
tjc committed
        for(int i = all_feature_segments.size() - 1;
            i >= index_of_second_segment; --i)
        {
tjc's avatar
tjc committed
          segment_feature.getSegments ().elementAt (i).removeFromFeature ();
        }

        // remove the first segment of new_feature index_of_first_segment times
tjc's avatar
tjc committed
        for (int i = 0; i <= index_of_first_segment; ++i) 
tjc's avatar
tjc committed
          new_feature.getSegments ().elementAt (0).removeFromFeature ();
tjc's avatar
tjc committed
        if(segment_feature.getEmblFeature() instanceof GFFStreamFeature)
        {
tjc's avatar
tjc committed
          GeneUtils.checkGeneBoundary(
              ((GFFStreamFeature)segment_feature.getEmblFeature()).getChadoGene());
          GeneUtils.checkGeneBoundary(
              ((GFFStreamFeature)new_feature.getEmblFeature()).getChadoGene());
tjc's avatar
tjc committed
        }
        selection.set (segment_feature.getSegments ().lastElement ());
        selection.add (new_feature.getSegments ().elementAt (0));
tjc's avatar
tjc committed
      } 
      catch (ReadOnlyException e) 
      {
tjc's avatar
tjc committed
        final String message =
          "the selected exons (in " +
          segment_feature.getIDString () +
          ") are in a read only entry - cannot continue";
        new MessageDialog (frame, message);
tjc's avatar
tjc committed
      } 
      catch (LastSegmentException e) 
      {
tjc's avatar
tjc committed
        throw new Error ("internal error - unexpected exception: " + e);
      }
tjc's avatar
tjc committed
    } 
    finally 
    {
tjc's avatar
tjc committed
      entry_group.getActionController ().endAction ();
    }
  }

tjc's avatar
tjc committed
  /**
   *  If the selection contains exactly one feature this routine will
   *  remove all the joins.
   *  @param frame The JFrame to use for MessageDialog components.
   *  @param selection The Selection containing the segments to unmerge.
   *  @param entry_group Used to get the ActionController for calling
   *    startAction() and endAction().
   **/
tjc's avatar
tjc committed
  private static void unmergeAllFeature(final JFrame frame,
tjc's avatar
tjc committed
                                 final Selection selection,
                                 final EntryGroup entry_group) 
  {
    try 
    {
      entry_group.getActionController ().startAction ();

      final FeatureVector delete_features = selection.getAllFeatures();
      if(delete_features.size() > 1)
      {
        new MessageDialog (frame, "Select just one feature");             
        return;
      }

      final FeatureSegmentVector selected_segments =
                   delete_features.elementAt(0).getSegments();
      try  
      {
        Vector new_features = new Vector();
        Vector segment_to_remove = new Vector();

        FeatureSegment[] selected_segments_array = new FeatureSegment[selected_segments.size()];
        for(int i=0; i<selected_segments.size(); i++)
        {
          FeatureSegment seg   = selected_segments.elementAt(i);
          int index_of_segment = selected_segments.indexOf(seg); 
          selected_segments_array[index_of_segment] = seg;
        }
        
        for(int i=0; i<selected_segments.size()-1; i++)
        {
tjc's avatar
tjc committed
          FeatureSegment segment  = selected_segments_array[i];
tjc's avatar
tjc committed
          Feature segment_feature = segment.getFeature();
tjc's avatar
tjc committed
          final Feature new_feature = segment_feature.duplicate();
tjc's avatar
tjc committed
          segment_to_remove.add(segment);

          FeatureSegmentVector new_segments = new_feature.getSegments();

          Vector removals = new Vector();
          for(int j = 0 ; j <new_segments.size(); j++)
          {
            if(i != j)
tjc's avatar
tjc committed
              removals.add(new_segments.elementAt(j));
tjc's avatar
tjc committed
          }

          for(int j = 0; j < removals.size(); j++)
            new_feature.removeSegment( (FeatureSegment)removals.get(j) );  

          new_features.add(new_feature);
        }

tjc's avatar
tjc committed
        final int size = segment_to_remove.size();
        for(int i=0; i<size; i++)
tjc's avatar
tjc committed
        {
tjc's avatar
tjc committed
          selected_segments_array[size-1].getFeature().removeSegment(
                           (FeatureSegment)segment_to_remove.get(i) );
tjc's avatar
tjc committed
        Feature feature;
tjc's avatar
tjc committed
        for(int i=0; i<new_features.size(); i++)
tjc's avatar
tjc committed
        {
          feature = (Feature)new_features.get(i);
          selection.add(feature.getSegments().elementAt(0));
          
          // set GFF ID's
          if(feature.getEmblFeature() instanceof GFFStreamFeature)
            setGffId(selected_segments_array[0].getFeature(), feature);
        }
        
        // set GFF ID's
        feature = selected_segments_array[size-1].getFeature();
        if(feature.getEmblFeature() instanceof GFFStreamFeature)
          setGffId(selected_segments_array[0].getFeature(), feature);
tjc's avatar
tjc committed
      } 
      catch(ReadOnlyException e)
      {
        final String message =
          "the selected exons (in " +
          delete_features.elementAt(0).getIDString () +
          ") are in a read only entry - cannot continue";
        new MessageDialog (frame, message);
      }
      catch (LastSegmentException e) 
      {
        throw new Error ("internal error - unexpected exception: " + e);
      }
    } 
    finally 
    {
      entry_group.getActionController ().endAction ();
    }
  }

tjc's avatar
tjc committed
  /**
   * Sets the ID based on the new features ranges.
   * @param feature_original
   * @param feature_new
   */
  private static void setGffId(final Feature feature_original,
                        final Feature feature_new)
  {
    RangeVector ranges = feature_new.getLocation().getRanges();
    GFFStreamFeature gff_feature = 
       (GFFStreamFeature)feature_original.getEmblFeature();
    String id1 = gff_feature.getSegmentID(ranges);
    
    try
    {
      feature_new.getEmblFeature().setQualifier(new Qualifier("ID", id1));
    }
    catch(ReadOnlyException e)
    {
      e.printStackTrace();
    }
    catch(EntryInformationException e)
    {
      e.printStackTrace();
    }
    
  }
tjc's avatar
tjc committed

tjc's avatar
tjc committed
  /**
   *  Create a QualifierEditor JFrame that acts on the selected features.
   *  @param frame The JFrame to use for MessageDialog components.
   **/
  private void addQualifiers (final JFrame frame, final Selection selection) {
    if (!checkForSelectionFeatures (frame, selection)) {
      return;
    }

    final FeatureVector selected_features = selection.getAllFeatures ();

    if (getReadOnlyFeatures (selected_features).size () > 0) {
      new MessageDialog (frame,
                         "one or more of the selected features is read-only " +
                         "- cannot continue");
      return;
    }

    final QualifierEditor qualifier_editor =
      new QualifierEditor (selected_features, getEntryGroup ());

    qualifier_editor.setVisible (true);
  }

  /**
   *  Offer the user a choice of qualifier to remove from the selected features
   **/
  private void removeQualifier (final JFrame frame, final Selection selection) {
    if (!checkForSelectionFeatures (frame, selection)) {
      return;
    }

    final FeatureVector selected_features = selection.getAllFeatures ();

    final StringVector qualifier_names =
      Feature.getAllQualifierNames (selected_features);

    if (qualifier_names.size () == 0) {
      new MessageDialog (getParentFrame (), "feature has no qualifiers");
      return;
    }

    final ChoiceFrame choice_frame =
      new ChoiceFrame ("Select a qualifer name", qualifier_names);

    final JComboBox choice = choice_frame.getChoice ();


    final int MAX_VISIBLE_ROWS = 30;
    
    choice.setMaximumRowCount (MAX_VISIBLE_ROWS);
    
    choice.addItemListener (new ItemListener () {
      public void itemStateChanged (ItemEvent _) {
        removeQualifierFromFeatures (selected_features,
                                     (String) choice.getSelectedItem ());
        choice_frame.setVisible (false);
        choice_frame.dispose ();
      }
    });

    choice_frame.getOKButton ().addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent _) {
        removeQualifierFromFeatures (selected_features,
                                     (String) choice.getSelectedItem ());
      }
    });

    choice_frame.setVisible (true);
  }
tjc's avatar
tjc committed
  private void convertKeys(final JFrame frame, 
                           final Selection selection)
  {
    if (!checkForSelectionFeatures (frame, selection)) 
      return;

    final FeatureVector selected_features = selection.getAllFeatures ();
    
    Entry default_entry = getEntryGroup().getDefaultEntry();
    if(default_entry == null)
      default_entry = getEntryGroup().elementAt(0);
  
    final EntryInformation default_entry_information =
                        default_entry.getEntryInformation();

    final KeyChoice key_selector = new KeyChoice(default_entry_information);
    final String options[] = { "Convert", "Cancel" };
    
    final int opt = JOptionPane.showOptionDialog(frame, 
        key_selector, "Convert Key(s) of Selected Features", 
        JOptionPane.OK_CANCEL_OPTION,
        JOptionPane.QUESTION_MESSAGE, null, options, options[0]);

    if(opt == 1)
      return;
    
    entry_group.getActionController ().startAction ();
    try
    {
      for(int i=0; i<selected_features.size(); i++)
      {
        Feature feature = selected_features.elementAt(i);
        feature.set(key_selector.getSelectedItem(), 
              feature.getLocation(), feature.getQualifiers());
      }
    }
    catch(ReadOnlyException e)
    {
      JOptionPane.showMessageDialog(frame, 
          "Cannot convert read-only features.", 
          "Error Converting Key(s)", JOptionPane.ERROR_MESSAGE);
    }
    catch(EntryInformationException e)
    {
      JOptionPane.showMessageDialog(frame, 
          e.getMessage(), 
          "Error Converting Key(s)", JOptionPane.ERROR_MESSAGE);
    }
    catch(OutOfRangeException e)
    {
      JOptionPane.showMessageDialog(frame, 
          e.getMessage(), 
          "Error Converting Key(s)", JOptionPane.ERROR_MESSAGE);
    }
    finally
    {
      entry_group.getActionController ().endAction();
    }
  }
  
tjc's avatar
tjc committed
  /**
   * Offer the user a choice of qualifier to convert the name of
   * from the selected features
   * @param frame
   * @param selection
   */
  private void convertQualifier (final JFrame frame, 
                                 final Selection selection) 
  {
    if (!checkForSelectionFeatures (frame, selection)) 
      return;

    final FeatureVector selected_features = selection.getAllFeatures ();
    final StringVector qualifier_names =
      Feature.getAllQualifierNames (selected_features);

    if(qualifier_names.size () == 0) 
    {
      new MessageDialog (getParentFrame (), "feature has no qualifiers");
      return;
    }

    Box yBox = Box.createVerticalBox();
    final JComboBox convertFrom = new JComboBox(qualifier_names);
    final QualifierChoice convertTo = new QualifierChoice(
        getEntryGroup().getDefaultEntry().getEntryInformation(),
        selected_features.elementAt(0).getKey(),null,
        false);
    
    Box xBox = Box.createHorizontalBox();
    xBox.add(new JLabel("Convert all qualifiers of type:"));
    xBox.add(Box.createHorizontalGlue());
    yBox.add(xBox);
    yBox.add(convertFrom);
    xBox = Box.createHorizontalBox();
    xBox.add(new JLabel("To:"));
    xBox.add(Box.createHorizontalGlue());
    yBox.add(xBox);
    yBox.add(convertTo);
    
    int select = JOptionPane.showConfirmDialog(frame, yBox, 
        "Convert Qualifiers", 
        JOptionPane.OK_CANCEL_OPTION, 
        JOptionPane.QUESTION_MESSAGE);
    
    String oldQualifierName = (String)convertFrom.getSelectedItem();
    String newQualifierName = (String)convertTo.getSelectedItem();
    
    if(select == JOptionPane.CANCEL_OPTION ||
        oldQualifierName.equals(newQualifierName))
      return;
    
    try
    {
      for(int i=0; i<selected_features.size(); i++)
      {
        Feature feature = selected_features.elementAt(i);
        QualifierVector qualifiers = feature.getQualifiers();
        int index = qualifiers.indexOfQualifierWithName(oldQualifierName);
        if(index == -1)
          continue;
        StringVector values = feature.getValuesOfQualifier(oldQualifierName);
        Qualifier newQualifier = new Qualifier(newQualifierName, values);
        qualifiers.add(index, newQualifier);
        qualifiers.removeQualifierByName(oldQualifierName);
      }
    }
    catch(EntryInformationException e)
    {
      e.printStackTrace();
    }

  }
tjc's avatar
tjc committed

  /**
   *  Remove the qualifier given by qualifier_name from the given features.
   *  Silently ignore features that don't have that qualifier.  Warn the user
   *  about read-only features.
   **/
  private void removeQualifierFromFeatures (final FeatureVector features,
                                            final String qualifier_name) {
    boolean found_read_only = false;

    try {
      entry_group.getActionController ().startAction ();

      for (int i = 0 ; i < features.size () ; ++i) {
        final Feature this_feature = features.elementAt (i);
        try {
          this_feature.removeQualifierByName (qualifier_name);
        } catch (OutOfDateException _) {
          // ignore
        } catch (EntryInformationException _) {
          // ignore
        } catch (ReadOnlyException _) {
          found_read_only = true;
        }
      }

      if (found_read_only) {
        final String message =
          "ignored one or more read-only features";
        new MessageDialog (getParentFrame (), message);
      }
    } finally {
      entry_group.getActionController ().endAction ();
    }
  }
tjc's avatar
tjc committed
  protected static Vector duplicateGeneFeatures(final JFrame frame,
tjc's avatar
tjc committed
      final FeatureVector features,
      final EntryGroup entry_group) 
  {
tjc's avatar
tjc committed
    if (getReadOnlyFeatures (features).size () > 0)
tjc's avatar
tjc committed
      new MessageDialog (frame,
                         "one or more of the selected features is read-only " +
                         "- cannot continue");
tjc's avatar
tjc committed
      return null;
tjc's avatar
tjc committed

tjc's avatar
tjc committed
    return GeneUtils.duplicateGeneModel(frame, features, entry_group);
tjc's avatar
tjc committed

  /**
   *  Duplicate the selected Feature objects.  If there are selected segments
   *  then the owning Feature of each segment will be duplicated.
   *  @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 duplicateFeatures (final JFrame frame,
tjc's avatar
tjc committed
                                 final Selection selection,
                                 final EntryGroup entry_group) {
    try {
      entry_group.getActionController ().startAction ();

      if (getReadOnlyFeatures (selection.getAllFeatures ()).size () > 0) {
        new MessageDialog (frame,
                           "one or more of the selected features is read-only " +
                           "- cannot continue");
        return;
      }

      if (Options.getOptions ().isNoddyMode ()) {
        final YesNoDialog dialog =
          new YesNoDialog (frame,
                           "Are you sure you want to duplicate the selected " +
                           "features?");

        if (!dialog.getResult ()) {
          return;
        }
      } else {
        if (!checkForSelectionFeatures (frame, selection,
                                        100, "really duplicate all (>100) " +
                                        "selected features?")) {
          return;
        }
      }

      final FeatureVector features_to_duplicate =
        selection.getAllFeatures ();

tjc's avatar
tjc committed
      FeatureVector chadoGenes = null;
      Vector chadoGeneNames = null;
      for (int i = 0 ; i < features_to_duplicate.size () ; ++i) 
      {
tjc's avatar
tjc committed
        final Feature this_feature = features_to_duplicate.elementAt (i);
tjc's avatar
tjc committed
        
        if(this_feature.getEmblFeature() instanceof GFFStreamFeature &&
           ((GFFStreamFeature)this_feature.getEmblFeature()).getChadoGene() != null)
        {
          if(chadoGenes == null)
            chadoGenes = new FeatureVector();
          if(chadoGeneNames == null)
            chadoGeneNames = new Vector();
          
          final String geneName =
            ((GFFStreamFeature)this_feature.getEmblFeature()).getChadoGene().getGeneUniqueName();
          
          if(!chadoGeneNames.contains(geneName))
          {
            chadoGenes.add(this_feature);
            chadoGeneNames.add(geneName);
          }
          continue;
        }
tjc's avatar
tjc committed

tjc's avatar
tjc committed
        try 
        {
          this_feature.duplicate (true);
tjc's avatar
tjc committed
        } catch (ReadOnlyException e) {
          final String message =
            "one of the selected features (" + this_feature.getIDString () +
            ")  is read only - cannot continue";
          new MessageDialog (frame, message);
          return;
        }
      }
tjc's avatar
tjc committed
      
      if(chadoGenes != null)
        duplicateGeneFeatures(frame, chadoGenes, entry_group);
tjc's avatar
tjc committed
    } finally {
      entry_group.getActionController ().endAction ();
    }
  }

  /**
   *  Delete the selected features.
   *  @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 deleteSelectedFeatures (final JFrame frame,
tjc's avatar
tjc committed
                                      final Selection selection,
tjc's avatar
tjc committed
      entry_group.getActionController ().startAction ();

      final FeatureVector features_to_delete = selection.getAllFeatures ();
      final String feature_count_string;
      if (features_to_delete.size () == 1)
tjc's avatar
tjc committed
        feature_count_string = "the selected feature";
tjc's avatar
tjc committed
        feature_count_string = features_to_delete.size () + " features";

      if (Options.getOptions ().isNoddyMode ()) 
      {
      	if(GeneUtils.isDatabaseEntry(entry_group))
      	{
      		Box boption = Box.createVerticalBox();
      		JCheckBox delete = new JCheckBox("permanently delete", 
      		  !Options.getOptions().getPropertyTruthValue("set_obsolete_on_delete"));
      		boption.add(new JLabel("Make "+feature_count_string+" obsolete?"));
      		boption.add(delete);
      		int res = JOptionPane.showConfirmDialog(frame, 
      				boption, "Make obsolete", JOptionPane.OK_CANCEL_OPTION, 
      				JOptionPane.QUESTION_MESSAGE);
      		if(res == JOptionPane.CANCEL_OPTION)
      			return;
      		
      		// make obsolete rather than permanently delete
      		if(!delete.isSelected())
      		{
      			for(int i=0; i<features_to_delete.size(); i++)
      			{
      				final Feature currentFeature = features_to_delete.elementAt(i);
      				try
							{
								currentFeature.setQualifier(new Qualifier("isObsolete", "true"));
	              PropertiesPanel.updateObsoleteSettings(
	      						(GFFStreamFeature)currentFeature.getEmblFeature());
							} 
      				catch (Exception e)
							{
Loading
Loading full blame...