svn commit: r15685 - trunk/src/argouml-app/src/org/argouml: ui/cmd util/osdep
[email protected] 8 Sep 2008 16:02:11 -0000
Newsgroups
gmane.comp.lang.uml.argouml.cvs
Message-ID
<[email protected] >
Author: tfmorris
Date: 2008-09-08 09:02:10-0700
New Revision: 15685
Added:
trunk/src/argouml-app/src/org/argouml/util/osdep/OSXAdapter.java (contents, props changed)
Modified:
trunk/src/argouml-app/src/org/argouml/ui/cmd/GenericArgoMenuBar.java
trunk/src/argouml-app/src/org/argouml/util/osdep/OsUtil.java
Log:
RESOLVED - task 3926: Quit via cmd-Q or app menu "quit" choice doesn't prompt for save
http://argouml.tigris.org/issues/show_bug.cgi?id=3926
Modified: trunk/src/argouml-app/src/org/argouml/ui/cmd/GenericArgoMenuBar.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/ui/cmd/GenericArgoMenuBar.java?view=diff&rev=15685&p1=trunk/src/argouml-app/src/org/argouml/ui/cmd/GenericArgoMenuBar.java&p2=trunk/src/argouml-app/src/org/argouml/ui/cmd/GenericArgoMenuBar.java&r1=15684&r2=15685
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/ui/cmd/GenericArgoMenuBar.java (original)
+++ trunk/src/argouml-app/src/org/argouml/ui/cmd/GenericArgoMenuBar.java 2008-09-08 09:02:10-0700
@@ -39,6 +39,7 @@
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
+import org.apache.log4j.Logger;
import org.argouml.application.helpers.ResourceLoaderWrapper;
import org.argouml.cognitive.critics.ui.ActionOpenCritics;
import org.argouml.cognitive.ui.ActionAutoCritique;
@@ -77,7 +78,8 @@
import org.argouml.uml.ui.ActionSequenceDiagram;
import org.argouml.uml.ui.ActionStateDiagram;
import org.argouml.uml.ui.ActionUseCaseDiagram;
-import org.tigris.gef.base.AdjustPageBreaksAction;
+import org.argouml.util.osdep.OSXAdapter;
+import org.argouml.util.osdep.OsUtil;
import org.tigris.gef.base.AlignAction;
import org.tigris.gef.base.DistributeAction;
import org.tigris.gef.base.ReorderAction;
@@ -113,6 +115,9 @@
*/
public class GenericArgoMenuBar extends JMenuBar implements
TargetListener {
+
+ private static final Logger LOG =
+ Logger.getLogger(GenericArgoMenuBar.class);
private static List<JMenu> moduleMenus = new ArrayList<JMenu>();
@@ -202,6 +207,12 @@
private Action navigateTargetForwardAction;
private Action navigateTargetBackAction;
+
+ // References to actions that we need for Mac hack
+ private ActionSettings settingsAction;
+ private ActionAboutArgoUML aboutAction;
+ private ActionExit exitAction;
+ private ActionOpenProject openAction;
/**
* The constructor.
@@ -210,6 +221,7 @@
initActions();
initMenus();
initModulesUI();
+ registerForMacEvents();
}
private void initActions() {
@@ -293,7 +305,8 @@
setMnemonic(newItem, "New");
ShortcutMgr.assignAccelerator(newItem, ShortcutMgr.ACTION_NEW_PROJECT);
toolbarTools.add((new ActionNew()));
- JMenuItem openProjectItem = file.add(new ActionOpenProject());
+ openAction = new ActionOpenProject();
+ JMenuItem openProjectItem = file.add(openAction);
setMnemonic(openProjectItem, "Open");
ShortcutMgr.assignAccelerator(openProjectItem,
ShortcutMgr.ACTION_OPEN_PROJECT);
@@ -364,14 +377,17 @@
mruList = new LastRecentlyUsedMenuList(file);
// and exit menu entry starting with separator.
- file.addSeparator();
- JMenuItem exitItem = file.add(new ActionExit());
- setMnemonic(exitItem, "Exit");
- /* The "Close window" shortcut (ALT+F4) actually can't
- * be registered as a shortcut,
- * because it closes the configuration dialog! */
- exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,
- InputEvent.ALT_MASK));
+ exitAction = new ActionExit();
+ if (!OsUtil.isMacOSX()) {
+ file.addSeparator();
+ JMenuItem exitItem = file.add(exitAction);
+ setMnemonic(exitItem, "Exit");
+ /* The "Close window" shortcut (ALT+F4) actually can't
+ * be registered as a shortcut,
+ * because it closes the configuration dialog! */
+ exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,
+ InputEvent.ALT_MASK));
+ }
fileToolbar = (new ToolBarFactory(toolbarTools)).createToolBar();
fileToolbar.setName(Translator.localize("misc.toolbar.file"));
@@ -458,10 +474,13 @@
ShortcutMgr.assignAccelerator(edit.add(new ActionPerspectiveConfig()),
ShortcutMgr.ACTION_PERSPECTIVE_CONFIG);
- JMenuItem settingsItem = edit.add(new ActionSettings());
- setMnemonic(settingsItem, "Settings");
- ShortcutMgr
- .assignAccelerator(settingsItem, ShortcutMgr.ACTION_SETTINGS);
+ settingsAction = new ActionSettings();
+ if (!OsUtil.isMacOSX()) {
+ JMenuItem settingsItem = edit.add(settingsAction);
+ setMnemonic(settingsItem, "Settings");
+ ShortcutMgr.assignAccelerator(settingsItem,
+ ShortcutMgr.ACTION_SETTINGS);
+ }
}
@@ -904,12 +923,16 @@
setMnemonic(systemInfo, "System Information");
ShortcutMgr.assignAccelerator(systemInfo,
ShortcutMgr.ACTION_SYSTEM_INFORMATION);
- help.addSeparator();
- JMenuItem aboutArgoUML = help.add(new ActionAboutArgoUML());
- setMnemonic(aboutArgoUML, "About ArgoUML");
- ShortcutMgr.assignAccelerator(aboutArgoUML,
- ShortcutMgr.ACTION_ABOUT_ARGOUML);
-
+
+ aboutAction = new ActionAboutArgoUML();
+ if (!OsUtil.isMacOSX()) {
+ help.addSeparator();
+ JMenuItem aboutArgoUML = help.add(aboutAction);
+ setMnemonic(aboutArgoUML, "About ArgoUML");
+ ShortcutMgr.assignAccelerator(aboutArgoUML,
+ ShortcutMgr.ACTION_ABOUT_ARGOUML);
+ }
+
// setHelpMenu(help);
add(help);
}
@@ -1081,4 +1104,53 @@
public static void registerCreateDiagramAction(Action action) {
moduleCreateDiagramActions.add(action);
}
+
+ private void registerForMacEvents() {
+ if (OsUtil.isMacOSX()) {
+ try {
+ // Generate and register the OSXAdapter, passing the methods
+ // we wish to use as delegates for various
+ // com.apple.eawt.ApplicationListener methods
+ OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod(
+ "macQuit", (Class[]) null));
+ OSXAdapter.setAboutHandler(this, getClass().getDeclaredMethod(
+ "macAbout", (Class[]) null));
+ OSXAdapter.setPreferencesHandler(this, getClass()
+ .getDeclaredMethod("macPreferences", (Class[]) null));
+ OSXAdapter.setFileHandler(this, getClass().getDeclaredMethod(
+ "macOpenFile", new Class[] {String.class}));
+ } catch (Exception e) {
+ LOG.error("Error while loading the OSXAdapter:", e);
+ }
+ }
+ }
+
+ /**
+ * Internal use only. Do not use.
+ */
+ public void macQuit() {
+ exitAction.actionPerformed(null);
+ }
+
+ /**
+ * Internal use only. Do not use.
+ */
+ public void macAbout() {
+ aboutAction.actionPerformed(null);
+ }
+
+ /**
+ * Internal use only. Do not use.
+ */
+ public void macPreferences() {
+ settingsAction.actionPerformed(null);
+ }
+
+ /**
+ * Internal use only. Do not use.
+ * @param filename name of file to be opened
+ */
+ public void macOpenFile(String filename) {
+ openAction.doCommand(filename);
+ }
}
Added: trunk/src/argouml-app/src/org/argouml/util/osdep/OSXAdapter.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/util/osdep/OSXAdapter.java?view=auto&rev=15685
==============================================================================
--- (empty file)
+++ trunk/src/argouml-app/src/org/argouml/util/osdep/OSXAdapter.java 2008-09-08 09:02:10-0700
@@ -0,0 +1,292 @@
+/* CHECKSTYLE:OFF - imported code
+
+File: OSXAdapter.java
+
+Abstract: Hooks existing preferences/about/quit functionality from an
+ existing Java app into handlers for the Mac OS X application menu.
+ Uses a Proxy object to dynamically implement the
+ com.apple.eawt.ApplicationListener interface and register it with the
+ com.apple.eawt.Application object. This allows the complete project
+ to be both built and run on any platform without any stubs or
+ placeholders. Useful for developers looking to implement Mac OS X
+ features while supporting multiple platforms with minimal impact.
+
+Version: 2.0
+
+Disclaimer: IMPORTANT: This Apple software is supplied to you by
+Apple Inc. ("Apple") in consideration of your agreement to the
+following terms, and your use, installation, modification or
+redistribution of this Apple software constitutes acceptance of these
+terms. If you do not agree with these terms, please do not use,
+install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, Apple grants you a personal, non-exclusive
+license, under Apple's copyrights in this original Apple software (the
+"Apple Software"), to use, reproduce, modify and redistribute the Apple
+Software, with or without modifications, in source and/or binary forms;
+provided that if you redistribute the Apple Software in its entirety and
+without modifications, you must retain this notice and the following
+text and disclaimers in all such redistributions of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc.
+may be used to endorse or promote products derived from the Apple
+Software without specific prior written permission from Apple. Except
+as expressly stated in this notice, no other rights or licenses, express
+or implied, are granted by Apple herein, including but not limited to
+any patent rights that may be infringed by your derivative works or by
+other works in which the Apple Software may be incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+Copyright © 2003-2007 Apple, Inc., All Rights Reserved
+
+*/
+
+package org.argouml.util.osdep;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Hooks existing preferences/about/quit functionality from an existing Java app
+ * into handlers for the Mac OS X application menu. Uses a Proxy object to
+ * dynamically implement the com.apple.eawt.ApplicationListener interface and
+ * register it with the com.apple.eawt.Application object. This allows the
+ * complete project to be both built and run on any platform without any stubs
+ * or placeholders. Useful for developers looking to implement Mac OS X features
+ * while supporting multiple platforms with minimal impact.<p>
+ *
+ * Modified by Tom Morris for ArgoUML as follows:
+ * - changed package
+ * - added Javadoc
+ * - disabled checkstyle warnings
+ * - switched error logging to use log4j instead of println
+ *
+ * @author Apple, Inc.
+ * @version 2.0
+ */
+public class OSXAdapter implements InvocationHandler {
+
+ private static final Logger LOG = Logger.getLogger(OSXAdapter.class);
+
+ protected Object targetObject;
+ protected Method targetMethod;
+ protected String proxySignature;
+
+ static Object macOSXApplication;
+
+ /**
+ * Pass this method an Object and Method equipped to perform application
+ * shutdown logic The method passed should return a boolean stating whether
+ * or not the quit should occur
+ *
+ * @param target object containing the method
+ * @param quitHandler method to handle quit
+ */
+ public static void setQuitHandler(Object target, Method quitHandler) {
+ setHandler(new OSXAdapter("handleQuit", target, quitHandler));
+ }
+
+ /**
+ * Set handler for About action. It will be called when the About menu item
+ * is selected from the application menu.
+ *
+ * @param target object containing the method
+ * @param aboutHandler method to invoke to handle About menu item
+ */
+ public static void setAboutHandler(Object target, Method aboutHandler) {
+ boolean enableAboutMenu = (target != null && aboutHandler != null);
+ if (enableAboutMenu) {
+ setHandler(new OSXAdapter("handleAbout", target, aboutHandler));
+ }
+ // If we're setting a handler, enable the About menu item by calling
+ // com.apple.eawt.Application reflectively
+ try {
+ Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", new Class[] { boolean.class });
+ enableAboutMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enableAboutMenu) });
+ } catch (Exception ex) {
+ LOG.error("OSXAdapter could not access the About Menu", ex);
+ }
+ }
+
+ /**
+ * Set handler for Mac preferences item. It will be called when the
+ * Preferences menu item is selected from the application menu.
+ *
+ * @param target object containing the method
+ * @param prefsHandler method to handle preferences action
+ */
+ public static void setPreferencesHandler(Object target, Method prefsHandler) {
+ boolean enablePrefsMenu = (target != null && prefsHandler != null);
+ if (enablePrefsMenu) {
+ setHandler(new OSXAdapter("handlePreferences", target, prefsHandler));
+ }
+ // If we're setting a handler, enable the Preferences menu item by calling
+ // com.apple.eawt.Application reflectively
+ try {
+ Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class });
+ enablePrefsMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enablePrefsMenu) });
+ } catch (Exception ex) {
+ LOG.error("OSXAdapter could not access the About Menu");
+ ex.printStackTrace();
+ }
+ }
+
+
+ /**
+ * Pass this method an Object and a Method equipped to handle document
+ * events from the Finder Documents are registered with the Finder via the
+ * CFBundleDocumentTypes dictionary in the application bundle's Info.plist
+ *
+ * @param target object containing method
+ * @param fileHandler method to invoke to open a new file
+ */
+ public static void setFileHandler(Object target, Method fileHandler) {
+ setHandler(new OSXAdapter("handleOpenFile", target, fileHandler) {
+ // Override OSXAdapter.callTarget to send information on the
+ // file to be opened
+ public boolean callTarget(Object appleEvent) {
+ if (appleEvent != null) {
+ try {
+ Method getFilenameMethod = appleEvent.getClass().getDeclaredMethod("getFilename", (Class[])null);
+ String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[])null);
+ this.targetMethod.invoke(this.targetObject, new Object[] { filename });
+ } catch (Exception ex) {
+
+ }
+ }
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Creates a Proxy object from the passed OSXAdapter and adds it as an
+ * ApplicationListener
+ *
+ * @param adapter an instance of this class
+ */
+ public static void setHandler(OSXAdapter adapter) {
+ try {
+ Class applicationClass = Class.forName("com.apple.eawt.Application");
+ if (macOSXApplication == null) {
+ macOSXApplication = applicationClass.getConstructor((Class[])null).newInstance((Object[])null);
+ }
+ Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener");
+ Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", new Class[] { applicationListenerClass });
+ // Create a proxy object around this handler that can be reflectively added as an Apple ApplicationListener
+ Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(), new Class[] { applicationListenerClass }, adapter);
+ addListenerMethod.invoke(macOSXApplication, new Object[] { osxAdapterProxy });
+ } catch (ClassNotFoundException cnfe) {
+ LOG.error("This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" + cnfe + ")");
+ } catch (Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking eawt.Application methods
+ LOG.error("Mac OS X Adapter could not talk to EAWT:");
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Each OSXAdapter has the name of the EAWT method it intends to listen for
+ * (handleAbout, for example), the Object that will ultimately perform the
+ * task, and the Method to be called on that Object
+ *
+ * @param proxySignature
+ * @param target
+ * @param handler
+ */
+ protected OSXAdapter(String proxySignature, Object target, Method handler) {
+ this.proxySignature = proxySignature;
+ this.targetObject = target;
+ this.targetMethod = handler;
+ }
+
+
+ /**
+ * Override this method to perform any operations on the event that comes
+ * with the various callbacks. See setFileHandler above for an example
+ *
+ * @param appleEvent the AppleEvent
+ * @return boolean result of method invocation
+ * @throws InvocationTargetException
+ * @throws IllegalAccessException
+ */
+ public boolean callTarget(Object appleEvent)
+ throws InvocationTargetException, IllegalAccessException {
+ Object result = targetMethod.invoke(targetObject, (Object[])null);
+ if (result == null) {
+ return true;
+ }
+ return Boolean.valueOf(result.toString()).booleanValue();
+ }
+
+ /**
+ * InvocationHandler implementation This is the entry point for our proxy
+ * object; it is called every time an ApplicationListener method is invoked
+ *
+ * @param proxy
+ * @param method
+ * @param args
+ * @return null
+ * @throws Throwable
+ * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
+ * java.lang.reflect.Method, java.lang.Object[])
+ */
+ public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
+ if (isCorrectMethod(method, args)) {
+ boolean handled = callTarget(args[0]);
+ setApplicationEventHandled(args[0], handled);
+ }
+ // All of the ApplicationListener methods are void; return null regardless of what happens
+ return null;
+ }
+
+ /**
+ * Compare the method that was called to the intended method when the
+ * OSXAdapter instance was created (e.g. handleAbout, handleQuit,
+ * handleOpenFile, etc.)
+ *
+ * @param method method to be invoked
+ * @param args argumnets
+ * @return a boolean result of the method invocation
+ */
+ protected boolean isCorrectMethod(Method method, Object[] args) {
+ return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1);
+ }
+
+ /**
+ * It is important to mark the ApplicationEvent as handled and cancel the
+ * default behavior This method checks for a boolean result from the proxy
+ * method and sets the event accordingly
+ *
+ * @param event
+ * @param handled
+ */
+ protected void setApplicationEventHandled(Object event, boolean handled) {
+ if (event != null) {
+ try {
+ Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class });
+ // If the target method returns a boolean, use that as a hint
+ setHandledMethod.invoke(event, new Object[] { Boolean.valueOf(handled) });
+ } catch (Exception ex) {
+ LOG.error("OSXAdapter was unable to handle an ApplicationEvent: " + event, ex);
+ }
+ }
+ }
+}
\ No newline at end of file
Modified: trunk/src/argouml-app/src/org/argouml/util/osdep/OsUtil.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/util/osdep/OsUtil.java?view=diff&rev=15685&p1=trunk/src/argouml-app/src/org/argouml/util/osdep/OsUtil.java&p2=trunk/src/argouml-app/src/org/argouml/util/osdep/OsUtil.java&r1=15684&r2=15685
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/util/osdep/OsUtil.java (original)
+++ trunk/src/argouml-app/src/org/argouml/util/osdep/OsUtil.java 2008-09-08 09:02:10-0700
@@ -58,6 +58,16 @@
}
/**
+ * Check whether this is a Mac running OS X.
+ *
+ * @return true if this is a Mac running OS X
+ */
+ public static boolean isMacOSX() {
+ return (System.getProperty("os.name").toLowerCase()
+ .startsWith("mac os x"));
+ }
+
+ /**
* Check whether we deal with a Sun Java.
*
* @return true if this is a Sun Java