diff options
author | Tor Norbye <tnorbye@google.com> | 2010-11-21 21:37:20 -0800 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2010-11-23 15:51:38 -0800 |
commit | 99fd7eee15c89fd45b884842c44371326f851930 (patch) | |
tree | 3c11ac8c5545459ded392ab0a440ee245f267a1b /eclipse/plugins/com.android.ide.eclipse.adt/src/com | |
parent | 9bff2af74a7d6c4034152e76e90993160b14c458 (diff) | |
download | sdk-99fd7eee15c89fd45b884842c44371326f851930.zip sdk-99fd7eee15c89fd45b884842c44371326f851930.tar.gz sdk-99fd7eee15c89fd45b884842c44371326f851930.tar.bz2 |
Open included layout on double click
On double click, if the clicked area originates from an <include>'ed
XML file, open the included layout in the editor. Also opens
files in the @android: namespace if available, for example if you
inculde @android:layout/select_dialog_multichoice.
Change-Id: I215c411257717f7b97e7b0ee1d5498c318cdb04d
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com')
7 files changed, 224 insertions, 11 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java index a4d528c..a5d772c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java @@ -43,7 +43,7 @@ public class TableLayoutRule extends LinearLayoutRule { } @Override - public void onChildInserted(INode node, INode parent, InsertType insertType) { + public void onChildInserted(INode child, INode parent, InsertType insertType) { // Overridden to inhibit the setting of layout_width/layout_height since // it should always be match_parent } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java index 7dc9169..74e86a0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java @@ -103,6 +103,15 @@ public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput, } /** + * Returns the {@link GraphicalEditorPart} associated with this editor + * + * @return the {@link GraphicalEditorPart} associated with this editor + */ + public GraphicalEditorPart getGraphicalEditor() { + return mGraphicalEditor; + } + + /** * @return The root node of the UI element hierarchy */ @Override 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 2021533..903e030 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 @@ -47,6 +47,13 @@ public final class LayoutDescriptors implements IDescriptorProvider { */ public static final String VIEW_INCLUDE = "include"; //$NON-NLS-1$ + /** + * The attribute name of the include tag's url naming the resource to be inserted + * <p> + * <b>NOTE</b>: The layout attribute is NOT in the Android namespace! + */ + public static final String ATTR_LAYOUT = "layout"; //$NON-NLS-1$ + // Public attributes names, attributes descriptors and elements descriptors public static final String ID_ATTR = "id"; //$NON-NLS-1$ @@ -293,7 +300,7 @@ public final class LayoutDescriptors implements IDescriptorProvider { null, //elementXmlName null, //nsUri new AttributeInfo( - "layout", //$NON-NLS-1$ + ATTR_LAYOUT, new Format[] { Format.REFERENCE } ), true, //required null); //overrides 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 303c58d..cc2c489 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 @@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.ide.common.api.Rect; import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.adt.internal.editors.layout.UiElementPullParser; +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; @@ -28,6 +29,7 @@ import org.eclipse.swt.graphics.Rectangle; import org.eclipse.ui.views.properties.IPropertyDescriptor; import org.eclipse.ui.views.properties.IPropertySheetPage; import org.eclipse.ui.views.properties.IPropertySource; +import org.w3c.dom.Element; import org.w3c.dom.Node; import java.util.ArrayList; @@ -370,4 +372,33 @@ public class CanvasViewInfo implements IPropertySource { return e; } + /** + * Returns the layout url attribute value for the closest surrounding include element + * parent, or null if this {@link CanvasViewInfo} is not rendered as part of an + * include tag. + * + * @return the layout url attribute value for the surrounding include tag, or null if + * not applicable + */ + public String getIncludeUrl() { + CanvasViewInfo curr = this; + while (curr != null) { + if (curr.mUiViewNode != null) { + Node node = curr.mUiViewNode.getXmlNode(); + if (node != null && node.getNamespaceURI() == null + && node.getNodeType() == Node.ELEMENT_NODE + && LayoutDescriptors.VIEW_INCLUDE.equals(node.getNodeName())) { + // Note: the layout attribute is NOT in the Android namespace + Element element = (Element) node; + String url = element.getAttribute(LayoutDescriptors.ATTR_LAYOUT); + if (url.length() > 0) { + return url; + } + } + } + curr = curr.mParent; + } + + return null; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java index 585f354..cb947d2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java @@ -336,7 +336,13 @@ public class GestureManager { // in rapid succession. In any case, we only want to let you double click the // first button to warp to XML: if (e.button == 1) { - mCanvas.showXml(e); + // Warp to the text editor and show the corresponding XML for the + // double-clicked widget + LayoutPoint p = ControlPoint.create(mCanvas, e).toLayout(); + CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p); + if (vi != null) { + mCanvas.show(vi); + } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java index 55d296d..22a1a88 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java @@ -64,9 +64,11 @@ import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.draw2d.geometry.Rectangle; @@ -1605,6 +1607,93 @@ public class GraphicalEditorPart extends EditorPart styledText.setStyleRange(sr); } + /** + * Looks up the resource file corresponding to the given type + * + * @param type The type of resource to look up, such as {@link ResourceType#LAYOUT} + * @param name The name of the resource (not including ".xml") + * @param isFrameworkResource if true, the resource is a framework resource, otherwise + * it's a project resource + * @return the resource file defining the named resource, or null if not found + */ + public IPath findResourceFile(ResourceType type, String name, boolean isFrameworkResource) { + // FIXME: This code does not handle theme value resolution. + // There is code to handle this, but it's in layoutlib; we should + // expose that and use it here. + + Map<String, Map<String, IResourceValue>> map; + map = isFrameworkResource ? mConfiguredFrameworkRes : mConfiguredProjectRes; + if (map == null) { + // Not yet configured + return null; + } + + Map<String, IResourceValue> layoutMap = map.get(type.getName()); + if (layoutMap != null) { + IResourceValue value = layoutMap.get(name); + if (value != null) { + String valueStr = value.getValue(); + if (valueStr.startsWith("?")) { //$NON-NLS-1$ + // FIXME: It's a reference. We should resolve this properly. + return null; + } + return new Path(valueStr); + } + } + + return null; + } + + /** + * Looks up the path to the file corresponding to the given attribute value, such as + * @layout/foo, which will return the foo.xml file in res/layout/. (The general format + * of the resource url is {@literal @[<package_name>:]<resource_type>/<resource_name>}. + * + * @param url the attribute url + * @return the path to the file defining this attribute, or null if not found + */ + public IPath findResourceFile(String url) { + if (!url.startsWith("@")) { //$NON-NLS-1$ + return null; + } + int typeEnd = url.indexOf('/', 1); + if (typeEnd == -1) { + return null; + } + int nameBegin = typeEnd + 1; + int typeBegin = 1; + int colon = url.lastIndexOf(':', typeEnd); + boolean isFrameworkResource = false; + if (colon != -1) { + // The URL contains a package name. + + // FIXME: We should consult the package and Do The Right Thing. + // If the package is @android (which is by far the most common case), + // then maybe we can look up the corresponding file in the "data/" folder + // in the SDK. + // Otherwise, the package MAY be the same package as the current project, + // in which case we can just ignore it (because it will be exactly + // relative to the current project's folder), and otherwise we may + // have to look in other projects. Fortunately, this is not common. + + String packageName = url.substring(typeBegin, colon); + if ("android".equals(packageName)) { //$NON-NLS-1$ + isFrameworkResource = true; + } + + typeBegin = colon + 1; + } + + String typeName = url.substring(typeBegin, typeEnd); + ResourceType type = ResourceType.getEnum(typeName); + if (type == null) { + return null; + } + + String name = url.substring(nameBegin); + return findResourceFile(type, name, isFrameworkResource); + } + /** This StyleRange represents a missing class link that the user can click */ private static class ClassLinkStyleRange extends StyleRange {} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java index 8a7dd7a..c0e69b1 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java @@ -31,11 +31,19 @@ import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; import com.android.layoutlib.api.LayoutScene; import com.android.sdklib.SdkConstants; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.swt.SWT; @@ -63,10 +71,14 @@ import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.ContributionItemFactory; import org.eclipse.ui.actions.TextActionHandler; import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; +import org.eclipse.ui.ide.IDE; import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.w3c.dom.Node; @@ -671,18 +683,77 @@ class LayoutCanvas extends Canvas { } /** - * Show the XML element corresponding to the point under the mouse event - * (unless it's a root). + * Shows the given {@link CanvasViewInfo}, which can mean exposing its XML or if it's + * an included element, its corresponding file. * - * @param e A mouse event pointing on the screen whose underlying XML - * element we want to view + * @param vi the {@link CanvasViewInfo} to be shown */ - public void showXml(MouseEvent e) { + public void show(CanvasViewInfo vi) { + String url = vi.getIncludeUrl(); + if (url != null) { + showInclude(url); + } else { + showXml(vi); + } + } + + /** + * Shows the layout file referenced by the given url in the same project. + * + * @param url The layout attribute url of the form @layout/foo + */ + private void showInclude(String url) { + GraphicalEditorPart graphicalEditor = mLayoutEditor.getGraphicalEditor(); + IPath filePath = graphicalEditor.findResourceFile(url); + + IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); + IPath workspacePath = workspace.getLocation(); + IEditorSite editorSite = graphicalEditor.getEditorSite(); + if (workspacePath.isPrefixOf(filePath)) { + IPath relativePath = filePath.makeRelativeTo(workspacePath); + IResource xmlFile = workspace.findMember(relativePath); + try { + EditorUtility.openInEditor(xmlFile, true); + return; + } catch (PartInitException ex) { + AdtPlugin.log(ex, "Can't open %$1s", url); //$NON-NLS-1$ + } + } else { + // It's not a path in the workspace; look externally + // (this is probably an @android: path) + if (filePath.isAbsolute()) { + IFileStore fileStore = EFS.getLocalFileSystem().getStore(filePath); + // fileStore = fileStore.getChild(names[i]); + if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) { + IWorkbenchPage page = editorSite.getWorkbenchWindow().getActivePage(); + try { + IDE.openEditorOnFileStore(page, fileStore); + return; + } catch (PartInitException ex) { + AdtPlugin.log(ex, "Can't open %$1s", url); //$NON-NLS-1$ + } + } + } + } + + // Failed: display message to the user + String message = String.format("Could not find resource %1$s", url); + IStatusLineManager status = editorSite.getActionBars().getStatusLineManager(); + status.setErrorMessage(message); + getDisplay().beep(); + } + + /** + * Show the XML element corresponding to the given {@link CanvasViewInfo} (unless it's + * a root). + * + * @param vi The clicked {@link CanvasViewInfo} whose underlying XML element we want + * to view + */ + private void showXml(CanvasViewInfo vi) { // Warp to the text editor and show the corresponding XML for the // double-clicked widget - LayoutPoint p = ControlPoint.create(this, e).toLayout(); - CanvasViewInfo vi = mViewHierarchy.findViewInfoAt(p); - if (vi == null || vi.isRoot()) { + if (vi.isRoot()) { return; } |