diff --git a/uk/ac/sanger/artemis/components/genebuilder/ButtonPanel.java b/uk/ac/sanger/artemis/components/genebuilder/ButtonPanel.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8c6d97d1b2f8990dd5a60bbd1e63cf6387c3849
--- /dev/null
+++ b/uk/ac/sanger/artemis/components/genebuilder/ButtonPanel.java
@@ -0,0 +1,449 @@
+package uk.ac.sanger.artemis.components.genebuilder;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.JToolBar;
+
+import uk.ac.sanger.artemis.EntryGroup;
+import uk.ac.sanger.artemis.Feature;
+import uk.ac.sanger.artemis.components.MessageDialog;
+import uk.ac.sanger.artemis.components.TransferAnnotationTool;
+import uk.ac.sanger.artemis.io.ChadoCanonicalGene;
+import uk.ac.sanger.artemis.io.DatabaseDocumentEntry;
+import uk.ac.sanger.artemis.io.DocumentEntry;
+import uk.ac.sanger.artemis.io.EntryInformationException;
+import uk.ac.sanger.artemis.io.GFFStreamFeature;
+import uk.ac.sanger.artemis.io.Location;
+import uk.ac.sanger.artemis.io.LocationParseException;
+import uk.ac.sanger.artemis.io.Qualifier;
+import uk.ac.sanger.artemis.io.QualifierVector;
+import uk.ac.sanger.artemis.io.Range;
+import uk.ac.sanger.artemis.io.RangeVector;
+import uk.ac.sanger.artemis.sequence.MarkerRange;
+import uk.ac.sanger.artemis.util.DatabaseDocument;
+import uk.ac.sanger.artemis.util.OutOfRangeException;
+
+
+public class ButtonPanel extends JToolBar
+{
+  private static final long serialVersionUID = 1L;
+  private BasicGeneBuilderFrame gbFrame;
+  
+  public ButtonPanel(final BasicGeneBuilderFrame gbFrame, 
+                     final EntryGroup entry_group)
+  {
+    super();
+    
+    this.gbFrame = gbFrame;
+    
+    final JButton complement_button = new JButton("Complement");
+    add(complement_button);
+    complement_button.addActionListener(new ActionListener () 
+    {
+      public void actionPerformed(ActionEvent e) 
+      {
+        complementLocation();
+      }
+    });
+    
+    if(GeneUtils.isDatabaseEntry(getFeature().getEmblFeature()))
+    {
+      final JButton refresh_button = new JButton("Refresh");
+      add(refresh_button);
+      refresh_button.addActionListener(new ActionListener()
+      {
+        public void actionPerformed(ActionEvent e)
+        {
+          refresh();
+        }
+      });
+    }
+
+    final JButton grab_button = new JButton("Grab Range");
+    add(grab_button);
+    grab_button.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        grabSelectedRange();
+      }
+    });
+
+    final JButton remove_button = new JButton("Remove Range");
+    add(remove_button);
+    remove_button.addActionListener(new ActionListener() 
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        removeSelectedRange();
+      }
+    });
+
+    final JButton select_button = new JButton("Select Feature");
+    add(select_button);
+    select_button.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e) 
+      {
+        gbFrame.getSelection().set(getFeature());
+      }
+    });
+    
+    final JButton transferAnnotationBbutton = new JButton("TAT");
+    add(transferAnnotationBbutton);
+    transferAnnotationBbutton.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e) 
+      {
+        java.util.List geneNames = null;
+        if(gbFrame.getMatchForm() != null)
+            geneNames = gbFrame.getMatchForm().getGeneNameList();
+        
+        new TransferAnnotationTool(getFeature(), entry_group, geneNames);
+      }
+    });
+
+  }
+  
+  private Feature getFeature()
+  {
+    return gbFrame.getFeature();
+  }
+  
+  /**
+   *   Complement the current location_text.
+   **/
+  private void complementLocation() 
+  {
+    if(GeneUtils.isDatabaseEntry(getFeature().getEmblFeature()))
+    {
+      final ChadoCanonicalGene chadoGene = 
+        ((GFFStreamFeature)getFeature().getEmblFeature()).getChadoGene();
+      GeneUtils.complementGeneModel(chadoGene);
+      gbFrame.dispose(true);
+      return;  
+    }
+    
+    if(rationalizeLocation()) 
+    {
+      if(gbFrame.getLocationText().getText().startsWith("complement(")) 
+      {
+        final String new_text = gbFrame.getLocationText().getText().substring(11);
+        if (new_text.endsWith(")")) 
+        {
+          final String new_text2 =
+            new_text.substring(0, new_text.length () - 1);
+          gbFrame.getLocationText().setText(new_text2);
+        } 
+        else 
+          gbFrame.getLocationText().setText(new_text);
+      }
+      else 
+      {
+        final String new_text = gbFrame.getLocationText().getText ();
+        gbFrame.getLocationText().setText("complement(" + new_text + ")");
+      }
+    } 
+    else 
+      new MessageDialog(null, "complement failed - " +
+                        "current location cannot be parsed");
+  }
+  
+  /**
+   *  Attempt to parse the current location_text as a Location.  If it can be
+   *  parsed it will be canonicalized (ie. the complement, if any, will be
+   *  outermost).  Returns true if and only if the location_text could be
+   *  parsed.
+   **/
+  private boolean rationalizeLocation()
+  {
+    try 
+    {
+      final Location location = new Location(gbFrame.getLocationText().getText());
+      gbFrame.getLocationText().setText(location.toStringShort());
+      return true;
+    }
+    catch(LocationParseException e) 
+    {
+      return false;
+    }
+  }
+  
+  /**
+   * Refresh the annotation for the active feature
+   */
+  private void refresh()
+  {
+    Feature edit_feature = gbFrame.getFeature();
+    // refresh from the database
+    final DatabaseDocument originalDocument =
+      (DatabaseDocument)((DocumentEntry)edit_feature.getEmblFeature().getEntry()).getDocument();
+
+    final Set uniquenames = 
+      ((GFFStreamFeature)edit_feature.getEmblFeature()).getSegmentRangeStore().keySet();
+    final Iterator it = uniquenames.iterator();
+    final String uniquename = (String)it.next();
+    final DatabaseDocument newDocument = new DatabaseDocument(originalDocument,
+        uniquename, null, true, null);
+    newDocument.setLazyFeatureLoad(false);
+    newDocument.setReadChildren(false);
+    
+    try
+    {
+      DatabaseDocumentEntry dbentry = new DatabaseDocumentEntry(newDocument, null);
+      uk.ac.sanger.artemis.io.Feature databaseFeature = dbentry.getAllFeatures().featureAt(0);
+      
+      // compare timelastmodified
+      Qualifier qualifier = edit_feature.getQualifierByName("timelastmodified");
+      String active_timelastmodified = (String)qualifier.getValues().get(0);
+      qualifier = databaseFeature.getQualifierByName("timelastmodified");
+      String database_timelastmodified = (String)qualifier.getValues().get(0);
+      
+      if(active_timelastmodified.equals(database_timelastmodified))
+      {
+        JOptionPane.showMessageDialog(this, 
+            "No new changes found for the feature\n"+
+            uniquename+"\n"+
+            "in the database since:\n"+database_timelastmodified, 
+            "No Updates", JOptionPane.INFORMATION_MESSAGE);
+        return;
+      }
+      else
+      {
+        JOptionPane.showMessageDialog(this, 
+            "Changes found for the feature\n"+
+            uniquename+"\n"+
+            "in the database at:\n"+database_timelastmodified, 
+            "Changes Found", JOptionPane.INFORMATION_MESSAGE);
+      }
+      
+      final QualifierVector db_qv = databaseFeature.getQualifiers();
+      final QualifierVector qv = edit_feature.getQualifiers();
+      final QualifierVector new_qv = new QualifierVector();
+      
+      for(int i=0; i<qv.size(); i++)
+      {
+        Qualifier q = (Qualifier)qv.get(i);
+        if(q.getName().equals("Parent") ||
+           q.getName().equals("Derives_from") ||
+           q.getName().equals("ID")  )
+          new_qv.addQualifierValues(q);
+      }
+      
+      
+      for(int i=0; i<db_qv.size(); i++)
+      {
+        Qualifier q = (Qualifier)db_qv.get(i);
+        if(q.getName().equals("Parent") ||
+           q.getName().equals("Derives_from") ||
+           q.getName().equals("ID")  )
+          continue;
+        new_qv.add(q);  
+      }
+      
+      edit_feature.getQualifiers().removeAllElements();
+      edit_feature.getQualifiers().addAll(new_qv);
+      gbFrame.setQualifiers();
+    }
+    catch(EntryInformationException e) {}
+    catch(IOException e) {}    
+  }
+  
+  
+  /**
+   *  Add the currently selected range to location_text.
+   **/
+  private void grabSelectedRange() 
+  {
+    if(!rationalizeLocation ()) 
+    {
+      new MessageDialog(null,
+                        "grab failed - current location cannot be parsed");
+      return;
+    }
+
+    final Range selected_range = gbFrame.getSelection().getSelectionRange();
+
+    if(selected_range == null) 
+    {
+      new MessageDialog(null, "grab failed - nothing is selected");
+      return;
+    }
+
+    // save it in case it gets mangled
+    final String old_location_text = gbFrame.getLocationText().getText();
+
+    if (old_location_text.endsWith ("))")) 
+    {
+      final String new_text =
+        old_location_text.substring(0, old_location_text.length () - 2);
+
+      gbFrame.getLocationText().setText(new_text + "," + selected_range.getStart () +
+                            ".." + selected_range.getEnd () + "))");
+    } 
+    else
+    {
+      if(old_location_text.endsWith (")")) 
+      {
+        final String new_text =
+          old_location_text.substring(0, old_location_text.length () - 1);
+
+        gbFrame.getLocationText().setText(new_text + "," + selected_range.getStart () +
+                              ".." + selected_range.getEnd () + ")");
+      } 
+      else
+        gbFrame.getLocationText().setText(old_location_text + "," +
+                              selected_range.getStart () +
+                              ".." + selected_range.getEnd ());
+    }
+
+    if(!rationalizeLocation())
+    {
+      gbFrame.getLocationText().setText (old_location_text);
+      new MessageDialog(null, "grab failed - location cannot be parsed after " +
+                        "grabbing");
+    }
+  }
+
+  /**
+   *  Remove the currently selected range of bases from location_text.
+   **/
+  private void removeSelectedRange()
+  {
+    if(!rationalizeLocation())
+    {
+      new MessageDialog(null,
+                        "grab failed - current location cannot be parsed");
+      return;
+    }
+
+    final MarkerRange selected_marker_range =
+                                       gbFrame.getSelection().getMarkerRange();
+
+    if(selected_marker_range == null) 
+    {
+      new MessageDialog(null, "remove range failed - no bases are selected");
+      return;
+    }
+
+    final Range selected_range = selected_marker_range.getRawRange();
+
+    if (selected_marker_range.getStrand() != getFeature().getStrand()) 
+    {
+      new MessageDialog(null, "remove range failed - you need to select " +
+                         "some bases on the other strand");
+      return;
+    }
+
+    final Location location;
+
+    try 
+    {
+      location = new Location(gbFrame.getLocationText().getText ());
+    }
+    catch (LocationParseException e) 
+    {
+      // this shouldn't happen because we called rationalizeLocation ()
+      throw new Error("internal error - unexpected exception: " + e);
+    }
+
+    final Range location_total_range = location.getTotalRange();
+
+    if(!selected_range.overlaps(location_total_range))
+    {
+      new MessageDialog(null, "remove range failed - the range you " +
+                        "selected does not overlap the feature");
+      return;
+    }
+
+    if(selected_range.contains(location_total_range)) 
+    {
+      new MessageDialog(null, "remove range failed - the range you " +
+                        "selected overlaps the whole feature");
+      return;
+    }
+
+    final RangeVector location_ranges = location.getRanges();
+    final boolean location_is_complemented = location.isComplement();
+
+    final RangeVector new_ranges = new RangeVector();
+
+    // if the selected_range completely covers a range remove the
+    // range. otherwise if the selected_range is completely within one of the
+    // ranges two new ranges are created.  if the selected_range is not
+    // completely contained then the appropriate end of the range is truncated
+    for(int i = 0; i < location_ranges.size(); ++i) 
+    {
+      final Range this_range = (Range)location_ranges.elementAt(i);
+
+      if(selected_range.overlaps(this_range)) 
+      {
+        try 
+        {
+          if(this_range.contains(selected_range) &&
+             this_range.getStart() != selected_range.getStart() &&
+             this_range.getEnd() != selected_range.getEnd()) 
+          {
+            // chop a piece out of the middle and make two new ranges
+            final Range new_start_range =
+                    this_range.change(selected_range.getEnd() + 1,
+                                      this_range.getEnd());
+            new_ranges.add(new_start_range);
+            final Range new_end_range =
+              this_range.change(this_range.getStart(),
+                                selected_range.getStart() - 1);
+            new_ranges.add(new_end_range);
+          } 
+          else
+          {
+            if(selected_range.contains(this_range)) {
+              // delete (ie. don't copy) the range
+            } 
+            else
+            {
+              if(this_range.getStart() < selected_range.getStart()) 
+              {
+                // truncate the end of the range
+                final Range new_start_range =
+                  this_range.change(this_range.getStart(),
+                                    selected_range.getStart() - 1);
+                new_ranges.add(new_start_range);
+              } 
+              else
+              {
+                if(this_range.getEnd() > selected_range.getEnd())
+                {
+                  // truncate the start of the range
+                  final Range new_end_range =
+                    this_range.change(selected_range.getEnd() + 1,
+                                      this_range.getEnd());
+                  new_ranges.add(new_end_range);
+                } 
+                else
+                  throw new Error ("internal error - can't remove range");
+              }
+            }
+          }
+        }
+        catch(OutOfRangeException e)
+        {
+          throw new Error ("internal error - unexpected exception: " + e);
+        }
+      }
+      else  
+        new_ranges.add(this_range); // copy it unchanged
+    }
+
+    final Location new_location =
+      new Location(new_ranges, location_is_complemented);
+
+    gbFrame.getLocationText().setText(new_location.toStringShort());
+  }
+  
+}
\ No newline at end of file