001: package abbot.editor;
002:
003: import java.awt.Frame;
004: import java.awt.event.ActionEvent;
005: import java.lang.ref.WeakReference;
006: import java.lang.reflect.InvocationHandler;
007: import java.lang.reflect.Method;
008: import java.lang.reflect.Proxy;
009: import java.util.Map;
010: import java.util.WeakHashMap;
011: import javax.swing.Action;
012: import abbot.Log;
013:
014: /** Provide access to OSX application hooks. Compilable on all platforms. */
015: public class OSXAdapter implements InvocationHandler {
016:
017: private static Object APPLICATION;
018:
019: private static synchronized Object application() {
020: if (APPLICATION == null) {
021: String cname = "com.apple.eawt.Application";
022: try {
023: APPLICATION = Class.forName(cname).newInstance();
024: } catch (Exception e) {
025: throw new Error("Can't load class " + cname + ": " + e);
026: }
027: }
028: return APPLICATION;
029: }
030:
031: /** Values are auto-generated proxy OSX listener. */
032: private static Map adapters = new WeakHashMap();
033: private WeakReference source;
034: private Action about;
035: private Action prefs;
036: private Action quit;
037: private Object listener;
038:
039: public static boolean isMac() {
040: return System.getProperty("os.name").toLowerCase().startsWith(
041: "mac");
042: }
043:
044: private OSXAdapter(Frame context, Action quit, Action about,
045: Action prefs) {
046: this .source = new WeakReference(context);
047: this .quit = quit;
048: this .about = about;
049: this .prefs = prefs;
050: try {
051: Object app = application();
052: Class iface = Class
053: .forName("com.apple.eawt.ApplicationListener");
054: listener = Proxy.newProxyInstance(OSXAdapter.class
055: .getClassLoader(), new Class[] { iface }, this );
056: Method m = app.getClass().getDeclaredMethod(
057: "addApplicationListener", new Class[] { iface });
058: m.invoke(app, new Object[] { listener });
059: enablePrefs(prefs != null);
060: Object old = adapters.put(context, this );
061: if (old instanceof OSXAdapter) {
062: ((OSXAdapter) old).unregister();
063: }
064: } catch (Exception e) {
065: Log.warn(e);
066: }
067: }
068:
069: private void unregister() {
070: try {
071: Object app = application();
072: Class iface = Class
073: .forName("com.apple.eawt.ApplicationListener");
074: Method m = app.getClass().getDeclaredMethod(
075: "removeApplicationListener", new Class[] { iface });
076: m.invoke(app, new Object[] { listener });
077: } catch (Exception e) {
078: Log.warn(e);
079: }
080: }
081:
082: private static final Class[] BOOL_ARGS = new Class[] { boolean.class, };
083:
084: private void setHandled(Object event, boolean handled) {
085: try {
086: Method m = event.getClass().getDeclaredMethod("setHandled",
087: BOOL_ARGS);
088: m.invoke(event, new Object[] { new Boolean(handled) });
089: } catch (Exception e) {
090: Log.warn(e);
091: }
092: }
093:
094: /** Returns non-null only if the owner frame is active or if there
095: * are no active frames.
096: */
097: private Object getSource() {
098: Object src = source.get();
099: if (src == null) {
100: unregister();
101: } else {
102: Frame[] frames = Frame.getFrames();
103: for (int i = 0; i < frames.length; i++) {
104: if (frames[i].isActive()) {
105: if (src == frames[i]) {
106: return src;
107: }
108: }
109: }
110: }
111: // It wasn't for our frame, so don't process the event
112: return null;
113: }
114:
115: private ActionEvent createEvent(Object src, Action action) {
116: return new ActionEvent(src, ActionEvent.ACTION_PERFORMED,
117: (String) action.getValue(Action.NAME), System
118: .currentTimeMillis(), 0);
119: }
120:
121: public void handleAbout(Object e) {
122: setHandled(e, true);
123: if (about != null) {
124: Object src = getSource();
125: if (src != null) {
126: about.actionPerformed(createEvent(src, about));
127: }
128: }
129: }
130:
131: public void handlePreferences(Object e) {
132: setHandled(e, true);
133: if (prefs != null) {
134: Object src = getSource();
135: if (src != null) {
136: prefs.actionPerformed(createEvent(src, prefs));
137: }
138: }
139: }
140:
141: public void handleQuit(Object e) {
142: // You MUST setHandled(false) if you want to delay or cancel the quit.
143: // This is important for cross-platform development -- have a
144: // universal quit routine that chooses whether or not to quit, so the
145: // functionality is identical on all platforms. This example simply
146: // cancels the AppleEvent-based quit and defers to that universal
147: // method.
148: setHandled(e, false);
149: Object src = getSource();
150: if (src != null) {
151: quit.actionPerformed(createEvent(src, quit));
152: }
153: }
154:
155: /** User clicked on application in the Dock. */
156: public void handleReOpenApplication(Object e) {
157: }
158:
159: public void handleOpenApplication(Object e) {
160: }
161:
162: public void handleOpenFile(Object e) {
163: }
164:
165: public void handlePrintFile(Object e) {
166: }
167:
168: /** Register the given special frame actions with OSX. About and prefs
169: * actions may be null, but quit must be non-null.
170: */
171: public static void register(Frame owner, Action quit, Action about,
172: Action prefs) {
173: if (isMac()) {
174: if (owner == null)
175: throw new NullPointerException(
176: "Parent Frame may not be null");
177: if (quit == null)
178: throw new NullPointerException(
179: "Quit action may not be null");
180: new OSXAdapter(owner, quit, about, prefs);
181: }
182: }
183:
184: /** Unregister the given frame's actions. */
185: public static void unregister(Frame owner) {
186: if (isMac()) {
187: Object listener = adapters.get(owner);
188: if (listener != null) {
189: Object adapter = Proxy.getInvocationHandler(listener);
190: if (adapter instanceof OSXAdapter) {
191: ((OSXAdapter) adapter).unregister();
192: }
193: }
194: }
195: }
196:
197: /** Another static entry point for EAWT functionality. Sets the enabled
198: * stste of the "Preferences..." menu item in the application menu.
199: */
200: public static void enablePrefs(boolean enabled) {
201: if (isMac()) {
202: try {
203: Object app = application();
204: Method m = app.getClass().getDeclaredMethod(
205: "setEnabledPreferencesMenu", BOOL_ARGS);
206: m.invoke(app, new Object[] { new Boolean(enabled) });
207: } catch (Exception e) {
208: Log.warn(e);
209: }
210: }
211: }
212:
213: /** Handle all calls from the host OS. */
214: public Object invoke(Object proxy, Method method, Object[] args)
215: throws Throwable {
216: String name = method.getName();
217: if ("handleAbout".equals(name)) {
218: handleAbout(args[0]);
219: } else if ("handlePreferences".equals(name)) {
220: handlePreferences(args[0]);
221: } else if ("handleQuit".equals(name)) {
222: handleQuit(args[0]);
223: } else if ("handleReOpenApplication".equals(name)) {
224: handleReOpenApplication(args[0]);
225: } else if ("handleOpenApplication".equals(name)) {
226: handleOpenApplication(args[0]);
227: } else if ("handleOpenFile".equals(name)) {
228: handleOpenFile(args[0]);
229: } else if ("handlePrintFile".equals(name)) {
230: handlePrintFile(args[0]);
231: } else {
232: throw new Error("Unimplemented method " + method);
233: }
234: return null;
235: }
236: }
|