package jalview.jbgui;
import java.io.File;
+import java.util.Date;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import com.formdev.flatlaf.extras.FlatDesktop;
+import jalview.bin.Console;
import jalview.io.BackupFiles;
import jalview.util.MessageManager;
+import jalview.util.Platform;
public class QuitHandler
{
+ public static enum QResponse
+ {
+ QUIT, CANCEL_QUIT, FORCE_QUIT
+ };
+
public static void setQuitHandler()
{
FlatDesktop.setQuitHandler(response -> {
- // confirm quit if needed and wanted
- boolean confirmQuit = jalview.bin.Cache
- .getDefault(jalview.gui.Desktop.CONFIRM_KEYBOARD_QUIT, true);
- /*
- if undostack is empty
- confirmQuit = false
- */
- int n = confirmQuit ? JOptionPane.CANCEL_OPTION
- : JOptionPane.OK_OPTION;
-
- // if going to confirm, do it before the save in progress check to give
- // the save time to finish!
- if (confirmQuit)
+ QResponse qresponse = getQuitResponse();
+ switch (qresponse)
{
- n = frameOnTop(MessageManager.getString("label.quit_jalview"),
- MessageManager.getString("action.quit"),
- JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
-
+ case QUIT:
+ response.performQuit();
+ break;
+ case CANCEL_QUIT:
+ response.cancelQuit();
+ break;
+ case FORCE_QUIT:
+ response.performQuit();
+ break;
+ default:
+ response.cancelQuit();
}
+ });
+ }
+
+ private static QResponse gotQuitResponse = QResponse.CANCEL_QUIT;
+
+ private static QResponse returnResponse(QResponse qresponse)
+ {
+ gotQuitResponse = qresponse;
+ return qresponse;
+ }
- if (BackupFiles.hasSavesInProgress())
+ public static QResponse gotQuitResponse()
+ {
+ return gotQuitResponse;
+ }
+
+ public static QResponse getQuitResponse()
+ {
+ return getQuitResponse(true);
+ }
+
+ public static QResponse getQuitResponse(boolean ui)
+ {
+ if (gotQuitResponse() != QResponse.CANCEL_QUIT)
+ {
+ return returnResponse(getQuitResponse());
+ }
+
+ boolean 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 (undostack is empty) {
+ 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");
+ }
+
+ int answer = JOptionPane.OK_OPTION;
+
+ // if going to confirm, do it before the save in progress check to give
+ // the save time to finish!
+ if (confirmQuit)
+ {
+ answer = frameOnTop(MessageManager.getString("label.quit_jalview"),
+ MessageManager.getString("action.quit"),
+ JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
+ }
+
+ if (answer == JOptionPane.CANCEL_OPTION)
+ {
+ Console.debug("QuitHandler: Quit action cancelled by user");
+ return returnResponse(QResponse.CANCEL_QUIT);
+ }
+
+ // check for saves in progress
+ int waitForSave = 5000; // MAKE THIS BETTER
+ int waitIncrement = 2000;
+ long startTime = new Date().getTime();
+ boolean saving = BackupFiles.hasSavesInProgress();
+ if (saving)
+ {
+ boolean waiting = (new Date().getTime() - startTime) < waitForSave;
+ while (saving && waiting)
{
- // sleep 1
- // ...
+ saving = !waitForSave(waitIncrement);
+ waiting = (new Date().getTime() - startTime) < waitForSave;
+ }
+ if (saving) // still saving after a wait
+ {
StringBuilder messageSB = new StringBuilder(
MessageManager.getString("label.save_in_progress"));
for (File file : BackupFiles.savesInProgressFiles())
messageSB.append("\n");
messageSB.append(file.getName());
}
- n = frameOnTop(messageSB.toString(),
- MessageManager.getString("action.force_quit"),
- JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
+ int waitLonger = interactive ? JOptionPane.YES_OPTION
+ : JOptionPane.NO_OPTION;
+ while (saving && waitLonger == JOptionPane.YES_OPTION)
+ {
+ waitLonger = waitForceQuitCancelQuitOptionDialog(
+ messageSB.toString(),
+ MessageManager.getString("action.wait"));
+ if (waitLonger == JOptionPane.YES_OPTION) // wait
+ {
+ Console.debug("*** YES answer=" + waitLonger);
+ // do wait stuff
+ saving = !waitForSave(waitIncrement);
+ }
+ else if (waitLonger == JOptionPane.NO_OPTION) // force quit
+ {
+ Console.debug("*** NO answer=" + waitLonger);
+ // do a force quit
+ return returnResponse(QResponse.FORCE_QUIT); // shouldn't reach this
+ }
+ else if (waitLonger == JOptionPane.CANCEL_OPTION) // cancel quit
+ {
+ Console.debug("*** CANCEL answer=" + waitLonger);
+ return returnResponse(QResponse.CANCEL_QUIT);
+ }
+ else
+ {
+ Console.debug("**** Shouldn't have got here!");
+ }
+ }
}
+ }
- boolean canQuit = (n == JOptionPane.OK_OPTION);
- if (canQuit)
- {
- response.performQuit();
- }
- else
- {
- response.cancelQuit();
- }
- });
+ // not cancelled and not saving
+ return returnResponse(QResponse.QUIT);
}
public static int frameOnTop(String label, String actionString,
int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
{
+ return frameOnTop(new JFrame(), label, actionString, JOPTIONPANE_OPTION,
+ JOPTIONPANE_MESSAGETYPE);
+ }
+
+ public static int frameOnTop(JFrame dialogParent, String label,
+ String actionString, int JOPTIONPANE_OPTION,
+ int JOPTIONPANE_MESSAGETYPE)
+ {
// ensure Jalview window is brought to front for Quit confirmation
// window to be visible
// a better hack which works instead
- JFrame dialogParent = new JFrame();
dialogParent.setAlwaysOnTop(true);
- int n = JOptionPane.showConfirmDialog(dialogParent, label, actionString,
- JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE);
+ int answer = JOptionPane.showConfirmDialog(dialogParent, label,
+ actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE);
dialogParent.setAlwaysOnTop(false);
dialogParent.dispose();
- return n;
+ return answer;
+ }
+
+ private static int waitForceQuitCancelQuitOptionDialog(Object message,
+ String title)
+ {
+ JFrame dialogParent = new JFrame();
+ dialogParent.setAlwaysOnTop(true);
+ String wait = MessageManager.getString("action.wait");
+ Object[] options = { wait,
+ MessageManager.getString("action.force_quit"),
+ MessageManager.getString("action.cancel_quit") };
+
+ int answer = JOptionPane.showOptionDialog(dialogParent, message, title,
+ JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE,
+ null, options, wait);
+
+ return answer;
+ }
+
+ 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;
}
}