Merge "minor update to error output of create_all_symlinks."
diff --git a/.gitignore b/.gitignore
index 0a98c64..80b6f7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,12 +11,7 @@
eclipse/final*Versions*.properties
eclipse/plugins/com.android.ide.eclipse.*/@dot*
eclipse/plugins/com.android.*/javaCompiler...args
-eclipse/plugins/com.android.ide.eclipse.adt/build.xml
-eclipse/plugins/com.android.ide.eclipse.adt.ndk/build.xml
-eclipse/plugins/com.android.ide.eclipse.adt.overlay/build.xml
-eclipse/plugins/com.android.ide.eclipse.ddms/build.xml
-eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/build.xml
-eclipse/plugins/com.android.ide.eclipse.traceview/build.xml
+eclipse/plugins/com.android.ide.eclipse.*/build.xml
eclipse/features/com.android.ide.eclipse.*/build.xml
eclipse/features/com.android.ide.eclipse.*/*.zip
/eclipse/v[0-9-]*-[0-9]*
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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
- 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_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 @@
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 @@
carlo
cf
changeset
+charset
checkbox
checkboxes
classloader
@@ -174,6 +175,7 @@
namespace
namespaces
newfound
+nexus
ninepatch
nodpi
num
@@ -205,6 +207,7 @@
programmatic
programmatically
proguard
+propertysheet
proxies
proxy
pulldown
@@ -268,6 +271,7 @@
subclassing
submenu
subregion
+superclasses
supertype
symlinks
syncs
@@ -278,6 +282,7 @@
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 @@
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
old mode 100755
new mode 100644
Binary files differ
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
old mode 100755
new mode 100644
Binary files differ
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
old mode 100755
new mode 100644
Binary files differ
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
new file mode 100644
index 0000000..5f5b078
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/filter_advanced_properties.png
Binary files differ
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
new file mode 100644
index 0000000..daac537
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/goto_definition.png
Binary files differ
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
old mode 100755
new mode 100644
Binary files differ
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
new file mode 100644
index 0000000..5696a31
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/properties_default.png
Binary files differ
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
old mode 100755
new mode 100644
Binary files differ
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
new file mode 100644
index 0000000..112e05b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/sort_alpha.png
Binary files differ
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.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 @@
// 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 @@
stopEditors();
IncludeFinder.stop();
+ DesignerPlugin.dispose();
+
mRed.dispose();
synchronized (AdtPlugin.class) {
sPlugin = null;
@@ -668,18 +684,19 @@
}
/**
- * 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 @@
// 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 @@
}
/**
+ * 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 @@
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 @@
@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.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 @@
}
/**
+ * 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.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.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 @@
// 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 @@
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 @@
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 @@
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.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 @@
/**
* 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 @@
*
* @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 @@
* @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 @@
*
* @return the parent {@link CanvasViewInfo}, which can be null
*/
+ @Nullable
public CanvasViewInfo getParent() {
return mParent;
}
@@ -162,6 +169,7 @@
*
* @return the children, never null
*/
+ @NonNull
public List<CanvasViewInfo> getChildren() {
return mChildren;
}
@@ -171,6 +179,7 @@
* 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 @@
*
* @return a non-empty list of siblings (including this), or null
*/
+ @Nullable
public List<CanvasViewInfo> getNodeSiblings() {
return mNodeSiblings;
}
@@ -218,6 +228,7 @@
* @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 @@
* @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 @@
* 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 @@
* 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 @@
*
* @return the {@link Margins} for this {@link CanvasViewInfo}
*/
+ @Nullable
public Margins getMargins() {
if (mViewInfo != null) {
int leftMargin = mViewInfo.getLeftMargin();
@@ -331,6 +347,7 @@
}
// ---- Implementation of IPropertySource
+ // TODO: Get rid of this once the old propertysheet implementation is fully gone
@Override
public Object getEditableValue() {
@@ -390,6 +407,7 @@
*
* @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 @@
*
* @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 @@
*
* @return A {@link SimpleElement} wrapping this info.
*/
- /* package */ SimpleElement toSimpleElement() {
+ @NonNull
+ SimpleElement toSimpleElement() {
UiViewElementNode uiNode = getUiViewNode();
@@ -538,6 +557,7 @@
* @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 @@
}
/** 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 @@
* @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 @@
* @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 @@
*
* @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 @@
}
/** 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 @@
* 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 @@
* @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 @@
*
* @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 @@
*
* @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.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 @@
*
* @return An immutable list of {@link SelectionItem}. Can be empty but not null.
*/
+ @NonNull
List<SelectionItem> getSelections() {
return mUnmodifiableSelection;
}
@@ -132,7 +134,12 @@
*
* @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 @@
return;
}
+ boolean changed = false;
try {
mInsideUpdateSelection = true;
@@ -211,7 +219,6 @@
return;
}
- boolean changed = false;
boolean redoLayout = false;
// Create a list of all currently selected view infos
@@ -242,6 +249,10 @@
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 @@
if (redoLayout) {
mCanvas.getEditorDelegate().recomputeLayout();
}
- if (changed) {
- redraw();
- updateActionsFromSelection();
- }
-
}
} finally {
mInsideUpdateSelection = false;
}
+
+ if (changed) {
+ redraw();
+ fireSelectionChanged();
+ updateActionsFromSelection();
+ }
}
/**
@@ -699,7 +711,7 @@
}
/** 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 @@
// 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 @@
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 @@
newChildren.add(viewInfo);
}
}
- mCanvas.getSelectionManager().selectMultiple(newChildren);
+ boolean found = nodes.size() == newChildren.size();
- return nodes.size() == newChildren.size();
+ if (found || newChildren.size() > 0) {
+ mCanvas.getSelectionManager().selectMultiple(newChildren);
+ }
+
+ 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 @@
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 @@
* @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 @@
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 @@
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 @@
}
/**
+ * 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 @@
} 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 @@
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 @@
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 @@
* <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 @@
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/lint/AddSuppressAnnotation.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java
index 6b945f8..1280bc7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java
@@ -312,7 +312,21 @@
boolean isClassDetector = issue != null && issue.getScope().contains(Scope.CLASS_FILE);
NodeFinder nodeFinder = new NodeFinder(root, offset, length);
- ASTNode coveringNode = nodeFinder.getCoveringNode();
+ ASTNode coveringNode;
+ if (offset <= 0) {
+ // Error added on the first line of a Java class: typically from a class-based
+ // detector which lacks line information. Map this to the top level class
+ // in the file instead.
+ coveringNode = root;
+ if (root.types() != null && root.types().size() > 0) {
+ Object type = root.types().get(0);
+ if (type instanceof ASTNode) {
+ coveringNode = (ASTNode) type;
+ }
+ }
+ } else {
+ coveringNode = nodeFinder.getCoveringNode();
+ }
for (ASTNode body = coveringNode; body != null; body = body.getParent()) {
if (body instanceof BodyDeclaration) {
BodyDeclaration declaration = (BodyDeclaration) body;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
index 3297756..cd077ec 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
@@ -115,7 +115,7 @@
// Use the non-namespace form of set attribute since we can't
// reference the namespace until the model has been reloaded
- mElement.setAttribute(prefix + ':' + ATTR_IGNORE, mId);
+ mElement.setAttribute(prefix + ':' + ATTR_IGNORE, ignore);
UiElementNode rootUiNode = mEditor.getUiRootNode();
if (rootUiNode != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
index 956ee9d..e9f94cc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
@@ -229,7 +229,7 @@
mRemoveAllAction = new LintViewAction("Remove All", ACTION_REMOVE_ALL,
sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL),
sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL_DISABLED));
- mRefreshAction = new LintViewAction("Refresh", ACTION_REFRESH,
+ mRefreshAction = new LintViewAction("Refresh (& Save Files)", ACTION_REFRESH,
iconFactory.getImageDescriptor(REFRESH_ICON), null);
mRemoveAllAction.setEnabled(true);
mCollapseAll = new LintViewAction("Collapse All", ACTION_COLLAPSE,
@@ -461,6 +461,11 @@
public void run() {
switch (mAction) {
case ACTION_REFRESH: {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ if (workbench != null) {
+ workbench.saveAllEditors(false /*confirm*/);
+ }
+
Job[] jobs = EclipseLintRunner.getCurrentJobs();
if (jobs.length > 0) {
EclipseLintRunner.cancelCurrentJobs(false);
@@ -471,9 +476,8 @@
}
Job job = EclipseLintRunner.startLint(resources, null,
false /*fatalOnly*/, false /*show*/);
- if (job != null) {
+ if (job != null && workbench != null) {
job.addJobChangeListener(LintViewPart.this);
- IWorkbench workbench = PlatformUI.getWorkbench();
ISharedImages sharedImages = workbench.getSharedImages();
setImageDescriptor(sharedImages.getImageDescriptor(
ISharedImages.IMG_ELCL_STOP));
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 @@
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 @@
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.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 @@
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 @@
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 @@
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 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 6e8095b..a6874ba 100755
--- a/eclipse/scripts/create_all_symlinks.sh
+++ b/eclipse/scripts/create_all_symlinks.sh
@@ -124,7 +124,7 @@
### 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 @@
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_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/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
index ad736c6..e221faa 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
@@ -950,9 +950,17 @@
if (classDetectors != null && classDetectors.size() > 0 && entries.size() > 0) {
mOuterClasses = new ArrayDeque<ClassNode>();
for (ClassEntry entry : entries) {
- ClassReader reader = new ClassReader(entry.bytes);
- ClassNode classNode = new ClassNode();
- reader.accept(classNode, 0 /* flags */);
+ ClassReader reader;
+ ClassNode classNode;
+ try {
+ reader = new ClassReader(entry.bytes);
+ classNode = new ClassNode();
+ reader.accept(classNode, 0 /* flags */);
+ } catch (Throwable t) {
+ mClient.log(null, "Error processing %1$s: broken class file?",
+ entry.path());
+ continue;
+ }
ClassNode peek;
while ((peek = mOuterClasses.peek()) != null) {
@@ -1019,9 +1027,13 @@
private void addSuperClasses(SuperclassVisitor visitor, List<ClassEntry> entries) {
for (ClassEntry entry : entries) {
- ClassReader reader = new ClassReader(entry.bytes);
- int flags = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
- reader.accept(visitor, flags);
+ try {
+ ClassReader reader = new ClassReader(entry.bytes);
+ int flags = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
+ reader.accept(visitor, flags);
+ } catch (Throwable t) {
+ mClient.log(null, "Error processing %1$s: broken class file?", entry.path());
+ }
}
}
@@ -1764,6 +1776,14 @@
this.bytes = bytes;
}
+ public String path() {
+ if (jarFile != null) {
+ return jarFile.getPath() + ':' + file.getPath();
+ } else {
+ return file.getPath();
+ }
+ }
+
@Override
public int compareTo(ClassEntry other) {
String p1 = file.getPath();
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
index 52eaaa8..05b640d 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
@@ -345,6 +345,9 @@
// each inherited method
if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) {
owner = context.getDriver().getSuperClass(owner);
+ } else if (node.getOpcode() == Opcodes.INVOKESTATIC && api == -1) {
+ // Inherit through static classes as well
+ owner = context.getDriver().getSuperClass(owner);
} else {
owner = null;
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
index dc65f26..04cb9b6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
@@ -179,7 +179,7 @@
"Use a %1$s of 0dip instead of %2$s for better performance",
dimension, size);
context.report(INEFFICIENT_WEIGHT,
- element,
+ weightChild,
context.getLocation(sizeNode != null ? sizeNode : weightChild), msg, null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
index 95fc107..e99005c 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
@@ -124,6 +124,9 @@
private static final String BOOL = "boolean"; //$NON-NLS-1$
private static final String BOOLEAN = "Boolean"; //$NON-NLS-1$
private static final String LONG = "Long"; //$NON-NLS-1$
+ private static final String CHARACTER = "Character"; //$NON-NLS-1$
+ private static final String DOUBLE = "Double"; //$NON-NLS-1$
+ private static final String FLOAT = "Float"; //$NON-NLS-1$
private static final String HASH_MAP = "HashMap"; //$NON-NLS-1$
private static final String CANVAS = "Canvas"; //$NON-NLS-1$
private static final String ON_DRAW = "onDraw"; //$NON-NLS-1$
@@ -204,11 +207,12 @@
TypeReference reference = node.astTypeReference();
typeName = reference.astParts().last().astIdentifier().astValue();
}
- if ((typeName.equals("Integer") //$NON-NLS-1$
- || typeName.equals("Boolean") //$NON-NLS-1$
- || typeName.equals("Float") //$NON-NLS-1$
- || typeName.equals("Character") //$NON-NLS-1$
- || typeName.equals("Double")) //$NON-NLS-1$
+ if ((typeName.equals(INTEGER)
+ || typeName.equals(BOOLEAN)
+ || typeName.equals(FLOAT)
+ || typeName.equals(CHARACTER)
+ || typeName.equals(LONG)
+ || typeName.equals(DOUBLE))
&& node.astTypeReference().astParts().size() == 1
&& node.astArguments().size() == 1) {
String argument = node.astArguments().first().toString();
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
index 6a6c1cc..8a611a1 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
@@ -345,7 +345,7 @@
if (exception != null) {
sb.append(exception.toString());
}
- fail(sb.toString());
+ System.err.println(sb);
}
@Override
@@ -387,7 +387,6 @@
return super.findResource(relativePath);
}
-
}
public class TestConfiguration extends Configuration {
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
index 0cde634..01ab449 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
@@ -183,6 +183,21 @@
));
}
+ public void testInheritStatic() throws Exception {
+ assertEquals(
+ "ApiCallTest5.java:16: Error: Call requires API level 11 (current min is 2): android.view.View#resolveSizeAndState\n" +
+ "ApiCallTest5.java:18: Error: Call requires API level 11 (current min is 2): android.view.View#resolveSizeAndState\n" +
+ "ApiCallTest5.java:20: Error: Call requires API level 11 (current min is 2): android.view.View#combineMeasuredStates\n" +
+ "ApiCallTest5.java:21: Error: Call requires API level 11 (current min is 2): android.view.View#combineMeasuredStates",
+
+ lintProject(
+ "apicheck/classpath=>.classpath",
+ "apicheck/minsdk2.xml=>AndroidManifest.xml",
+ "apicheck/ApiCallTest5.java.txt=>src/foo/bar/ApiCallTest5.java",
+ "apicheck/ApiCallTest5.class.data=>bin/classes/foo/bar/ApiCallTest5.class"
+ ));
+ }
+
public void testInheritLocal() throws Exception {
// Test virtual dispatch in a local class which extends some other local class (which
// in turn extends an Android API)
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
index ccbf26b..95d8b09 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
@@ -71,4 +71,14 @@
"bytecode/GetterTest.jar.data=>bin/classes.jar"
));
}
+
+ public void testTruncatedData() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "bytecode/classpath-jar=>.classpath",
+ "bytecode/GetterTest.jar.data=>bin/test/pkg/bogus.class"
+ ));
+ }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/InefficientWeightDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
index 8300cbd..ed820c6 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
@@ -60,4 +60,13 @@
"baseline_weights3.xml:2: Warning: Set android:baselineAligned=\"false\" on this element for better performance",
lintFiles("res/layout/baseline_weights3.xml"));
}
+
+ public void testSuppressed() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintFiles("res/layout/inefficient_weight2.xml"));
+ }
+
+
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
index cfcda6a..100c3f7 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
@@ -34,6 +34,7 @@
"JavaPerformanceTest.java:116: Warning: Avoid object allocations during draw operations: Use Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() which allocates a temporary Rect\n" +
"JavaPerformanceTest.java:140: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead)\n" +
"JavaPerformanceTest.java:145: Warning: Use Integer.valueOf(42) instead\n" +
+ "JavaPerformanceTest.java:146: Warning: Use Long.valueOf(42L) instead\n" +
"JavaPerformanceTest.java:147: Warning: Use Boolean.valueOf(true) instead\n" +
"JavaPerformanceTest.java:148: Warning: Use Character.valueOf('c') instead\n" +
"JavaPerformanceTest.java:149: Warning: Use Float.valueOf(1.0f) instead\n" +
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
new file mode 100644
index 0000000..ad0cd48
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
new file mode 100644
index 0000000..4be8258
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
@@ -0,0 +1,23 @@
+package test.pkg;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.view.View;
+
+public class ApiCallTest5 extends View {
+ public ApiCallTest5(Context context) {
+ super(context);
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ @TargetApi(2)
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int measuredWidth = View.resolveSizeAndState(widthMeasureSpec,
+ widthMeasureSpec, 0);
+ int measuredHeight = resolveSizeAndState(heightMeasureSpec,
+ heightMeasureSpec, 0);
+ View.combineMeasuredStates(0, 0);
+ ApiCallTest5.combineMeasuredStates(0, 0);
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
new file mode 100644
index 0000000..0e7f396
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:max="100"
+ android:paddingBottom="10dip"
+ android:paddingLeft="15dip"
+ android:paddingRight="15dip"
+ android:paddingTop="10dip"
+ android:secondaryProgress="0"
+ tools:ignore="InefficientWeight" />
+
+</LinearLayout>
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 @@
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 @@
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 @@
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 @@
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_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_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 @@
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 @@
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)