JAL-1742 suppress 'add chain' option for Jmol, pull up shared code for
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 19 Aug 2015 08:39:26 +0000 (09:39 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 19 Aug 2015 08:39:26 +0000 (09:39 +0100)
Jmol/Chimera

src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/AppJmol.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/StructureViewerBase.java
src/jalview/structures/models/AAStructureBindingModel.java
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java
test/jalview/structures/models/AAStructureBindingModelTest.java

index 7cc72cf..d53d498 100644 (file)
@@ -836,7 +836,7 @@ public class ChimeraManager
       }
     } catch (Exception e)
     {
-      logger.error("REST call " + command + " failed: " + e.getMessage());
+      logger.error("REST call '" + command + "' failed: " + e.getMessage());
     } finally
     {
       if (response != null)
index 296892c..8743554 100644 (file)
@@ -63,11 +63,6 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
         implements JmolStatusListener, JmolSelectionListener,
         ComponentListener
 {
-  /*
-   * state flag used to check if the Jmol viewer's paint method can be called
-   */
-  private boolean finishedInit = false;
-
   boolean allChainsSelected = false;
 
   /*
@@ -1411,16 +1406,6 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     return null;
   }
 
-  public boolean isFinishedInit()
-  {
-    return finishedInit;
-  }
-
-  public void setFinishedInit(boolean finishedInit)
-  {
-    this.finishedInit = finishedInit;
-  }
-
   /**
    * 
    */
index cee271a..b648bea 100644 (file)
@@ -86,11 +86,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    */
   private boolean loadingFinished = true;
 
-  /*
-   * state flag used to check if the Chimera viewer's paint method can be called
-   */
-  private boolean finishedInit = false;
-
   public String fileLoadingError;
 
   /*
@@ -1079,16 +1074,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     return true;
   }
 
-  public boolean isFinishedInit()
-  {
-    return finishedInit;
-  }
-
-  public void setFinishedInit(boolean finishedInit)
-  {
-    this.finishedInit = finishedInit;
-  }
-
   /**
    * Returns a list of chains mapped in this viewer. Note this list is not
    * currently scoped per structure.
index bd1cc88..dcd4f7d 100644 (file)
@@ -27,7 +27,6 @@ import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.gui.StructureViewer.ViewerType;
-import jalview.io.AppletFormatAdapter;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
 import jalview.schemes.BuriedColourScheme;
@@ -59,6 +58,8 @@ import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
@@ -84,8 +85,6 @@ public class AppJmol extends StructureViewerBase
 
   RenderPanel renderPanel;
 
-  private boolean addingStructures = false;
-
   ViewSelectionMenu seqColourBy;
 
   /**
@@ -251,98 +250,39 @@ public class AppJmol extends StructureViewerBase
           final AlignmentPanel ap)
   {
     progressBar = ap.alignFrame;
-    // ////////////////////////////////
-    // Is the pdb file already loaded?
-    String alreadyMapped = ap.getStructureSelectionManager()
-            .alreadyMappedToFile(pdbentry.getId());
+    String pdbId = pdbentry.getId();
 
-    if (alreadyMapped != null)
+    /*
+     * 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))
     {
-      int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
-              MessageManager.formatMessage(
-                      "label.pdb_entry_is_already_displayed", new String[]
-                      { pdbentry.getId() }), MessageManager.formatMessage(
-                      "label.map_sequences_to_visible_window", new String[]
-                      { pdbentry.getId() }),
-              JOptionPane.YES_NO_CANCEL_OPTION);
-
-      if (option == JOptionPane.CANCEL_OPTION)
-      {
-        return;
-      }
-      if (option == JOptionPane.YES_OPTION)
-      {
-        // TODO : Fix multiple seq to one chain issue here.
-        ap.getStructureSelectionManager().setMapping(seq, chains,
-                alreadyMapped, AppletFormatAdapter.FILE);
-        if (ap.getSeqPanel().seqCanvas.fr != null)
-        {
-          ap.getSeqPanel().seqCanvas.fr.featuresAdded();
-          ap.paintAlignment(true);
-        }
-
-        // Now this AppJmol is mapped to new sequences. We must add them to
-        // the exisiting array
-        JInternalFrame[] frames = Desktop.instance.getAllFrames();
-
-        for (int i = 0; i < frames.length; i++)
-        {
-          if (frames[i] instanceof AppJmol)
-          {
-            final AppJmol topJmol = ((AppJmol) frames[i]);
-            // JBPNOTE: this looks like a binding routine, rather than a gui
-            // routine
-            for (int pe = 0; pe < topJmol.jmb.getPdbCount(); pe++)
-            {
-              if (topJmol.jmb.getPdbEntry(pe).getFile()
-                      .equals(alreadyMapped))
-              {
-                topJmol.jmb.addSequence(pe, seq);
-                topJmol.addAlignmentPanel(ap);
-                // add it to the set used for colouring
-                topJmol.useAlignmentPanelForColourbyseq(ap);
-                topJmol.buildActionMenu();
-                ap.getStructureSelectionManager()
-                        .sequenceColoursChanged(ap);
-                break;
-              }
-            }
-          }
-        }
-
-        return;
-      }
+      return;
     }
 
     /*
      * Check if there are other Jmol views involving this alignment and prompt
      * user about adding this molecule to one of them
      */
-    for (AppJmol topJmol : getJmolsFor(ap))
+    if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId))
     {
-      // TODO: highlight topJmol in view somehow
-      int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
-              MessageManager.formatMessage("label.add_pdbentry_to_view",
-                      new String[]
-                      { pdbentry.getId(), topJmol.getTitle() }),
-              MessageManager
-                      .getString("label.align_to_existing_structure_view"),
-              JOptionPane.YES_NO_CANCEL_OPTION);
-      if (option == JOptionPane.CANCEL_OPTION)
-      {
-        return;
-      }
-      if (option == JOptionPane.YES_OPTION)
-      {
-        topJmol.useAlignmentPanelForSuperposition(ap);
-        topJmol.addStructure(pdbentry, seq, chains, true, ap.alignFrame);
-        return;
-      }
+      return;
     }
-    // /////////////////////////////////
-    openNewJmol(ap, new PDBEntry[]
-    { pdbentry }, new SequenceI[][]
-    { seq });
+
+    /*
+     * If the options above are declined or do not apply, open a new viewer
+     */
+    openNewJmol(ap, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq });
+  }
+
+  /**
+   * Answers true if this viewer already involves the given PDB ID
+   */
+  @Override
+  protected boolean hasPdbId(String pdbId)
+  {
+    return jmb.hasPdbId(pdbId);
   }
 
   private void openNewJmol(AlignmentPanel ap, PDBEntry[] pdbentrys,
@@ -392,78 +332,23 @@ public class AppJmol extends StructureViewerBase
   }
 
   /**
-   * pdb retrieval thread.
+   * Returns a list of any Jmol viewers. The list is restricted to those linked
+   * to the given alignment panel if it is not null.
    */
-  private Thread worker = null;
-
-  /**
-   * add a new structure (with associated sequences and chains) to this viewer,
-   * retrieving it if necessary first.
-   * 
-   * @param pdbentry
-   * @param seq
-   * @param chains
-   * @param alignFrame
-   * @param align
-   *          if true, new structure(s) will be align using associated alignment
-   */
-  private void addStructure(final PDBEntry pdbentry, final SequenceI[] seq,
-          final String[] chains, final boolean b,
-          final IProgressIndicator alignFrame)
-  {
-    if (pdbentry.getFile() == null)
-    {
-      if (worker != null && worker.isAlive())
-      {
-        // a retrieval is in progress, wait around and add ourselves to the
-        // queue.
-        new Thread(new Runnable()
-        {
-          public void run()
-          {
-            while (worker != null && worker.isAlive() && _started)
-            {
-              try
-              {
-                Thread.sleep(100 + ((int) Math.random() * 100));
-
-              } catch (Exception e)
-              {
-              }
-
-            }
-            // and call ourselves again.
-            addStructure(pdbentry, seq, chains, b, alignFrame);
-          }
-        }).start();
-        return;
-      }
-    }
-    // otherwise, start adding the structure.
-    jmb.addSequenceAndChain(new PDBEntry[]
-    { pdbentry }, new SequenceI[][]
-    { seq }, new String[][]
-    { chains });
-    addingStructures = true;
-    _started = false;
-    alignAddedStructures = b;
-    progressBar = alignFrame; // visual indication happens on caller frame.
-    (worker = new Thread(this)).start();
-    return;
-  }
-
-  private Vector<AppJmol> getJmolsFor(AlignmentPanel apanel)
+  @Override
+  protected List<StructureViewerBase> getViewersFor(AlignmentPanel apanel)
   {
-    Vector<AppJmol> result = new Vector<AppJmol>();
+    List<StructureViewerBase> result = new ArrayList<StructureViewerBase>();
     JInternalFrame[] frames = Desktop.instance.getAllFrames();
 
     for (JInternalFrame frame : frames)
     {
       if (frame instanceof AppJmol)
       {
-        if (((AppJmol) frame).isLinkedWith(apanel))
+        if (apanel == null
+                || ((StructureViewerBase) frame).isLinkedWith(apanel))
         {
-          result.addElement((AppJmol) frame);
+          result.add((StructureViewerBase) frame);
         }
       }
     }
@@ -547,8 +432,6 @@ public class AppJmol extends StructureViewerBase
 
   boolean allChainsSelected = false;
 
-  private boolean alignAddedStructures = false;
-
   void centerViewer()
   {
     Vector<String> toshow = new Vector<String>();
@@ -568,8 +451,11 @@ public class AppJmol extends StructureViewerBase
 
   public void closeViewer(boolean closeExternalViewer)
   {
-    // JMol does not use an external viewer
-    jmb.closeViewer();
+    // Jmol does not use an external viewer
+    if (jmb != null)
+    {
+      jmb.closeViewer();
+    }
     setAlignmentPanel(null);
     _aps.clear();
     _alignwith.clear();
@@ -579,11 +465,6 @@ public class AppJmol extends StructureViewerBase
     jmb = null;
   }
 
-  /**
-   * state flag for PDB retrieval thread
-   */
-  private boolean _started = false;
-
   public void run()
   {
     _started = true;
@@ -1228,4 +1109,10 @@ public class AppJmol extends StructureViewerBase
     return ViewerType.JMOL;
   }
 
+  @Override
+  protected AAStructureBindingModel getBindingModel()
+  {
+    return jmb;
+  }
+
 }
index 1d60d6c..72c5054 100644 (file)
  */
 package jalview.gui;
 
+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.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.structures.models.AAStructureBindingModel;
+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;
@@ -50,31 +75,6 @@ import javax.swing.event.InternalFrameEvent;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
-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.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.structures.models.AAStructureBindingModel;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.ws.dbsources.Pdb;
-
 /**
  * GUI elements for handling an external chimera display
  * 
@@ -87,23 +87,9 @@ public class ChimeraViewFrame extends StructureViewerBase
 
   private boolean allChainsSelected = false;
 
-  private boolean alignAddedStructures = false;
-
-  /*
-   * state flag for PDB retrieval thread
-   */
-  private boolean _started = false;
-
-  private boolean addingStructures = false;
-
   private IProgressIndicator progressBar = null;
 
   /*
-   * pdb retrieval thread.
-   */
-  private Thread worker = null;
-
-  /*
    * Path to Chimera session file. This is set when an open Jalview/Chimera
    * session is saved, or on restore from a Jalview project (if it holds the
    * filename of any saved Chimera sessions).
@@ -211,158 +197,32 @@ public class ChimeraViewFrame extends StructureViewerBase
           String[] chains, final AlignmentPanel ap)
   {
     super();
-
-    /*
-     * is the pdb file already loaded?
-     */
     String pdbId = pdbentry.getId();
-    String alreadyMapped = ap.getStructureSelectionManager()
-            .alreadyMappedToFile(pdbId);
-
-    if (alreadyMapped != null)
-    {
-      int option = chooseAddSequencesToViewer(pdbId);
-      if (option == JOptionPane.CANCEL_OPTION)
-      {
-        return;
-      }
-      if (option == JOptionPane.YES_OPTION)
-      {
-        addSequenceMappingsToStructure(seq, chains, ap, alreadyMapped);
-        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
+     * If the PDB file is already loaded, the user may just choose to add to an
+     * existing viewer (or cancel)
      */
-    List<ChimeraViewFrame> existingViews = getChimeraWindowsFor(ap);
-    for (ChimeraViewFrame view : existingViews)
+    if (addAlreadyLoadedFile(seq, chains, ap, pdbId))
     {
-      // TODO: highlight view somehow
-      /*
-       * JAL-1742 exclude view with this structure already mapped (don't offer
-       * to align chain B to chain A of the same structure)
-       */
-      if (view.hasPdbId(pdbId))
-      {
-        continue;
-      }
-      int option = chooseAlignStructureToViewer(pdbId, view);
-      if (option == JOptionPane.CANCEL_OPTION)
-      {
-        return;
-      }
-      if (option == JOptionPane.YES_OPTION)
-      {
-        view.useAlignmentPanelForSuperposition(ap);
-        view.addStructure(pdbentry, seq, chains, true, ap.alignFrame);
-        return;
-      }
+      return;
     }
 
     /*
-     * If the options above are declined or do not apply, open a new viewer
-     */
-    openNewChimera(ap, new PDBEntry[]
-    { pdbentry }, new SequenceI[][]
-    { seq });
-  }
-
-  /**
-   * Presents a dialog with the option to add an align a structure to an
-   * existing Chimera view
-   * 
-   * @param pdbId
-   * @param view
-   * @return YES, NO or CANCEL JOptionPane code
-   */
-  protected int chooseAlignStructureToViewer(String pdbId,
-          ChimeraViewFrame view)
-  {
-    int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
-            MessageManager.formatMessage("label.add_pdbentry_to_view",
-                    new Object[]
-                    { pdbId, view.getTitle() }), MessageManager
-                    .getString("label.align_to_existing_structure_view"),
-            JOptionPane.YES_NO_CANCEL_OPTION);
-    return option;
-  }
-
-  /**
-   * Presents a dialog with the option to add sequences to a viewer which
-   * already has their structure open
-   * 
-   * @param pdbId
-   * @return YES, NO or CANCEL JOptionPane code
-   */
-  protected int chooseAddSequencesToViewer(String pdbId)
-  {
-    int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
-            MessageManager.formatMessage(
-                    "label.pdb_entry_is_already_displayed", new Object[]
-                    { pdbId }), MessageManager.formatMessage(
-                    "label.map_sequences_to_visible_window", new Object[]
-                    { pdbId }), JOptionPane.YES_NO_CANCEL_OPTION);
-    return option;
-  }
-
-  /**
-   * Adds mappings for the given sequences to an already opened PDB structure,
-   * and updates any viewers that have the PDB file
-   * 
-   * @param seq
-   * @param chains
-   * @param ap
-   * @param pdbFilename
-   */
-  protected void addSequenceMappingsToStructure(SequenceI[] seq,
-          String[] chains, final AlignmentPanel ap, String pdbFilename)
-  {
-    // TODO : Fix multiple seq to one chain issue here.
-    /*
-     * create the mappings
-     */
-    ap.getStructureSelectionManager().setMapping(seq, chains, pdbFilename,
-            AppletFormatAdapter.FILE);
-
-    /*
-     * alert the FeatureRenderer to show new (PDB RESNUM) features
+     * 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 (ap.getSeqPanel().seqCanvas.fr != null)
+    if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId))
     {
-      ap.getSeqPanel().seqCanvas.fr.featuresAdded();
-      ap.paintAlignment(true);
+      return;
     }
 
     /*
-     * add the sequences to any other Chimera viewers for this pdb file
+     * If the options above are declined or do not apply, show the structure in
+     * a new viewer
      */
-    // JBPNOTE: this looks like a binding routine, rather than a gui routine
-    for (JInternalFrame frame : Desktop.instance.getAllFrames())
-    {
-      if (frame instanceof ChimeraViewFrame)
-      {
-        ChimeraViewFrame chimeraView = ((ChimeraViewFrame) frame);
-        for (int pe = 0; pe < chimeraView.jmb.getPdbCount(); pe++)
-        {
-          if (chimeraView.jmb.getPdbEntry(pe).getFile().equals(pdbFilename))
-          {
-            chimeraView.jmb.addSequence(pe, seq);
-            chimeraView.addAlignmentPanel(ap);
-            /*
-             * add it to the set of alignments used for colouring structure by
-             * sequence
-             */
-            chimeraView.useAlignmentPanelForColourbyseq(ap);
-            chimeraView.buildActionMenu();
-            ap.getStructureSelectionManager().sequenceColoursChanged(ap);
-            break;
-          }
-        }
-      }
-    }
+    openNewChimera(ap, new PDBEntry[] { pdbentry },
+            new SequenceI[][] { seq });
   }
 
   /**
@@ -376,6 +236,10 @@ 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);
@@ -521,73 +385,22 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * add a new structure (with associated sequences and chains) to this viewer,
-   * retrieving it if necessary first.
-   * 
-   * @param pdbentry
-   * @param seq
-   * @param chains
-   * @param alignFrame
-   * @param align
-   *          if true, new structure(s) will be align using associated alignment
+   * 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.
    */
-  private void addStructure(final PDBEntry pdbentry, final SequenceI[] seq,
-          final String[] chains, final boolean b,
-          final IProgressIndicator alignFrame)
-  {
-    if (pdbentry.getFile() == null)
-    {
-      if (worker != null && worker.isAlive())
-      {
-        // a retrieval is in progress, wait around and add ourselves to the
-        // queue.
-        new Thread(new Runnable()
-        {
-          public void run()
-          {
-            while (worker != null && worker.isAlive() && _started)
-            {
-              try
-              {
-                Thread.sleep(100 + ((int) Math.random() * 100));
-
-              } catch (Exception e)
-              {
-              }
-
-            }
-            // and call ourselves again.
-            addStructure(pdbentry, seq, chains, b, alignFrame);
-          }
-        }).start();
-        return;
-      }
-    }
-    // otherwise, start adding the structure.
-    jmb.addSequenceAndChain(new PDBEntry[]
-    { pdbentry }, new SequenceI[][]
-    { seq }, new String[][]
-    { chains });
-    addingStructures = true;
-    _started = false;
-    alignAddedStructures = b;
-    // progressBar = alignFrame; // visual indication happens on caller frame.
-    (worker = new Thread(this)).start();
-    return;
-  }
-
-  private List<ChimeraViewFrame> getChimeraWindowsFor(AlignmentPanel apanel)
+  @Override
+  protected List<StructureViewerBase> getViewersFor(AlignmentPanel ap)
   {
-    List<ChimeraViewFrame> result = new ArrayList<ChimeraViewFrame>();
+    List<StructureViewerBase> result = new ArrayList<StructureViewerBase>();
     JInternalFrame[] frames = Desktop.instance.getAllFrames();
 
     for (JInternalFrame frame : frames)
     {
       if (frame instanceof ChimeraViewFrame)
       {
-        if (((StructureViewerBase) frame).isLinkedWith(apanel))
+        if (ap == null || ((StructureViewerBase) frame).isLinkedWith(ap))
         {
-          result.add((ChimeraViewFrame) frame);
+          result.add((StructureViewerBase) frame);
         }
       }
     }
@@ -713,7 +526,7 @@ public class ChimeraViewFrame extends StructureViewerBase
    */
   public void closeViewer(boolean closeChimera)
   {
-    if (jmb.isChimeraRunning())
+    if (jmb != null && jmb.isChimeraRunning())
     {
       if (!closeChimera)
       {
@@ -1395,4 +1208,10 @@ public class ChimeraViewFrame extends StructureViewerBase
   {
     return ViewerType.CHIMERA;
   }
+
+  @Override
+  protected AAStructureBindingModel getBindingModel()
+  {
+    return jmb;
+  }
 }
index 1feaa7c..587e08a 100644 (file)
@@ -1,15 +1,21 @@
 package jalview.gui;
 
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.gui.ViewSelectionMenu.ViewSetProvider;
+import jalview.io.AppletFormatAdapter;
+import jalview.jbgui.GStructureViewer;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.MessageManager;
+
 import java.awt.Component;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Vector;
 
 import javax.swing.JMenuItem;
-
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.gui.ViewSelectionMenu.ViewSetProvider;
-import jalview.jbgui.GStructureViewer;
+import javax.swing.JOptionPane;
 
 /**
  * Base class with common functionality for JMol, Chimera or other structure
@@ -37,6 +43,10 @@ public abstract class StructureViewerBase extends GStructureViewer
   protected Vector<AlignmentPanel> _colourwith = new Vector<AlignmentPanel>();
   private String viewId = null;
   private AlignmentPanel ap;
+  protected boolean alignAddedStructures = false;
+  protected boolean _started = false;
+  protected boolean addingStructures = false;
+  protected Thread worker = null;
 
   /**
    * 
@@ -225,4 +235,227 @@ public abstract class StructureViewerBase extends GStructureViewer
   }
 
   public abstract ViewerType getViewerType();
+
+  protected abstract AAStructureBindingModel getBindingModel();
+
+  /**
+   * add a new structure (with associated sequences and chains) to this viewer,
+   * retrieving it if necessary first.
+   * 
+   * @param pdbentry
+   * @param seqs
+   * @param chains
+   * @param align
+   *          if true, new structure(s) will be aligned using associated
+   *          alignment
+   * @param alignFrame
+   */
+  protected void addStructure(final PDBEntry pdbentry, final SequenceI[] seqs, final String[] chains,
+          final boolean align, final IProgressIndicator alignFrame)
+  {
+    if (pdbentry.getFile() == null)
+    {
+      if (worker != null && worker.isAlive())
+      {
+        // a retrieval is in progress, wait around and add ourselves to the
+        // queue.
+        new Thread(new Runnable()
+        {
+          public void run()
+          {
+            while (worker != null && worker.isAlive() && _started)
+            {
+              try
+              {
+                Thread.sleep(100 + ((int) Math.random() * 100));
+  
+              } catch (Exception e)
+              {
+              }
+            }
+            // and call ourselves again.
+            addStructure(pdbentry, seqs, chains, align, alignFrame);
+          }
+        }).start();
+        return;
+      }
+    }
+    // otherwise, start adding the structure.
+    getBindingModel().addSequenceAndChain(new PDBEntry[] { pdbentry },
+            new SequenceI[][] { seqs }, new String[][] { chains });
+    addingStructures = true;
+    _started = false;
+    alignAddedStructures = align;
+    worker = new Thread(this);
+    worker.start();
+    return;
+  }
+
+  /**
+   * Presents a dialog with the option to add an align a structure to an
+   * existing structure view
+   * 
+   * @param pdbId
+   * @param view
+   * @return YES, NO or CANCEL JOptionPane code
+   */
+  protected int chooseAlignStructureToViewer(String pdbId, StructureViewerBase view)
+  {
+    int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
+            MessageManager.formatMessage("label.add_pdbentry_to_view",
+                    new Object[]
+                    { pdbId, view.getTitle() }), MessageManager
+                    .getString("label.align_to_existing_structure_view"),
+            JOptionPane.YES_NO_CANCEL_OPTION);
+    return option;
+  }
+
+  protected abstract boolean hasPdbId(String pdbId);
+
+  protected abstract List<StructureViewerBase> getViewersFor(
+          AlignmentPanel alp);
+
+  /**
+   * Check for any existing views involving this alignment and give user the
+   * option to add and align this molecule to one of them
+   * 
+   * @param pdbentry
+   * @param seq
+   * @param chains
+   * @param apanel
+   * @param pdbId
+   * @return true if user adds to a view, or cancels entirely, else false
+   */
+  protected boolean addToExistingViewer(PDBEntry pdbentry, SequenceI[] seq,
+          String[] chains, final AlignmentPanel apanel, String pdbId)
+  {
+    for (StructureViewerBase view : getViewersFor(apanel))
+    {
+      // TODO: highlight the view somehow
+      /*
+       * JAL-1742 exclude view with this structure already mapped (don't offer
+       * to align chain B to chain A of the same structure)
+       */
+      if (view.hasPdbId(pdbId))
+      {
+        continue;
+      }
+      int option = chooseAlignStructureToViewer(pdbId, view);
+      if (option == JOptionPane.CANCEL_OPTION)
+      {
+        return true;
+      }
+      else if (option == JOptionPane.YES_OPTION)
+      {
+        view.useAlignmentPanelForSuperposition(apanel);
+        view.addStructure(pdbentry, seq, chains, true, apanel.alignFrame);
+        return true;
+      }
+      else
+      {
+        // NO_OPTION - offer the next viewer if any
+      }
+    }
+  
+    /*
+     * nothing offered and selected
+     */
+    return false;
+  }
+
+  /**
+   * Adds mappings for the given sequences to an already opened PDB structure,
+   * and updates any viewers that have the PDB file
+   * 
+   * @param seq
+   * @param chains
+   * @param apanel
+   * @param pdbFilename
+   */
+  protected void addSequenceMappingsToStructure(SequenceI[] seq,
+          String[] chains, final AlignmentPanel apanel, String pdbFilename)
+  {
+    // TODO : Fix multiple seq to one chain issue here.
+    /*
+     * create the mappings
+     */
+    apanel.getStructureSelectionManager().setMapping(seq, chains, pdbFilename,
+            AppletFormatAdapter.FILE);
+  
+    /*
+     * alert the FeatureRenderer to show new (PDB RESNUM) features
+     */
+    if (apanel.getSeqPanel().seqCanvas.fr != null)
+    {
+      apanel.getSeqPanel().seqCanvas.fr.featuresAdded();
+      apanel.paintAlignment(true);
+    }
+  
+    /*
+     * add the sequences to any other viewers (of the same type) for this pdb
+     * file
+     */
+    // JBPNOTE: this looks like a binding routine, rather than a gui routine
+    for (StructureViewerBase viewer : getViewersFor(null))
+    {
+      AAStructureBindingModel bindingModel = viewer.getBindingModel();
+      for (int pe = 0; pe < bindingModel.getPdbCount(); pe++)
+      {
+        if (bindingModel.getPdbEntry(pe).getFile().equals(pdbFilename))
+        {
+          bindingModel.addSequence(pe, seq);
+          viewer.addAlignmentPanel(apanel);
+          /*
+           * add it to the set of alignments used for colouring structure by
+           * sequence
+           */
+          viewer.useAlignmentPanelForColourbyseq(apanel);
+          viewer.buildActionMenu();
+          apanel.getStructureSelectionManager().sequenceColoursChanged(apanel);
+          break;
+        }
+      }
+    }
+  }
+
+  /**
+   * Check if the PDB file is already loaded, if so offer to add it to the
+   * existing viewer
+   * 
+   * @param seq
+   * @param chains
+   * @param apanel
+   * @param pdbId
+   * @return true if the user chooses to add to a viewer, or to cancel entirely
+   */
+  protected boolean addAlreadyLoadedFile(SequenceI[] seq, String[] chains,
+          final AlignmentPanel apanel, String pdbId)
+  {
+    boolean finished = false;
+    String alreadyMapped = apanel.getStructureSelectionManager()
+            .alreadyMappedToFile(pdbId);
+  
+    if (alreadyMapped != null)
+    {
+      /*
+       * the PDB file is already loaded
+       */
+      int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
+      MessageManager.formatMessage(
+              "label.pdb_entry_is_already_displayed", new Object[]
+              { pdbId }), MessageManager.formatMessage(
+              "label.map_sequences_to_visible_window", new Object[]
+              { pdbId }), JOptionPane.YES_NO_CANCEL_OPTION);
+      if (option == JOptionPane.CANCEL_OPTION)
+      {
+        finished = true;
+      }
+      else if (option == JOptionPane.YES_OPTION)
+      {
+        addSequenceMappingsToStructure(seq, chains, apanel, alreadyMapped);
+        finished = true;
+      }
+    }
+    return finished;
+  }
 }
index e77a23c..b844b52 100644 (file)
@@ -52,6 +52,8 @@ public abstract class AAStructureBindingModel extends
 
   private boolean nucleotide;
 
+  private boolean finishedInit = false;
+
   /**
    * Data bean class to simplify parameterisation in superposeStructures
    */
@@ -623,4 +625,14 @@ public abstract class AAStructureBindingModel extends
     }
     return false;
   }
+
+  public boolean isFinishedInit()
+  {
+    return finishedInit;
+  }
+
+  public void setFinishedInit(boolean fi)
+  {
+    this.finishedInit = fi;
+  }
 }
\ No newline at end of file
index 4bfabb3..b9cab9c 100644 (file)
@@ -1,10 +1,13 @@
 package jalview.ext.rbvi.chimera;
 
+import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
+import jalview.gui.Preferences;
 import jalview.gui.StructureViewer;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.FormatAdapter;
@@ -32,19 +35,13 @@ public class JalviewChimeraView
   @AfterClass
   public static void tearDownAfterClass() throws Exception
   {
-    try
-    {
     jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
-    } catch (Exception e)
-    {
-      // ignore NullPointerException thrown by JMol
-    }
-
   }
 
   @Test(groups ={ "Functional" })
   public void testSingleSeqViewJMol()
   {
+    Cache.setProperty(Preferences.STRUCTURE_DISPLAY, ViewerType.JMOL.name());
     String inFile = "examples/1gaq.txt";
     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
             inFile, FormatAdapter.FILE);
@@ -68,11 +65,14 @@ public class JalviewChimeraView
                           new SequenceI[]
                   { sq }, af.getCurrentView().getAlignPanel());
           /*
-           * Wait for viewer thread to start
+           * Wait for viewer load thread to complete
            */
           try
           {
-            Thread.sleep(1000);
+            while (!jmolViewer.getBinding().isFinishedInit())
+            {
+              Thread.sleep(500);
+            }
           } catch (InterruptedException e)
           {
           }
@@ -89,12 +89,15 @@ public class JalviewChimeraView
   @Test(groups ={ "Functional" })
   public void testSingleSeqViewChimera()
   {
+    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
+            ViewerType.CHIMERA.name());
     String inFile = "examples/1gaq.txt";
     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
             inFile, FormatAdapter.FILE);
     assertTrue("Didn't read input file " + inFile, af != null);
     for (SequenceI sq : af.getViewport().getAlignment().getSequences())
     {
+      System.out.println("** sq=" + sq.getName());
       SequenceI dsq = sq.getDatasetSequence();
       while (dsq.getDatasetSequence() != null)
       {
@@ -112,14 +115,18 @@ public class JalviewChimeraView
                           new SequenceI[]
                           { sq }, af.getCurrentView().getAlignPanel());
           /*
-           * Wait for viewer thread to start
+           * Wait for viewer load thread to complete
            */
-          try
-          {
-            Thread.sleep(1000);
-          } catch (InterruptedException e)
+          while (!chimeraViewer.getBinding().isFinishedInit())
           {
+            try
+            {
+              Thread.sleep(500);
+            } catch (InterruptedException e)
+            {
+            }
           }
+          assertEquals(1, chimeraViewer.getBinding().getPdbCount());
           chimeraViewer.closeViewer(true);
           // todo: break here means only once through this loop?
           break;
index 3354e38..1466760 100644 (file)
@@ -116,7 +116,7 @@ public class AAStructureBindingModelTest
   }
 
   /**
-   * Verify that the method determines that columns 2, 5 and 6 of the aligment
+   * Verify that the method determines that columns 2, 5 and 6 of the alignment
    * are alignable in structure
    */
   @Test(groups ={ "Functional" })