diff options
author | Raphael Moll <ralf@android.com> | 2011-04-13 23:51:39 -0700 |
---|---|---|
committer | Raphael Moll <ralf@android.com> | 2011-04-15 11:52:59 -0700 |
commit | 51cb7801f4d3abf5d98702ebf8c6b16d44097365 (patch) | |
tree | 8906249527c75a869324368533022103456c55e6 | |
parent | cc422bcaf6dd9328d949600739dcc4db8deb2b61 (diff) | |
download | sdk-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.atree | 1 | ||||
-rw-r--r-- | swtmenubar/.classpath | 7 | ||||
-rw-r--r-- | swtmenubar/.gitignore | 1 | ||||
-rw-r--r-- | swtmenubar/.project | 17 | ||||
-rw-r--r-- | swtmenubar/Android.mk | 33 | ||||
-rw-r--r-- | swtmenubar/MODULE_LICENSE_EPL | 0 | ||||
-rw-r--r-- | swtmenubar/NOTICE | 224 | ||||
-rwxr-xr-x | swtmenubar/README | 65 | ||||
-rwxr-xr-x | swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java | 131 | ||||
-rwxr-xr-x | swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java.txt | 334 | ||||
-rw-r--r-- | swtmenubar/src/com/android/menubar/IMenuBarCallback.java | 42 | ||||
-rw-r--r-- | swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java | 46 | ||||
-rw-r--r-- | swtmenubar/src/com/android/menubar/MenuBarEnhancer.java | 137 |
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; + } + +} |