diff options
75 files changed, 3439 insertions, 502 deletions
diff --git a/attribute_stats/src/Analyzer.java b/attribute_stats/src/Analyzer.java index a6bbb4a..8da53ea 100644 --- a/attribute_stats/src/Analyzer.java +++ b/attribute_stats/src/Analyzer.java @@ -34,8 +34,11 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -59,6 +62,7 @@ public class Analyzer { private List<File> mDirectories; private File mCurrentFile; + private boolean mListAdvanced; /** Map from view id to map from attribute to frequency count */ private Map<String, Map<String, Usage>> mFrequencies = @@ -74,9 +78,10 @@ public class Analyzer { private int mLayoutFileCount; private File mXmlMetadataFile; - private Analyzer(List<File> directories, File xmlMetadataFile) { + private Analyzer(List<File> directories, File xmlMetadataFile, boolean listAdvanced) { mDirectories = directories; mXmlMetadataFile = xmlMetadataFile; + mListAdvanced = listAdvanced; } public static void main(String[] args) { @@ -90,14 +95,21 @@ public class Analyzer { File metadataFile = null; List<File> directories = new ArrayList<File>(); + boolean listAdvanced = false; for (int i = 0, n = args.length; i < n; i++) { String arg = args[i]; + if (arg.equals("--list")) { + // List ALL encountered attributes + listAdvanced = true; + continue; + } + // The -metadata flag takes a pointer to an ADT extra-view-metadata.xml file // and attempts to insert topAttrs attributes into it (and saves it as same // file +.mod as an extension). This isn't listed on the usage flag because // it's pretty brittle and requires some manual fixups to the file afterwards. - if (arg.equals("-metadata")) { + if (arg.equals("--metadata")) { i++; File file = new File(args[i]); if (!file.exists()) { @@ -125,13 +137,18 @@ public class Analyzer { directories.add(directory); } - new Analyzer(directories, metadataFile).analyze(); + new Analyzer(directories, metadataFile, listAdvanced).analyze(); } private void analyze() { for (File directory : mDirectories) { scanDirectory(directory); } + + if (mListAdvanced) { + listAdvanced(); + } + printStatistics(); if (mXmlMetadataFile != null) { @@ -523,6 +540,69 @@ public class Analyzer { System.out.println("Done - wrote " + output.getPath()); } + //private File mPublicFile = new File(location, "data/res/values/public.xml"); + private File mPublicFile = new File("/Volumes/AndroidWork/git/frameworks/base/core/res/res/values/public.xml"); + + private void listAdvanced() { + Set<String> keys = new HashSet<String>(1000); + + // Merged usages across view types + Map<String, Usage> mergedUsages = new HashMap<String, Usage>(100); + + for (Entry<String,Map<String,Usage>> entry : mFrequencies.entrySet()) { + String view = entry.getKey(); + if (view.indexOf('.') != -1 && !view.startsWith("android.")) { + // Skip custom views etc + continue; + } + Map<String, Usage> map = entry.getValue(); + for (Usage usage : map.values()) { +// if (usage.count == 1) { +// System.out.println("Only found *one* usage of " + usage.attribute); +// } +// if (usage.count < 4) { +// System.out.println("Only found " + usage.count + " usage of " + usage.attribute); +// } + + String attribute = usage.attribute; + int index = attribute.indexOf(':'); + if (index == -1 || attribute.startsWith("android:")) { + Usage merged = mergedUsages.get(attribute); + if (merged == null) { + merged = new Usage(attribute); + merged.count = usage.count; + mergedUsages.put(attribute, merged); + } else { + merged.count += usage.count; + } + } + } + } + + for (Usage usage : mergedUsages.values()) { + String attribute = usage.attribute; + if (usage.count < 4) { + System.out.println("Only found " + usage.count + " usage of " + usage.attribute); + continue; + } + int index = attribute.indexOf(':'); + if (index != -1) { + attribute = attribute.substring(index + 1); // +1: skip ':' + } + keys.add(attribute); + } + + List<String> sorted = new ArrayList<String>(keys); + Collections.sort(sorted); + System.out.println("\nEncountered Attributes"); + System.out.println("-----------------------------"); + for (String attribute : sorted) { + System.out.println(attribute); + } + + System.out.println(); + } + private static class Usage implements Comparable<Usage> { public String attribute; public int count; @@ -539,6 +619,7 @@ public class Analyzer { count++; } + @Override public int compareTo(Usage o) { // Sort by decreasing frequency, then sort alphabetically int frequencyDelta = o.count - count; diff --git a/build/tools.atree b/build/tools.atree index 54164a9..89401e2 100644 --- a/build/tools.atree +++ b/build/tools.atree @@ -121,9 +121,9 @@ external/emma/lib/emma_ant.jar tools/lib/emma_ant framework/jcommon-1.0.12.jar tools/lib/jcommon-1.0.12.jar framework/jfreechart-1.0.9.jar tools/lib/jfreechart-1.0.9.jar framework/jfreechart-1.0.9-swt.jar tools/lib/jfreechart-1.0.9-swt.jar -framework/org.eclipse.core.commands_3.4.0.I20080509-2000.jar tools/lib/org.eclipse.core.commands_3.4.0.I20080509-2000.jar -framework/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar tools/lib/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar -framework/org.eclipse.jface_3.4.2.M20090107-0800.jar tools/lib/org.eclipse.jface_3.4.2.M20090107-0800.jar +framework/org.eclipse.core.commands_3.6.0.I20100512-1500.jar tools/lib/org.eclipse.core.commands_3.6.0.I20100512-1500.jar +framework/org.eclipse.equinox.common_3.6.0.v20100503.jar tools/lib/org.eclipse.equinox.common_3.6.0.v20100503.jar +framework/org.eclipse.jface_3.6.2.M20110210-1200.jar tools/lib/org.eclipse.jface_3.6.2.M20110210-1200.jar framework/osgi.jar tools/lib/osgi.jar framework/swing-worker-1.1.jar tools/lib/swing-worker-1.1.jar prebuilts/tools/common/asm-tools/asm-4.0.jar tools/lib/asm-4.0.jar diff --git a/ddms/app/.classpath b/ddms/app/.classpath index 978cb46..f8f8a9c 100644 --- a/ddms/app/.classpath +++ b/ddms/app/.classpath @@ -7,9 +7,9 @@ <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/> <classpathentry combineaccessrules="false" kind="src" path="/SdkStatsService"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.4.0.I20080509-2000.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.4.2.M20090107-0800.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/osgi/osgi.jar"/> <classpathentry kind="output" path="bin"/> diff --git a/ddms/app/Android.mk b/ddms/app/Android.mk index d1e4a52..74d0f6f 100644 --- a/ddms/app/Android.mk +++ b/ddms/app/Android.mk @@ -18,9 +18,9 @@ LOCAL_JAVA_LIBRARIES := \ ddmuilib \ swt \ swtmenubar \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.equinox.common_3.6.0.v20100503 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 LOCAL_MODULE := ddms include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/ddms/app/README b/ddms/app/README index 42efb7b..0d9bbc4 100644 --- a/ddms/app/README +++ b/ddms/app/README @@ -45,7 +45,7 @@ In order to compile the project: - Create a new user library named ANDROID_OSGI - Add the following JAR file: - - prebuilt/common/osgi/osgi.jar + - prebuilt/common/eclipse/org.eclipse.osgi_3.*.jar ------- diff --git a/ddms/app/etc/manifest.txt b/ddms/app/etc/manifest.txt index 8c6ab23..3907767 100644 --- a/ddms/app/etc/manifest.txt +++ b/ddms/app/etc/manifest.txt @@ -1,3 +1,3 @@ Main-Class: com.android.ddms.Main -Class-Path: androidprefs.jar sdkstats.jar ddmlib.jar ddmuilib.jar swtmenubar.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar +Class-Path: androidprefs.jar sdkstats.jar ddmlib.jar ddmuilib.jar swtmenubar.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar diff --git a/ddms/libs/ddmuilib/.classpath b/ddms/libs/ddmuilib/.classpath index 82faa31..027a79c 100644 --- a/ddms/libs/ddmuilib/.classpath +++ b/ddms/libs/ddmuilib/.classpath @@ -4,9 +4,9 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.4.0.I20080509-2000.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.4.2.M20090107-0800.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/jfreechart/jcommon-1.0.12.jar"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/jfreechart/jfreechart-1.0.9-swt.jar"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/jfreechart/jfreechart-1.0.9.jar"/> diff --git a/ddms/libs/ddmuilib/Android.mk b/ddms/libs/ddmuilib/Android.mk index d35b861..4b34500 100644 --- a/ddms/libs/ddmuilib/Android.mk +++ b/ddms/libs/ddmuilib/Android.mk @@ -15,9 +15,9 @@ LOCAL_JAR_MANIFEST := etc/manifest.txt LOCAL_JAVA_LIBRARIES := \ ddmlib \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 \ + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.equinox.common_3.6.0.v20100503 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 \ jcommon-1.0.12 \ jfreechart-1.0.9 \ jfreechart-1.0.9-swt diff --git a/ddms/libs/ddmuilib/etc/manifest.txt b/ddms/libs/ddmuilib/etc/manifest.txt index 1db70de..b74fd14 100644 --- a/ddms/libs/ddmuilib/etc/manifest.txt +++ b/ddms/libs/ddmuilib/etc/manifest.txt @@ -1 +1 @@ -Class-Path: ddmlib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar
\ No newline at end of file +Class-Path: ddmlib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar diff --git a/ddms/libs/ddmuilib/tests/Android.mk b/ddms/libs/ddmuilib/tests/Android.mk index dc187ec..6bbff34 100644 --- a/ddms/libs/ddmuilib/tests/Android.mk +++ b/ddms/libs/ddmuilib/tests/Android.mk @@ -26,9 +26,9 @@ LOCAL_JAVA_LIBRARIES := \ ddmlib \ ddmuilib \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 \ + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.equinox.common_3.6.0.v20100503 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 \ junit include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt index c1640c0..c4d9746 100644 --- a/eclipse/dictionary.txt +++ b/eclipse/dictionary.txt @@ -36,6 +36,7 @@ callbacks carlo cf changeset +charset checkbox checkboxes classloader @@ -174,6 +175,7 @@ multimaps namespace namespaces newfound +nexus ninepatch nodpi num @@ -205,6 +207,7 @@ pristine programmatic programmatically proguard +propertysheet proxies proxy pulldown @@ -268,6 +271,7 @@ subclassed subclassing submenu subregion +superclasses supertype symlinks syncs @@ -278,6 +282,7 @@ textfield textfields thematically themed +thumbnail timestamp tmp toolbar diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath index f874a9e..a3376bd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath @@ -14,6 +14,7 @@ <classpathentry kind="lib" path="libs/assetstudio.jar" sourcepath="/assetstudio"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> + <classpathentry kind="var" path="ANDROID_SRC/sdk/eclipse/plugins/com.android.ide.eclipse.adt/libs/propertysheet.jar" sourcepath="/ANDROID_SRC/external/eclipse-windowbuilder/propertysheet/src"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-tree-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/lombok-ast/lombok-ast-0.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/lombok-ast/src.zip"/> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index 0b40004..dcef3c9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -15,7 +15,8 @@ Bundle-ClassPath: ., libs/lint_checks.jar, libs/lombok-ast-0.2.jar, libs/asm-4.0.jar, - libs/asm-tree-4.0.jar + libs/asm-tree-4.0.jar, + libs/propertysheet.jar Bundle-Activator: com.android.ide.eclipse.adt.AdtPlugin Bundle-Vendor: The Android Open Source Project Require-Bundle: com.android.ide.eclipse.base, diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_design.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_design.png Binary files differindex a19f3b0..a19f3b0 100755..100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_design.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_design.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_source.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_source.png Binary files differindex 874cc1e..874cc1e 100755..100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_source.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_source.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_palette.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_palette.png Binary files differindex c682f57..c682f57 100755..100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_palette.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_palette.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/filter_advanced_properties.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/filter_advanced_properties.png Binary files differnew file mode 100644 index 0000000..5f5b078 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/filter_advanced_properties.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/goto_definition.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/goto_definition.png Binary files differnew file mode 100644 index 0000000..daac537 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/goto_definition.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png Binary files differindex 79ffc2d..79ffc2d 100755..100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/properties_default.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/properties_default.png Binary files differnew file mode 100644 index 0000000..5696a31 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/properties_default.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/sdk_manager.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/sdk_manager.png Binary files differindex 08ffda8..08ffda8 100755..100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/sdk_manager.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/sdk_manager.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/sort_alpha.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/sort_alpha.png Binary files differnew file mode 100644 index 0000000..112e05b --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/sort_alpha.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index f6fe09f..f8f6311 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -16,6 +16,11 @@ package com.android.ide.eclipse.adt; +import static com.android.sdklib.SdkConstants.CURRENT_PLATFORM; +import static com.android.sdklib.SdkConstants.PLATFORM_DARWIN; +import static com.android.sdklib.SdkConstants.PLATFORM_LINUX; +import static com.android.sdklib.SdkConstants.PLATFORM_WINDOWS; + import com.android.AndroidConstants; import com.android.ide.common.log.ILogger; import com.android.ide.common.resources.ResourceFile; @@ -94,6 +99,7 @@ import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.ui.texteditor.AbstractTextEditor; +import org.eclipse.wb.internal.core.DesignerPlugin; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -257,6 +263,14 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { // load preferences. AdtPrefs.getPrefs().loadValues(null /*event*/); + // initialize property-sheet library + DesignerPlugin.initialize( + this, + PLUGIN_ID, + CURRENT_PLATFORM == PLATFORM_WINDOWS, + CURRENT_PLATFORM == PLATFORM_DARWIN, + CURRENT_PLATFORM == PLATFORM_LINUX); + // initialize editors startEditors(); @@ -286,6 +300,8 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { stopEditors(); IncludeFinder.stop(); + DesignerPlugin.dispose(); + mRed.dispose(); synchronized (AdtPlugin.class) { sPlugin = null; @@ -668,18 +684,19 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { } /** - * Reads the contents of an {@link InputStreamReader} and return it as a String + * Reads the contents of a {@link Reader} and return it as a String. This + * method will close the input reader. * - * @param inputStream the input stream to be read from - * @return the String read from the stream, or null if there was an error + * @param reader the reader to be read from + * @return the String read from reader, or null if there was an error */ - public static String readFile(Reader inputStream) { - BufferedReader reader = null; + public static String readFile(Reader reader) { + BufferedReader bufferedReader = null; try { - reader = new BufferedReader(inputStream); + bufferedReader = new BufferedReader(reader); StringBuilder sb = new StringBuilder(2000); while (true) { - int c = reader.read(); + int c = bufferedReader.read(); if (c == -1) { return sb.toString(); } else { @@ -690,8 +707,8 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { // pass -- ignore files we can't read } finally { try { - if (reader != null) { - reader.close(); + if (bufferedReader != null) { + bufferedReader.close(); } } catch (IOException e) { AdtPlugin.log(e, "Can't read input stream"); //$NON-NLS-1$ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java index 08bb7d8..7e2a44d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java @@ -103,6 +103,32 @@ public class AdtUtils { } /** + * Returns true if the given string starts with the given prefix, using a + * case-insensitive comparison. + * + * @param string the full string to be checked + * @param prefix the prefix to be checked for + * @return true if the string case-insensitively starts with the given prefix + */ + public static boolean startsWithIgnoreCase(String string, String prefix) { + return string.regionMatches(true /* ignoreCase */, 0, prefix, 0, prefix.length()); + } + + /** + * Returns true if the given string starts at the given offset with the + * given prefix, case insensitively. + * + * @param string the full string to be checked + * @param offset the offset in the string to start looking + * @param prefix the prefix to be checked for + * @return true if the string case-insensitively starts at the given offset + * with the given prefix + */ + public static boolean startsWith(String string, int offset, String prefix) { + return string.regionMatches(true /* ignoreCase */, offset, prefix, 0, prefix.length()); + } + + /** * Strips the whitespace from the given string * * @param string the string to be cleaned up diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java index 22aa687..0a12ba4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java @@ -373,5 +373,4 @@ public class IconFactory { return data; } } - } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java index 4ccab2d..61db9f3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java @@ -110,7 +110,7 @@ public class XmlEditorMultiOutline extends Page implements IContentOutlinePage, @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { if (mListeners == null) { - mListeners = new ArrayList<ISelectionChangedListener>(); + mListeners = new ArrayList<ISelectionChangedListener>(2); } mListeners.add(listener); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java index 20096f4..b00656e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java @@ -40,6 +40,7 @@ import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.La import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_INCLUDE; import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE; +import com.android.annotations.NonNull; import com.android.ide.common.api.IAttributeInfo.Format; import com.android.ide.common.resources.platform.AttributeInfo; import com.android.ide.eclipse.adt.AdtConstants; @@ -308,6 +309,66 @@ public final class DescriptorsUtils { } /** + * Similar to {@link #prettyAttributeUiName(String)}, but it will capitalize + * all words, not just the first one. + * <p/> + * The original xml name starts with a lower case and is camel-case, e.g. + * "maxWidthForView". The corresponding return value is + * "Max Width For View". + * + * @param name the attribute name, which should be a camel case name, e.g. + * "maxWidth" + * @return the corresponding display name, e.g. "Max Width" + */ + @NonNull + public static String capitalize(@NonNull String name) { + if (name.isEmpty()) { + return name; + } + StringBuilder buf = new StringBuilder(2 * name.length()); + + char c = name.charAt(0); + // Use upper case initial letter + buf.append(Character.toUpperCase(c)); + int len = name.length(); + for (int i = 1; i < len; i++) { + c = name.charAt(i); + if (Character.isUpperCase(c)) { + // Break camel case into separate words + buf.append(' '); + // Use a lower case initial letter for the next word, except if the + // word is solely X, Y or Z. + buf.append(c); + } else if (c == '_') { + buf.append(' '); + if (i < len -1 && Character.isLowerCase(name.charAt(i + 1))) { + buf.append(Character.toUpperCase(name.charAt(i + 1))); + i++; + } + } else { + buf.append(c); + } + } + + name = buf.toString(); + + // Replace these acronyms by upper-case versions + // - (?<=^| ) means "if preceded by a space or beginning of string" + // - (?=$| ) means "if followed by a space or end of string" + if (name.contains("Sdk")) { + name = name.replaceAll("(?<=^| )Sdk(?=$| )", "SDK"); + } + if (name.contains("Uri")) { + name = name.replaceAll("(?<=^| )Uri(?=$| )", "URI"); + } + if (name.contains("Ime")) { + name = name.replaceAll("(?<=^| )Ime(?=$| )", "IME"); + } + + return name; + } + + /** * Formats the javadoc tooltip to be usable in a tooltip. */ public static String formatTooltip(String javadoc) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java index df39a3d..5f2b79b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java @@ -36,8 +36,9 @@ import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorP import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutActionBar; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.OutlinePage; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.PropertySheetPage; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionManager; import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine; +import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertySheetPage; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.internal.sdk.Sdk; @@ -54,6 +55,8 @@ import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; @@ -476,6 +479,16 @@ public class LayoutEditorDelegate extends CommonXmlDelegate // https://bugs.eclipse.org/bugs/show_bug.cgi?id=1917 if (mMultiOutline == null || mMultiOutline.isDisposed()) { mMultiOutline = new XmlEditorMultiOutline(); + mMultiOutline.addSelectionChangedListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + ISelection selection = event.getSelection(); + getEditor().getSite().getSelectionProvider().setSelection(selection); + SelectionManager manager = + mGraphicalEditor.getCanvasControl().getSelectionManager(); + manager.setSelection(selection); + } + }); updateOutline(getEditor().getActivePageInstance()); } @@ -484,7 +497,7 @@ public class LayoutEditorDelegate extends CommonXmlDelegate if (IPropertySheetPage.class == adapter && mGraphicalEditor != null) { if (mPropertyPage == null) { - mPropertyPage = new PropertySheetPage(); + mPropertyPage = new PropertySheetPage(mGraphicalEditor); } return mPropertyPage; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java index 10e1a4d..2133abd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java @@ -305,8 +305,6 @@ public final class LayoutDescriptors implements IDescriptorProvider { AttributeInfo[] attrList = link.getAttributes(); if (attrList.length > 0) { attributeSources.add(link.getFullClassName()); - attributes.add(new SeparatorAttributeDescriptor( - String.format("Attributes from %1$s", link.getShortClassName()))); DescriptorsUtils.appendAttributes(attributes, null, // elementName SdkConstants.NS_RESOURCES, @@ -321,28 +319,11 @@ public final class LayoutDescriptors implements IDescriptorProvider { LayoutParamsInfo layoutParams = info.getLayoutData(); for(; layoutParams != null; layoutParams = layoutParams.getSuperClass()) { - boolean needSeparator = true; for (AttributeInfo attrInfo : layoutParams.getAttributes()) { if (DescriptorsUtils.containsAttribute(layoutAttributes, SdkConstants.NS_RESOURCES, attrInfo)) { continue; } - if (needSeparator) { - ViewClassInfo viewLayoutClass = layoutParams.getViewLayoutClass(); - String title; - String shortClassName = viewLayoutClass.getShortClassName(); - if (layoutParams.getShortClassName().equals( - SdkConstants.CLASS_NAME_LAYOUTPARAMS)) { - title = String.format("Layout Attributes from %1$s", - shortClassName); - } else { - title = String.format("Layout Attributes from %1$s (%2$s)", - shortClassName, - layoutParams.getShortClassName()); - } - layoutAttributes.add(new SeparatorAttributeDescriptor(title)); - needSeparator = false; - } DescriptorsUtils.appendAttribute(layoutAttributes, null, // elementName SdkConstants.NS_RESOURCES, diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java index dd103c5..c8497da 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java @@ -21,6 +21,8 @@ import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE_V7; import static com.android.ide.common.layout.LayoutConstants.GESTURE_OVERLAY_VIEW; import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE; +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.common.api.Margins; import com.android.ide.common.api.Rect; import com.android.ide.common.layout.GridLayoutRule; @@ -70,8 +72,7 @@ public class CanvasViewInfo implements IPropertySource { /** * Minimal size of the selection, in case an empty view or layout is selected. */ - private static final int SELECTION_MIN_SIZE = 6; - + public static final int SELECTION_MIN_SIZE = 6; private final Rectangle mAbsRect; private final Rectangle mSelectionRect; @@ -121,17 +122,21 @@ public class CanvasViewInfo implements IPropertySource { * * @return the bounding box in absolute coordinates */ + @NonNull public Rectangle getAbsRect() { return mAbsRect; } - /* - * Returns the absolute selection bounds of the view info as a rectangle. - * The selection bounds will always have a size greater or equal to - * {@link #SELECTION_MIN_SIZE}. - * The width/height is inclusive (i.e. width = right-left-1). - * This is in absolute "screen" coordinates (relative to the rendered bitmap). - */ + /** + * Returns the absolute selection bounds of the view info as a rectangle. + * The selection bounds will always have a size greater or equal to + * {@link #SELECTION_MIN_SIZE}. + * The width/height is inclusive (i.e. width = right-left-1). + * This is in absolute "screen" coordinates (relative to the rendered bitmap). + * + * @return the absolute selection bounds + */ + @NonNull public Rectangle getSelectionRect() { return mSelectionRect; } @@ -141,6 +146,7 @@ public class CanvasViewInfo implements IPropertySource { * @return An {@link UiViewElementNode} that uniquely identifies the object in the XML model. * @see ViewInfo#getCookie() */ + @Nullable public UiViewElementNode getUiViewNode() { return mUiViewNode; } @@ -151,6 +157,7 @@ public class CanvasViewInfo implements IPropertySource { * * @return the parent {@link CanvasViewInfo}, which can be null */ + @Nullable public CanvasViewInfo getParent() { return mParent; } @@ -162,6 +169,7 @@ public class CanvasViewInfo implements IPropertySource { * * @return the children, never null */ + @NonNull public List<CanvasViewInfo> getChildren() { return mChildren; } @@ -171,6 +179,7 @@ public class CanvasViewInfo implements IPropertySource { * children of a {@code <merge>} tag included into a separate layout, return the * "primary" view, the first view that is rendered */ + @Nullable private CanvasViewInfo getPrimaryNodeSibling() { if (mNodeSiblings == null || mNodeSiblings.size() == 0) { return null; @@ -200,6 +209,7 @@ public class CanvasViewInfo implements IPropertySource { * * @return a non-empty list of siblings (including this), or null */ + @Nullable public List<CanvasViewInfo> getNodeSiblings() { return mNodeSiblings; } @@ -218,6 +228,7 @@ public class CanvasViewInfo implements IPropertySource { * @return list of {@link CanvasViewInfo} objects that are children of this view, * never null */ + @NonNull public List<CanvasViewInfo> getUniqueChildren() { boolean haveHidden = false; @@ -261,10 +272,7 @@ public class CanvasViewInfo implements IPropertySource { * @param potentialParent the view info to check * @return true if the given info is a parent of this view */ - public boolean isParent(CanvasViewInfo potentialParent) { - if (potentialParent == null) { - - } + public boolean isParent(@NonNull CanvasViewInfo potentialParent) { CanvasViewInfo p = mParent; while (p != null) { if (p == potentialParent) { @@ -281,10 +289,11 @@ public class CanvasViewInfo implements IPropertySource { * Experience shows this is the full qualified Java name of the View. * TODO: Rename this method to getFqcn. * - * @return the name of the view info, or null + * @return the name of the view info * * @see ViewInfo#getClassName() */ + @NonNull public String getName() { return mName; } @@ -293,10 +302,16 @@ public class CanvasViewInfo implements IPropertySource { * Returns the View object associated with the {@link CanvasViewInfo}. * @return the view object or null. */ + @Nullable public Object getViewObject() { return mViewObject; } + /** + * Returns the baseline of this object, or -1 if it does not support a baseline + * + * @return the baseline or -1 + */ public int getBaseline() { if (mViewInfo != null) { int baseline = mViewInfo.getBaseLine(); @@ -313,6 +328,7 @@ public class CanvasViewInfo implements IPropertySource { * * @return the {@link Margins} for this {@link CanvasViewInfo} */ + @Nullable public Margins getMargins() { if (mViewInfo != null) { int leftMargin = mViewInfo.getLeftMargin(); @@ -331,6 +347,7 @@ public class CanvasViewInfo implements IPropertySource { } // ---- Implementation of IPropertySource + // TODO: Get rid of this once the old propertysheet implementation is fully gone @Override public Object getEditableValue() { @@ -390,6 +407,7 @@ public class CanvasViewInfo implements IPropertySource { * * @return The XML node corresponding to this info object, or null */ + @Nullable public Node getXmlNode() { UiViewElementNode uiView = getUiViewNode(); if (uiView != null) { @@ -480,7 +498,7 @@ public class CanvasViewInfo implements IPropertySource { * * @param exploded New value of the exploded property to mark this info with. */ - /* package */ void setExploded(boolean exploded) { + void setExploded(boolean exploded) { this.mExploded = exploded; } @@ -489,7 +507,8 @@ public class CanvasViewInfo implements IPropertySource { * * @return A {@link SimpleElement} wrapping this info. */ - /* package */ SimpleElement toSimpleElement() { + @NonNull + SimpleElement toSimpleElement() { UiViewElementNode uiNode = getUiViewNode(); @@ -538,6 +557,7 @@ public class CanvasViewInfo implements IPropertySource { * @return the layout url attribute value for the surrounding include tag, or null if * not applicable */ + @Nullable public String getIncludeUrl() { CanvasViewInfo curr = this; while (curr != null) { @@ -568,12 +588,12 @@ public class CanvasViewInfo implements IPropertySource { } /** Adds the given {@link CanvasViewInfo} as a new last child of this view */ - private void addChild(CanvasViewInfo child) { + private void addChild(@NonNull CanvasViewInfo child) { mChildren.add(child); } /** Adds the given {@link CanvasViewInfo} as a child at the given index */ - private void addChildAt(int index, CanvasViewInfo child) { + private void addChildAt(int index, @NonNull CanvasViewInfo child) { mChildren.add(index, child); } @@ -584,7 +604,7 @@ public class CanvasViewInfo implements IPropertySource { * @param child the child to be removed * @return true if it was a child and was removed */ - public boolean removeChild(CanvasViewInfo child) { + public boolean removeChild(@NonNull CanvasViewInfo child) { return mChildren.remove(child); } @@ -623,6 +643,7 @@ public class CanvasViewInfo implements IPropertySource { * @param root the root {@link ViewInfo} to build from * @return a {@link CanvasViewInfo} hierarchy */ + @NonNull public static Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root, boolean layoutlib5) { return new Builder(layoutlib5).create(root); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java deleted file mode 100644 index d785faf..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.eclipse.adt.internal.editors.layout.gle2; - -import static com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor.DEPRECATED_CATEGORY; - -import org.eclipse.jface.action.IStatusLineManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeItem; -import org.eclipse.ui.IActionBars; -import org.eclipse.ui.views.properties.IPropertySheetEntry; -import org.eclipse.ui.views.properties.PropertySheetEntry; -import org.eclipse.ui.views.properties.PropertySheetSorter; - -/** - * A customized property sheet page for the graphical layout editor v2. - * <p/> - * Currently it just provides a custom tooltip to display attributes javadocs. - * <p/> - * The property sheet is linked to the current site's selection service. - * <p/> - * Note: this is an exact copy of GLE1's UiPropertySheetPage implementation. - * The idea is that eventually GLE1 will go away and we'll upgrade this to be - * a more robust property editor (it currently lacks on so many levels, it's not - * even worth listing the flaws.) - * - * @since GLE2 - */ -public class PropertySheetPage extends org.eclipse.ui.views.properties.PropertySheetPage { - private static final String MISC_CATEGORY = "Misc"; - - public PropertySheetPage() { - super(); - - setSorter(new PropertySheetSorter() { - @Override - public int compareCategories(String categoryA, String categoryB) { - // Sort the "Deprecated" category to the bottom, and the "Misc" - // category second to last. - if (categoryA.equals(DEPRECATED_CATEGORY)) { - return 1; - } else if (categoryB.equals(DEPRECATED_CATEGORY)) { - return -1; - } - if (categoryA.equals(MISC_CATEGORY)) { - return 1; - } else if (categoryB.equals(MISC_CATEGORY)) { - return -1; - } - - return super.compareCategories(categoryA, categoryB); - } - }); - } - - @Override - public void createControl(Composite parent) { - super.createControl(parent); - - setupTooltip(); - - // Override parent class' "set status message" behavior. The parent will set - // the status message to the property's "getDescription()" field. That field - // may contain newlines, which means the text gets cut off. We want to instead - // show ALL the text, fit on a single line, and since we don't get to subclass - // the viewer we will just replace the status message with our own, which works - // since our mouse listener is registered later so runs later. - final Tree tree = (Tree) getControl(); - tree.addMouseListener(new MouseAdapter() { - @Override - public void mouseDown(MouseEvent event) { - Point pt = new Point(event.x, event.y); - TreeItem item = tree.getItem(pt); - if (item != null) { - Object object = item.getData(); - if (object instanceof IPropertySheetEntry) { - IPropertySheetEntry entry = (IPropertySheetEntry) object; - String help = entry.getDescription(); - if (help != null) { - // Strip out newlines to make this a single line entry - help = help.replace('\n', ' '); - // Remove repeated spaces in case there were trailing spaces - help = help.replaceAll(" ", " "); //$NON-NLS-1$ //$NON-NLS-2$ - IActionBars actionBars = getSite().getActionBars(); - IStatusLineManager status = actionBars.getStatusLineManager(); - status.setMessage(help); - } - } - } - } - }); - - // Fix the selection background. In Eclipse 3.5 and 3.6, the selection color - // is white, painted on top of a white or light blue background (table striping), - // which is practically unreadable. This is fixed in 3.7M3, but we need a workaround - // for earlier releases. This just paints a solid color under the current line in - // the left column. - tree.addListener(SWT.EraseItem, new Listener() { - @Override - public void handleEvent(Event event) { - if ((event.detail & SWT.SELECTED) != 0 && event.index == 0) { - GC gc = event.gc; - Rectangle rect = event.getBounds(); - Color background = gc.getBackground(); - Display display = tree.getDisplay(); - gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION)); - gc.fillRectangle(rect.x, rect.y, rect.width, rect.height); - gc.setBackground(background); - } - } - }); - } - - /** - * Sets up a custom tooltip when hovering over tree items. - * <p/> - * The tooltip will display the element's javadoc, if any, or the item's getText otherwise. - */ - private void setupTooltip() { - final Tree tree = (Tree) getControl(); - - /* - * Reference: - * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup - */ - - final Listener listener = new Listener() { - Shell tip = null; - Label label = null; - - @Override - public void handleEvent(Event event) { - switch(event.type) { - case SWT.Dispose: - case SWT.KeyDown: - case SWT.MouseExit: - case SWT.MouseDown: - case SWT.MouseMove: - if (tip != null) { - tip.dispose(); - tip = null; - label = null; - } - break; - case SWT.MouseHover: - if (tip != null) { - tip.dispose(); - tip = null; - label = null; - } - - String tooltip = null; - - TreeItem item = tree.getItem(new Point(event.x, event.y)); - if (item != null) { - Object data = item.getData(); - if (data instanceof PropertySheetEntry) { - tooltip = ((PropertySheetEntry) data).getDescription(); - } - - if (tooltip == null) { - tooltip = item.getText(); - } else { - tooltip = item.getText() + ":\r" + tooltip; - } - - if (tooltip != null) { - Shell shell = tree.getShell(); - Display display = tree.getDisplay(); - - tip = new Shell(shell, SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL); - tip.setBackground(display .getSystemColor(SWT.COLOR_INFO_BACKGROUND)); - FillLayout layout = new FillLayout(); - layout.marginWidth = 2; - tip.setLayout(layout); - label = new Label(tip, SWT.NONE); - label.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); - label.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); - label.setData("_TABLEITEM", item); - label.setText(tooltip); - label.addListener(SWT.MouseExit, this); - label.addListener(SWT.MouseDown, this); - Point size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT); - Rectangle rect = item.getBounds(0); - // Display the tooltip on the same line as the property, - // but offset to the right of wherever the mouse cursor was, - // such that it does not obscure the list of properties. - Point pt = tree.toDisplay(event.x + 15, rect.y); - tip.setBounds(pt.x, pt.y, size.x, size.y); - tip.setVisible(true); - } - } - } - } - }; - - tree.addListener(SWT.Dispose, listener); - tree.addListener(SWT.KeyDown, listener); - tree.addListener(SWT.MouseMove, listener); - tree.addListener(SWT.MouseHover, listener); - - } - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java index 7c2f7f5..5d49426 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java @@ -16,6 +16,8 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.common.api.ResizePolicy; import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; @@ -97,11 +99,22 @@ class SelectionItem { * * @return the selected view info. Cannot be null. */ + @NonNull public CanvasViewInfo getViewInfo() { return mCanvasViewInfo; } /** + * Returns the selected node. + * + * @return the selected node, or null + */ + @Nullable + public UiViewElementNode getUiNode() { + return mCanvasViewInfo.getUiViewNode(); + } + + /** * Returns the selection border rectangle. Cannot be null. * * @return the selection border rectangle, never null @@ -111,11 +124,13 @@ class SelectionItem { } /** Returns the node associated with this selection (may be null) */ + @Nullable NodeProxy getNode() { return mNodeProxy; } /** Returns the canvas associated with this selection (never null) */ + @NonNull LayoutCanvas getCanvas() { return mCanvas; } @@ -126,6 +141,7 @@ class SelectionItem { * Gets the XML text from the given selection for a text transfer. * The returned string can be empty but not null. */ + @NonNull static String getAsText(LayoutCanvas canvas, List<SelectionItem> selection) { StringBuilder sb = new StringBuilder(); @@ -152,6 +168,7 @@ class SelectionItem { * @param items Items to wrap in elements * @return An array of wrapper elements. Never null. */ + @NonNull static SimpleElement[] getAsElements(List<SelectionItem> items) { ArrayList<SimpleElement> elements = new ArrayList<SimpleElement>(); @@ -184,6 +201,7 @@ class SelectionItem { * * @return the {@link SelectionHandles} for this {@link SelectionItem}, never null */ + @NonNull public SelectionHandles getSelectionHandles() { if (mHandles == null) { mHandles = new SelectionHandles(this); @@ -197,6 +215,7 @@ class SelectionItem { * * @return the {@link ResizePolicy} for this item, never null */ + @NonNull public ResizePolicy getResizePolicy() { if (mResizePolicy == null && mNodeProxy != null) { mResizePolicy = ViewMetadataRepository.get().getResizePolicy(mNodeProxy.getFqcn()); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java index e2c573a..1450768 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java @@ -20,6 +20,7 @@ import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE_V7; import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_MARGIN; import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_RADIUS; +import com.android.annotations.NonNull; import com.android.ide.common.api.INode; import com.android.ide.common.layout.GridLayoutRule; import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; @@ -122,6 +123,7 @@ public class SelectionManager implements ISelectionProvider { * * @return An immutable list of {@link SelectionItem}. Can be empty but not null. */ + @NonNull List<SelectionItem> getSelections() { return mUnmodifiableSelection; } @@ -132,7 +134,12 @@ public class SelectionManager implements ISelectionProvider { * * @return A copy of the current selection. Never null. */ - /* package */ List<SelectionItem> getSnapshot() { + @NonNull + public List<SelectionItem> getSnapshot() { + if (mSelectionListeners.isEmpty()) { + return Collections.emptyList(); + } + return new ArrayList<SelectionItem>(mSelections); } @@ -190,6 +197,7 @@ public class SelectionManager implements ISelectionProvider { return; } + boolean changed = false; try { mInsideUpdateSelection = true; @@ -211,7 +219,6 @@ public class SelectionManager implements ISelectionProvider { return; } - boolean changed = false; boolean redoLayout = false; // Create a list of all currently selected view infos @@ -242,6 +249,10 @@ public class SelectionManager implements ISelectionProvider { if (newVi.isInvisible()) { redoLayout = true; } + } else { + // Unrelated selection (e.g. user clicked in the Project Explorer + // or something) -- just ignore these + return; } } @@ -257,15 +268,16 @@ public class SelectionManager implements ISelectionProvider { if (redoLayout) { mCanvas.getEditorDelegate().recomputeLayout(); } - if (changed) { - redraw(); - updateActionsFromSelection(); - } - } } finally { mInsideUpdateSelection = false; } + + if (changed) { + redraw(); + fireSelectionChanged(); + updateActionsFromSelection(); + } } /** @@ -699,7 +711,7 @@ public class SelectionManager implements ISelectionProvider { } /** Sync the selection with an updated view info tree */ - /* package */ void sync() { + void sync() { // Check if the selection is still the same (based on the object keys) // and eventually recompute their bounds. for (ListIterator<SelectionItem> it = mSelections.listIterator(); it.hasNext(); ) { @@ -714,6 +726,9 @@ public class SelectionManager implements ISelectionProvider { // we need to recompute its bounds in case it moved so we'll insert a new one // at the same place. it.remove(); + if (vi == null) { + vi = findCorresponding(s.getViewInfo(), viewHierarchy.getRoot()); + } if (vi != null) { it.add(createSelection(vi)); } @@ -724,6 +739,39 @@ public class SelectionManager implements ISelectionProvider { mAltSelection = null; } + /** Finds the corresponding {@link CanvasViewInfo} in the new hierarchy */ + private CanvasViewInfo findCorresponding(CanvasViewInfo old, CanvasViewInfo newRoot) { + CanvasViewInfo oldParent = old.getParent(); + if (oldParent != null) { + CanvasViewInfo newParent = findCorresponding(oldParent, newRoot); + if (newParent == null) { + return null; + } + + List<CanvasViewInfo> oldSiblings = oldParent.getChildren(); + List<CanvasViewInfo> newSiblings = newParent.getChildren(); + Iterator<CanvasViewInfo> oldIterator = oldSiblings.iterator(); + Iterator<CanvasViewInfo> newIterator = newSiblings.iterator(); + while (oldIterator.hasNext() && newIterator.hasNext()) { + CanvasViewInfo oldSibling = oldIterator.next(); + CanvasViewInfo newSibling = newIterator.next(); + + if (oldSibling.getName().equals(newSibling.getName())) { + // Structure has changed: can't do a proper search + return null; + } + + if (oldSibling == old) { + return newSibling; + } + } + } else { + return newRoot; + } + + return null; + } + /** * Notifies listeners that the selection has changed. */ @@ -884,9 +932,13 @@ public class SelectionManager implements ISelectionProvider { newChildren.add(viewInfo); } } - mCanvas.getSelectionManager().selectMultiple(newChildren); + boolean found = nodes.size() == newChildren.size(); + + if (found || newChildren.size() > 0) { + mCanvas.getSelectionManager().selectMultiple(newChildren); + } - return nodes.size() == newChildren.size(); + return found; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java index d0e957e..99d6505 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.ide.common.api.Rect; import org.eclipse.swt.SWTException; +import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; @@ -123,7 +124,7 @@ public class SwtUtils { * @return A new SWT {@link Image} with the same contents as the source * {@link BufferedImage} */ - public static Image convertToSwt(Display display, BufferedImage awtImage, + public static Image convertToSwt(Device display, BufferedImage awtImage, boolean transferAlpha, int globalAlpha) { if (!isSupportedPaletteType(awtImage.getType())) { awtImage = convertToCompatibleFormat(awtImage); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java index f6437fc..579ef44 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java @@ -18,6 +18,8 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE; +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.common.api.INode; import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.ViewInfo; @@ -179,7 +181,7 @@ public class ViewHierarchy { if (root != null) { infos = CanvasViewInfo.create(root, layoutlib5); if (DUMP_INFO) { - dump(root, 0); + dump(session, root, 0); } } else { infos = null; @@ -693,12 +695,27 @@ public class ViewHierarchy { } /** + * Returns a map of the default properties for the given view object in this session + * + * @param viewObject the object to look up the properties map for + * @return the map of properties, or null if not found + */ + @Nullable + public Map<String, String> getDefaultProperties(@NonNull Object viewObject) { + if (mSession != null) { + return mSession.getDefaultProperties(viewObject); + } + + return null; + } + + /** * Dumps a {@link ViewInfo} hierarchy to stdout * * @param info the {@link ViewInfo} object to dump * @param depth the depth to indent it to */ - public static void dump(ViewInfo info, int depth) { + public static void dump(RenderSession session, ViewInfo info, int depth) { if (DUMP_INFO) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < depth; i++) { @@ -724,11 +741,19 @@ public class ViewHierarchy { } else if (cookie != null) { sb.append(" " + cookie); //$NON-NLS-1$ } + /* Display defaults? + if (info.getViewObject() != null) { + Map<String, String> defaults = session.getDefaultProperties(info.getCookie()); + sb.append(" - defaults: "); //$NON-NLS-1$ + sb.append(defaults); + sb.append('\n'); + } + */ System.out.println(sb.toString()); for (ViewInfo child : info.getChildren()) { - dump(child, depth + 1); + dump(session, child, depth + 1); } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java index 3380a38..d6d5cd9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java @@ -47,6 +47,7 @@ public class PaletteMetadataDescriptor extends ViewElementDescriptor { descriptor.getChildren(), descriptor.getMandatory() == Mandatory.MANDATORY); mInitString = initString; mIconName = iconName; + setSuperClass(descriptor.getSuperClassDesc()); } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/BooleanXmlPropertyEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/BooleanXmlPropertyEditor.java new file mode 100644 index 0000000..32b1192 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/BooleanXmlPropertyEditor.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import static com.android.ide.common.layout.LayoutConstants.VALUE_FALSE; +import static com.android.ide.common.layout.LayoutConstants.VALUE_TRUE; + +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.ui.DrawUtils; + +/** + * Handle an XML property which represents booleans. + * + * Similar to the WindowBuilder PropertyEditor, but operates on Strings rather + * than Booleans (which means it is a tri-state boolean: true, false, not set) + */ +public class BooleanXmlPropertyEditor extends XmlPropertyEditor { + public static final BooleanXmlPropertyEditor INSTANCE = new BooleanXmlPropertyEditor(); + + private static final Image mTrueImage = DesignerPlugin.getImage("properties/true.png"); + private static final Image mFalseImage = DesignerPlugin.getImage("properties/false.png"); + private static final Image mNullImage = + DesignerPlugin.getImage("properties/BooleanNull.png"); + private static final Image mUnknownImage = + DesignerPlugin.getImage("properties/BooleanUnknown.png"); + + private BooleanXmlPropertyEditor() { + } + + @Override + public void paint(Property property, GC gc, int x, int y, int width, int height) + throws Exception { + Object value = property.getValue(); + assert value == null || value instanceof String; + if (value == null || value instanceof String) { + String text = (String) value; + Image image; + if (VALUE_TRUE.equals(text)) { + image = mTrueImage; + } else if (VALUE_FALSE.equals(text)) { + image = mFalseImage; + } else if (text == null) { + image = mNullImage; + } else { + // Probably something like a reference, e.g. @boolean/foo + image = mUnknownImage; + } + + // draw image + DrawUtils.drawImageCV(gc, image, x, y, height); + + // prepare new position/width + int imageWidth = image.getBounds().width + 2; + width -= imageWidth; + + // draw text + if (text != null) { + x += imageWidth; + DrawUtils.drawStringCV(gc, text, x, y, width, height); + } + } + } + + @Override + public boolean activate(PropertyTable propertyTable, Property property, Point location) + throws Exception { + // check that user clicked on image + if (location == null || location.x < mTrueImage.getBounds().width + 2) { + cycleValue(property); + } + // don't activate + return false; + } + + @Override + public void doubleClick(Property property, Point location) throws Exception { + cycleValue(property); + } + + /** + * Cycles through the values + */ + private void cycleValue(Property property) throws Exception { + Object value = property.getValue(); + if (value == null || value instanceof String) { + // Cycle null => true => false => null + String text = (String) value; + if (VALUE_TRUE.equals(text)) { + property.setValue(VALUE_FALSE); + } else if (VALUE_FALSE.equals(text)) { + property.setValue(null); + } else { + property.setValue(VALUE_TRUE); + } + } else { + assert false; + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/EnumXmlPropertyEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/EnumXmlPropertyEditor.java new file mode 100644 index 0000000..f1a3f2a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/EnumXmlPropertyEditor.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ListAttributeDescriptor; + +import org.eclipse.wb.core.controls.CCombo3; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.AbstractComboPropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.ITextValuePropertyEditor; + +class EnumXmlPropertyEditor extends AbstractComboPropertyEditor implements + ITextValuePropertyEditor { + public static final EnumXmlPropertyEditor INSTANCE = new EnumXmlPropertyEditor(); + + private EnumXmlPropertyEditor() { + } + + @Override + protected String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value == null) { + return ""; + } else if (value instanceof String) { + return (String) value; + } else if (value == Property.UNKNOWN_VALUE) { + return "<varies>"; + } else { + return ""; + } + } + + private String[] getItems(Property property) { + XmlProperty xmlProperty = (XmlProperty) property; + AttributeDescriptor descriptor = xmlProperty.getDescriptor(); + assert descriptor instanceof ListAttributeDescriptor; + ListAttributeDescriptor list = (ListAttributeDescriptor) descriptor; + return list.getValues(); + } + + @Override + protected void addItems(Property property, CCombo3 combo) throws Exception { + for (String item : getItems(property)) { + combo.add(item); + } + } + + @Override + protected void selectItem(Property property, CCombo3 combo) throws Exception { + combo.setText(getText(property)); + } + + @Override + protected void toPropertyEx(Property property, CCombo3 combo, int index) throws Exception { + property.setValue(getItems(property)[index]); + } + + @Override + public void setText(Property property, String text) throws Exception { + property.setValue(text); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagValueCompleter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagValueCompleter.java new file mode 100644 index 0000000..d3707a5 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagValueCompleter.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import com.android.ide.eclipse.adt.AdtUtils; + +import org.eclipse.jface.fieldassist.ContentProposal; +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; + +import java.util.ArrayList; +import java.util.List; + +/** Resource value completion for the given property */ +class FlagValueCompleter implements IContentProposalProvider { + protected final XmlProperty mProperty; + private String[] mValues; + + FlagValueCompleter(XmlProperty property, String[] values) { + mProperty = property; + mValues = values; + } + + @Override + public IContentProposal[] getProposals(String contents, int position) { + List<IContentProposal> proposals = new ArrayList<IContentProposal>(mValues.length); + String prefix = contents; + int flagStart = prefix.lastIndexOf('|'); + String prepend = null; + if (flagStart != -1) { + prepend = prefix.substring(0, flagStart + 1); + prefix = prefix.substring(flagStart + 1).trim(); + } + + boolean exactMatch = false; + for (String value : mValues) { + if (prefix.equals(value)) { + exactMatch = true; + proposals.add(new ContentProposal(contents)); + + break; + } + } + + if (exactMatch) { + prepend = contents + '|'; + prefix = ""; + } + + for (String value : mValues) { + if (AdtUtils.startsWithIgnoreCase(value, prefix)) { + if (prepend != null && prepend.contains(value)) { + continue; + } + String match; + if (prepend != null) { + match = prepend + value; + } else { + match = value; + } + proposals.add(new ContentProposal(match)); + } + } + + return proposals.toArray(new IContentProposal[proposals.size()]); + } +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagXmlPropertyDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagXmlPropertyDialog.java new file mode 100644 index 0000000..0276b6c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagXmlPropertyDialog.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import com.android.annotations.NonNull; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.google.common.base.Splitter; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; +import org.eclipse.wb.internal.core.utils.execution.RunnableEx; +import org.eclipse.wb.internal.core.utils.ui.dialogs.ResizableDialog; + +import java.util.ArrayList; +import java.util.List; + +class FlagXmlPropertyDialog extends ResizableDialog +implements IStructuredContentProvider, ICheckStateListener, SelectionListener, KeyListener { + private final String mTitle; + private final XmlProperty mProperty; + private final String[] mFlags; + private final boolean mIsRadio; + + private Table mTable; + private CheckboxTableViewer mViewer; + + FlagXmlPropertyDialog( + @NonNull Shell parentShell, + @NonNull String title, + boolean isRadio, + @NonNull String[] flags, + @NonNull XmlProperty property) { + super(parentShell, AdtPlugin.getDefault()); + mTitle = title; + mIsRadio = isRadio; + mFlags = flags; + mProperty = property; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(mTitle); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite container = (Composite) super.createDialogArea(parent); + + mViewer = CheckboxTableViewer.newCheckList(container, + SWT.BORDER | SWT.FULL_SELECTION | SWT.HIDE_SELECTION); + mTable = mViewer.getTable(); + mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + + mViewer.setContentProvider(this); + mViewer.setInput(mFlags); + + String current = mProperty.getStringValue(); + if (current != null) { + Object[] checked = null; + if (mIsRadio) { + checked = new String[] { current }; + } else { + List<String> flags = new ArrayList<String>(); + for (String s : Splitter.on('|').omitEmptyStrings().trimResults().split(current)) { + flags.add(s); + } + checked = flags.toArray(new String[flags.size()]); + } + mViewer.setCheckedElements(checked); + } + if (mFlags.length > 0) { + mTable.setSelection(0); + } + + if (mIsRadio) { + // Enforce single-item selection + mViewer.addCheckStateListener(this); + } + mTable.addSelectionListener(this); + mTable.addKeyListener(this); + + return container; + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); + createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); + } + + @Override + protected Point getDefaultSize() { + return new Point(450, 400); + } + + @Override + protected void okPressed() { + // Apply the value + ExecutionUtils.runLog(new RunnableEx() { + @Override + public void run() throws Exception { + StringBuilder sb = new StringBuilder(30); + for (Object o : mViewer.getCheckedElements()) { + if (sb.length() > 0) { + sb.append('|'); + } + sb.append((String) o); + } + String value = sb.length() > 0 ? sb.toString() : null; + mProperty.setValue(value); + } + }); + + // close dialog + super.okPressed(); + } + + // ---- Implements IStructuredContentProvider ---- + + @Override + public Object[] getElements(Object inputElement) { + return (Object []) inputElement; + } + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + // ---- Implements ICheckStateListener ---- + + @Override + public void checkStateChanged(CheckStateChangedEvent event) { + // Try to disable other elements that conflict with this + boolean isChecked = event.getChecked(); + if (isChecked) { + Object selected = event.getElement(); + for (Object other : mViewer.getCheckedElements()) { + if (other != selected) { + mViewer.setChecked(other, false); + } + } + } else { + + } + } + + // ---- Implements SelectionListener ---- + + @Override + public void widgetSelected(SelectionEvent e) { + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + if (e.item instanceof TableItem) { + TableItem item = (TableItem) e.item; + item.setChecked(!item.getChecked()); + } + } + + // ---- Implements KeyListener ---- + + @Override + public void keyPressed(KeyEvent e) { + // Let space toggle checked state + if (e.keyCode == ' ' /* SWT.SPACE requires Eclipse 3.7 */) { + if (mTable.getSelectionCount() == 1) { + TableItem item = mTable.getSelection()[0]; + item.setChecked(!item.getChecked()); + } + } + } + + @Override + public void keyReleased(KeyEvent e) { + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyFactory.java new file mode 100644 index 0000000..bdd7c29 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyFactory.java @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import static com.android.ide.common.layout.LayoutConstants.ATTR_ID; +import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_MARGIN; +import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX; + +import com.android.annotations.Nullable; +import com.android.ide.common.api.IAttributeInfo; +import com.android.ide.common.api.IAttributeInfo.Format; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; +import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; +import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; +import com.android.tools.lint.detector.api.LintUtils; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import org.eclipse.wb.internal.core.editor.structure.property.PropertyListIntersector; +import org.eclipse.wb.internal.core.model.property.ComplexProperty; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.category.PropertyCategory; +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * The {@link PropertyFactory} creates (and caches) the set of {@link Property} + * instances applicable to a given node. It's also responsible for ordering + * these, and sometimes combining them into {@link ComplexProperty} category + * nodes. + * <p> + * TODO: For any properties that are *set* in XML, they should NOT be labeled as + * advanced (which would make them disappear) + */ +class PropertyFactory { + /** Disable cache during development only */ + private static final boolean CACHE_ENABLED = true || !LintUtils.assertionsEnabled(); + static { + if (!CACHE_ENABLED) { + System.err.println("WARNING: The property cache is disabled"); + } + } + + private static final Property[] NO_PROPERTIES = new Property[0]; + + private static final int PRIO_FIRST = -100000; + private static final int PRIO_SECOND = PRIO_FIRST + 10; + private static final int PRIO_LAST = 100000; + + private final GraphicalEditorPart mGraphicalEditorPart; + private final PropertyTable mPropertyTable; + private Map<UiViewElementNode, Property[]> mCache = + new WeakHashMap<UiViewElementNode, Property[]>(); + private UiViewElementNode mCurrentViewCookie; + + /** Sorting orders for the properties */ + public enum SortingMode { + NATURAL, + BY_ORIGIN, + ALPHABETICAL; + } + + /** The default sorting mode */ + public static final SortingMode DEFAULT_MODE = SortingMode.BY_ORIGIN; + + private SortingMode mSortMode = DEFAULT_MODE; + private SortingMode mCacheSortMode; + + PropertyFactory(GraphicalEditorPart graphicalEditorPart, PropertyTable propertyTable) { + mGraphicalEditorPart = graphicalEditorPart; + mPropertyTable = propertyTable; + } + + /** + * Get the properties for the given list of selection items. + * + * @param items the {@link CanvasViewInfo} instances to get an intersected + * property list for + * @return the properties for the given items + */ + public Property[] getProperties(List<CanvasViewInfo> items) { + mCurrentViewCookie = null; + + if (items == null || items.size() == 0) { + return NO_PROPERTIES; + } else if (items.size() == 1) { + CanvasViewInfo item = items.get(0); + mCurrentViewCookie = item.getUiViewNode(); + + return getProperties(item); + } else { + // intersect properties + PropertyListIntersector intersector = new PropertyListIntersector(); + for (CanvasViewInfo node : items) { + intersector.intersect(getProperties(node)); + } + + return intersector.getProperties(); + } + } + + private Property[] getProperties(CanvasViewInfo item) { + UiViewElementNode node = item.getUiViewNode(); + if (node == null) { + return NO_PROPERTIES; + } + + if (mCacheSortMode != mSortMode) { + mCacheSortMode = mSortMode; + mCache.clear(); + } + + Property[] properties = mCache.get(node); + if (!CACHE_ENABLED) { + properties = null; + } + if (properties == null) { + Collection<? extends Property> propertyList = getProperties(node, mPropertyTable); + if (propertyList == null) { + properties = new Property[0]; + } else { + properties = propertyList.toArray(new Property[propertyList.size()]); + } + mCache.put(node, properties); + } + return properties; + } + + + protected Collection<? extends Property> getProperties( + UiViewElementNode node, + PropertyTable propertyTable) { + ViewMetadataRepository repository = ViewMetadataRepository.get(); + ViewElementDescriptor viewDescriptor = (ViewElementDescriptor) node.getDescriptor(); + String fqcn = viewDescriptor.getFullClassName(); + Set<String> top = new HashSet<String>(repository.getTopAttributes(fqcn)); + AttributeDescriptor[] attributeDescriptors = node.getAttributeDescriptors(); + + List<XmlProperty> properties = new ArrayList<XmlProperty>(attributeDescriptors.length); + int priority = 0; + for (final AttributeDescriptor descriptor : attributeDescriptors) { + // TODO: Filter out non-public properties!! + // (They shouldn't be in the descriptors at all) + + assert !(descriptor instanceof SeparatorAttributeDescriptor); // No longer inserted + if (descriptor instanceof XmlnsAttributeDescriptor) { + continue; + } + + PropertyEditor editor = XmlPropertyEditor.INSTANCE; + IAttributeInfo info = descriptor.getAttributeInfo(); + if (info != null) { + EnumSet<Format> formats = info.getFormats(); + if (formats.contains(Format.BOOLEAN)) { + editor = BooleanXmlPropertyEditor.INSTANCE; + } else if (formats.contains(Format.ENUM)) { + editor = EnumXmlPropertyEditor.INSTANCE; + } + } + + XmlProperty property = new XmlProperty(editor, this, node, descriptor); + // Assign ids sequentially. This ensures that the properties will mostly keep their + // relative order (such as placing width before height), even though we will regroup + // some (such as properties in the same category, and the layout params etc) + priority += 10; + + PropertyCategory category = PropertyCategory.NORMAL; + String name = descriptor.getXmlLocalName(); + if (top.contains(name) || PropertyMetadata.isPreferred(name)) { + category = PropertyCategory.PREFERRED; + property.setPriority(PRIO_FIRST + priority); + } else { + property.setPriority(priority); + + // Prefer attributes defined on the specific type of this + // widget + // NOTE: This doesn't work very well for TextViews + /* IAttributeInfo attributeInfo = descriptor.getAttributeInfo(); + if (attributeInfo != null && fqcn.equals(attributeInfo.getDefinedBy())) { + category = PropertyCategory.PREFERRED; + } else*/ if (PropertyMetadata.isAdvanced(name)) { + category = PropertyCategory.ADVANCED; + } + } + if (category != null) { + property.setCategory(category); + } + properties.add(property); + } + + switch (mSortMode) { + case BY_ORIGIN: + return sortByOrigin(node, properties); + + case ALPHABETICAL: + return sortAlphabetically(node, properties); + + default: + case NATURAL: + return sortNatural(node, properties); + } + } + + protected Collection<? extends Property> sortAlphabetically( + UiViewElementNode node, + List<XmlProperty> properties) { + Collections.sort(properties, Property.ALPHABETICAL); + return properties; + } + + protected Collection<? extends Property> sortByOrigin( + UiViewElementNode node, + List<XmlProperty> properties) { + List<Property> collapsed = new ArrayList<Property>(properties.size()); + List<Property> layoutProperties = Lists.newArrayListWithExpectedSize(20); + List<Property> marginProperties = null; + List<Property> deprecatedProperties = null; + Map<String, ComplexProperty> categoryToProperty = new HashMap<String, ComplexProperty>(); + Multimap<String, Property> categoryToProperties = ArrayListMultimap.create(); + + + ViewElementDescriptor parent = (ViewElementDescriptor) properties.get(0).getDescriptor() + .getParent(); + Map<String, Integer> categoryPriorities = Maps.newHashMap(); + int nextCategoryPriority = 100; + while (parent != null) { + categoryPriorities.put(parent.getFullClassName(), nextCategoryPriority += 100); + parent = parent.getSuperClassDesc(); + } + + for (int i = 0, max = properties.size(); i < max; i++) { + XmlProperty property = properties.get(i); + + AttributeDescriptor descriptor = property.getDescriptor(); + if (descriptor.isDeprecated()) { + if (deprecatedProperties == null) { + deprecatedProperties = Lists.newArrayListWithExpectedSize(10); + } + deprecatedProperties.add(property); + continue; + } + + String firstName = descriptor.getXmlLocalName(); + if (firstName.startsWith(ATTR_LAYOUT_PREFIX)) { + if (firstName.startsWith(ATTR_LAYOUT_MARGIN)) { + if (marginProperties == null) { + marginProperties = Lists.newArrayListWithExpectedSize(5); + } + marginProperties.add(property); + } else { + layoutProperties.add(property); + } + continue; + } + + if (firstName.equals(ATTR_ID)) { + // Add id to the front (though the layout parameters will be added to + // the front of this at the end) + property.setPriority(PRIO_FIRST); + collapsed.add(property); + continue; + } + + if (property.getCategory() == PropertyCategory.PREFERRED) { + collapsed.add(property); + // Fall through: these are *duplicated* inside their defining categories! + // However, create a new instance of the property, such that the propertysheet + // doesn't see the same property instance twice (when selected, it will highlight + // both, etc.) Also, set the category to Normal such that we don't draw attention + // to it again. We want it to appear in both places such that somebody looking + // within a category will always find it there, even if for this specific + // view type it's a common attribute and replicated up at the top. + XmlProperty oldProperty = property; + property = new XmlProperty(oldProperty.getEditor(), this, node, + oldProperty.getDescriptor()); + property.setPriority(oldProperty.getPriority()); + } + + IAttributeInfo attributeInfo = descriptor.getAttributeInfo(); + if (attributeInfo != null && attributeInfo.getDefinedBy() != null) { + String category = attributeInfo.getDefinedBy(); + ComplexProperty complex = categoryToProperty.get(category); + if (complex == null) { + complex = new ComplexProperty( + category.substring(category.lastIndexOf('.') + 1), + "[]", + null /* properties */); + categoryToProperty.put(category, complex); + Integer categoryPriority = categoryPriorities.get(category); + if (categoryPriority != null) { + complex.setPriority(categoryPriority); + } else { + // Descriptor for an attribute whose definedBy does *not* + // correspond to one of the known superclasses of this widget. + // This sometimes happens; for example, a RatingBar will pull in + // an ImageView's minWidth attribute. Probably an error in the + // metadata, but deal with it gracefully here. + categoryPriorities.put(category, nextCategoryPriority += 100); + complex.setPriority(nextCategoryPriority); + } + } + categoryToProperties.put(category, property); + continue; + } else { + collapsed.add(property); + } + } + + // Update the complex properties + for (String category : categoryToProperties.keySet()) { + Collection<Property> subProperties = categoryToProperties.get(category); + if (subProperties.size() > 1) { + ComplexProperty complex = categoryToProperty.get(category); + assert complex != null : category; + Property[] subArray = new Property[subProperties.size()]; + complex.setProperties(subProperties.toArray(subArray)); + //complex.setPriority(subArray[0].getPriority()); + + collapsed.add(complex); + + boolean allAdvanced = true; + boolean isPreferred = false; + for (Property p : subProperties) { + PropertyCategory c = p.getCategory(); + if (c != PropertyCategory.ADVANCED) { + allAdvanced = false; + } + if (c == PropertyCategory.PREFERRED) { + isPreferred = true; + } + } + if (isPreferred) { + complex.setCategory(PropertyCategory.PREFERRED); + } else if (allAdvanced) { + complex.setCategory(PropertyCategory.ADVANCED); + } + } else if (subProperties.size() == 1) { + collapsed.add(subProperties.iterator().next()); + } + } + + if (layoutProperties.size() > 0 || marginProperties != null) { + if (marginProperties != null) { + XmlProperty[] m = + marginProperties.toArray(new XmlProperty[marginProperties.size()]); + Property marginProperty = new ComplexProperty( + "Margins", + "[]", + m); + layoutProperties.add(marginProperty); + marginProperty.setPriority(PRIO_LAST); + + for (XmlProperty p : m) { + p.setParent(marginProperty); + } + } + Property[] l = layoutProperties.toArray(new Property[layoutProperties.size()]); + Arrays.sort(l, Property.PRIORITY); + Property property = new ComplexProperty( + "Layout Parameters", + "[]", + l); + for (Property p : l) { + if (p instanceof XmlProperty) { + ((XmlProperty) p).setParent(property); + } + } + property.setCategory(PropertyCategory.PREFERRED); + collapsed.add(property); + property.setPriority(PRIO_SECOND); + } + + if (deprecatedProperties != null && deprecatedProperties.size() > 0) { + Property property = new ComplexProperty( + "Deprecated", + "(Deprecated Properties)", + deprecatedProperties.toArray(new Property[deprecatedProperties.size()])); + property.setPriority(PRIO_LAST); + collapsed.add(property); + } + + Collections.sort(collapsed, Property.PRIORITY); + + return collapsed; + } + + protected Collection<? extends Property> sortNatural( + UiViewElementNode node, + List<XmlProperty> properties) { + Collections.sort(properties, Property.ALPHABETICAL); + List<Property> collapsed = new ArrayList<Property>(properties.size()); + List<Property> layoutProperties = Lists.newArrayListWithExpectedSize(20); + List<Property> marginProperties = null; + List<Property> deprecatedProperties = null; + Map<String, ComplexProperty> categoryToProperty = new HashMap<String, ComplexProperty>(); + Multimap<String, Property> categoryToProperties = ArrayListMultimap.create(); + + for (int i = 0, max = properties.size(); i < max; i++) { + XmlProperty property = properties.get(i); + + AttributeDescriptor descriptor = property.getDescriptor(); + if (descriptor.isDeprecated()) { + if (deprecatedProperties == null) { + deprecatedProperties = Lists.newArrayListWithExpectedSize(10); + } + deprecatedProperties.add(property); + continue; + } + + String firstName = descriptor.getXmlLocalName(); + if (firstName.startsWith(ATTR_LAYOUT_PREFIX)) { + if (firstName.startsWith(ATTR_LAYOUT_MARGIN)) { + if (marginProperties == null) { + marginProperties = Lists.newArrayListWithExpectedSize(5); + } + marginProperties.add(property); + } else { + layoutProperties.add(property); + } + continue; + } + + if (firstName.equals(ATTR_ID)) { + // Add id to the front (though the layout parameters will be added to + // the front of this at the end) + property.setPriority(PRIO_FIRST); + collapsed.add(property); + continue; + } + + String category = PropertyMetadata.getCategory(firstName); + if (category != null) { + ComplexProperty complex = categoryToProperty.get(category); + if (complex == null) { + complex = new ComplexProperty( + category, + "[]", + null /* properties */); + categoryToProperty.put(category, complex); + complex.setPriority(property.getPriority()); + } + categoryToProperties.put(category, property); + continue; + } + + // Index of second word in the first name, so in fooBar it's 3 (index of 'B') + int firstNameIndex = firstName.length(); + for (int k = 0, kn = firstName.length(); k < kn; k++) { + if (Character.isUpperCase(firstName.charAt(k))) { + firstNameIndex = k; + break; + } + } + + // Scout forwards and see how many properties we can combine + int j = i + 1; + if (property.getCategory() != PropertyCategory.PREFERRED + && !property.getDescriptor().isDeprecated()) { + for (; j < max; j++) { + XmlProperty next = properties.get(j); + String nextName = next.getName(); + if (nextName.regionMatches(0, firstName, 0, firstNameIndex) + // Also make sure we begin the second word at the next + // character; if not, we could have something like + // scrollBar + // scrollingBehavior + && nextName.length() > firstNameIndex + && Character.isUpperCase(nextName.charAt(firstNameIndex))) { + + // Deprecated attributes, and preferred attributes, should not + // be pushed into normal clusters (preferred stay top-level + // and sort to the top, deprecated are all put in the same cluster at + // the end) + + if (next.getCategory() == PropertyCategory.PREFERRED) { + break; + } + if (next.getDescriptor().isDeprecated()) { + break; + } + + // This property should be combined with the previous + // property + } else { + break; + } + } + } + if (j - i > 1) { + // Combining multiple properties: all the properties from i + // through j inclusive + XmlProperty[] subprops = new XmlProperty[j - i]; + for (int k = i, index = 0; k < j; k++, index++) { + subprops[index] = properties.get(k); + } + Arrays.sort(subprops, Property.PRIORITY); + + // See if we can compute a LONGER base than just the first word. + // For example, if we have "lineSpacingExtra" and "lineSpacingMultiplier" + // we'd like the base to be "lineSpacing", not "line". + int common = firstNameIndex; + for (int k = firstNameIndex + 1, n = firstName.length(); k < n; k++) { + if (Character.isUpperCase(firstName.charAt(k))) { + common = k; + break; + } + } + if (common > firstNameIndex) { + for (int k = 0, n = subprops.length; k < n; k++) { + String nextName = subprops[k].getName(); + if (nextName.regionMatches(0, firstName, 0, common) + // Also make sure we begin the second word at the next + // character; if not, we could have something like + // scrollBar + // scrollingBehavior + && nextName.length() > common + && Character.isUpperCase(nextName.charAt(common))) { + // New prefix is okay + } else { + common = firstNameIndex; + break; + } + } + firstNameIndex = common; + } + + String base = firstName.substring(0, firstNameIndex); + base = DescriptorsUtils.capitalize(base); + Property complexProperty = new ComplexProperty( + base, + "[]", + subprops); + complexProperty.setPriority(subprops[0].getPriority()); + //complexProperty.setCategory(PropertyCategory.PREFERRED); + collapsed.add(complexProperty); + boolean allAdvanced = true; + boolean isPreferred = false; + for (XmlProperty p : subprops) { + p.setParent(complexProperty); + PropertyCategory c = p.getCategory(); + if (c != PropertyCategory.ADVANCED) { + allAdvanced = false; + } + if (c == PropertyCategory.PREFERRED) { + isPreferred = true; + } + } + if (isPreferred) { + complexProperty.setCategory(PropertyCategory.PREFERRED); + } else if (allAdvanced) { + complexProperty.setCategory(PropertyCategory.PREFERRED); + } + } else { + // Add the individual properties (usually 1, sometimes 2 + for (int k = i; k < j; k++) { + collapsed.add(properties.get(k)); + } + } + + i = j - 1; // -1: compensate in advance for the for-loop adding 1 + } + + // Update the complex properties + for (String category : categoryToProperties.keySet()) { + Collection<Property> subProperties = categoryToProperties.get(category); + if (subProperties.size() > 1) { + ComplexProperty complex = categoryToProperty.get(category); + assert complex != null : category; + Property[] subArray = new Property[subProperties.size()]; + complex.setProperties(subProperties.toArray(subArray)); + complex.setPriority(subArray[0].getPriority()); + collapsed.add(complex); + + boolean allAdvanced = true; + boolean isPreferred = false; + for (Property p : subProperties) { + PropertyCategory c = p.getCategory(); + if (c != PropertyCategory.ADVANCED) { + allAdvanced = false; + } + if (c == PropertyCategory.PREFERRED) { + isPreferred = true; + } + } + if (isPreferred) { + complex.setCategory(PropertyCategory.PREFERRED); + } else if (allAdvanced) { + complex.setCategory(PropertyCategory.ADVANCED); + } + } else if (subProperties.size() == 1) { + collapsed.add(subProperties.iterator().next()); + } + } + + if (layoutProperties.size() > 0 || marginProperties != null) { + if (marginProperties != null) { + XmlProperty[] m = + marginProperties.toArray(new XmlProperty[marginProperties.size()]); + Property marginProperty = new ComplexProperty( + "Margins", + "[]", + m); + layoutProperties.add(marginProperty); + marginProperty.setPriority(PRIO_LAST); + + for (XmlProperty p : m) { + p.setParent(marginProperty); + } + } + Property[] l = layoutProperties.toArray(new Property[layoutProperties.size()]); + Arrays.sort(l, Property.PRIORITY); + Property property = new ComplexProperty( + "Layout Parameters", + "[]", + l); + for (Property p : l) { + if (p instanceof XmlProperty) { + ((XmlProperty) p).setParent(property); + } + } + property.setCategory(PropertyCategory.PREFERRED); + collapsed.add(property); + property.setPriority(PRIO_SECOND); + } + + if (deprecatedProperties != null && deprecatedProperties.size() > 0) { + Property property = new ComplexProperty( + "Deprecated", + "(Deprecated Properties)", + deprecatedProperties.toArray(new Property[deprecatedProperties.size()])); + property.setPriority(PRIO_LAST); + collapsed.add(property); + } + + Collections.sort(collapsed, Property.PRIORITY); + + return collapsed; + } + + PropertyTable getPropertyTable() { + return mPropertyTable; + } + + @Nullable + GraphicalEditorPart getGraphicalEditor() { + return mGraphicalEditorPart; + } + + // HACK: This should be passed into each property instead + public Object getCurrentViewObject() { + return mCurrentViewCookie; + } + + public void setSortingMode(SortingMode sortingMode) { + mSortMode = sortingMode; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyMetadata.java new file mode 100644 index 0000000..cdf7664 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyMetadata.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import static com.android.ide.common.layout.LayoutConstants.ATTR_CONTENT_DESCRIPTION; +import static com.android.ide.common.layout.LayoutConstants.ATTR_HINT; +import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; + +import java.util.HashSet; +import java.util.Set; + +/** Extra metadata about properties not available from the descriptors (yet) */ +class PropertyMetadata { + static boolean isAdvanced(@NonNull String name) { + return sAdvanced.contains(name); + } + + static boolean isPreferred(@NonNull String name) { + return sPreferred.contains(name); + } + + @Nullable + static String getCategory(@NonNull String name) { + //return sCategories.get(name); + assert false : "Disabled to save memory since this method is not currently used."; + return null; + } + + private static final int ADVANCED_MAP_SIZE = 134; + private static final Set<String> sAdvanced = new HashSet<String>(ADVANCED_MAP_SIZE); + static { + // This metadata about which attributes are "advanced" was generated as follows: + // First, I ran the sdk/attribute_stats project with the --list argument to dump out + // *all* referenced XML attributes found in layouts, run against a bunch of + // sample Android code (development/samples, packages/apps, vendor, etc. + // + // Then I iterated over the LayoutDescriptors' ViewElementDescriptors' + // AttributeDescriptors, and basically diffed the two: any attribute descriptor name + // which was *not* found in any of the representative layouts is added here + // as an advanced property. + // + // Then I manually edited in some attributes that were referenced in the sample + // layouts but which I still consider to be advanced: + // -- nothing right now + + // I also manually *removed* some entries from the below list: + // drawableBottom (the others, drawableTop, drawableLeft and drawableRight were all + // NOT on the list so keep bottom off for symmetry) + // rating (useful when you deal with a RatingsBar component) + + + // Automatically generated, see above: + sAdvanced.add("alwaysDrawnWithCache"); + sAdvanced.add("animationCache"); + sAdvanced.add("animationDuration"); + sAdvanced.add("animationResolution"); + sAdvanced.add("baseline"); + sAdvanced.add("bufferType"); + sAdvanced.add("calendarViewShown"); + sAdvanced.add("completionHint"); + sAdvanced.add("completionHintView"); + sAdvanced.add("completionThreshold"); + sAdvanced.add("cursorVisible"); + sAdvanced.add("dateTextAppearance"); + sAdvanced.add("dial"); + sAdvanced.add("digits"); + sAdvanced.add("disableChildrenWhenDisabled"); + sAdvanced.add("disabledAlpha"); + sAdvanced.add("drawableAlpha"); + sAdvanced.add("drawableEnd"); + sAdvanced.add("drawableStart"); + sAdvanced.add("drawingCacheQuality"); + sAdvanced.add("dropDownAnchor"); + sAdvanced.add("dropDownHeight"); + sAdvanced.add("dropDownHorizontalOffset"); + sAdvanced.add("dropDownSelector"); + sAdvanced.add("dropDownVerticalOffset"); + sAdvanced.add("dropDownWidth"); + sAdvanced.add("editorExtras"); + sAdvanced.add("ems"); + sAdvanced.add("endYear"); + sAdvanced.add("eventsInterceptionEnabled"); + sAdvanced.add("fadeDuration"); + sAdvanced.add("fadeEnabled"); + sAdvanced.add("fadeOffset"); + sAdvanced.add("fadeScrollbars"); + sAdvanced.add("filterTouchesWhenObscured"); + sAdvanced.add("firstDayOfWeek"); + sAdvanced.add("flingable"); + sAdvanced.add("focusedMonthDateColor"); + sAdvanced.add("foregroundInsidePadding"); + sAdvanced.add("format"); + sAdvanced.add("gestureColor"); + sAdvanced.add("gestureStrokeAngleThreshold"); + sAdvanced.add("gestureStrokeLengthThreshold"); + sAdvanced.add("gestureStrokeSquarenessThreshold"); + sAdvanced.add("gestureStrokeType"); + sAdvanced.add("gestureStrokeWidth"); + sAdvanced.add("hand_hour"); + sAdvanced.add("hand_minute"); + sAdvanced.add("hapticFeedbackEnabled"); + sAdvanced.add("id"); + sAdvanced.add("imeActionId"); + sAdvanced.add("imeActionLabel"); + sAdvanced.add("indeterminateDrawable"); + sAdvanced.add("indeterminateDuration"); + sAdvanced.add("inputMethod"); + sAdvanced.add("interpolator"); + sAdvanced.add("isScrollContainer"); + sAdvanced.add("keepScreenOn"); + sAdvanced.add("layerType"); + sAdvanced.add("layoutDirection"); + sAdvanced.add("maxDate"); + sAdvanced.add("minDate"); + sAdvanced.add("mode"); + sAdvanced.add("numeric"); + sAdvanced.add("paddingEnd"); + sAdvanced.add("paddingStart"); + sAdvanced.add("persistentDrawingCache"); + sAdvanced.add("phoneNumber"); + sAdvanced.add("popupBackground"); + sAdvanced.add("popupPromptView"); + sAdvanced.add("privateImeOptions"); + sAdvanced.add("quickContactWindowSize"); + //sAdvanced.add("rating"); + sAdvanced.add("requiresFadingEdge"); + sAdvanced.add("rotation"); + sAdvanced.add("rotationX"); + sAdvanced.add("rotationY"); + sAdvanced.add("saveEnabled"); + sAdvanced.add("scaleX"); + sAdvanced.add("scaleY"); + sAdvanced.add("scrollX"); + sAdvanced.add("scrollY"); + sAdvanced.add("scrollbarAlwaysDrawHorizontalTrack"); + sAdvanced.add("scrollbarDefaultDelayBeforeFade"); + sAdvanced.add("scrollbarFadeDuration"); + sAdvanced.add("scrollbarSize"); + sAdvanced.add("scrollbarThumbHorizontal"); + sAdvanced.add("scrollbarThumbVertical"); + sAdvanced.add("scrollbarTrackHorizontal"); + sAdvanced.add("scrollbarTrackVertical"); + sAdvanced.add("secondaryProgress"); + sAdvanced.add("selectedDateVerticalBar"); + sAdvanced.add("selectedWeekBackgroundColor"); + sAdvanced.add("selectionDivider"); + sAdvanced.add("selectionDividerHeight"); + sAdvanced.add("showWeekNumber"); + sAdvanced.add("shownWeekCount"); + sAdvanced.add("solidColor"); + sAdvanced.add("soundEffectsEnabled"); + sAdvanced.add("spinnerMode"); + sAdvanced.add("spinnersShown"); + sAdvanced.add("startYear"); + sAdvanced.add("switchMinWidth"); + sAdvanced.add("switchPadding"); + sAdvanced.add("switchTextAppearance"); + sAdvanced.add("textColorHighlight"); + sAdvanced.add("textCursorDrawable"); + sAdvanced.add("textDirection"); + sAdvanced.add("textEditNoPasteWindowLayout"); + sAdvanced.add("textEditPasteWindowLayout"); + sAdvanced.add("textEditSideNoPasteWindowLayout"); + sAdvanced.add("textEditSidePasteWindowLayout"); + sAdvanced.add("textEditSuggestionItemLayout"); + sAdvanced.add("textIsSelectable"); + sAdvanced.add("textOff"); + sAdvanced.add("textOn"); + sAdvanced.add("textScaleX"); + sAdvanced.add("textSelectHandle"); + sAdvanced.add("textSelectHandleLeft"); + sAdvanced.add("textSelectHandleRight"); + sAdvanced.add("thumbOffset"); + sAdvanced.add("thumbTextPadding"); + sAdvanced.add("tint"); + sAdvanced.add("track"); + sAdvanced.add("transformPivotX"); + sAdvanced.add("transformPivotY"); + sAdvanced.add("translationX"); + sAdvanced.add("translationY"); + sAdvanced.add("uncertainGestureColor"); + sAdvanced.add("unfocusedMonthDateColor"); + sAdvanced.add("unselectedAlpha"); + sAdvanced.add("verticalScrollbarPosition"); + sAdvanced.add("weekDayTextAppearance"); + sAdvanced.add("weekNumberColor"); + sAdvanced.add("weekSeparatorLineColor"); + + assert sAdvanced.size() == ADVANCED_MAP_SIZE : sAdvanced.size(); + + } + + private static final int PREFERRED_MAP_SIZE = 7; + private static final Set<String> sPreferred = new HashSet<String>(PREFERRED_MAP_SIZE); + static { + // Manual registrations of attributes that should be treated as preferred if + // they are available on a widget even if they don't show up in the top 10% of + // usages (which the view metadata provides) + sPreferred.add(ATTR_TEXT); + sPreferred.add(ATTR_CONTENT_DESCRIPTION); + sPreferred.add(ATTR_HINT); + sPreferred.add("indeterminate"); + sPreferred.add("progress"); + sPreferred.add("rating"); + sPreferred.add("max"); + assert sPreferred.size() == PREFERRED_MAP_SIZE : sPreferred.size(); + } + + /* + private static final int CATEGORY_MAP_SIZE = 62; + private static final Map<String, String> sCategories = + new HashMap<String, String>(CATEGORY_MAP_SIZE); + static { + sCategories.put("requiresFadingEdge", "Scrolling"); + sCategories.put("fadingEdgeLength", "Scrolling"); + sCategories.put("scrollbarSize", "Scrolling"); + sCategories.put("scrollbarThumbVertical", "Scrolling"); + sCategories.put("scrollbarThumbHorizontal", "Scrolling"); + sCategories.put("scrollbarTrackHorizontal", "Scrolling"); + sCategories.put("scrollbarTrackVertical", "Scrolling"); + sCategories.put("scrollbarAlwaysDrawHorizontalTrack", "Scrolling"); + sCategories.put("scrollbarAlwaysDrawVerticalTrack", "Scrolling"); + sCategories.put("scrollViewStyle", "Scrolling"); + sCategories.put("scrollbars", "Scrolling"); + sCategories.put("scrollingCache", "Scrolling"); + sCategories.put("scrollHorizontally", "Scrolling"); + sCategories.put("scrollbarFadeDuration", "Scrolling"); + sCategories.put("scrollbarDefaultDelayBeforeFade", "Scrolling"); + sCategories.put("fastScrollEnabled", "Scrolling"); + sCategories.put("smoothScrollbar", "Scrolling"); + sCategories.put("isScrollContainer", "Scrolling"); + sCategories.put("fadeScrollbars", "Scrolling"); + sCategories.put("overScrollMode", "Scrolling"); + sCategories.put("overScrollHeader", "Scrolling"); + sCategories.put("overScrollFooter", "Scrolling"); + sCategories.put("verticalScrollbarPosition", "Scrolling"); + sCategories.put("fastScrollAlwaysVisible", "Scrolling"); + sCategories.put("fastScrollThumbDrawable", "Scrolling"); + sCategories.put("fastScrollPreviewBackgroundLeft", "Scrolling"); + sCategories.put("fastScrollPreviewBackgroundRight", "Scrolling"); + sCategories.put("fastScrollTrackDrawable", "Scrolling"); + sCategories.put("fastScrollOverlayPosition", "Scrolling"); + sCategories.put("horizontalScrollViewStyle", "Scrolling"); + sCategories.put("fastScrollTextColor", "Scrolling"); + sCategories.put("scrollbarSize", "Scrolling"); + sCategories.put("scrollbarSize", "Scrolling"); + sCategories.put("scrollbarSize", "Scrolling"); + sCategories.put("scrollbarSize", "Scrolling"); + sCategories.put("scrollbarSize", "Scrolling"); + + // TODO: All the styles: radioButtonStyle, ratingBarStyle, progressBarStyle, ... + + sCategories.put("focusable", "Focus"); + sCategories.put("focusableInTouchMode", "Focus"); + sCategories.put("nextFocusLeft", "Focus"); + sCategories.put("nextFocusRight", "Focus"); + sCategories.put("nextFocusUp", "Focus"); + sCategories.put("nextFocusDown", "Focus"); + sCategories.put("descendantFocusability", "Focus"); + sCategories.put("selectAllOnFocus", "Focus"); + sCategories.put("nextFocusForward", "Focus"); + sCategories.put("colorFocusedHighlight", "Focus"); + + sCategories.put("rotation", "Transforms"); + sCategories.put("scrollX", "Transforms"); + sCategories.put("scrollY", "Transforms"); + sCategories.put("rotationX", "Transforms"); + sCategories.put("rotationY", "Transforms"); + sCategories.put("transformPivotX", "Transforms"); + sCategories.put("transformPivotY", "Transforms"); + sCategories.put("translationX", "Transforms"); + sCategories.put("translationY", "Transforms"); + sCategories.put("scaleX", "Transforms"); + sCategories.put("scaleY", "Transforms"); + + sCategories.put("width", "Size"); + sCategories.put("height", "Size"); + sCategories.put("minWidth", "Size"); + sCategories.put("minHeight", "Size"); + + sCategories.put("longClickable", "Clicks"); + sCategories.put("onClick", "Clicks"); + sCategories.put("clickable", "Clicks"); + sCategories.put("hapticFeedbackEnabled", "Clicks"); + + sCategories.put("duplicateParentState", "State"); + sCategories.put("addStatesFromChildren", "State"); + + assert sCategories.size() == CATEGORY_MAP_SIZE : sCategories.size(); + } + */ + +// private static final int PRIO_CLZ_LAYOUT = 1000; +// private static final int PRIO_CLZ_TEXT = 2000; +// private static final int PRIO_CLZ_DRAWABLE = 3000; +// private static final int PRIO_CLZ_ANIMATION = 4000; +// private static final int PRIO_CLZ_FOCUS = 5000; +// +// private static final int PRIORITY_MAP_SIZE = 100; +// private static final Map<String, Integer> sPriorities = +// new HashMap<String, Integer>(PRIORITY_MAP_SIZE); +// static { +// // TODO: I should put all the properties roughly based on their original order: this +// // will correspond to the rough order they came in with +// // TODO: How can I make similar complex properties show up adjacent; e.g. min and max +// sPriorities.put("min", PRIO_CLZ_LAYOUT); +// sPriorities.put("max", PRIO_CLZ_LAYOUT); +// +// assert sPriorities.size() == PRIORITY_MAP_SIZE : sPriorities.size(); +// } + + // TODO: Emit metadata into a file +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertySheetPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertySheetPage.java new file mode 100644 index 0000000..4a33223 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertySheetPage.java @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.IconFactory; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; +import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertyFactory.SortingMode; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.part.Page; +import org.eclipse.ui.views.properties.IPropertySheetPage; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.IPropertyExceptionHandler; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Property sheet page used when the graphical layout editor is chosen + */ +public class PropertySheetPage extends Page implements IPropertySheetPage, IUiUpdateListener { + private PropertyTable mPropertyTable; + private final GraphicalEditorPart mEditor; + private PropertyFactory mPropertyFactory; + private Property mActiveProperty; + private Action mDefaultValueAction; + private Action mShowAdvancedPropertiesAction; + private Action mSortAlphaAction; + private Action mCollapseAll; + private Action mExpandAll; + private List<CanvasViewInfo> mSelection; + + private static final String EXPAND_DISABLED_ICON = "expandall-disabled"; //$NON-NLS-1$ + private static final String EXPAND_ICON = "expandall"; //$NON-NLS-1$ + private static final String DEFAULT_ICON = "properties_default"; //$NON-NLS-1$ + private static final String ADVANCED_ICON = "filter_advanced_properties"; //$NON-NLS-1$ + private static final String ALPHA_ICON = "sort_alpha"; //$NON-NLS-1$ + // TODO: goto-definition.png + + /** + * Constructs a new {@link PropertySheetPage} associated with the given + * editor + * + * @param editor the editor associated with this property sheet page + */ + public PropertySheetPage(GraphicalEditorPart editor) { + mEditor = editor; + } + + PropertyFactory getPropertyFactory() { + if (mPropertyFactory == null) { + assert mPropertyTable != null; + mPropertyFactory = new PropertyFactory(mEditor, mPropertyTable); + } + + return mPropertyFactory; + } + + @Override + public void createControl(Composite parent) { + mPropertyTable = new PropertyTable(parent, SWT.NONE); + mPropertyTable.setExceptionHandler(new IPropertyExceptionHandler() { + @Override + public void handle(Throwable e) { + AdtPlugin.log(e, null); + } + }); + mPropertyTable.setDefaultCollapsedNames(Arrays.asList( + "Deprecated", + "Layout Parameters|Margins")); + + createActions(); + setPropertyTableContextMenu(); + } + + @Override + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + if (selection instanceof TreeSelection + && mPropertyTable != null && !mPropertyTable.isDisposed()) { + TreeSelection treeSelection = (TreeSelection) selection; + + stopTrackingSelection(); + + if (treeSelection.isEmpty()) { + mSelection = Collections.emptyList(); + } else { + int selectionCount = treeSelection.size(); + List<CanvasViewInfo> newSelection = new ArrayList<CanvasViewInfo>(selectionCount); + Iterator<?> iterator = treeSelection.iterator(); + while (iterator.hasNext()) { + Object next = iterator.next(); + if (next instanceof CanvasViewInfo) { + CanvasViewInfo info = (CanvasViewInfo) next; + newSelection.add(info); + } + } + mSelection = newSelection; + } + + startTrackingSelection(); + + refreshProperties(); + } + } + + @Override + public void dispose() { + stopTrackingSelection(); + super.dispose(); + } + + private void startTrackingSelection() { + if (mSelection != null && !mSelection.isEmpty()) { + for (CanvasViewInfo item : mSelection) { + UiViewElementNode node = item.getUiViewNode(); + if (node != null) { + node.addUpdateListener(this); + } + } + } + } + + private void stopTrackingSelection() { + if (mSelection != null && !mSelection.isEmpty()) { + for (CanvasViewInfo item : mSelection) { + UiViewElementNode node = item.getUiViewNode(); + if (node != null) { + node.removeUpdateListener(this); + } + } + } + mSelection = null; + } + + // Implements IUiUpdateListener + @Override + public void uiElementNodeUpdated(UiElementNode node, UiUpdateState state) { + refreshProperties(); + } + + @Override + public Control getControl() { + return mPropertyTable; + } + + @Override + public void setFocus() { + mPropertyTable.setFocus(); + } + + @Override + public void makeContributions(IMenuManager menuManager, + IToolBarManager toolBarManager, IStatusLineManager statusLineManager) { + toolBarManager.add(mShowAdvancedPropertiesAction); + toolBarManager.add(new Separator()); + toolBarManager.add(mSortAlphaAction); + toolBarManager.add(new Separator()); + toolBarManager.add(mDefaultValueAction); + toolBarManager.add(new Separator()); + toolBarManager.add(mExpandAll); + toolBarManager.add(mCollapseAll); + toolBarManager.add(new Separator()); + } + + private void createActions() { + ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); + IconFactory iconFactory = IconFactory.getInstance(); + + mExpandAll = new PropertySheetAction( + IAction.AS_PUSH_BUTTON, + "Expand All", + ACTION_EXPAND, + iconFactory.getImageDescriptor(EXPAND_ICON), + iconFactory.getImageDescriptor(EXPAND_DISABLED_ICON)); + + mCollapseAll = new PropertySheetAction( + IAction.AS_PUSH_BUTTON, + "Collapse All", + ACTION_COLLAPSE, + sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL), + sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL_DISABLED)); + + mShowAdvancedPropertiesAction = new PropertySheetAction( + IAction.AS_CHECK_BOX, + "Show Advanced Properties", + ACTION_SHOW_ADVANCED, + iconFactory.getImageDescriptor(ADVANCED_ICON), + null); + + mSortAlphaAction = new PropertySheetAction( + IAction.AS_CHECK_BOX, + "Sort Alphabetically", + ACTION_SORT_ALPHA, + iconFactory.getImageDescriptor(ALPHA_ICON), + null); + + mDefaultValueAction = new PropertySheetAction( + IAction.AS_PUSH_BUTTON, + "Restore Default Value", + ACTION_DEFAULT_VALUE, + iconFactory.getImageDescriptor(DEFAULT_ICON), + null); + + // Listen on the selection in the property sheet so we can update the + // Restore Default Value action + ISelectionChangedListener listener = new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + StructuredSelection selection = (StructuredSelection) event.getSelection(); + mActiveProperty = (Property) selection.getFirstElement(); + updateDefaultValueAction(); + } + }; + mPropertyTable.addSelectionChangedListener(listener); + } + + /** + * Updates the state of {@link #mDefaultValueAction}. + */ + private void updateDefaultValueAction() { + if (mActiveProperty != null) { + try { + mDefaultValueAction.setEnabled(mActiveProperty.isModified()); + } catch (Exception e) { + AdtPlugin.log(e, null); + } + } else { + mDefaultValueAction.setEnabled(false); + } + } + + /** + * Sets the context menu for {@link #mPropertyTable}. + */ + private void setPropertyTableContextMenu() { + final MenuManager manager = new MenuManager(); + manager.setRemoveAllWhenShown(true); + manager.addMenuListener(new IMenuListener() { + @Override + public void menuAboutToShow(IMenuManager m) { + // dispose items to avoid caching + for (MenuItem item : manager.getMenu().getItems()) { + item.dispose(); + } + // apply new items + fillContextMenu(); + } + + private void fillContextMenu() { + manager.add(mDefaultValueAction); + manager.add(mSortAlphaAction); + manager.add(mShowAdvancedPropertiesAction); + } + }); + + mPropertyTable.setMenu(manager.createContextMenu(mPropertyTable)); + } + + /** + * Shows {@link Property}'s of current objects. + */ + private void refreshProperties() { + PropertyFactory factory = getPropertyFactory(); + mPropertyTable.setInput(factory.getProperties(mSelection)); + updateDefaultValueAction(); + } + + // ---- Actions ---- + + private static final int ACTION_DEFAULT_VALUE = 1; + private static final int ACTION_SHOW_ADVANCED = 2; + private static final int ACTION_COLLAPSE = 3; + private static final int ACTION_EXPAND = 4; + private static final int ACTION_SORT_ALPHA = 5; + + private class PropertySheetAction extends Action { + private final int mAction; + + private PropertySheetAction(int style, String label, int action, + ImageDescriptor imageDesc, ImageDescriptor disabledImageDesc) { + super(label, style); + mAction = action; + setImageDescriptor(imageDesc); + if (disabledImageDesc != null) { + setDisabledImageDescriptor(disabledImageDesc); + } + setToolTipText(label); + } + + @Override + public void run() { + switch (mAction) { + case ACTION_COLLAPSE: { + mPropertyTable.collapseAll(); + break; + } + case ACTION_EXPAND: { + mPropertyTable.expandAll(); + break; + } + case ACTION_SHOW_ADVANCED: { + boolean show = mShowAdvancedPropertiesAction.isChecked(); + mPropertyTable.setShowAdvancedProperties(show); + break; + } + case ACTION_SORT_ALPHA: { + boolean isAlphabetical = mSortAlphaAction.isChecked(); + getPropertyFactory().setSortingMode( + isAlphabetical ? SortingMode.ALPHABETICAL : PropertyFactory.DEFAULT_MODE); + refreshProperties(); + break; + } + case ACTION_DEFAULT_VALUE: + try { + mActiveProperty.setValue(Property.UNKNOWN_VALUE); + } catch (Exception e) { + // Ignore warnings from setters + } + break; + default: + assert false : mAction; + } + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java new file mode 100644 index 0000000..19dc0bc --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX; +import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF; +import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF; +import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG; + +import com.android.ide.common.resources.ResourceItem; +import com.android.ide.common.resources.ResourceRepository; +import com.android.ide.eclipse.adt.AdtUtils; +import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode; +import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; +import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; +import com.android.resources.ResourceType; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.fieldassist.ContentProposal; +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Resource value completion for the given property + * <p> + * TODO: + * <ul> + * <li>also offer other values seen in the app + * <li>also offer previously set values for this property + * <li>also complete on properties + * </ul> + */ +class ResourceValueCompleter implements IContentProposalProvider { + protected final XmlProperty xmlProperty; + + ResourceValueCompleter(XmlProperty xmlProperty) { + this.xmlProperty = xmlProperty; + } + + @Override + public IContentProposal[] getProposals(String contents, int position) { + if (contents.startsWith(PREFIX_RESOURCE_REF)) { + CommonXmlEditor editor = this.xmlProperty.getXmlEditor(); + if (editor != null) { + String[] matches = computeResourceStringMatches( + editor, + this.xmlProperty.mDescriptor, contents.substring(0, position)); + List<IContentProposal> proposals = null; + if (matches != null && matches.length > 0) { + proposals = new ArrayList<IContentProposal>(matches.length); + for (String match : matches) { + proposals.add(new ContentProposal(match)); + } + return proposals.toArray(new IContentProposal[proposals.size()]); + } + } + } + + return new IContentProposal[0]; + } + + /** + * Similar to {@link UiResourceAttributeNode#computeResourceStringMatches} + * but computes complete results up front rather than dividing it up into + * smaller chunks like @{code @android:}, {@code string/}, and {@code ok}. + */ + static String[] computeResourceStringMatches(AndroidXmlEditor editor, + AttributeDescriptor attributeDescriptor, String prefix) { + List<String> results = new ArrayList<String>(200); + + // System matches: only do this if the value already matches at least @a, + // and doesn't start with something that can't possibly be @android + if (prefix.startsWith("@a") && //$NON-NLS-1$ + prefix.regionMatches(true /* ignoreCase */, 0, PREFIX_ANDROID_RESOURCE_REF, 0, + Math.min(prefix.length() - 1, PREFIX_ANDROID_RESOURCE_REF.length()))) { + AndroidTargetData data = editor.getTargetData(); + if (data != null) { + ResourceRepository repository = data.getFrameworkResources(); + addMatches(repository, prefix, true /* isSystem */, results); + } + } + + // When completing project resources skip framework resources unless + // the prefix possibly completes both, such as "@an" which can match + // both the project resource @animator as well as @android:string + if (!prefix.startsWith("@and")) { //$NON-NLS-1$ + IProject project = editor.getProject(); + if (project != null) { + // get the resource repository for this project and the system resources. + ResourceManager manager = ResourceManager.getInstance(); + ResourceRepository repository = manager.getProjectResources(project); + if (repository != null) { + // We have a style name and a repository. Find all resources that match this + // type and recreate suggestions out of them. + addMatches(repository, prefix, false /* isSystem */, results); + } + + } + } + + if (attributeDescriptor != null) { + UiResourceAttributeNode.sortAttributeChoices(attributeDescriptor, results); + } else { + Collections.sort(results); + } + + return results.toArray(new String[results.size()]); + } + + private static void addMatches(ResourceRepository repository, String prefix, boolean isSystem, + List<String> results) { + int typeStart = isSystem + ? PREFIX_ANDROID_RESOURCE_REF.length() : PREFIX_RESOURCE_REF.length(); + + for (ResourceType type : repository.getAvailableResourceTypes()) { + if (prefix.regionMatches(typeStart, type.getName(), 0, + Math.min(type.getName().length(), prefix.length() - typeStart))) { + StringBuilder sb = new StringBuilder(); + sb.append(PREFIX_RESOURCE_REF); + + if (type == ResourceType.ID && prefix.startsWith(NEW_ID_PREFIX)) { + sb.append('+'); + } + + if (isSystem) { + sb.append(ANDROID_PKG).append(':'); + } + + sb.append(type.getName()).append('/'); + String base = sb.toString(); + + int nameStart = typeStart + type.getName().length() + 1; // +1: add "/" divider + String namePrefix = + prefix.length() <= nameStart ? "" : prefix.substring(nameStart); + for (ResourceItem item : repository.getResourceItemsOfType(type)) { + String name = item.getName(); + if (AdtUtils.startsWithIgnoreCase(name, namePrefix)) { + results.add(base + name); + } + } + } + } + } +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/StringXmlPropertyDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/StringXmlPropertyDialog.java new file mode 100644 index 0000000..3fb72a9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/StringXmlPropertyDialog.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import org.eclipse.swt.widgets.Shell; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.string.StringPropertyDialog; + +class StringXmlPropertyDialog extends StringPropertyDialog { + StringXmlPropertyDialog(Shell parentShell, Property property) throws Exception { + super(parentShell, property); + } + + @Override + protected boolean isMultiLine() { + return false; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlProperty.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlProperty.java new file mode 100644 index 0000000..c846ea1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlProperty.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_MARGIN; +import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.common.api.IAttributeInfo; +import com.android.ide.common.api.IAttributeInfo.Format; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ViewHierarchy; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; + +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.views.properties.IPropertyDescriptor; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; +import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipProvider; +import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipTextProvider; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; + +import java.util.EnumSet; +import java.util.Map; + +/** + * An Android XML property + */ +class XmlProperty extends Property { + private PropertyFactory mFactory; + final AttributeDescriptor mDescriptor; + private UiViewElementNode mNode; + private Property mParent; + + XmlProperty( + @NonNull PropertyEditor editor, + @NonNull PropertyFactory factory, + @NonNull UiViewElementNode node, + @NonNull AttributeDescriptor descriptor) { + super(editor); + mFactory = factory; + mNode = node; + mDescriptor = descriptor; + } + + public PropertyFactory getFactory() { + return mFactory; + } + + public UiViewElementNode getNode() { + return mNode; + } + + public AttributeDescriptor getDescriptor() { + return mDescriptor; + } + + @Override + public String getName() { + return mDescriptor.getXmlLocalName(); + } + + @Override + public String getTitle() { + String name = mDescriptor.getXmlLocalName(); + int nameLength = name.length(); + + if (name.startsWith(ATTR_LAYOUT_PREFIX)) { + if (name.startsWith(ATTR_LAYOUT_MARGIN) + && nameLength > ATTR_LAYOUT_MARGIN.length()) { + name = name.substring(ATTR_LAYOUT_MARGIN.length()); + } else { + name = name.substring(ATTR_LAYOUT_PREFIX.length()); + } + } + + // Capitalize + name = DescriptorsUtils.capitalize(name); + + // If we're nested within a complex property, say "Line Spacing", don't + // include "Line Spacing " as a prefix for each property here + if (mParent != null) { + String parentTitle = mParent.getTitle(); + if (name.startsWith(parentTitle)) { + int parentTitleLength = parentTitle.length(); + if (parentTitleLength < nameLength) { + if (nameLength > parentTitleLength && + Character.isWhitespace(name.charAt(parentTitleLength))) { + parentTitleLength++; + } + name = name.substring(parentTitleLength); + } + } + } + + return name; + } + + @Override + public <T> T getAdapter(Class<T> adapter) { + // tooltip + if (adapter == PropertyTooltipProvider.class) { + return adapter.cast(new PropertyTooltipTextProvider() { + @Override + protected String getText(Property p) throws Exception { + if (mDescriptor instanceof IPropertyDescriptor) { + IPropertyDescriptor d = (IPropertyDescriptor) mDescriptor; + return d.getDescription(); + } + + return null; + } + }); + } else if (adapter == IContentProposalProvider.class) { + IAttributeInfo info = mDescriptor.getAttributeInfo(); + if (info != null) { + EnumSet<Format> formats = info.getFormats(); + if (formats.contains(Format.FLAG)) { + return adapter.cast(new FlagValueCompleter(this, info.getFlagValues())); + } else if (formats.contains(Format.ENUM)) { + return adapter.cast(new FlagValueCompleter(this, info.getEnumValues())); + } + } + // Fallback: complete values on resource values + return adapter.cast(new ResourceValueCompleter(this)); + } else if (adapter == ILabelProvider.class) { + return adapter.cast(new LabelProvider() { + @Override + public Image getImage(Object element) { + return AdtPlugin.getAndroidLogo(); + } + + @Override + public String getText(Object element) { + return ((IContentProposal) element).getLabel(); + } + }); + } + return super.getAdapter(adapter); + } + + @Override + public boolean isModified() throws Exception { + Object s = null; + try { + Element element = (Element) mNode.getXmlNode(); + String name = mDescriptor.getXmlLocalName(); + String uri = mDescriptor.getNamespaceUri(); + if (uri != null) { + return element.hasAttributeNS(uri, name); + } else { + return element.hasAttribute(name); + } + } catch (Exception e) { + // pass + } + return s != null && s.toString().length() > 0; + } + + public String getStringValue() { + Element element = (Element) mNode.getXmlNode(); + String name = mDescriptor.getXmlLocalName(); + String uri = mDescriptor.getNamespaceUri(); + Attr attr; + if (uri != null) { + attr = element.getAttributeNodeNS(uri, name); + } else { + attr = element.getAttributeNode(name); + } + if (attr != null) { + return attr.getValue(); + } + + Object viewObject = getFactory().getCurrentViewObject(); + if (viewObject != null) { + ViewHierarchy views = getGraphicalEditor().getCanvasControl().getViewHierarchy(); + Map<String, String> defaultProperties = views.getDefaultProperties(viewObject); + if (defaultProperties != null) { + return defaultProperties.get(name); + } + } + + return null; + } + + @Override + public Object getValue() throws Exception { + return getStringValue(); + } + + @Override + public void setValue(Object value) throws Exception { + CommonXmlEditor editor = getXmlEditor(); + if (editor == null) { + return; + } + final String attribute = mDescriptor.getXmlLocalName(); + final String xmlValue = value != null && value != UNKNOWN_VALUE ? value.toString() : null; + editor.wrapUndoEditXmlModel( + String.format("Set \"%1$s\" to \"%2$s\"", attribute, xmlValue), + new Runnable() { + @Override + public void run() { + mNode.setAttributeValue(attribute, + mDescriptor.getNamespaceUri(), xmlValue, true /*override*/); + mNode.commitDirtyAttributesToXml(); + } + }); + } + + @Override + public Property getComposite(Property[] properties) { + return XmlPropertyComposite.create(properties); + } + + @Nullable + GraphicalEditorPart getGraphicalEditor() { + return mFactory.getGraphicalEditor(); + } + + @Nullable CommonXmlEditor getXmlEditor() { + GraphicalEditorPart graphicalEditor = getGraphicalEditor(); + if (graphicalEditor != null) { + return graphicalEditor.getEditorDelegate().getEditor(); + } + + return null; + } + + public Property getParent() { + return mParent; + } + + public void setParent(Property parent) { + mParent = parent; + } + + @Override + public String toString() { + return getName() + ":" + getPriority(); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyComposite.java new file mode 100644 index 0000000..7abc91c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyComposite.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import com.google.common.base.Objects; + +import org.eclipse.wb.internal.core.model.property.Property; + +import java.util.Arrays; + +/** + * Property holding multiple instances of the same {@link XmlProperty} (but + * bound to difference objects. This is used when multiple objects are selected + * in the layout editor and the common properties are shown; editing a value + * will (via {@link #setValue(Object)}) set it on all selected objects. + * <p> + * Similar to + * org.eclipse.wb.internal.core.model.property.GenericPropertyComposite + */ +class XmlPropertyComposite extends XmlProperty { + private static final Object NO_VALUE = new Object(); + + private final XmlProperty[] mProperties; + + public XmlPropertyComposite(XmlProperty primary, XmlProperty[] properties) { + super( + primary.getEditor(), + primary.getFactory(), + primary.getNode(), + primary.getDescriptor()); + mProperties = properties; + } + + @Override + public String getTitle() { + return mProperties[0].getTitle(); + } + + @Override + public int hashCode() { + return mProperties.length; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof XmlPropertyComposite) { + XmlPropertyComposite property = (XmlPropertyComposite) obj; + return Arrays.equals(mProperties, property.mProperties); + } + + return false; + } + + @Override + public boolean isModified() throws Exception { + for (Property property : mProperties) { + if (property.isModified()) { + return true; + } + } + + return false; + } + + @Override + public Object getValue() throws Exception { + Object value = NO_VALUE; + for (Property property : mProperties) { + Object propertyValue = property.getValue(); + if (value == NO_VALUE) { + value = propertyValue; + } else if (!Objects.equal(value, propertyValue)) { + return UNKNOWN_VALUE; + } + } + + return value; + } + + @Override + public void setValue(final Object value) throws Exception { + // TBD: Wrap in ExecutionUtils.run? + for (Property property : mProperties) { + property.setValue(value); + } + } + + public static XmlPropertyComposite create(Property... properties) { + // Cast from Property into XmlProperty + XmlProperty[] xmlProperties = new XmlProperty[properties.length]; + for (int i = 0; i < properties.length; i++) { + Property property = properties[i]; + xmlProperties[i] = (XmlProperty) property; + } + + XmlPropertyComposite composite = new XmlPropertyComposite(xmlProperties[0], xmlProperties); + composite.setCategory(xmlProperties[0].getCategory()); + return composite; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyEditor.java new file mode 100644 index 0000000..628cda6 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyEditor.java @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.properties; + +import static com.android.ide.common.layout.LayoutConstants.ATTR_ID; +import static com.android.ide.eclipse.adt.AdtConstants.DOT_PNG; +import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML; + +import com.android.annotations.NonNull; +import com.android.ide.common.api.IAttributeInfo; +import com.android.ide.common.api.IAttributeInfo.Format; +import com.android.ide.common.rendering.api.ResourceValue; +import com.android.ide.common.resources.ResourceRepository; +import com.android.ide.common.resources.ResourceResolver; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderService; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils; +import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; +import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; +import com.android.ide.eclipse.adt.internal.ui.ReferenceChooserDialog; +import com.android.ide.eclipse.adt.internal.ui.ResourceChooser; +import com.android.ide.eclipse.adt.internal.ui.ResourcePreviewHelper; +import com.android.resources.ResourceType; +import com.google.common.collect.Maps; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.wb.draw2d.IColorConstants; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.AbstractTextPropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.presentation.ButtonPropertyEditorPresentation; +import org.eclipse.wb.internal.core.model.property.editor.presentation.PropertyEditorPresentation; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.ui.DrawUtils; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; + +import javax.imageio.ImageIO; + +/** + * Special property editor used for the {@link XmlProperty} instances which handles + * editing the XML properties, rendering defaults by looking up the actual colors and images, + */ +class XmlPropertyEditor extends AbstractTextPropertyEditor { + public static final XmlPropertyEditor INSTANCE = new XmlPropertyEditor(); + private static final int SAMPLE_SIZE = 10; + private static final int SAMPLE_MARGIN = 3; + + protected XmlPropertyEditor() { + } + + private final PropertyEditorPresentation mPresentation = + new ButtonPropertyEditorPresentation() { + @Override + protected void onClick(PropertyTable propertyTable, Property property) throws Exception { + openDialog(propertyTable, property); + } + }; + + @Override + public PropertyEditorPresentation getPresentation() { + return mPresentation; + } + + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof String) { + return (String) value; + } + return null; + } + + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + public void paint(Property property, GC gc, int x, int y, int width, int height) + throws Exception { + String text = getText(property); + if (text != null) { + ResourceValue resValue = null; + String resolvedText = null; + + // TODO: Use the constants for @, ?, @android: etc + if (text.startsWith("@") || text.startsWith("?")) { //$NON-NLS-1$ //$NON-NLS-2$ + // Yes, try to resolve it in order to show better info + XmlProperty xmlProperty = (XmlProperty) property; + ResourceResolver resolver = xmlProperty.getGraphicalEditor().getResourceResolver(); + boolean isFramework = text.startsWith("@android:") || text.startsWith("?android:"); + resValue = resolver.findResValue(text, isFramework); + while (resValue != null && resValue.getValue() != null) { + String value = resValue.getValue(); + if (value.startsWith("@") || value.startsWith("?")) { + // TODO: do I have to strip off the @ too? + isFramework = isFramework || value.startsWith("@android:") || value.startsWith("?android:");; + ResourceValue v = resolver.findResValue(text, isFramework); + if (v != null && !value.equals(v.getValue())) { + resValue = v; + } else { + break; + } + } else { + break; + } + } + } else if (text.startsWith("#") && text.matches("#\\p{XDigit}+")) { //$NON-NLS-1$ + resValue = new ResourceValue(ResourceType.COLOR, property.getName(), text, false); + } + + if (resValue != null && resValue.getValue() != null) { + String value = resValue.getValue(); + // Decide whether it's a color, an image, a nine patch etc + // and decide how to render it + if (value.startsWith("#") || value.endsWith(DOT_XML) //$NON-NLS-1$ + && value.contains("res/color")) { //$NON-NLS-1$ // TBD: File.separator? + XmlProperty xmlProperty = (XmlProperty) property; + ResourceResolver resolver = + xmlProperty.getGraphicalEditor().getResourceResolver(); + RGB rgb = ResourceHelper.resolveColor(resolver, resValue); + if (rgb != null) { + Color color = new Color(gc.getDevice(), rgb); + // draw color sample + Color oldBackground = gc.getBackground(); + Color oldForeground = gc.getForeground(); + try { + int width_c = SAMPLE_SIZE; + int height_c = SAMPLE_SIZE; + int x_c = x; + int y_c = y + (height - height_c) / 2; + // update rest bounds + int delta = SAMPLE_SIZE + SAMPLE_MARGIN; + x += delta; + width -= delta; + // fill + gc.setBackground(color); + gc.fillRectangle(x_c, y_c, width_c, height_c); + // draw line + gc.setForeground(IColorConstants.gray); + gc.drawRectangle(x_c, y_c, width_c, height_c); + } finally { + gc.setBackground(oldBackground); + gc.setForeground(oldForeground); + } + color.dispose(); + } + } else { + Image swtImage = null; + if (value.endsWith(DOT_XML) && value.contains("res/drawable")) { // TBD: Filesep? + Map<String, Image> cache = getImageCache(property); + swtImage = cache.get(value); + if (swtImage == null) { + XmlProperty xmlProperty = (XmlProperty) property; + GraphicalEditorPart graphicalEditor = xmlProperty.getGraphicalEditor(); + RenderService service = RenderService.create(graphicalEditor); + service.setSize(SAMPLE_SIZE, SAMPLE_SIZE); + BufferedImage drawable = service.renderDrawable(resValue); + if (drawable != null) { + swtImage = SwtUtils.convertToSwt(gc.getDevice(), drawable, + true /*transferAlpha*/, -1); + cache.put(value, swtImage); + } + } + } else if (value.endsWith(DOT_PNG)) { + // TODO: 9-patch handling? + //if (text.endsWith(DOT_9PNG)) { + // // 9-patch image: How do we paint this? + // URL url = new File(text).toURI().toURL(); + // NinePatch ninepatch = NinePatch.load(url, false /* ?? */); + // BufferedImage image = ninepatch.getImage(); + //} + Map<String, Image> cache = getImageCache(property); + swtImage = cache.get(value); + if (swtImage == null) { + File file = new File(value); + if (file.exists()) { + try { + BufferedImage awtImage = ImageIO.read(file); + if (awtImage != null && awtImage.getWidth() > 0 + && awtImage.getHeight() > 0) { + awtImage = ImageUtils.cropBlank(awtImage, null); + if (awtImage != null) { + // Scale image + int imageWidth = awtImage.getWidth(); + int imageHeight = awtImage.getHeight(); + int maxWidth = 3 * height; + + if (imageWidth > maxWidth || imageHeight > height) { + double scale = height / (double) imageHeight; + int scaledWidth = (int) (imageWidth * scale); + if (scaledWidth > maxWidth) { + scale = maxWidth / (double) imageWidth; + } + awtImage = ImageUtils.scale(awtImage, scale, + scale); + } + swtImage = SwtUtils.convertToSwt(gc.getDevice(), + awtImage, true /*transferAlpha*/, -1); + } + } + } catch (IOException e) { + AdtPlugin.log(e, value); + } + } + cache.put(value, swtImage); + } + + } else if (value != null) { + // It's a normal string: if different from the text, paint + // it in parentheses, e.g. + // @string/foo: Foo Bar (probably cropped) + if (!value.equals(text) && !value.equals("@null")) { //$NON-NLS-1$ + resolvedText = value; + } + } + + if (swtImage != null) { + // Make a square the size of the height + ImageData imageData = swtImage.getImageData(); + int imageWidth = imageData.width; + int imageHeight = imageData.height; + if (imageWidth > 0 && imageHeight > 0) { + gc.drawImage(swtImage, x, y + (height - imageHeight) / 2); + int delta = imageWidth + SAMPLE_MARGIN; + x += delta; + width -= delta; + } + } + } + } + + DrawUtils.drawStringCV(gc, text, x, y, width, height); + + if (resolvedText != null && resolvedText.length() > 0) { + Point size = gc.stringExtent(text); + x += size.x; + width -= size.x; + + x += SAMPLE_MARGIN; + width -= SAMPLE_MARGIN; + + if (width > 0) { + Color oldForeground = gc.getForeground(); + try { + gc.setForeground(PropertyTable.COLOR_PROPERTY_FG_DEFAULT); + DrawUtils.drawStringCV(gc, '(' + resolvedText + ')', x, y, width, height); + } finally { + gc.setForeground(oldForeground); + } + } + } + } + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + property.setValue(text); + return true; + } + + private void openDialog(PropertyTable propertyTable, Property property) throws Exception { + XmlProperty xmlProperty = (XmlProperty) property; + IAttributeInfo attributeInfo = xmlProperty.getDescriptor().getAttributeInfo(); + + boolean isId = xmlProperty.getDescriptor().getXmlLocalName().equals(ATTR_ID); + if (isId) { + // When editing the id attribute, don't offer a resource chooser: usually + // you want to enter a *new* id here + attributeInfo = null; + } + + boolean referenceAllowed = false; + if (attributeInfo != null) { + EnumSet<Format> formats = attributeInfo.getFormats(); + ResourceType type = null; + List<ResourceType> types = null; + if (formats.contains(Format.FLAG)) { + FlagXmlPropertyDialog dialog = + new FlagXmlPropertyDialog(propertyTable.getShell(), + "Select Flag Values", false /* radio */, + attributeInfo.getFlagValues(), xmlProperty); + + dialog.open(); + return; + + } else if (formats.contains(Format.ENUM)) { + FlagXmlPropertyDialog dialog = + new FlagXmlPropertyDialog(propertyTable.getShell(), + "Select Enum Value", true /* radio */, + attributeInfo.getEnumValues(), xmlProperty); + dialog.open(); + return; + } else { + for (Format format : formats) { + ResourceType t = format.getResourceType(); + if (t != null) { + if (type != null) { + if (types == null) { + types = new ArrayList<ResourceType>(); + types.add(type); + } + types.add(t); + } + type = t; + } else if (format == Format.REFERENCE) { + referenceAllowed = true; + } + } + } + if (types != null || referenceAllowed) { + // Multiple resource types (such as string *and* boolean): + // just use a reference chooser + GraphicalEditorPart graphicalEditor = xmlProperty.getGraphicalEditor(); + LayoutEditorDelegate delegate = graphicalEditor.getEditorDelegate(); + IProject project = delegate.getEditor().getProject(); + if (project != null) { + // get the resource repository for this project and the system resources. + ResourceRepository projectRepository = + ResourceManager.getInstance().getProjectResources(project); + Shell shell = AdtPlugin.getDisplay().getActiveShell(); + ReferenceChooserDialog dlg = new ReferenceChooserDialog( + project, + projectRepository, + shell); + dlg.setPreviewHelper(new ResourcePreviewHelper(dlg, graphicalEditor)); + + String currentValue = (String) property.getValue(); + dlg.setCurrentResource(currentValue); + + if (dlg.open() == Window.OK) { + String resource = dlg.getCurrentResource(); + if (resource != null) { + // Returns null for cancel, "" for clear and otherwise a new value + if (resource.length() > 0) { + property.setValue(resource); + } else { + property.setValue(null); + } + } + } + + return; + } + + } else if (type != null) { + // Single resource type: use a resource chooser + GraphicalEditorPart graphicalEditor = xmlProperty.getGraphicalEditor(); + String currentValue = (String) property.getValue(); + // TODO: Add validator factory? + String resource = ResourceChooser.chooseResource(graphicalEditor, + type, currentValue, null /* validator */); + // Returns null for cancel, "" for clear and otherwise a new value + if (resource != null) { + if (resource.length() > 0) { + property.setValue(resource); + } else { + property.setValue(null); + } + } + + return; + } + } + + // Fallback: Just use a plain string editor + StringXmlPropertyDialog dialog = + new StringXmlPropertyDialog(propertyTable.getShell(), property); + if (dialog.open() == Window.OK) { + // TODO: Do I need to activate? + } + } + + /** Qualified name for the per-project persistent property include-map */ + private final static QualifiedName CACHE_NAME = new QualifiedName(AdtPlugin.PLUGIN_ID, + "property-images");//$NON-NLS-1$ + + @NonNull + private static Map<String, Image> getImageCache(@NonNull Property property) { + XmlProperty xmlProperty = (XmlProperty) property; + IProject project = xmlProperty.getGraphicalEditor().getProject(); + try { + Map<String, Image> cache = (Map<String, Image>) project.getSessionProperty(CACHE_NAME); + if (cache == null) { + cache = Maps.newHashMap(); + project.setSessionProperty(CACHE_NAME, cache); + } + + return cache; + } catch (CoreException e) { + AdtPlugin.log(e, null); + return Maps.newHashMap(); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java index e5fe678..219754b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java @@ -130,7 +130,7 @@ public class UiElementNode implements IPropertySource { private Map<String, AttributeDescriptor> mCachedHiddenAttributes; /** An optional list of {@link IUiUpdateListener}. Most element nodes will not have any * listeners attached, so the list is only created on demand and can be null. */ - private ArrayList<IUiUpdateListener> mUiUpdateListeners; + private List<IUiUpdateListener> mUiUpdateListeners; /** A provider that knows how to create {@link ElementDescriptor} from unmapped XML names. * The default is to have one that creates new {@link ElementDescriptor}. */ private IUnknownDescriptorProvider mUnknownDescProvider; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java index 055d432..e2b2094 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java @@ -309,8 +309,11 @@ public class UiResourceAttributeNode extends UiTextAttributeNode { * <p> * For example, if you are editing a style attribute, it's likely that among the * resource values you would rather see @style or @android than @string. + * @param descriptor the descriptor that the resource values are being completed for, + * used to prioritize some of the resource types + * @param choices the set of string resource values */ - private static void sortAttributeChoices(AttributeDescriptor descriptor, + public static void sortAttributeChoices(AttributeDescriptor descriptor, List<String> choices) { final IAttributeInfo attributeInfo = descriptor.getAttributeInfo(); Collections.sort(choices, new Comparator<String>() { @@ -319,7 +322,7 @@ public class UiResourceAttributeNode extends UiTextAttributeNode { int compare = score(attributeInfo, s1) - score(attributeInfo, s2); if (compare == 0) { // Sort alphabetically as a fallback - compare = s1.compareTo(s2); + compare = s1.compareToIgnoreCase(s2); } return compare; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java index 54b5716..4906730 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java @@ -135,7 +135,9 @@ public class ReferenceChooserDialog extends SelectionStatusDialog { createFilteredTree(top); // setup the initial selection - setupInitialSelection(); + if (mCurrentResource != null) { + setupInitialSelection(); + } // create the "New Resource" button createNewResButtons(top); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java index 263cc2d..202e1cf 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java @@ -20,6 +20,7 @@ package com.android.ide.eclipse.adt.internal.ui; import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF; import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF; +import com.android.annotations.NonNull; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.resources.ResourceItem; import com.android.ide.common.resources.ResourceRepository; @@ -27,6 +28,7 @@ import com.android.ide.common.resources.ResourceResolver; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtUtils; import com.android.ide.eclipse.adt.internal.assetstudio.OpenCreateAssetSetWizardAction; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring; import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard; import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; @@ -770,4 +772,24 @@ public class ResourceChooser extends AbstractElementListSelectionDialog implemen updateStatus(status); } } + + /** + * Open the resource chooser for the given type, associated with the given + * editor + * + * @param graphicalEditor the editor associated with the resource to be + * chosen (used to find the associated Android target to be used + * for framework resources etc) + * @param type the resource type to be chosen + * @param currentValue the current value, or null + * @param validator a validator to be used, or null + * @return the chosen resource, null if cancelled and "" if value should be + * cleared + */ + public static String chooseResource( + @NonNull GraphicalEditorPart graphicalEditor, + @NonNull ResourceType type, + String currentValue, IInputValidator validator) { + return ResourceChooser.chooseResource(graphicalEditor, type, currentValue, validator); + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java index 1eb1b4a..177662d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java @@ -31,6 +31,28 @@ public class AdtUtilsTest extends TestCase { assertFalse(AdtUtils.endsWithIgnoreCase("foo", "fo")); } + public void testStartsWithIgnoreCase() { + assertTrue(AdtUtils.startsWithIgnoreCase("foo", "foo")); + assertTrue(AdtUtils.startsWithIgnoreCase("foo", "Foo")); + assertTrue(AdtUtils.startsWithIgnoreCase("foo", "foo")); + assertTrue(AdtUtils.startsWithIgnoreCase("barfoo", "bar")); + assertTrue(AdtUtils.startsWithIgnoreCase("BarFoo", "bar")); + assertTrue(AdtUtils.startsWithIgnoreCase("BarFoo", "bAr")); + + assertFalse(AdtUtils.startsWithIgnoreCase("bfoo", "foo")); + assertFalse(AdtUtils.startsWithIgnoreCase("fo", "foo")); + } + + public void testStartsWith() { + assertTrue(AdtUtils.startsWith("foo", 0, "foo")); + assertTrue(AdtUtils.startsWith("foo", 0, "Foo")); + assertTrue(AdtUtils.startsWith("Foo", 0, "foo")); + assertTrue(AdtUtils.startsWith("aFoo", 1, "foo")); + + assertFalse(AdtUtils.startsWith("aFoo", 0, "foo")); + assertFalse(AdtUtils.startsWith("aFoo", 2, "foo")); + } + public void testEndsWith() { assertTrue(AdtUtils.endsWith("foo", "foo")); assertTrue(AdtUtils.endsWith("foobar", "obar")); diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java index 51781be..447f6de 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java @@ -58,6 +58,44 @@ public class DescriptorsUtilsTest extends TestCase { assertEquals("The Y axis", DescriptorsUtils.prettyAttributeUiName("theYAxis")); assertEquals("The Z axis", DescriptorsUtils.prettyAttributeUiName("theZAxis")); assertEquals("The t axis", DescriptorsUtils.prettyAttributeUiName("theTAxis")); + + // Special cases for "uri" and "sdk" etc + assertEquals("Grant URI permission", + DescriptorsUtils.prettyAttributeUiName("grantUriPermission")); + assertEquals("URI permission", + DescriptorsUtils.prettyAttributeUiName("uriPermission")); + assertEquals("Min SDK version", DescriptorsUtils.prettyAttributeUiName("minSdkVersion")); + assertEquals("SDK version", DescriptorsUtils.prettyAttributeUiName("sdkVersion")); + assertEquals("IME action method", + DescriptorsUtils.prettyAttributeUiName("imeActionMethod")); + } + + public void testCapitalize() { + assertEquals("", DescriptorsUtils.capitalize("")); + + assertEquals("Max Width For View", + DescriptorsUtils.capitalize("maxWidthForView")); + + assertEquals("Layout Width", + DescriptorsUtils.capitalize("layout_width")); + + assertEquals("Axis X", DescriptorsUtils.capitalize("axisX")); + assertEquals("Axis Y", DescriptorsUtils.capitalize("axisY")); + assertEquals("Axis Z", DescriptorsUtils.capitalize("axisZ")); + assertEquals("Axis T", DescriptorsUtils.capitalize("axisT")); + + assertEquals("The X Axis", DescriptorsUtils.capitalize("theXAxis")); + assertEquals("The Y Axis", DescriptorsUtils.capitalize("theYAxis")); + assertEquals("The Z Axis", DescriptorsUtils.capitalize("theZAxis")); + assertEquals("The T Axis", DescriptorsUtils.capitalize("theTAxis")); + + // Special cases for "uri" and "sdk" etc + assertEquals("Grant URI Permission", DescriptorsUtils.capitalize("grantUriPermission")); + assertEquals("Min SDK Version", DescriptorsUtils.capitalize("minSdkVersion")); + assertEquals("IME Action Method", DescriptorsUtils.capitalize("imeActionMethod")); + assertEquals("URI Permission", DescriptorsUtils.capitalize("uriPermission")); + assertEquals("SDK Version", DescriptorsUtils.capitalize("sdkVersion")); + assertEquals("Grant IME", DescriptorsUtils.capitalize("GrantIme")); } public void testFormatTooltip() { diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java index bca7cae..891e77b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java @@ -39,6 +39,7 @@ import java.util.Set; import junit.framework.TestCase; +@SuppressWarnings("javadoc") public class CanvasViewInfoTest extends TestCase { public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) { diff --git a/eclipse/scripts/create_all_symlinks.sh b/eclipse/scripts/create_all_symlinks.sh index fcc86af..65b8abe 100755 --- a/eclipse/scripts/create_all_symlinks.sh +++ b/eclipse/scripts/create_all_symlinks.sh @@ -124,7 +124,7 @@ CP_FILES="$CP_FILES @:$BASE_PLUGIN_DEST $BASE_PLUGIN_LIBS $BASE_PLUGIN_PREBUILTS ### ADT ### ADT_DEST="sdk/eclipse/plugins/com.android.ide.eclipse.adt/libs" -ADT_LIBS="layoutlib_api lint_api lint_checks ide_common rule_api ninepatch sdkuilib assetstudio" +ADT_LIBS="layoutlib_api lint_api lint_checks ide_common rule_api ninepatch sdkuilib assetstudio propertysheet" ADT_PREBUILTS="\ prebuilt/common/kxml2/kxml2-2.3.0.jar \ prebuilts/tools/common/asm-tools/asm-4.0.jar \ diff --git a/hierarchyviewer2/app/.classpath b/hierarchyviewer2/app/.classpath index a3dc06e..51ce9f3 100644 --- a/hierarchyviewer2/app/.classpath +++ b/hierarchyviewer2/app/.classpath @@ -8,8 +8,8 @@ <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.4.0.I20080509-2000.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.4.2.M20090107-0800.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/hierarchyviewer2/app/Android.mk b/hierarchyviewer2/app/Android.mk index 0e00273..076e32b 100644 --- a/hierarchyviewer2/app/Android.mk +++ b/hierarchyviewer2/app/Android.mk @@ -25,8 +25,8 @@ LOCAL_JAVA_LIBRARIES := \ ddmuilib \ hierarchyviewerlib \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 \ + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 \ sdklib \ swtmenubar diff --git a/hierarchyviewer2/app/etc/manifest.txt b/hierarchyviewer2/app/etc/manifest.txt index 52759c9..0caa3c2 100644 --- a/hierarchyviewer2/app/etc/manifest.txt +++ b/hierarchyviewer2/app/etc/manifest.txt @@ -1,2 +1,2 @@ Main-Class: com.android.hierarchyviewer.HierarchyViewerApplication -Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar sdklib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar +Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar sdklib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar org.eclipse.equinox.common_3.6.0.v20100503.jar diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/.classpath b/hierarchyviewer2/libs/hierarchyviewerlib/.classpath index 924c4c1..105d22e 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/.classpath +++ b/hierarchyviewer2/libs/hierarchyviewerlib/.classpath @@ -5,8 +5,8 @@ <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.4.0.I20080509-2000.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.4.2.M20090107-0800.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt b/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt index ac6ab55..3805b59 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt +++ b/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt @@ -1 +1 @@ -Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar +Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar org.eclipse.equinox.common_3.6.0.v20100503.jar diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk index 3ca63dd..1afbc92 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk @@ -23,8 +23,8 @@ LOCAL_JAR_MANIFEST := ../manifest.txt LOCAL_JAVA_LIBRARIES := ddmlib \ ddmuilib \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 LOCAL_MODULE := hierarchyviewerlib diff --git a/rule_api/src/com/android/ide/common/api/IAttributeInfo.java b/rule_api/src/com/android/ide/common/api/IAttributeInfo.java index 7fd253c..997eeb4 100644 --- a/rule_api/src/com/android/ide/common/api/IAttributeInfo.java +++ b/rule_api/src/com/android/ide/common/api/IAttributeInfo.java @@ -18,6 +18,7 @@ package com.android.ide.common.api; import com.android.annotations.NonNull; import com.android.annotations.Nullable; +import com.android.resources.ResourceType; import com.google.common.annotations.Beta; import java.util.EnumSet; @@ -90,6 +91,39 @@ public interface IAttributeInfo { return EnumSet.of(this); } } + + /** Returns the corresponding resource type for this attribute info, + * or null if there is no known or corresponding resource type (such as for + * enums and flags) + * + * @return the corresponding resource type, or null + */ + @Nullable + public ResourceType getResourceType() { + switch (this) { + case STRING: + return ResourceType.STRING; + case BOOLEAN: + return ResourceType.BOOL; + case COLOR: + return ResourceType.COLOR; + case DIMENSION: + return ResourceType.DIMEN; + case FRACTION: + return ResourceType.FRACTION; + case INTEGER: + return ResourceType.INTEGER; + + // No direct corresponding resource type + case ENUM: + case FLAG: + case FLOAT: + case REFERENCE: + return null; + } + + return null; + } } /** Returns the XML Name of the attribute */ diff --git a/sdkmanager/app/.classpath b/sdkmanager/app/.classpath index 75e5a03..6ca79d9 100644 --- a/sdkmanager/app/.classpath +++ b/sdkmanager/app/.classpath @@ -9,9 +9,9 @@ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> <classpathentry combineaccessrules="false" kind="src" path="/common"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.4.0.I20080509-2000.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.4.2.M20090107-0800.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/sdkmanager/app/Android.mk b/sdkmanager/app/Android.mk index d7b630e..6431695 100644 --- a/sdkmanager/app/Android.mk +++ b/sdkmanager/app/Android.mk @@ -20,9 +20,9 @@ LOCAL_JAVA_LIBRARIES := \ sdklib \ sdkuilib \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.equinox.common_3.6.0.v20100503 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 LOCAL_MODULE := sdkmanager diff --git a/sdkmanager/app/etc/manifest.txt b/sdkmanager/app/etc/manifest.txt index bb2e8c4..12d22e5 100644 --- a/sdkmanager/app/etc/manifest.txt +++ b/sdkmanager/app/etc/manifest.txt @@ -1,2 +1,2 @@ Main-Class: com.android.sdkmanager.Main -Class-Path: androidprefs.jar common.jar sdklib.jar sdkuilib.jar swtmenubar.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar +Class-Path: androidprefs.jar common.jar sdklib.jar sdkuilib.jar swtmenubar.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar diff --git a/sdkmanager/libs/sdkuilib/.classpath b/sdkmanager/libs/sdkuilib/.classpath index 101d407..3517cf7 100644 --- a/sdkmanager/libs/sdkuilib/.classpath +++ b/sdkmanager/libs/sdkuilib/.classpath @@ -8,9 +8,9 @@ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> <classpathentry combineaccessrules="false" kind="src" path="/common"/> <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.4.0.I20080509-2000.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.4.2.M20090107-0800.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/sdkmanager/libs/sdkuilib/Android.mk b/sdkmanager/libs/sdkuilib/Android.mk index 740615e..3ce5c95 100644 --- a/sdkmanager/libs/sdkuilib/Android.mk +++ b/sdkmanager/libs/sdkuilib/Android.mk @@ -29,9 +29,9 @@ LOCAL_JAVA_LIBRARIES := \ androidprefs \ swtmenubar \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.equinox.common_3.6.0.v20100503 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 LOCAL_MODULE := sdkuilib diff --git a/sdkstats/.classpath b/sdkstats/.classpath index 58178e8..e823aa5 100644 --- a/sdkstats/.classpath +++ b/sdkstats/.classpath @@ -4,8 +4,8 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.4.0.I20080509-2000.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.4.2.M20090107-0800.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/sdkstats/src/Android.mk b/sdkstats/src/Android.mk index e95e67a..b7100b4 100644 --- a/sdkstats/src/Android.mk +++ b/sdkstats/src/Android.mk @@ -7,9 +7,9 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_JAVA_LIBRARIES := \ androidprefs \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.equinox.common_3.6.0.v20100503 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 LOCAL_MODULE := sdkstats include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/swtmenubar/Android.mk b/swtmenubar/Android.mk index 25e80da..321a78a 100644 --- a/swtmenubar/Android.mk +++ b/swtmenubar/Android.mk @@ -29,7 +29,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LIBRARIES := \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 + org.eclipse.jface_3.6.2.M20110210-1200 include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java deleted file mode 100755 index 45dacfb..0000000 --- a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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 MenuBarMode getMenuBarMode() { - return MenuBarMode.MAC_OS; - } - - public void setupMenu( - String appName, - Display display, - final IMenuBarCallback callbacks) { - - // 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/com/android/menubar/MenuBarEnhancer.java b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java index eb3e817..7ca6471 100644 --- a/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java +++ b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java @@ -191,9 +191,7 @@ public final class MenuBarEnhancer { 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$ + if ("cocoa".equals(p)) { //$NON-NLS-1$ className = "com.android.menubar.internal.MenuBarEnhancerCocoa"; //$NON-NLS-1$ } diff --git a/traceview/.classpath b/traceview/.classpath index d27c013..301f785 100644 --- a/traceview/.classpath +++ b/traceview/.classpath @@ -5,8 +5,8 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/SdkStatsService"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.4.0.I20080509-2000.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.4.2.M20090107-0800.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/traceview/src/Android.mk b/traceview/src/Android.mk index fd901f1..cd63141 100644 --- a/traceview/src/Android.mk +++ b/traceview/src/Android.mk @@ -11,9 +11,9 @@ LOCAL_JAVA_LIBRARIES := \ androidprefs \ sdkstats \ swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.equinox.common_3.6.0.v20100503 \ + org.eclipse.core.commands_3.6.0.I20100512-1500 LOCAL_MODULE := traceview include $(BUILD_HOST_JAVA_LIBRARY) |