diff options
Diffstat (limited to 'sdkmanager/libs')
13 files changed, 1028 insertions, 38 deletions
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java index 5759613..2a2efe7 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java @@ -95,6 +95,10 @@ final class AddOnTarget implements IAndroidTarget { } } + public String getLocation() { + return mLocation; + } + public String getName() { return mName; } @@ -103,6 +107,10 @@ final class AddOnTarget implements IAndroidTarget { return mVendor; } + public String getFullName() { + return String.format("%1$s (%2$s)", mName, mVendor); + } + public String getDescription() { return mDescription; } @@ -140,6 +148,28 @@ final class AddOnTarget implements IAndroidTarget { return mLibraries; } + public boolean isCompatibleBaseFor(IAndroidTarget target) { + // basic test + if (target == this) { + return true; + } + + // if the receiver has no optional library, then anything with api version number >= to + // the receiver is compatible. + if (mLibraries.length == 0) { + return target.getApiVersionNumber() >= getApiVersionNumber(); + } + + // Otherwise, target is only compatible if the vendor and name are equals with the api + // number greater or equal (ie target is a newer version of this add-on). + if (target.isPlatform() == false) { + return (mVendor.equals(target.getVendor()) && mName.equals(target.getName()) && + target.getApiVersionNumber() >= getApiVersionNumber()); + } + + return false; + } + public String hashString() { return String.format(ADD_ON_FORMAT, mVendor, mName, mBasePlatform.getApiVersionNumber()); } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java index e5d45b2..0e2b109 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java @@ -22,23 +22,41 @@ package com.android.sdklib; */ public interface IAndroidTarget extends Comparable<IAndroidTarget> { + /** OS Path to the "android.jar" file. */ public static int ANDROID_JAR = 1; + /** OS Path to the "framework.aidl" file. */ public static int ANDROID_AIDL = 2; + /** OS Path to "images" folder which contains the emulator system images. */ public static int IMAGES = 3; + /** OS Path to the "samples" folder which contains sample projects. */ public static int SAMPLES = 4; + /** OS Path to the "skins" folder which contains the emulator skins. */ public static int SKINS = 5; + /** OS Path to the "templates" folder which contains the templates for new projects. */ public static int TEMPLATES = 6; + /** OS Path to the "data" folder which contains data & libraries for the SDK tools. */ public static int DATA = 7; + /** OS Path to the "attrs.xml" file. */ public static int ATTRIBUTES = 8; + /** OS Path to the "attrs_manifest.xml" file. */ public static int MANIFEST_ATTRIBUTES = 9; + /** OS Path to the "data/layoutlib.jar" library. */ public static int LAYOUT_LIB = 10; + /** OS Path to the "data/res" folder. */ public static int RESOURCES = 11; + /** OS Path to the "data/fonts" folder. */ public static int FONTS = 12; + /** OS Path to the "data/widgets.txt" file. */ public static int WIDGETS = 13; + /** OS Path to the "data/activity_actions.txt" file. */ public static int ACTIONS_ACTIVITY = 14; + /** OS Path to the "data/broadcast_actions.txt" file. */ public static int ACTIONS_BROADCAST = 15; + /** OS Path to the "data/service_actions.txt" file. */ public static int ACTIONS_SERVICE = 16; + /** OS Path to the "data/categories.txt" file. */ public static int CATEGORIES = 17; + /** OS Path to the "sources" folder. */ public static int SOURCES = 18; public interface IOptionalLibrary { @@ -48,6 +66,11 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { } /** + * Returns the target location. + */ + String getLocation(); + + /** * Returns the name of the vendor of the target. */ String getVendor(); @@ -58,6 +81,12 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { String getName(); /** + * Returns the full name of the target, possibly including vendor name. + * @return + */ + String getFullName(); + + /** * Returns the description of the target. */ String getDescription(); @@ -80,7 +109,7 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { /** * Returns the path of a platform component. * @param pathId the id representing the path to return. Any of the constants defined in the - * {@link ITargetDataProvider} interface can be used. + * {@link IAndroidTarget} interface can be used. */ String getPath(int pathId); @@ -96,6 +125,15 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { IOptionalLibrary[] getOptionalLibraries(); /** + * Returns whether the given target is compatible with the receiver. + * <p/>A target is considered compatible if applications developed for the receiver can run on + * the given target. + * + * @param target the IAndroidTarget to test. + */ + boolean isCompatibleBaseFor(IAndroidTarget target); + + /** * Returns a string able to uniquely identify a target. * Typically the target will encode information such as api level, whether it's a platform * or add-on, and if it's an add-on vendor and add-on name. diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java index 3eda37f..8cbe44a 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java @@ -21,5 +21,6 @@ package com.android.sdklib; */ public interface ISdkLog { void warning(String warningFormat, Object... args); - void error(String errorFormat, Object... args); + void error(Throwable t, String errorFormat, Object... args); + void printf(String msgFormat, Object... args); } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java index f5a1f6d..59fa81c 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java @@ -95,6 +95,10 @@ final class PlatformTarget implements IAndroidTarget { public String getName() { return mName; } + + public String getFullName() { + return mName; + } /* * (non-Javadoc) @@ -136,7 +140,17 @@ final class PlatformTarget implements IAndroidTarget { public IOptionalLibrary[] getOptionalLibraries() { return null; } + + public boolean isCompatibleBaseFor(IAndroidTarget target) { + // basic test + if (target == this) { + return true; + } + // target is compatible wit the receiver as long as its api version number is greater or + // equal. + return target.getApiVersionNumber() >= mApiVersionNumber; + } public String hashString() { return String.format(PLATFORM_HASH, mApiVersionNumber); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java index 78d1fda..ede0d86 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java @@ -64,6 +64,40 @@ public final class SdkConstants { /** Skin layout file */ public final static String FN_SKIN_LAYOUT = "layout";//$NON-NLS-1$ + /* Folder Names for Android Projects . */ + + /** Resources folder name, i.e. "res". */ + public final static String FD_RESOURCES = "res"; //$NON-NLS-1$ + /** Assets folder name, i.e. "assets" */ + public final static String FD_ASSETS = "assets"; //$NON-NLS-1$ + /** Default source folder name, i.e. "src" */ + public final static String FD_SOURCES = "src"; //$NON-NLS-1$ + /** Default native library folder name inside the project, i.e. "libs" + * While the folder inside the .apk is "lib", we call that one libs because + * that's what we use in ant for both .jar and .so and we need to make the 2 development ways + * compatible. */ + public final static String FD_NATIVE_LIBS = "libs"; //$NON-NLS-1$ + /** Native lib folder inside the APK: "lib" */ + public final static String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$ + /** Default output folder name, i.e. "bin" */ + public final static String FD_OUTPUT = "bin"; //$NON-NLS-1$ + /** Default anim resource folder name, i.e. "anim" */ + public final static String FD_ANIM = "anim"; //$NON-NLS-1$ + /** Default color resource folder name, i.e. "color" */ + public final static String FD_COLOR = "color"; //$NON-NLS-1$ + /** Default drawable resource folder name, i.e. "drawable" */ + public final static String FD_DRAWABLE = "drawable"; //$NON-NLS-1$ + /** Default layout resource folder name, i.e. "layout" */ + public final static String FD_LAYOUT = "layout"; //$NON-NLS-1$ + /** Default menu resource folder name, i.e. "menu" */ + public final static String FD_MENU = "menu"; //$NON-NLS-1$ + /** Default values resource folder name, i.e. "values" */ + public final static String FD_VALUES = "values"; //$NON-NLS-1$ + /** Default xml resource folder name, i.e. "xml" */ + public final static String FD_XML = "xml"; //$NON-NLS-1$ + /** Default raw resource folder name, i.e. "raw" */ + public final static String FD_RAW = "raw"; //$NON-NLS-1$ + /* Folder Names for the Android SDK */ /** Name of the SDK platforms folder. */ @@ -90,13 +124,12 @@ public final class SdkConstants { public final static String FD_RES = "res"; /** Name of the SDK font folder, i.e. "fonts" */ public final static String FD_FONTS = "fonts"; - /** Default values resource folder name, i.e. "values" */ - public final static String FD_VALUES = "values"; /** Name of the android sources directory */ public static final String FD_ANDROID_SOURCES = "sources"; /** Name of the addon libs folder. */ public final static String FD_ADDON_LIBS = "libs"; + /* Folder path relative to the SDK root */ /** Path of the documentation directory relative to the sdk folder. * This is an OS path, ending with a separator. */ diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java index 67b8499..b4de51a 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java @@ -36,8 +36,8 @@ import java.util.regex.Pattern; */ public final class SdkManager { - private final static String PROP_VERSION_SDK = "ro.build.version.sdk"; - private final static String PROP_VERSION_RELEASE = "ro.build.version.release"; + public final static String PROP_VERSION_SDK = "ro.build.version.sdk"; + public final static String PROP_VERSION_RELEASE = "ro.build.version.release"; private final static String ADDON_NAME = "name"; private final static String ADDON_VENDOR = "vendor"; @@ -73,7 +73,7 @@ public final class SdkManager { return manager; } catch (IllegalArgumentException e) { if (log != null) { - log.error(e.getMessage()); + log.error(e, "Error parsing the sdk."); } } @@ -188,13 +188,14 @@ public final class SdkManager { // looks like apiNumber does not parse to a number. // Ignore this platform. if (log != null) { - log.error("Ignoring platform '%1$s': %2$s is not a valid number in %3$s.", + log.error(null, + "Ignoring platform '%1$s': %2$s is not a valid number in %3$s.", platform.getName(), PROP_VERSION_SDK, SdkConstants.FN_BUILD_PROP); } } } } else if (log != null) { - log.error("Ignoring platform '%1$s': %2$s is missing.", platform.getName(), + log.error(null, "Ignoring platform '%1$s': %2$s is missing.", platform.getName(), SdkConstants.FN_BUILD_PROP); } @@ -281,7 +282,7 @@ public final class SdkManager { if (baseTarget == null) { if (log != null) { - log.error( + log.error(null, "Ignoring add-on '%1$s': Unable to find base platform with API level %2$d", addon.getName(), apiValue); } @@ -292,7 +293,7 @@ public final class SdkManager { // looks like apiNumber does not parse to a number. // Ignore this add-on. if (log != null) { - log.error( + log.error(null, "Ignoring add-on '%1$s': %2$s is not a valid number in %3$s.", addon.getName(), ADDON_API, SdkConstants.FN_BUILD_PROP); } @@ -331,7 +332,7 @@ public final class SdkManager { return target; } } else if (log != null) { - log.error("Ignoring add-on '%1$s': %2$s is missing.", addon.getName(), + log.error(null, "Ignoring add-on '%1$s': %2$s is missing.", addon.getName(), SdkConstants.FN_MANIFEST_INI); } @@ -340,7 +341,7 @@ public final class SdkManager { private void displayAddonManifestError(ISdkLog log, String addonName, String valueName) { if (log != null) { - log.error("Ignoring add-on '%1$s': '%2$s' is missing from %3$s.", + log.error(null, "Ignoring add-on '%1$s': '%2$s' is missing from %3$s.", addonName, valueName, SdkConstants.FN_MANIFEST_INI); } } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java new file mode 100644 index 0000000..1184fc2 --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.sdklib.project; + +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.ISdkLog; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.project.ProjectProperties.PropertyType; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Creates the basic files needed to get an Android project up and running. Also + * allows creation of IntelliJ project files. + * + * @hide + */ +public class ProjectCreator { + + private final static String PH_JAVA_FOLDER = "PACKAGE_PATH"; + private final static String PH_PACKAGE = "PACKAGE"; + private final static String PH_ACTIVITY_NAME = "ACTIVITY_NAME"; + + private final static String FOLDER_TESTS = "tests"; + + public enum OutputLevel { + SILENT, NORMAL, VERBOSE; + } + + private static class ProjectCreateException extends Exception { + /** default UID. This will not be serialized anyway. */ + private static final long serialVersionUID = 1L; + + ProjectCreateException(String message) { + super(message); + } + + ProjectCreateException(Throwable t, String format, Object... args) { + super(format != null ? String.format(format, args) : format, t); + } + + ProjectCreateException(String format, Object... args) { + super(String.format(format, args)); + } + } + + private final OutputLevel mLevel; + + private final ISdkLog mLog; + private final String mSdkFolder; + + public ProjectCreator(String sdkFolder, OutputLevel level, ISdkLog log) { + mSdkFolder = sdkFolder; + mLevel = level; + mLog = log; + } + + /** + * Creates a new project. + * @param folderPath the folder of the project to create. This folder must exist. + * @param projectName the name of the project. + * @param packageName the package of the project. + * @param activityName the activity of the project as it will appear in the manifest. + * @param target the project target. + * @param isTestProject whether the project to create is a test project. + */ + public void createProject(String folderPath, String projectName, + String packageName, String activityName, IAndroidTarget target, + boolean isTestProject) { + + // check project folder exists. + File projectFolder = new File(folderPath); + if (projectFolder.isDirectory() == false) { + mLog.error(null, "Folder '%s' does not exist. Aborting...", folderPath); + return; + } + + try { + // first create the project properties. + + // location of the SDK goes in localProperty + ProjectProperties localProperties = ProjectProperties.create(folderPath, + PropertyType.LOCAL); + localProperties.setProperty(ProjectProperties.PROPERTY_SDK, mSdkFolder); + localProperties.save(); + + // target goes in default properties + ProjectProperties defaultProperties = ProjectProperties.create(folderPath, + PropertyType.DEFAULT); + defaultProperties.setAndroidTarget(target); + defaultProperties.save(); + + // create the map for place-holders of values to replace in the templates + final HashMap<String, String> keywords = new HashMap<String, String>(); + + // create the required folders. + // compute src folder path + final String packagePath = + stripString(packageName.replace(".", File.separator), + File.separatorChar); + + // put this path in the place-holder map for project files that needs to list + // files manually. + keywords.put(PH_JAVA_FOLDER, packagePath); + + keywords.put(PH_PACKAGE, packageName); + if (activityName != null) { + keywords.put(PH_ACTIVITY_NAME, activityName); + } + + // create the source folder and the java package folders. + final String srcFolderPath = SdkConstants.FD_SOURCES + File.separator + packagePath; + File sourceFolder = createDirs(projectFolder, srcFolderPath); + String javaTemplate = "java_file.template"; + String activityFileName = activityName + ".java"; + if (isTestProject) { + javaTemplate = "java_tests_file.template"; + activityFileName = activityName + "Test.java"; + } + installTemplate(javaTemplate, new File(sourceFolder, activityFileName), + keywords, target); + + // create other useful folders + File resourceFodler = createDirs(projectFolder, SdkConstants.FD_RESOURCES); + createDirs(projectFolder, SdkConstants.FD_OUTPUT); + createDirs(projectFolder, SdkConstants.FD_NATIVE_LIBS); + + if (isTestProject == false) { + /* Make res files only for non test projects */ + File valueFolder = createDirs(resourceFodler, SdkConstants.FD_VALUES); + installTemplate("strings.template", new File(valueFolder, "strings.xml"), + keywords, target); + + File layoutFolder = createDirs(resourceFodler, SdkConstants.FD_LAYOUT); + installTemplate("layout.template", new File(layoutFolder, "main.xml"), + keywords, target); + } + + /* Make AndroidManifest.xml and build.xml files */ + String manifestTemplate = "AndroidManifest.template"; + if (isTestProject) { + manifestTemplate = "AndroidManifest.tests.template"; + } + + installTemplate(manifestTemplate, new File(projectFolder, "AndroidManifest.xml"), + keywords, target); + + installTemplate("build.template", new File(projectFolder, "build.xml"), keywords); + + // if this is not a test project, then we create one. + if (isTestProject == false) { + // create the test project folder. + createDirs(projectFolder, FOLDER_TESTS); + File testProjectFolder = new File(folderPath, FOLDER_TESTS); + + createProject(testProjectFolder.getAbsolutePath(), projectName, packageName, + activityName, target, true /*isTestProject*/); + } + } catch (ProjectCreateException e) { + mLog.error(e, null); + } catch (IOException e) { + mLog.error(e, null); + } + } + + /** + * Installs a new file that is based on a template file provided by a given target. + * Each match of each key from the place-holder map in the template will be replaced with its + * corresponding value in the created file. + * + * @param templateName the name of to the template file + * @param dest the path to the destination file, relative to the project + * @param placeholderMap a map of (place-holder, value) to create the file from the template. + * @param target the Target of the project that will be providing the template. + * @throws ProjectCreateException + */ + private void installTemplate(String templateName, File destFile, + Map<String, String> placeholderMap, IAndroidTarget target) + throws ProjectCreateException { + // query the target for its template directory + String templateFolder = target.getPath(IAndroidTarget.TEMPLATES); + final String sourcePath = templateFolder + File.separator + templateName; + + installFullPathTemplate(sourcePath, destFile, placeholderMap); + } + + /** + * Installs a new file that is based on a template file provided by the tools folder. + * Each match of each key from the place-holder map in the template will be replaced with its + * corresponding value in the created file. + * + * @param templateName the name of to the template file + * @param dest the path to the destination file, relative to the project + * @param placeholderMap a map of (place-holder, value) to create the file from the template. + * @throws ProjectCreateException + */ + private void installTemplate(String templateName, File destFile, + Map<String, String> placeholderMap) + throws ProjectCreateException { + // query the target for its template directory + String templateFolder = mSdkFolder + File.separator + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER; + final String sourcePath = templateFolder + File.separator + templateName; + + installFullPathTemplate(sourcePath, destFile, placeholderMap); + } + + /** + * Installs a new file that is based on a template. + * Each match of each key from the place-holder map in the template will be replaced with its + * corresponding value in the created file. + * + * @param sourcePath the full path to the source template file + * @param destFile the destination file + * @param placeholderMap a map of (place-holder, value) to create the file from the template. + * @throws ProjectCreateException + */ + private void installFullPathTemplate(String sourcePath, File destFile, + Map<String, String> placeholderMap) throws ProjectCreateException { + try { + BufferedWriter out = new BufferedWriter(new FileWriter(destFile)); + BufferedReader in = new BufferedReader(new FileReader(sourcePath)); + String line; + + while ((line = in.readLine()) != null) { + for (String key : placeholderMap.keySet()) { + line = line.replace(key, placeholderMap.get(key)); + } + + out.write(line); + out.newLine(); + } + + out.close(); + in.close(); + } catch (Exception e) { + throw new ProjectCreateException(e, "Could not access %1$s: %2$s", + destFile, e.getMessage()); + } + + println("Added file %1$s", destFile); + } + + + /** + * Prints a message unless silence is enabled. + * @param format Format for String.format + * @param args Arguments for String.format + */ + private void println(String format, Object... args) { + if (mLevel == OutputLevel.VERBOSE) { + System.out.println(String.format(format, args)); + } + } + + /** + * Creates a new folder, along with any parent folders that do not exists. + * + * @param parent the parent folder + * @param name the name of the directory to create. + * @throws ProjectCreateException + */ + private File createDirs(File parent, String name) throws ProjectCreateException { + final File newFolder = new File(parent, name); + boolean existedBefore = true; + + if (!newFolder.exists()) { + if (!newFolder.mkdirs()) { + throw new ProjectCreateException("Could not create directory: %1$s", newFolder); + } + existedBefore = false; + } + + if (newFolder.isDirectory()) { + if (!newFolder.canWrite()) { + throw new ProjectCreateException("Path is not writable: %1$s", newFolder); + } + } else { + throw new ProjectCreateException("Path is not a directory: %1$s", newFolder); + } + + if (!existedBefore) { + try { + println("Created directory %1$s", newFolder.getCanonicalPath()); + } catch (IOException e) { + throw new ProjectCreateException( + "Could not determine canonical path of created directory", e); + } + } + + return newFolder; + } + + /** + * Strips the string of beginning and trailing characters (multiple + * characters will be stripped, example stripString("..test...", '.') + * results in "test"; + * + * @param s the string to strip + * @param strip the character to strip from beginning and end + * @return the stripped string or the empty string if everything is stripped. + */ + private static String stripString(String s, char strip) { + final int sLen = s.length(); + int newStart = 0, newEnd = sLen - 1; + + while (newStart < sLen && s.charAt(newStart) == strip) { + newStart++; + } + while (newEnd >= 0 && s.charAt(newEnd) == strip) { + newEnd--; + } + + /* + * newEnd contains a char we want, and substring takes end as being + * exclusive + */ + newEnd++; + + if (newStart >= sLen || newEnd < 0) { + return ""; + } + + return s.substring(newStart, newEnd); + } +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java index c0c1fe3..473f284 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java @@ -16,6 +16,7 @@ package com.android.sdklib.project; +import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkManager; import java.io.File; @@ -32,38 +33,87 @@ import java.util.Map.Entry; public final class ProjectProperties { /** The property name for the project target */ public final static String PROPERTY_TARGET = "target"; - public final static String PROPERTY_SDK = "sdk-folder"; + public final static String PROPERTY_SDK = "sdk-location"; - private final static String PROPERTIES_FILE = "default.properties"; + public static enum PropertyType { + BUILD("build.properties", BUILD_HEADER), + DEFAULT("default.properties", DEFAULT_HEADER), + LOCAL("local.properties", LOCAL_HEADER); + + private final String mFilename; + private final String mHeader; + + PropertyType(String filename, String header) { + mFilename = filename; + mHeader = header; + } + } - private final static String PROP_HEADER = + private final static String LOCAL_HEADER = +// 1-------10--------20--------30--------40--------50--------60--------70--------80 "# This file is automatically generated by Android Tools.\n" + "# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" + - "# For customized properties when using Ant, set new values\n" + - "# in a \"build.properties\" file.\n\n"; + "# \n" + + "# This file must *NOT* be checked in Version Control Systems,\n" + + "# as it contains information specific to your local configuration.\n" + + "\n"; + + private final static String DEFAULT_HEADER = +// 1-------10--------20--------30--------40--------50--------60--------70--------80 + "# This file is automatically generated by Android Tools.\n" + + "# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" + + "# \n" + + "# This file must be checked in Version Control Systems.\n" + + "# \n" + + "# To customize properties used by the Ant build system use,\n" + + "# \"build.properties\", and override values to adapt the script to your" + + "# project structure.\n" + + "\n"; + + private final static String BUILD_HEADER = +// 1-------10--------20--------30--------40--------50--------60--------70--------80 + "# This file is used to override default values used by the Ant build system.\n" + + "# \n" + + "# This file must be checked in Version Control Systems, as it is" + + "# integral to the build system of your project.\n" + + "# \n" + + "# Use this file to change values like:\n" + + "# application-package\n:" + + "# the name of your application package as defined in the manifest.\n" + + "# Used by the 'uninstall' rule.\n"+ + "# source-folder\n:" + + "# the name of the source folder. Default is 'src'.\n" + + "# out-folder\n:" + + "# the name of the output folder. Default is 'bin'\n" + + "\n"; private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>(); static { - COMMENT_MAP.put(PROPERTY_TARGET, "# Project target.\n"); - COMMENT_MAP.put(PROPERTY_SDK, "# location of the SDK. Only used by Ant.\n"); +// 1-------10--------20--------30--------40--------50--------60--------70--------80 + COMMENT_MAP.put(PROPERTY_TARGET, + "# Project target.\n"); + COMMENT_MAP.put(PROPERTY_SDK, "# location of the SDK. This is only used by Ant\n" + + "# For customization when using a Version Control System, please read the\n" + + "# header note.\n"); } private final String mProjectFolderOsPath; private final Map<String, String> mProperties; + private final PropertyType mType; /** * Loads a project properties file and return a {@link ProjectProperties} object * containing the properties * @param projectFolderOsPath the project folder. */ - public static ProjectProperties load(String projectFolderOsPath) { + public static ProjectProperties load(String projectFolderOsPath, PropertyType type) { File projectFolder = new File(projectFolderOsPath); if (projectFolder.isDirectory()) { - File defaultFile = new File(projectFolder, PROPERTIES_FILE); + File defaultFile = new File(projectFolder, type.mFilename); if (defaultFile.isFile()) { Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */); if (map != null) { - return new ProjectProperties(projectFolderOsPath, map); + return new ProjectProperties(projectFolderOsPath, map, type); } } } @@ -71,13 +121,14 @@ public final class ProjectProperties { } /** - * Creates a new project properties file, with no properties. + * Creates a new project properties object, with no properties. * <p/>The file is not created until {@link #save()} is called. * @param projectFolderOsPath the project folder. + * @param type */ - public static ProjectProperties create(String projectFolderOsPath) { + public static ProjectProperties create(String projectFolderOsPath, PropertyType type) { // create and return a ProjectProperties with an empty map. - return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>()); + return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>(), type); } /** @@ -90,6 +141,15 @@ public final class ProjectProperties { } /** + * Sets the target property to the given {@link IAndroidTarget} object. + * @param target the Android target. + */ + public void setAndroidTarget(IAndroidTarget target) { + assert mType == PropertyType.DEFAULT; + mProperties.put(PROPERTY_TARGET, target.hashString()); + } + + /** * Returns the value of a property. * @param name the name of the property. * @return the property value or null if the property is not set. @@ -103,12 +163,12 @@ public final class ProjectProperties { * @throws IOException */ public void save() throws IOException { - File toSave = new File(mProjectFolderOsPath, PROPERTIES_FILE); + File toSave = new File(mProjectFolderOsPath, mType.mFilename); FileWriter writer = new FileWriter(toSave); // write the header - writer.write(PROP_HEADER); + writer.write(mType.mHeader); // write the properties. for (Entry<String, String> entry : mProperties.entrySet()) { @@ -128,9 +188,12 @@ public final class ProjectProperties { * Use {@link #load(String)} or {@link #create(String)} to instantiate. * @param projectFolderOsPath * @param map + * @param type */ - private ProjectProperties(String projectFolderOsPath, Map<String, String> map) { + private ProjectProperties(String projectFolderOsPath, Map<String, String> map, + PropertyType type) { mProjectFolderOsPath = projectFolderOsPath; mProperties = map; + mType = type; } } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java index a9f1b17..a28561d 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java @@ -76,9 +76,27 @@ public final class VmManager { buildVmList(sdk); } + /** + * Returns the existing VMs. + * @return a newly allocated arrays containing all the VMs. + */ public VmInfo[] getVms() { return mVmList.toArray(new VmInfo[mVmList.size()]); } + + /** + * Returns the {@link VmInfo} matching the given <var>name</var>. + * @return the matching VmInfo or <code>null</code> if none were found. + */ + public VmInfo getVm(String name) { + for (VmInfo info : mVmList) { + if (info.name.equals(name)) { + return info; + } + } + + return null; + } /** * Creates a new VM. @@ -101,7 +119,7 @@ public final class VmManager { File rootDirectory = new File(parentFolder); if (rootDirectory.isDirectory() == false) { if (log != null) { - log.error("%s does not exists.", parentFolder); + log.error(null, "%s does not exists.", parentFolder); } return; } @@ -109,7 +127,7 @@ public final class VmManager { File vmFolder = new File(parentFolder, name + ".avm"); if (vmFolder.exists()) { if (log != null) { - log.error("%s already exists.", vmFolder.getAbsolutePath()); + log.error(null, "%s already exists.", vmFolder.getAbsolutePath()); } return; } diff --git a/sdkmanager/libs/sdkuilib/.classpath b/sdkmanager/libs/sdkuilib/.classpath new file mode 100644 index 0000000..eb5af7e --- /dev/null +++ b/sdkmanager/libs/sdkuilib/.classpath @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/> + <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/sdkmanager/libs/sdkuilib/.project b/sdkmanager/libs/sdkuilib/.project new file mode 100644 index 0000000..da430c8 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>SdkUiLib</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java index ddc492e..fc951f2 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java @@ -40,6 +40,11 @@ import java.util.ArrayList; /** * The SDK target selector is a table that is added to the given parent composite. + * <p/> + * To use, create it using {@link #SdkTargetSelector(Composite, IAndroidTarget[], boolean)} then + * call {@link #setSelection(IAndroidTarget)}, {@link #setSelectionListener(SelectionListener)} + * and finally use {@link #getFirstSelected()} or {@link #getAllSelected()} to retrieve the + * selection. */ public class SdkTargetSelector { @@ -49,6 +54,14 @@ public class SdkTargetSelector { private Table mTable; private Label mDescription; + /** + * Creates a new SDK Target Selector. + * + * @param parent The parent composite where the selector will be added. + * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify. + * @param allowMultipleSelection True if more than one SDK target can be selected at the same + * time. + */ public SdkTargetSelector(Composite parent, IAndroidTarget[] targets, boolean allowMultipleSelection) { mTargets = targets; @@ -81,14 +94,25 @@ public class SdkTargetSelector { column1.setText("Vendor"); final TableColumn column2 = new TableColumn(mTable, SWT.NONE); column2.setText("API Level"); + final TableColumn column3 = new TableColumn(mTable, SWT.NONE); + column3.setText("SDK"); - adjustColumnsWidth(mTable, column0, column1, column2); + adjustColumnsWidth(mTable, column0, column1, column2, column3); setupSelectionListener(mTable); fillTable(mTable); setupTooltip(mTable); } /** + * Returns the list of known targets. + * <p/> + * This is not a copy. Callers must <em>not</em> modify this array. + */ + public IAndroidTarget[] getTargets() { + return mTargets; + } + + /** * Sets a selection listener. Set it to null to remove it. * The listener will be called <em>after</em> this table processed its selection * events so that the caller can see the updated state. @@ -107,20 +131,33 @@ public class SdkTargetSelector { /** * Sets the current target selection. + * <p/> + * If the selection is actually changed, this will invoke the selection listener + * (if any) with a null event. + * * @param target the target to be selection * @return true if the target could be selected, false otherwise. */ public boolean setSelection(IAndroidTarget target) { boolean found = false; + boolean modified = false; for (TableItem i : mTable.getItems()) { if ((IAndroidTarget) i.getData() == target) { found = true; - i.setChecked(true); - } else { + if (!i.getChecked()) { + modified = true; + i.setChecked(true); + } + } else if (i.getChecked()) { + modified = true; i.setChecked(false); } } + if (modified && mSelectionListener != null) { + mSelectionListener.widgetSelected(null); + } + return found; } @@ -166,15 +203,17 @@ public class SdkTargetSelector { private void adjustColumnsWidth(final Table table, final TableColumn column0, final TableColumn column1, - final TableColumn column2) { + final TableColumn column2, + final TableColumn column3) { // Add a listener to resize the column to the full width of the table table.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { Rectangle r = table.getClientArea(); - column0.setWidth(r.width * 3 / 10); // 30% - column1.setWidth(r.width * 5 / 10); // 50% - column2.setWidth(r.width * 2 / 10); // 20% + column0.setWidth(r.width * 30 / 100); // 30% + column1.setWidth(r.width * 45 / 100); // 45% + column2.setWidth(r.width * 15 / 100); // 15% + column3.setWidth(r.width * 10 / 100); // 10% } }); } @@ -238,6 +277,7 @@ public class SdkTargetSelector { * <li>column 0: sdk name * <li>column 1: sdk vendor * <li>column 2: sdk api name + * <li>column 3: sdk version * </ul> */ private void fillTable(final Table table) { @@ -249,6 +289,7 @@ public class SdkTargetSelector { item.setText(0, target.getName()); item.setText(1, target.getVendor()); item.setText(2, target.getApiVersionName()); + item.setText(3, Integer.toString(target.getApiVersionNumber())); } } else { table.setEnabled(false); @@ -257,6 +298,7 @@ public class SdkTargetSelector { item.setText(0, "--"); item.setText(1, "No target available"); item.setText(2, "--"); + item.setText(3, "--"); } } diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java new file mode 100644 index 0000000..dcc0b9e --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2008 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.sdkuilib; + +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.vm.VmManager.VmInfo; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; + +import java.util.ArrayList; + + +/** + * The VM selector is a table that is added to the given parent composite. + * <p/> + * To use, create it using {@link #VmSelector(Composite, VmInfo[], boolean)} then + * call {@link #setSelection(VmInfo)}, {@link #setSelectionListener(SelectionListener)} + * and finally use {@link #getFirstSelected()} or {@link #getAllSelected()} to retrieve the + * selection. + */ +public final class VmSelector { + + private VmInfo[] mVms; + private final boolean mAllowMultipleSelection; + private SelectionListener mSelectionListener; + private Table mTable; + private Label mDescription; + + /** + * Creates a new SDK Target Selector. + * + * @param parent The parent composite where the selector will be added. + * @param vms The list of vms. This is <em>not</em> copied, the caller must not modify. + * @param allowMultipleSelection True if more than one SDK target can be selected at the same + * time. + */ + public VmSelector(Composite parent, VmInfo[] vms, boolean allowMultipleSelection) { + mVms = vms; + + // Layout has 1 column + Composite group = new Composite(parent, SWT.NONE); + group.setLayout(new GridLayout()); + group.setLayoutData(new GridData(GridData.FILL_BOTH)); + group.setFont(parent.getFont()); + + mAllowMultipleSelection = allowMultipleSelection; + mTable = new Table(group, SWT.CHECK | SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER); + mTable.setHeaderVisible(true); + mTable.setLinesVisible(false); + + GridData data = new GridData(); + data.grabExcessVerticalSpace = true; + data.grabExcessHorizontalSpace = true; + data.horizontalAlignment = GridData.FILL; + data.verticalAlignment = GridData.FILL; + mTable.setLayoutData(data); + + mDescription = new Label(group, SWT.WRAP); + mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // create the table columns + final TableColumn column0 = new TableColumn(mTable, SWT.NONE); + column0.setText("VM Name"); + final TableColumn column1 = new TableColumn(mTable, SWT.NONE); + column1.setText("Target Name"); + final TableColumn column2 = new TableColumn(mTable, SWT.NONE); + column2.setText("API Level"); + final TableColumn column3 = new TableColumn(mTable, SWT.NONE); + column3.setText("SDK"); + + adjustColumnsWidth(mTable, column0, column1, column2, column3); + setupSelectionListener(mTable); + fillTable(mTable, null /* target filter */); + setupTooltip(mTable); + } + + /** + * Sets a new set of VM, with an optional filter. + * <p/>This must be called from the UI thread. + * + * @param vms The list of vms. This is <em>not</em> copied, the caller must not modify. + * @param filter An IAndroidTarget. If non-null, only VM whose target are compatible with the + * filter target will displayed an available for selection. + */ + public void setVms(VmInfo[] vms, IAndroidTarget filter) { + mVms = vms; + fillTable(mTable, filter); + } + + /** + * Returns the list of known Vms. + * <p/> + * This is not a copy. Callers must <em>not</em> modify this array. + */ + public VmInfo[] getVms() { + return mVms; + } + + /** + * Sets a selection listener. Set it to null to remove it. + * The listener will be called <em>after</em> this table processed its selection + * events so that the caller can see the updated state. + * <p/> + * The event's item contains a {@link TableItem}. + * The {@link TableItem#getData()} contains an {@link IAndroidTarget}. + * <p/> + * It is recommended that the caller uses the {@link #getFirstSelected()} and + * {@link #getAllSelected()} methods instead. + * + * @param selectionListener The new listener or null to remove it. + */ + public void setSelectionListener(SelectionListener selectionListener) { + mSelectionListener = selectionListener; + } + + /** + * Sets the current target selection. + * <p/> + * If the selection is actually changed, this will invoke the selection listener + * (if any) with a null event. + * + * @param target the target to be selection + * @return true if the target could be selected, false otherwise. + */ + public boolean setSelection(VmInfo target) { + boolean found = false; + boolean modified = false; + for (TableItem i : mTable.getItems()) { + if ((VmInfo) i.getData() == target) { + found = true; + if (!i.getChecked()) { + modified = true; + i.setChecked(true); + } + } else if (i.getChecked()) { + modified = true; + i.setChecked(false); + } + } + + if (modified && mSelectionListener != null) { + mSelectionListener.widgetSelected(null); + } + + return found; + } + + /** + * Returns all selected items. + * This is useful when the table is in multiple-selection mode. + * + * @see #getFirstSelected() + * @return An array of selected items. The list can be empty but not null. + */ + public VmInfo[] getAllSelected() { + ArrayList<IAndroidTarget> list = new ArrayList<IAndroidTarget>(); + for (TableItem i : mTable.getItems()) { + if (i.getChecked()) { + list.add((IAndroidTarget) i.getData()); + } + } + return list.toArray(new VmInfo[list.size()]); + } + + /** + * Returns the first selected item. + * This is useful when the table is in single-selection mode. + * + * @see #getAllSelected() + * @return The first selected item or null. + */ + public VmInfo getFirstSelected() { + for (TableItem i : mTable.getItems()) { + if (i.getChecked()) { + return (VmInfo) i.getData(); + } + } + return null; + } + + /** + * Adds a listener to adjust the columns width when the parent is resized. + * <p/> + * If we need something more fancy, we might want to use this: + * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co + */ + private void adjustColumnsWidth(final Table table, + final TableColumn column0, + final TableColumn column1, + final TableColumn column2, + final TableColumn column3) { + // Add a listener to resize the column to the full width of the table + table.addControlListener(new ControlAdapter() { + @Override + public void controlResized(ControlEvent e) { + Rectangle r = table.getClientArea(); + column0.setWidth(r.width * 30 / 100); // 30% + column1.setWidth(r.width * 45 / 100); // 45% + column2.setWidth(r.width * 15 / 100); // 15% + column3.setWidth(r.width * 10 / 100); // 10% + } + }); + } + + + /** + * Creates a selection listener that will check or uncheck the whole line when + * double-clicked (aka "the default selection"). + */ + private void setupSelectionListener(final Table table) { + // Add a selection listener that will check/uncheck items when they are double-clicked + table.addSelectionListener(new SelectionListener() { + /** Default selection means double-click on "most" platforms */ + public void widgetDefaultSelected(SelectionEvent e) { + if (e.item instanceof TableItem) { + TableItem i = (TableItem) e.item; + i.setChecked(!i.getChecked()); + enforceSingleSelection(i); + updateDescription(i); + } + + if (mSelectionListener != null) { + mSelectionListener.widgetDefaultSelected(e); + } + } + + public void widgetSelected(SelectionEvent e) { + if (e.item instanceof TableItem) { + TableItem i = (TableItem) e.item; + enforceSingleSelection(i); + updateDescription(i); + } + + if (mSelectionListener != null) { + mSelectionListener.widgetSelected(e); + } + } + + /** + * If we're not in multiple selection mode, uncheck all other + * items when this one is selected. + */ + private void enforceSingleSelection(TableItem item) { + if (!mAllowMultipleSelection && item.getChecked()) { + Table parentTable = item.getParent(); + for (TableItem i2 : parentTable.getItems()) { + if (i2 != item && i2.getChecked()) { + i2.setChecked(false); + } + } + } + } + }); + } + + /** + * Fills the table with all VM. + * The table columns are: + * <ul> + * <li>column 0: sdk name + * <li>column 1: sdk vendor + * <li>column 2: sdk api name + * <li>column 3: sdk version + * </ul> + */ + private void fillTable(final Table table, IAndroidTarget filter) { + table.removeAll(); + if (mVms != null && mVms.length > 0) { + table.setEnabled(true); + for (VmInfo vm : mVms) { + if (filter == null || filter.isCompatibleBaseFor(vm.getTarget())) { + TableItem item = new TableItem(table, SWT.NONE); + item.setData(vm); + item.setText(0, vm.getName()); + IAndroidTarget target = vm.getTarget(); + item.setText(1, target.getFullName()); + item.setText(2, target.getApiVersionName()); + item.setText(3, Integer.toString(target.getApiVersionNumber())); + } + } + } + + if (table.getItemCount() == 0) { + table.setEnabled(false); + TableItem item = new TableItem(table, SWT.NONE); + item.setData(null); + item.setText(0, "--"); + item.setText(1, "No VM available"); + item.setText(2, "--"); + item.setText(3, "--"); + } + } + + /** + * Sets up a tooltip that displays the current item description. + * <p/> + * Displaying a tooltip over the table looks kind of odd here. Instead we actually + * display the description in a label under the table. + */ + private void setupTooltip(final Table table) { + /* + * 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() { + public void handleEvent(Event event) { + + switch(event.type) { + case SWT.KeyDown: + case SWT.MouseExit: + case SWT.MouseDown: + return; + + case SWT.MouseHover: + updateDescription(table.getItem(new Point(event.x, event.y))); + break; + + case SWT.Selection: + if (event.item instanceof TableItem) { + updateDescription((TableItem) event.item); + } + break; + + default: + return; + } + + } + }; + + table.addListener(SWT.Dispose, listener); + table.addListener(SWT.KeyDown, listener); + table.addListener(SWT.MouseMove, listener); + table.addListener(SWT.MouseHover, listener); + } + + /** + * Updates the description label with the path of the item's VM, if any. + */ + private void updateDescription(TableItem item) { + if (item != null) { + Object data = item.getData(); + if (data instanceof VmInfo) { + String newTooltip = ((VmInfo) data).getPath(); + mDescription.setText(newTooltip == null ? "" : newTooltip); //$NON-NLS-1$ + } + } + } +} |