Merge branch 'releases/Release_2_11_3_Branch'
[jalview.git] / src / jalview / gui / StructureViewerBase.java
index 1e12f7f..bd757e8 100644 (file)
@@ -26,6 +26,7 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
+import java.beans.PropertyVetoException;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -34,6 +35,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 import java.util.Vector;
 
@@ -46,6 +48,7 @@ import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
 import jalview.api.AlignmentViewPanel;
+import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.bin.Console;
 import jalview.datamodel.AlignmentI;
@@ -87,6 +90,30 @@ public abstract class StructureViewerBase extends GStructureViewer
   }
 
   /**
+   * Singleton list of all (open) instances of structureViewerBase TODO:
+   * JAL-3362 - review and adopt the swingJS-safe singleton pattern so each
+   * structure viewer base instance is kept to its own JalviewJS parent
+   */
+  private static List<JalviewStructureDisplayI> svbs = new ArrayList<>();
+
+  /**
+   * 
+   * @return list with all existing StructureViewers instance
+   */
+  public static List<JalviewStructureDisplayI> getAllStructureViewerBases()
+  {
+    List<JalviewStructureDisplayI> goodSvbs = new ArrayList<>();
+    for (JalviewStructureDisplayI s : svbs)
+    {
+      if (s != null && !goodSvbs.contains(s))
+      {
+        goodSvbs.add(s);
+      }
+    }
+    return goodSvbs;
+  }
+
+  /**
    * list of sequenceSet ids associated with the view
    */
   protected List<String> _aps = new ArrayList<>();
@@ -116,6 +143,8 @@ public abstract class StructureViewerBase extends GStructureViewer
 
   protected boolean allChainsSelected = false;
 
+  protected boolean allHetatmBeingSelected = false;
+
   protected JMenu viewSelectionMenu;
 
   /**
@@ -136,6 +165,7 @@ public abstract class StructureViewerBase extends GStructureViewer
   {
     super();
     setFrameIcon(null);
+    svbs.add(this);
   }
 
   /**
@@ -179,6 +209,7 @@ public abstract class StructureViewerBase extends GStructureViewer
     return _aps.contains(ap2.av.getSequenceSetId());
   }
 
+  @Override
   public boolean isUsedforaligment(AlignmentViewPanel ap2)
   {
 
@@ -578,6 +609,88 @@ public abstract class StructureViewerBase extends GStructureViewer
     }
   }
 
+  void setHetatmMenuItems(Map<String, String> hetatmNames)
+  {
+    hetatmMenu.removeAll();
+    if (hetatmNames == null || hetatmNames.isEmpty())
+    {
+      hetatmMenu.setVisible(false);
+      return;
+    }
+    hetatmMenu.setVisible(true);
+    allHetatmBeingSelected = false;
+    JMenuItem allMenuItem = new JMenuItem(
+            MessageManager.getString("label.all"));
+    JMenuItem noneMenuItem = new JMenuItem(
+            MessageManager.getString("label.none"));
+    allMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        {
+          allHetatmBeingSelected = true;
+          // Toggle state of everything - on
+          for (int i = 0; i < hetatmMenu.getItemCount(); i++)
+          {
+            if (hetatmMenu.getItem(i) instanceof JCheckBoxMenuItem)
+            {
+              ((JCheckBoxMenuItem) hetatmMenu.getItem(i)).setSelected(true);
+            }
+          }
+          allHetatmBeingSelected = false;
+          showSelectedHetatms();
+        }
+      }
+    });
+
+    noneMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        {
+          allHetatmBeingSelected = true;
+          // Toggle state of everything off
+          for (int i = 0; i < hetatmMenu.getItemCount(); i++)
+          {
+            if (hetatmMenu.getItem(i) instanceof JCheckBoxMenuItem)
+            {
+              ((JCheckBoxMenuItem) hetatmMenu.getItem(i))
+                      .setSelected(false);
+            }
+          }
+          allHetatmBeingSelected = false;
+          showSelectedHetatms();
+        }
+      }
+    });
+    hetatmMenu.add(noneMenuItem);
+    hetatmMenu.add(allMenuItem);
+
+    for (Map.Entry<String, String> chain : hetatmNames.entrySet())
+    {
+      JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(chain.getKey(),
+              false);
+      menuItem.setToolTipText(chain.getValue());
+      menuItem.addItemListener(new ItemListener()
+      {
+        @Override
+        public void itemStateChanged(ItemEvent evt)
+        {
+          if (!allHetatmBeingSelected)
+          {
+            // update viewer only when we were clicked, not programmatically
+            // checked/unchecked
+            showSelectedHetatms();
+          }
+        }
+      });
+
+      hetatmMenu.add(menuItem);
+    }
+  }
+
   /**
    * Action on selecting one of Jalview's registered colour schemes
    */
@@ -982,6 +1095,7 @@ public abstract class StructureViewerBase extends GStructureViewer
       return;
     }
     setChainMenuItems(binding.getChainNames());
+    setHetatmMenuItems(binding.getHetatmNames());
 
     this.setTitle(binding.getViewerTitle(getViewerName(), true));
 
@@ -1133,6 +1247,26 @@ public abstract class StructureViewerBase extends GStructureViewer
   }
 
   /**
+   * Display selected hetatms in viewer
+   */
+  protected void showSelectedHetatms()
+  {
+    List<String> toshow = new ArrayList<>();
+    for (int i = 0; i < hetatmMenu.getItemCount(); i++)
+    {
+      if (hetatmMenu.getItem(i) instanceof JCheckBoxMenuItem)
+      {
+        JCheckBoxMenuItem item = (JCheckBoxMenuItem) hetatmMenu.getItem(i);
+        if (item.isSelected())
+        {
+          toshow.add(item.getText());
+        }
+      }
+    }
+    getBinding().showHetatms(toshow);
+  }
+
+  /**
    * Tries to fetch a PDB file and save to a temporary local file. Returns the
    * saved file path if successful, or null if not.
    * 
@@ -1196,7 +1330,7 @@ public abstract class StructureViewerBase extends GStructureViewer
       }
     } catch (Exception e)
     {
-      System.err.println(
+      jalview.bin.Console.errPrintln(
               "Error retrieving PDB id " + pdbid + ": " + e.getMessage());
     } finally
     {
@@ -1251,6 +1385,20 @@ public abstract class StructureViewerBase extends GStructureViewer
     return session;
   }
 
+  private static boolean quitClose = false;
+
+  public static void setQuitClose(boolean b)
+  {
+    quitClose = b;
+  }
+
+  @Override
+  public boolean stillRunning()
+  {
+    AAStructureBindingModel binding = getBinding();
+    return binding != null && binding.isViewerRunning();
+  }
+
   /**
    * 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
@@ -1263,18 +1411,30 @@ public abstract class StructureViewerBase extends GStructureViewer
   public void closeViewer(boolean forceClose)
   {
     AAStructureBindingModel binding = getBinding();
-    if (binding != null && binding.isViewerRunning())
+    if (stillRunning())
     {
       if (!forceClose)
       {
         String viewerName = getViewerName();
-        String prompt = MessageManager
-                .formatMessage("label.confirm_close_viewer", new Object[]
-                { binding.getViewerTitle(viewerName, false), viewerName });
-        prompt = JvSwingUtils.wrapTooltip(true, prompt);
-        int confirm = JvOptionPane.showConfirmDialog(this, prompt,
-                MessageManager.getString("label.close_viewer"),
-                JvOptionPane.YES_NO_CANCEL_OPTION);
+
+        int confirm = JvOptionPane.CANCEL_OPTION;
+        if (QuitHandler.quitting())
+        {
+          // already asked about closing external windows
+          confirm = quitClose ? JvOptionPane.YES_OPTION
+                  : JvOptionPane.NO_OPTION;
+        }
+        else
+        {
+          String prompt = MessageManager
+                  .formatMessage("label.confirm_close_viewer", new Object[]
+                  { binding.getViewerTitle(viewerName, false),
+                      viewerName });
+          prompt = JvSwingUtils.wrapTooltip(true, prompt);
+          String title = MessageManager.getString("label.close_viewer");
+          confirm = showCloseDialog(title, prompt);
+        }
+
         /*
          * abort closure if user hits escape or Cancel
          */
@@ -1283,7 +1443,16 @@ public abstract class StructureViewerBase extends GStructureViewer
         {
           // abort possible quit handling if CANCEL chosen
           if (confirm == JvOptionPane.CANCEL_OPTION)
+          {
+            try
+            {
+              // this is a bit futile
+              this.setClosed(false);
+            } catch (PropertyVetoException e)
+            {
+            }
             QuitHandler.abortQuit();
+          }
           return;
         }
         forceClose = confirm == JvOptionPane.YES_OPTION;
@@ -1300,9 +1469,29 @@ public abstract class StructureViewerBase extends GStructureViewer
     // TODO: check for memory leaks where instance isn't finalised because jmb
     // holds a reference to the window
     // jmb = null;
+
+    try
+    {
+      svbs.remove(this);
+    } catch (Throwable t)
+    {
+      Console.info(
+              "Unexpected exception when deregistering structure viewer",
+              t);
+    }
     dispose();
   }
 
+  private int showCloseDialog(final String title, final String prompt)
+  {
+    int confirmResponse = JvOptionPane.CANCEL_OPTION;
+    confirmResponse = JvOptionPane.showConfirmDialog(this, prompt,
+            MessageManager.getString("label.close_viewer"),
+            JvOptionPane.YES_NO_CANCEL_OPTION,
+            JvOptionPane.WARNING_MESSAGE);
+    return confirmResponse;
+  }
+
   @Override
   public void showHelp_actionPerformed()
   {
@@ -1333,4 +1522,5 @@ public abstract class StructureViewerBase extends GStructureViewer
             && viewerActionMenu.getItemCount() > 0
             && viewerActionMenu.isVisible();
   }
+
 }