aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaphael Moll <ralf@android.com>2011-04-13 23:51:39 -0700
committerRaphael Moll <ralf@android.com>2011-04-15 11:52:59 -0700
commit51cb7801f4d3abf5d98702ebf8c6b16d44097365 (patch)
tree8906249527c75a869324368533022103456c55e6
parentcc422bcaf6dd9328d949600739dcc4db8deb2b61 (diff)
downloadsdk-51cb7801f4d3abf5d98702ebf8c6b16d44097365.zip
sdk-51cb7801f4d3abf5d98702ebf8c6b16d44097365.tar.gz
sdk-51cb7801f4d3abf5d98702ebf8c6b16d44097365.tar.bz2
swtmenubar library for the SDK.
This little EPL library provides a way for the SDK apps to integrate with the Mac menu bar -- that is correctly hook into the About and Preferences menu items. On other platforms (Windows, Linux), corresponding items are added to the provided menu. The library provides both a Carbon and a Cocoa implementation. However the Cocoa implemented is currently commented out since we only link with a Carbon-aware version of SWT.jar. Added a README that explain how to use this. Change-Id: I4b8457f0269946df056b5dd597c7263da1c4e784
-rw-r--r--build/tools.atree1
-rw-r--r--swtmenubar/.classpath7
-rw-r--r--swtmenubar/.gitignore1
-rw-r--r--swtmenubar/.project17
-rw-r--r--swtmenubar/Android.mk33
-rw-r--r--swtmenubar/MODULE_LICENSE_EPL0
-rw-r--r--swtmenubar/NOTICE224
-rwxr-xr-xswtmenubar/README65
-rwxr-xr-xswtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java131
-rwxr-xr-xswtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java.txt334
-rw-r--r--swtmenubar/src/com/android/menubar/IMenuBarCallback.java42
-rw-r--r--swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java46
-rw-r--r--swtmenubar/src/com/android/menubar/MenuBarEnhancer.java137
13 files changed, 1038 insertions, 0 deletions
diff --git a/build/tools.atree b/build/tools.atree
index 36913a8..89481fc 100644
--- a/build/tools.atree
+++ b/build/tools.atree
@@ -77,6 +77,7 @@ sdk/files/android.el tools/lib/android.el
# Java Libraries for the tools
framework/androidprefs.jar tools/lib/androidprefs.jar
framework/common.jar tools/lib/common.jar
+framework/swtmenubar.jar tools/lib/swtmenubar.jar
sdk/apkbuilder/etc/apkbuilder tools/apkbuilder
framework/sdkstats.jar tools/lib/sdkstats.jar
framework/archquery.jar tools/lib/archquery.jar
diff --git a/swtmenubar/.classpath b/swtmenubar/.classpath
new file mode 100644
index 0000000..827ba88
--- /dev/null
+++ b/swtmenubar/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry excluding="org/eclipse/ui/internal/cocoa/CocoaUIEnhancer.java" kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/swtmenubar/.gitignore b/swtmenubar/.gitignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/swtmenubar/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/swtmenubar/.project b/swtmenubar/.project
new file mode 100644
index 0000000..484282a
--- /dev/null
+++ b/swtmenubar/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>SwtMenuBar</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/swtmenubar/Android.mk b/swtmenubar/Android.mk
new file mode 100644
index 0000000..333684f
--- /dev/null
+++ b/swtmenubar/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Eclipse Public License, Version 1.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.eclipse.org/org/documents/epl-v10.php
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := src
+
+ifeq ($(HOST_OS),darwin)
+LOCAL_SRC_FILES += $(call all-java-files-under, src-$(HOST_OS))
+LOCAL_JAVA_RESOURCE_DIRS += src-$(HOST_OS)
+endif
+
+LOCAL_MODULE := swtmenubar
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := swt
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/swtmenubar/MODULE_LICENSE_EPL b/swtmenubar/MODULE_LICENSE_EPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/swtmenubar/MODULE_LICENSE_EPL
diff --git a/swtmenubar/NOTICE b/swtmenubar/NOTICE
new file mode 100644
index 0000000..49c101d
--- /dev/null
+++ b/swtmenubar/NOTICE
@@ -0,0 +1,224 @@
+*Eclipse Public License - v 1.0*
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
+THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+*1. DEFINITIONS*
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and
+documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and
+are distributed by that particular Contributor. A Contribution
+'originates' from a Contributor if it was added to the Program by such
+Contributor itself or anyone acting on such Contributor's behalf.
+Contributions do not include additions to the Program which: (i) are
+separate modules of software distributed in conjunction with the Program
+under their own license agreement, and (ii) are not derivative works of
+the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+*2. GRANT OF RIGHTS*
+
+a) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free copyright
+license to reproduce, prepare derivative works of, publicly display,
+publicly perform, distribute and sublicense the Contribution of such
+Contributor, if any, and such derivative works, in source code and
+object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free patent license
+under Licensed Patents to make, use, sell, offer to sell, import and
+otherwise transfer the Contribution of such Contributor, if any, in
+source code and object code form. This patent license shall apply to the
+combination of the Contribution and the Program if, at the time the
+Contribution is added by the Contributor, such addition of the
+Contribution causes such combination to be covered by the Licensed
+Patents. The patent license shall not apply to any other combinations
+which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the
+licenses to its Contributions set forth herein, no assurances are
+provided by any Contributor that the Program does not infringe the
+patent or other intellectual property rights of any other entity. Each
+Contributor disclaims any liability to Recipient for claims brought by
+any other entity based on infringement of intellectual property rights
+or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to
+secure any other intellectual property rights needed, if any. For
+example, if a third party patent license is required to allow Recipient
+to distribute the Program, it is Recipient's responsibility to acquire
+that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient
+copyright rights in its Contribution, if any, to grant the copyright
+license set forth in this Agreement.
+
+*3. REQUIREMENTS*
+
+A Contributor may choose to distribute the Program in object code form
+under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties
+and conditions, express and implied, including warranties or conditions
+of title and non-infringement, and implied warranties or conditions of
+merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for
+damages, including direct, indirect, special, incidental and
+consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are
+offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such
+Contributor, and informs licensees how to obtain it in a reasonable
+manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+*4. COMMERCIAL DISTRIBUTION*
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program, the
+Contributor who includes the Program in a commercial product offering
+should do so in a manner which does not create potential liability for
+other Contributors. Therefore, if a Contributor includes the Program in
+a commercial product offering, such Contributor ("Commercial
+Contributor") hereby agrees to defend and indemnify every other
+Contributor ("Indemnified Contributor") against any losses, damages and
+costs (collectively "Losses") arising from claims, lawsuits and other
+legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the
+Program in a commercial product offering. The obligations in this
+section do not apply to any claims or Losses relating to any actual or
+alleged intellectual property infringement. In order to qualify, an
+Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial
+Contributor to control, and cooperate with the Commercial Contributor
+in, the defense and any related settlement negotiations. The Indemnified
+Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.
+
+*5. NO WARRANTY*
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED
+ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
+OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR
+A PARTICULAR PURPOSE. Each Recipient is solely responsible for
+determining the appropriateness of using and distributing the Program
+and assumes all risks associated with its exercise of rights under this
+Agreement , including but not limited to the risks and costs of program
+errors, compliance with applicable laws, damage to or loss of data,
+programs or equipment, and unavailability or interruption of operations.
+
+*6. DISCLAIMER OF LIABILITY*
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. GENERAL*
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including
+a cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails
+to comply with any of the material terms or conditions of this Agreement
+and does not cure such failure in a reasonable period of time after
+becoming aware of such noncompliance. If all Recipient's rights under
+this Agreement terminate, Recipient agrees to cease use and distribution
+of the Program as soon as reasonably practicable. However, Recipient's
+obligations under this Agreement and any licenses granted by Recipient
+relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and may
+only be modified in the following manner. The Agreement Steward reserves
+the right to publish new versions (including revisions) of this
+Agreement from time to time. No one other than the Agreement Steward has
+the right to modify this Agreement. The Eclipse Foundation is the
+initial Agreement Steward. The Eclipse Foundation may assign the
+responsibility to serve as the Agreement Steward to a suitable separate
+entity. Each new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+distributed subject to the version of the Agreement under which it was
+received. In addition, after a new version of the Agreement is
+published, Contributor may elect to distribute the Program (including
+its Contributions) under the new version. Except as expressly stated in
+Sections 2(a) and 2(b) above, Recipient receives no rights or licenses
+to the intellectual property of any Contributor under this Agreement,
+whether expressly, by implication, estoppel or otherwise. All rights in
+the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to
+this Agreement will bring a legal action under this Agreement more than
+one year after the cause of action arose. Each party waives its rights
+to a jury trial in any resulting litigation.
+
+
+
diff --git a/swtmenubar/README b/swtmenubar/README
new file mode 100755
index 0000000..cc9fa61
--- /dev/null
+++ b/swtmenubar/README
@@ -0,0 +1,65 @@
+Using the Eclipse project SwtMenuBar
+------------------------------------
+
+This project provides a platform-specific way to hook into
+the default OS menu bar.
+
+On MacOS, it allows an SWT app to have an About menu item
+and to hook into the default Preferences menu item.
+
+On Windows and Linux, an SWT Menu should be provided (typically
+named "Tools") into which the About and Options menu items
+will be added.
+
+
+Consequently the implementation contains platform-specific source
+folders for the Java files that rely on a platform-specific version
+of SWT.jar.
+
+Right now we have the following source folders:
+- src/ - Generic implementation for all platforms.
+- src-darwin/ - Implementation for MacOS Carbon.
+
+*Only* the default "src/" folder is declared in the project .classpath
+so that the project can be opened in Eclipse on any platform and still
+work. However that means that on MacOS the custom src-darwin folder is
+not used by default.
+
+
+
+1- To build the library:
+
+Do not use Eclipse to build the library. Instead use the makefile:
+
+$ cd $TOP_OF_ANDROID_TREE
+$ . build/envsetup.sh && lunch sdk-eng
+$ make swtmenubar
+
+This will create a Jar in <Android tree>/out/host/<platform>/framework/
+that can then be included in the target application.
+
+
+2- To use the library in a target application:
+
+Build the swtmenubar library as explained in step 1.
+
+In the target application, define a classpath variable in Eclipse:
+- Open Preferences > Java > Build Path > Classpath Variables
+- Create a new classpath variable named ANDROID_OUT_FRAMEWORK
+- Set its folder value to <Android tree>/out/host/<platform>/framework
+
+Then add a variable to the Build Path of the target project:
+- Open Project > Properties > Java Build Path
+- Select the "Libraries" tab
+- Use "Add Variable"
+- Select ANDROID_OUT_FRAMEWORK
+- Select "Extend..."
+- Select swtmenubar.jar (which you previously built at step 1)
+
+
+Remember that if you then edit the SwtMenuBar project in Eclipse
+you will need to rebuild it using the command-line before the
+changes are propagated in the target applications that uses it.
+
+--
+EOF
diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java
new file mode 100755
index 0000000..328597a
--- /dev/null
+++ b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.menubar.internal;
+
+import com.android.menubar.IMenuBarCallback;
+import com.android.menubar.IMenuBarEnhancer;
+
+import org.eclipse.swt.internal.Callback;
+import org.eclipse.swt.internal.carbon.HICommand;
+import org.eclipse.swt.internal.carbon.OS;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+
+
+/**
+ * Implementation of IMenuBarEnhancer for MacOS Carbon SWT.
+ */
+public final class MenuBarEnhancerCarbon implements IMenuBarEnhancer {
+
+ private static final int kHICommandPreferences = ('p'<<24) + ('r'<<16) + ('e'<<8) + 'f';
+ private static final int kHICommandAbout = ('a'<<24) + ('b'<<16) + ('o'<<8) + 'u';
+ private static final int kHICommandServices = ('s'<<24) + ('e'<<16) + ('r'<<8) + 'v';
+
+ public MenuBarEnhancerCarbon() {
+ }
+
+ public void setupMenu(
+ String appName,
+ Menu swtMenu,
+ final IMenuBarCallback callbacks) {
+ final Display display = swtMenu.getDisplay();
+
+ // Callback target
+ Object target = new Object() {
+ @SuppressWarnings("unused")
+ int commandProc(int nextHandler, int theEvent, int userData) {
+ if (OS.GetEventKind(theEvent) == OS.kEventProcessCommand) {
+ HICommand command = new HICommand();
+ OS.GetEventParameter(
+ theEvent,
+ OS.kEventParamDirectObject,
+ OS.typeHICommand,
+ null,
+ HICommand.sizeof,
+ null,
+ command);
+ switch (command.commandID) {
+ case kHICommandPreferences:
+ callbacks.onPreferencesMenuSelected();
+ return OS.eventNotHandledErr; // TODO wrong
+ case kHICommandAbout:
+ callbacks.onAboutMenuSelected();
+ return OS.eventNotHandledErr;// TODO wrong
+ default:
+ break;
+ }
+ }
+ return OS.eventNotHandledErr;
+ }
+ };
+
+ final Callback commandCallback= new Callback(target, "commandProc", 3); //$NON-NLS-1$
+ int commandProc = commandCallback.getAddress();
+ if (commandProc == 0) {
+ commandCallback.dispose();
+ log(callbacks, "%1$s: commandProc hook failed.", getClass().getSimpleName()); //$NON-NLS-1$
+ return; // give up
+ }
+
+ // Install event handler for commands
+ int[] mask = new int[] {
+ OS.kEventClassCommand, OS.kEventProcessCommand
+ };
+ OS.InstallEventHandler(
+ OS.GetApplicationEventTarget(), commandProc, mask.length / 2, mask, 0, null);
+
+ // create About Eclipse menu command
+ int[] outMenu = new int[1];
+ short[] outIndex = new short[1];
+ if (OS.GetIndMenuItemWithCommandID(
+ 0, kHICommandPreferences, 1, outMenu, outIndex) == OS.noErr && outMenu[0] != 0) {
+ int menu = outMenu[0];
+
+ // add About menu item (which isn't present by default)
+ String about = "About " + appName;
+ int l = about.length();
+ char buffer[] = new char[l];
+ about.getChars(0, l, buffer, 0);
+ int str = OS.CFStringCreateWithCharacters(OS.kCFAllocatorDefault, buffer, l);
+ OS.InsertMenuItemTextWithCFString(menu, str, (short) 0, 0, kHICommandAbout);
+ OS.CFRelease(str);
+
+ // add separator between About & Preferences
+ OS.InsertMenuItemTextWithCFString(menu, 0, (short) 1, OS.kMenuItemAttrSeparator, 0);
+
+ // enable pref menu
+ OS.EnableMenuCommand(menu, kHICommandPreferences);
+
+ // disable services menu
+ OS.DisableMenuCommand(menu, kHICommandServices);
+ }
+
+ // schedule disposal of callback object
+ display.disposeExec(
+ new Runnable() {
+ public void run() {
+ commandCallback.dispose();
+ }
+ }
+ );
+ }
+
+ private void log(IMenuBarCallback callbacks, String format, Object... args) {
+ callbacks.printError(format , args);
+ }
+
+}
diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java.txt b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java.txt
new file mode 100755
index 0000000..591f253
--- /dev/null
+++ b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java.txt
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.menubar.internal;
+
+import com.android.menubar.IMenuBarCallback;
+import com.android.menubar.IMenuBarEnhancer;
+import com.android.menubar.MenuBarEnhancer;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.internal.C;
+import org.eclipse.swt.internal.Callback;
+import org.eclipse.swt.internal.cocoa.NSApplication;
+import org.eclipse.swt.internal.cocoa.NSMenu;
+import org.eclipse.swt.internal.cocoa.NSMenuItem;
+import org.eclipse.swt.internal.cocoa.NSObject;
+import org.eclipse.swt.internal.cocoa.OS;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ * Implementation of IMenuBarEnhancer for MacOS Cocoa SWT
+ * <p/>
+ * Note: this is currently deactivated since we are not currently
+ * shipping a version of SWT.jar that has cocoa support (only carbon).
+ */
+public final class MenuBarEnhancerCocoa implements IMenuBarEnhancer {
+
+ private static final int kAboutMenuItem = 0;
+ private static final int kPreferencesMenuItem = 2;
+ private static final int kServicesMenuItem = 4;
+ private static final int kHideApplicationMenuItem = 6;
+ private static final int kQuitMenuItem = 10;
+
+ private EnhancerDelegate mDelegate;
+ private long mDelegateJniRef;
+ private IMenuBarCallback mCallbacks;
+
+ public static class EnhancerDelegate extends NSObject {
+ public EnhancerDelegate() {
+ super();
+ }
+ public EnhancerDelegate(int id) {
+ super(id);
+ }
+
+ }
+ static long sel_preferencesMenuItemSelected_;
+ static long sel_aboutMenuItemSelected_;
+
+ /* This callback is not freed */
+ static Callback proc3Args;
+ static final byte[] SWT_OBJECT = { 'S', 'W', 'T', '_', 'O', 'B', 'J', 'E', 'C', 'T', '\0' };
+
+ public MenuBarEnhancerCocoa() {
+ }
+
+ public void setupMenu(
+ String appName,
+ Menu swtMenu,
+ IMenuBarCallback callbacks) {
+ mCallbacks = callbacks;
+ final Display display = swtMenu.getDisplay();
+
+ init1();
+
+ try {
+ mDelegate = new EnhancerDelegate();
+ mDelegate.alloc().init();
+ //call OS.NewGlobalRef
+ Method method = OS.class.getMethod("NewGlobalRef", new Class[] { Object.class });
+ Object object = method.invoke(OS.class, new Object[] { this });
+ mDelegateJniRef = convertToLong(object);
+ } catch (Exception e) {
+ // theoretically, one of SecurityException, Illegal*Exception,
+ // InvocationTargetException, NoSuch*Exception
+ // not expected to happen at all.
+ log(e);
+ }
+
+ if (mDelegateJniRef == 0) {
+ SWT.error(SWT.ERROR_NO_HANDLES);
+ }
+
+ try {
+ Field idField = EnhancerDelegate.class.getField("id");
+ Object idValue = idField.get(mDelegate);
+ invokeMethod(OS.class, "object_setInstanceVariable",
+ new Object[] { idValue, SWT_OBJECT, wrapPointer(mDelegateJniRef) });
+
+ hookApplicationMenu(appName);
+
+ // schedule disposal of callback object
+ display.disposeExec(new Runnable() {
+ public void run() {
+ if (mDelegateJniRef != 0) {
+ try {
+ invokeMethod(OS.class, "DeleteGlobalRef", new Object[] { wrapPointer(mDelegateJniRef) });
+ } catch (Exception e) {
+ // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception
+ // not expected to happen at all.
+ log(e);
+ }
+ }
+ mDelegateJniRef = 0;
+
+ if (mDelegate != null) {
+ mDelegate.release();
+ mDelegate = null;
+ }
+ }
+ });
+ } catch (Exception e) {
+ // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception
+ // not expected to happen at all.
+ log(e);
+ }
+ }
+
+ private long registerName(String name) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ Class clazz = OS.class;
+ Object object = invokeMethod(clazz, "sel_registerName", new Object[] {name});
+ return convertToLong(object);
+ }
+
+ private void init1() {
+ try {
+ if (sel_aboutMenuItemSelected_ == 0) {
+ sel_preferencesMenuItemSelected_ = registerName("preferencesMenuItemSelected:"); //$NON-NLS-1$
+ sel_aboutMenuItemSelected_ = registerName("aboutMenuItemSelected:"); //$NON-NLS-1$
+ init2();
+ }
+ } catch (Exception e) {
+ // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception
+ // not expected to happen at all.
+ log(e);
+ }
+ }
+
+ private void init2() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
+ // TODO: These should either move out of Display or be accessible to this class.
+ byte[] types = {'*','\0'};
+ int size = C.PTR_SIZEOF, align = C.PTR_SIZEOF == 4 ? 2 : 3;
+
+ Class clazz = this.getClass();
+
+ proc3Args = new Callback(clazz, "actionProc", 3); //$NON-NLS-1$
+ //call getAddress
+ Method getAddress = Callback.class.getMethod("getAddress", new Class[0]); //$NON-NLS-1$
+ Object object = getAddress.invoke(proc3Args, null);
+ long proc3 = convertToLong(object);
+ if (proc3 == 0) {
+ SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
+ }
+
+ //call objc_allocateClassPair
+ Field field = OS.class.getField("class_NSObject"); //$NON-NLS-1$
+ Object fieldObj = field.get(OS.class);
+ object = invokeMethod(OS.class, "objc_allocateClassPair", //$NON-NLS-1$
+ new Object[] { fieldObj, "SWTCocoaEnhancerDelegate", wrapPointer(0) }); //$NON-NLS-1$
+ long cls = convertToLong(object);
+
+ invokeMethod(OS.class, "class_addIvar", new Object[] { //$NON-NLS-1$
+ wrapPointer(cls), SWT_OBJECT, wrapPointer(size),
+ new Byte((byte) align), types });
+
+ // Add the action callback
+ invokeMethod(OS.class, "class_addMethod", new Object[] { //$NON-NLS-1$
+ wrapPointer(cls),
+ wrapPointer(sel_preferencesMenuItemSelected_),
+ wrapPointer(proc3), "@:@" }); //$NON-NLS-1$
+ invokeMethod(OS.class, "class_addMethod", new Object[] { //$NON-NLS-1$
+ wrapPointer(cls),
+ wrapPointer(sel_aboutMenuItemSelected_),
+ wrapPointer(proc3), "@:@" }); //$NON-NLS-1$
+
+ invokeMethod(OS.class, "objc_registerClassPair", //$NON-NLS-1$
+ new Object[] { wrapPointer(cls) });
+ }
+
+ private void log(Exception e) {
+ mCallbacks.printError("%1$s: %2$s", getClass().getSimpleName(), e.toString()); //$NON-NLS-1$
+ }
+
+ private void hookApplicationMenu(String appName) {
+ try {
+ // create About Eclipse menu command
+ NSMenu mainMenu = NSApplication.sharedApplication().mainMenu();
+ NSMenuItem mainMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, mainMenu,
+ "itemAtIndex", new Object[] {wrapPointer(0)}); //$NON-NLS-1$
+ NSMenu appMenu = mainMenuItem.submenu();
+
+ // add the about action
+ NSMenuItem aboutMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu,
+ "itemAtIndex", new Object[] {wrapPointer(kAboutMenuItem)});
+ aboutMenuItem.setTitle(NSString.stringWith("About " + appName)); //$NON-NLS-1$
+
+ // enable pref menu
+ NSMenuItem prefMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu,
+ "itemAtIndex", new Object[] {wrapPointer(kPreferencesMenuItem)}); //$NON-NLS-1$
+ prefMenuItem.setEnabled(true);
+
+ // disable services menu
+ NSMenuItem servicesMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu,
+ "itemAtIndex", new Object[] {wrapPointer(kServicesMenuItem)}); //$NON-NLS-1$
+ servicesMenuItem.setEnabled(false);
+
+ // Register as a target on the prefs and quit items.
+ prefMenuItem.setTarget(mDelegate);
+ invokeMethod(NSMenuItem.class, prefMenuItem,
+ "setAction", new Object[] {wrapPointer(sel_preferencesMenuItemSelected_)}); //$NON-NLS-1$
+ aboutMenuItem.setTarget(mDelegate);
+ invokeMethod(NSMenuItem.class, aboutMenuItem,
+ "setAction", new Object[] {wrapPointer(sel_aboutMenuItemSelected_)}); //$NON-NLS-1$
+ } catch (Exception e) {
+ // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception
+ // not expected to happen at all.
+ log(e);
+ }
+ }
+
+ void preferencesMenuItemSelected() {
+ try {
+ NSMenu mainMenu = NSApplication.sharedApplication().mainMenu();
+ NSMenuItem mainMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, mainMenu,
+ "itemAtIndex", new Object[] {wrapPointer(0)}); //$NON-NLS-1$
+ NSMenu appMenu = mainMenuItem.submenu();
+ NSMenuItem prefMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu, "" + //$NON-NLS-1$
+ "itemAtIndex", new Object[] {wrapPointer(kPreferencesMenuItem)}); //$NON-NLS-1$
+ try {
+ prefMenuItem.setEnabled(false);
+
+ mCallbacks.onPreferencesMenuSelected();
+ }
+ finally {
+ prefMenuItem.setEnabled(true);
+ }
+ } catch (Exception e) {
+ // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception
+ // not expected to happen at all.
+ log(e);
+ }
+ }
+
+ void aboutMenuItemSelected() {
+ try {
+ NSMenu mainMenu = NSApplication.sharedApplication().mainMenu();
+ NSMenuItem mainMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, mainMenu,
+ "itemAtIndex", new Object[] {wrapPointer(0)}); //$NON-NLS-1$
+ NSMenu appMenu = mainMenuItem.submenu();
+ NSMenuItem aboutMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu,
+ "itemAtIndex", new Object[] {wrapPointer(kAboutMenuItem)}); //$NON-NLS-1$
+ try {
+ aboutMenuItem.setEnabled(false);
+
+ mCallbacks.onAboutMenuSelected();
+ }
+ finally {
+ aboutMenuItem.setEnabled(true);
+ }
+ } catch (Exception e) {
+ // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception
+ // not expected to happen at all.
+ log(e);
+ }
+ }
+
+ private long convertToLong(Object object) {
+ if (object instanceof Integer) {
+ Integer i = (Integer) object;
+ return i.longValue();
+ }
+ if (object instanceof Long) {
+ Long l = (Long) object;
+ return l.longValue();
+ }
+ return 0;
+ }
+
+ private static Object invokeMethod(Class clazz, String methodName,
+ Object[] args) throws IllegalArgumentException,
+ IllegalAccessException, InvocationTargetException,
+ SecurityException, NoSuchMethodException {
+ return invokeMethod(clazz, null, methodName, args);
+ }
+
+ private static Object invokeMethod(Class clazz, Object target,
+ String methodName, Object[] args) throws IllegalArgumentException,
+ IllegalAccessException, InvocationTargetException,
+ SecurityException, NoSuchMethodException {
+ Class[] signature = new Class[args.length];
+ for (int i = 0; i < args.length; i++) {
+ Class thisClass = args[i].getClass();
+ if (thisClass == Integer.class) {
+ signature[i] = int.class;
+ } else if (thisClass == Long.class) {
+ signature[i] = long.class;
+ } else if (thisClass == Byte.class) {
+ signature[i] = byte.class;
+ } else {
+ signature[i] = thisClass;
+ }
+ }
+ Method method = clazz.getMethod(methodName, signature);
+ return method.invoke(target, args);
+ }
+
+ private static Object wrapPointer(long value) {
+ Class PTR_CLASS = C.PTR_SIZEOF == 8 ? long.class : int.class;
+ if (PTR_CLASS == long.class) {
+ return new Long(value);
+ } else {
+ return new Integer((int)value);
+ }
+ }
+}
diff --git a/swtmenubar/src/com/android/menubar/IMenuBarCallback.java b/swtmenubar/src/com/android/menubar/IMenuBarCallback.java
new file mode 100644
index 0000000..b0d6568
--- /dev/null
+++ b/swtmenubar/src/com/android/menubar/IMenuBarCallback.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.menubar;
+
+
+
+/**
+ * Callbacks used by {@link IMenuBarEnhancer}.
+ */
+public interface IMenuBarCallback {
+ /**
+ * Invoked when the About menu item is selected by the user.
+ */
+ abstract public void onAboutMenuSelected();
+
+ /**
+ * Invoked when the Preferences or Options menu item is selected by the user.
+ */
+ abstract public void onPreferencesMenuSelected();
+
+ /**
+ * Used by the enhancer implementations to report errors.
+ *
+ * @param format A printf-like format string.
+ * @param args The parameters for the printf-like format string.
+ */
+ abstract public void printError(String format, Object...args);
+}
diff --git a/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java b/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java
new file mode 100644
index 0000000..1d587e9
--- /dev/null
+++ b/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.menubar;
+
+import org.eclipse.swt.widgets.Menu;
+
+
+/**
+ * Interface to the platform-specific MenuBarEnhancer implementation returned by
+ * {@link MenuBarEnhancer#setupMenu}.
+ */
+public interface IMenuBarEnhancer {
+
+ /**
+ * Updates the menu bar to provide an About menu item and a Preferences menu item.
+ * Depending on the platform, the menu items might be decorated with the
+ * given {@code appName}.
+ * <p/>
+ * Users should not call this directly.
+ * {@link MenuBarEnhancer#setupMenu} should be used instead.
+ *
+ * @param appName Name used for the About menu item and similar. Must not be null.
+ * @param swtMenu For non-mac platform this is the menu where the "About" and
+ * the "Preferences" menu items are created. Must not be null.
+ * @param callbacks Callbacks called when "About" and "Preferences" menu items are invoked.
+ * Must not be null.
+ */
+ public void setupMenu(
+ String appName,
+ Menu swtMenu,
+ IMenuBarCallback callbacks);
+}
diff --git a/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java
new file mode 100644
index 0000000..e40fbe0
--- /dev/null
+++ b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.menubar;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+
+
+/**
+ * On Mac, {@link MenuBarEnhancer#setupMenu} plugs a listener on the About and the
+ * Preferences menu items of the standard "application" menu in the menu bar.
+ * On Windows or Linux, it adds relevant items to a given {@link Menu} linked to
+ * the same listeners.
+ */
+public final class MenuBarEnhancer {
+
+ private MenuBarEnhancer() {
+ }
+
+ /**
+ * Creates an instance of {@link IMenuBarEnhancer} specific to the current platform
+ * and invoke its {@link IMenuBarEnhancer#setupMenu} to updates the menu bar.
+ * <p/>
+ * Depending on the platform, this will either hook into the existing About menu item
+ * and a Preferences or Options menu item or add new ones to the given {@code swtMenu}.
+ * Depending on the platform, the menu items might be decorated with the
+ * given {@code appName}.
+ * <p/>
+ * Potential errors are reported through {@link IMenuBarCallback}.
+ *
+ * @param appName Name used for the About menu item and similar. Must not be null.
+ * @param swtMenu For non-mac platform this is the menu where the "About" and
+ * the "Options" menu items are created. Typically the menu might be
+ * called "Tools". Must not be null.
+ * @param callbacks Callbacks called when "About" and "Preferences" menu items are invoked.
+ * Must not be null.
+ * @return A actual {@link IMenuBarEnhancer} implementation. Never null.
+ * This is currently not of any use for the caller but is left in case
+ * we want to expand the functionality later.
+ */
+ public static IMenuBarEnhancer setupMenu(
+ String appName,
+ Menu swtMenu,
+ IMenuBarCallback callbacks) {
+
+ IMenuBarEnhancer enhancer = null;
+ String p = SWT.getPlatform();
+ String className = null;
+ if ("carbon".equals(p)) { //$NON-NLS-1$
+ className = "com.android.menubar.internal.MenuBarEnhancerCarbon"; //$NON-NLS-1$
+ } else if ("cocoa".equals(p)) { //$NON-NLS-1$
+ // Note: we have a Cocoa implementation that is currently disabled
+ // since the SWT.jar that we use only contain Carbon implementations.
+ //
+ // className = "com.android.menubar.internal.MenuBarEnhancerCocoa"; //$NON-NLS-1$
+ }
+
+ if (className != null) {
+ try {
+ Class<?> clazz = p.getClass().forName(className);
+ enhancer = (IMenuBarEnhancer) clazz.newInstance();
+ } catch (Exception e) {
+ // Log an error and fallback on the default implementation.
+ callbacks.printError(
+ "Failed to instantiate %1$s: %2$s", //$NON-NLS-1$
+ className,
+ e.toString());
+ }
+ }
+
+ // Default implementation for other platforms
+ if (enhancer == null) {
+ enhancer = new IMenuBarEnhancer() {
+ public void setupMenu(
+ String appName,
+ Menu menu,
+ final IMenuBarCallback callbacks) {
+ new MenuItem(menu, SWT.SEPARATOR);
+
+ // Note: we use "Preferences" on Mac and "Options" on Windows/Linux.
+ final MenuItem pref = new MenuItem(menu, SWT.NONE);
+ pref.setText("Options...");
+
+ final MenuItem about = new MenuItem(menu, SWT.NONE);
+ about.setText("About...");
+
+ pref.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ try {
+ pref.setEnabled(false);
+ callbacks.onPreferencesMenuSelected();
+ super.widgetSelected(e);
+ } finally {
+ pref.setEnabled(true);
+ }
+ }
+ });
+
+ about.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ try {
+ about.setEnabled(false);
+ callbacks.onAboutMenuSelected();
+ super.widgetSelected(e);
+ } finally {
+ about.setEnabled(true);
+ }
+ }
+ });
+ }
+ };
+ }
+
+ enhancer.setupMenu(appName, swtMenu, callbacks);
+ return enhancer;
+ }
+
+}