Merge branch 'develop' into releases/Release_2_11_2_Branch
authorJim Procter <j.procter@dundee.ac.uk>
Mon, 8 Aug 2022 15:42:51 +0000 (16:42 +0100)
committerJim Procter <j.procter@dundee.ac.uk>
Mon, 8 Aug 2022 15:42:51 +0000 (16:42 +0100)
doc/building.md
help/help/html/features/3dbeacons_button.png
help/help/html/features/schooser_main.png
help/markdown/releases/release-2_11_2_4.md
help/markdown/whatsnew/whatsnew-2_11_2_4.md [new file with mode: 0644]
resources/lang/Messages.properties
src/jalview/gui/JvOptionPane.java
src/jalview/gui/StructureChooser.java
src/jalview/jbgui/GStructureChooser.java

index 8e21d57..34d5d67 100644 (file)
@@ -6,7 +6,7 @@
 # download
 git clone http://source.jalview.org/git/jalview.git
 # compile
-cd jalview
+cd ./jalview
 gradle shadowJar
 # run
 java -jar build/libs/jalview-all-*-j11.jar
@@ -14,7 +14,7 @@ java -jar build/libs/jalview-all-*-j11.jar
 # and/or create launcher
 gradle getdown
 # use launcher
-cd getdown/files
+cd ./build/getdown/files/11
 java -jar getdown-launcher.jar . jalview
 ```
 
index 2dae1f6..b80f70b 100644 (file)
Binary files a/help/help/html/features/3dbeacons_button.png and b/help/help/html/features/3dbeacons_button.png differ
index 07a1a3c..5a7fe0a 100644 (file)
Binary files a/help/help/html/features/schooser_main.png and b/help/help/html/features/schooser_main.png differ
index 4f1e8d7..d9050be 100644 (file)
@@ -7,6 +7,10 @@ channel: "release"
 ## New Features
 
 - <!-- JAL-4036 --> Migrated Uniprot Free Text Search to latest Uniprot search API
+- <!-- JAL-4034 --> Improved Structure Chooser's 3D-Beacons search button design and visual delay indicators
+- <!-- JAL-4034 --> 3D beacons Fetch Uniprot References confirmation dialog is only shown when number of sequences exceeds a threshold
+- <!-- -->
+
 
 
 
diff --git a/help/markdown/whatsnew/whatsnew-2_11_2_4.md b/help/markdown/whatsnew/whatsnew-2_11_2_4.md
new file mode 100644 (file)
index 0000000..250a0b7
--- /dev/null
@@ -0,0 +1,2 @@
+Jalview 2.11.2.4 is the fourth patch release in the 2.11.2 series. It includes a new client for the latest Uniprot Search API (released June 2022), updated build documentation and improved user experience when discovering models and experimental structures from 3D-Beacons.
+
index 9ce5a63..dda5624 100644 (file)
@@ -514,11 +514,11 @@ label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences =
 label.standard_databases = Standard Databases
 label.fetch_embl_uniprot = Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources
 label.fetch_uniprot_references = Fetch Uniprot references
-label.search_3dbeacons = 3D-Beacons Search
+label.search_3dbeacons = Search 3D-Beacons
 label.find_models_from_3dbeacons = Search 3D-Beacons for 3D structures and models
 label.3dbeacons = 3D-Beacons
 label.fetch_references_for = Fetch database references for {0} sequences ?
-label.fetch_references_for_3dbeacons = 3D Beacons needs Uniprot References. Fetch database references for {0} sequences ?
+label.fetch_references_for_3dbeacons = 3D Beacons needs to fetch Uniprot References for {0} sequences.  Do you want to continue ?
 label.reset_min_max_colours_to_defaults = Reset min and max colours to defaults from user preferences.
 label.align_structures_using_linked_alignment_views = Superpose structures using {0} selected alignment view(s)
 label.threshold_feature_display_by_score = Threshold the feature display by score.
index 733223d..028e50b 100644 (file)
 package jalview.gui;
 
 import java.awt.Component;
+import java.awt.Dialog.ModalityType;
 import java.awt.HeadlessException;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executors;
 
 import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JInternalFrame;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.UIManager;
 
 import jalview.util.Platform;
 import jalview.util.dialogrunner.DialogRunnerI;
@@ -756,7 +766,14 @@ public class JvOptionPane extends JOptionPane
   public void showDialog(String message, String title, int optionType,
           int messageType, Icon icon, Object[] options, Object initialValue)
   {
+    showDialog(message, title, optionType, messageType, icon, options,
+            initialValue, true);
+  }
 
+  public void showDialog(String message, String title, int optionType,
+          int messageType, Icon icon, Object[] options, Object initialValue,
+          boolean modal)
+  {
     if (!isInteractiveMode())
     {
       handleResponse(getMockResponse());
@@ -778,21 +795,127 @@ public class JvOptionPane extends JOptionPane
 
     ourOptions = Arrays.asList(options);
 
-    int response = JOptionPane.showOptionDialog(parentComponent, message,
-            title, optionType, messageType, icon, options, initialValue);
-
-    /*
-     * In Java, the response is returned to this thread and handled here;
-     * (for Javascript, see propertyChange)
-     */
-    if (!Platform.isJS())
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
+    if (modal)
     {
-      handleResponse(response);
+      // use a JOptionPane as usual
+      int response = JOptionPane.showOptionDialog(parentComponent, message,
+              title, optionType, messageType, icon, options, initialValue);
+
+      /*
+       * In Java, the response is returned to this thread and handled here;
+       * (for Javascript, see propertyChange)
+       */
+      if (!Platform.isJS())
+      /**
+       * Java only
+       * 
+       * @j2sIgnore
+       */
+      {
+        handleResponse(response);
+      }
+    }
+    else
+    {
+      /*
+       * This is java similar to the swingjs handling, with the callbacks
+       * attached to the button press of the dialog.  This means we can use
+       * a non-modal JDialog for the confirmation without blocking the GUI.
+       */
+      JOptionPane joptionpane = new JOptionPane();
+      // Make button options
+      int[] buttonActions = { JvOptionPane.YES_OPTION,
+          JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
+
+      // we need the strings to make the buttons with actionEventListener
+      if (options == null)
+      {
+        ArrayList<String> options_default = new ArrayList<>();
+        options_default
+                .add(UIManager.getString("OptionPane.yesButtonText"));
+        if (optionType == JvOptionPane.YES_NO_OPTION
+                || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
+        {
+          options_default
+                  .add(UIManager.getString("OptionPane.noButtonText"));
+        }
+        if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
+        {
+          options_default
+                  .add(UIManager.getString("OptionPane.cancelButtonText"));
+        }
+        options = options_default.toArray();
+      }
+
+      ArrayList<JButton> options_btns = new ArrayList<>();
+      Object initialValue_btn = null;
+      if (!Platform.isJS()) // JalviewJS already uses callback, don't need to add them here
+      {
+        for (int i = 0; i < options.length && i < 3; i++)
+        {
+          Object o = options[i];
+          int buttonAction = buttonActions[i];
+          Runnable action = callbacks.get(buttonAction);
+          JButton jb = new JButton();
+          jb.setText((String) o);
+          jb.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              joptionpane.setValue(buttonAction);
+              if (action != null)
+                Executors.defaultThreadFactory().newThread(action).start();
+              // joptionpane.transferFocusBackward();
+              joptionpane.transferFocusBackward();
+              joptionpane.setVisible(false);
+              // put focus and raise parent window if possible, unless cancel
+              // button pressed
+              boolean raiseParent = (parentComponent != null);
+              if (buttonAction == JvOptionPane.CANCEL_OPTION)
+                raiseParent = false;
+              if (optionType == JvOptionPane.YES_NO_OPTION
+                      && buttonAction == JvOptionPane.NO_OPTION)
+                raiseParent = false;
+              if (raiseParent)
+              {
+                parentComponent.requestFocus();
+                if (parentComponent instanceof JInternalFrame)
+                {
+                  JInternalFrame jif = (JInternalFrame) parentComponent;
+                  jif.show();
+                  jif.moveToFront();
+                  jif.grabFocus();
+                }
+                else if (parentComponent instanceof Window)
+                {
+                  Window w = (Window) parentComponent;
+                  w.toFront();
+                  w.requestFocus();
+                }
+              }
+              joptionpane.setVisible(false);
+            }
+          });
+          options_btns.add(jb);
+          if (o.equals(initialValue))
+            initialValue_btn = jb;
+        }
+      }
+      joptionpane.setMessage(message);
+      joptionpane.setMessageType(messageType);
+      joptionpane.setOptionType(optionType);
+      joptionpane.setIcon(icon);
+      joptionpane.setOptions(
+              Platform.isJS() ? options : options_btns.toArray());
+      joptionpane.setInitialValue(
+              Platform.isJS() ? initialValue : initialValue_btn);
+
+      JDialog dialog = joptionpane.createDialog(parentComponent, title);
+      dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
+              : ModalityType.MODELESS);
+      dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+      dialog.setVisible(true);
     }
   }
 
index af5f8a4..07eec2b 100644 (file)
@@ -35,7 +35,6 @@ import java.util.concurrent.Executors;
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
-import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JPopupMenu;
 import javax.swing.JTable;
@@ -44,6 +43,7 @@ import javax.swing.table.AbstractTableModel;
 
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.bin.Jalview;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
@@ -81,6 +81,11 @@ public class StructureChooser extends GStructureChooser
 {
   private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
 
+  /**
+   * warn user if need to fetch more than this many uniprot records at once
+   */
+  private static final int THRESHOLD_WARN_UNIPROT_FETCH_NEEDED = 20;
+
   private SequenceI selectedSequence;
 
   private SequenceI[] selectedSequences;
@@ -197,6 +202,7 @@ public class StructureChooser extends GStructureChooser
 
     Executors.defaultThreadFactory().newThread(new Runnable()
     {
+      @Override
       public void run()
       {
         populateSeqsWithoutSourceDBRef();
@@ -263,6 +269,7 @@ public class StructureChooser extends GStructureChooser
         progressBar.setProgressBar(
                 MessageManager.getString("status.searching_3d_beacons"),
                 progressId);
+        btn_queryTDB.setEnabled(false);
         // TODO: warn if no accessions discovered
         populateSeqsWithoutSourceDBRef();
         // redo initial discovery - this time with 3d beacons
@@ -280,11 +287,13 @@ public class StructureChooser extends GStructureChooser
           btn_queryTDB.setToolTipText(MessageManager.getString(
                   "status.no_structures_discovered_from_3d_beacons"));
           btn_queryTDB.setEnabled(false);
+          pnl_queryTDB.setVisible(false);
         }
         else
         {
           cmb_filterOption.setSelectedIndex(0); // select 'best'
           btn_queryTDB.setVisible(false);
+          pnl_queryTDB.setVisible(false);
           progressBar.setProgressBar(null, progressId);
         }
         mainFrame.setEnabled(true);
@@ -312,6 +321,7 @@ public class StructureChooser extends GStructureChooser
       @Override
       public void run()
       {
+        btn_queryTDB.setEnabled(false);
         populateSeqsWithoutSourceDBRef();
 
         final int y = seqsWithoutSourceDBRef.size();
@@ -324,6 +334,7 @@ public class StructureChooser extends GStructureChooser
                   { new jalview.ws.dbsources.Uniprot() }, null, false);
           dbRefFetcher.addListener(afterDbRefFetch);
           // ideally this would also gracefully run with callbacks
+
           dbRefFetcher.fetchDBRefs(true);
         }
         else
@@ -336,6 +347,7 @@ public class StructureChooser extends GStructureChooser
     };
     final Runnable revertview = new Runnable()
     {
+      @Override
       public void run()
       {
         if (lastSelected != null)
@@ -344,7 +356,10 @@ public class StructureChooser extends GStructureChooser
         }
       };
     };
-    if (ignoreGui)
+    int threshold = Cache.getDefault("UNIPROT_AUTOFETCH_THRESHOLD",
+            THRESHOLD_WARN_UNIPROT_FETCH_NEEDED);
+    Console.debug("Using Uniprot fetch threshold of " + threshold);
+    if (ignoreGui || seqsWithoutSourceDBRef.size() < threshold)
     {
       Executors.defaultThreadFactory().newThread(discoverCanonicalDBrefs)
               .start();
@@ -352,7 +367,9 @@ public class StructureChooser extends GStructureChooser
     }
     // need cancel and no to result in the discoverPDB action - mocked is
     // 'cancel' TODO: mock should be OK
-    JvOptionPane.newOptionDialog(this)
+
+    StructureChooser thisSC = this;
+    JvOptionPane.newOptionDialog(thisSC.getFrame())
             .setResponseHandler(JvOptionPane.OK_OPTION,
                     discoverCanonicalDBrefs)
             .setResponseHandler(JvOptionPane.CANCEL_OPTION, revertview)
@@ -366,7 +383,7 @@ public class StructureChooser extends GStructureChooser
                     null, new Object[]
                     { MessageManager.getString("action.ok"),
                         MessageManager.getString("action.cancel") },
-                    MessageManager.getString("action.ok"));
+                    MessageManager.getString("action.ok"), false);
   }
 
   /**
@@ -707,6 +724,7 @@ public class StructureChooser extends GStructureChooser
     if (canQueryTDB && notQueriedTDBYet)
     {
       btn_queryTDB.setVisible(true);
+      pnl_queryTDB.setVisible(true);
     }
 
     if (cachedPDBExist)
@@ -862,6 +880,7 @@ public class StructureChooser extends GStructureChooser
       popup.add(viewUrl);
       SwingUtilities.invokeLater(new Runnable()
       {
+        @Override
         public void run()
         {
           popup.show(getResultTable(), x, y);
@@ -1372,6 +1391,7 @@ public class StructureChooser extends GStructureChooser
   {
     if (selectedSequences != null)
     {
+      lbl_loading.setVisible(true);
       Thread refreshThread = new Thread(new Runnable()
       {
         @Override
@@ -1383,6 +1403,7 @@ public class StructureChooser extends GStructureChooser
           filterResultSet(
                   ((FilterOption) cmb_filterOption.getSelectedItem())
                           .getValue());
+          lbl_loading.setVisible(false);
         }
       });
       refreshThread.start();
index 10e000f..834e873 100644 (file)
 
 package jalview.jbgui;
 
-import jalview.datamodel.SequenceI;
-import jalview.fts.api.FTSDataColumnI;
-import jalview.fts.core.FTSDataColumnPreferences;
-import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
-import jalview.fts.service.pdb.PDBFTSRestClient;
-import jalview.gui.AlignmentPanel;
-import jalview.gui.Desktop;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.StructureViewer;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.CardLayout;
 import java.awt.Component;
@@ -39,6 +28,7 @@ import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.Font;
 import java.awt.GridLayout;
+import java.awt.Insets;
 import java.awt.Point;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -67,8 +57,8 @@ import javax.swing.JTabbedPane;
 import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.ListCellRenderer;
+import javax.swing.SwingConstants;
 import javax.swing.Timer;
-import javax.swing.UIManager;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
@@ -76,6 +66,15 @@ import javax.swing.event.DocumentListener;
 import javax.swing.event.InternalFrameEvent;
 import javax.swing.table.TableColumn;
 
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.Desktop;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.StructureViewer;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
 import net.miginfocom.swing.MigLayout;
 
 @SuppressWarnings("serial")
@@ -122,6 +121,9 @@ public abstract class GStructureChooser extends JPanel
 
   protected JButton btn_pdbFromFile = new JButton();
 
+  // holder for icon and button
+  protected JPanel pnl_queryTDB;
+
   protected JButton btn_queryTDB = new JButton();
 
   protected JCheckBox chk_superpose = new JCheckBox(
@@ -594,13 +596,23 @@ public abstract class GStructureChooser extends JPanel
     });
 
     chk_invertFilter.addItemListener(this);
-    btn_queryTDB.setFont(VERDANA_12);
-    // btn_queryTDB.setPreferredSize(new Dimension(200,22));
+    btn_queryTDB = new JButton();
+    if (Platform.isMac())
+    {
+      // needed to make icon button have round corners in vaqua
+      btn_queryTDB.putClientProperty("JButton.buttonType", "bevel");
+    }
+    btn_queryTDB.setMargin(new Insets(0, 16, 0, 20));
     btn_queryTDB
             .setText(MessageManager.getString("label.search_3dbeacons"));
+    btn_queryTDB.setIconTextGap(12);
+    btn_queryTDB.setIcon(tdbImage);
+    btn_queryTDB.setVerticalTextPosition(SwingConstants.CENTER);
+    btn_queryTDB.setHorizontalTextPosition(SwingConstants.TRAILING);
+    btn_queryTDB.setFont(VERDANA_12);
     btn_queryTDB.setToolTipText(
             MessageManager.getString("label.find_models_from_3dbeacons"));
-    btn_queryTDB.setIcon(tdbImage);
+    // btn_queryTDB.setPreferredSize(new Dimension(200, 32));
     btn_queryTDB.setVisible(false);
 
     targetView.setVisible(false);
@@ -614,7 +626,13 @@ public abstract class GStructureChooser extends JPanel
 
     JPanel pnl_main = new JPanel(new BorderLayout());
     JPanel pnl_controls = new JPanel();
-    pnl_main.add(btn_queryTDB, BorderLayout.NORTH);
+    pnl_queryTDB = new JPanel();
+    pnl_queryTDB.setLayout(new FlowLayout(FlowLayout.CENTER, 4, 4));
+    pnl_queryTDB.setBackground(getBackground());
+    pnl_queryTDB.add(btn_queryTDB);
+
+    pnl_queryTDB.setVisible(false);
+    pnl_main.add(pnl_queryTDB, BorderLayout.NORTH);
     pnl_controls.add(cmb_filterOption);
     pnl_controls.add(lbl_loading);
     pnl_controls.add(chk_invertFilter);
@@ -942,4 +960,9 @@ public abstract class GStructureChooser extends JPanel
   protected abstract void tabRefresh();
 
   protected abstract void validateSelections();
-}
\ No newline at end of file
+
+  public JInternalFrame getFrame()
+  {
+    return mainFrame;
+  }
+}