JAL-1753 ChimeraViewFrame hosts its own ProgressBar
[jalview.git] / src / jalview / gui / ChimeraViewFrame.java
index 4e313b5..fdc7d0b 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 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.
  * 
  */
 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.Jalview2XML.ViewerData;
-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;
@@ -57,6 +32,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Random;
 import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
@@ -70,6 +46,30 @@ 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.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 handlnig an external chimera display
  * 
@@ -99,9 +99,13 @@ public class ChimeraViewFrame extends StructureViewerBase
   private Thread worker = null;
 
   /*
-   * Path to Chimera session file - set in saveSession()
+   * 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).
    */
-  private String chimeraSessionFile = "";
+  private String chimeraSessionFile = null;
+
+  private Random random = new Random();
 
   /**
    * Initialise menu options.
@@ -271,6 +275,14 @@ public class ChimeraViewFrame extends StructureViewerBase
     for (ChimeraViewFrame topView : existingViews)
     {
       // TODO: highlight topView in 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 (topView.hasPdbId(pdbentry.getId()))
+      {
+        continue;
+      }
       int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
               MessageManager.formatMessage("label.add_pdbentry_to_view",
                       new Object[]
@@ -295,10 +307,27 @@ public class ChimeraViewFrame extends StructureViewerBase
     { seq });
   }
 
+  /**
+   * Create a helper to manage progress bar display
+   */
+  protected void createProgressBar()
+  {
+    if (progressBar == null)
+    {
+      progressBar = new ProgressBar(statusPanel, statusBar);
+    }
+  }
+
+  protected boolean hasPdbId(String pdbId)
+  {
+    return jmb.hasPdbId(pdbId);
+  }
+
   private void openNewChimera(AlignmentPanel ap, PDBEntry[] pdbentrys,
           SequenceI[][] seqs)
   {
-    progressBar = ap.alignFrame;
+    createProgressBar();
+
     jmb = new JalviewChimeraBindingModel(this,
             ap.getStructureSelectionManager(), pdbentrys, seqs, null, null);
     addAlignmentPanel(ap);
@@ -311,17 +340,16 @@ public class ChimeraViewFrame extends StructureViewerBase
     jmb.setColourBySequence(true);
     setSize(400, 400); // probably should be a configurable/dynamic default here
     initMenus();
-    worker = null;
-    {
-      addingStructures = false;
-      worker = new Thread(this);
-      worker.start();
-    }
+
+    addingStructures = false;
+    worker = new Thread(this);
+    worker.start();
+
     this.addInternalFrameListener(new InternalFrameAdapter()
     {
       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
       {
-        closeViewer();
+        closeViewer(false);
       }
     });
 
@@ -343,20 +371,37 @@ public class ChimeraViewFrame extends StructureViewerBase
   }
 
   /**
-   * Create a new viewer from saved session state data.
+   * Create a new viewer from saved session state data including Chimera session
+   * file.
+   * 
+   * @param chimeraSession
    * 
-   * @param viewerData
-   * @param af
+   * @param alignPanel
+   * @param pdbArray
+   * @param seqsArray
+   * @param colourByChimera
+   * @param colourBySequence
    */
-  public ChimeraViewFrame(ViewerData viewerData, AlignFrame af)
+  public ChimeraViewFrame(String chimeraSession, AlignmentPanel alignPanel,
+          PDBEntry[] pdbArray,
+          SequenceI[][] seqsArray, boolean colourByChimera,
+          boolean colourBySequence)
   {
     super();
-    String chimeraSessionFile = viewerData.stateData;
-    openNewChimera(af.alignPanel, new PDBEntry[]
-    {}, new SequenceI[][]
-    {});
-    initChimera("open " + chimeraSessionFile);
-    // TODO restore mappings
+    this.chimeraSessionFile = chimeraSession;
+    openNewChimera(alignPanel, pdbArray, seqsArray);
+    if (colourByChimera)
+    {
+      jmb.setColourBySequence(false);
+      seqColour.setSelected(false);
+      viewerColour.setSelected(true);
+    }
+    else if (colourBySequence)
+    {
+      jmb.setColourBySequence(true);
+      seqColour.setSelected(true);
+      viewerColour.setSelected(false);
+    }
   }
 
   /**
@@ -433,19 +478,34 @@ public class ChimeraViewFrame extends StructureViewerBase
     return result;
   }
 
-  void initChimera(String command)
+  /**
+   * 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);
-    // TODO: consider waiting until the structure/view is fully loaded before
-    // displaying
     jalview.gui.Desktop.addInternalFrame(this, jmb.getViewerTitle("Chimera", true),
             getBounds().width, getBounds().height);
-    if (command == null)
+
+    /*
+     * Pass an empty 'command' to launch Chimera
+     */
+    jmb.evalStateCommand("", false);
+
+    if (this.chimeraSessionFile != null)
     {
-      command = "";
+      boolean opened = jmb.openSession(chimeraSessionFile);
+      if (!opened)
+      {
+        System.err
+                .println("An error occurred opening Chimera session file "
+                        + chimeraSessionFile);
+      }
     }
-    jmb.evalStateCommand(command, false);
     jmb.setFinishedInit(true);
+
+    jmb.startChimeraListener();
   }
 
   void setChainMenuItems(List<String> chainNames)
@@ -515,19 +575,26 @@ public class ChimeraViewFrame extends StructureViewerBase
    * Close down this instance of Jalview's Chimera viewer, giving the user the
    * option to close the associated Chimera window (process). They may wish to
    * keep it open until they have had an opportunity to save any work.
+   * 
+   * @param closeChimera
+   *          if true, close any linked Chimera process; if false, prompt first
    */
-  public void closeViewer()
+  public void closeViewer(boolean closeChimera)
   {
     if (jmb.isChimeraRunning())
     {
-      String prompt = MessageManager
-              .formatMessage("label.confirm_close_chimera", new Object[]
-              { jmb.getViewerTitle("Chimera", false) });
-      prompt = JvSwingUtils.wrapTooltip(true, prompt);
-      int confirm = JOptionPane.showConfirmDialog(this, prompt,
-              MessageManager.getString("label.close_viewer"),
-              JOptionPane.YES_NO_OPTION);
-      jmb.closeViewer(confirm == JOptionPane.YES_OPTION);
+      if (!closeChimera)
+      {
+        String prompt = MessageManager.formatMessage(
+                "label.confirm_close_chimera", new Object[]
+                { jmb.getViewerTitle("Chimera", false) });
+        prompt = JvSwingUtils.wrapTooltip(true, prompt);
+        int confirm = JOptionPane.showConfirmDialog(this, prompt,
+                MessageManager.getString("label.close_viewer"),
+                JOptionPane.YES_NO_OPTION);
+        closeChimera = confirm == JOptionPane.YES_OPTION;
+      }
+      jmb.closeViewer(closeChimera);
     }
     setAlignmentPanel(null);
     _aps.clear();
@@ -625,7 +692,7 @@ public class ChimeraViewFrame extends StructureViewerBase
       {
         try
         {
-          initChimera("");
+          initChimera();
         } catch (Exception ex)
         {
           Cache.log.error("Couldn't open Chimera viewer!", ex);
@@ -640,6 +707,8 @@ public class ChimeraViewFrame extends StructureViewerBase
           try
           {
             int pos = filePDBpos.get(num).intValue();
+            long startTime = startProgressBar("Chimera "
+                    + MessageManager.getString("status.opening_file"));
             jmb.openFile(pe);
             jmb.addSequence(pos, jmb.getSequence()[pos]);
             File fl = new File(pe.getFile());
@@ -652,6 +721,9 @@ public class ChimeraViewFrame extends StructureViewerBase
               }
             } catch (Throwable e)
             {
+            } finally
+            {
+              stopProgressBar("", startTime);
             }
             // Explicitly map to the filename used by Chimera ;
             // TODO: use pe.getId() instead of pe.getFile() ?
@@ -713,13 +785,9 @@ public class ChimeraViewFrame extends StructureViewerBase
     Pdb pdbclient = new Pdb();
     AlignmentI pdbseq = null;
     String pdbid = processingEntry.getId();
-    long hdl = pdbid.hashCode() - System.currentTimeMillis();
-    if (progressBar != null)
-    {
-      progressBar.setProgressBar(MessageManager.formatMessage(
-              "status.fetching_pdb", new Object[]
-              { pdbid }), hdl);
-    }
+    long hdl = startProgressBar(MessageManager.formatMessage(
+            "status.fetching_pdb", new Object[]
+            { pdbid }));
     try
     {
       pdbseq = pdbclient.getSequenceRecords(pdbid);
@@ -728,16 +796,9 @@ public class ChimeraViewFrame extends StructureViewerBase
       new OOMWarning("Retrieving PDB id " + pdbid, oomerror);
     } finally
     {
-      if (progressBar != null)
-      {
-        progressBar
-                .setProgressBar(
-                        pdbid
-                                + " "
-                                + MessageManager
-                                        .getString("label.state_completed"),
-                        hdl);
-      }
+      String msg = pdbid + " "
+              + MessageManager.getString("label.state_completed");
+      stopProgressBar(msg, hdl);
     }
     /*
      * If PDB data were saved and are not invalid (empty alignment), return the
@@ -746,13 +807,40 @@ public class ChimeraViewFrame extends StructureViewerBase
     if (pdbseq != null && pdbseq.getHeight() > 0)
     {
       // just use the file name from the first sequence's first PDBEntry
-      filePath = new File(((PDBEntry) pdbseq.getSequenceAt(0).getPDBId()
-              .elementAt(0)).getFile()).getAbsolutePath();
+      filePath = new File(pdbseq.getSequenceAt(0).getPDBId()
+              .elementAt(0).getFile()).getAbsolutePath();
       processingEntry.setFile(filePath);
     }
     return filePath;
   }
 
+  /**
+   * Convenience method to update the progress bar if there is one. Be sure to
+   * call stopProgressBar with the returned handle to remove the message.
+   * 
+   * @param msg
+   * @param handle
+   */
+  public long startProgressBar(String msg)
+  {
+    // TODO would rather have startProgress/stopProgress as the
+    // IProgressIndicator interface
+    long tm = random.nextLong();
+    if (progressBar != null)
+    {
+      progressBar.setProgressBar(msg, tm);
+    }
+    return tm;
+  }
+
+  public void stopProgressBar(String msg, long handle)
+  {
+    if (progressBar != null)
+    {
+      progressBar.setProgressBar(msg, handle);
+    }
+  }
+
   @Override
   public void pdbFile_actionPerformed(ActionEvent actionEvent)
   {
@@ -874,7 +962,7 @@ public class ChimeraViewFrame extends StructureViewerBase
       // Set the colour using the current view for the associated alignframe
       for (AlignmentPanel ap : _colourwith)
       {
-        jmb.colourBySequence(ap.av.showSequenceFeatures, ap);
+        jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
       }
     }
   }
@@ -986,7 +1074,7 @@ public class ChimeraViewFrame extends StructureViewerBase
       repaint();
       return;
     }
-    setChainMenuItems(jmb.chainNames);
+    setChainMenuItems(jmb.getChainNames());
 
     this.setTitle(jmb.getViewerTitle("Chimera", true));
     if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)