JAL-3190 rough Proof of Concept of JalviewJS - Chimera
[jalview.git] / src / jalview / gui / ChimeraViewFrame.java
index b9fb2c9..8392606 100644 (file)
  */
 package jalview.gui;
 
+import jalview.api.FeatureRenderer;
 import jalview.bin.Cache;
-import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.ext.rbvi.chimera.ChimeraCommands;
 import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
 import jalview.gui.StructureViewer.ViewerType;
-import jalview.io.AppletFormatAdapter;
-import jalview.io.JalviewFileChooser;
-import jalview.io.JalviewFileView;
-import jalview.schemes.BuriedColourScheme;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.HelixColourScheme;
-import jalview.schemes.HydrophobicColourScheme;
-import jalview.schemes.PurinePyrimidineColourScheme;
-import jalview.schemes.StrandColourScheme;
-import jalview.schemes.TaylorColourScheme;
-import jalview.schemes.TurnColourScheme;
-import jalview.schemes.ZappoColourScheme;
+import jalview.io.DataSourceType;
+import jalview.io.StructureFile;
 import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.BrowserLauncher;
+import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.ws.dbsources.Pdb;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.io.BufferedReader;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Random;
-import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JColorChooser;
 import javax.swing.JInternalFrame;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
-import javax.swing.event.MenuEvent;
-import javax.swing.event.MenuListener;
 
 /**
  * GUI elements for handling an external chimera display
@@ -83,8 +67,6 @@ public class ChimeraViewFrame extends StructureViewerBase
 {
   private JalviewChimeraBinding jmb;
 
-  private boolean allChainsSelected = false;
-
   private IProgressIndicator progressBar = null;
 
   /*
@@ -96,113 +78,125 @@ public class ChimeraViewFrame extends StructureViewerBase
 
   private Random random = new Random();
 
+  private int myWidth = 500;
+
+  private int myHeight = 150;
+
   /**
    * Initialise menu options.
    */
-  private void initMenus()
+  @Override
+  protected void initMenus()
   {
+    super.initMenus();
+
     viewerActionMenu.setText(MessageManager.getString("label.chimera"));
-    viewerColour.setText(MessageManager
-            .getString("label.colour_with_chimera"));
+
+    viewerColour
+            .setText(MessageManager.getString("label.colour_with_chimera"));
     viewerColour.setToolTipText(MessageManager
             .getString("label.let_chimera_manage_structure_colours"));
-    helpItem.setText(MessageManager.getString("label.chimera_help"));
-    seqColour.setSelected(jmb.isColourBySequence());
-    viewerColour.setSelected(!jmb.isColourBySequence());
-    if (_colourwith == null)
-    {
-      _colourwith = new Vector<AlignmentPanel>();
-    }
-    if (_alignwith == null)
-    {
-      _alignwith = new Vector<AlignmentPanel>();
-    }
 
-    // save As not yet implemented
-    savemenu.setVisible(false);
-
-    ViewSelectionMenu seqColourBy = new ViewSelectionMenu(
-            MessageManager.getString("label.colour_by"), this, _colourwith,
-            new ItemListener()
-            {
-              @Override
-              public void itemStateChanged(ItemEvent e)
-              {
-                if (!seqColour.isSelected())
-                {
-                  seqColour.doClick();
-                }
-                else
-                {
-                  // update the Chimera display now.
-                  seqColour_actionPerformed(null);
-                }
-              }
-            });
-    viewMenu.add(seqColourBy);
+    helpItem.setText(MessageManager.getString("label.chimera_help"));
+    savemenu.setVisible(false); // not yet implemented
     viewMenu.add(fitToWindow);
 
-    final ItemListener handler;
-    JMenu alpanels = new ViewSelectionMenu(
-            MessageManager.getString("label.superpose_with"), this,
-            _alignwith, handler = new ItemListener()
-            {
-              @Override
-              public void itemStateChanged(ItemEvent e)
-              {
-                alignStructs.setEnabled(_alignwith.size() > 0);
-                alignStructs.setToolTipText(MessageManager
-                        .formatMessage(
-                                "label.align_structures_using_linked_alignment_views",
-                                new Object[] { new Integer(_alignwith
-                                        .size()).toString() }));
-              }
-            });
-    handler.itemStateChanged(null);
-    viewerActionMenu.add(alpanels);
-    viewerActionMenu.addMenuListener(new MenuListener()
+    JMenuItem writeFeatures = new JMenuItem(
+            MessageManager.getString("label.create_chimera_attributes"));
+    writeFeatures.setToolTipText(MessageManager
+            .getString("label.create_chimera_attributes_tip"));
+    writeFeatures.addActionListener(new ActionListener()
     {
-
       @Override
-      public void menuSelected(MenuEvent e)
+      public void actionPerformed(ActionEvent e)
       {
-        handler.itemStateChanged(null);
+        sendFeaturesToChimera();
       }
+    });
+    viewerActionMenu.add(writeFeatures);
 
-      @Override
-      public void menuDeselected(MenuEvent e)
-      {
-        // TODO Auto-generated method stub
-      }
+    final JMenu fetchAttributes = new JMenu(
+            MessageManager.getString("label.fetch_chimera_attributes"));
+    fetchAttributes.setToolTipText(
+            MessageManager.getString("label.fetch_chimera_attributes_tip"));
+    fetchAttributes.addMouseListener(new MouseAdapter()
+    {
 
       @Override
-      public void menuCanceled(MenuEvent e)
+      public void mouseEntered(MouseEvent e)
       {
-        // TODO Auto-generated method stub
+        buildAttributesMenu(fetchAttributes);
       }
     });
-    JMenuItem writeFeatures = new JMenuItem("Write Jalview features");
-    writeFeatures
-            .setToolTipText("Create attributes in Chimera for features in Jalview");
-    writeFeatures.addActionListener(new ActionListener()
+    viewerActionMenu.add(fetchAttributes);
+  }
+
+  /**
+   * Query Chimera for its residue attribute names and add them as items off the
+   * attributes menu
+   * 
+   * @param attributesMenu
+   */
+  protected void buildAttributesMenu(JMenu attributesMenu)
+  {
+    List<String> atts = jmb.sendChimeraCommand("list resattr", true);
+    if (atts == null)
     {
-      @Override
-      public void actionPerformed(ActionEvent e)
+      return;
+    }
+    attributesMenu.removeAll();
+    Collections.sort(atts);
+    for (String att : atts)
+    {
+      final String attName = att.split(" ")[1];
+
+      /*
+       * ignore 'jv_*' attributes, as these are Jalview features that have
+       * been transferred to residue attributes in Chimera!
+       */
+      if (!attName.startsWith(ChimeraCommands.NAMESPACE_PREFIX))
       {
-        sendFeaturesToChimera();
+        JMenuItem menuItem = new JMenuItem(attName);
+        menuItem.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
+            getChimeraAttributes(attName);
+          }
+        });
+        attributesMenu.add(menuItem);
       }
-    });
-    viewerActionMenu.add(writeFeatures);
+    }
   }
 
-  protected void sendFeaturesToChimera()
+  /**
+   * Read residues in Chimera with the given attribute name, and set as features
+   * on the corresponding sequence positions (if any)
+   * 
+   * @param attName
+   */
+  protected void getChimeraAttributes(String attName)
   {
-    // TODO Auto-generated method stub
+    jmb.copyStructureAttributesToFeatures(attName, getAlignmentPanel());
+  }
 
+  /**
+   * Send a command to Chimera to create residue attributes for Jalview features
+   * <p>
+   * The syntax is: setattr r <attName> <attValue> <atomSpec>
+   * <p>
+   * For example: setattr r jv:chain "Ferredoxin-1, Chloroplastic" #0:94.A
+   */
+  protected void sendFeaturesToChimera()
+  {
+    int count = jmb.sendFeaturesToViewer(getAlignmentPanel());
+    statusBar.setText(
+            MessageManager.formatMessage("label.attributes_set", count));
   }
 
   /**
-   * add a single PDB structure to a new or existing Chimera view
+   * open a single PDB structure in a new Chimera view
    * 
    * @param pdbentry
    * @param seq
@@ -213,32 +207,10 @@ public class ChimeraViewFrame extends StructureViewerBase
           String[] chains, final AlignmentPanel ap)
   {
     this();
-    String pdbId = pdbentry.getId();
-
-    /*
-     * If the PDB file is already loaded, the user may just choose to add to an
-     * existing viewer (or cancel)
-     */
-    if (addAlreadyLoadedFile(seq, chains, ap, pdbId))
-    {
-      return;
-    }
-
-    /*
-     * Check if there are other Chimera views involving this alignment and give
-     * user the option to add and align this molecule to one of them (or cancel)
-     */
-    if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId))
-    {
-      return;
-    }
 
-    /*
-     * If the options above are declined or do not apply, show the structure in
-     * a new viewer
-     */
     openNewChimera(ap, new PDBEntry[] { pdbentry },
-            new SequenceI[][] { seq });
+            new SequenceI[][]
+            { seq });
   }
 
   /**
@@ -252,33 +224,21 @@ public class ChimeraViewFrame extends StructureViewerBase
     }
   }
 
-  /**
-   * Answers true if this viewer already involves the given PDB ID
-   */
-  @Override
-  protected boolean hasPdbId(String pdbId)
-  {
-    return jmb.hasPdbId(pdbId);
-  }
-
   private void openNewChimera(AlignmentPanel ap, PDBEntry[] pdbentrys,
           SequenceI[][] seqs)
   {
     createProgressBar();
-    // FIXME extractChains needs pdbentries to match IDs to PDBEntry(s) on seqs
-    String[][] chains = extractChains(seqs);
     jmb = new JalviewChimeraBindingModel(this,
-            ap.getStructureSelectionManager(), pdbentrys, seqs, chains,
-            null);
+            ap.getStructureSelectionManager(), pdbentrys, seqs, null);
     addAlignmentPanel(ap);
     useAlignmentPanelForColourbyseq(ap);
+
     if (pdbentrys.length > 1)
     {
-      alignAddedStructures = true;
       useAlignmentPanelForSuperposition(ap);
     }
     jmb.setColourBySequence(true);
-    setSize(400, 400); // probably should be a configurable/dynamic default here
+    setSize(myWidth, myHeight);
     initMenus();
 
     addingStructures = false;
@@ -288,7 +248,8 @@ public class ChimeraViewFrame extends StructureViewerBase
     this.addInternalFrameListener(new InternalFrameAdapter()
     {
       @Override
-      public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
+      public void internalFrameClosing(
+              InternalFrameEvent internalFrameEvent)
       {
         closeViewer(false);
       }
@@ -297,42 +258,6 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * Retrieve chains for sequences by inspecting their PDB refs. The hope is
-   * that the first will be to the sequence's own chain. Really need a more
-   * managed way of doing this.
-   * 
-   * @param seqs
-   * @return
-   */
-  protected String[][] extractChains(SequenceI[][] seqs)
-  {
-    String[][] chains = new String[seqs.length][];
-    for (int i = 0; i < seqs.length; i++)
-    {
-      chains[i] = new String[seqs[i].length];
-      int seqno = 0;
-      for (SequenceI seq : seqs[i])
-      {
-        String chain = null;
-        if (seq.getDatasetSequence() != null)
-        {
-          Vector<PDBEntry> pdbrefs = seq.getDatasetSequence()
-                  .getAllPDBEntries();
-          if (pdbrefs != null && pdbrefs.size() > 0)
-          {
-            // FIXME: SequenceI.PDBEntry[0] chain mapping used for
-            // ChimeraViewFrame. Is this even used ???
-
-            chain = pdbrefs.get(0).getChainCode();
-          }
-        }
-        chains[i][seqno++] = chain;
-      }
-    }
-    return chains;
-  }
-
-  /**
    * Create a new viewer from saved session state data including Chimera session
    * file
    * 
@@ -368,17 +293,19 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * create a new viewer containing several structures superimposed using the
-   * given alignPanel.
+   * create a new viewer containing several structures, optionally superimposed
+   * using the given alignPanel.
    * 
    * @param pe
    * @param seqs
    * @param ap
    */
-  public ChimeraViewFrame(PDBEntry[] pe, SequenceI[][] seqs,
+  public ChimeraViewFrame(PDBEntry[] pe, boolean alignAdded,
+          SequenceI[][] seqs,
           AlignmentPanel ap)
   {
     this();
+    setAlignAddedStructures(alignAdded);
     openNewChimera(ap, pe, seqs);
   }
 
@@ -397,45 +324,22 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * Returns a list of any Chimera viewers in the desktop. The list is
-   * restricted to those linked to the given alignment panel if it is not null.
-   */
-  @Override
-  protected List<StructureViewerBase> getViewersFor(AlignmentPanel ap)
-  {
-    List<StructureViewerBase> result = new ArrayList<StructureViewerBase>();
-    JInternalFrame[] frames = Desktop.instance.getAllFrames();
-
-    for (JInternalFrame frame : frames)
-    {
-      if (frame instanceof ChimeraViewFrame)
-      {
-        if (ap == null || ((StructureViewerBase) frame).isLinkedWith(ap))
-        {
-          result.add((StructureViewerBase) frame);
-        }
-      }
-    }
-    return result;
-  }
-
-  /**
    * Launch Chimera. If we have a chimera session file name, send Chimera the
    * command to open its saved session file.
    */
   void initChimera()
   {
     jmb.setFinishedInit(false);
-    jalview.gui.Desktop.addInternalFrame(this,
-            jmb.getViewerTitle("Chimera", true), getBounds().width,
+    Desktop.addInternalFrame(this,
+            jmb.getViewerTitle(getViewerName(), true), getBounds().width,
             getBounds().height);
 
     if (!jmb.launchChimera())
     {
-      JOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.desktop,
               MessageManager.getString("label.chimera_failed"),
               MessageManager.getString("label.error_loading_file"),
-              JOptionPane.ERROR_MESSAGE);
+              JvOptionPane.ERROR_MESSAGE);
       this.dispose();
       return;
     }
@@ -445,76 +349,24 @@ public class ChimeraViewFrame extends StructureViewerBase
       boolean opened = jmb.openSession(chimeraSessionFile);
       if (!opened)
       {
-        System.err
-                .println("An error occurred opening Chimera session file "
-                        + chimeraSessionFile);
+        System.err.println("An error occurred opening Chimera session file "
+                + chimeraSessionFile);
       }
     }
-    jmb.setFinishedInit(true);
-
-    jmb.startChimeraListener();
-  }
 
-  /**
-   * If the list is not empty, add menu items for 'All' and each individual
-   * chain to the "View | Show Chain" sub-menu. Multiple selections are allowed.
-   * 
-   * @param chainNames
-   */
-  void setChainMenuItems(List<String> chainNames)
-  {
-    chainMenu.removeAll();
-    if (chainNames == null || chainNames.isEmpty())
+    if (!Platform.isJS())
     {
-      return;
-    }
-    JMenuItem menuItem = new JMenuItem(
-            MessageManager.getString("label.all"));
-    menuItem.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent evt)
-      {
-        allChainsSelected = true;
-        for (int i = 0; i < chainMenu.getItemCount(); i++)
-        {
-          if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
-          {
-            ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
-          }
-        }
-        showSelectedChains();
-        allChainsSelected = false;
-      }
-    });
-
-    chainMenu.add(menuItem);
-
-    for (String chainName : chainNames)
-    {
-      menuItem = new JCheckBoxMenuItem(chainName, true);
-      menuItem.addItemListener(new ItemListener()
-      {
-        @Override
-        public void itemStateChanged(ItemEvent evt)
-        {
-          if (!allChainsSelected)
-          {
-            showSelectedChains();
-          }
-        }
-      });
-
-      chainMenu.add(menuItem);
+       jmb.startChimeraListener();
     }
   }
 
   /**
    * Show only the selected chain(s) in the viewer
    */
+  @Override
   void showSelectedChains()
   {
-    List<String> toshow = new ArrayList<String>();
+    List<String> toshow = new ArrayList<>();
     for (int i = 0; i < chainMenu.getItemCount(); i++)
     {
       if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
@@ -544,22 +396,22 @@ public class ChimeraViewFrame extends StructureViewerBase
     {
       if (!closeChimera)
       {
-        String prompt = MessageManager.formatMessage(
-                "label.confirm_close_chimera",
-                new Object[] { jmb.getViewerTitle("Chimera", false) });
+        String prompt = MessageManager
+                .formatMessage("label.confirm_close_chimera", new Object[]
+                { jmb.getViewerTitle(getViewerName(), false) });
         prompt = JvSwingUtils.wrapTooltip(true, prompt);
-        int confirm = JOptionPane.showConfirmDialog(this, prompt,
+        int confirm = JvOptionPane.showConfirmDialog(this, prompt,
                 MessageManager.getString("label.close_viewer"),
-                JOptionPane.YES_NO_CANCEL_OPTION);
+                JvOptionPane.YES_NO_CANCEL_OPTION);
         /*
          * abort closure if user hits escape or Cancel
          */
-        if (confirm == JOptionPane.CANCEL_OPTION
-                || confirm == JOptionPane.CLOSED_OPTION)
+        if (confirm == JvOptionPane.CANCEL_OPTION
+                || confirm == JvOptionPane.CLOSED_OPTION)
         {
           return;
         }
-        closeChimera = confirm == JOptionPane.YES_OPTION;
+        closeChimera = confirm == JvOptionPane.YES_OPTION;
       }
       jmb.closeViewer(closeChimera);
     }
@@ -584,12 +436,13 @@ public class ChimeraViewFrame extends StructureViewerBase
     // todo - record which pdbids were successfully imported.
     StringBuilder errormsgs = new StringBuilder(128);
     StringBuilder files = new StringBuilder(128);
-    List<PDBEntry> filePDB = new ArrayList<PDBEntry>();
-    List<Integer> filePDBpos = new ArrayList<Integer>();
+    List<PDBEntry> filePDB = new ArrayList<>();
+    List<Integer> filePDBpos = new ArrayList<>();
     PDBEntry thePdbEntry = null;
+    StructureFile pdb = null;
     try
     {
-      String[] curfiles = jmb.getPdbFile(); // files currently in viewer
+      String[] curfiles = jmb.getStructureFiles(); // files currently in viewer
       // TODO: replace with reference fetching/transfer code (validate PDBentry
       // as a DBRef?)
       for (int pi = 0; pi < jmb.getPdbCount(); pi++)
@@ -641,21 +494,23 @@ public class ChimeraViewFrame extends StructureViewerBase
     } catch (Exception ex)
     {
       ex.printStackTrace();
-      errormsgs.append("When retrieving pdbfiles for '"
-              + thePdbEntry.getId() + "'");
+      errormsgs.append(
+              "When retrieving pdbfiles for '" + thePdbEntry.getId() + "'");
     }
     if (errormsgs.length() > 0)
     {
 
-      JOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
-              .formatMessage("label.pdb_entries_couldnt_be_retrieved",
-                      new Object[] { errormsgs.toString() }),
+      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+              MessageManager.formatMessage(
+                      "label.pdb_entries_couldnt_be_retrieved", new Object[]
+                      { errormsgs.toString() }),
               MessageManager.getString("label.couldnt_load_file"),
-              JOptionPane.ERROR_MESSAGE);
+              JvOptionPane.ERROR_MESSAGE);
     }
 
     if (files.length() > 0)
     {
+      jmb.setFinishedInit(false);
       if (!addingStructures)
       {
         try
@@ -675,18 +530,18 @@ public class ChimeraViewFrame extends StructureViewerBase
           try
           {
             int pos = filePDBpos.get(num).intValue();
-            long startTime = startProgressBar("Chimera "
+            long startTime = startProgressBar(getViewerName() + " "
                     + MessageManager.getString("status.opening_file_for")
                     + " " + pe.getId());
             jmb.openFile(pe);
             jmb.addSequence(pos, jmb.getSequence()[pos]);
             File fl = new File(pe.getFile());
-            String protocol = AppletFormatAdapter.URL;
+            DataSourceType protocol = DataSourceType.URL;
             try
             {
               if (fl.exists())
               {
-                protocol = AppletFormatAdapter.FILE;
+                protocol = DataSourceType.FILE;
               }
             } catch (Throwable e)
             {
@@ -695,8 +550,12 @@ public class ChimeraViewFrame extends StructureViewerBase
               stopProgressBar("", startTime);
             }
             // Explicitly map to the filename used by Chimera ;
-            jmb.getSsm().setMapping(jmb.getSequence()[pos],
-                    jmb.getChains()[pos], pe.getFile(), protocol);
+
+            pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos],
+                    jmb.getChains()[pos], pe.getFile(), protocol,
+                    progressBar);
+            stashFoundChains(pdb, pe.getFile());
+
           } catch (OutOfMemoryError oomerror)
           {
             new OOMWarning(
@@ -704,24 +563,37 @@ public class ChimeraViewFrame extends StructureViewerBase
                     oomerror);
           } catch (Exception ex)
           {
-            Cache.log.error("Couldn't open " + pe.getFile()
-                    + " in Chimera viewer!", ex);
+            Cache.log.error(
+                    "Couldn't open " + pe.getFile() + " in Chimera viewer!",
+                    ex);
           } finally
           {
             Cache.log.debug("File locations are " + files);
           }
         }
       }
+
+      jmb.refreshGUI();
       jmb.setFinishedInit(true);
       jmb.setLoadingFromArchive(false);
 
+      /*
+       * ensure that any newly discovered features (e.g. RESNUM)
+       * are added to any open feature settings dialog
+       */
+      FeatureRenderer fr = getBinding().getFeatureRenderer(null);
+      if (fr != null)
+      {
+        fr.featuresAdded();
+      }
+
       // refresh the sequence colours for the new structure(s)
       for (AlignmentPanel ap : _colourwith)
       {
         jmb.updateColours(ap);
       }
       // do superposition if asked to
-      if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures)
+      if (alignAddedStructures)
       {
         new Thread(new Runnable()
         {
@@ -731,7 +603,6 @@ public class ChimeraViewFrame extends StructureViewerBase
             alignStructs_withAllAlignPanels();
           }
         }).start();
-        alignAddedStructures = false;
       }
       addingStructures = false;
     }
@@ -741,15 +612,26 @@ public class ChimeraViewFrame extends StructureViewerBase
 
   /**
    * Fetch PDB data and save to a local file. Returns the full path to the file,
-   * or null if fetch fails.
+   * or null if fetch fails. TODO: refactor to common with Jmol ? duplication
    * 
    * @param processingEntry
    * @return
    * @throws Exception
    */
+
+  private void stashFoundChains(StructureFile pdb, String file)
+  {
+    for (int i = 0; i < pdb.getChains().size(); i++)
+    {
+      String chid = new String(
+              pdb.getId() + ":" + pdb.getChains().elementAt(i).id);
+      jmb.getChainNames().add(chid);
+      jmb.getChainFile().put(chid, file);
+    }
+  }
+
   private String fetchPdbFile(PDBEntry processingEntry) throws Exception
   {
-    // FIXME: this is duplicated code with Jmol frame ?
     String filePath = null;
     Pdb pdbclient = new Pdb();
     AlignmentI pdbseq = null;
@@ -761,7 +643,8 @@ public class ChimeraViewFrame extends StructureViewerBase
      * Write 'fetching PDB' progress on AlignFrame as we are not yet visible
      */
     String msg = MessageManager.formatMessage("status.fetching_pdb",
-            new Object[] { pdbid });
+            new Object[]
+            { pdbid });
     getAlignmentPanel().alignFrame.setProgressBar(msg, handle);
     // long hdl = startProgressBar(MessageManager.formatMessage(
     // "status.fetching_pdb", new Object[]
@@ -827,213 +710,10 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   @Override
-  public void pdbFile_actionPerformed(ActionEvent actionEvent)
-  {
-    JalviewFileChooser chooser = new JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
-
-    chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle(MessageManager.getString("label.save_pdb_file"));
-    chooser.setToolTipText(MessageManager.getString("action.save"));
-
-    int value = chooser.showSaveDialog(this);
-
-    if (value == JalviewFileChooser.APPROVE_OPTION)
-    {
-      BufferedReader in = null;
-      try
-      {
-        // TODO: cope with multiple PDB files in view
-        in = new BufferedReader(new FileReader(jmb.getPdbFile()[0]));
-        File outFile = chooser.getSelectedFile();
-
-        PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
-        String data;
-        while ((data = in.readLine()) != null)
-        {
-          if (!(data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1))
-          {
-            out.println(data);
-          }
-        }
-        out.close();
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
-      } finally
-      {
-        if (in != null)
-        {
-          try
-          {
-            in.close();
-          } catch (IOException e)
-          {
-            e.printStackTrace();
-          }
-        }
-      }
-    }
-  }
-
-  @Override
-  public void viewMapping_actionPerformed(ActionEvent actionEvent)
-  {
-    jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();
-    try
-    {
-      cap.appendText(jmb.printMappings());
-    } catch (OutOfMemoryError e)
-    {
-      new OOMWarning(
-              "composing sequence-structure alignments for display in text box.",
-              e);
-      cap.dispose();
-      return;
-    }
-    jalview.gui.Desktop.addInternalFrame(cap,
-            MessageManager.getString("label.pdb_sequence_mapping"), 550,
-            600);
-  }
-
-  @Override
-  public void eps_actionPerformed(ActionEvent e)
-  {
-    throw new Error(
-            MessageManager
-                    .getString("error.eps_generation_not_implemented"));
-  }
-
-  @Override
-  public void png_actionPerformed(ActionEvent e)
-  {
-    throw new Error(
-            MessageManager
-                    .getString("error.png_generation_not_implemented"));
-  }
-
-  @Override
-  public void viewerColour_actionPerformed(ActionEvent actionEvent)
-  {
-    if (viewerColour.isSelected())
-    {
-      // disable automatic sequence colouring.
-      jmb.setColourBySequence(false);
-    }
-  }
-
-  @Override
-  public void seqColour_actionPerformed(ActionEvent actionEvent)
-  {
-    jmb.setColourBySequence(seqColour.isSelected());
-    if (_colourwith == null)
-    {
-      _colourwith = new Vector<AlignmentPanel>();
-    }
-    if (jmb.isColourBySequence())
-    {
-      if (!jmb.isLoadingFromArchive())
-      {
-        if (_colourwith.size() == 0 && getAlignmentPanel() != null)
-        {
-          // Make the currently displayed alignment panel the associated view
-          _colourwith.add(getAlignmentPanel().alignFrame.alignPanel);
-        }
-      }
-      // Set the colour using the current view for the associated alignframe
-      for (AlignmentPanel ap : _colourwith)
-      {
-        jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
-      }
-    }
-  }
-
-  @Override
-  public void chainColour_actionPerformed(ActionEvent actionEvent)
-  {
-    chainColour.setSelected(true);
-    jmb.colourByChain();
-  }
-
-  @Override
-  public void chargeColour_actionPerformed(ActionEvent actionEvent)
-  {
-    chargeColour.setSelected(true);
-    jmb.colourByCharge();
-  }
-
-  @Override
-  public void zappoColour_actionPerformed(ActionEvent actionEvent)
-  {
-    zappoColour.setSelected(true);
-    jmb.setJalviewColourScheme(new ZappoColourScheme());
-  }
-
-  @Override
-  public void taylorColour_actionPerformed(ActionEvent actionEvent)
-  {
-    taylorColour.setSelected(true);
-    jmb.setJalviewColourScheme(new TaylorColourScheme());
-  }
-
-  @Override
-  public void hydroColour_actionPerformed(ActionEvent actionEvent)
-  {
-    hydroColour.setSelected(true);
-    jmb.setJalviewColourScheme(new HydrophobicColourScheme());
-  }
-
-  @Override
-  public void helixColour_actionPerformed(ActionEvent actionEvent)
-  {
-    helixColour.setSelected(true);
-    jmb.setJalviewColourScheme(new HelixColourScheme());
-  }
-
-  @Override
-  public void strandColour_actionPerformed(ActionEvent actionEvent)
-  {
-    strandColour.setSelected(true);
-    jmb.setJalviewColourScheme(new StrandColourScheme());
-  }
-
-  @Override
-  public void turnColour_actionPerformed(ActionEvent actionEvent)
-  {
-    turnColour.setSelected(true);
-    jmb.setJalviewColourScheme(new TurnColourScheme());
-  }
-
-  @Override
-  public void buriedColour_actionPerformed(ActionEvent actionEvent)
-  {
-    buriedColour.setSelected(true);
-    jmb.setJalviewColourScheme(new BuriedColourScheme());
-  }
-
-  @Override
-  public void purinePyrimidineColour_actionPerformed(ActionEvent actionEvent)
+  public void makePDBImage(TYPE imageType)
   {
-    setJalviewColourScheme(new PurinePyrimidineColourScheme());
-  }
-
-  @Override
-  public void userColour_actionPerformed(ActionEvent actionEvent)
-  {
-    userColour.setSelected(true);
-    new UserDefinedColours(this, null);
-  }
-
-  @Override
-  public void backGround_actionPerformed(ActionEvent actionEvent)
-  {
-    java.awt.Color col = JColorChooser
-            .showDialog(this, MessageManager
-                    .getString("label.select_backgroud_colour"), null);
-    if (col != null)
-    {
-      jmb.setBackgroundColour(col);
-    }
+    throw new UnsupportedOperationException(
+            "Image export for Chimera is not implemented");
   }
 
   @Override
@@ -1041,109 +721,13 @@ public class ChimeraViewFrame extends StructureViewerBase
   {
     try
     {
-      jalview.util.BrowserLauncher
+      BrowserLauncher
               .openURL("https://www.cgl.ucsf.edu/chimera/docs/UsersGuide");
-    } catch (Exception ex)
+    } catch (IOException ex)
     {
     }
   }
 
-  public void updateTitleAndMenus()
-  {
-    if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
-    {
-      repaint();
-      return;
-    }
-    setChainMenuItems(jmb.getChainNames());
-
-    this.setTitle(jmb.getViewerTitle("Chimera", true));
-    // if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
-    // {
-      viewerActionMenu.setVisible(true);
-    // }
-    if (!jmb.isLoadingFromArchive())
-    {
-      seqColour_actionPerformed(null);
-    }
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see
-   * jalview.jbgui.GStructureViewer#alignStructs_actionPerformed(java.awt.event
-   * .ActionEvent)
-   */
-  @Override
-  protected void alignStructs_actionPerformed(ActionEvent actionEvent)
-  {
-    alignStructs_withAllAlignPanels();
-  }
-
-  private void alignStructs_withAllAlignPanels()
-  {
-    if (getAlignmentPanel() == null)
-    {
-      return;
-    }
-
-    if (_alignwith.size() == 0)
-    {
-      _alignwith.add(getAlignmentPanel());
-    }
-
-    try
-    {
-      AlignmentI[] als = new Alignment[_alignwith.size()];
-      ColumnSelection[] alc = new ColumnSelection[_alignwith.size()];
-      int[] alm = new int[_alignwith.size()];
-      int a = 0;
-
-      for (AlignmentPanel ap : _alignwith)
-      {
-        als[a] = ap.av.getAlignment();
-        alm[a] = -1;
-        alc[a++] = ap.av.getColumnSelection();
-      }
-      jmb.superposeStructures(als, alm, alc);
-    } catch (Exception e)
-    {
-      StringBuffer sp = new StringBuffer();
-      for (AlignmentPanel ap : _alignwith)
-      {
-        sp.append("'" + ap.alignFrame.getTitle() + "' ");
-      }
-      Cache.log.info("Couldn't align structures with the " + sp.toString()
-              + "associated alignment panels.", e);
-    }
-  }
-
-  @Override
-  public void setJalviewColourScheme(ColourSchemeI ucs)
-  {
-    jmb.setJalviewColourScheme(ucs);
-
-  }
-
-  /**
-   * 
-   * @param alignment
-   * @return first alignment panel displaying given alignment, or the default
-   *         alignment panel
-   */
-  public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment)
-  {
-    for (AlignmentPanel ap : getAllAlignmentPanels())
-    {
-      if (ap.av.getAlignment() == alignment)
-      {
-        return ap;
-      }
-    }
-    return getAlignmentPanel();
-  }
-
   @Override
   public AAStructureBindingModel getBinding()
   {
@@ -1233,8 +817,30 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   @Override
-  protected AAStructureBindingModel getBindingModel()
+  protected String getViewerName()
   {
-    return jmb;
+    return "Chimera";
+  }
+
+  /**
+   * Sends commands to align structures according to associated alignment(s).
+   * 
+   * @return
+   */
+  @Override
+  protected String alignStructs_withAllAlignPanels()
+  {
+    String reply = super.alignStructs_withAllAlignPanels();
+    if (reply != null)
+    {
+      statusBar.setText("Superposition failed: " + reply);
+    }
+    return reply;
+  }
+
+  @Override
+  protected IProgressIndicator getIProgressIndicator()
+  {
+    return progressBar;
   }
 }