JAL-2351 first refactoring and outline of read/write Jmol properties
[jalview.git] / src / jalview / gui / ChimeraViewFrame.java
index c902093..bac8f60 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,9 +28,10 @@ import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
 import jalview.gui.StructureViewer.ViewerType;
-import jalview.io.AppletFormatAdapter;
+import jalview.io.DataSourceType;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
+import jalview.io.StructureFile;
 import jalview.schemes.BuriedColourScheme;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.HelixColourScheme;
@@ -49,6 +50,8 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
@@ -59,9 +62,7 @@ import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.Random;
-import java.util.Set;
 import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
@@ -69,7 +70,6 @@ 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;
@@ -83,7 +83,7 @@ import javax.swing.event.MenuListener;
  */
 public class ChimeraViewFrame extends StructureViewerBase
 {
-  private JalviewChimeraBinding jmb;
+  JalviewChimeraBinding jmb;
 
   private boolean allChainsSelected = false;
 
@@ -103,7 +103,8 @@ public class ChimeraViewFrame extends StructureViewerBase
    */
   private void initMenus()
   {
-    viewerActionMenu.setText(MessageManager.getString("label.chimera"));
+    String chimera = MessageManager.getString("label.chimera");
+    viewerActionMenu.setText(chimera);
     viewerColour.setText(MessageManager
             .getString("label.colour_with_chimera"));
     viewerColour.setToolTipText(MessageManager
@@ -164,25 +165,74 @@ public class ChimeraViewFrame extends StructureViewerBase
     viewerActionMenu.add(alpanels);
     viewerActionMenu.addMenuListener(new MenuListener()
     {
-
       @Override
       public void menuSelected(MenuEvent e)
       {
         handler.itemStateChanged(null);
       }
-
       @Override
       public void menuDeselected(MenuEvent e)
       {
-        // TODO Auto-generated method stub
       }
-
       @Override
       public void menuCanceled(MenuEvent e)
       {
-        // TODO Auto-generated method stub
       }
     });
+
+    JMenuItem writeFeatures = new JMenuItem(
+            MessageManager.getString("label.create_viewer_attributes"));
+    writeFeatures.setToolTipText(MessageManager.formatMessage(
+            "label.create_viewer_attributes_tip", chimera));
+    writeFeatures.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        sendFeaturesToViewer();
+      }
+    });
+    viewerActionMenu.add(writeFeatures);
+
+    final JMenu fetchAttributes = new JMenu(MessageManager.formatMessage(
+            "label.fetch_viewer_attributes", chimera));
+    fetchAttributes.setToolTipText(MessageManager.formatMessage(
+            "label.fetch_viewer_attributes_tip", chimera));
+    fetchAttributes.addMouseListener(new MouseAdapter()
+    {
+
+      @Override
+      public void mouseEntered(MouseEvent e)
+      {
+        buildAttributesMenu(fetchAttributes);
+      }
+    });
+    viewerActionMenu.add(fetchAttributes);
+  }
+
+  /**
+   * Asks Chimera for residues with the given attribute name, and set as
+   * features on the corresponding sequence positions (if any)
+   * 
+   * @param attName
+   */
+  @Override
+  protected void getResidueAttributes(String attName)
+  {
+    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
+   */
+  @Override
+  protected void sendFeaturesToViewer()
+  {
+    jmb.sendFeaturesToViewer(getAlignmentPanel());
   }
 
   /**
@@ -196,7 +246,7 @@ public class ChimeraViewFrame extends StructureViewerBase
   public ChimeraViewFrame(PDBEntry pdbentry, SequenceI[] seq,
           String[] chains, final AlignmentPanel ap)
   {
-    super();
+    this();
     String pdbId = pdbentry.getId();
 
     /*
@@ -249,10 +299,8 @@ public class ChimeraViewFrame extends StructureViewerBase
           SequenceI[][] seqs)
   {
     createProgressBar();
-    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)
@@ -270,6 +318,7 @@ public class ChimeraViewFrame extends StructureViewerBase
 
     this.addInternalFrameListener(new InternalFrameAdapter()
     {
+      @Override
       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
       {
         closeViewer(false);
@@ -279,39 +328,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)
-          {
-            chain = pdbrefs.get(0).getChainCode();
-          }
-        }
-        chains[i][seqno++] = chain;
-      }
-    }
-    return chains;
-  }
-
-  /**
    * Create a new viewer from saved session state data including Chimera session
    * file
    * 
@@ -328,7 +344,7 @@ public class ChimeraViewFrame extends StructureViewerBase
           SequenceI[][] seqsArray, boolean colourByChimera,
           boolean colourBySequence, String newViewId)
   {
-    super();
+    this();
     setViewId(newViewId);
     this.chimeraSessionFile = chimeraSessionFile;
     openNewChimera(alignPanel, pdbArray, seqsArray);
@@ -357,31 +373,22 @@ public class ChimeraViewFrame extends StructureViewerBase
   public ChimeraViewFrame(PDBEntry[] pe, SequenceI[][] seqs,
           AlignmentPanel ap)
   {
-    super();
+    this();
     openNewChimera(ap, pe, seqs);
   }
 
-  public ChimeraViewFrame(Map<PDBEntry, List<SequenceI>> toView,
-          AlignmentPanel alignPanel)
+  /**
+   * Default constructor
+   */
+  public ChimeraViewFrame()
   {
     super();
 
     /*
-     * Convert the map of sequences per pdb entry into the tied arrays expected
-     * by openNewChimera
-     * 
-     * TODO pass the Map down to openNewChimera and its callees instead
+     * closeViewer will decide whether or not to close this frame
+     * depending on whether user chooses to Cancel or not
      */
-    final Set<PDBEntry> pdbEntries = toView.keySet();
-    PDBEntry[] pdbs = pdbEntries.toArray(new PDBEntry[pdbEntries.size()]);
-    SequenceI[][] seqsForPdbs = new SequenceI[pdbEntries.size()][];
-    for (int i = 0; i < pdbs.length; i++)
-    {
-      final List<SequenceI> seqsForPdb = toView.get(pdbs[i]);
-      seqsForPdbs[i] = seqsForPdb.toArray(new SequenceI[seqsForPdb.size()]);
-    }
-
-    openNewChimera(alignPanel, pdbs, seqsForPdbs);
+    setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
   }
 
   /**
@@ -413,17 +420,15 @@ public class ChimeraViewFrame extends StructureViewerBase
    */
   void initChimera()
   {
-    jmb.setFinishedInit(false);
-    jalview.gui.Desktop.addInternalFrame(this,
-            jmb.getViewerTitle("Chimera", true), getBounds().width,
-            getBounds().height);
+    Desktop.addInternalFrame(this, jmb.getViewerTitle("Chimera", 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;
     }
@@ -438,7 +443,6 @@ public class ChimeraViewFrame extends StructureViewerBase
                         + chimeraSessionFile);
       }
     }
-    jmb.setFinishedInit(true);
 
     jmb.startChimeraListener();
   }
@@ -449,6 +453,7 @@ public class ChimeraViewFrame extends StructureViewerBase
    * 
    * @param chainNames
    */
+  @Override
   void setChainMenuItems(List<String> chainNames)
   {
     chainMenu.removeAll();
@@ -460,6 +465,7 @@ public class ChimeraViewFrame extends StructureViewerBase
             MessageManager.getString("label.all"));
     menuItem.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent evt)
       {
         allChainsSelected = true;
@@ -482,6 +488,7 @@ public class ChimeraViewFrame extends StructureViewerBase
       menuItem = new JCheckBoxMenuItem(chainName, true);
       menuItem.addItemListener(new ItemListener()
       {
+        @Override
         public void itemStateChanged(ItemEvent evt)
         {
           if (!allChainsSelected)
@@ -498,6 +505,7 @@ public class ChimeraViewFrame extends StructureViewerBase
   /**
    * Show only the selected chain(s) in the viewer
    */
+  @Override
   void showSelectedChains()
   {
     List<String> toshow = new ArrayList<String>();
@@ -523,6 +531,7 @@ public class ChimeraViewFrame extends StructureViewerBase
    * @param closeChimera
    *          if true, close any linked Chimera process; if false, prompt first
    */
+  @Override
   public void closeViewer(boolean closeChimera)
   {
     if (jmb != null && jmb.isChimeraRunning())
@@ -533,10 +542,18 @@ public class ChimeraViewFrame extends StructureViewerBase
                 "label.confirm_close_chimera",
                 new Object[] { jmb.getViewerTitle("Chimera", 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_OPTION);
-        closeChimera = confirm == JOptionPane.YES_OPTION;
+                JvOptionPane.YES_NO_CANCEL_OPTION);
+        /*
+         * abort closure if user hits escape or Cancel
+         */
+        if (confirm == JvOptionPane.CANCEL_OPTION
+                || confirm == JvOptionPane.CLOSED_OPTION)
+        {
+          return;
+        }
+        closeChimera = confirm == JvOptionPane.YES_OPTION;
       }
       jmb.closeViewer(closeChimera);
     }
@@ -547,12 +564,14 @@ public class ChimeraViewFrame extends StructureViewerBase
     // TODO: check for memory leaks where instance isn't finalised because jmb
     // holds a reference to the window
     jmb = null;
+    dispose();
   }
 
   /**
    * Open any newly added PDB structures in Chimera, having first fetched data
    * from PDB (if not already saved).
    */
+  @Override
   public void run()
   {
     _started = true;
@@ -562,6 +581,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     List<PDBEntry> filePDB = new ArrayList<PDBEntry>();
     List<Integer> filePDBpos = new ArrayList<Integer>();
     PDBEntry thePdbEntry = null;
+    StructureFile pdb = null;
     try
     {
       String[] curfiles = jmb.getPdbFile(); // files currently in viewer
@@ -622,15 +642,16 @@ public class ChimeraViewFrame extends StructureViewerBase
     if (errormsgs.length() > 0)
     {
 
-      JOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
+      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
@@ -651,16 +672,17 @@ public class ChimeraViewFrame extends StructureViewerBase
           {
             int pos = filePDBpos.get(num).intValue();
             long startTime = startProgressBar("Chimera "
-                    + MessageManager.getString("status.opening_file"));
+                    + 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)
             {
@@ -669,8 +691,9 @@ public class ChimeraViewFrame extends StructureViewerBase
               stopProgressBar("", startTime);
             }
             // Explicitly map to the filename used by Chimera ;
-            jmb.getSsm().setMapping(jmb.getSequence()[pos],
+            pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos],
                     jmb.getChains()[pos], pe.getFile(), protocol);
+            stashFoundChains(pdb, pe.getFile());
           } catch (OutOfMemoryError oomerror)
           {
             new OOMWarning(
@@ -686,6 +709,8 @@ public class ChimeraViewFrame extends StructureViewerBase
           }
         }
       }
+
+      jmb.refreshGUI();
       jmb.setFinishedInit(true);
       jmb.setLoadingFromArchive(false);
 
@@ -699,6 +724,7 @@ public class ChimeraViewFrame extends StructureViewerBase
       {
         new Thread(new Runnable()
         {
+          @Override
           public void run()
           {
             alignStructs_withAllAlignPanels();
@@ -720,8 +746,20 @@ public class ChimeraViewFrame extends StructureViewerBase
    * @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;
@@ -1030,10 +1068,10 @@ public class ChimeraViewFrame extends StructureViewerBase
     setChainMenuItems(jmb.getChainNames());
 
     this.setTitle(jmb.getViewerTitle("Chimera", true));
-    if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
-    {
+    // if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
+    // {
       viewerActionMenu.setVisible(true);
-    }
+    // }
     if (!jmb.isLoadingFromArchive())
     {
       seqColour_actionPerformed(null);
@@ -1091,6 +1129,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     }
   }
 
+  @Override
   public void setJalviewColourScheme(ColourSchemeI ucs)
   {
     jmb.setJalviewColourScheme(ucs);
@@ -1208,4 +1247,10 @@ public class ChimeraViewFrame extends StructureViewerBase
   {
     return jmb;
   }
+
+  @Override
+  protected List<String> getResidueAttributeNames()
+  {
+    return jmb.getResidueAttributes();
+  }
 }