From: Ben Soares Date: Fri, 4 Nov 2022 17:53:36 +0000 (+0000) Subject: JAL-1998 JAL-3772 JAL-3416 Merge conflict resolution of JalviewFileChooser and JvOpti... X-Git-Tag: Release_2_11_3_0~23^2~4^2~18 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=952d6b6fecfb976c0c113f013ed2b8980af3d190;hp=-c;p=jalview.git JAL-1998 JAL-3772 JAL-3416 Merge conflict resolution of JalviewFileChooser and JvOptionPane --- 952d6b6fecfb976c0c113f013ed2b8980af3d190 diff --combined src/jalview/bin/Jalview.java index b5e1958,9f37540..b87a14d --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@@ -61,8 -61,6 +61,8 @@@ import jalview.ext.so.SequenceOntology import jalview.gui.AlignFrame; import jalview.gui.Desktop; import jalview.gui.PromptUserConfig; +import jalview.gui.QuitHandler; +import jalview.gui.QuitHandler.QResponse; import jalview.io.AppletFormatAdapter; import jalview.io.BioJsHTMLOutput; import jalview.io.DataSourceType; @@@ -273,28 -271,6 +273,28 @@@ public class Jalvie if (!Platform.isJS()) { System.setSecurityManager(null); + + Runtime.getRuntime().addShutdownHook(new Thread() + { + public void run() + { + Console.debug("Running shutdown hook"); + if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT) + { + // Got to here by a SIGTERM signal. + // Note we will not actually cancel the quit from here -- it's too + // late -- but we can wait for saving files. + Console.debug("Checking for saving files"); + QuitHandler.getQuitResponse(false); + } + else + { + Console.debug("Nothing more to do"); + } + Console.debug("Exiting, bye!"); + // shutdownHook cannot be cancelled, JVM will now halt + } + }); } System.out @@@ -303,7 -279,6 +303,7 @@@ System.out.println(System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version")); + String val = System.getProperty("sys.install4jVersion"); if (val != null) { @@@ -324,12 -299,10 +324,12 @@@ Cache.loadBuildProperties(true); ArgsParser aparser = new ArgsParser(args); + boolean headless = false; String usrPropsFile = aparser.getValue("props"); - Cache.loadProperties(usrPropsFile); // must do this before + Cache.loadProperties(usrPropsFile); // must do this + // before if (usrPropsFile != null) { System.out.println( @@@ -407,9 -380,7 +407,9 @@@ try { Console.initLogger(); - } catch (NoClassDefFoundError error) + } catch ( + + NoClassDefFoundError error) { error.printStackTrace(); System.out.println("\nEssential logging libraries not found." @@@ -584,11 -555,8 +584,11 @@@ } String file = null, data = null; + FileFormatI format = null; + DataSourceType protocol = null; + FileLoader fileLoader = new FileLoader(!headless); String groovyscript = null; // script to execute after all loading is @@@ -602,7 -570,6 +602,7 @@@ System.out.println("No files to open!"); System.exit(1); } + long progress = -1; // Finally, deal with the remaining input data. if (file != null) @@@ -862,7 -829,6 +862,7 @@@ } } } + AlignFrame startUpAlframe = null; // We'll only open the default file if the desktop is visible. // And the user @@@ -1157,6 -1123,8 +1157,8 @@@ UIManager.put("TabbedPane.tabWidthMode", "compact"); UIManager.put("TabbedPane.selectedBackground", Color.white); } + + Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true)); return set; } @@@ -1403,12 -1371,19 +1405,12 @@@ } /** - * Quit method delegates to Desktop.quit - unless running in headless mode - * when it just ends the JVM + * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit */ public void quit() { - if (desktop != null) - { - desktop.quit(); - } - else - { - System.exit(0); - } + // System.exit will run the shutdownHook first + System.exit(0); } public static AlignFrame getCurrentAlignFrame() diff --combined src/jalview/gui/AlignFrame.java index d8fec3a,abc7437..c11b866 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@@ -59,7 -59,6 +59,7 @@@ import java.util.Hashtable import java.util.List; import java.util.Locale; import java.util.Vector; +import java.util.concurrent.Callable; import javax.swing.ButtonGroup; import javax.swing.JCheckBoxMenuItem; @@@ -350,6 -349,8 +350,8 @@@ public class AlignFrame extends GAlignF */ void init() { + setFrameIcon(WindowIcons.alignmentIcon); + // setBackground(Color.white); // BH 2019 if (!Jalview.isHeadlessMode()) @@@ -1259,12 -1260,6 +1261,12 @@@ lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file, shortName); + Console.debug("lastSaveSuccessful=" + lastSaveSuccessful); + if (lastSaveSuccessful) + { + this.getViewport().setSavedUpToDate(true); + } + statusBar.setText(MessageManager.formatMessage( "label.successfully_saved_to_file_in_format", new Object[] { file, format })); @@@ -1273,89 -1268,97 +1275,89 @@@ } AlignExportSettingsI options = new AlignExportSettingsAdapter(false); - Runnable cancelAction = new Runnable() - { - @Override - public void run() + Callable cancelAction = () -> { + lastSaveSuccessful = false; + return null; + }; + Callable outputAction = () -> { + // todo defer this to inside formatSequences (or later) + AlignmentExportData exportData = viewport.getAlignExportData(options); + String output = new FormatAdapter(alignPanel, options) + .formatSequences(format, exportData.getAlignment(), + exportData.getOmitHidden(), + exportData.getStartEndPostions(), + viewport.getAlignment().getHiddenColumns()); + if (output == null) { lastSaveSuccessful = false; } - }; - Runnable outputAction = new Runnable() - { - @Override - public void run() + else { - // todo defer this to inside formatSequences (or later) - AlignmentExportData exportData = viewport - .getAlignExportData(options); - String output = new FormatAdapter(alignPanel, options) - .formatSequences(format, exportData.getAlignment(), - exportData.getOmitHidden(), - exportData.getStartEndPostions(), - viewport.getAlignment().getHiddenColumns()); - if (output == null) + // create backupfiles object and get new temp filename destination + boolean doBackup = BackupFiles.getEnabled(); + BackupFiles backupfiles = null; + if (doBackup) { - lastSaveSuccessful = false; + Console.trace("ALIGNFRAME making backupfiles object for " + file); + backupfiles = new BackupFiles(file); } - else + try { - // create backupfiles object and get new temp filename destination - boolean doBackup = BackupFiles.getEnabled(); - BackupFiles backupfiles = null; - if (doBackup) + String tempFilePath = doBackup ? backupfiles.getTempFilePath() + : file; + Console.trace("ALIGNFRAME setting PrintWriter"); + PrintWriter out = new PrintWriter(new FileWriter(tempFilePath)); + + if (backupfiles != null) { - Console.trace( - "ALIGNFRAME making backupfiles object for " + file); - backupfiles = new BackupFiles(file); + Console.trace("ALIGNFRAME about to write to temp file " + + backupfiles.getTempFilePath()); } - try - { - String tempFilePath = doBackup ? backupfiles.getTempFilePath() - : file; - Console.trace("ALIGNFRAME setting PrintWriter"); - PrintWriter out = new PrintWriter(new FileWriter(tempFilePath)); - if (backupfiles != null) - { - Console.trace("ALIGNFRAME about to write to temp file " - + backupfiles.getTempFilePath()); - } + out.print(output); + Console.trace("ALIGNFRAME about to close file"); + out.close(); + Console.trace("ALIGNFRAME closed file"); + AlignFrame.this.setTitle(file); + statusBar.setText(MessageManager.formatMessage( + "label.successfully_saved_to_file_in_format", new Object[] + { fileName, format.getName() })); + lastSaveSuccessful = true; + } catch (IOException e) + { + lastSaveSuccessful = false; + Console.error( + "ALIGNFRAME Something happened writing the temp file"); + Console.error(e.getMessage()); + Console.debug(Cache.getStackTraceString(e)); + } catch (Exception ex) + { + lastSaveSuccessful = false; + Console.error( + "ALIGNFRAME Something unexpected happened writing the temp file"); + Console.error(ex.getMessage()); + Console.debug(Cache.getStackTraceString(ex)); + } - out.print(output); - Console.trace("ALIGNFRAME about to close file"); - out.close(); - Console.trace("ALIGNFRAME closed file"); - AlignFrame.this.setTitle(file); - statusBar.setText(MessageManager.formatMessage( - "label.successfully_saved_to_file_in_format", - new Object[] - { fileName, format.getName() })); - lastSaveSuccessful = true; - } catch (IOException e) - { - lastSaveSuccessful = false; - Console.error( - "ALIGNFRAME Something happened writing the temp file"); - Console.error(e.getMessage()); - Console.debug(Cache.getStackTraceString(e)); - } catch (Exception ex) - { - lastSaveSuccessful = false; - Console.error( - "ALIGNFRAME Something unexpected happened writing the temp file"); - Console.error(ex.getMessage()); - Console.debug(Cache.getStackTraceString(ex)); - } + if (doBackup) + { + backupfiles.setWriteSuccess(lastSaveSuccessful); + Console.debug("ALIGNFRAME writing temp file was " + + (lastSaveSuccessful ? "" : "NOT ") + "successful"); + // do the backup file roll and rename the temp file to actual file + Console.trace("ALIGNFRAME about to rollBackupsAndRenameTempFile"); + lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile(); + Console.debug("ALIGNFRAME performed rollBackupsAndRenameTempFile " + + (lastSaveSuccessful ? "" : "un") + "successfully"); + } - if (doBackup) - { - backupfiles.setWriteSuccess(lastSaveSuccessful); - Console.debug("ALIGNFRAME writing temp file was " - + (lastSaveSuccessful ? "" : "NOT ") + "successful"); - // do the backup file roll and rename the temp file to actual file - Console.trace( - "ALIGNFRAME about to rollBackupsAndRenameTempFile"); - lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile(); - Console.debug( - "ALIGNFRAME performed rollBackupsAndRenameTempFile " - + (lastSaveSuccessful ? "" : "un") - + "successfully"); - } + Console.debug("lastSaveSuccessful=" + lastSaveSuccessful); + if (lastSaveSuccessful) + { + AlignFrame.this.getViewport().setSavedUpToDate(true); } } + return null; }; /* @@@ -1371,14 -1374,7 +1373,14 @@@ } else { - outputAction.run(); + try + { + outputAction.call(); + } catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } } } @@@ -1395,29 -1391,34 +1397,29 @@@ FileFormatI fileFormat = FileFormats.getInstance() .forName(fileFormatName); AlignExportSettingsI options = new AlignExportSettingsAdapter(false); - Runnable outputAction = new Runnable() - { - @Override - public void run() + Callable outputAction = () -> { + // todo defer this to inside formatSequences (or later) + AlignmentExportData exportData = viewport.getAlignExportData(options); + CutAndPasteTransfer cap = new CutAndPasteTransfer(); + cap.setForInput(null); + try { - // todo defer this to inside formatSequences (or later) - AlignmentExportData exportData = viewport - .getAlignExportData(options); - CutAndPasteTransfer cap = new CutAndPasteTransfer(); - cap.setForInput(null); - try - { - FileFormatI format = fileFormat; - cap.setText(new FormatAdapter(alignPanel, options) - .formatSequences(format, exportData.getAlignment(), - exportData.getOmitHidden(), - exportData.getStartEndPostions(), - viewport.getAlignment().getHiddenColumns())); - Desktop.addInternalFrame(cap, MessageManager.formatMessage( - "label.alignment_output_command", new Object[] - { fileFormat.getName() }), 600, 500); - } catch (OutOfMemoryError oom) - { - new OOMWarning("Outputting alignment as " + fileFormat.getName(), - oom); - cap.dispose(); - } + FileFormatI format = fileFormat; + cap.setText(new FormatAdapter(alignPanel, options).formatSequences( + format, exportData.getAlignment(), + exportData.getOmitHidden(), + exportData.getStartEndPostions(), + viewport.getAlignment().getHiddenColumns())); + Desktop.addInternalFrame(cap, MessageManager.formatMessage( + "label.alignment_output_command", new Object[] + { fileFormat.getName() }), 600, 500); + } catch (OutOfMemoryError oom) + { + new OOMWarning("Outputting alignment as " + fileFormat.getName(), + oom); + cap.dispose(); } + return null; }; /* @@@ -1432,13 -1433,7 +1434,13 @@@ } else { - outputAction.run(); + try + { + outputAction.call(); + } catch (Exception e) + { + e.printStackTrace(); + } } } @@@ -1546,11 -1541,15 +1548,11 @@@ .getString("label.load_jalview_annotations"); chooser.setDialogTitle(tooltip); chooser.setToolTipText(tooltip); - chooser.setResponseHandler(0, new Runnable() - { - @Override - public void run() - { - String choice = chooser.getSelectedFile().getPath(); - Cache.setProperty("LAST_DIRECTORY", choice); - loadJalviewDataFile(chooser.getSelectedFile(), null, null, null); - } + chooser.setResponseHandler(0, () -> { + String choice = chooser.getSelectedFile().getPath(); + Cache.setProperty("LAST_DIRECTORY", choice); + loadJalviewDataFile(chooser.getSelectedFile(), null, null, null); + return null; }); chooser.showOpenDialog(this); @@@ -2472,31 -2471,36 +2474,31 @@@ return; } - Runnable okAction = new Runnable() - { - @Override - public void run() - { - SequenceI[] cut = sg.getSequences() - .toArray(new SequenceI[sg.getSize()]); + Callable okAction = () -> { + SequenceI[] cut = sg.getSequences() + .toArray(new SequenceI[sg.getSize()]); - addHistoryItem(new EditCommand( - MessageManager.getString("label.cut_sequences"), Action.CUT, - cut, sg.getStartRes(), - sg.getEndRes() - sg.getStartRes() + 1, - viewport.getAlignment())); + addHistoryItem(new EditCommand( + MessageManager.getString("label.cut_sequences"), Action.CUT, + cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1, + viewport.getAlignment())); - viewport.setSelectionGroup(null); - viewport.sendSelection(); - viewport.getAlignment().deleteGroup(sg); + viewport.setSelectionGroup(null); + viewport.sendSelection(); + viewport.getAlignment().deleteGroup(sg); - viewport.firePropertyChange("alignment", null, - viewport.getAlignment().getSequences()); - if (viewport.getAlignment().getHeight() < 1) + viewport.firePropertyChange("alignment", null, + viewport.getAlignment().getSequences()); + if (viewport.getAlignment().getHeight() < 1) + { + try + { + AlignFrame.this.setClosed(true); + } catch (Exception ex) { - try - { - AlignFrame.this.setClosed(true); - } catch (Exception ex) - { - } } } + return null; }; /* @@@ -2520,13 -2524,7 +2522,13 @@@ } else { - okAction.run(); + try + { + okAction.call(); + } catch (Exception e) + { + e.printStackTrace(); + } } } @@@ -3447,6 -3445,7 +3449,7 @@@ .formatMessage("label.overview_params", new Object[] { this.getTitle() }), true, frame.getWidth(), frame.getHeight(), true, true); + frame.setFrameIcon(WindowIcons.overviewIcon); frame.pack(); frame.setLayer(JLayeredPane.PALETTE_LAYER); frame.addInternalFrameListener( @@@ -4079,31 -4078,36 +4082,31 @@@ chooser.setToolTipText( MessageManager.getString("label.load_tree_file")); - chooser.setResponseHandler(0, new Runnable() - { - @Override - public void run() + chooser.setResponseHandler(0, () -> { + String filePath = chooser.getSelectedFile().getPath(); + Cache.setProperty("LAST_DIRECTORY", filePath); + NewickFile fin = null; + try { - String filePath = chooser.getSelectedFile().getPath(); - Cache.setProperty("LAST_DIRECTORY", filePath); - NewickFile fin = null; - try - { - fin = new NewickFile(new FileParse(chooser.getSelectedFile(), - DataSourceType.FILE)); - viewport.setCurrentTree(showNewickTree(fin, filePath).getTree()); - } catch (Exception ex) - { - JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(), - MessageManager - .getString("label.problem_reading_tree_file"), - JvOptionPane.WARNING_MESSAGE); - ex.printStackTrace(); - } - if (fin != null && fin.hasWarningMessage()) - { - JvOptionPane.showMessageDialog(Desktop.desktop, - fin.getWarningMessage(), - MessageManager.getString( - "label.possible_problem_with_tree_file"), - JvOptionPane.WARNING_MESSAGE); - } + fin = new NewickFile(new FileParse(chooser.getSelectedFile(), + DataSourceType.FILE)); + viewport.setCurrentTree(showNewickTree(fin, filePath).getTree()); + } catch (Exception ex) + { + JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(), + MessageManager.getString("label.problem_reading_tree_file"), + JvOptionPane.WARNING_MESSAGE); + ex.printStackTrace(); } + if (fin != null && fin.hasWarningMessage()) + { + JvOptionPane.showMessageDialog(Desktop.desktop, + fin.getWarningMessage(), + MessageManager + .getString("label.possible_problem_with_tree_file"), + JvOptionPane.WARNING_MESSAGE); + } + return null; }); chooser.showOpenDialog(this); } @@@ -5875,12 -5879,16 +5878,12 @@@ chooser.setDialogTitle(MessageManager.getString("label.load_vcf_file")); chooser.setToolTipText(MessageManager.getString("label.load_vcf_file")); final AlignFrame us = this; - chooser.setResponseHandler(0, new Runnable() - { - @Override - public void run() - { - String choice = chooser.getSelectedFile().getPath(); - Cache.setProperty("LAST_DIRECTORY", choice); - SequenceI[] seqs = viewport.getAlignment().getSequencesArray(); - new VCFLoader(choice).loadVCF(seqs, us); - } + chooser.setResponseHandler(0, () -> { + String choice = chooser.getSelectedFile().getPath(); + Cache.setProperty("LAST_DIRECTORY", choice); + SequenceI[] seqs = viewport.getAlignment().getSequencesArray(); + new VCFLoader(choice).loadVCF(seqs, us); + return null; }); chooser.showOpenDialog(null); diff --combined src/jalview/gui/Desktop.java index 9e3eb55,20532be..2605195 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@@ -64,7 -64,6 +64,7 @@@ 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; @@@ -82,7 -81,6 +82,7 @@@ import javax.swing.JCheckBox import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDesktopPane; +import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; @@@ -92,7 -90,6 +92,7 @@@ import javax.swing.JProgressBar import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkEvent.EventType; import javax.swing.event.InternalFrameAdapter; @@@ -105,7 -102,6 +105,7 @@@ import jalview.api.AlignmentViewPanel import jalview.bin.Cache; import jalview.bin.Jalview; import jalview.gui.ImageExporter.ImageWriterI; +import jalview.gui.QuitHandler.QResponse; import jalview.io.BackupFiles; import jalview.io.DataSourceType; import jalview.io.FileFormat; @@@ -191,6 -187,14 +191,14 @@@ public class Desktop extends jalview.jb public static HashMap savingFiles = new HashMap(); + private static int DRAG_MODE = JDesktopPane.OUTLINE_DRAG_MODE; + + public static void setLiveDragMode(boolean b) + { + DRAG_MODE = b ? JDesktopPane.LIVE_DRAG_MODE + : JDesktopPane.OUTLINE_DRAG_MODE; + } + private JalviewChangeSupport changeSupport = new JalviewChangeSupport(); public static boolean nosplash = false; @@@ -455,14 -459,13 +463,14 @@@ setIconImages(ChannelProperties.getIconList()); + // override quit handling when GUI OS close [X] button pressed + this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent ev) { - quit(); + QuitHandler.QResponse ret = desktopQuit(true, true); // ui, disposeFlag } }); @@@ -487,7 -490,7 +495,7 @@@ } getContentPane().add(desktop, BorderLayout.CENTER); - desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); + desktop.setDragMode(DRAG_MODE); // This line prevents Windows Look&Feel resizing all new windows to maximum // if previous window was maximised @@@ -576,6 -579,15 +584,6 @@@ this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this)); - this.addWindowListener(new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent evt) - { - quit(); - } - }); - MouseAdapter ma; this.addMouseListener(ma = new MouseAdapter() { @@@ -1176,32 -1188,36 +1184,32 @@@ MessageManager.getString("label.open_local_file")); chooser.setToolTipText(MessageManager.getString("action.open")); - chooser.setResponseHandler(0, new Runnable() - { - @Override - public void run() - { - File selectedFile = chooser.getSelectedFile(); - Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent()); + chooser.setResponseHandler(0, () -> { + File selectedFile = chooser.getSelectedFile(); + Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent()); - FileFormatI format = chooser.getSelectedFormat(); + FileFormatI format = chooser.getSelectedFormat(); - /* - * Call IdentifyFile to verify the file contains what its extension implies. - * Skip this step for dynamically added file formats, because IdentifyFile does - * not know how to recognise them. - */ - if (FileFormats.getInstance().isIdentifiable(format)) + /* + * Call IdentifyFile to verify the file contains what its extension implies. + * Skip this step for dynamically added file formats, because IdentifyFile does + * not know how to recognise them. + */ + if (FileFormats.getInstance().isIdentifiable(format)) + { + try { - try - { - format = new IdentifyFile().identify(selectedFile, - DataSourceType.FILE); - } catch (FileFormatException e) - { - // format = null; //?? - } + format = new IdentifyFile().identify(selectedFile, + DataSourceType.FILE); + } catch (FileFormatException e) + { + // format = null; //?? } - - new FileLoader().LoadFile(viewport, selectedFile, - DataSourceType.FILE, format); } + + new FileLoader().LoadFile(viewport, selectedFile, DataSourceType.FILE, + format); + return null; }); chooser.showOpenDialog(this); } @@@ -1257,60 -1273,64 +1265,60 @@@ Object[] options = new Object[] { MessageManager.getString("action.ok"), MessageManager.getString("action.cancel") }; - Runnable action = new Runnable() - { - @Override - public void run() + Callable action = () -> { + @SuppressWarnings("unchecked") + String url = (history instanceof JTextField + ? ((JTextField) history).getText() + : ((JComboBox) history).getEditor().getItem() + .toString().trim()); + + if (url.toLowerCase(Locale.ROOT).endsWith(".jar")) { - @SuppressWarnings("unchecked") - String url = (history instanceof JTextField - ? ((JTextField) history).getText() - : ((JComboBox) history).getEditor().getItem() - .toString().trim()); - - if (url.toLowerCase(Locale.ROOT).endsWith(".jar")) + if (viewport != null) { - if (viewport != null) - { - new FileLoader().LoadFile(viewport, url, DataSourceType.URL, - FileFormat.Jalview); - } - else - { - new FileLoader().LoadFile(url, DataSourceType.URL, - FileFormat.Jalview); - } + new FileLoader().LoadFile(viewport, url, DataSourceType.URL, + FileFormat.Jalview); } else { - FileFormatI format = null; - try - { - format = new IdentifyFile().identify(url, DataSourceType.URL); - } catch (FileFormatException e) - { - // TODO revise error handling, distinguish between - // URL not found and response not valid - } + new FileLoader().LoadFile(url, DataSourceType.URL, + FileFormat.Jalview); + } + } + else + { + FileFormatI format = null; + try + { + format = new IdentifyFile().identify(url, DataSourceType.URL); + } catch (FileFormatException e) + { + // TODO revise error handling, distinguish between + // URL not found and response not valid + } - if (format == null) - { - String msg = MessageManager - .formatMessage("label.couldnt_locate", url); - JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg, - MessageManager.getString("label.url_not_found"), - JvOptionPane.WARNING_MESSAGE); + if (format == null) + { + String msg = MessageManager.formatMessage("label.couldnt_locate", + url); + JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg, + MessageManager.getString("label.url_not_found"), + JvOptionPane.WARNING_MESSAGE); - return; - } + return null; // Void + } - if (viewport != null) - { - new FileLoader().LoadFile(viewport, url, DataSourceType.URL, - format); - } - else - { - new FileLoader().LoadFile(url, DataSourceType.URL, format); - } + if (viewport != null) + { + new FileLoader().LoadFile(viewport, url, DataSourceType.URL, + format); + } + else + { + new FileLoader().LoadFile(url, DataSourceType.URL, format); } } + return null; // Void }; String dialogOption = MessageManager .getString("label.input_alignment_from_url"); @@@ -1340,79 -1360,39 +1348,79 @@@ } /* - * Exit the program + * Check with user and saving files before actually quitting */ - @Override - public void quit() + public void desktopQuit() { - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + ""); - Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + ""); - storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y, - getWidth(), getHeight())); + desktopQuit(true, false); + } - if (jconsole != null) - { - storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds()); - jconsole.stopConsole(); - } - if (jvnews != null) - { - storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds()); + public QuitHandler.QResponse desktopQuit(boolean ui, boolean disposeFlag) + { + final Callable doDesktopQuit = () -> { + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + ""); + Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + ""); + storeLastKnownDimensions("", new Rectangle(getBounds().x, + getBounds().y, getWidth(), getHeight())); - } - if (dialogExecutor != null) - { - dialogExecutor.shutdownNow(); - } - closeAll_actionPerformed(null); + if (jconsole != null) + { + storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds()); + jconsole.stopConsole(); + } - if (groovyConsole != null) - { - // suppress a possible repeat prompt to save script - groovyConsole.setDirty(false); - groovyConsole.exit(); - } + if (jvnews != null) + { + storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds()); + + } + + if (dialogExecutor != null) + { + dialogExecutor.shutdownNow(); + } + + closeAll_actionPerformed(null); + + if (groovyConsole != null) + { + // suppress a possible repeat prompt to save script + groovyConsole.setDirty(false); + groovyConsole.exit(); + } + + if (QuitHandler.gotQuitResponse() == QResponse.FORCE_QUIT) + { + // note that shutdown hook will not be run + jalview.bin.Console.debug("Force Quit selected by user"); + Runtime.getRuntime().halt(0); + } + + jalview.bin.Console.debug("Quit selected by user"); + if (disposeFlag) + { + instance.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + // instance.dispose(); + } + instance.quit(); + + return null; // Void + }; + + return QuitHandler.getQuitResponse(ui, doDesktopQuit, doDesktopQuit, + QuitHandler.defaultCancelQuit); + } + + /** + * Don't call this directly, use desktopQuit() above. Exits the program. + */ + @Override + public void quit() + { + // 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); } @@@ -1850,37 -1830,42 +1858,37 @@@ // allowBackupFiles chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle(MessageManager.getString("label.restore_state")); - chooser.setResponseHandler(0, new Runnable() - { - @Override - public void run() + chooser.setResponseHandler(0, () -> { + File selectedFile = chooser.getSelectedFile(); + setProjectFile(selectedFile); + String choice = selectedFile.getAbsolutePath(); + Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent()); + new Thread(new Runnable() { - File selectedFile = chooser.getSelectedFile(); - setProjectFile(selectedFile); - String choice = selectedFile.getAbsolutePath(); - Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent()); - new Thread(new Runnable() + @Override + public void run() { - @Override - public void run() + try { - try - { - new Jalview2XML().loadJalviewAlign(selectedFile); - } catch (OutOfMemoryError oom) - { - new OOMWarning("Whilst loading project from " + choice, oom); - } catch (Exception ex) - { - jalview.bin.Console.error( - "Problems whilst loading project from " + choice, ex); - JvOptionPane.showMessageDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.error_whilst_loading_project_from", - new Object[] - { choice }), - MessageManager - .getString("label.couldnt_load_project"), - JvOptionPane.WARNING_MESSAGE); - } + new Jalview2XML().loadJalviewAlign(selectedFile); + } catch (OutOfMemoryError oom) + { + new OOMWarning("Whilst loading project from " + choice, oom); + } catch (Exception ex) + { + jalview.bin.Console.error( + "Problems whilst loading project from " + choice, ex); + JvOptionPane.showMessageDialog(Desktop.desktop, + MessageManager.formatMessage( + "label.error_whilst_loading_project_from", + new Object[] + { choice }), + MessageManager.getString("label.couldnt_load_project"), + JvOptionPane.WARNING_MESSAGE); } - }, "Project Loader").start(); - } + } + }, "Project Loader").start(); + return null; }); chooser.showOpenDialog(this); @@@ -2544,7 -2529,7 +2552,7 @@@ @Override public void actionPerformed(ActionEvent e) { - quit(); + desktopQuit(); } }); } diff --combined src/jalview/gui/FeatureSettings.java index 0d6d371,922cc45..ebd4712 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@@ -411,6 -411,7 +411,7 @@@ public class FeatureSettings extends JP { frame = new JInternalFrame(); frame.setContentPane(this); + frame.setFrameIcon(WindowIcons.featuresIcon); Rectangle bounds = af.getFeatureSettingsGeometry(); String title; if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels( @@@ -947,10 -948,14 +948,10 @@@ chooser.setDialogTitle( MessageManager.getString("label.load_feature_colours")); chooser.setToolTipText(MessageManager.getString("action.load")); - chooser.setResponseHandler(0, new Runnable() - { - @Override - public void run() - { - File file = chooser.getSelectedFile(); - load(file); - } + chooser.setResponseHandler(0, () -> { + File file = chooser.getSelectedFile(); + load(file); + return null; }); chooser.showOpenDialog(this); } diff --combined src/jalview/gui/JvOptionPane.java index b58ea41,0b684d0..752f25c --- a/src/jalview/gui/JvOptionPane.java +++ b/src/jalview/gui/JvOptionPane.java @@@ -21,12 -21,20 +21,20 @@@ package jalview.gui; + import java.awt.AWTEvent; + import java.awt.ActiveEvent; import java.awt.Component; + import java.awt.Container; import java.awt.Dialog.ModalityType; + import java.awt.EventQueue; import java.awt.HeadlessException; + import java.awt.MenuComponent; + import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; + import java.awt.event.MouseAdapter; + import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; @@@ -34,18 -42,18 +42,21 @@@ import java.util.Arrays import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import java.util.concurrent.Executors; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JDialog; +import javax.swing.JFrame; import javax.swing.JInternalFrame; + import javax.swing.JLayeredPane; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import javax.swing.UIManager; + import javax.swing.event.InternalFrameEvent; + import javax.swing.event.InternalFrameListener; import jalview.util.Platform; import jalview.util.dialogrunner.DialogRunnerI; @@@ -55,13 -63,13 +66,13 @@@ public class JvOptionPane extends JOpti { private static final long serialVersionUID = -3019167117756785229L; - private static Object mockResponse = JOptionPane.CANCEL_OPTION; + private static Object mockResponse = JvOptionPane.CANCEL_OPTION; private static boolean interactiveMode = true; private Component parentComponent; - private Map callbacks = new HashMap<>(); + private Map> callbacks = new HashMap<>(); /* * JalviewJS reports user choice in the dialog as the selected @@@ -72,6 -80,7 +83,6 @@@ public JvOptionPane(final Component parent) { this.parentComponent = Platform.isJS() ? this : parent; - this.setIcon(WindowIcons.logoIcon); } public static int showConfirmDialog(Component parentComponent, @@@ -103,17 -112,17 +114,17 @@@ } switch (optionType) { - case JOptionPane.YES_NO_CANCEL_OPTION: + case JvOptionPane.YES_NO_CANCEL_OPTION: // FeatureRenderer amendFeatures ?? TODO ?? // Chimera close // PromptUserConfig // $FALL-THROUGH$ default: - case JOptionPane.YES_NO_OPTION: + case JvOptionPane.YES_NO_OPTION: // PromptUserConfig usage stats // for now treated as "OK CANCEL" // $FALL-THROUGH$ - case JOptionPane.OK_CANCEL_OPTION: + case JvOptionPane.OK_CANCEL_OPTION: // will fall back to simple HTML return JOptionPane.showConfirmDialog(parentComponent, message, title, optionType); @@@ -201,13 -210,13 +212,13 @@@ } switch (optionType) { - case JOptionPane.YES_NO_CANCEL_OPTION: + case JvOptionPane.YES_NO_CANCEL_OPTION: // ColourMenuHelper.addMenuItmers.offerRemoval TODO - case JOptionPane.YES_NO_OPTION: + case JvOptionPane.YES_NO_OPTION: // UserDefinedColoursSave -- relevant? TODO // $FALL-THROUGH$ default: - case JOptionPane.OK_CANCEL_OPTION: + case JvOptionPane.OK_CANCEL_OPTION: // EditNameDialog --- uses panel for messsage TODO @@@ -236,13 -245,13 +247,13 @@@ } switch (optionType) { - case JOptionPane.YES_NO_CANCEL_OPTION: - case JOptionPane.YES_NO_OPTION: + case JvOptionPane.YES_NO_CANCEL_OPTION: + case JvOptionPane.YES_NO_OPTION: // UserQuestionanaireCheck // VamsasApplication // $FALL-THROUGH$ default: - case JOptionPane.OK_CANCEL_OPTION: + case JvOptionPane.OK_CANCEL_OPTION: // will fall back to simple HTML return JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageType); @@@ -270,11 -279,11 +281,11 @@@ } switch (optionType) { - case JOptionPane.YES_NO_CANCEL_OPTION: - case JOptionPane.YES_NO_OPTION: + case JvOptionPane.YES_NO_CANCEL_OPTION: + case JvOptionPane.YES_NO_OPTION: //$FALL-THROUGH$ default: - case JOptionPane.OK_CANCEL_OPTION: + case JvOptionPane.OK_CANCEL_OPTION: // Preferences editLink/newLink return JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageType, icon); @@@ -711,7 -720,7 +722,7 @@@ public static void resetMock() { - setMockResponse(JOptionPane.CANCEL_OPTION); + setMockResponse(JvOptionPane.CANCEL_OPTION); setInteractiveMode(true); } @@@ -734,10 -743,10 +745,10 @@@ { switch (messageType) { - case JOptionPane.WARNING_MESSAGE: + case JvOptionPane.WARNING_MESSAGE: prefix = "WARNING! "; break; - case JOptionPane.ERROR_MESSAGE: + case JvOptionPane.ERROR_MESSAGE: prefix = "ERROR! "; break; default: @@@ -761,11 -770,6 +772,11 @@@ * @param string2 * @return */ + public static JvOptionPane newOptionDialog() + { + return new JvOptionPane(null); + } + public static JvOptionPane newOptionDialog(Component parentComponent) { return new JvOptionPane(parentComponent); @@@ -778,18 -782,10 +789,18 @@@ initialValue, true); } - public void showDialog(String message, String title, int optionType, + public void showDialog(Object message, String title, int optionType, int messageType, Icon icon, Object[] options, Object initialValue, boolean modal) { + showDialog(message, title, optionType, messageType, icon, options, + initialValue, modal, null); + } + + public void showDialog(Object message, String title, int optionType, + int messageType, Icon icon, Object[] options, Object initialValue, + boolean modal, JButton[] buttons) + { if (!isInteractiveMode()) { handleResponse(getMockResponse()); @@@ -813,98 -809,9 +824,98 @@@ if (modal) { + boolean useButtons = false; + Object initialValueButton = null; + NOTNULL: if (buttons != null) + { + if (buttons.length != options.length) + { + jalview.bin.Console.error( + "Supplied buttons array not the same length as supplied options array."); + break NOTNULL; + } + int[] buttonActions = { JOptionPane.YES_OPTION, + JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION }; + for (int i = 0; i < options.length; i++) + { + Object o = options[i]; + jalview.bin.Console.debug( + "Setting button " + i + " to '" + o.toString() + "'"); + JButton jb = buttons[i]; + + if (o.equals(initialValue)) + initialValueButton = jb; + + int buttonAction = buttonActions[i]; + Callable action = callbacks.get(buttonAction); + jb.setText((String) o); + jb.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + + Object obj = e.getSource(); + if (obj == null || !(obj instanceof Component)) + { + jalview.bin.Console.debug( + "Could not find Component source of event object " + + obj); + return; + } + Object joptionpaneObject = SwingUtilities.getAncestorOfClass( + JOptionPane.class, (Component) obj); + if (joptionpaneObject == null + || !(joptionpaneObject instanceof JOptionPane)) + { + jalview.bin.Console.debug( + "Could not find JOptionPane ancestor of event object " + + obj); + return; + } + JOptionPane joptionpane = (JOptionPane) joptionpaneObject; + joptionpane.setValue(buttonAction); + if (action != null) + Executors.newSingleThreadExecutor().submit(action); + joptionpane.transferFocusBackward(); + joptionpane.setVisible(false); + // put focus and raise parent window if possible, unless cancel or + // no button pressed + boolean raiseParent = (parentComponent != null); + if (buttonAction == JOptionPane.CANCEL_OPTION) + raiseParent = false; + if (optionType == JOptionPane.YES_NO_OPTION + && buttonAction == JOptionPane.NO_OPTION) + raiseParent = false; + if (raiseParent) + { + parentComponent.requestFocus(); + if (parentComponent instanceof JInternalFrame) + { + JInternalFrame jif = (JInternalFrame) parentComponent; + jif.show(); + jif.moveToFront(); + jif.grabFocus(); + } + else if (parentComponent instanceof Window) + { + Window w = (Window) parentComponent; + w.toFront(); + w.requestFocus(); + } + } + joptionpane.setVisible(false); + } + }); + + } + useButtons = true; + } // use a JOptionPane as usual int response = JOptionPane.showOptionDialog(parentComponent, message, - title, optionType, messageType, icon, options, initialValue); + title, optionType, messageType, icon, + useButtons ? buttons : options, + useButtons ? initialValueButton : initialValue); /* * In Java, the response is returned to this thread and handled here; @@@ -927,13 -834,102 +938,101 @@@ * attached to the button press of the dialog. This means we can use * a non-modal JDialog for the confirmation without blocking the GUI. */ + JOptionPane joptionpane = new JOptionPane(); + // Make button options + int[] buttonActions = { JvOptionPane.YES_OPTION, + JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION }; - JDialog dialog = createDialog(parentComponent, message, title, - optionType, messageType, icon, options, initialValue, modal, - buttons); - jalview.bin.Console.debug("About to setVisible(true)"); + // we need the strings to make the buttons with actionEventListener + if (options == null) + { + ArrayList options_default = new ArrayList<>(); + options_default + .add(UIManager.getString("OptionPane.yesButtonText")); + if (optionType == JvOptionPane.YES_NO_OPTION + || optionType == JvOptionPane.YES_NO_CANCEL_OPTION) + { + options_default + .add(UIManager.getString("OptionPane.noButtonText")); + } + if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION) + { + options_default + .add(UIManager.getString("OptionPane.cancelButtonText")); + } + options = options_default.toArray(); + } + + ArrayList options_btns = new ArrayList<>(); + Object initialValue_btn = null; - if (!Platform.isJS()) // JalviewJS already uses callback, don't need to - // add them here ++ if (!Platform.isJS()) // JalviewJS already uses callback, don't need to add them here + { + for (int i = 0; i < options.length && i < 3; i++) + { + Object o = options[i]; + int buttonAction = buttonActions[i]; + Runnable action = callbacks.get(buttonAction); + JButton jb = new JButton(); + jb.setText((String) o); + jb.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + joptionpane.setValue(buttonAction); + if (action != null) + Executors.defaultThreadFactory().newThread(action).start(); + // joptionpane.transferFocusBackward(); + joptionpane.transferFocusBackward(); + joptionpane.setVisible(false); + // put focus and raise parent window if possible, unless cancel + // button pressed + boolean raiseParent = (parentComponent != null); + if (buttonAction == JvOptionPane.CANCEL_OPTION) + raiseParent = false; + if (optionType == JvOptionPane.YES_NO_OPTION + && buttonAction == JvOptionPane.NO_OPTION) + raiseParent = false; + if (raiseParent) + { + parentComponent.requestFocus(); + if (parentComponent instanceof JInternalFrame) + { + JInternalFrame jif = (JInternalFrame) parentComponent; + jif.show(); + jif.moveToFront(); + jif.grabFocus(); + } + else if (parentComponent instanceof Window) + { + Window w = (Window) parentComponent; + w.toFront(); + w.requestFocus(); + } + } + joptionpane.setVisible(false); + } + }); + options_btns.add(jb); + if (o.equals(initialValue)) + initialValue_btn = jb; + } + } + joptionpane.setMessage(message); + joptionpane.setMessageType(messageType); + joptionpane.setOptionType(optionType); + joptionpane.setIcon(icon); + joptionpane.setOptions( + Platform.isJS() ? options : options_btns.toArray()); + joptionpane.setInitialValue( + Platform.isJS() ? initialValue : initialValue_btn); + + JDialog dialog = joptionpane.createDialog(parentComponent, title); + dialog.setIconImage(WindowIcons.logoIcon.getImage()); + dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL + : ModalityType.MODELESS); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setVisible(true); - jalview.bin.Console.debug("Just setVisible(true)"); } } @@@ -946,20 -942,76 +1045,77 @@@ handleResponse(getMockResponse()); } + // need to set these separately so we can set the title bar icon later + this.setOptionType(yesNoCancelOption); + this.setMessageType(questionMessage); + this.setIcon(icon); + this.setInitialValue(initresponse); + this.setOptions(options); + this.setMessage(mainPanel); + ourOptions = Arrays.asList(options); + int response; if (parentComponent != this) { - response = JOptionPane.showInternalOptionDialog(parentComponent, - mainPanel, title, yesNoCancelOption, questionMessage, icon, - options, initresponse); + JInternalFrame jif = this.createInternalFrame(parentComponent, title); + jif.setFrameIcon(WindowIcons.logoIcon); + jif.addInternalFrameListener(new InternalFrameListener() + { + @Override + public void internalFrameActivated(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameClosed(InternalFrameEvent arg0) + { + JvOptionPane.this.internalDialogHandleResponse(); + } + + @Override + public void internalFrameClosing(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameDeactivated(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameDeiconified(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameIconified(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameOpened(InternalFrameEvent arg0) + { + } + }); + jif.setVisible(true); + startModal(jif); + return; } else { - response = JOptionPane.showOptionDialog(parentComponent, mainPanel, - title, yesNoCancelOption, questionMessage, icon, options, - initresponse); + JDialog dialog = this.createDialog(parentComponent, title); + dialog.setIconImage(WindowIcons.logoIcon.getImage()); + dialog.setVisible(true); // blocking + this.internalDialogHandleResponse(); + return; } + } + + private void internalDialogHandleResponse() + { + String responseString = (String) this.getValue(); + int response = ourOptions.indexOf(responseString); + if (!Platform.isJS()) /** * Java only @@@ -971,103 -1023,14 +1127,103 @@@ } } + /* @Override public JvOptionPane setResponseHandler(Object response, Runnable action) { + callbacks.put(response, new Callable() + { + @Override + public Void call() + { + action.run(); + return null; + } + }); + return this; + } + */ + @Override + public JvOptionPane setResponseHandler(Object response, + Callable action) + { callbacks.put(response, action); return this; } /** + * showDialogOnTop will create a dialog that (attempts to) come to top of OS + * desktop windows + */ + public static int showDialogOnTop(String label, String actionString, + int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE) + { + // Ensure Jalview window is brought to front (primarily for Quit + // confirmation window to be visible) + + // This method of raising the Jalview window is broken in java + // jalviewDesktop.setVisible(true); + // jalviewDesktop.toFront(); + + // A better hack which works is to create a new JFrame parent with + // setAlwaysOnTop(true) + JFrame dialogParent = new JFrame(); + dialogParent.setAlwaysOnTop(true); + + int answer = JOptionPane.showConfirmDialog(dialogParent, label, + actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE); + + dialogParent.setAlwaysOnTop(false); + dialogParent.dispose(); + + return answer; + } + + public void showDialogOnTopAsync(String label, String actionString, + int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon, + Object[] options, Object initialValue, boolean modal) + { + showDialogOnTopAsync(new JFrame(), label, actionString, + JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options, + initialValue, modal); + } + + public void showDialogOnTopAsync(JFrame dialogParent, Object label, + String actionString, int JOPTIONPANE_OPTION, + int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options, + Object initialValue, boolean modal) + { + showDialogOnTopAsync(dialogParent, label, actionString, + JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options, + initialValue, modal, null); + } + + public void showDialogOnTopAsync(JFrame dialogParent, Object label, + String actionString, int JOPTIONPANE_OPTION, + int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options, + Object initialValue, boolean modal, JButton[] buttons) + { + // Ensure Jalview window is brought to front (primarily for Quit + // confirmation window to be visible) + + // This method of raising the Jalview window is broken in java + // jalviewDesktop.setVisible(true); + // jalviewDesktop.toFront(); + + // A better hack which works is to create a new JFrame parent with + // setAlwaysOnTop(true) + dialogParent.setAlwaysOnTop(true); + parentComponent = dialogParent; + + showDialog(label, actionString, JOPTIONPANE_OPTION, + JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal, + buttons); + + dialogParent.setAlwaysOnTop(false); + dialogParent.dispose(); + } + + /** * JalviewJS signals option selection by a property change event for the * option e.g. "OK". This methods responds to that by running the response * action that corresponds to that option. @@@ -1100,162 -1063,85 +1256,236 @@@ { return; } - Runnable action = callbacks.get(response); + Callable action = callbacks.get(response); if (action != null) { - action.run(); - parentComponent.requestFocus(); + try + { + action.call(); + } catch (Exception e) + { + e.printStackTrace(); + } + if (parentComponent != null) + parentComponent.requestFocus(); + } + } + + /** + * Create a non-modal confirm dialog + */ + public JDialog createDialog(Component parentComponent, Object message, + String title, int optionType, int messageType, Icon icon, + Object[] options, Object initialValue, boolean modal) + { + return createDialog(parentComponent, message, title, optionType, + messageType, icon, options, initialValue, modal, null); + } + + public JDialog createDialog(Component parentComponent, Object message, + String title, int optionType, int messageType, Icon icon, + Object[] options, Object initialValue, boolean modal, + JButton[] buttons) + { + JButton[] optionsButtons = null; + Object initialValueButton = null; + JOptionPane joptionpane = new JOptionPane(); + // Make button options + int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION, + JOptionPane.CANCEL_OPTION }; + + // we need the strings to make the buttons with actionEventListener + if (options == null) + { + ArrayList options_default = new ArrayList<>(); + options_default.add(UIManager.getString("OptionPane.yesButtonText")); + if (optionType == JOptionPane.YES_NO_OPTION + || optionType == JOptionPane.YES_NO_CANCEL_OPTION) + { + options_default.add(UIManager.getString("OptionPane.noButtonText")); + } + if (optionType == JOptionPane.YES_NO_CANCEL_OPTION) + { + options_default + .add(UIManager.getString("OptionPane.cancelButtonText")); + } + options = options_default.toArray(); } + if (!Platform.isJS()) // JalviewJS already uses callback, don't need to + // add them here + { + if (((optionType == JOptionPane.YES_OPTION + || optionType == JOptionPane.NO_OPTION + || optionType == JOptionPane.CANCEL_OPTION + || optionType == JOptionPane.OK_OPTION + || optionType == JOptionPane.DEFAULT_OPTION) + && options.length < 1) + || ((optionType == JOptionPane.YES_NO_OPTION + || optionType == JOptionPane.OK_CANCEL_OPTION) + && options.length < 2) + || (optionType == JOptionPane.YES_NO_CANCEL_OPTION + && options.length < 3)) + { + jalview.bin.Console + .debug("JvOptionPane: not enough options for dialog type"); + } + optionsButtons = new JButton[options.length]; + for (int i = 0; i < options.length && i < 3; i++) + { + Object o = options[i]; + int buttonAction = buttonActions[i]; + Callable action = callbacks.get(buttonAction); + JButton jb; + if (buttons != null && buttons.length > i && buttons[i] != null) + { + jb = buttons[i]; + } + else + { + jb = new JButton(); + } + jb.setText((String) o); + jb.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + joptionpane.setValue(buttonAction); + if (action != null) + Executors.newSingleThreadExecutor().submit(action); + // joptionpane.transferFocusBackward(); + joptionpane.transferFocusBackward(); + joptionpane.setVisible(false); + // put focus and raise parent window if possible, unless cancel + // button pressed + boolean raiseParent = (parentComponent != null); + if (buttonAction == JOptionPane.CANCEL_OPTION) + raiseParent = false; + if (optionType == JOptionPane.YES_NO_OPTION + && buttonAction == JOptionPane.NO_OPTION) + raiseParent = false; + if (raiseParent) + { + parentComponent.requestFocus(); + if (parentComponent instanceof JInternalFrame) + { + JInternalFrame jif = (JInternalFrame) parentComponent; + jif.show(); + jif.moveToFront(); + jif.grabFocus(); + } + else if (parentComponent instanceof Window) + { + Window w = (Window) parentComponent; + w.toFront(); + w.requestFocus(); + } + } + joptionpane.setVisible(false); + } + }); + optionsButtons[i] = jb; + if (o.equals(initialValue)) + initialValueButton = jb; + } + } + joptionpane.setMessage(message); + joptionpane.setMessageType(messageType); + joptionpane.setOptionType(optionType); + joptionpane.setIcon(icon); + joptionpane.setOptions(Platform.isJS() ? options : optionsButtons); + joptionpane.setInitialValue( + Platform.isJS() ? initialValue : initialValueButton); + + JDialog dialog = joptionpane.createDialog(parentComponent, title); + dialog.setModalityType( + modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + return dialog; + } + + /** + * Utility to programmatically click a button on a JOptionPane (as a JFrame) + * + * returns true if button was found + */ + public static boolean clickButton(JFrame frame, int buttonType) + { + + return false; } + + /** + * This helper method makes the JInternalFrame wait until it is notified by an + * InternalFrameClosing event. This method also adds the given JOptionPane to + * the JInternalFrame and sizes it according to the JInternalFrame's preferred + * size. + * + * @param f + * The JInternalFrame to make modal. + */ + private static void startModal(JInternalFrame f) + { + // We need to add an additional glasspane-like component directly + // below the frame, which intercepts all mouse events that are not + // directed at the frame itself. + JPanel modalInterceptor = new JPanel(); + modalInterceptor.setOpaque(false); + JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f); + lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue()); + modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight()); + modalInterceptor.addMouseListener(new MouseAdapter() + { + }); + modalInterceptor.addMouseMotionListener(new MouseMotionAdapter() + { + }); + lp.add(modalInterceptor); + f.toFront(); + + // We need to explicitly dispatch events when we are blocking the event + // dispatch thread. + EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); + try + { + while (!f.isClosed()) + { + if (EventQueue.isDispatchThread()) + { + // The getNextEventMethod() issues wait() when no + // event is available, so we don't need do explicitly wait(). + AWTEvent ev = queue.getNextEvent(); + // This mimics EventQueue.dispatchEvent(). We can't use + // EventQueue.dispatchEvent() directly, because it is + // protected, unfortunately. + if (ev instanceof ActiveEvent) + ((ActiveEvent) ev).dispatch(); + else if (ev.getSource() instanceof Component) + ((Component) ev.getSource()).dispatchEvent(ev); + else if (ev.getSource() instanceof MenuComponent) + ((MenuComponent) ev.getSource()).dispatchEvent(ev); + // Other events are ignored as per spec in + // EventQueue.dispatchEvent + } + else + { + // Give other threads a chance to become active. + Thread.yield(); + } + } + } catch (InterruptedException ex) + { + // If we get interrupted, then leave the modal state. + } finally + { + // Clean up the modal interceptor. + lp.remove(modalInterceptor); + + // Remove the internal frame from its parent, so it is no longer + // lurking around and clogging memory. + Container parent = f.getParent(); + if (parent != null) + parent.remove(f); + } + } } diff --combined src/jalview/gui/StructureViewerBase.java index 6765366,f4970ae..82b6759 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@@ -135,6 -135,7 +135,7 @@@ public abstract class StructureViewerBa public StructureViewerBase() { super(); + setFrameIcon(WindowIcons.structureIcon); } /** @@@ -1280,9 -1281,6 +1281,9 @@@ if (confirm == JvOptionPane.CANCEL_OPTION || confirm == JvOptionPane.CLOSED_OPTION) { + // abort possible quit handling if CANCEL chosen + if (confirm == JvOptionPane.CANCEL_OPTION) + QuitHandler.abortQuit(); return; } forceClose = confirm == JvOptionPane.YES_OPTION; diff --combined src/jalview/gui/UserDefinedColours.java index 4db8bfa,1a7ae4a..4979746 --- a/src/jalview/gui/UserDefinedColours.java +++ b/src/jalview/gui/UserDefinedColours.java @@@ -148,6 -148,7 +148,7 @@@ public class UserDefinedColours extend { colorChooser.getSelectionModel().addChangeListener(this); frame = new JInternalFrame(); + frame.setFrameIcon(WindowIcons.logoIcon); frame.setContentPane(this); Desktop.addInternalFrame(frame, MessageManager.getString("label.user_defined_colours"), @@@ -651,41 -652,45 +652,41 @@@ chooser.setDialogTitle( MessageManager.getString("label.load_colour_scheme")); chooser.setToolTipText(MessageManager.getString("action.load")); - chooser.setResponseHandler(0, new Runnable() - { - @Override - public void run() - { - File choice = chooser.getSelectedFile(); - Cache.setProperty(LAST_DIRECTORY, choice.getParent()); - - UserColourScheme ucs = ColourSchemeLoader - .loadColourScheme(choice.getAbsolutePath()); - Color[] colors = ucs.getColours(); - schemeName.setText(ucs.getSchemeName()); + chooser.setResponseHandler(0, () -> { + File choice = chooser.getSelectedFile(); + Cache.setProperty(LAST_DIRECTORY, choice.getParent()); - if (ucs.getLowerCaseColours() != null) - { - caseSensitive.setSelected(true); - lcaseColour.setEnabled(true); - resetButtonPanel(true); - for (int i = 0; i < lowerCaseButtons.size(); i++) - { - JButton button = lowerCaseButtons.get(i); - button.setBackground(ucs.getLowerCaseColours()[i]); - } - } - else - { - caseSensitive.setSelected(false); - lcaseColour.setEnabled(false); - resetButtonPanel(false); - } + UserColourScheme ucs = ColourSchemeLoader + .loadColourScheme(choice.getAbsolutePath()); + Color[] colors = ucs.getColours(); + schemeName.setText(ucs.getSchemeName()); - for (int i = 0; i < upperCaseButtons.size(); i++) + if (ucs.getLowerCaseColours() != null) + { + caseSensitive.setSelected(true); + lcaseColour.setEnabled(true); + resetButtonPanel(true); + for (int i = 0; i < lowerCaseButtons.size(); i++) { - JButton button = upperCaseButtons.get(i); - button.setBackground(colors[i]); + JButton button = lowerCaseButtons.get(i); + button.setBackground(ucs.getLowerCaseColours()[i]); } + } + else + { + caseSensitive.setSelected(false); + lcaseColour.setEnabled(false); + resetButtonPanel(false); + } - addNewColourScheme(choice.getPath()); + for (int i = 0; i < upperCaseButtons.size(); i++) + { + JButton button = upperCaseButtons.get(i); + button.setBackground(colors[i]); } + + addNewColourScheme(choice.getPath()); + return null; }); chooser.showOpenDialog(this); diff --combined src/jalview/io/JalviewFileChooser.java index dba7ad1,8cd5406..077bffb --- a/src/jalview/io/JalviewFileChooser.java +++ b/src/jalview/io/JalviewFileChooser.java @@@ -21,6 -21,6 +21,12 @@@ ////////////////////////////////////////////////////////////////// package jalview.io; ++import jalview.bin.Cache; ++import jalview.gui.JvOptionPane; ++import jalview.util.MessageManager; ++import jalview.util.Platform; ++import jalview.util.dialogrunner.DialogRunnerI; ++ import java.awt.Component; import java.awt.Dimension; import java.awt.EventQueue; @@@ -38,14 -38,13 +44,15 @@@ import java.util.List import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; +import java.util.concurrent.Callable; import javax.swing.BoxLayout; import javax.swing.DefaultListCellRenderer; import javax.swing.JCheckBox; + import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JList; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SpringLayout; @@@ -54,6 -53,7 +61,7 @@@ import javax.swing.plaf.basic.BasicFile import jalview.bin.Cache; import jalview.gui.JvOptionPane; + import jalview.gui.WindowIcons; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.util.dialogrunner.DialogRunnerI; @@@ -72,7 -72,7 +80,7 @@@ public class JalviewFileChooser extend { private static final long serialVersionUID = 1L; - private Map callbacks = new HashMap<>(); + private Map callbacks = new HashMap<>(); File selectedFile = null; @@@ -490,13 -490,10 +498,13 @@@ if (selectedFile.exists()) { - int confirm = JvOptionPane.showConfirmDialog(this, - MessageManager.getString("label.overwrite_existing_file"), - MessageManager.getString("label.file_already_exists"), - JvOptionPane.YES_NO_OPTION); + int confirm = Cache.getDefault("CONFIRM_OVERWRITE_FILE", true) + ? JvOptionPane.showConfirmDialog(this, + MessageManager + .getString("label.overwrite_existing_file"), + MessageManager.getString("label.file_already_exists"), + JvOptionPane.YES_NO_OPTION) + : JOptionPane.YES_OPTION; if (confirm != JvOptionPane.YES_OPTION) { @@@ -601,26 -598,8 +609,26 @@@ } + /* + @Override + public JalviewFileChooser setResponseHandler(Object response, + Runnable action) + { + callbacks.put(response, new Callable() + { + @Override + public Void call() + { + action.run(); + return null; + } + }); + return this; + } + */ + @Override - public DialogRunnerI setResponseHandler(Object response, Runnable action) + public DialogRunnerI setResponseHandler(Object response, Callable action) { callbacks.put(response, action); return this; @@@ -636,16 -615,10 +644,16 @@@ { return; } - Runnable action = callbacks.get(response); + Callable action = callbacks.get(response); if (action != null) { - action.run(); + try + { + action.call(); + } catch (Exception e) + { + e.printStackTrace(); + } } } @@@ -670,4 -643,13 +678,13 @@@ break; } } + + @Override + protected JDialog createDialog(Component parent) throws HeadlessException + { + JDialog dialog = super.createDialog(parent); + dialog.setIconImage(WindowIcons.logoIcon.getImage()); + return dialog; + } + }