+++ /dev/null
-package jalview.jbgui;
-
-import java.io.File;
-import java.util.List;
-import java.util.concurrent.Callable;
-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.JButton;
-import javax.swing.JFrame;
-import javax.swing.JOptionPane;
-import javax.swing.JTextPane;
-
-import com.formdev.flatlaf.extras.FlatDesktop;
-
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.Cache;
-import jalview.bin.Console;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.gui.Desktop;
-import jalview.gui.JvOptionPane;
-import jalview.io.BackupFiles;
-import jalview.project.Jalview2XML;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-
-public class QuitHandler
-{
- private static final int MIN_WAIT_FOR_SAVE = 3000;
-
- private static final int MAX_WAIT_FOR_SAVE = 20000;
-
- private static final int NON_INTERACTIVE_WAIT_CYCLES = 2;
-
- public static enum QResponse
- {
- NULL, QUIT, CANCEL_QUIT, FORCE_QUIT
- };
-
- private static ExecutorService executor = Executors.newFixedThreadPool(3);
-
- public static QResponse setQuitHandler()
- {
- FlatDesktop.setQuitHandler(response -> {
- Callable<QResponse> performQuit = () -> {
- response.performQuit();
- return setResponse(QResponse.QUIT);
- };
- Callable<QResponse> performForceQuit = () -> {
- response.performQuit();
- return setResponse(QResponse.FORCE_QUIT);
- };
- Callable<QResponse> cancelQuit = () -> {
- response.cancelQuit();
- // reset
- setResponse(QResponse.NULL);
- // but return cancel
- return QResponse.CANCEL_QUIT;
- };
- QResponse qresponse = getQuitResponse(true, performQuit,
- performForceQuit, cancelQuit);
- });
-
- return gotQuitResponse();
- }
-
- private static QResponse gotQuitResponse = QResponse.NULL;
-
- private static QResponse setResponse(QResponse qresponse)
- {
- gotQuitResponse = qresponse;
- return qresponse;
- }
-
- public static QResponse gotQuitResponse()
- {
- return gotQuitResponse;
- }
-
- public static final Callable<QResponse> defaultCancelQuit = () -> {
- Console.debug("QuitHandler: (default) Quit action CANCELLED by user");
- // reset
- setResponse(QResponse.NULL);
- // and return cancel
- return QResponse.CANCEL_QUIT;
- };
-
- public static final Callable<QResponse> defaultOkQuit = () -> {
- Console.debug("QuitHandler: (default) Quit action CONFIRMED by user");
- return setResponse(QResponse.QUIT);
- };
-
- public static final Callable<QResponse> defaultForceQuit = () -> {
- Console.debug("QuitHandler: (default) Quit action FORCED by user");
- // note that shutdown hook will not be run
- Runtime.getRuntime().halt(0);
- return setResponse(QResponse.FORCE_QUIT); // this line never reached!
- };
-
- public static QResponse getQuitResponse(boolean ui)
- {
- return getQuitResponse(ui, defaultOkQuit, defaultForceQuit,
- defaultCancelQuit);
- }
-
- private static boolean interactive = true;
-
- public static QResponse getQuitResponse(boolean ui,
- Callable<QResponse> okQuit, Callable<QResponse> forceQuit,
- Callable<QResponse> cancelQuit)
- {
- QResponse got = gotQuitResponse();
- if (got != QResponse.NULL && got != QResponse.CANCEL_QUIT)
- {
- // quit has already been selected, continue with calling quit method
- return got;
- }
-
- interactive = ui && !Platform.isHeadless();
- // confirm quit if needed and wanted
- boolean confirmQuit = true;
-
- if (!interactive)
- {
- Console.debug("Non interactive quit -- not confirming");
- confirmQuit = false;
- }
- else if (Jalview2XML.allSavedUpToDate())
- {
- Console.debug("Nothing changed -- not confirming quit");
- confirmQuit = false;
- }
- else
- {
- confirmQuit = jalview.bin.Cache
- .getDefault(jalview.gui.Desktop.CONFIRM_KEYBOARD_QUIT, true);
- Console.debug("Jalview property '"
- + jalview.gui.Desktop.CONFIRM_KEYBOARD_QUIT
- + "' is/defaults to " + confirmQuit + " -- "
- + (confirmQuit ? "" : "not ") + "confirming quit");
- }
- got = confirmQuit ? QResponse.NULL : QResponse.QUIT;
- setResponse(got);
-
- if (confirmQuit)
- {
- JvOptionPane.newOptionDialog()
- .setResponseHandler(JOptionPane.YES_OPTION, defaultOkQuit)
- .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();
- boolean wait = false;
- if (got == QResponse.CANCEL_QUIT)
- {
- // reset
- setResponse(QResponse.NULL);
- // but return cancel
- return QResponse.CANCEL_QUIT;
- }
- else if (got == QResponse.QUIT)
- {
- if (Cache.getDefault("WAIT_FOR_SAVE", true)
- && BackupFiles.hasSavesInProgress())
- {
- QResponse waitResponse = waitQuit(interactive, okQuit, forceQuit,
- cancelQuit);
- wait = waitResponse == QResponse.QUIT;
- }
- }
-
- Callable<QResponse> next = null;
- switch (gotQuitResponse())
- {
- case QUIT:
- next = okQuit;
- break;
- case FORCE_QUIT: // not actually an option at this stage
- next = forceQuit;
- break;
- default:
- next = cancelQuit;
- break;
- }
- try
- {
- got = executor.submit(next).get();
- } catch (InterruptedException | ExecutionException e)
- {
- jalview.bin.Console
- .debug("Exception during quit handling (final choice)", e);
- }
- setResponse(got);
-
- return gotQuitResponse();
- }
-
- private static QResponse waitQuit(boolean interactive,
- Callable<QResponse> okQuit, Callable<QResponse> forceQuit,
- Callable<QResponse> cancelQuit)
- {
- // check for saves in progress
- if (!BackupFiles.hasSavesInProgress())
- return QResponse.QUIT;
-
- int size = 0;
- AlignFrame[] afArray = Desktop.getAlignFrames();
- if (!(afArray == null || afArray.length == 0))
- {
- for (int i = 0; i < afArray.length; i++)
- {
- AlignFrame af = afArray[i];
- List<? extends AlignmentViewPanel> avpList = af.getAlignPanels();
- for (AlignmentViewPanel avp : avpList)
- {
- AlignmentI a = avp.getAlignment();
- List<SequenceI> sList = a.getSequences();
- for (SequenceI s : sList)
- {
- size += s.getLength();
- }
- }
- }
- }
- 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;
-
- int iteration = 0;
- boolean doIterations = true; // note iterations not used in the gui now,
- // only one pass without the "Wait" button
- 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
- {
- 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())
- {
- boolean showForceQuit = iteration > 0; // iteration > 1 to not show
- // force quit the first time
- JFrame parent = new JFrame();
- JButton[] buttons = { new JButton(), new JButton() };
- JvOptionPane waitDialog = JvOptionPane.newOptionDialog();
- JTextPane messagePane = new JTextPane();
- messagePane.setBackground(waitDialog.getBackground());
- messagePane.setBorder(null);
- messagePane.setText(waitingForSaveMessage());
- // callback as each file finishes saving
- for (CompletableFuture<Boolean> cf : BackupFiles
- .savesInProgressCompletableFutures(false))
- {
- cf.whenComplete((ret, e) -> {
- if (BackupFiles.hasSavesInProgress())
- {
- // update the list of saving files as they save too
- messagePane.setText(waitingForSaveMessage());
- }
- else
- {
- if (!(QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT
- || QuitHandler.gotQuitResponse() == QResponse.NULL))
- {
- for (int i = 0; i < buttons.length; i++)
- {
- Console.debug("DISABLING BUTTON " + buttons[i].getText());
- buttons[i].setEnabled(false);
- buttons[i].setVisible(false);
- }
- // 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());
- messagePane.setEditable(false);
- try
- {
- Thread.sleep(1500);
- } catch (InterruptedException e1)
- {
- }
- parent.dispose();
- }
- }
- });
- }
-
- String[] options;
- int dialogType = -1;
- if (showForceQuit)
- {
- options = new String[2];
- options[0] = MessageManager.getString("action.force_quit");
- options[1] = MessageManager.getString("action.cancel_quit");
- dialogType = JOptionPane.YES_NO_OPTION;
- waitDialog.setResponseHandler(JOptionPane.YES_OPTION, forceQuit)
- .setResponseHandler(JOptionPane.NO_OPTION, cancelQuit);
- }
- else
- {
- options = new String[1];
- options[0] = MessageManager.getString("action.cancel_quit");
- dialogType = JOptionPane.YES_OPTION;
- waitDialog.setResponseHandler(JOptionPane.YES_OPTION, cancelQuit);
- }
- waitDialog.showDialogOnTopAsync(parent, messagePane,
- MessageManager.getString("label.wait_for_save"), dialogType,
- JOptionPane.WARNING_MESSAGE, null, options,
- MessageManager.getString("action.cancel_quit"), true,
- buttons);
-
- parent.dispose();
- final QResponse thisWaitResponse = gotQuitResponse();
- switch (thisWaitResponse)
- {
- case QUIT: // wait -- do another iteration
- break;
- case FORCE_QUIT:
- doIterations = false;
- break;
- case CANCEL_QUIT:
- doIterations = false;
- break;
- case NULL: // already cancelled
- doIterations = false;
- break;
- default:
- }
- } // end if interactive
-
- } // end while wait iteration loop
- waitResponse = gotQuitResponse();
-
- return waitResponse;
- };
-
- private static String waitingForSaveMessage()
- {
- 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)
- {
- for (File file : files)
- {
- messageSB.append("\n\u2022 ").append(file.getName());
- }
- }
- else
- {
- messageSB.append(MessageManager.getString("label.unknown"));
- }
- messageSB.append("\n\n")
- .append(MessageManager.getString("label.quit_after_saving"));
- return messageSB.toString();
- }
-}
\ No newline at end of file