JAL-1988 JAL-3772 Fixed wait between waitDialog appearances, ensuring all files savin...
authorBen Soares <b.soares@dundee.ac.uk>
Fri, 28 Oct 2022 11:48:56 +0000 (12:48 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Fri, 28 Oct 2022 11:48:56 +0000 (12:48 +0100)
src/jalview/jbgui/QuitHandler.java

index ac14a2f..ef8ec55 100644 (file)
@@ -7,6 +7,8 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.swing.JFrame;
 import javax.swing.JOptionPane;
@@ -29,7 +31,9 @@ import jalview.util.Platform;
 
 public class QuitHandler
 {
-  private static final int INITIAL_WAIT_FOR_SAVE = 3000;
+  private static final int MIN_WAIT_FOR_SAVE = 5000;
+
+  private static final int MAX_WAIT_FOR_SAVE = 20000;
 
   private static final int NON_INTERACTIVE_WAIT_CYCLES = 2;
 
@@ -70,7 +74,6 @@ public class QuitHandler
   private static QResponse setResponse(QResponse qresponse)
   {
     gotQuitResponse = qresponse;
-    Console.debug("##### Setting gotQuitResponse to " + qresponse);
     return qresponse;
   }
 
@@ -115,7 +118,6 @@ public class QuitHandler
     if (got != QResponse.NULL && got != QResponse.CANCEL_QUIT)
     {
       // quit has already been selected, continue with calling quit method
-      Console.debug("##### getQuitResponse called. gotQuitResponse=" + got);
       return got;
     }
 
@@ -148,28 +150,25 @@ public class QuitHandler
 
     if (confirmQuit)
     {
-
-      Console.debug("********************ABOUT TO CONFIRM QUIT");
-      JvOptionPane quitDialog = JvOptionPane.newOptionDialog()
+      JvOptionPane.newOptionDialog()
               .setResponseHandler(JOptionPane.YES_OPTION, defaultOkQuit)
-              .setResponseHandler(JOptionPane.NO_OPTION, defaultCancelQuit);
-      quitDialog.showDialogOnTopAsync(
-              new StringBuilder(
-                      MessageManager.getString("label.quit_jalview"))
-                              .append("\n")
-                              .append(MessageManager
-                                      .getString("label.unsaved_changes"))
-                              .toString(),
-              MessageManager.getString("action.quit"),
-              JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
-              new Object[]
-              { MessageManager.getString("action.quit"),
-                  MessageManager.getString("action.cancel") },
-              MessageManager.getString("action.quit"), true);
+              .setResponseHandler(JOptionPane.NO_OPTION, defaultCancelQuit)
+              .showDialogOnTopAsync(
+                      new StringBuilder(MessageManager
+                              .getString("label.quit_jalview"))
+                                      .append("\n")
+                                      .append(MessageManager.getString(
+                                              "label.unsaved_changes"))
+                                      .toString(),
+                      MessageManager.getString("action.quit"),
+                      JOptionPane.YES_NO_OPTION,
+                      JOptionPane.QUESTION_MESSAGE, null, new Object[]
+                      { MessageManager.getString("action.quit"),
+                          MessageManager.getString("action.cancel") },
+                      MessageManager.getString("action.quit"), true);
     }
 
     got = gotQuitResponse();
-    Console.debug("first user response, got=" + got);
     boolean wait = false;
     if (got == QResponse.CANCEL_QUIT)
     {
@@ -193,15 +192,12 @@ public class QuitHandler
     switch (gotQuitResponse())
     {
     case QUIT:
-      Console.debug("### User selected QUIT");
       next = okQuit;
       break;
     case FORCE_QUIT: // not actually an option at this stage
-      Console.debug("### User selected FORCE QUIT");
       next = forceQuit;
       break;
     default:
-      Console.debug("### User selected CANCEL QUIT");
       next = cancelQuit;
       break;
     }
@@ -213,7 +209,6 @@ public class QuitHandler
       jalview.bin.Console
               .debug("Exception during quit handling (final choice)", e);
     }
-    jalview.bin.Console.debug("### nextResponse=" + got.toString());
     setResponse(got);
 
     return gotQuitResponse();
@@ -223,16 +218,14 @@ public class QuitHandler
           Callable<QResponse> okQuit, Callable<QResponse> forceQuit,
           Callable<QResponse> cancelQuit)
   {
-    jalview.bin.Console.debug("#### waitQuit started");
     // check for saves in progress
     if (!BackupFiles.hasSavesInProgress())
       return QResponse.QUIT;
 
-    int waitTime = INITIAL_WAIT_FOR_SAVE; // start with 3 second wait
+    int size = 0;
     AlignFrame[] afArray = Desktop.getAlignFrames();
     if (!(afArray == null || afArray.length == 0))
     {
-      int size = 0;
       for (int i = 0; i < afArray.length; i++)
       {
         AlignFrame af = afArray[i];
@@ -247,67 +240,65 @@ public class QuitHandler
           }
         }
       }
-      waitTime = Math.max(waitTime, size / 2);
-      Console.debug("Set waitForSave to " + waitTime);
     }
-    final int waitTimeFinal = waitTime;
+    int waitTime = Math.min(MAX_WAIT_FOR_SAVE,
+            Math.max(MIN_WAIT_FOR_SAVE, size / 2));
+    Console.debug("Set waitForSave to " + waitTime);
     QResponse waitResponse = QResponse.NULL;
 
-    // future that returns a Boolean when all files are saved
-    CompletableFuture<Boolean> filesAllSaved = new CompletableFuture<>();
-
-    // callback as each file finishes saving
-    for (CompletableFuture<Boolean> cf : BackupFiles
-            .savesInProgressCompletableFutures(false))
-    {
-      // if this is the last one then complete filesAllSaved
-      cf.whenComplete((ret, e) -> {
-        if (!BackupFiles.hasSavesInProgress())
-        {
-          filesAllSaved.complete(true);
-        }
-      });
-    }
-
-    // timeout the wait -- will result in another wait button when looped
-    CompletableFuture<Boolean> waitTimeout = CompletableFuture
-            .supplyAsync(() -> {
-              Console.debug("################# STARTING WAIT SLEEP");
-              try
-              {
-                Thread.sleep(waitTimeFinal);
-              } catch (InterruptedException e)
-              {
-                // probably interrupted by all files saving
-              }
-              return true;
-            });
-    CompletableFuture<Object> waitForSave = CompletableFuture
-            .anyOf(waitTimeout, filesAllSaved);
-
     int iteration = 0;
     boolean doIterations = true;
     while (doIterations && BackupFiles.hasSavesInProgress()
             && iteration++ < (interactive ? 100 : 5))
     {
+      // future that returns a Boolean when all files are saved
+      CompletableFuture<Boolean> filesAllSaved = new CompletableFuture<>();
+
+      // callback as each file finishes saving
+      for (CompletableFuture<Boolean> cf : BackupFiles
+              .savesInProgressCompletableFutures(false))
+      {
+        // if this is the last one then complete filesAllSaved
+        cf.whenComplete((ret, e) -> {
+          if (!BackupFiles.hasSavesInProgress())
+          {
+            filesAllSaved.complete(true);
+          }
+        });
+      }
       try
       {
-        waitForSave.copy().get();
+        filesAllSaved.get(waitTime, TimeUnit.MILLISECONDS);
       } catch (InterruptedException | ExecutionException e1)
       {
         Console.debug(
                 "Exception whilst waiting for files to save before quit",
                 e1);
+      } catch (TimeoutException e2)
+      {
+        // this Exception to be expected
       }
+
       if (interactive && BackupFiles.hasSavesInProgress())
       {
-        Console.debug("********************About to make waitDialog");
+        boolean allowForceQuit = iteration > 0; // iteration > 1 to not show
+                                                // force quit the first time
         JFrame parent = new JFrame();
-        JvOptionPane waitDialog = JvOptionPane.newOptionDialog()
-                .setResponseHandler(JOptionPane.YES_OPTION, defaultOkQuit)
-                .setResponseHandler(JOptionPane.NO_OPTION, forceQuit)
-                .setResponseHandler(JOptionPane.CANCEL_OPTION, cancelQuit);
-
+        JvOptionPane waitDialog = JvOptionPane.newOptionDialog();
+        if (allowForceQuit)
+        {
+          waitDialog
+                  .setResponseHandler(JOptionPane.YES_OPTION, defaultOkQuit)
+                  .setResponseHandler(JOptionPane.NO_OPTION, forceQuit)
+                  .setResponseHandler(JOptionPane.CANCEL_OPTION,
+                          cancelQuit);
+        }
+        else
+        {
+          waitDialog
+                  .setResponseHandler(JOptionPane.YES_OPTION, defaultOkQuit)
+                  .setResponseHandler(JOptionPane.NO_OPTION, cancelQuit);
+        }
         JTextPane messagePane = new JTextPane();
         messagePane.setBackground(waitDialog.getBackground());
         messagePane.setBorder(null);
@@ -317,12 +308,24 @@ public class QuitHandler
                 .savesInProgressCompletableFutures(false))
         {
           cf.whenComplete((ret, e) -> {
-            Console.debug("############# A FILE SAVED!");
-            // update the list of saving files as they save too
-            messagePane.setText(waitingForSaveMessage());
-            // if this is the last one then close the dialog
-            if (!BackupFiles.hasSavesInProgress())
+            if (BackupFiles.hasSavesInProgress())
+              // update the list of saving files as they save too
+              messagePane.setText(waitingForSaveMessage());
+            else
             {
+              // if this is the last one then close the dialog
+              messagePane.setText(new StringBuilder()
+                      .append(MessageManager.getString("label.all_saved"))
+                      .append("\n").append(MessageManager
+                              .getString("label.quitting_bye"))
+                      .toString());
+              try
+              {
+                Console.debug("WAITING FOR MESSAGE");
+                Thread.sleep(500);
+              } catch (InterruptedException e1)
+              {
+              }
               // like a click on Wait button
               waitDialog.setValue(JOptionPane.YES_OPTION);
               parent.dispose();
@@ -330,18 +333,27 @@ public class QuitHandler
           });
         }
 
+        String[] options = new String[allowForceQuit ? 3 : 2];
+        if (allowForceQuit)
+        {
+          options[0] = MessageManager.getString("action.wait");
+          options[1] = MessageManager.getString("action.force_quit");
+          options[2] = MessageManager.getString("action.cancel_quit");
+        }
+        else
+        {
+          options[0] = MessageManager.getString("action.wait");
+          options[1] = MessageManager.getString("action.cancel_quit");
+        }
         waitDialog.showDialogOnTopAsync(parent, messagePane,
                 MessageManager.getString("action.wait"),
-                JOptionPane.YES_NO_CANCEL_OPTION,
-                JOptionPane.WARNING_MESSAGE, null, new Object[]
-                { MessageManager.getString("action.wait"),
-                    MessageManager.getString("action.force_quit"),
-                    MessageManager.getString("action.cancel_quit") },
+                allowForceQuit ? JOptionPane.YES_NO_CANCEL_OPTION
+                        : JOptionPane.YES_NO_OPTION,
+                JOptionPane.WARNING_MESSAGE, null, options,
                 MessageManager.getString("action.wait"), true);
-        Console.debug("********************Finished waitDialog");
 
+        parent.dispose();
         final QResponse thisWaitResponse = gotQuitResponse();
-        Console.debug("####### WAITFORSAVE SET: " + thisWaitResponse);
         switch (thisWaitResponse)
         {
         case QUIT: // wait -- do another iteration
@@ -362,7 +374,6 @@ public class QuitHandler
     } // end while wait iteration loop
     waitResponse = gotQuitResponse();
 
-    Console.debug("####### WAITFORSAVE RETURNING: " + waitResponse);
     return waitResponse;
   };
 
@@ -387,11 +398,11 @@ public class QuitHandler
   {
     StringBuilder messageSB = new StringBuilder();
 
+    messageSB.append(MessageManager.getString("label.save_in_progress"));
     List<File> files = BackupFiles.savesInProgressFiles(false);
     boolean any = files.size() > 0;
     if (any)
     {
-      messageSB.append(MessageManager.getString("label.save_in_progress"));
       for (File file : files)
       {
         messageSB.append("\n- ").append(file.getName());
@@ -399,43 +410,9 @@ public class QuitHandler
     }
     else
     {
-      messageSB.append(MessageManager.getString("label.all_saved"))
-              .append("\n")
-              .append(MessageManager.getString("label.quitting_bye"));
+      messageSB.append(MessageManager.getString("label.unknown"));
     }
     return messageSB.toString();
   }
 
-  private static Boolean waitForSave(long t)
-  {
-    boolean ret = false;
-    try
-    {
-      Console.debug("Wait for save to complete: " + t + "ms");
-      long c = 0;
-      int i = 100;
-      while (c < t)
-      {
-        Thread.sleep(i);
-        c += i;
-        ret = !BackupFiles.hasSavesInProgress();
-        if (ret)
-        {
-          Console.debug(
-                  "Save completed whilst waiting (" + c + "/" + t + "ms)");
-          return ret;
-        }
-        if (c % 1000 < i) // just gone over another second
-        {
-          Console.debug("...waiting (" + c + "/" + t + "ms]");
-        }
-      }
-    } catch (InterruptedException e)
-    {
-      Console.debug("Wait for save interrupted");
-    }
-    Console.debug("Save has " + (ret ? "" : "not ") + "completed");
-    return ret;
-  }
-
-}
+}
\ No newline at end of file