From 3a3f08c3625510708606106bfcd3cc68b3a017cc Mon Sep 17 00:00:00 2001 From: Ben Soares Date: Fri, 28 Oct 2022 12:48:56 +0100 Subject: [PATCH] JAL-1988 JAL-3772 Fixed wait between waitDialog appearances, ensuring all files saving will quit straight away. Goodbye message to user if waitDialog still visible. --- src/jalview/jbgui/QuitHandler.java | 217 ++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 120 deletions(-) diff --git a/src/jalview/jbgui/QuitHandler.java b/src/jalview/jbgui/QuitHandler.java index ac14a2f..ef8ec55 100644 --- a/src/jalview/jbgui/QuitHandler.java +++ b/src/jalview/jbgui/QuitHandler.java @@ -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 okQuit, Callable forceQuit, Callable 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 filesAllSaved = new CompletableFuture<>(); - - // callback as each file finishes saving - for (CompletableFuture 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 waitTimeout = CompletableFuture - .supplyAsync(() -> { - Console.debug("################# STARTING WAIT SLEEP"); - try - { - Thread.sleep(waitTimeFinal); - } catch (InterruptedException e) - { - // probably interrupted by all files saving - } - return true; - }); - CompletableFuture 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 filesAllSaved = new CompletableFuture<>(); + + // callback as each file finishes saving + for (CompletableFuture 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 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 -- 1.7.10.2