After being convinced ("schooled") that Swing apps on Mac do look native, I'm trying to make mine look as native as possible. Everything looks great, but when I hit command+Q or do it from the menu, my windowStateChanged(WindowEvent e) is not firing on my main JFrame (if I exit in any other way, it does fire). How can I respond to the real Apple quit?
You can implement com.apple.eawt.ApplicationListener and respond to the Quit event. An example may be found in the Mac OS X Reference Library example, OSXAdapter.
Addendum: See Java for Mac OS X 10.6 Update 3 and 10.5 Update 8 Release Notes for information on deprecation, the redesigned com.apple.eawt.Application class, and the location of API documentation for the Apple Java extensions. Control-click or right-click on the .jdk file to Show Package Contents. You can browse the classes of com.apple.eawt among the OpenJDK sources.
As shown in this complete example, you can specify the desired
QuitStrategy; a WindowListener will respond to ⌘Q:
Application.getApplication().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
As noted here, you can set the property from the command line
java -Dapple.eawt.quitStrategy=CLOSE_ALL_WINDOWS -cp build/classes gui.QuitStrategyTest
or early in the program, before posting any GUI events:
System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
EventQueue.invokeLater(new QuitStrategyTest()::display);
Console, after ⌘Q:
java.vendor: Oracle Corporation
java.version: 1.8.0_60
os.name: Mac OS X
os.version: 10.11
apple.eawt.quitStrategy: CLOSE_ALL_WINDOWS
java.awt.event.WindowEvent[WINDOW_CLOSING,opposite=null,oldState=0,newState=0] on frame0
Code:
package gui;
import java.awt.EventQueue;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JTextArea;
/**
* #see https://stackoverflow.com/a/7457102/230513
*/
public class QuitStrategyTest {
private void display() {
JFrame f = new JFrame("QuitStrategyTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.out.println(e);
}
});
f.add(new JTextArea(getInfo()));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private String getInfo() {
String[] props = {
"java.vendor",
"java.version",
"os.name",
"os.version",
"apple.eawt.quitStrategy"
};
StringBuilder sb = new StringBuilder();
for (String prop : props) {
sb.append(prop);
sb.append(": ");
sb.append(System.getProperty(prop));
sb.append(System.getProperty("line.separator"));
}
System.out.print(sb);
return sb.toString();
}
public static void main(String[] args) {
System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
EventQueue.invokeLater(new QuitStrategyTest()::display);
}
}
The top voted answer is excellent but just to fill in the "best way":
System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
This will trigger the standard window closing callback event which should work really nicely for portable code.
As a result of the discussion below it seems that its crucial to do this really early in the app. I wrote this early in the static initializer of the main class before any UI code was executed.
This is a pretty good question, and I must admit I don't have the answer. However, a couple years ago when I was working on a Java app and faced this problem, I solved it by registering a shutdown hook with the runtime that would do what I wanted the app to do before quitting. It's a heavy-handed solution but it worked. You can take a look at my code and see if it helps.
I was originally seeing a 'access restriction' violation when trying to access the com.apple.eawt.Application and com.apple.eawt.* subclasses.
(Note: I'm programming on a MAC, using Eclipse, with Java 1.6 using Swing)
So I needed to modify my java build path to allow access to the apple subclasses by adding "com/apple/eawt/**" access rule. After that this code below was able to compile and work for me:
//NOTE: This code only works for MAC OS. If you run this on Windows
//the application never starts (so you literally need to remove this block of code)
import com.apple.eawt.*;
import com.apple.eawt.QuitHandler;
Application a = Application.getApplication();
a.setQuitHandler(new QuitHandler() {
#Override
public void handleQuitRequestWith(com.apple.eawt.AppEvent.QuitEvent qe, com.apple.eawt.QuitResponse qr) {
// TODO Auto-generated method stub
int res = JOptionPane.showConfirmDialog(frame, "Are you sure you want to exit the program?", "Quit ?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (res == JOptionPane.YES_OPTION)
qr.performQuit();
else
qr.cancelQuit();
}
});
Have you tried setting up command-Q as an accelerator in your menu? Can you make your app respond to it?
I'm not positive, but I think this works in Linux and probably Windows with the equivalent Alt-F4. My app responds to the "killing" keystroke, I process some cleanup code and then I do a programmatic System.exit().
If you're "just" after graceful exit handling, you may also want to catch the WindowEvent WINDOW_CLOSING, where traditionally "are you sure?" stuff gets done.
Looking at the link to Java for Mac OS X 10.6 Update 3 and 10.5 Update 8 Release Notes I noticed that there is a section on Default Quit Action. This describes a system property to request that all windows are closed in response to the "Quit" menu item, which sounds like exactly what is needed? I have used this in my own application (using Info.plist to set the property on OS X only), and it seems to work as described. This would presumably only work on recent Java/OS X versions, but for those platforms seems like a neat solution, and doesn't require any code changes.
Related
Is there a way to check if a specific program is installed on Windows using Java?
I'm trying to develop a Java program that automatically creates zip archives by using the code line command from 7-Zip.
So, I would like to check in Java if on my windows OS '7-Zip' is already installed. No check for running apps or if OS is Windows or Linux. I want to get a bool (true/false) if '7-Zip' is installed on Windows.
The library Apache Commons has a class called SystemUtils - full documentation is available at https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/SystemUtils.html.
In this library you have the following static boolean properties at your disposal:
SystemUtils.IS_OS_LINUX
SystemUtils.IS_OS_WINDOWS
The unix-like solution would be to simply try to run the program with --version flag (on windows probably the /? or - like in the 7zip case - without any at all) and check whether it fails, or what the return code will be.
Something like:
public boolean is7zipInstalled() {
try {
Process process = Runtime.getRuntime().exec("7zip.exe");
int code = process.waitFor();
return code == 0;
} catch (Exception e) {
return false;
}
}
I assume that you're talking about Windows. As Java is intended to be a platform-independent language and the way how to determine it differs per platform, there's no standard Java API to check that. You can however do it with help of JNI calls on a DLL which crawls the Windows registry. You can then just check if the registry key associated with the software is present in the registry. There's a 3rd party Java API with which you can crawl the Windows registry: jRegistryKey.
Here's an SSCCE with help of jRegistryKey:
package com.stackoverflow.q2439984;
import java.io.File;
import java.util.Iterator;
import ca.beq.util.win32.registry.RegistryKey;
import ca.beq.util.win32.registry.RootKey;
public class Test {
public static void main(String... args) throws Exception {
RegistryKey.initialize(Test.class.getResource("jRegistryKey.dll").getFile());
RegistryKey key = new RegistryKey(RootKey.HKLM, "Software\\Mozilla");
for (Iterator<RegistryKey> subkeys = key.subkeys(); subkeys.hasNext();) {
RegistryKey subkey = subkeys.next();
System.out.println(subkey.getName()); // You need to check here if there's anything which matches "Mozilla FireFox".
}
}
}
If you however intend to have a platformindependent application, then you'll also have to take into account the Linux/UNIX/Mac/Solaris/etc. (in other words: anywhere where Java is able to run) ways to detect whether FF is installed. Else you'll have to distribute it as a Windows-only application and do a System#exit() along with a warning whenever System.getProperty("os.name") is not Windows.
Sorry, I don't know how to detect in other platforms whether FF is installed or not, so don't expect an answer from me for that ;)
I'm just now getting into GUI's in Java and when experimenting with JFrame I get the following error:
java[3126:71534] Can't open input server /Library/InputManagers/Inquisitor
Despite the error the program runs fine, but I'd like to know what this is about as I couldn't find much about Inquisitor anywhere.
Running Netbeans 8.0.2 and Java 8 Update 40 on OS X Yosemite (10.10.2). The java code being run is:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ikkuna extends JFrame implements ActionListener{
JTextField syöte;
JLabel vastaus;
JButton painike;
public void setTitle(String string){
super.setTitle(string);
}
public Ikkuna(){
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setTitle("Celsius / Fahrenheit -muunnos");
this.setSize(400, 200);
this.setResizable(false);
JPanel paneeli = (JPanel) getContentPane();
syöte = new JTextField(10);
vastaus = new JLabel("tuntematon");
painike = new JButton("Laske");
painike.addActionListener(this);
syöte.addActionListener(this);
paneeli.setLayout(new FlowLayout(FlowLayout.LEFT, 10,10));
paneeli.add(syöte);
paneeli.add(vastaus);
paneeli.add(painike);
setVisible(true);
}
public void actionPerformed(ActionEvent e){
vastaus.setText("" + ((Integer.parseInt(syöte.getText())*1.8+32)));
}
public static void main(String[] args){
Ikkuna i = new Ikkuna();
}
}
TLDR: Don't worry about it, it has nothing to do with your code, although you're probably better of removing Inquisitor extension as there's a good chance that it's not working properly.
InputManagers are intended to be Keyboard/Mouse extensions for applications. The idea is that the code is loaded into applications and can change their behavior in relation to Mouse/Keyboard input, but are generally used as a mechanism for extending applications. They became more restricted in 10.5, where they had to be in the system supported location only (/Library/InputManagers), and stopped being loaded entirely on 64bit mode.
Java 8 is a 64bit only application, so if you're seeing this error, then it's probably because the extension is not being loaded because it's a 64bit application and the system doesn't load extensions in this case.
The extension Inquisitor doesn't look to have been updated in a long time, and was used to extend the functionality of Safari, adding auto-complete to the search bar. The default search functionality of Safari now includes auto-complete, so I would consider it obsoleted.
If you want extended functionality for Safari, there are other plugins which extend the behaviour, such as Glims (I used to use it, but stopped because I felt it destabilized safari more than usual).
Opening the /Library/InputManagers folder (open a Finder window, hit Command-G to get a location field and type in the directory, then hit enter) will allow you to see what input managers are present on the system.
What is the simplest way to customize this panel considered that the application is written in Java 7 and my favorite environment is Mac Os?
Thanks
You need to use the com.apple.eawt classes. For example, this scratch program shows a Java dialog instead of the Mac one.
import javax.swing.*;
import com.apple.eawt.*;
import com.apple.eawt.AppEvent.*;
public class Foo
extends JPanel
implements AboutHandler
{
public static void main(String[] args)
throws Exception
{
Foo r = new Foo();
}
public Foo() {
Application.getApplication().setAboutHandler(this);
}
public void handleAbout(final AboutEvent e) {
JOptionPane.showMessageDialog(null, "hello, world");
}
}
Unfortunately all of this stuff is deprecated. Apple used to develop and support all of this but doesn’t anymore, and there are a lot of dead links around the internet.
I figured out the API from reading the Javadoc comments in the source code for these classes.
And running mdfind -name apple | grep -i jar turned up /usr/share/java/Stubs/AppleJavaExtensions.jar on my machine, which allowed the above program to compile and run on my machine. But I have no idea where that file came from or if it’ll be in future versions of Mac OS.
I'm writing an app that requires some widgets to use different look & feels. The rest of the app uses the system look & feel.
My approach to this was while initializing the app to swap the LF for the desired one, create the component and then reset the LF. This worked fine on Windows operating systems, but while testing it on Ubuntu it failed to set the LF for the component in a bizarre way.
On Ubuntu, UIManager.getSystemLookAndFeelClassName() reports that that the LF is a javax.swing.plaf.metal.MetalLookAndFeel. This is the first bit of trouble - I'd expect it to return com.sun.java.swing.plaf.gtk.GTKLookAndFeel. The strange part is that my approach works when the program is using the Metal LF, but fails when using the GTK LF, where it sets the entire frame and all components within it to the GTK LF.
The following SSCCE demonstrates the issue:
import javax.swing.*;
import java.awt.*;
import static javax.swing.UIManager.*;
public class LaFTest {
public static void main(String[] argv) throws ReflectiveOperationException, UnsupportedLookAndFeelException {
String[] keys = {
"java.specification.version",
"java.version",
"java.vm.version",
"java.specification.vendor",
};
for (String key : keys) {
System.out.printf("%-40s%s\n", key, System.getProperty(key));
}
System.out.println(UIManager.getSystemLookAndFeelClassName());
// Swap for "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"
setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFrame frame = new JFrame("LaF Test");
Container content = frame.getContentPane();
content.setLayout(new GridLayout(2, 0));
JButton button = new JButton("System");
content.add(button);
LookAndFeel previous = getLookAndFeel();
for (LookAndFeelInfo feel : getInstalledLookAndFeels()) {
if (feel.getName().equals("Nimbus")) {
setLookAndFeel(feel.getClassName());
}
}
JButton second = new JButton("Nimbus");
content.add(second);
setLookAndFeel(previous);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Note, however, that I am unsure regarding the reproducibility of this issue. As quick Google shows no similar issues, it may be something specific to my machine. Regardless, it has persisted when updating from Ubuntu 12 to 13, and 13 to 14. From this I assume it's an issue with Swing and not the underlying system.
The output of the above program is, on my machine,
java.specification.version 1.7
java.version 1.7.0_51
java.vm.version 24.51-b03
java.specification.vendor Oracle Corporation
javax.swing.plaf.metal.MetalLookAndFeel
Using the Metal feel, the resulting window is
Swapping for the GTK feel,
I've been playing around with this for a while now, with no results. Is there something I'm missing? Can anyone reproduce this, and if so is it a bug in the Java distribution or my code? Is there a better way to achieve my goal?
I would have loved to debug this in depth, but a nearing deadline forced me to install the Oracle Java package, which fixed the issue. A disappointing answer, but it did fix the issue permanently.
I am trying to grab filesystem events on OS / Kernel level on OS X.
There are 2 requirements i have to follow. The first one is to do this in java as the whole project im developing for is written in java. The second one is that i have to find out when a document is opened.
For Linux I used inotify-java, but I can't find a good equivalent on OS X. Also the JNA doesn't provide a helpful binding. Currently I'm avoiding catching events by frequently calling the lsof program. This, however, is a bad solution.
Thanks for the help.
You can use dtrace on OSX, but since it needs root privileges it's not something you'd want to put into a runtime of a system.
In any case, you won't be able to do this in pure Java (any Java API would be a wrapper around some lower level C introspection, and if you're doing it kernel-wide, would need to be done as root).
If you just want to track when your program is opening files (as opposed to other files on the same system) then you can install your own Security Manager and implement the checkRead() family of methods, which should give you an idea of when accesses are happening.
import java.io.*;
public class Demo {
public static void main(String args[]) throws Exception {
System.setSecurityManager(new Sniffer());
File f = new File("/tmp/file");
new FileInputStream(f);
}
}
class Sniffer extends SecurityManager {
public void checkRead(String name) {
System.out.println("Opening " + name);
}
}