Merge branch 'develop' into feature/JAL-3416_update_to_flatlaf_3.1.1_with_unpacked_na...
[jalview.git] / src / jalview / gui / Desktop.java
index 653cc73..8eebfc1 100644 (file)
@@ -64,7 +64,6 @@ import java.util.List;
 import java.util.ListIterator;
 import java.util.Locale;
 import java.util.Vector;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Semaphore;
@@ -102,8 +101,13 @@ import org.stackoverflowusers.file.WindowsShortcut;
 
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
 import jalview.gui.ImageExporter.ImageWriterI;
 import jalview.gui.QuitHandler.QResponse;
 import jalview.io.BackupFiles;
@@ -430,9 +434,14 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       if (LaunchUtils.getJavaVersion() >= 11)
       {
-        jalview.bin.Console.info(
+        /*
+         * Send this message to stderr as the warning that follows (due to
+         * reflection) also goes to stderr.
+         */
+        System.err.println(
                 "Linux platform only! You may have the following warning next: \"WARNING: An illegal reflective access operation has occurred\"\nThis is expected and cannot be avoided, sorry about that.");
       }
+      final String awtAppClassName = "awtAppClassName";
       try
       {
         Toolkit xToolkit = Toolkit.getDefaultToolkit();
@@ -440,10 +449,10 @@ public class Desktop extends jalview.jbgui.GDesktop
         Field awtAppClassNameField = null;
 
         if (Arrays.stream(declaredFields)
-                .anyMatch(f -> f.getName().equals("awtAppClassName")))
+                .anyMatch(f -> f.getName().equals(awtAppClassName)))
         {
           awtAppClassNameField = xToolkit.getClass()
-                  .getDeclaredField("awtAppClassName");
+                  .getDeclaredField(awtAppClassName);
         }
 
         String title = ChannelProperties.getProperty("app_name");
@@ -454,11 +463,12 @@ public class Desktop extends jalview.jbgui.GDesktop
         }
         else
         {
-          jalview.bin.Console.debug("XToolkit: awtAppClassName not found");
+          jalview.bin.Console
+                  .debug("XToolkit: " + awtAppClassName + " not found");
         }
       } catch (Exception e)
       {
-        jalview.bin.Console.debug("Error setting awtAppClassName");
+        jalview.bin.Console.debug("Error setting " + awtAppClassName);
         jalview.bin.Console.trace(Cache.getStackTraceString(e));
       }
     }
@@ -798,26 +808,64 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   public void paste()
   {
-    try
+    // quick patch for JAL-4150 - needs some more work and test coverage
+    // TODO - unify below and AlignFrame.paste()
+    // TODO - write tests and fix AlignFrame.paste() which doesn't track if
+    // clipboard has come from a different alignment window than the one where
+    // paste has been called! JAL-4151
+
+    if (Desktop.jalviewClipboard != null)
     {
-      Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
-      Transferable contents = c.getContents(this);
+      // The clipboard was filled from within Jalview, we must use the
+      // sequences
+      // And dataset from the copied alignment
+      SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
+      // be doubly sure that we create *new* sequence objects.
+      SequenceI[] sequences = new SequenceI[newseq.length];
+      for (int i = 0; i < newseq.length; i++)
+      {
+        sequences[i] = new Sequence(newseq[i]);
+      }
+      Alignment alignment = new Alignment(sequences);
+      // dataset is inherited
+      alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
+      AlignFrame af = new AlignFrame(alignment, AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+      String newtitle = new String("Copied sequences");
+
+      if (Desktop.jalviewClipboard[2] != null)
+      {
+        HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+        af.viewport.setHiddenColumns(hc);
+      }
 
-      if (contents != null)
+      Desktop.addInternalFrame(af, newtitle, AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+
+    }
+    else
+    {
+      try
       {
-        String file = (String) contents
-                .getTransferData(DataFlavor.stringFlavor);
+        Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
+        Transferable contents = c.getContents(this);
 
-        FileFormatI format = new IdentifyFile().identify(file,
-                DataSourceType.PASTE);
+        if (contents != null)
+        {
+          String file = (String) contents
+                  .getTransferData(DataFlavor.stringFlavor);
+
+          FileFormatI format = new IdentifyFile().identify(file,
+                  DataSourceType.PASTE);
 
-        new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
+          new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
 
+        }
+      } catch (Exception ex)
+      {
+        System.out.println(
+                "Unable to paste alignment from system clipboard:\n" + ex);
       }
-    } catch (Exception ex)
-    {
-      System.out.println(
-              "Unable to paste alignment from system clipboard:\n" + ex);
     }
   }
 
@@ -1215,7 +1263,6 @@ public class Desktop extends jalview.jbgui.GDesktop
 
       new FileLoader().LoadFile(viewport, selectedFile, DataSourceType.FILE,
               format);
-      return null;
     });
     chooser.showOpenDialog(this);
   }
@@ -1271,7 +1318,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     Object[] options = new Object[] { MessageManager.getString("action.ok"),
         MessageManager.getString("action.cancel") };
-    Callable<Void> action = () -> {
+    Runnable action = () -> {
       @SuppressWarnings("unchecked")
       String url = (history instanceof JTextField
               ? ((JTextField) history).getText()
@@ -1310,8 +1357,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
                   MessageManager.getString("label.url_not_found"),
                   JvOptionPane.WARNING_MESSAGE);
-
-          return null; // Void
+          return;
         }
 
         if (viewport != null)
@@ -1324,7 +1370,6 @@ public class Desktop extends jalview.jbgui.GDesktop
           new FileLoader().LoadFile(url, DataSourceType.URL, format);
         }
       }
-      return null; // Void
     };
     String dialogOption = MessageManager
             .getString("label.input_alignment_from_url");
@@ -1363,7 +1408,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   public QuitHandler.QResponse desktopQuit(boolean ui, boolean disposeFlag)
   {
-    final Callable<Void> doDesktopQuit = () -> {
+    final Runnable doDesktopQuit = () -> {
       Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
       Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
       Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
@@ -1379,7 +1424,17 @@ public class Desktop extends jalview.jbgui.GDesktop
       if (jvnews != null)
       {
         storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
+      }
 
+      // Frames should all close automatically. Keeping external
+      // viewers open should already be decided by user.
+      closeAll_actionPerformed(null);
+
+      // check for aborted quit
+      if (QuitHandler.quitCancelled())
+      {
+        jalview.bin.Console.debug("Desktop aborting quit");
+        return;
       }
 
       if (dialogExecutor != null)
@@ -1387,8 +1442,6 @@ public class Desktop extends jalview.jbgui.GDesktop
         dialogExecutor.shutdownNow();
       }
 
-      closeAll_actionPerformed(null);
-
       if (groovyConsole != null)
       {
         // suppress a possible repeat prompt to save script
@@ -1410,8 +1463,6 @@ public class Desktop extends jalview.jbgui.GDesktop
         // instance.dispose();
       }
       instance.quit();
-
-      return null; // Void
     };
 
     return QuitHandler.getQuitResponse(ui, doDesktopQuit, doDesktopQuit,
@@ -1427,7 +1478,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     // this will run the shutdownHook but QuitHandler.getQuitResponse() should
     // not run a second time if gotQuitResponse flag has been set (i.e. user
     // confirmed quit of some kind).
-    System.exit(0);
+    Jalview.exit("Desktop exiting.", 0);
   }
 
   private void storeLastKnownDimensions(String string, Rectangle jc)
@@ -1573,6 +1624,22 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
   }
 
+  public int structureViewersStillRunningCount()
+  {
+    int count = 0;
+    JInternalFrame[] frames = desktop.getAllFrames();
+    for (int i = 0; i < frames.length; i++)
+    {
+      if (frames[i] != null
+              && frames[i] instanceof JalviewStructureDisplayI)
+      {
+        if (((JalviewStructureDisplayI) frames[i]).stillRunning())
+          count++;
+      }
+    }
+    return count;
+  }
+
   @Override
   public void raiseRelated_actionPerformed(ActionEvent e)
   {
@@ -1894,7 +1961,6 @@ public class Desktop extends jalview.jbgui.GDesktop
           }
         }
       }, "Project Loader").start();
-      return null;
     });
 
     chooser.showOpenDialog(this);
@@ -2974,7 +3040,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   /**
    * single thread that handles display of dialogs to user.
    */
-  ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
+  ExecutorService dialogExecutor = Executors.newFixedThreadPool(3);
 
   /**
    * flag indicating if dialogExecutor should try to acquire a permit