Skip to content

Commit

Permalink
Merge pull request #1452 from SiboVG/issue-1100
Browse files Browse the repository at this point in the history
[fixes #1100] Implement macOS idle application mode after all windows are closed
  • Loading branch information
SiboVG committed Jun 16, 2022
2 parents de3591a + f15094b commit 300e165
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 24 deletions.
56 changes: 50 additions & 6 deletions swing/src/net/sf/openrocket/gui/main/BasicFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.appearance.DecalImage;
import net.sf.openrocket.arch.SystemInfo;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.OpenRocketDocumentFactory;
import net.sf.openrocket.document.StorageOptions;
Expand All @@ -60,6 +61,7 @@
import net.sf.openrocket.gui.main.componenttree.ComponentTree;
import net.sf.openrocket.gui.main.flightconfigpanel.FlightConfigurationPanel;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.gui.util.DummyFrameMenuOSX;
import net.sf.openrocket.gui.util.FileHelper;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.Icons;
Expand Down Expand Up @@ -99,9 +101,9 @@ public class BasicFrame extends JFrame {
private static final Translator trans = Application.getTranslator();
private static final Preferences prefs = Application.getPreferences();

private static final int SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
public static final int SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();

private static final int SHIFT_SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() |
public static final int SHIFT_SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() |
SHIFT_DOWN_MASK;

public static final int COMPONENT_TAB = 0;
Expand Down Expand Up @@ -1191,6 +1193,10 @@ public int getSelectedTab() {


private void openAction() {
openAction(this);
}

public static void openAction(Window parent) {
JFileChooser chooser = new JFileChooser();

chooser.addChoosableFileFilter(FileHelper.ALL_DESIGNS_FILTER);
Expand All @@ -1200,7 +1206,7 @@ private void openAction() {
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(true);
chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
int option = chooser.showOpenDialog(this);
int option = chooser.showOpenDialog(parent);
if (option != JFileChooser.APPROVE_OPTION) {
log.info(Markers.USER_MARKER, "Decided not to open files, option=" + option);
return;
Expand All @@ -1213,7 +1219,7 @@ private void openAction() {

for (File file : files) {
log.info("Opening file: " + file);
if (open(file, this)) {
if (open(file, parent)) {
MRUDesignFile opts = MRUDesignFile.getInstance();
opts.addFile(file.getAbsolutePath());
}
Expand Down Expand Up @@ -1691,8 +1697,13 @@ private boolean closeAction() {

frames.remove(this);
if (frames.isEmpty()) {
log.info("Last frame closed, exiting");
System.exit(0);
// Don't quit the application on macOS, but keep the application open
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) {
DummyFrameMenuOSX.createDummyDialog();
} else {
log.info("Last frame closed, exiting");
System.exit(0);
}
}
return true;
}
Expand All @@ -1707,6 +1718,31 @@ public void printAction() {
new PrintDialog(this, document, rotation).setVisible(true);
}

/**
* Opens a new design file or the last design file, if set in the preferences.
* Can be used for reopening the application or opening it the first time.
*/
public static void reopen() {
if (!Application.getPreferences().isAutoOpenLastDesignOnStartupEnabled()) {
BasicFrame.newAction();
} else {
String lastFile = MRUDesignFile.getInstance().getLastEditedDesignFile();
if (lastFile != null) {
log.info("Opening last design file: " + lastFile);
if (!BasicFrame.open(new File(lastFile), null)) {
MRUDesignFile.getInstance().removeFile(lastFile);
BasicFrame.newAction();
}
else {
MRUDesignFile.getInstance().addFile(lastFile);
}
}
else {
BasicFrame.newAction();
}
}
}


/**
* Open a new design window with a basic rocket+stage.
Expand Down Expand Up @@ -1779,6 +1815,14 @@ public static BasicFrame findFrame(Rocket rocket) {
return null;
}

/**
* Checks whether all the BasicFrames are closed.
* @return true if all the BasicFrames are closed, false if not
*/
public static boolean isFramesEmpty() {
return frames.isEmpty();
}

/**
* Find a currently open document by the rocket object. This method can be used
* to map a Rocket to OpenRocketDocument from GUI methods.
Expand Down
185 changes: 185 additions & 0 deletions swing/src/net/sf/openrocket/gui/util/DummyFrameMenuOSX.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package net.sf.openrocket.gui.util;

import net.sf.openrocket.gui.dialogs.AboutDialog;
import net.sf.openrocket.gui.dialogs.LicenseDialog;
import net.sf.openrocket.gui.help.tours.GuidedTourSelectionDialog;
import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.gui.main.ExampleDesignFileAction;
import net.sf.openrocket.gui.main.MRUDesignFileAction;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.Markers;
import net.sf.openrocket.startup.Application;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

/**
* This dummy frame will be generated when all design windows are close on macOS.
* It serves to maintain and customize the application menu bar when all the BasicFrame windows are closed.
*
* @author Sibo Van Gool <sibo.vangool@hotmail.com>
*/
public class DummyFrameMenuOSX extends JFrame {
private static final Translator trans = Application.getTranslator();
private static final Logger log = LoggerFactory.getLogger(BasicFrame.class);

private static DummyFrameMenuOSX dialog;

private DummyFrameMenuOSX() {
createMenu();
setUndecorated(true);
setBackground(new Color(0, 0, 0, 0));
setVisible(true); // this is needed to show the menu bar
}

public static DummyFrameMenuOSX createDummyDialog() {
dialog = new DummyFrameMenuOSX();
return dialog;
}

public static DummyFrameMenuOSX getDummyDialog() {
return dialog;
}

public static void removeDummyDialog() {
if (dialog != null) {
dialog.dispose();
dialog = null;
}
}

private void createMenu() {
JMenuBar menubar = new JMenuBar();
JMenu menu;
JMenuItem item;

//// File
menu = new JMenu(trans.get("main.menu.file"));
menu.setMnemonic(KeyEvent.VK_F);
//// File-handling related tasks
menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.desc"));
menubar.add(menu);

//// New
item = new JMenuItem(trans.get("main.menu.file.new"), KeyEvent.VK_N);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, BasicFrame.SHORTCUT_KEY));
item.setMnemonic(KeyEvent.VK_N);
//// Create a new rocket design
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.new.desc"));
item.setIcon(Icons.FILE_NEW);
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
DummyFrameMenuOSX.removeDummyDialog();
log.info(Markers.USER_MARKER, "New... selected");
BasicFrame.newAction();
}
});
menu.add(item);

//// Open...
item = new JMenuItem(trans.get("main.menu.file.open"), KeyEvent.VK_O);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, BasicFrame.SHORTCUT_KEY));
//// Open a rocket design
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.open.desc"));
item.setIcon(Icons.FILE_OPEN);
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
DummyFrameMenuOSX.removeDummyDialog();
log.info(Markers.USER_MARKER, "Open... selected");
BasicFrame.openAction(DummyFrameMenuOSX.this);
}
});
menu.add(item);

//// Open Recent...
item = new MRUDesignFileAction(trans.get("main.menu.file.openRecent"), this);
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.openRecent.desc"));
item.setIcon(Icons.FILE_OPEN);
menu.add(item);

//// Open example...
item = new ExampleDesignFileAction(trans.get("main.menu.file.openExample"), null);
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.openExample.desc"));
item.setIcon(Icons.FILE_OPEN_EXAMPLE);
menu.add(item);

menu.addSeparator();

//// Quit
item = new JMenuItem(trans.get("main.menu.file.quit"), KeyEvent.VK_Q);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, BasicFrame.SHORTCUT_KEY));
//// Quit the program
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.quit.desc"));
item.setIcon(Icons.FILE_QUIT);
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
log.info(Markers.USER_MARKER, "Quit selected");
BasicFrame.quitAction();
}
});
menu.add(item);


//// Help
menu = new JMenu(trans.get("main.menu.help"));
menu.setMnemonic(KeyEvent.VK_H);
menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.desc"));
menubar.add(menu);

//// Guided tours
item = new JMenuItem(trans.get("main.menu.help.tours"), KeyEvent.VK_L);
item.setIcon(Icons.HELP_TOURS);
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.tours.desc"));
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
log.info(Markers.USER_MARKER, "Guided tours selected");
GuidedTourSelectionDialog.showDialog(DummyFrameMenuOSX.this);
}
});
menu.add(item);

menu.addSeparator();

//// License
item = new JMenuItem(trans.get("main.menu.help.license"), KeyEvent.VK_L);
item.setIcon(Icons.HELP_LICENSE);
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.license.desc"));
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
log.info(Markers.USER_MARKER, "License selected");
new LicenseDialog(DummyFrameMenuOSX.this).setVisible(true);
}
});
menu.add(item);

//// About
item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A);
item.setIcon(Icons.HELP_ABOUT);
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.about.desc"));
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
log.info(Markers.USER_MARKER, "About selected");
new AboutDialog(DummyFrameMenuOSX.this).setVisible(true);
}
});
menu.add(item);


this.setJMenuBar(menubar);
}
}
10 changes: 10 additions & 0 deletions swing/src/net/sf/openrocket/startup/OSXSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import java.awt.desktop.OpenFilesHandler;
import java.awt.desktop.PreferencesHandler;
import java.awt.desktop.QuitHandler;
import java.awt.desktop.AppReopenedListener;

import net.sf.openrocket.gui.util.DummyFrameMenuOSX;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -50,6 +52,13 @@ final class OSXSetup {
r.cancelQuit();
};

private static final AppReopenedListener APP_REOPENED_HANDLER = (e) -> {
if (BasicFrame.isFramesEmpty()) {
log.info("App re-opened");
BasicFrame.reopen();
}
};

/**
* The handler for the About item in the OSX app menu
*/
Expand Down Expand Up @@ -92,6 +101,7 @@ static void setupOSX() {
osxDesktop.setAboutHandler(ABOUT_HANDLER);
osxDesktop.setPreferencesHandler(PREFERENCES_HANDLER);
osxDesktop.setQuitHandler(QUIT_HANDLER);
osxDesktop.addAppEventListener(APP_REOPENED_HANDLER);

// Set the dock icon to the largest icon
final Image dockIcon = Toolkit.getDefaultToolkit().getImage(
Expand Down
22 changes: 4 additions & 18 deletions swing/src/net/sf/openrocket/startup/SwingStartup.java
Original file line number Diff line number Diff line change
Expand Up @@ -216,28 +216,14 @@ private void runInEDT(String[] args) {
Databases.fakeMethod();

// Set up the OSX file open handler here so that it can handle files that are opened when OR is not yet running.
OSXSetup.setupOSXOpenFileHandler();
if (SystemInfo.getPlatform() == Platform.MAC_OS) {
OSXSetup.setupOSXOpenFileHandler();
}

// Starting action (load files or open new document)
log.info("Opening main application window");
if (!handleCommandLine(args)) {
if (!Application.getPreferences().isAutoOpenLastDesignOnStartupEnabled()) {
BasicFrame.newAction();
} else {
String lastFile = MRUDesignFile.getInstance().getLastEditedDesignFile();
if (lastFile != null) {
if (!BasicFrame.open(new File(lastFile), null)) {
MRUDesignFile.getInstance().removeFile(lastFile);
BasicFrame.newAction();
}
else {
MRUDesignFile.getInstance().addFile(lastFile);
}
}
else {
BasicFrame.newAction();
}
}
BasicFrame.reopen();
}

// Check whether update info has been fetched or whether it needs more time
Expand Down

0 comments on commit 300e165

Please sign in to comment.